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