1 /*
2 * Copyright (c) 2024, 2026, 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 java.util.*;
29 import java.util.stream.Collectors;
30
31 /**
32 * A (basic) block containing an ordered sequence of operations, where the last operation is
33 * a {@link Op.Terminating terminating} operation.
34 * <p>
35 * The terminating operation, according to its specification, may branch to other blocks contained in the
36 * same parent body, by way of its {@link Op#successors() successors}, or exit the parent body and optionally
37 * yield a result.
38 * <p>
39 * Blocks declare zero or more block parameters.
40 * <p>
41 * A block is built using a {@link Block.Builder block builder} that is used to append operations to the block
42 * being built.
43 */
44 public final class Block implements CodeElement<Block, Op> {
45
46 /**
47 * A value that is a block parameter
48 */
49 public static final class Parameter extends Value {
50 Parameter(Block block, TypeElement type) {
51 super(block, type);
52 }
53
54 @Override
55 public String toString() {
56 return "%param@" + Integer.toHexString(hashCode());
57 }
58
59 @Override
60 public SequencedSet<Value> dependsOn() {
61 return Collections.emptyNavigableSet();
62 }
63
64 /**
65 * Returns the invokable operation associated with this block parameter.
66 * <p>
67 * If this block parameter is declared in an entry block and that
68 * block's ancestor operation (the parent of the entry block's parent body)
69 * is an instance of {@link Op.Invokable}, then that instance is returned,
70 * otherwise {@code null} is returned.
71 * <p>
72 * A non-{@code null} result implies this parameter is an invokable parameter.
73 *
74 * @apiNote
75 * This method may be used to pattern match on the returned result:
76 * {@snippet lang = "java":
77 * if (p.invokableOperation() instanceof CoreOp.FuncOp f) {
78 * assert f.parameters().indexOf(p) == p.index(); // @link substring="parameters()" target="Op.Invokable#parameters()"
79 * }
80 *}
81 *
82 * @return the invokable operation, otherwise {@code null} if the operation
83 * is not an instance of {@link Op.Invokable}.
84 * @see Op.Invokable#parameters()
85 */
86 public Op.Invokable invokableOperation() {
87 if (declaringBlock().isEntryBlock() &&
88 declaringBlock().ancestorOp() instanceof Op.Invokable o) {
89 return o;
90 } else {
91 return null;
92 }
93 }
94
95 /**
96 * {@return the index of this block parameter in the parameters of its declaring block.}
97 * @see Value#declaringBlock()
98 * @see Block#parameters()
99 */
100 public int index() {
101 return declaringBlock().parameters().indexOf(this);
102 }
103 }
104
105 /**
106 * A block reference that refers to a block with arguments.
107 * <p>
108 * A terminating operation may refer, via a block reference, to one or more blocks as its successors.
109 * When control is passed from a block to a successor block the values of the block reference's arguments are
110 * assigned, in order, to the successor block's parameters.
111 * <p>
112 * A block reference is considered unbuilt if it's {@link #targetBlock() target block} is unbuilt and
113 * is therefore in accessible. A block reference is considered built when the target block is built
114 * and therefore is accessible.
115 */
116 public static final class Reference implements CodeItem {
117 final Block target;
118 final List<Value> arguments;
119
120 /**
121 * Constructs a block reference for a given target block and arguments.
122 *
123 * @param target the target block.
124 * @param arguments the target block arguments, a copy will be made as needed.
125 */
126 Reference(Block target, List<? extends Value> arguments) {
127 this.target = target;
128 this.arguments = List.copyOf(arguments);
129 }
130
131 /**
132 * {@return the target block.}
133 * @throws IllegalStateException if this block reference is unbuilt because its target block is unbuilt.
134 */
135 public Block targetBlock() {
136 if (!isBuilt()) {
137 throw new IllegalStateException("Target block is unbuilt");
138 }
139
140 return target;
141 }
142
143 /**
144 * {@return the block arguments.}
145 */
146 public List<Value> arguments() {
147 return arguments;
148 }
149
150 boolean isBuilt() {
151 return target.isBuilt();
152 }
153 }
154
155 final Body parentBody;
156
157 final List<Parameter> parameters;
158
159 final List<Op> ops;
160
161 // @@@ In topological order
162 // @@@ Create lazily
163 // Can the representation be more efficient e.g. an array?
164 final SequencedSet<Block> predecessors;
165
166 // Reverse postorder index
167 // Set when block's body has sorted its blocks and therefore set when built
168 // Block is inoperable when < 0 i.e., when not built
169 int index = -1;
170
171 Block(Body parentBody) {
172 this(parentBody, List.of());
173 }
174
175 Block(Body parentBody, List<TypeElement> parameterTypes) {
176 this.parentBody = parentBody;
177 this.parameters = new ArrayList<>();
178 for (TypeElement param : parameterTypes) {
179 parameters.add(new Parameter(this, param));
180 }
181 this.ops = new ArrayList<>();
182 this.predecessors = new LinkedHashSet<>();
183 }
184
185
186 @Override
187 public String toString() {
188 return "^block_" + index + "@" + Integer.toHexString(hashCode());
189 }
190
191 /**
192 * Returns this block's parent body.
193 *
194 * @return this block's parent body.
195 */
196 @Override
197 public Body parent() {
198 return parentBody;
199 }
200
201 @Override
202 public List<Op> children() {
203 return ops();
204 }
205
206 /**
207 * Returns the sequence of operations contained in this block.
208 *
209 * @return returns the sequence operations, as an unmodifiable list.
210 */
211 public List<Op> ops() {
212 return Collections.unmodifiableList(ops);
213 }
214
215 /**
216 * Returns this block's index within the parent body's blocks.
217 * <p>
218 * The following identity holds true:
219 * {@snippet lang = "java" :
220 * this.parentBody().blocks().indexOf(this) == this.index();
221 * }
222 *
223 * @apiNote
224 * The block's index may be used to efficiently track blocks using
225 * bits sets or boolean arrays.
226 *
227 * @return the block index.
228 */
229 public int index() {
230 return index;
231 }
232
233 /**
234 * Returns the block parameters.
235 *
236 * @return the block parameters, as an unmodifiable list.
237 */
238 public List<Parameter> parameters() {
239 return Collections.unmodifiableList(parameters);
240 }
241
242 /**
243 * Returns the block parameter types.
244 *
245 * @return the block parameter types, as am unmodifiable list.
246 */
247 public List<TypeElement> parameterTypes() {
248 return parameters.stream().map(Value::type).toList();
249 }
250
251 /**
252 * Returns the first operation in this block.
253 *
254 * @return the first operation in this block.
255 */
256 public Op firstOp() {
257 return ops.getFirst();
258 }
259
260 /**
261 * Returns the last, terminating, operation in this block.
262 * <p>
263 * The terminating operation implements {@link Op.Terminating}.
264 *
265 * @return the last, terminating, operation in this block.
266 */
267 public Op terminatingOp() {
268 Op lop = ops.getLast();
269 assert lop instanceof Op.Terminating;
270 return lop;
271 }
272
273 /**
274 * Returns the next operation after the given operation, otherwise {@code null}
275 * if this operation is the last operation.
276 *
277 * @param op the operation
278 * @return the next operation after the given operation.
279 * @throws IllegalArgumentException if the operation is not a child of this block
280 */
281 public Op nextOp(Op op) {
282 int i = ops.indexOf(op);
283 if (i == -1) {
284 throw new IllegalArgumentException();
285 }
286 return i < ops().size() - 1 ? ops.get(i + 1) : null;
287 }
288
289 /**
290 * Returns the set of predecessors, the set containing each block in the parent
291 * body that refers to this block as a successor.
292 *
293 * @return the set of predecessors, as an unmodifiable sequenced set. The encounter order is unspecified
294 * and determined by the order in which operations are built.
295 * @apiNote A block may refer to itself as a successor and therefore also be its predecessor.
296 */
297 public SequencedSet<Block> predecessors() {
298 return Collections.unmodifiableSequencedSet(predecessors);
299 }
300
301 /**
302 * Returns the list of predecessor references to this block.
303 * <p>
304 * This method behaves is if it returns the result of the following expression:
305 * {@snippet lang = java:
306 * predecessors.stream().flatMap(p -> successors().stream())
307 * .filter(r -> r.targetBlock() == this)
308 * .toList();
309 *}
310 *
311 * @return the list of predecessor references to this block, as an unmodifiable list.
312 * @apiNote A predecessor block may reference it successor block one or more times.
313 */
314 public List<Block.Reference> predecessorReferences() {
315 return predecessors.stream().flatMap(p -> p.successors().stream())
316 .filter(r -> r.targetBlock() == this)
317 .toList();
318 }
319
320 /**
321 * Returns the list of successors referring to other blocks.
322 * <p>
323 * The successors are declared by the terminating operation contained in this block.
324 *
325 * @return the list of successors, as an unmodifiable list.
326 * @apiNote given a block, A say, whose successor targets a block, B say, we can
327 * state that B is a successor block of A and A is a predecessor block of B.
328 */
329 public List<Reference> successors() {
330 return ops.getLast().successors();
331 }
332
333 /**
334 * Returns the set of target blocks referred to by the successors of this block.
335 * <p>
336 * This method behaves is if it returns the result of the following expression:
337 * {@snippet lang = java:
338 * successors().stream()
339 * .map(Block.Reference::targetBlock)
340 * .collect(Collectors.toCollection(LinkedHashSet::new));
341 *}
342 *
343 * @return the set of target blocks, as an unmodifiable set.
344 */
345 public SequencedSet<Block> successorTargets() {
346 return successors().stream().map(Block.Reference::targetBlock)
347 .collect(Collectors.toCollection(LinkedHashSet::new));
348 }
349
350 /**
351 * Returns true if this block is an entry block, the first block occurring
352 * in the parent body's list of blocks.
353 *
354 * @return true if this block is an entry block.
355 */
356 public boolean isEntryBlock() {
357 return parentBody.entryBlock() == this;
358 }
359
360 /**
361 * Returns {@code true} if this block is
362 * <a href="https://en.wikipedia.org/wiki/Dominator_(graph_theory)">dominated by</a> the given block {@code dom}.
363 * This block is dominated by {@code dom}, if every path from the root entry block to this block passes through
364 * {@code dom}.
365 * <p>
366 * If this block, {@code b} say, and {@code dom} are not in the same parent body,
367 * then {@code b} becomes the nearest ancestor block, result of {@code b.ancestorBlock()},
368 * and so on until either:
369 * {@code b} is {@code null}, therefore {@code b} is <b>not</b> dominated by {@code dom} and this method
370 * returns {@code false}; or
371 * {@code b.parentBody() == dom.parentBody()}, therefore this method returns the result
372 * of {@code b.isDominatedBy(dom)}.
373 * <p>
374 * If this method returns {@code true} then {@code dom.isDominatedBy(this)}
375 * will return {@code false}. However, if this method returns {@code false} then it
376 * does not imply {@code dom.isDominatedBy(this)} returns {@code true}, as neither
377 * block may dominate the other.
378 *
379 * @param dom the dominating block
380 * @return {@code true} if this block is dominated by the given block.
381 */
382 public boolean isDominatedBy(Block dom) {
383 Block b = findBlockForDomBody(this, dom.ancestorBody());
384 if (b == null) {
385 return false;
386 }
387
388 // A block non-strictly dominates itself
389 if (b == dom) {
390 return true;
391 }
392
393 // The entry block in b's body dominates all other blocks in the body
394 Block entry = b.ancestorBody().entryBlock();
395 if (dom == entry) {
396 return true;
397 }
398
399 // Traverse the immediate dominators until dom is reached or the entry block
400 Map<Block, Block> idoms = b.ancestorBody().immediateDominators();
401 Block idom = idoms.get(b);
402 while (idom != entry) {
403 if (idom == dom) {
404 return true;
405 }
406
407 idom = idoms.get(idom);
408 }
409
410 return false;
411 }
412
413 /**
414 * Returns the immediate dominator of this block, otherwise {@code null} if this block is the entry block.
415 * Both this block and the immediate dominator (if defined) have the same parent body.
416 * <p>
417 * The immediate dominator is the unique block that strictly dominates this block, but does not strictly dominate
418 * any other block that strictly dominates this block.
419 *
420 * @return the immediate dominator of this block, otherwise {@code null} if this block is the entry block.
421 */
422 public Block immediateDominator() {
423 if (this == ancestorBody().entryBlock()) {
424 return null;
425 }
426
427 Map<Block, Block> idoms = ancestorBody().immediateDominators();
428 return idoms.get(this);
429 }
430
431 /**
432 * Returns the immediate post dominator of this block, otherwise {@link Body#IPDOM_EXIT} if this block is the
433 * only block with no successors or if this block is one of many blocks that has no successors.
434 * Both this block and the immediate post dominator (if defined) have the same parent body.
435 * <p>
436 * The post immediate dominator is the unique block that strictly post dominates this block, but does not strictly
437 * post dominate any other block that strictly post dominates this block.
438 *
439 * @return the immediate dominator of this block, otherwise {@code null} if this block is the entry block.
440 */
441 public Block immediatePostDominator() {
442 if (this == ancestorBody().entryBlock()) {
443 return null;
444 }
445
446 Map<Block, Block> ipdoms = ancestorBody().immediatePostDominators();
447 Block ipdom = ipdoms.get(this);
448 return ipdom == this ? Body.IPDOM_EXIT : ipdom;
449 }
450
451 // @@@ isPostDominatedBy and immediatePostDominator
452
453 private static Block findBlockForDomBody(Block b, final Body domr) {
454 Body rb = b.ancestorBody();
455 while (domr != rb) {
456 // @@@ What if body is isolated
457
458 b = rb.ancestorBlock();
459 // null when op is top-level (and its body is isolated), or not yet assigned to block
460 if (b == null) {
461 return null;
462 }
463 rb = b.ancestorBody();
464 }
465 return b;
466 }
467
468 /**
469 * A builder of a block.
470 * <p>
471 * A block builder is built when its {@link #parent() parent} body builder is {@link Body.Builder#build(Op) built}.
472 * <p>
473 * If a built builder is operated on to append a block parameter, append an operation, build a sibling block,
474 * then an {@code IllegalStateException} is thrown.
475 */
476 public final class Builder {
477 final Body.Builder parentBody;
478 final CodeContext cc;
479 final CodeTransformer ot;
480
481 Builder(Body.Builder parentBody, CodeContext cc, CodeTransformer ot) {
482 this.parentBody = parentBody;
483 this.cc = cc;
484 this.ot = ot;
485 }
486
487 void check() {
488 parentBody.check();
489 }
490
491 Block target() {
492 return Block.this;
493 }
494
495 /**
496 * {@return the block builder's operation transformer}
497 */
498 public CodeTransformer transformer() {
499 return ot;
500 }
501
502 /**
503 * {@return the block builder's context}
504 */
505 public CodeContext context() {
506 return cc;
507 }
508
509 /**
510 * {@return the parent body builder}
511 */
512 public Body.Builder parentBody() {
513 return parentBody;
514 }
515
516 /**
517 * Returns the entry block builder for parent body.
518 *
519 * <p>The returned block is rebound if necessary to this block builder's
520 * context and transformer.
521 *
522 * @return the entry block builder for parent body builder
523 */
524 public Block.Builder entryBlock() {
525 return parentBody.entryBlock.rebind(cc, ot);
526 }
527
528 /**
529 * {@return true if this block builder is a builder of the entry block}
530 */
531 public boolean isEntryBlock() {
532 return Block.this == parentBody.target().entryBlock();
533 }
534
535 /**
536 * Rebinds this block builder with the given context and operation transformer.
537 *
538 * <p>Either this block builder and the returned block builder may be operated on to build
539 * the same block.
540 * Both are equal to each other, and both are closed when the parent body builder is closed.
541 *
542 * @param cc the context
543 * @param ot the operation transformer
544 * @return the rebound block builder
545 */
546 public Block.Builder rebind(CodeContext cc, CodeTransformer ot) {
547 return this.cc == cc && this.ot == ot
548 ? this
549 : this.target().new Builder(parentBody(), cc, ot);
550 }
551
552 /**
553 * Adds a new block to the parent body.
554 *
555 * @param params the block's parameter types
556 * @return the new block builder
557 */
558 public Block.Builder block(TypeElement... params) {
559 return block(List.of(params));
560 }
561
562 /**
563 * Adds a new block to the parent body.
564 *
565 * @param params the block's parameter types
566 * @return the new block builder
567 */
568 public Block.Builder block(List<TypeElement> params) {
569 return parentBody.block(params, cc, ot);
570 }
571
572 /**
573 * Returns an unmodifiable list of the block's parameters.
574 *
575 * @return the unmodifiable list of the block's parameters
576 */
577 public List<Parameter> parameters() {
578 return Collections.unmodifiableList(parameters);
579 }
580
581 /**
582 * Appends an unbuilt block parameter to the block's parameters.
583 *
584 * @param p the parameter type
585 * @return the appended block parameter
586 */
587 public Parameter parameter(TypeElement p) {
588 check();
589 return appendBlockParameter(p);
590 }
591
592 /**
593 * Creates an unbuilt block reference to this block that can be used as a successor of a terminating operation.
594 *
595 * @param args the unbuilt block arguments
596 * @return the reference to this block
597 * @throws IllegalStateException if this block builder is associated with the entry block.
598 * @throws IllegalArgumentException if a block argument is built because its declaring block is built.
599 */
600 public Reference successor(Value... args) {
601 return successor(List.of(args));
602 }
603
604 /**
605 * Creates an unbuilt block reference to this block that can be used as a successor of a terminating operation.
606 *
607 * @param args the unbuilt block arguments
608 * @return the reference to this block
609 * @throws IllegalStateException if this block builder is associated with the entry block.
610 * @throws IllegalArgumentException if a block argument is built because its declaring block is built.
611 */
612 public Reference successor(List<? extends Value> args) {
613 if (isEntryBlock()) {
614 throw new IllegalStateException("Entry block cannot be referred to as a successor");
615 }
616 for (Value operand : args) {
617 if (operand.isBuilt()) {
618 throw new IllegalArgumentException("Operand's declaring block is built: " + operand);
619 }
620 }
621
622 return new Reference(Block.this, List.copyOf(args));
623 }
624
625 /**
626 * Transforms a body starting from this block builder, using a code transformer.
627 * <p>
628 * This method first rebinds this builder with a child context created from
629 * this builder's context and the given operation transformer, and then
630 * transforms the body using the code transformer by
631 * {@link CodeTransformer#acceptBody(Builder, Body, List) accepting}
632 * the rebound builder, the body, and the values.
633 *
634 * @apiNote
635 * Creation of a child context ensures block and value mappings produced by
636 * the transformation do not affect this builder's context.
637 *
638 * @param body the body to transform
639 * @param values the output values to map to the input parameters of the body's entry block
640 * @param ot the operation transformer
641 * @see CodeTransformer#acceptBody(Builder, Body, List)
642 */
643 public void body(Body body, List<? extends Value> values,
644 CodeTransformer ot) {
645 check();
646
647 ot.acceptBody(rebind(CodeContext.create(cc), ot), body, values);
648 }
649
650 /**
651 * Transforms a body starting from this block builder, using a given operation transformer.
652 * <p>
653 * This method first rebinds this builder with the given context
654 * and the given operation transformer, and then
655 * transforms the body using the operation transformer by
656 * {@link CodeTransformer#acceptBody(Builder, Body, List) accepting}
657 * the rebound builder, the body, and the values.
658 *
659 * @apiNote
660 * The passing of a context can ensure block and value mappings produced by
661 * the transformation do not affect this builder's context.
662 *
663 * @param body the body to transform
664 * @param values the output values to map to the input parameters of the body's entry block
665 * @param cc the code context
666 * @param ot the operation transformer
667 * @see CodeTransformer#acceptBody(Builder, Body, List)
668 */
669 public void body(Body body, List<? extends Value> values,
670 CodeContext cc, CodeTransformer ot) {
671 check();
672
673 ot.acceptBody(rebind(cc, ot), body, values);
674 }
675
676 /**
677 * Appends an operation to this block builder, first transforming the operation if it is built or unbuilt-bound.
678 * <p>
679 * If the operation is unbuilt, then the operation is appended and bound to this block to become unbuilt-bound.
680 * Otherwise, if the operation is built or unbuilt-bound, the operation is first
681 * {@link Op#transform(CodeContext, CodeTransformer) transformed} with this builder's context and
682 * code transformer, the resulting transformed unbuilt operation is appended, and the
683 * operation's result mapped to the transformed operation's unbuilt result, using the builder's context.
684 * <p>
685 * If the unbuilt operation (transformed, or otherwise) is structurally invalid then an
686 * {@code IllegalStateException} is thrown. An unbuilt operation is structurally invalid if:
687 * <ul>
688 * <li>any of its bodies does not have the same ancestor body as this block's parent body.
689 * <li>any of its operands (values) is not reachable from this block.
690 * <li>any of its successors is not a sibling of this block.
691 * <li>any of its successors arguments (values) is not reachable from this block.
692 * <li>it's a terminating operation and a terminating operation is already appended.
693 * </ul>
694 * A value is reachable from this block if there is a path from this block's parent body,
695 * via its ancestor bodies, to the value's declaring block's parent body. (Note this structural check
696 * ensures values are only used from the same code model tree being built, but it is weaker than a
697 * dominance check that can only be performed when the parent body is built.)
698 *
699 * @param op the operation to append
700 * @return the unbuilt operation result of the appended operation
701 * @throws IllegalStateException if the operation is structurally invalid
702 */
703 public Op.Result op(Op op) {
704 check();
705
706 final Op.Result oprToTransform = op.result();
707
708 Op transformedOp = op;
709 if (op.isRoot() || oprToTransform != null) {
710 // If operation is assigned to block, or it's sealed, then copy it and transform its contents
711 transformedOp = op.transform(cc, ot);
712 assert transformedOp.result == null;
713 }
714
715 Op.Result transformedOpr = insertOp(transformedOp);
716
717 if (oprToTransform != null) {
718 // Map the result of the first transformation
719 // @@@ If the same operation is transformed more than once then subsequent
720 // transformed ops will not get implicitly mapped
721 // Should this be an error?
722 cc.mapValueIfAbsent(oprToTransform, transformedOpr);
723 }
724
725 return transformedOpr;
726 }
727
728 /**
729 * Returns true if this block builder is equal to the other object.
730 * <p>This block builder is equal if the other object is an instance of a block builder, and they are
731 * associated with the same block (but perhaps bound to different contexts and transformers).
732 *
733 * @param o the other object
734 * @return true if this builder is equal to the other object.
735 */
736 @Override
737 public boolean equals(Object o) {
738 if (this == o) return true;
739 return o instanceof Builder that && Block.this == that.target();
740 }
741
742 @Override
743 public int hashCode() {
744 return Block.this.hashCode();
745 }
746 }
747
748 // Modifying methods
749
750 // Create block parameter associated with this block
751 private Parameter appendBlockParameter(TypeElement type) {
752 Parameter blockParameter = new Parameter(this, type);
753 parameters.add(blockParameter);
754
755 return blockParameter;
756 }
757
758 // Create an operation, adding to the end of the list of existing operations
759 private Op.Result insertOp(Op op) {
760 Op.Result opResult = new Op.Result(this, op);
761 bindOp(opResult, op);
762
763 ops.add(op);
764 return opResult;
765 }
766
767 private void bindOp(Op.Result opr, Op op) {
768 // Structural checks
769 if (!ops.isEmpty() && ops.getLast() instanceof Op.Terminating) {
770 throw new IllegalStateException("Operation cannot be appended, the block has a terminal operation");
771 }
772
773 for (Body b : op.bodies()) {
774 if (b.ancestorBody != null && b.ancestorBody != this.parentBody) {
775 throw new IllegalStateException("Body of operation is bound to a different ancestor body: ");
776 }
777 }
778
779 for (Value v : op.operands()) {
780 if (!isReachable(v)) {
781 throw new IllegalStateException(
782 String.format("Operand of operation %s is not defined in tree: %s", op, v));
783 }
784 assert !v.isBuilt();
785 }
786
787 for (Reference s : op.successors()) {
788 if (s.target.parentBody != this.parentBody) {
789 throw new IllegalStateException("Target of block reference is not a sibling of this block");
790 }
791
792 for (Value v : s.arguments()) {
793 if (!isReachable(v)) {
794 throw new IllegalStateException(
795 String.format("Argument of block reference %s of terminating operation %s is not defined in tree: %s", s, op, v));
796 }
797 assert !v.isBuilt();
798 }
799 }
800
801 // State updates after structural checks
802 // @@@ The alternative is to close the body builder on failure, rendering it inoperable,
803 // so checks and updates can be merged
804 for (Value v : op.operands()) {
805 v.uses.add(opr);
806 }
807
808 for (Reference s : op.successors()) {
809 for (Value v : s.arguments()) {
810 v.uses.add(opr);
811 }
812
813 s.target.predecessors.add(Block.this);
814 }
815
816 op.result = opr;
817 }
818
819 // Determine if the parent body of value's block is an ancestor of this block
820 private boolean isReachable(Value v) {
821 Body b = parentBody;
822 while (b != null && b != v.block.parentBody) {
823 b = b.ancestorBody;
824 }
825 return b != null;
826 }
827
828 //
829
830 boolean isBuilt() {
831 return index >= 0;
832 }
833 }