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, ArkThrowStmt } from '../core/base/Stmt'; 17import { Trap } from '../core/base/Trap'; 18import { BasicBlock } from '../core/graph/BasicBlock'; 19import { Cfg } from '../core/graph/Cfg'; 20 21export enum CodeBlockType { 22 NORMAL, 23 IF, 24 ELSE, 25 BREAK, 26 CONTINUE, 27 DO, 28 DO_WHILE, 29 WHILE, 30 FOR, 31 COMPOUND_END, 32 TRY, 33 CATCH, 34 FINALLY, 35} 36 37export type TraversalCallback = (block: BasicBlock | undefined, type: CodeBlockType) => void; 38 39export class AbstractFlowGraph { 40 private nodes: AbstractNode[] = []; 41 private entry: AbstractNode; 42 private block2NodeMap: Map<BasicBlock, AbstractNode>; 43 private structOf: Map<AbstractNode, Region> = new Map(); 44 private structTypes: Map<Region, RegionType> = new Map(); 45 private structBlocks: Map<AbstractNode, Set<AbstractNode>> = new Map(); 46 private loopMap: Map<AbstractNode, NaturalLoopRegion> = new Map(); 47 48 constructor(cfg: Cfg, traps?: Trap[]) { 49 this.block2NodeMap = new Map<BasicBlock, AbstractNode>(); 50 for (const bb of cfg.getBlocks()) { 51 let an = new AbstractNode(); 52 an.setBlock(bb); 53 this.block2NodeMap.set(bb, an); 54 } 55 56 for (const bb of cfg.getBlocks()) { 57 let an = this.block2NodeMap.get(bb)!; 58 for (const succ of bb.getSuccessors()) { 59 an.addSucc(this.block2NodeMap.get(succ)!); 60 } 61 for (const pred of bb.getPredecessors()) { 62 an.addPred(this.block2NodeMap.get(pred)!); 63 } 64 } 65 66 let trapRegions = this.buildTrap(traps); 67 this.searchTrapFinallyNodes(trapRegions); 68 this.trapsStructuralAnalysis(trapRegions); 69 70 this.entry = this.block2NodeMap.get(cfg.getStartingBlock()!)!; 71 this.entry = this.structuralAnalysis(this.entry); 72 } 73 74 public getEntry(): AbstractNode { 75 return this.entry; 76 } 77 78 public getForIncBlock(block: BasicBlock): BasicBlock { 79 let node = this.block2NodeMap.get(block)!; 80 let loop = this.loopMap.get(node)! as ForLoopRegion; 81 return loop.inc.getBlock()!; 82 } 83 84 public preOrder(node: AbstractNode, callback: TraversalCallback, visitor: Set<AbstractNode> = new Set()): void { 85 visitor.add(node); 86 node.traversal(callback, CodeBlockType.NORMAL); 87 for (const succ of node.getSucc()) { 88 if (!visitor.has(succ)) { 89 this.preOrder(succ, callback, visitor); 90 } 91 } 92 } 93 94 private structuralAnalysis(entry: AbstractNode, scope?: Set<AbstractNode>): AbstractNode { 95 let preds = entry.getPred(); 96 let entryBak = entry; 97 this.nodes = this.dfsPostOrder(entry, scope); 98 this.entry = entry; 99 this.buildCyclicStructural(); 100 101 // acyclic structural 102 let postMax = this.nodes.length; 103 let change = true; 104 while (postMax > 1 && change) { 105 change = false; 106 for (let i = 0; i < postMax; i++) { 107 let node = this.nodes[i]; 108 let nset = new Set<AbstractNode>(); 109 let rtype = this.identifyRegionType(node, nset, scope); 110 if (!rtype) { 111 continue; 112 } 113 114 let p = this.reduce(rtype, nset); 115 if (!p) { 116 continue; 117 } 118 119 scope?.add(p); 120 if (nset.has(entry)) { 121 entry = p; 122 } 123 this.nodes = this.dfsPostOrder(entry, scope); 124 change = postMax !== this.nodes.length; 125 postMax = this.nodes.length; 126 } 127 } 128 129 for (const pred of preds) { 130 pred.replaceSucc(entryBak, entry); 131 entry.addPred(pred); 132 } 133 134 return entry; 135 } 136 137 private dfsPostOrder( 138 node: AbstractNode, 139 scope?: Set<AbstractNode>, 140 visitor: Set<AbstractNode> = new Set(), 141 postOrder: AbstractNode[] = [] 142 ): AbstractNode[] { 143 visitor.add(node); 144 for (const succ of node.getSucc()) { 145 if (visitor.has(succ)) { 146 continue; 147 } 148 149 if (scope && !scope.has(succ)) { 150 continue; 151 } 152 153 this.dfsPostOrder(succ, scope, visitor, postOrder); 154 } 155 postOrder.push(node); 156 return postOrder; 157 } 158 159 private buildCyclicStructural(): void { 160 for (const loop of this.prepareBuildLoops()) { 161 let nset = new Set<AbstractNode | Region>(); 162 for (const n of loop) { 163 if (this.structOf.has(n)) { 164 nset.add(this.structOf.get(n)!); 165 } else { 166 nset.add(n); 167 } 168 } 169 let rtype = this.cyclicRegionType(nset); 170 let region = this.createRegion(rtype, nset)! as NaturalLoopRegion; 171 region.revise(); 172 this.structTypes.set(region, rtype); 173 let blocks = new Set<AbstractNode>(); 174 for (const s of nset) { 175 this.handleRegion(s, region, blocks); 176 } 177 this.structBlocks.set(region, blocks); 178 this.loopMap.set(region.header, region); 179 } 180 } 181 182 private handleRegion(s: AbstractNode | Region, region: NaturalLoopRegion, blocks: Set<AbstractNode>): void { 183 if (!this.structOf.has(s)) { 184 this.structOf.set(s, region); 185 } 186 if (this.structBlocks.has(s as Region)) { 187 for (const b of this.structBlocks.get(s as Region)!) { 188 blocks.add(b); 189 } 190 } else { 191 blocks.add(s); 192 } 193 } 194 195 private prepareBuildLoops(): Set<AbstractNode>[] { 196 let dom = this.buildDominator(); 197 let loops: Set<AbstractNode>[] = []; 198 for (const header of this.nodes) { 199 let innermost: Set<AbstractNode> | undefined; 200 let longest: number = 0; 201 202 let backEdges = this.getBackEdges(dom, header); 203 if (backEdges.size === 0) { 204 continue; 205 } 206 207 if (this.isSelfLoopNode(header)) { 208 loops.push(new Set<AbstractNode>([header])); 209 } 210 211 for (const start of backEdges) { 212 let loop = this.naturalLoop(start, header); 213 if (!innermost || loop.size > longest) { 214 innermost = loop; 215 longest = loop.size; 216 } 217 } 218 loops.push(innermost!); 219 } 220 loops.sort((a, b) => a.size - b.size); 221 return loops; 222 } 223 224 private buildDominator(): Map<AbstractNode, Set<AbstractNode>> { 225 let domin = new Map<AbstractNode, Set<AbstractNode>>(); 226 domin.set(this.entry, new Set<AbstractNode>([this.entry])); 227 for (const node of this.nodes) { 228 if (node !== this.entry) { 229 domin.set(node, new Set<AbstractNode>(this.nodes)); 230 } 231 } 232 233 let change = true; 234 while (change) { 235 change = false; 236 for (const node of this.nodes) { 237 if (node === this.entry) { 238 continue; 239 } 240 let t = new Set<AbstractNode>(domin.get(node)!); 241 for (const p of node.getPred()) { 242 t = this.setIntersect(t, domin.get(p)!); 243 } 244 t.add(node); 245 if (!this.isSetEqual(t, domin.get(node)!)) { 246 change = true; 247 domin.set(node, t); 248 } 249 } 250 } 251 252 return domin; 253 } 254 255 private getBackEdges(dom: Map<AbstractNode, Set<AbstractNode>>, header: AbstractNode): Set<AbstractNode> { 256 let backEdges = new Set<AbstractNode>(); 257 for (const n of header.getPred()) { 258 // h dom n && n -> h 259 if (dom.get(n)?.has(header)) { 260 backEdges.add(n); 261 } 262 } 263 return backEdges; 264 } 265 266 private naturalLoop(backEdgeStart: AbstractNode, backEdgeEnd: AbstractNode): Set<AbstractNode> { 267 let stack: AbstractNode[] = []; 268 let loop = new Set<AbstractNode>([backEdgeEnd, backEdgeStart]); 269 270 stack.push(backEdgeStart); 271 272 while (stack.length > 0) { 273 let m = stack.shift()!; 274 for (const pred of m.getPred()) { 275 if (loop.has(pred)) { 276 continue; 277 } 278 loop.add(pred); 279 stack.push(pred); 280 } 281 } 282 283 return loop; 284 } 285 286 private isSelfLoopNode(node: AbstractNode): boolean { 287 let inSucc = false; 288 let inPred = false; 289 290 for (const pred of node.getPred()) { 291 if (pred === node) { 292 inPred = true; 293 } 294 } 295 296 for (const succ of node.getSucc()) { 297 if (succ === node) { 298 inSucc = true; 299 } 300 } 301 302 return inSucc && inPred; 303 } 304 305 private isForLoopIncNode(node: AbstractNode): boolean { 306 for (const loop of this.loopMap.values()) { 307 if (loop.getType() === RegionType.FOR_LOOP_REGION) { 308 if (node === (loop as ForLoopRegion).inc) { 309 return true; 310 } 311 } 312 } 313 return false; 314 } 315 316 private isValidInBlocks(node: AbstractNode, scope?: Set<AbstractNode>): boolean { 317 if (this.isForLoopIncNode(node) || node.hasIfStmt()) { 318 return false; 319 } 320 if (scope && !scope.has(node)) { 321 return false; 322 } 323 324 return true; 325 } 326 327 private isIfRegion(node: AbstractNode, nodeSet: Set<AbstractNode>): boolean { 328 nodeSet.clear(); 329 if (node.getSucc().length !== 2) { 330 return false; 331 } 332 let m = node.getSucc()[0]; 333 let n = node.getSucc()[1]; 334 if (m.getSucc().length === 1 && m.getSucc()[0] === n) { 335 nodeSet.add(node).add(m); 336 return true; 337 } 338 339 return false; 340 } 341 342 private isIfExitRegion(node: AbstractNode, nodeSet: Set<AbstractNode>): boolean { 343 nodeSet.clear(); 344 if (node.getSucc().length !== 2) { 345 return false; 346 } 347 let m = node.getSucc()[0]; 348 if (m.hasReturnStmt()) { 349 nodeSet.add(node).add(m); 350 return true; 351 } 352 353 return false; 354 } 355 356 private isIfElseRegion(node: AbstractNode, nodeSet: Set<AbstractNode>): boolean { 357 nodeSet.clear(); 358 if (node.getSucc().length !== 2) { 359 return false; 360 } 361 let m = node.getSucc()[0]; 362 let n = node.getSucc()[1]; 363 if ( 364 (m.getSucc().length === 1 && 365 n.getSucc().length === 1 && 366 m.getPred().length === 1 && 367 n.getPred().length === 1 && 368 m.getSucc()[0] === n.getSucc()[0]) || 369 (m.getSucc().length === 0 && n.getSucc().length === 0) 370 ) { 371 nodeSet.add(node).add(m).add(n); 372 return true; 373 } 374 375 return false; 376 } 377 378 private isBlockRegion(node: AbstractNode, nodeSet: Set<AbstractNode>, scope?: Set<AbstractNode>): boolean { 379 let n = node; 380 let p = true; 381 let s = n.getSucc().length === 1; 382 nodeSet.clear(); 383 384 let blocks = []; 385 while (p && s && !nodeSet.has(n) && this.isValidInBlocks(n, scope)) { 386 nodeSet.add(n); 387 blocks.push(n); 388 n = n.getSucc()[0]; 389 p = n.getPred().length === 1; 390 s = n.getSucc().length === 1; 391 } 392 393 if (p && this.isValidInBlocks(n, scope)) { 394 if (!nodeSet.has(n)) { 395 blocks.push(n); 396 } 397 nodeSet.add(n); 398 } 399 400 n = node; 401 p = n.getPred().length === 1; 402 s = true; 403 while (p && s && this.isValidInBlocks(n, scope)) { 404 if (!nodeSet.has(n)) { 405 blocks.unshift(n); 406 } 407 nodeSet.add(n); 408 n = n.getPred()[0]; 409 if (nodeSet.has(n)) { 410 break; 411 } 412 p = n.getPred().length === 1; 413 s = n.getSucc().length === 1; 414 } 415 416 if (s && this.isValidInBlocks(n, scope)) { 417 if (!nodeSet.has(n)) { 418 blocks.unshift(n); 419 } 420 nodeSet.add(n); 421 } 422 423 nodeSet.clear(); 424 for (const n of blocks) { 425 nodeSet.add(n); 426 } 427 428 if (nodeSet.size >= 2) { 429 return true; 430 } 431 return false; 432 } 433 434 private isIfBreakRegion(node: AbstractNode, nodeSet: Set<AbstractNode>, loop: NaturalLoopRegion): boolean { 435 let m = node.getSucc()[0]; 436 nodeSet.clear(); 437 if (this.isExitLoop(m, this.structBlocks.get(loop)!)) { 438 nodeSet.add(node); 439 return true; 440 } 441 442 if (m.getSucc().length === 1 && this.isExitLoop(m.getSucc()[0], this.structBlocks.get(loop)!)) { 443 nodeSet.add(node).add(m); 444 return true; 445 } 446 447 return false; 448 } 449 450 private isIfContinueRegion(node: AbstractNode, nodeSet: Set<AbstractNode>, loop: NaturalLoopRegion): boolean { 451 nodeSet.clear(); 452 let m = node.getSucc()[0]; 453 let n = node.getSucc()[1]; 454 if (loop.control.has(m)) { 455 nodeSet.add(node); 456 return true; 457 } 458 459 if (m.getSucc().length === 1 && loop.control.has(m.getSucc()[0]) && !loop.control.has(n) && !this.isIfElseRegion(node, nodeSet)) { 460 nodeSet.add(node).add(m); 461 return true; 462 } 463 return false; 464 } 465 466 private isWhileRegion(node: AbstractNode, nodeSet: Set<AbstractNode>, loop: NaturalLoopRegion): boolean { 467 nodeSet.clear(); 468 let m = node.getSucc()[0]; 469 if (loop.header === node && m.getSucc().length === 1 && m.getPred().length === 1 && m.getSucc()[0] === node) { 470 nodeSet.add(node).add(m); 471 return true; 472 } 473 return false; 474 } 475 476 private isForRegion(node: AbstractNode, nodeSet: Set<AbstractNode>, loop: NaturalLoopRegion): boolean { 477 nodeSet.clear(); 478 if (loop.header === node && loop.getType() === RegionType.FOR_LOOP_REGION) { 479 let forLoop = loop as ForLoopRegion; 480 let blocks = node.getSucc()[0]; 481 if (forLoop.inc.getPred().length === 1 && forLoop.inc.getPred()[0] === blocks && blocks.getSucc().length === 1) { 482 nodeSet.add(node).add(forLoop.inc).add(blocks); 483 return true; 484 } 485 } 486 return false; 487 } 488 489 private isDoWhileRegion(node: AbstractNode, nodeSet: Set<AbstractNode>, loop: NaturalLoopRegion): boolean { 490 nodeSet.clear(); 491 if (loop.back === node && loop.getType() === RegionType.DO_WHILE_LOOP_REGION) { 492 let blocks = node.getPred()[0]; 493 if (blocks.getSucc().length === 1 && blocks.getSucc()[0] === node && node.getSucc()[0] === blocks) { 494 nodeSet.add(blocks).add(node); 495 return true; 496 } 497 } 498 return false; 499 } 500 501 private identifyRegionType(node: AbstractNode, nodeSet: Set<AbstractNode>, scope?: Set<AbstractNode>): RegionType | undefined { 502 if (this.isBlockRegion(node, nodeSet, scope)) { 503 return RegionType.BLOCK_REGION; 504 } 505 506 let inLoop = false; 507 let region = this.structOf.get(node); 508 if (region && LOOP_TYPES.has(region?.getType())) { 509 inLoop = true; 510 } 511 512 if (new Set(node.getPred()).has(node) && new Set(node.getSucc()).has(node)) { 513 nodeSet.add(node); 514 if (inLoop) { 515 return region?.getType(); 516 } 517 return RegionType.SELF_LOOP_REGION; 518 } 519 520 if (node.getSucc().length !== 2) { 521 return undefined; 522 } 523 524 if (inLoop) { 525 let loop = region as NaturalLoopRegion; 526 if (!loop.control.has(node)) { 527 if (this.isIfBreakRegion(node, nodeSet, loop)) { 528 return RegionType.IF_THEN_BREAK_REGION; 529 } 530 531 if (this.isIfContinueRegion(node, nodeSet, loop)) { 532 return RegionType.IF_THEN_CONTINUE_REGION; 533 } 534 } 535 if (this.isWhileRegion(node, nodeSet, loop)) { 536 return RegionType.WHILE_LOOP_REGION; 537 } 538 539 if (this.isForRegion(node, nodeSet, loop)) { 540 return RegionType.FOR_LOOP_REGION; 541 } 542 543 if (this.isDoWhileRegion(node, nodeSet, loop)) { 544 return RegionType.DO_WHILE_LOOP_REGION; 545 } 546 } 547 548 // check for if 549 if (this.isIfExitRegion(node, nodeSet)) { 550 return RegionType.IF_THEN_EXIT_REGION; 551 } 552 if (this.isIfRegion(node, nodeSet)) { 553 return RegionType.IF_REGION; 554 } 555 556 // check for an if else 557 if (this.isIfElseRegion(node, nodeSet)) { 558 return RegionType.IF_ELSE_REGION; 559 } 560 561 return undefined; 562 } 563 564 private cyclicRegionType(nodeSet: Set<AbstractNode>): RegionType { 565 let nodes = Array.from(nodeSet); 566 let header = nodes[0]; 567 if (nodeSet.size === 1) { 568 let tail = nodes[0].getBlock()?.getTail(); 569 if (tail instanceof ArkIfStmt) { 570 return RegionType.DO_WHILE_LOOP_REGION; 571 } 572 return RegionType.WHILE_LOOP_REGION; 573 } 574 575 let back = nodes[1]; 576 // exit loop from back 577 if (!this.hasExitLoopSucc(header, nodeSet) && this.hasExitLoopSucc(back, nodeSet)) { 578 return RegionType.DO_WHILE_LOOP_REGION; 579 } 580 581 if (this.hasExitLoopSucc(header, nodeSet) && this.hasExitLoopSucc(back, nodeSet)) { 582 // header true exit loop --> exit is break 583 if (!nodeSet.has(header.getSucc()[0])) { 584 return RegionType.DO_WHILE_LOOP_REGION; 585 } 586 } 587 588 // for 589 if (back.getSucc().length === 1 && back.getBlock()?.getStmts()?.length === 1) { 590 let isForLoop = true; 591 for (const pred of header.getPred()) { 592 if (nodeSet.has(pred) && pred !== back) { 593 isForLoop = false; 594 } 595 } 596 if (isForLoop) { 597 return RegionType.FOR_LOOP_REGION; 598 } 599 } 600 601 return RegionType.WHILE_LOOP_REGION; 602 } 603 604 private hasExitLoopSucc(node: AbstractNode, nodeSet: Set<AbstractNode>): boolean { 605 for (const succ of node.getSucc()) { 606 if (!nodeSet.has(succ)) { 607 return true; 608 } 609 } 610 611 return false; 612 } 613 614 private isExitLoop(node: AbstractNode | Region, nodeSet: Set<AbstractNode>): boolean { 615 if (this.structBlocks.has(node)) { 616 for (const n of this.structBlocks.get(node)!) { 617 if (!nodeSet.has(n)) { 618 return true; 619 } 620 } 621 } else { 622 if (!nodeSet.has(node)) { 623 return true; 624 } 625 } 626 627 return false; 628 } 629 630 private createRegion(rtype: RegionType, nodeSet: Set<AbstractNode>): Region | undefined { 631 let node: Region | undefined; 632 if (rtype === RegionType.BLOCK_REGION) { 633 node = new BlockRegion(nodeSet); 634 } else if (rtype === RegionType.IF_ELSE_REGION) { 635 node = new IfElseRegion(nodeSet); 636 } else if (rtype === RegionType.IF_REGION) { 637 node = new IfRegion(nodeSet); 638 } else if (rtype === RegionType.IF_THEN_EXIT_REGION) { 639 node = new IfExitRegion(nodeSet); 640 } else if (rtype === RegionType.IF_THEN_BREAK_REGION) { 641 node = new IfBreakRegion(nodeSet); 642 } else if (rtype === RegionType.IF_THEN_CONTINUE_REGION) { 643 node = new IfContinueRegion(nodeSet); 644 } else if (rtype === RegionType.SELF_LOOP_REGION) { 645 node = new SelfLoopRegion(nodeSet); 646 } else if (rtype === RegionType.WHILE_LOOP_REGION) { 647 let whileLoop = new WhileLoopRegion(nodeSet); 648 this.loopMap.set(whileLoop.header, whileLoop); 649 node = whileLoop; 650 } else if (rtype === RegionType.FOR_LOOP_REGION) { 651 let forLoop = new ForLoopRegion(nodeSet); 652 this.loopMap.set(forLoop.header, forLoop); 653 node = forLoop; 654 } else if (rtype === RegionType.DO_WHILE_LOOP_REGION) { 655 let doWhileLoop = new DoWhileLoopRegion(nodeSet); 656 this.loopMap.set(doWhileLoop.header, doWhileLoop); 657 node = doWhileLoop; 658 } else if (rtype === RegionType.TRY_CATCH_REGION || rtype === RegionType.TRY_FINALLY_REGION || rtype === RegionType.TRY_CATCH_FINALLY_REGION) { 659 node = new TrapRegion(nodeSet, rtype); 660 } 661 662 return node; 663 } 664 665 private reduce(rtype: RegionType, nodeSet: Set<AbstractNode>): Region | undefined { 666 let region = this.createRegion(rtype, nodeSet); 667 region?.replace(); 668 if (region === undefined) { 669 return undefined; 670 } 671 this.structTypes.set(region, rtype); 672 let blocks = new Set<AbstractNode>(); 673 for (const s of nodeSet) { 674 this.structOf.set(s, region); 675 if (this.structBlocks.has(s as Region)) { 676 for (const b of this.structBlocks.get(s as Region)!) { 677 blocks.add(b); 678 } 679 } else { 680 blocks.add(s); 681 } 682 } 683 this.structBlocks.set(region, blocks); 684 return region; 685 } 686 687 private setIntersect(a: Set<AbstractNode>, b: Set<AbstractNode>): Set<AbstractNode> { 688 let r = new Set<AbstractNode>(); 689 if (!b) { 690 return r; 691 } 692 for (const n of b) { 693 if (a.has(n)) { 694 r.add(n); 695 } 696 } 697 698 return r; 699 } 700 701 private isSetEqual(a: Set<AbstractNode>, b: Set<AbstractNode>): boolean { 702 if (a.size !== b.size) { 703 return false; 704 } 705 706 return this.setIntersect(a, b).size === a.size; 707 } 708 709 private buildTrap(traps?: Trap[]): NaturalTrapRegion[] { 710 if (!traps) { 711 return []; 712 } 713 traps.sort((a, b) => a.getTryBlocks().length + a.getCatchBlocks().length - (b.getTryBlocks().length + b.getCatchBlocks().length)); 714 715 let trapRegions: NaturalTrapRegion[] = []; 716 717 for (const trap of traps) { 718 let region = new NaturalTrapRegion(trap, this.block2NodeMap); 719 let findTrapRegion = this.getNaturalTrapRegion(region); 720 721 if (!findTrapRegion) { 722 for (const n of region.getNodes()) { 723 this.structOf.set(n, region); 724 } 725 trapRegions.push(region); 726 continue; 727 } 728 if (findTrapRegion.type === RegionType.TRY_FINALLY_REGION) { 729 findTrapRegion.trySet = region.trySet; 730 findTrapRegion.catchSet = region.catchSet; 731 region = findTrapRegion; 732 } else { 733 findTrapRegion.finallySet = region.finallySet; 734 region = findTrapRegion; 735 } 736 737 for (const n of region.getNodes()) { 738 this.structOf.set(n, region); 739 } 740 region.type = RegionType.TRY_CATCH_FINALLY_REGION; 741 } 742 743 this.structOf.clear(); 744 745 return trapRegions; 746 } 747 748 private searchTrapFinallyNodes(trapRegions: NaturalTrapRegion[]): void { 749 // search finally 750 for (const region of trapRegions) { 751 if (region.type === RegionType.TRY_CATCH_REGION) { 752 continue; 753 } 754 755 this.bfs(region); 756 } 757 } 758 759 private bfs(region: NaturalTrapRegion): void { 760 let finallyNodes = new Set<AbstractNode>(); 761 let count = (region as NaturalTrapRegion).finallySet!.size; 762 let queue = [region.getSucc()[0]]; 763 while (queue.length > 0 && finallyNodes.size < count) { 764 let node = queue[0]; 765 queue.splice(0, 1); 766 finallyNodes.add(node); 767 (region as NaturalTrapRegion).identifyFinallySet.add(node); 768 for (const succ of node.getSucc()) { 769 if (!finallyNodes.has(succ)) { 770 queue.push(succ); 771 } 772 } 773 } 774 } 775 776 private getNaturalTrapRegion(trap: NaturalTrapRegion): NaturalTrapRegion | undefined { 777 let findTrap = this.findNaturalTrapRegion(trap.trySet); 778 if (findTrap) { 779 return findTrap; 780 } 781 if (trap.catchSet) { 782 findTrap = this.findNaturalTrapRegion(trap.catchSet); 783 } 784 785 if (findTrap) { 786 return findTrap; 787 } 788 789 if (trap.finallySet) { 790 findTrap = this.findNaturalTrapRegion(trap.finallySet); 791 } 792 793 return findTrap; 794 } 795 796 private findNaturalTrapRegion(nodes: Set<AbstractNode>): NaturalTrapRegion | undefined { 797 let findTrap: NaturalTrapRegion | undefined; 798 for (const node of nodes) { 799 if (!this.structOf.has(node)) { 800 return undefined; 801 } 802 if (!findTrap) { 803 findTrap = this.structOf.get(node)! as NaturalTrapRegion; 804 continue; 805 } 806 if (findTrap !== this.structOf.get(node)) { 807 return undefined; 808 } 809 } 810 return findTrap; 811 } 812 813 private trapsStructuralAnalysis(trapRegions: NaturalTrapRegion[]): void { 814 trapRegions.sort((a, b) => a.size() - b.size()); 815 816 for (const trap of trapRegions) { 817 let tryNode = this.trapsSubStructuralAnalysis(trap.trySet)!; 818 let catchNode: AbstractNode | undefined = this.trapsSubStructuralAnalysis(trap.catchSet); 819 let finnallyNode: AbstractNode | undefined = this.trapsSubStructuralAnalysis(trap.identifyFinallySet); 820 821 if (catchNode === undefined) { 822 this.reduce(RegionType.TRY_FINALLY_REGION, new Set([tryNode, finnallyNode!])); 823 } else if (finnallyNode === undefined) { 824 this.reduce(RegionType.TRY_CATCH_REGION, new Set([tryNode, catchNode!])); 825 } else { 826 this.reduce(RegionType.TRY_CATCH_FINALLY_REGION, new Set([tryNode, catchNode!, finnallyNode!])); 827 } 828 } 829 } 830 831 private trapsSubStructuralAnalysis(nodes?: Set<AbstractNode>): AbstractNode | undefined { 832 if (!nodes) { 833 return undefined; 834 } 835 let entry = Array.from(nodes)[0]; 836 if (nodes.size <= 1) { 837 return entry; 838 } 839 840 for (const node of nodes) { 841 if (this.structOf.has(node)) { 842 nodes.add(this.structOf.get(node)!); 843 } 844 } 845 846 return this.structuralAnalysis(entry, nodes); 847 } 848} 849 850enum RegionType { 851 ABSTRACT_NODE, 852 TRY_NODE, 853 CATCH_NODE, 854 FINALLY_NODE, 855 /* Sequence of blocks. */ 856 BLOCK_REGION, 857 IF_REGION, 858 IF_ELSE_REGION, 859 IF_THEN_EXIT_REGION, 860 IF_THEN_BREAK_REGION, 861 IF_THEN_CONTINUE_REGION, 862 SELF_LOOP_REGION, 863 NATURAL_LOOP_REGION, 864 WHILE_LOOP_REGION, 865 DO_WHILE_LOOP_REGION, 866 FOR_LOOP_REGION, 867 CASE_REGION, 868 SWITCH_REGION, 869 TRY_CATCH_REGION, 870 TRY_FINALLY_REGION, 871 TRY_CATCH_FINALLY_REGION, 872} 873 874const LOOP_TYPES = new Set<RegionType>([ 875 RegionType.SELF_LOOP_REGION, 876 RegionType.NATURAL_LOOP_REGION, 877 RegionType.WHILE_LOOP_REGION, 878 RegionType.FOR_LOOP_REGION, 879 RegionType.DO_WHILE_LOOP_REGION, 880]); 881 882class AbstractNode { 883 type: RegionType; 884 private predNodes: AbstractNode[] = []; 885 private succNodes: AbstractNode[] = []; 886 private bb: BasicBlock | undefined; 887 888 constructor() { 889 this.type = RegionType.ABSTRACT_NODE; 890 } 891 892 public traversal(callback: TraversalCallback, type: CodeBlockType): void { 893 callback(this.bb, type); 894 } 895 896 public getType(): RegionType { 897 return this.type; 898 } 899 900 public getSucc(): AbstractNode[] { 901 return this.succNodes; 902 } 903 904 public addSucc(node: AbstractNode): void { 905 this.succNodes.push(node); 906 } 907 908 public replaceSucc(src: AbstractNode, dst: AbstractNode): void { 909 for (let i = 0; i < this.succNodes.length; i++) { 910 if (this.succNodes[i] === src) { 911 this.succNodes[i] = dst; 912 break; 913 } 914 } 915 } 916 917 public removeSucc(src: AbstractNode): void { 918 for (let i = 0; i < this.predNodes.length; i++) { 919 if (this.succNodes[i] === src) { 920 this.succNodes.splice(i, 1); 921 break; 922 } 923 } 924 } 925 926 public getPred(): AbstractNode[] { 927 return this.predNodes; 928 } 929 930 public addPred(block: AbstractNode): void { 931 let set = new Set(this.predNodes); 932 if (set.has(block)) { 933 return; 934 } 935 this.predNodes.push(block); 936 } 937 938 public replacePred(src: AbstractNode, dst: AbstractNode): void { 939 for (let i = 0; i < this.predNodes.length; i++) { 940 if (this.predNodes[i] === src) { 941 this.predNodes[i] = dst; 942 break; 943 } 944 } 945 } 946 947 public removePred(src: AbstractNode): void { 948 for (let i = 0; i < this.predNodes.length; i++) { 949 if (this.predNodes[i] === src) { 950 this.predNodes.splice(i, 1); 951 break; 952 } 953 } 954 } 955 956 public setBlock(bb: BasicBlock): void { 957 this.bb = bb; 958 } 959 960 public getBlock(): BasicBlock | undefined { 961 return this.bb; 962 } 963 964 public hasIfStmt(): boolean { 965 if (!this.bb) { 966 return false; 967 } 968 969 for (let stmt of this.bb.getStmts()) { 970 if (stmt instanceof ArkIfStmt) { 971 return true; 972 } 973 } 974 return false; 975 } 976 977 public hasReturnStmt(): boolean { 978 if (!this.bb) { 979 return false; 980 } 981 for (let stmt of this.bb.getStmts()) { 982 if (stmt instanceof ArkReturnStmt) { 983 return true; 984 } 985 } 986 return false; 987 } 988} 989 990abstract class Region extends AbstractNode { 991 nset: Set<AbstractNode>; 992 constructor(nset: Set<AbstractNode>, type: RegionType) { 993 super(); 994 this.nset = nset; 995 this.type = type; 996 } 997 998 public getBlock(): BasicBlock | undefined { 999 if (this.nset.size === 0) { 1000 return undefined; 1001 } 1002 1003 return Array.from(this.nset)[0].getBlock(); 1004 } 1005 1006 public abstract replace(): void; 1007} 1008 1009class BlockRegion extends Region { 1010 blocks: AbstractNode[]; 1011 constructor(nset: Set<AbstractNode>) { 1012 super(nset, RegionType.BLOCK_REGION); 1013 this.blocks = Array.from(nset); 1014 } 1015 1016 public replace(): void { 1017 for (let pred of this.blocks[0].getPred()) { 1018 pred.replaceSucc(this.blocks[0], this); 1019 this.addPred(pred); 1020 } 1021 1022 for (let succ of this.blocks[this.blocks.length - 1].getSucc()) { 1023 succ.replacePred(this.blocks[this.blocks.length - 1], this); 1024 this.addSucc(succ); 1025 } 1026 } 1027 1028 public traversal(callback: TraversalCallback): void { 1029 for (const node of this.blocks) { 1030 node.traversal(callback, CodeBlockType.NORMAL); 1031 } 1032 } 1033} 1034 1035abstract class NaturalLoopRegion extends Region { 1036 header: AbstractNode; 1037 back: AbstractNode; 1038 control: Set<AbstractNode>; 1039 1040 constructor(nset: Set<AbstractNode>, type: RegionType = RegionType.NATURAL_LOOP_REGION) { 1041 super(nset, type); 1042 let nodes = Array.from(nset); 1043 this.header = nodes[0]; 1044 if (nset.size > 1) { 1045 this.back = nodes[1]; 1046 } else { 1047 this.back = nodes[0]; 1048 } 1049 this.control = new Set([this.header]); 1050 } 1051 1052 public replace(): void { 1053 for (let pred of this.header.getPred()) { 1054 if (!this.nset.has(pred)) { 1055 pred.replaceSucc(this.header, this); 1056 this.addPred(pred); 1057 } 1058 } 1059 1060 let succNodes = new Set<AbstractNode>(); 1061 for (let node of this.nset) { 1062 for (let succ of node.getSucc()) { 1063 if (!this.nset.has(succ)) { 1064 succNodes.add(succ); 1065 } 1066 } 1067 } 1068 1069 if (succNodes.size === 0) { 1070 return; 1071 } 1072 1073 let pred = Array.from(succNodes)[0]; 1074 let replaced = false; 1075 for (let succ of pred.getPred()) { 1076 if (this.nset.has(succ)) { 1077 if (!replaced) { 1078 pred.replacePred(succ, this); 1079 this.addSucc(pred); 1080 replaced = true; 1081 } else { 1082 pred.removePred(succ); 1083 } 1084 } 1085 } 1086 } 1087 1088 public revise(): void { 1089 // add node to loop sets 1090 for (const node of this.nset) { 1091 for (const succ of node.getSucc()) { 1092 if (!this.nset.has(succ) && succ !== this.getExitNode() && succ.getSucc().length === 1 && succ.getSucc()[0] === this.getExitNode()) { 1093 this.nset.add(succ); 1094 } 1095 } 1096 } 1097 } 1098 1099 abstract getExitNode(): AbstractNode; 1100} 1101 1102class SelfLoopRegion extends NaturalLoopRegion { 1103 constructor(nset: Set<AbstractNode>) { 1104 super(nset, RegionType.SELF_LOOP_REGION); 1105 this.back = this.header; 1106 } 1107 1108 public replace(): void { 1109 for (let pred of this.header.getPred()) { 1110 if (pred !== this.header) { 1111 pred.replaceSucc(this.header, this); 1112 this.addPred(pred); 1113 } 1114 } 1115 1116 for (let succ of this.header.getSucc()) { 1117 if (succ !== this.header) { 1118 succ.replacePred(this.header, this); 1119 this.addSucc(succ); 1120 } 1121 } 1122 } 1123 1124 getExitNode(): AbstractNode { 1125 return this.header.getSucc()[1]; 1126 } 1127} 1128 1129class WhileLoopRegion extends NaturalLoopRegion { 1130 constructor(nset: Set<AbstractNode>) { 1131 super(nset, RegionType.WHILE_LOOP_REGION); 1132 } 1133 1134 public traversal(callback: TraversalCallback): void { 1135 this.header.traversal(callback, CodeBlockType.WHILE); 1136 if (this.header !== this.back) { 1137 this.back.traversal(callback, CodeBlockType.NORMAL); 1138 } 1139 callback(undefined, CodeBlockType.COMPOUND_END); 1140 } 1141 1142 getExitNode(): AbstractNode { 1143 return this.header.getSucc()[1]; 1144 } 1145} 1146 1147class DoWhileLoopRegion extends NaturalLoopRegion { 1148 constructor(nset: Set<AbstractNode>) { 1149 super(nset, RegionType.DO_WHILE_LOOP_REGION); 1150 this.control.clear(); 1151 this.control.add(this.back); 1152 } 1153 1154 public traversal(callback: TraversalCallback): void { 1155 callback(undefined, CodeBlockType.DO); 1156 if (this.header !== this.back) { 1157 this.header.traversal(callback, CodeBlockType.NORMAL); 1158 } 1159 this.back.traversal(callback, CodeBlockType.DO_WHILE); 1160 } 1161 1162 getExitNode(): AbstractNode { 1163 return this.back.getSucc()[1]; 1164 } 1165} 1166 1167class ForLoopRegion extends NaturalLoopRegion { 1168 inc: AbstractNode; 1169 1170 constructor(nset: Set<AbstractNode>) { 1171 super(nset, RegionType.FOR_LOOP_REGION); 1172 this.inc = this.back; 1173 this.control.add(this.inc); 1174 } 1175 1176 public traversal(callback: TraversalCallback): void { 1177 this.header.traversal(callback, CodeBlockType.FOR); 1178 for (const node of this.nset) { 1179 if (node !== this.header && node !== this.inc) { 1180 node.traversal(callback, CodeBlockType.NORMAL); 1181 } 1182 } 1183 callback(undefined, CodeBlockType.COMPOUND_END); 1184 } 1185 1186 getExitNode(): AbstractNode { 1187 return this.header.getSucc()[1]; 1188 } 1189} 1190 1191class IfRegion extends Region { 1192 contition: AbstractNode; 1193 then: AbstractNode; 1194 1195 constructor(nset: Set<AbstractNode>) { 1196 super(nset, RegionType.IF_REGION); 1197 let nodes = Array.from(nset); 1198 this.contition = nodes[0]; 1199 this.then = nodes[1]; 1200 } 1201 1202 public replace(): void { 1203 this.replaceContitionPred(); 1204 1205 for (let succ of this.then.getSucc()) { 1206 if (succ !== this.then) { 1207 succ.replacePred(this.then, this); 1208 succ.removePred(this.contition); 1209 this.addSucc(succ); 1210 } 1211 } 1212 } 1213 1214 public traversal(callback: TraversalCallback): void { 1215 this.contition.traversal(callback, CodeBlockType.IF); 1216 this.then.traversal(callback, CodeBlockType.NORMAL); 1217 callback(undefined, CodeBlockType.COMPOUND_END); 1218 } 1219 1220 protected replaceContitionPred(): void { 1221 for (let pred of this.contition.getPred()) { 1222 if (pred !== this.contition) { 1223 pred.replaceSucc(this.contition, this); 1224 this.addPred(pred); 1225 } 1226 } 1227 } 1228} 1229 1230class IfExitRegion extends IfRegion { 1231 constructor(nset: Set<AbstractNode>) { 1232 super(nset); 1233 this.type = RegionType.IF_THEN_EXIT_REGION; 1234 } 1235 1236 public replace(): void { 1237 this.replaceContitionPred(); 1238 1239 let succ = this.contition.getSucc()[1]; 1240 succ.replacePred(this.contition, this); 1241 this.addSucc(succ); 1242 } 1243} 1244 1245class IfBreakRegion extends IfRegion { 1246 constructor(nset: Set<AbstractNode>) { 1247 super(nset); 1248 this.type = RegionType.IF_THEN_BREAK_REGION; 1249 } 1250 1251 public replace(): void { 1252 this.replaceContitionPred(); 1253 1254 let succ = this.contition.getSucc()[1]; 1255 succ.replacePred(this.contition, this); 1256 this.addSucc(succ); 1257 1258 if (this.then) { 1259 succ = this.then.getSucc()[0]; 1260 succ.removePred(this.then); 1261 } else { 1262 succ = this.contition.getSucc()[0]; 1263 succ.removePred(this.contition); 1264 } 1265 } 1266 1267 public traversal(callback: TraversalCallback): void { 1268 this.contition.traversal(callback, CodeBlockType.IF); 1269 this.then?.traversal(callback, CodeBlockType.NORMAL); 1270 callback(undefined, CodeBlockType.BREAK); 1271 callback(undefined, CodeBlockType.COMPOUND_END); 1272 } 1273} 1274 1275class IfContinueRegion extends IfBreakRegion { 1276 constructor(nset: Set<AbstractNode>) { 1277 super(nset); 1278 this.type = RegionType.IF_THEN_CONTINUE_REGION; 1279 } 1280 1281 public traversal(callback: TraversalCallback): void { 1282 this.contition.traversal(callback, CodeBlockType.IF); 1283 this.then?.traversal(callback, CodeBlockType.NORMAL); 1284 callback(undefined, CodeBlockType.CONTINUE); 1285 callback(undefined, CodeBlockType.COMPOUND_END); 1286 } 1287} 1288 1289class IfElseRegion extends Region { 1290 contition: AbstractNode; 1291 then: AbstractNode; 1292 else: AbstractNode; 1293 1294 constructor(nset: Set<AbstractNode>) { 1295 super(nset, RegionType.IF_ELSE_REGION); 1296 let nodes = Array.from(nset); 1297 this.contition = nodes[0]; 1298 this.then = nodes[1]; 1299 this.else = nodes[2]; 1300 } 1301 1302 public replace(): void { 1303 for (let pred of this.contition.getPred()) { 1304 if (pred !== this.contition) { 1305 pred.replaceSucc(this.contition, this); 1306 this.addPred(pred); 1307 } 1308 } 1309 1310 for (let succ of this.then.getSucc()) { 1311 if (succ !== this.then) { 1312 succ.replacePred(this.then, this); 1313 succ.removePred(this.else); 1314 this.addSucc(succ); 1315 } 1316 } 1317 } 1318 1319 public traversal(callback: TraversalCallback): void { 1320 this.contition.traversal(callback, CodeBlockType.IF); 1321 this.then.traversal(callback, CodeBlockType.NORMAL); 1322 callback(undefined, CodeBlockType.ELSE); 1323 this.else.traversal(callback, CodeBlockType.NORMAL); 1324 callback(undefined, CodeBlockType.COMPOUND_END); 1325 } 1326} 1327 1328class TrapRegion extends Region { 1329 tryNode: AbstractNode; 1330 catchNode?: AbstractNode; 1331 finallyNode?: AbstractNode; 1332 1333 constructor(nset: Set<AbstractNode>, type: RegionType) { 1334 super(nset, type); 1335 let nodes = Array.from(nset); 1336 1337 this.tryNode = nodes[0]; 1338 if (type === RegionType.TRY_CATCH_REGION) { 1339 this.catchNode = nodes[1]; 1340 } else if (type === RegionType.TRY_FINALLY_REGION) { 1341 this.finallyNode = nodes[1]; 1342 } else { 1343 this.catchNode = nodes[1]; 1344 this.finallyNode = nodes[2]; 1345 } 1346 } 1347 1348 public replace(): void { 1349 for (let pred of this.tryNode.getPred()) { 1350 if (pred !== this.tryNode) { 1351 pred.replaceSucc(this.tryNode, this); 1352 this.addPred(pred); 1353 } 1354 } 1355 1356 if (this.finallyNode) { 1357 for (let succ of this.finallyNode.getSucc()) { 1358 if (succ !== this.finallyNode) { 1359 succ.replacePred(this.finallyNode, this); 1360 this.addSucc(succ); 1361 } 1362 } 1363 } else { 1364 for (let succ of this.tryNode.getSucc()) { 1365 if (succ !== this.tryNode) { 1366 succ.replacePred(this.tryNode, this); 1367 this.addSucc(succ); 1368 } 1369 } 1370 } 1371 } 1372 1373 public traversal(callback: TraversalCallback): void { 1374 callback(undefined, CodeBlockType.TRY); 1375 this.tryNode.traversal(callback, CodeBlockType.NORMAL); 1376 if (this.catchNode) { 1377 callback(this.catchNode.getBlock(), CodeBlockType.CATCH); 1378 this.catchNode?.traversal(callback, CodeBlockType.NORMAL); 1379 } 1380 if (this.finallyNode) { 1381 callback(undefined, CodeBlockType.FINALLY); 1382 this.finallyNode?.traversal(callback, CodeBlockType.NORMAL); 1383 } 1384 callback(undefined, CodeBlockType.COMPOUND_END); 1385 } 1386} 1387 1388class NaturalTrapRegion extends Region { 1389 trySet: Set<AbstractNode>; 1390 catchSet?: Set<AbstractNode>; 1391 finallySet?: Set<AbstractNode>; 1392 identifyFinallySet: Set<AbstractNode>; 1393 1394 constructor(trap: Trap, block2NodeMap: Map<BasicBlock, AbstractNode>) { 1395 super(new Set<AbstractNode>(), RegionType.TRY_CATCH_FINALLY_REGION); 1396 this.trySet = new Set<AbstractNode>(); 1397 this.catchSet = new Set<AbstractNode>(); 1398 this.identifyFinallySet = new Set<AbstractNode>(); 1399 1400 for (const block of trap.getTryBlocks()) { 1401 this.trySet.add(block2NodeMap.get(block)!); 1402 } 1403 1404 for (const block of trap.getCatchBlocks()) { 1405 this.catchSet.add(block2NodeMap.get(block)!); 1406 } 1407 1408 if (this.isFinallyNode(Array.from(this.catchSet!)[this.catchSet.size - 1])!) { 1409 this.type = RegionType.TRY_FINALLY_REGION; 1410 this.finallySet = this.catchSet; 1411 this.catchSet = undefined; 1412 } else { 1413 this.type = RegionType.TRY_CATCH_REGION; 1414 } 1415 } 1416 1417 private isFinallyNode(node: AbstractNode): boolean { 1418 let block = node.getBlock(); 1419 if (!block) { 1420 return false; 1421 } 1422 1423 let stmtLen = block.getStmts().length; 1424 if (stmtLen < 1) { 1425 return false; 1426 } 1427 1428 let stmtLast = block.getStmts()[stmtLen - 1]; 1429 return stmtLast instanceof ArkThrowStmt; 1430 } 1431 1432 public size(): number { 1433 let size = this.trySet.size; 1434 if (this.catchSet) { 1435 size += this.catchSet.size; 1436 } 1437 if (this.finallySet) { 1438 size += this.finallySet.size; 1439 } 1440 return size; 1441 } 1442 1443 public replace(): void {} 1444 1445 public getNodes(): AbstractNode[] { 1446 let nodes = Array.from(this.trySet); 1447 1448 if (this.catchSet) { 1449 nodes.push(...this.catchSet); 1450 } 1451 1452 if (this.finallySet) { 1453 nodes.push(...this.finallySet); 1454 } 1455 return nodes; 1456 } 1457 1458 public getSucc(): AbstractNode[] { 1459 let succ = new Set<AbstractNode>(); 1460 1461 for (const node of this.trySet) { 1462 for (const s of node.getSucc()) { 1463 if (!this.trySet.has(s)) { 1464 succ.add(s); 1465 } 1466 } 1467 } 1468 return Array.from(succ); 1469 } 1470} 1471