• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024-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
16import { ArkPhiExpr } from '../core/base/Expr';
17import { Local } from '../core/base/Local';
18import { ArkAssignStmt, Stmt } from '../core/base/Stmt';
19import { BasicBlock } from '../core/graph/BasicBlock';
20import { Cfg } from '../core/graph/Cfg';
21import { DominanceFinder } from '../core/graph/DominanceFinder';
22import { DominanceTree } from '../core/graph/DominanceTree';
23import { ArkBody } from '../core/model/ArkBody';
24
25export class StaticSingleAssignmentFormer {
26    public transformBody(body: ArkBody): void {
27        let cfg = body.getCfg();
28
29        let blockToDefs = new Map<BasicBlock, Set<Local>>();
30        let localToBlocks = new Map<Local, Set<BasicBlock>>();
31        for (const block of cfg.getBlocks()) {
32            let defs = new Set<Local>();
33            for (const stmt of block.getStmts()) {
34                this.transformStmt(stmt, defs, localToBlocks, block);
35            }
36            blockToDefs.set(block, defs);
37        }
38
39        let dominanceFinder = new DominanceFinder(cfg);
40        let blockToPhiStmts = this.decideBlockToPhiStmts(body, dominanceFinder, blockToDefs, localToBlocks);
41        this.addPhiStmts(blockToPhiStmts, cfg, blockToDefs);
42        let dominanceTree = new DominanceTree(dominanceFinder);
43
44        this.renameLocals(body, dominanceTree, blockToPhiStmts);
45    }
46
47    private transformStmt(stmt: Stmt, defs: Set<Local>, localToBlocks: Map<Local, Set<BasicBlock>>, block: BasicBlock): void {
48        if (stmt.getDef() != null && stmt.getDef() instanceof Local) {
49            let local = stmt.getDef() as Local;
50            defs.add(local);
51            if (localToBlocks.has(local)) {
52                localToBlocks.get(local)?.add(block);
53            } else {
54                let blcoks = new Set<BasicBlock>();
55                blcoks.add(block);
56                localToBlocks.set(local, blcoks);
57            }
58        }
59    }
60
61    private decideBlockToPhiStmts(
62        body: ArkBody,
63        dominanceFinder: DominanceFinder,
64        blockToDefs: Map<BasicBlock, Set<Local>>,
65        localToBlocks: Map<Local, Set<BasicBlock>>
66    ): Map<BasicBlock, Set<Stmt>> {
67        let blockToPhiStmts = new Map<BasicBlock, Set<Stmt>>();
68        let blockToPhiLocals = new Map<BasicBlock, Set<Local>>();
69        let localToPhiBlock = new Map<Local, Set<BasicBlock>>();
70
71        for (const [_, local] of body.getLocals()) {
72            localToPhiBlock.set(local, new Set());
73            let phiBlocks = localToPhiBlock.get(local) as Set<BasicBlock>;
74            let blocks = Array.from(localToBlocks.get(local) as Set<BasicBlock>);
75            while (blocks.length !== 0) {
76                let block = blocks.splice(0, 1)[0] as BasicBlock;
77                let dfs = dominanceFinder.getDominanceFrontiers(block);
78                for (const df of dfs) {
79                    this.handleDf(blockToPhiStmts, blockToPhiLocals, phiBlocks, df, local, blockToDefs, blocks);
80                }
81            }
82        }
83
84        return blockToPhiStmts;
85    }
86
87    private handleDf(
88        blockToPhiStmts: Map<BasicBlock, Set<Stmt>>,
89        blockToPhiLocals: Map<BasicBlock, Set<Local>>,
90        phiBlocks: Set<BasicBlock>,
91        df: BasicBlock,
92        local: Local,
93        blockToDefs: Map<BasicBlock, Set<Local>>,
94        blocks: BasicBlock[]
95    ): void {
96        if (!phiBlocks.has(df)) {
97            phiBlocks.add(df);
98
99            let phiStmt = this.createEmptyPhiStmt(local);
100            if (blockToPhiStmts.has(df)) {
101                blockToPhiStmts.get(df)?.add(phiStmt);
102                blockToPhiLocals.get(df)?.add(local);
103            } else {
104                let phiStmts = new Set<Stmt>();
105                phiStmts.add(phiStmt);
106                blockToPhiStmts.set(df, phiStmts);
107                let phiLocals = new Set<Local>();
108                phiLocals.add(local);
109                blockToPhiLocals.set(df, phiLocals);
110            }
111            blockToDefs.get(df)?.add(local);
112
113            if (!blockToDefs.get(df)?.has(local)) {
114                blocks.push(df);
115            }
116        }
117    }
118
119    private handleBlockWithSucc(
120        blockToPhiStmts: Map<BasicBlock, Set<Stmt>>,
121        succ: BasicBlock,
122        blockToDefs: Map<BasicBlock, Set<Local>>,
123        block: BasicBlock,
124        phiArgsNum: Map<Stmt, number>
125    ): void {
126        for (const phi of blockToPhiStmts.get(succ) as Set<Stmt>) {
127            let local = phi.getDef() as Local;
128            if (blockToDefs.get(block)?.has(local)) {
129                if (phiArgsNum.has(phi)) {
130                    let num = phiArgsNum.get(phi) as number;
131                    phiArgsNum.set(phi, num + 1);
132                } else {
133                    phiArgsNum.set(phi, 1);
134                }
135            }
136        }
137    }
138
139    private addPhiStmts(blockToPhiStmts: Map<BasicBlock, Set<Stmt>>, cfg: Cfg, blockToDefs: Map<BasicBlock, Set<Local>>): void {
140        let phiArgsNum = new Map<Stmt, number>();
141        for (const block of cfg.getBlocks()) {
142            let succs = Array.from(block.getSuccessors());
143            for (const succ of succs) {
144                if (blockToPhiStmts.has(succ)) {
145                    this.handleBlockWithSucc(blockToPhiStmts, succ, blockToDefs, block, phiArgsNum);
146                }
147            }
148        }
149
150        for (const block of blockToPhiStmts.keys()) {
151            let phis = blockToPhiStmts.get(block) as Set<Stmt>;
152            let phisTocheck = new Set(phis);
153            for (const phi of phisTocheck) {
154                if ((phiArgsNum.get(phi) as number) < 2) {
155                    phis.delete(phi);
156                }
157            }
158
159            for (const phi of phis) {
160                cfg.insertBefore(phi, block.getHead() as Stmt);
161            }
162        }
163    }
164
165    private renameUseAndDef(stmt: Stmt, localToNameStack: Map<Local, Local[]>, nextFreeIdx: number, newLocals: Set<Local>, newPhiStmts: Set<Stmt>): number {
166        let uses = stmt.getUses();
167        if (uses.length > 0 && !this.constainsPhiExpr(stmt)) {
168            for (const use of uses) {
169                if (use instanceof Local) {
170                    let nameStack = localToNameStack.get(use) as Local[];
171                    let newUse = nameStack[nameStack.length - 1];
172                    stmt.replaceUse(use, newUse);
173                }
174            }
175        }
176
177        // rename def
178        let def = stmt.getDef();
179        if (def != null && def instanceof Local) {
180            let newName = def.getName() + '#' + nextFreeIdx;
181            nextFreeIdx++;
182            let newDef = new Local(newName);
183            newDef.setOriginalValue(def);
184            newLocals.add(newDef);
185            localToNameStack.get(def)?.push(newDef);
186            (<ArkAssignStmt>stmt).setLeftOp(newDef);
187            if (this.constainsPhiExpr(stmt)) {
188                newPhiStmts.add(stmt);
189            }
190        }
191        return nextFreeIdx;
192    }
193
194    private renameLocals(body: ArkBody, dominanceTree: DominanceTree, blockToPhiStmts: Map<BasicBlock, Set<Stmt>>): void {
195        let newLocals = new Set(body.getLocals().values());
196        let localToNameStack = new Map<Local, Local[]>();
197        for (const local of newLocals) {
198            localToNameStack.set(local, new Array<Local>());
199        }
200
201        let blockStack = new Array<BasicBlock>();
202        let visited = new Set<BasicBlock>();
203        let dfsBlocks = dominanceTree.getAllNodesDFS();
204        let nextFreeIdx = 0;
205        for (const block of dfsBlocks) {
206            let newPhiStmts = new Set<Stmt>();
207            for (const stmt of block.getStmts()) {
208                // rename uses and def
209                nextFreeIdx = this.renameUseAndDef(stmt, localToNameStack, nextFreeIdx, newLocals, newPhiStmts);
210            }
211            visited.add(block);
212            blockStack.push(block);
213            if (blockToPhiStmts.has(block)) {
214                blockToPhiStmts.set(block, newPhiStmts);
215            }
216
217            // rename phiStmts' args
218            let succs = Array.from(block.getSuccessors());
219            for (const succ of succs) {
220                if (!blockToPhiStmts.has(succ)) {
221                    continue;
222                }
223                let phiStmts = blockToPhiStmts.get(succ) as Set<Stmt>;
224                for (const phiStmt of phiStmts) {
225                    let def = phiStmt.getDef() as Local;
226                    let oriDef = this.getOriginalLocal(def, new Set(localToNameStack.keys())) as Local;
227                    let nameStack = localToNameStack.get(oriDef) as Local[];
228                    let arg = nameStack[nameStack.length - 1];
229                    this.addNewArgToPhi(phiStmt, arg, block);
230                }
231            }
232
233            // if a block's children in dominance tree are visited, remove it
234            this.removeVisitedTree(blockStack, dominanceTree, visited, localToNameStack);
235        }
236        body.setLocals(newLocals);
237    }
238
239    private removeVisitedTree(blockStack: BasicBlock[], dominanceTree: DominanceTree, visited: Set<BasicBlock>, localToNameStack: Map<Local, Local[]>): void {
240        let top = blockStack[blockStack.length - 1];
241        let children = dominanceTree.getChildren(top);
242        while (this.containsAllChildren(visited, children)) {
243            blockStack.pop();
244            for (const stmt of top.getStmts()) {
245                let def = stmt.getDef();
246                if (def != null && def instanceof Local) {
247                    let oriDef = this.getOriginalLocal(def, new Set(localToNameStack.keys())) as Local;
248                    localToNameStack.get(oriDef)?.pop();
249                }
250            }
251
252            // next block to check
253            if (blockStack.length > 0) {
254                top = blockStack[blockStack.length - 1];
255                children = dominanceTree.getChildren(top);
256            } else {
257                break;
258            }
259        }
260    }
261
262    private constainsPhiExpr(stmt: Stmt): boolean {
263        if (stmt instanceof ArkAssignStmt && stmt.getUses().length > 0) {
264            for (const use of stmt.getUses()) {
265                if (use instanceof ArkPhiExpr) {
266                    return true;
267                }
268            }
269        }
270        return false;
271    }
272
273    private getOriginalLocal(local: Local, locals: Set<Local>): Local | null {
274        if (locals.has(local)) {
275            return local;
276        }
277        let hashPos = local.getName().indexOf('#');
278        let oriName = local.getName().substring(0, hashPos);
279        for (const oriLocal of locals) {
280            if (oriLocal.getName() === oriName) {
281                return oriLocal;
282            }
283        }
284        return null;
285    }
286
287    private addNewArgToPhi(phiStmt: Stmt, arg: Local, block: BasicBlock): void {
288        for (let use of phiStmt.getUses()) {
289            if (use instanceof ArkPhiExpr) {
290                let phiExpr = use as ArkPhiExpr;
291                let args = phiExpr.getArgs();
292                let argToBlock = phiExpr.getArgToBlock();
293                args.push(arg);
294                argToBlock.set(arg, block);
295                phiExpr.setArgs(args);
296                phiExpr.setArgToBlock(argToBlock);
297                break;
298            }
299        }
300    }
301
302    private containsAllChildren(blockSet: Set<BasicBlock>, children: BasicBlock[]): boolean {
303        for (const child of children) {
304            if (!blockSet.has(child)) {
305                return false;
306            }
307        }
308        return true;
309    }
310
311    private createEmptyPhiStmt(local: Local): ArkAssignStmt {
312        let phiExpr = new ArkPhiExpr();
313        return new ArkAssignStmt(local, phiExpr);
314    }
315}
316