• 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 { 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