1 /* 2 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.incubator.code; 27 28 import jdk.incubator.code.dialect.core.CoreType; 29 import jdk.incubator.code.dialect.core.FunctionType; 30 import java.util.*; 31 32 /** 33 * A body containing a sequence of (basic) blocks. 34 * <p> 35 * The sequence of blocks form a graph. The last operation in a block, a terminating operation, 36 * may refer to other blocks in the sequence as successors, thus forming the graph. Otherwise, the last 37 * operation defines how the body passes control flow back to the parent operation, and in doing so may optionally 38 * yield a value. 39 * <p> 40 * The first block in the sequence is the entry block, and no other blocks refer to it as a successor. 41 * <p> 42 * A body has a function type whose return type is the body's yield type and whose parameter types are the entry 43 * block's parameters types, in order. 44 * The function type describes the sequence of input parameters types for arguments that are passed to the 45 * body when control flow is passed to it, and describes the return type of values that are returned when body passes 46 * control back to its parent operation. 47 */ 48 public final class Body implements CodeElement<Body, Block> { 49 // Parent operation 50 // Non-null when body is built, and therefore bound to operation 51 Op parentOp; 52 53 // The ancestor body, when null the body is isolated and cannot refer to values defined outside 54 // When non-null and body is built, ancestorBody == parentOp.result.block.parentBody 55 final Body ancestorBody; 56 57 final TypeElement yieldType; 58 59 // Sorted in reverse postorder 60 final List<Block> blocks; 61 62 // Map of a block to its immediate dominator 63 // Computed lazily, null if not computed 64 Map<Block, Block> idoms; 65 66 /** 67 * Constructs a body, whose ancestor is the given ancestor body. 68 */ 69 Body(Body ancestorBody, TypeElement yieldType) { 70 this.ancestorBody = ancestorBody; 71 this.yieldType = yieldType; 72 this.blocks = new ArrayList<>(); 73 } 74 75 @Override 76 public String toString() { 77 return "body@" + Integer.toHexString(hashCode()); 78 } 79 80 /** 81 * Returns this body's parent operation. 82 * 83 * @return the body's parent operation. 84 */ 85 @Override 86 public Op parent() { 87 return parentOp; 88 } 89 90 /** 91 * Returns this body's parent operation. 92 * 93 * @return the body's parent operation. 94 */ 95 public Op parentOp() { 96 return parentOp; 97 } 98 99 @Override 100 public List<Block> children() { 101 return blocks(); 102 } 103 104 /** 105 * Returns body's blocks in reverse-postorder as an unmodifiable list. 106 * 107 * @return the body's blocks in reverse-postorder. 108 */ 109 public List<Block> blocks() { 110 return Collections.unmodifiableList(blocks); 111 } 112 113 /** 114 * {@return the yield type of this body} 115 */ 116 public TypeElement yieldType() { 117 return yieldType; 118 } 119 120 /** 121 * Returns the body's function type. 122 * <p>The function type is composed of the body's entry block parameter types and 123 * the body's yield type. 124 * 125 * @return the body type. 126 */ 127 public FunctionType bodyType() { 128 Block entryBlock = entryBlock(); 129 return CoreType.functionType(yieldType, entryBlock.parameterTypes()); 130 } 131 132 /** 133 * Finds the block in this body that is the ancestor of the given block. 134 * 135 * @param b the given block. 136 * @return the block in this body that is the ancestor of the given block, 137 * otherwise {@code null} 138 */ 139 public Block findAncestorBlockInBody(Block b) { 140 Objects.requireNonNull(b); 141 142 while (b != null && b.parentBody() != this) { 143 b = b.parentBody().parentOp().parentBlock(); 144 } 145 146 return b; 147 } 148 149 /** 150 * Returns this body's entry block. 151 * <p> 152 * The entry block is the first block in the sequence. No other blocks refer to it as a successor. 153 * 154 * @return the body's entry block 155 */ 156 public Block entryBlock() { 157 return blocks.getFirst(); 158 } 159 160 /** 161 * Returns a map of block to its immediate dominator. 162 * 163 * @return a map of block to its immediate dominator, as an unmodifiable map 164 */ 165 public Map<Block, Block> immediateDominators() { 166 /* 167 * Compute dominators of blocks in a body. 168 * <p> 169 * https://www.cs.rice.edu/~keith/EMBED/dom.pdf 170 * A Simple, Fast Dominance Algorithm 171 * Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy 172 */ 173 174 if (idoms != null) { 175 return idoms; 176 } 177 178 // @@@ Compute the idoms as a block index mapping using int[] 179 // and wrap and a specific map implementation 180 181 Map<Block, Block> doms = new HashMap<>(); 182 doms.put(entryBlock(), entryBlock()); 183 184 // Blocks are sorted in reverse postorder 185 boolean changed; 186 do { 187 changed = false; 188 // Iterate through blocks in reverse postorder, except for entry block 189 for (int i = 1; i < blocks.size(); i++) { 190 Block b = blocks.get(i); 191 192 // Find first processed predecessor of b 193 Block newIdom = null; 194 for (Block p : b.predecessors()) { 195 if (doms.containsKey(p)) { 196 newIdom = p; 197 break; 198 } 199 } 200 assert b.predecessors().isEmpty() || newIdom != null : b; 201 202 // For all other predecessors, p, of b 203 for (Block p : b.predecessors()) { 204 if (p == newIdom) { 205 continue; 206 } 207 208 if (doms.containsKey(p)) { 209 // If already calculated 210 newIdom = intersect(doms, p, newIdom); 211 } 212 } 213 214 if (doms.get(b) != newIdom) { 215 doms.put(b, newIdom); 216 changed = true; 217 } 218 } 219 } while (changed); 220 221 return idoms = Collections.unmodifiableMap(doms); 222 } 223 224 static Block intersect(Map<Block, Block> doms, Block b1, Block b2) { 225 while (b1 != b2) { 226 while (b1.index > b2.index) { 227 b1 = doms.get(b1); 228 } 229 230 while (b2.index > b1.index) { 231 b2 = doms.get(b2); 232 } 233 } 234 235 return b1; 236 } 237 238 /** 239 * Returns the dominance frontier of each block in the body. 240 * <p> 241 * The dominance frontier of block, {@code B} say, is the set of all blocks, {@code C} say, 242 * such that {@code B} dominates a predecessor of {@code C} but does not strictly dominate 243 * {@code C}. 244 * 245 * @return the dominance frontier of each block in the body, as a modifiable map 246 */ 247 public Map<Block, Set<Block>> dominanceFrontier() { 248 // @@@ cache result? 249 Map<Block, Block> idoms = immediateDominators(); 250 Map<Block, Set<Block>> df = new HashMap<>(); 251 252 for (Block b : blocks) { 253 Set<Block> preds = b.predecessors(); 254 255 if (preds.size() > 1) { 256 for (Block p : preds) { 257 Block runner = p; 258 while (runner != idoms.get(b)) { 259 df.computeIfAbsent(runner, _ -> new LinkedHashSet<>()).add(b); 260 runner = idoms.get(runner); 261 } 262 } 263 } 264 } 265 266 return df; 267 } 268 269 /** 270 * A synthetic block representing the post dominator of all blocks 271 * when two or more blocks have no successors. 272 * <p> 273 * Computing the immediate post dominators requires a single exit point, 274 * one block that has no successors. When there are two or more blocks 275 * with no successors then this block represents the immediate post 276 * dominator of those blocks 277 */ 278 public static final Block IPDOM_EXIT; 279 static { 280 IPDOM_EXIT = new Block(null); 281 IPDOM_EXIT.index = Integer.MAX_VALUE; 282 } 283 284 /** 285 * Returns a map of block to its immediate post dominator. 286 * <p> 287 * If there are two or more blocks with no successors then 288 * a single exit point is synthesized using the {@link #IPDOM_EXIT} 289 * block, which represents the immediate post dominator of those blocks. 290 * 291 * @return a map of block to its immediate post dominator, as an unmodifiable map 292 */ 293 public Map<Block, Block> immediatePostDominators() { 294 Map<Block, Block> pdoms = new HashMap<>(); 295 296 // If there are multiple exit blocks (those with zero successors) 297 // then use the block IPDOM_EXIT that is the synthetic successor of 298 // the exit blocks 299 boolean nSuccessors = blocks.stream().filter(b -> b.successors().isEmpty()).count() > 1; 300 301 if (nSuccessors) { 302 pdoms.put(IPDOM_EXIT, IPDOM_EXIT); 303 } else { 304 Block exit = blocks.getLast(); 305 assert blocks.stream().filter(b -> b.successors().isEmpty()).findFirst().orElseThrow() == exit; 306 pdoms.put(exit, exit); 307 } 308 309 // Blocks are sorted in reverse postorder 310 boolean changed; 311 do { 312 changed = false; 313 // Iterate in reverse through blocks in reverse postorder, except for exit block 314 for (int i = blocks.size() - (nSuccessors ? 1 : 2); i >= 0; i--) { 315 Block b = blocks.get(i); 316 317 // Find first processed successor of b 318 Block newIpdom = null; 319 Collection<Block> targets = b.successorTargets(); 320 for (Block s : nSuccessors && targets.isEmpty() ? List.of(IPDOM_EXIT) : targets) { 321 if (pdoms.containsKey(s)) { 322 newIpdom = s; 323 break; 324 } 325 } 326 327 if (newIpdom == null) { 328 // newIpdom can be null if all successors reference 329 // prior blocks (back branch) yet to be encountered 330 // in the dominator treee 331 continue; 332 } 333 334 // For all other successors, s, of b 335 for (Block s : b.successorTargets()) { 336 if (s == newIpdom) { 337 continue; 338 } 339 340 if (pdoms.containsKey(s)) { 341 // If already calculated 342 newIpdom = postIntersect(pdoms, s, newIpdom, blocks.size()); 343 } 344 } 345 346 if (pdoms.get(b) != newIpdom) { 347 pdoms.put(b, newIpdom); 348 changed = true; 349 } 350 } 351 } while (changed); 352 353 return Collections.unmodifiableMap(pdoms); 354 } 355 356 static Block postIntersect(Map<Block, Block> doms, Block b1, Block b2, int exitIndex) { 357 while (b1 != b2) { 358 while (b1.index() < b2.index()) { 359 b1 = doms.get(b1); 360 } 361 362 while (b2.index() < b1.index()) { 363 b2 = doms.get(b2); 364 } 365 } 366 367 return b1; 368 } 369 370 /** 371 * Returns the post dominance frontier of each block in the body. 372 * <p> 373 * The post dominance frontier of block, {@code B} say, is the set of all blocks, {@code C} say, 374 * such that {@code B} post dominates a successor of {@code C} but does not strictly post dominate 375 * {@code C}. 376 * 377 * @return the post dominance frontier of each block in the body, as a modifiable map 378 */ 379 public Map<Block, Set<Block>> postDominanceFrontier() { 380 // @@@ cache result? 381 Map<Block, Block> idoms = immediatePostDominators(); 382 Map<Block, Set<Block>> df = new HashMap<>(); 383 384 for (Block b : blocks) { 385 Set<Block> succs = b.successorTargets(); 386 387 if (succs.size() > 1) { 388 for (Block s : succs) { 389 Block runner = s; 390 while (runner != idoms.get(b)) { 391 df.computeIfAbsent(runner, _ -> new LinkedHashSet<>()).add(b); 392 runner = idoms.get(runner); 393 } 394 } 395 } 396 } 397 398 return df; 399 } 400 401 /** 402 * Returns {@code true} if this body is dominated by the given body {@code dom}. 403 * <p> 404 * A body, {@code b} say, is dominated by {@code dom} if {@code b} is the same as {@code dom} or a descendant of 405 * {@code dom}. Specifically, if {@code b} and {@code dom} are not equal then {@code b} becomes the nearest ancestor 406 * body, the result of {@code b.parentOp().parentBlock().parentBody()}, and so on until either: 407 * {@code b == dom}, therefore {@code b} is dominated by {@code dom} and this method returns {@code true}; 408 * or {@code b.parentOp().parentBlock() == null}, therefore {@code b} is <b>not</b> dominated 409 * by {@code dom} and this method returns {@code false}. 410 * 411 * @param dom the dominating body 412 * @return {@code true} if this body is dominated by the given body {@code dom}. 413 */ 414 public boolean isDominatedBy(Body dom) { 415 return isDominatedBy(this, dom); 416 } 417 418 static boolean isDominatedBy(Body r, Body dom) { 419 while (r != dom) { 420 Block eb = r.parentOp().parentBlock(); 421 if (eb == null) { 422 return false; 423 } 424 425 r = eb.parentBody(); 426 } 427 428 return true; 429 } 430 431 /** 432 * Computes values captured by this body. A captured value is a value that dominates 433 * this body and is used by a descendant operation of this body. 434 * <p> 435 * The order of the captured values is first use encountered in depth 436 * first search of this body's descendant operations. 437 * 438 * @return the list of captured values, modifiable 439 */ 440 public List<Value> capturedValues() { 441 Set<Value> cvs = new LinkedHashSet<>(); 442 443 capturedValues(cvs, new ArrayDeque<>(), this); 444 return new ArrayList<>(cvs); 445 } 446 447 static void capturedValues(Set<Value> capturedValues, Deque<Body> bodyStack, Body body) { 448 bodyStack.push(body); 449 450 for (Block b : body.blocks()) { 451 for (Op op : b.ops()) { 452 for (Body childBody : op.bodies()) { 453 capturedValues(capturedValues, bodyStack, childBody); 454 } 455 456 for (Value a : op.operands()) { 457 if (!bodyStack.contains(a.declaringBlock().parentBody())) { 458 capturedValues.add(a); 459 } 460 } 461 462 for (Block.Reference s : op.successors()) { 463 for (Value a : s.arguments()) { 464 if (!bodyStack.contains(a.declaringBlock().parentBody())) { 465 capturedValues.add(a); 466 } 467 } 468 } 469 } 470 } 471 472 bodyStack.pop(); 473 } 474 475 /** 476 * A builder of a body. 477 * <p> 478 * When the body builder is built any associated block builders are also considered built. 479 */ 480 public final class Builder { 481 /** 482 * Creates a body build with a new context, and a copying transformer. 483 * 484 * @param ancestorBody the nearest ancestor body builder 485 * @param bodyType the body's function type 486 * @return the body builder 487 * @throws IllegalStateException if the ancestor body builder is built 488 * @see #of(Builder, FunctionType, CopyContext, OpTransformer) 489 */ 490 public static Builder of(Builder ancestorBody, FunctionType bodyType) { 491 // @@@ Creation of CopyContext 492 return of(ancestorBody, bodyType, CopyContext.create(), OpTransformer.COPYING_TRANSFORMER); 493 } 494 495 /** 496 * Creates a body build with a copying transformer. 497 * 498 * @param ancestorBody the nearest ancestor body builder 499 * @param bodyType the body's function type 500 * @param cc the context 501 * @return the body builder 502 * @throws IllegalStateException if the ancestor body builder is built 503 * @see #of(Builder, FunctionType, CopyContext, OpTransformer) 504 */ 505 public static Builder of(Builder ancestorBody, FunctionType bodyType, CopyContext cc) { 506 return of(ancestorBody, bodyType, cc, OpTransformer.COPYING_TRANSFORMER); 507 } 508 509 /** 510 * Creates a body builder. 511 * <p> 512 * Structurally, the created body builder must be built before its ancestor body builder (if non-null) is built, 513 * otherwise an {@code IllegalStateException} will occur. 514 * <p> 515 * A function type, referred to as the body type, defines the body's yield type and the initial sequence of 516 * entry block parameters. 517 * The body's yield type is the function type's return type. 518 * An entry block builder is created with appended block parameters corresponding, in order, to 519 * the function type's parameter types. 520 * <p> 521 * If the ancestor body is null then the created body builder is isolated and descendant operations may only 522 * refer to values declared within the created body builder. Otherwise, operations 523 * may refer to values declared in the ancestor body builders (outside the created body builder). 524 * 525 * @param ancestorBody the nearest ancestor body builder, may be null if isolated 526 * @param bodyType the body's function type 527 * @param cc the context 528 * @param ot the transformer 529 * @return the body builder 530 * @throws IllegalStateException if the ancestor body builder is built 531 * @see #of(Builder, FunctionType, CopyContext, OpTransformer) 532 */ 533 public static Builder of(Builder ancestorBody, FunctionType bodyType, 534 CopyContext cc, OpTransformer ot) { 535 Body body = new Body(ancestorBody != null ? ancestorBody.target() : null, bodyType.returnType()); 536 return body.new Builder(ancestorBody, bodyType, cc, ot); 537 } 538 539 // The ancestor body, may be null 540 final Builder ancestorBody; 541 542 // The entry block of this body, whose parameters are given by the body's function type 543 final Block.Builder entryBlock; 544 545 // When non-null contains one or more great-grandchildren 546 List<Builder> greatgrandchildren; 547 548 // True when built 549 boolean closed; 550 551 Builder(Builder ancestorBody, FunctionType bodyType, 552 CopyContext cc, OpTransformer ot) { 553 // Structural check 554 // The ancestor body should not be built before this body is created 555 if (ancestorBody != null) { 556 ancestorBody.check(); 557 ancestorBody.addGreatgrandchild(this); 558 } 559 560 this.ancestorBody = ancestorBody; 561 // Create entry block from the body's function type 562 Block eb = Body.this.createBlock(bodyType.parameterTypes()); 563 this.entryBlock = eb.new Builder(this, cc, ot); 564 } 565 566 void addGreatgrandchild(Builder greatgrandchild) { 567 var l = greatgrandchildren == null 568 ? (greatgrandchildren = new ArrayList<>()) : greatgrandchildren; 569 l.add(greatgrandchild); 570 } 571 572 /** 573 * Builds the body and its blocks, associating the body with a parent operation. 574 * <p> 575 * Structurally, any descendant body builders must be built before this body builder is built, 576 * otherwise an {@code IllegalStateException} will occur. 577 * <p> 578 * Blocks are sorted in reserve postorder. 579 * <p> 580 * Any unreferenced empty blocks are removed. An unreferenced block is a non-entry block with no predecessors. 581 * 582 * @param op the parent operation 583 * @return the build body 584 * @throws IllegalStateException if this body builder is built 585 * @throws IllegalStateException if any descendant body builders are not built 586 * @throws IllegalStateException if a block has no terminal operation, unless unreferenced and empty 587 */ 588 // @@@ Validation 589 // e.g., every operand dominates the operation result (potentially expensive) 590 public Body build(Op op) { 591 // Structural check 592 // This body should not be closed 593 check(); 594 closed = true; 595 596 // Structural check 597 // All great-grandchildren bodies should be built 598 if (greatgrandchildren != null) { 599 for (Builder greatgrandchild : greatgrandchildren) { 600 if (!greatgrandchild.closed) { 601 throw new IllegalStateException("Descendant body builder is not built"); 602 } 603 } 604 } 605 606 Iterator<Block> i = blocks.iterator(); 607 while (i.hasNext()) { 608 Block block = i.next(); 609 610 // Structural check 611 // All referenced blocks should have a terminating operation as the last operation 612 if (block.ops.isEmpty()) { 613 if (block.isEntryBlock() || !block.predecessors.isEmpty()) { 614 throw noTerminatingOperation(); 615 } 616 617 // Remove unreferenced empty block 618 assert !block.isEntryBlock() && block.predecessors.isEmpty(); 619 i.remove(); 620 } else if (!(block.ops.getLast() instanceof Op.Terminating)) { 621 throw noTerminatingOperation(); 622 } 623 } 624 625 sortReversePostorder(); 626 627 Body.this.parentOp = op; 628 return Body.this; 629 } 630 631 static IllegalStateException noTerminatingOperation() { 632 return new IllegalStateException("Block has no terminating operation as the last operation"); 633 } 634 635 /** 636 * Returns the body builder's function type. 637 * <p> 638 * The function type is composed of the body builder's yield type, as the function type's return type, and the 639 * currently built entry block's parameter types, in order, as the function type's parameter types. 640 * 641 * @return the body builder's function type 642 */ 643 public FunctionType bodyType() { 644 TypeElement returnType = Body.this.yieldType(); 645 Block eb = Body.this.entryBlock(); 646 return CoreType.functionType(returnType, eb.parameterTypes()); 647 } 648 649 /** 650 * {@return the body builder's nearest ancestor body builder} 651 */ 652 public Builder ancestorBody() { 653 return ancestorBody; 654 } 655 656 /** 657 * {@return the body's entry block builder} 658 */ 659 public Block.Builder entryBlock() { 660 return entryBlock; 661 } 662 663 @Override 664 public boolean equals(Object o) { 665 if (this == o) return true; 666 return o instanceof Builder that && Body.this == that.target(); 667 } 668 669 @Override 670 public int hashCode() { 671 return Body.this.hashCode(); 672 } 673 674 void check() { 675 if (closed) { 676 throw new IllegalStateException("Builder is closed"); 677 } 678 } 679 680 Body target() { 681 return Body.this; 682 } 683 684 // Build new block in body 685 Block.Builder block(List<TypeElement> params, CopyContext cc, OpTransformer ot) { 686 check(); 687 Block block = Body.this.createBlock(params); 688 689 return block.new Builder(this, cc, ot); 690 } 691 } 692 693 /** 694 * Copies the contents of this body. 695 * 696 * @param cc the copy context 697 * @return the builder of a body containing the copied body 698 * @see #transform(CopyContext, OpTransformer) 699 */ 700 public Builder copy(CopyContext cc) { 701 return transform(cc, OpTransformer.COPYING_TRANSFORMER); 702 } 703 704 /** 705 * Transforms this body. 706 * <p> 707 * A new body builder is created with the same function type as this body. 708 * Then, this body is {@link Block.Builder#transformBody(Body, java.util.List, CopyContext, OpTransformer) transformed} 709 * into the body builder's entry block builder with the given copy context, operation transformer, and values 710 * that are the entry block's parameters. 711 * 712 * @param cc the copy context 713 * @param ot the operation transformer 714 * @return a body builder containing the transformed body 715 */ 716 public Builder transform(CopyContext cc, OpTransformer ot) { 717 Block.Builder ancestorBlockBuilder = ancestorBody != null 718 ? cc.getBlock(ancestorBody.entryBlock()) : null; 719 Builder ancestorBodyBuilder = ancestorBlockBuilder != null 720 ? ancestorBlockBuilder.parentBody() : null; 721 Builder body = Builder.of(ancestorBodyBuilder, 722 // Create function type with just the return type and add parameters afterward 723 CoreType.functionType(yieldType), 724 cc, ot); 725 726 for (Block.Parameter p : entryBlock().parameters()) { 727 body.entryBlock.parameter(p.type()); 728 } 729 730 body.entryBlock.transformBody(this, body.entryBlock.parameters(), cc, ot); 731 return body; 732 } 733 734 // Sort blocks in reverse post order 735 // After sorting the following holds for a block 736 // block.parentBody().blocks().indexOf(block) == block.index() 737 private void sortReversePostorder() { 738 if (blocks.size() < 2) { 739 for (int i = 0; i < blocks.size(); i++) { 740 blocks.get(i).index = i; 741 } 742 return; 743 } 744 745 // Reset block indexes 746 // Also ensuring blocks with no predecessors occur last 747 for (Block b : blocks) { 748 b.index = Integer.MAX_VALUE; 749 } 750 751 Deque<Block> stack = new ArrayDeque<>(); 752 stack.push(blocks.get(0)); 753 754 // Postorder iteration 755 int index = blocks.size(); 756 while (!stack.isEmpty()) { 757 Block n = stack.peek(); 758 if (n.index == Integer.MIN_VALUE) { 759 // If n's successor has been processed then add n 760 stack.pop(); 761 n.index = --index; 762 } else if (n.index < Integer.MAX_VALUE) { 763 // If n has already been processed then ignore 764 stack.pop(); 765 } else { 766 // Mark before processing successors, a successor may refer back to n 767 n.index = Integer.MIN_VALUE; 768 for (Block.Reference s : n.successors()) { 769 if (s.target.index < Integer.MAX_VALUE) { 770 continue; 771 } 772 773 stack.push(s.target); 774 } 775 } 776 } 777 778 blocks.sort(Comparator.comparingInt(b -> b.index)); 779 if (blocks.get(0).index > 0) { 780 // There are blocks with no predecessors 781 // Reassign indexes to their natural indexes, sort order is preserved 782 for (int i = 0; i < blocks.size(); i++) { 783 blocks.get(i).index = i; 784 } 785 } 786 } 787 788 // Modifying methods 789 790 // Create block 791 private Block createBlock(List<TypeElement> params) { 792 Block b = new Block(this, params); 793 blocks.add(b); 794 return b; 795 } 796 }