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 { ArkIfStmt, ArkReturnStmt, ArkReturnVoidStmt, Stmt } from '../base/Stmt'; 17import { ArkError, ArkErrorCode } from '../common/ArkError'; 18import Logger, { LOG_MODULE_TYPE } from '../../utils/logger'; 19const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'BasicBlock'); 20 21/** 22 * @category core/graph 23 * A `BasicBlock` is composed of: 24 * - ID: a **number** that uniquely identify the basic block, initialized as -1. 25 * - Statements: an **array** of statements in the basic block. 26 * - Predecessors: an **array** of basic blocks in front of the current basic block. More accurately, these basic 27 * blocks can reach the current block through edges. 28 * - Successors: an **array** of basic blocks after the current basic block. More accurately, the current block can 29 * reach these basic blocks through edges. 30 */ 31export class BasicBlock { 32 private id: number = -1; 33 private stmts: Stmt[] = []; 34 private predecessorBlocks: BasicBlock[] = []; 35 private successorBlocks: BasicBlock[] = []; 36 private exceptionalSuccessorBlocks?: BasicBlock[]; 37 38 constructor() {} 39 40 public getId(): number { 41 return this.id; 42 } 43 44 public setId(id: number): void { 45 this.id = id; 46 } 47 48 /** 49 * Returns an array of the statements in a basic block. 50 * @returns An array of statements in a basic block. 51 */ 52 public getStmts(): Stmt[] { 53 return this.stmts; 54 } 55 56 public addStmt(stmt: Stmt): void { 57 this.stmts.push(stmt); 58 } 59 60 /** 61 * Adds the given stmt at the beginning of the basic block. 62 * @param stmt 63 */ 64 public addHead(stmt: Stmt | Stmt[]): void { 65 if (stmt instanceof Stmt) { 66 this.stmts.unshift(stmt); 67 } else { 68 this.stmts.unshift(...stmt); 69 } 70 } 71 72 /** 73 * Adds the given stmt at the end of the basic block. 74 * @param stmt 75 */ 76 public addTail(stmt: Stmt | Stmt[]): void { 77 if (stmt instanceof Stmt) { 78 this.stmts.push(stmt); 79 } else { 80 stmt.forEach(stmt => this.stmts.push(stmt)); 81 } 82 } 83 84 /** 85 * Inserts toInsert in the basic block after point. 86 * @param toInsert 87 * @param point 88 * @returns The number of successfully inserted statements 89 */ 90 public insertAfter(toInsert: Stmt | Stmt[], point: Stmt): number { 91 let index = this.stmts.indexOf(point); 92 if (index < 0) { 93 return 0; 94 } 95 return this.insertPos(index + 1, toInsert); 96 } 97 98 /** 99 * Inserts toInsert in the basic block befor point. 100 * @param toInsert 101 * @param point 102 * @returns The number of successfully inserted statements 103 */ 104 public insertBefore(toInsert: Stmt | Stmt[], point: Stmt): number { 105 let index = this.stmts.indexOf(point); 106 if (index < 0) { 107 return 0; 108 } 109 return this.insertPos(index, toInsert); 110 } 111 112 /** 113 * Removes the given stmt from this basic block. 114 * @param stmt 115 * @returns 116 */ 117 public remove(stmt: Stmt): void { 118 let index = this.stmts.indexOf(stmt); 119 if (index < 0) { 120 return; 121 } 122 this.stmts.splice(index, 1); 123 } 124 125 /** 126 * Removes the first stmt from this basic block. 127 */ 128 public removeHead(): void { 129 this.stmts.splice(0, 1); 130 } 131 132 /** 133 * Removes the last stmt from this basic block. 134 */ 135 public removeTail(): void { 136 this.stmts.splice(this.stmts.length - 1, 1); 137 } 138 139 public getHead(): Stmt | null { 140 if (this.stmts.length === 0) { 141 return null; 142 } 143 return this.stmts[0]; 144 } 145 146 public getTail(): Stmt | null { 147 let size = this.stmts.length; 148 if (size === 0) { 149 return null; 150 } 151 return this.stmts[size - 1]; 152 } 153 154 /** 155 * Returns successors of the current basic block, whose types are also basic blocks (i.e.{@link BasicBlock}). 156 * @returns Successors of the current basic block. 157 * @example 158 * 1. get block successors. 159 160 ```typescript 161 const body = arkMethod.getBody(); 162 const blocks = [...body.getCfg().getBlocks()] 163 for (let i = 0; i < blocks.length; i++) { 164 const block = blocks[i] 165 ... 166 for (const next of block.getSuccessors()) { 167 ... 168 } 169 } 170 ``` 171 */ 172 public getSuccessors(): BasicBlock[] { 173 return this.successorBlocks; 174 } 175 176 /** 177 * Returns predecessors of the current basic block, whose types are also basic blocks. 178 * @returns An array of basic blocks. 179 */ 180 public getPredecessors(): BasicBlock[] { 181 return this.predecessorBlocks; 182 } 183 184 public addPredecessorBlock(block: BasicBlock): void { 185 this.predecessorBlocks.push(block); 186 } 187 188 public setPredecessorBlock(idx: number, block: BasicBlock): boolean { 189 if (idx < this.predecessorBlocks.length) { 190 this.predecessorBlocks[idx] = block; 191 return true; 192 } 193 return false; 194 } 195 196 public setSuccessorBlock(idx: number, block: BasicBlock): boolean { 197 if (idx < this.successorBlocks.length) { 198 this.successorBlocks[idx] = block; 199 return true; 200 } 201 return false; 202 } 203 204 // Temp just for SSA 205 public addStmtToFirst(stmt: Stmt): void { 206 this.addHead(stmt); 207 } 208 209 // Temp just for SSA 210 public addSuccessorBlock(block: BasicBlock): void { 211 this.successorBlocks.push(block); 212 } 213 214 public removePredecessorBlock(block: BasicBlock): boolean { 215 let index = this.predecessorBlocks.indexOf(block); 216 if (index < 0) { 217 return false; 218 } 219 this.predecessorBlocks.splice(index, 1); 220 return true; 221 } 222 223 public removeSuccessorBlock(block: BasicBlock): boolean { 224 let index = this.successorBlocks.indexOf(block); 225 if (index < 0) { 226 return false; 227 } 228 this.successorBlocks.splice(index, 1); 229 return true; 230 } 231 232 public toString(): string { 233 let strs: string[] = []; 234 for (const stmt of this.stmts) { 235 strs.push(stmt.toString() + '\n'); 236 } 237 return strs.join(''); 238 } 239 240 public validate(): ArkError { 241 let branchStmts: Stmt[] = []; 242 for (const stmt of this.stmts) { 243 if (stmt instanceof ArkIfStmt || stmt instanceof ArkReturnStmt || stmt instanceof ArkReturnVoidStmt) { 244 branchStmts.push(stmt); 245 } 246 } 247 248 if (branchStmts.length > 1) { 249 let errMsg = `More than one branch or return stmts in the block: ${branchStmts.map(value => value.toString()).join('\n')}`; 250 logger.error(errMsg); 251 return { 252 errCode: ArkErrorCode.BB_MORE_THAN_ONE_BRANCH_RET_STMT, 253 errMsg: errMsg, 254 }; 255 } 256 257 if (branchStmts.length === 1 && branchStmts[0] !== this.stmts[this.stmts.length - 1]) { 258 let errMsg = `${branchStmts[0].toString()} not at the end of block.`; 259 logger.error(errMsg); 260 return { 261 errCode: ArkErrorCode.BB_BRANCH_RET_STMT_NOT_AT_END, 262 errMsg: errMsg, 263 }; 264 } 265 266 return { errCode: ArkErrorCode.OK }; 267 } 268 269 private insertPos(index: number, toInsert: Stmt | Stmt[]): number { 270 if (toInsert instanceof Stmt) { 271 this.stmts.splice(index, 0, toInsert); 272 return 1; 273 } 274 this.stmts.splice(index, 0, ...toInsert); 275 return toInsert.length; 276 } 277 278 public getExceptionalSuccessorBlocks(): BasicBlock[] | undefined { 279 return this.exceptionalSuccessorBlocks; 280 } 281 282 public addExceptionalSuccessorBlock(block: BasicBlock): void { 283 if (!this.exceptionalSuccessorBlocks) { 284 this.exceptionalSuccessorBlocks = []; 285 } 286 this.exceptionalSuccessorBlocks.push(block); 287 } 288} 289