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