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