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 }