• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16/**
17 * Reaching Definitions Data Flow Analysis
18 *
19 * This module implements the Reaching Definitions data flow analysis algorithm.
20 * Reaching Definitions is a forward data flow analysis that determines, for each
21 * program point, the set of variable definitions (assignments) that may reach
22 * that point without being overwritten.
23 *
24 * Key Components:
25 * 1. **Transfer Function**:
26 *    - Computes the out set for each node based on its in set.
27 *    - Uses gen and kill sets to model the effects of assignments:
28 *      - **gen**: The set of definitions generated by the current node.
29 *      - **kill**: The set of definitions killed (overwritten) by the current node.
30 *
31 * 2. **Meet Operation**:
32 *    - Combines data flow values from multiple paths (e.g., union for reaching definitions).
33 *    - Ensures that the analysis is conservative (safe) by over-approximating the result.
34 *
35 *  The analysis is forward, meaning it propagates information from predecessors to successors.
36 *
37 */
38
39import { ArkAssignStmt, Stmt } from '../base/Stmt';
40import { Value } from '../base/Value';
41import { BaseImplicitGraph, NodeID } from '../graph/BaseImplicitGraph';
42import { ArkMethod } from '../model/ArkMethod';
43import { DataFlowProblem, TransferFunction, FlowGraph } from './GenericDataFlow';
44import { SparseBitVector } from '../../utils/SparseBitVector';
45import { Cfg } from '../graph/Cfg';
46
47type RDNode = Stmt;
48type DFNodeCollection = SparseBitVector;
49let coCtor: new (s: number) => SparseBitVector = SparseBitVector;
50const BV_SIZE = 32;
51
52export class ReachingDefProblem implements DataFlowProblem<NodeID, DFNodeCollection> {
53    flowGraph: ReachingDefFlowGraph;
54    transferFunction: ReachingDefTransferFunction;
55    meet: (a: DFNodeCollection, b: DFNodeCollection) => DFNodeCollection;
56    initIn: Map<NodeID, DFNodeCollection>;
57    initOut: Map<NodeID, DFNodeCollection>;
58    forward: boolean;
59    empty: DFNodeCollection = new coCtor(BV_SIZE);
60
61    constructor(method: ArkMethod, forward: boolean = true) {
62        this.flowGraph = new ReachingDefFlowGraph(method);
63        this.transferFunction = new ReachingDefTransferFunction(this.flowGraph);
64        this.meet = (x: DFNodeCollection, y: DFNodeCollection): DFNodeCollection => {
65            let r = x.clone();
66            r.unionWith(y);
67            return r;
68        };
69        this.initIn = new Map(this.flowGraph.nodesInPostOrder.map(i => [i, new coCtor(BV_SIZE)]));
70        this.initOut = new Map(this.flowGraph.nodesInPostOrder.map(i => [i, new coCtor(BV_SIZE)]));
71        this.forward = forward;
72    }
73}
74
75/**
76 * Represents the control flow graph (CFG) for reaching definitions analysis.
77 * This class implements the FlowGraph interface and provides methods to retrieve
78 * successors and predecessors of nodes, as well as topological orderings of nodes.
79 */
80class ReachingDefFlowGraph extends BaseImplicitGraph<RDNode> implements FlowGraph<NodeID> {
81    nodesInPostOrder: NodeID[];
82
83    constructor(method: ArkMethod) {
84        super();
85        const cfg = method.getCfg();
86        if (!cfg) {
87            throw new Error('CFG not found');
88        }
89
90        const nodes = cfg.getStmts();
91        this.nodeToIdMap = new Map(nodes.map((x, i) => [x, i]));
92        this.idToNodeMap = new Map(nodes.map((x, i) => [i, x]));
93        this.nodesInPostOrder = nodes.map((_, i) => i);
94
95        this.initSuccPred(nodes, cfg);
96    }
97
98    getGraphName(): string {
99        return 'Reaching Definition Flow Graph';
100    }
101
102    dumpNodes(): void {
103        this.nodeToIdMap?.forEach((id, node) => console.log(id + ': ' + node.toString()));
104    }
105
106    private initSuccPred(nodes: RDNode[], cfg: Cfg): void {
107        this.succMap = new Map();
108        this.predMap = new Map();
109
110        cfg.getBlocks().forEach(bb => {
111            let stmts = bb.getStmts();
112            if (stmts.length === 0) {
113                return;
114            }
115
116            for (let i = 0; i < stmts.length - 1; i++) {
117                let c = this.nodeToIdMap!.get(stmts[i])!;
118                let n = this.nodeToIdMap!.get(stmts[i + 1])!;
119                if (c === undefined || n === undefined) {
120                    continue;
121                }
122                this.succMap.set(c, [n]);
123                this.predMap.set(n, [c]);
124            }
125
126            let terminate = bb.getTail();
127            if (!terminate) {
128                throw new Error('cfg has no terminal');
129            }
130
131            let successors = bb.getSuccessors();
132            // try...catch语句,catch所在的block在CFG表示里是没有前驱block的,需要在这里额外查找并将exceptionalSuccessorBlocks作为try块的后继块之一
133            const exceptionalSuccessorBlocks = bb.getExceptionalSuccessorBlocks();
134            if (exceptionalSuccessorBlocks !== undefined) {
135                successors.push(...exceptionalSuccessorBlocks);
136            }
137            successors.forEach(succBB => {
138                let head = succBB.getHead();
139                if (!head) {
140                    return;
141                }
142                let t = this.nodeToIdMap?.get(terminate!)!;
143                let h = this.nodeToIdMap?.get(head)!;
144                if (t === undefined || h === undefined) {
145                    return;
146                }
147                // Terminate's succ
148                let succ = this.succMap.get(t) ?? [];
149                succ.push(h);
150                this.succMap.set(t, succ);
151
152                // Head's pred
153                let pred = this.predMap.get(h) ?? [];
154                pred.push(t);
155                this.predMap.set(h, pred);
156            });
157        });
158    }
159}
160
161/**
162 * Represents the transfer function for reaching definitions analysis.
163 */
164export class ReachingDefTransferFunction implements TransferFunction<NodeID, DFNodeCollection> {
165    gen: DFNodeCollection;
166    kill: Map<NodeID, DFNodeCollection>;
167
168    constructor(flowGraph: ReachingDefFlowGraph) {
169        this.gen = new coCtor(BV_SIZE);
170        this.kill = new Map();
171        this.initGenKill(flowGraph);
172    }
173
174    apply(n: NodeID, x: DFNodeCollection): DFNodeCollection {
175        const result = x.clone();
176        if (this.gen.test(n)) {
177            result.set(n);
178        }
179
180        const killSet = this.kill.get(n);
181        if (killSet) {
182            for (const item of killSet) {
183                result.reset(item);
184            }
185        }
186        return result;
187    }
188
189    private initGenKill(g: ReachingDefFlowGraph): void {
190        let genValue2Nodes: Map<Value, DFNodeCollection> = new Map();
191        // Init Gen
192        g.getNodeToIdMap().forEach((id, node) => {
193            if (node instanceof ArkAssignStmt) {
194                let lop = node.getLeftOp();
195                let genNodes = genValue2Nodes.get(lop) ?? new coCtor(BV_SIZE);
196                genNodes.set(id);
197                genValue2Nodes.set(lop, genNodes);
198                this.gen.set(id);
199            }
200        });
201
202        // Init Kill
203        genValue2Nodes.forEach((defNodes, v) => {
204            for (const i of defNodes) {
205                const killSet = defNodes.clone();
206                killSet.reset(i);
207                this.kill.set(i, killSet);
208            }
209        });
210    }
211}
212