1 /*
   2  * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.incubator.code.dialect.core;
  27 
  28 import jdk.incubator.code.*;
  29 import jdk.incubator.code.dialect.java.*;
  30 import jdk.incubator.code.extern.ExternalizedOp;
  31 import jdk.incubator.code.extern.OpFactory;
  32 import jdk.incubator.code.internal.OpDeclaration;
  33 
  34 import java.lang.invoke.MethodHandles;
  35 import java.lang.reflect.Method;
  36 import java.util.*;
  37 import java.util.function.Consumer;
  38 import java.util.function.Function;
  39 
  40 /**
  41  * The top-level operation class for the set of enclosed core operations.
  42  * <p>
  43  * A code model, produced by the Java compiler from Java program source, may consist of core operations and Java
  44  * operations. Such a model represents the same Java program and preserves the program meaning as defined by the
  45  * Java Language Specification
  46  */
  47 public sealed abstract class CoreOp extends Op {
  48 
  49     CoreOp(Op that, CodeContext cc) {
  50         super(that, cc);
  51     }
  52 
  53     CoreOp(List<? extends Value> operands) {
  54         super(operands);
  55     }
  56 
  57     @Override
  58     public String externalizeOpName() {
  59         OpDeclaration opDecl = this.getClass().getDeclaredAnnotation(OpDeclaration.class);
  60         assert opDecl != null : this.getClass().getName();
  61         return opDecl.value();
  62     }
  63 
  64     /**
  65      * The function operation, that can model a Java method declaration.
  66      */
  67     @OpDeclaration(FuncOp.NAME)
  68     public static final class FuncOp extends CoreOp
  69             implements Op.Invokable, Op.Isolated, Op.Lowerable {
  70 
  71         /**
  72          * Builder for function operations.
  73          */
  74         public static class Builder {
  75             final Body.Builder ancestorBody;
  76             final String funcName;
  77             final FunctionType funcType;
  78 
  79             Builder(Body.Builder ancestorBody, String funcName, FunctionType funcType) {
  80                 this.ancestorBody = ancestorBody;
  81                 this.funcName = funcName;
  82                 this.funcType = funcType;
  83             }
  84 
  85             /**
  86              * Builds a function operation using a block builder.
  87              *
  88              * @param c consumer that populates the function body using a block builder
  89              * @return the function operation
  90              */
  91             public FuncOp body(Consumer<Block.Builder> c) {
  92                 Body.Builder body = Body.Builder.of(ancestorBody, funcType);
  93                 c.accept(body.entryBlock());
  94                 return new FuncOp(funcName, body);
  95             }
  96         }
  97 
  98         static final String NAME = "func";
  99 
 100         /**
 101          * The externalized attribute modelling the function name
 102          */
 103         public static final String ATTRIBUTE_FUNC_NAME = NAME + ".name";
 104 
 105         final String funcName;
 106         final Body body;
 107 
 108         FuncOp(ExternalizedOp def) {
 109             if (!def.operands().isEmpty()) {
 110                 throw new IllegalStateException("Bad op " + def.name());
 111             }
 112 
 113             String funcName = def.extractAttributeValue(ATTRIBUTE_FUNC_NAME, true,
 114                     v -> switch (v) {
 115                         case String s -> s;
 116                         case null, default -> throw new UnsupportedOperationException("Unsupported func name value:" + v);
 117                     });
 118 
 119             this(funcName, def.bodyDefinitions().get(0));
 120         }
 121 
 122         FuncOp(FuncOp that, CodeContext cc, CodeTransformer ot) {
 123             super(that, cc);
 124 
 125             this.funcName = that.funcName;
 126             this.body = that.body.transform(cc, ot).build(this);
 127         }
 128 
 129         FuncOp(FuncOp that, String funcName, CodeContext cc, CodeTransformer ot) {
 130             super(that, cc);
 131 
 132             this.funcName = funcName;
 133             this.body = that.body.transform(cc, ot).build(this);
 134         }
 135 
 136         @Override
 137         public FuncOp transform(CodeContext cc, CodeTransformer ot) {
 138             return new FuncOp(this, cc, ot);
 139         }
 140 
 141         /**
 142          * Transforms a function operation using the given code transformer and a new context.
 143          *
 144          * @param ot code transformer to apply to the function operation
 145          * @return the transformed function operation
 146          */
 147         public FuncOp transform(CodeTransformer ot) {
 148             return new FuncOp(this, CodeContext.create(), ot);
 149         }
 150 
 151         /**
 152          * Transforms a function operation using the given function name, code transformer,
 153          * and a new context.
 154          *
 155          * @param funcName the new function name
 156          * @param ot code transformer to apply to the function operation
 157          * @return the transformed function operation
 158          */
 159         public FuncOp transform(String funcName, CodeTransformer ot) {
 160             return new FuncOp(this, funcName, CodeContext.create(), ot);
 161         }
 162 
 163         FuncOp(String funcName, Body.Builder bodyBuilder) {
 164             super(List.of());
 165 
 166             this.funcName = funcName;
 167             this.body = bodyBuilder.build(this);
 168         }
 169 
 170         @Override
 171         public List<Body> bodies() {
 172             return List.of(body);
 173         }
 174 
 175         @Override
 176         public Map<String, Object> externalize() {
 177             return Map.of("", funcName);
 178         }
 179 
 180         @Override
 181         public FunctionType invokableType() {
 182             return body.bodyType();
 183         }
 184 
 185         /**
 186          * {@return the function name}
 187          */
 188         public String funcName() {
 189             return funcName;
 190         }
 191 
 192         @Override
 193         public Body body() {
 194             return body;
 195         }
 196 
 197         @Override
 198         public Block.Builder lower(Block.Builder b, CodeTransformer _ignore) {
 199             // Isolate body with respect to ancestor transformations
 200             b.rebind(b.context(), CodeTransformer.LOWERING_TRANSFORMER).op(this);
 201             return b;
 202         }
 203 
 204         @Override
 205         public TypeElement resultType() {
 206             return JavaType.VOID;
 207         }
 208     }
 209 
 210     /**
 211      * The function call operation, that models a call to a function, by name, declared in the module op that is also an
 212      * ancestor of this operation.
 213      */
 214     // @@@ stack effects equivalent to the call operation as if the function were a Java method?
 215     @OpDeclaration(FuncCallOp.NAME)
 216     public static final class FuncCallOp extends CoreOp {
 217         static final String NAME = "func.call";
 218 
 219         /**
 220          * The externalized attribute modelling the name of the invoked function
 221          */
 222         public static final String ATTRIBUTE_FUNC_NAME = NAME + ".name";
 223 
 224         final String funcName;
 225         final TypeElement resultType;
 226 
 227         FuncCallOp(ExternalizedOp def) {
 228             String funcName = def.extractAttributeValue(ATTRIBUTE_FUNC_NAME, true,
 229                     v -> switch (v) {
 230                         case String s -> s;
 231                         case null, default -> throw new UnsupportedOperationException("Unsupported func name value:" + v);
 232                     });
 233 
 234             this(funcName, def.resultType(), def.operands());
 235         }
 236 
 237         FuncCallOp(FuncCallOp that, CodeContext cc) {
 238             super(that, cc);
 239 
 240             this.funcName = that.funcName;
 241             this.resultType = that.resultType;
 242         }
 243 
 244         @Override
 245         public FuncCallOp transform(CodeContext cc, CodeTransformer ot) {
 246             return new FuncCallOp(this, cc);
 247         }
 248 
 249         FuncCallOp(String funcName, TypeElement resultType, List<Value> args) {
 250             super(args);
 251 
 252             this.funcName = funcName;
 253             this.resultType = resultType;
 254         }
 255 
 256         @Override
 257         public Map<String, Object> externalize() {
 258             return Map.of("", funcName);
 259         }
 260 
 261         /**
 262          * {@return the function name}
 263          */
 264         public String funcName() {
 265             return funcName;
 266         }
 267 
 268         @Override
 269         public TypeElement resultType() {
 270             return resultType;
 271         }
 272     }
 273 
 274     /**
 275      * The module operation, modeling a collection of functions,
 276      * and creating a symbol table of function name to function
 277      */
 278     @OpDeclaration(ModuleOp.NAME)
 279     public static final class ModuleOp extends CoreOp
 280             implements Op.Isolated, Op.Lowerable {
 281 
 282         static final String NAME = "module";
 283 
 284         final SequencedMap<String, FuncOp> table;
 285         final Body body;
 286 
 287         ModuleOp(ExternalizedOp def) {
 288             if (!def.operands().isEmpty()) {
 289                 throw new IllegalStateException("Bad op " + def.name());
 290             }
 291 
 292             this(def.bodyDefinitions().get(0));
 293         }
 294 
 295         ModuleOp(ModuleOp that, CodeContext cc, CodeTransformer ot) {
 296             super(that, cc);
 297 
 298             this.body = that.body.transform(cc, ot).build(this);
 299             this.table = createTable(body);
 300         }
 301 
 302         static SequencedMap<String, FuncOp> createTable(Body body) {
 303             SequencedMap<String, FuncOp> table = new LinkedHashMap<>();
 304             for (var op : body.entryBlock().ops()) {
 305                 if (op instanceof FuncOp fop) {
 306                     table.put(fop.funcName(), fop);
 307                 } else if (!(op instanceof Op.Terminating)) {
 308                     throw new IllegalArgumentException("Bad operation in module: " + op);
 309                 }
 310             }
 311             return Collections.unmodifiableSequencedMap(table);
 312         }
 313 
 314         @Override
 315         public ModuleOp transform(CodeContext cc, CodeTransformer ot) {
 316             return new ModuleOp(this, cc, ot);
 317         }
 318 
 319         /**
 320          * Transforms a module operation using the given code transformer and a new context.
 321          *
 322          * @param ot code transformer to apply to the module operation
 323          * @return the transformed module operation
 324          */
 325         public ModuleOp transform(CodeTransformer ot) {
 326             return new ModuleOp(this, CodeContext.create(), ot);
 327         }
 328 
 329         ModuleOp(Body.Builder bodyBuilder) {
 330             super(List.of());
 331 
 332             this.body = bodyBuilder.build(this);
 333             this.table = createTable(body);
 334         }
 335 
 336         ModuleOp(List<FuncOp> functions) {
 337             Body.Builder bodyC = Body.Builder.of(null, CoreType.FUNCTION_TYPE_VOID);
 338             Block.Builder entryBlock = bodyC.entryBlock();
 339             for (FuncOp f : functions) {
 340                 entryBlock.op(f);
 341             }
 342             entryBlock.op(CoreOp.unreachable());
 343 
 344             this(bodyC);
 345         }
 346 
 347         @Override
 348         public List<Body> bodies() {
 349             return List.of(body);
 350         }
 351 
 352         /**
 353          * {@return a symbol table of function name to function}
 354          */
 355         public SequencedMap<String, FuncOp> functionTable() {
 356             return table;
 357         }
 358 
 359         @Override
 360         public TypeElement resultType() {
 361             return JavaType.VOID;
 362         }
 363 
 364         @Override
 365         public Block.Builder lower(Block.Builder b, CodeTransformer _ignore) {
 366             b.rebind(b.context(), CodeTransformer.LOWERING_TRANSFORMER).op(this);
 367             return b;
 368         }
 369 
 370         static CoreOp.FuncOp invokeToFuncOp(JavaOp.InvokeOp invokeOp, MethodHandles.Lookup l) {
 371             try {
 372                 Method method = invokeOp.invokeDescriptor().resolveToMethod(l);
 373                 return Op.ofMethod(method).orElse(null);
 374             } catch (ReflectiveOperationException e) {
 375                 throw new IllegalStateException("Could not resolve invokeOp to method");
 376             }
 377         }
 378 
 379         /**
 380          * Creates a module operation using a root function operation, collecting all reachable function operations.
 381          * The symbol table of the returned module operation lists function operations in the order encountered within the
 382          * root function operation, preceded by the function obtained from the root function itself.
 383          *
 384          * @param root the root function operation
 385          * @param l the lookup used to resolve method references nested in the root function
 386          * @return a module operation containing the root and reachable functions
 387          */
 388         public static CoreOp.ModuleOp ofFuncOp(CoreOp.FuncOp root, MethodHandles.Lookup l) {
 389             SequencedSet<FuncOp> visited = new LinkedHashSet<>();
 390             Map<FuncOp, String> funcNames = new HashMap<>(); // holds the original funcOps and their new names
 391             Deque<CoreOp.FuncOp> stack = new LinkedList<>(); // holds worklist of og funcOps to process
 392             SequencedSet<FuncOp> transformed = new LinkedHashSet<>();
 393 
 394             stack.push(root);
 395             funcNames.put(root, root.funcName() + "_" + funcNames.size());
 396             while (!stack.isEmpty()) {
 397                 CoreOp.FuncOp cur = stack.pop();
 398 
 399                 if (!visited.add(cur)) {
 400                     continue;
 401                 }
 402 
 403                 List<CoreOp.FuncOp> calledFuncs = new ArrayList<>();
 404                 // traversing to convert invokeOps -> funcCallOps and gathering invokeOps to be processed later
 405                 transformed.add(cur.transform(funcNames.get(cur), (blockBuilder, op) -> {
 406                     if (op instanceof JavaOp.InvokeOp iop) {
 407                         Method invokeOpCalledMethod = null;
 408                         try {
 409                             invokeOpCalledMethod = iop.invokeDescriptor().resolveToMethod(l);
 410                         } catch (ReflectiveOperationException e) {
 411                             throw new RuntimeException("Could not resolve invokeOp to method");
 412                         }
 413                         if (invokeOpCalledMethod instanceof Method m &&
 414                                 Op.ofMethod(m).orElse(null) instanceof CoreOp.FuncOp calledFunc) {
 415                             calledFuncs.add(calledFunc);
 416                             funcNames.computeIfAbsent(calledFunc,
 417                                     f -> f.funcName() + "_" + funcNames.size());
 418                             Op.Result result = blockBuilder.op(CoreOp.funcCall(
 419                                     funcNames.get(calledFunc),
 420                                     calledFunc.invokableType(),
 421                                     blockBuilder.context().getValues(iop.operands())));
 422                             blockBuilder.context().mapValue(op.result(), result);
 423                             return blockBuilder;
 424                         }
 425                     }
 426                     blockBuilder.op(op);
 427                     return blockBuilder;
 428                 }));
 429 
 430                 for (FuncOp f : calledFuncs.reversed()) {
 431                     if (!stack.contains(f)) stack.push(f);
 432                 }
 433             }
 434             return CoreOp.module(transformed.stream().toList());
 435         }
 436 
 437         /**
 438          * Creates a module operation using a lambda operation, method handles lookup, and a name for the root lambda.
 439          * The symbol table of the returned module operation lists function operations in the order encountered within the
 440          * root lambda operation, preceded by the function obtained from the provided lambda operation.
 441          *
 442          * @param lambdaOp the lambda operation
 443          * @param l the lookup used to resolve method references nested in the lambda operation
 444          * @param lambdaName the name to use for the root function (or {@code null})
 445          * @return a module operation containing a root function (obtained from {@code lambdaOp}) and reachable functions
 446          */
 447         public static CoreOp.ModuleOp ofLambdaOp(JavaOp.LambdaOp lambdaOp, MethodHandles.Lookup l, String lambdaName) {
 448             if (lambdaName == null) lambdaName = "";
 449             CoreOp.FuncOp funcOp = lambdaOp.directInvocation().isPresent() ?
 450                     invokeToFuncOp(lambdaOp.directInvocation().get(), l) :
 451                     lambdaOp.toFuncOp(lambdaName);
 452             return ofFuncOp(funcOp, l);
 453         }
 454     }
 455 
 456     /**
 457      * The quoted operation, that models the quoting of an operation.
 458      */
 459     @OpDeclaration(QuotedOp.NAME)
 460     public static final class QuotedOp extends CoreOp
 461             implements Op.Nested, Op.Lowerable, Op.Pure {
 462         static final String NAME = "quoted";
 463 
 464         /**
 465          * The java type element modelling the parameterized type {@code Quoted<Op>}
 466          * that is the result type of a QuotedOp instance.
 467          */
 468         public static final JavaType QUOTED_OP_TYPE = JavaType.parameterized(
 469                 JavaType.type(Quoted.class), JavaType.type(Op.class));
 470 
 471         final Body quotedBody;
 472 
 473         final Op quotedOp;
 474 
 475         QuotedOp(ExternalizedOp def) {
 476             this(def.bodyDefinitions().get(0));
 477         }
 478 
 479         QuotedOp(QuotedOp that, CodeContext cc, CodeTransformer ot) {
 480             super(that, cc);
 481 
 482             this.quotedBody = that.quotedBody.transform(cc, ot).build(this);
 483             this.quotedOp = that.quotedOp;
 484         }
 485 
 486         @Override
 487         public QuotedOp transform(CodeContext cc, CodeTransformer ot) {
 488             return new QuotedOp(this, cc, ot);
 489         }
 490 
 491         QuotedOp(Body.Builder bodyC) {
 492             super(List.of());
 493 
 494             this.quotedBody = bodyC.build(this);
 495             if (quotedBody.blocks().size() > 1) {
 496                 throw new IllegalArgumentException();
 497             }
 498             if (!(quotedBody.entryBlock().terminatingOp() instanceof YieldOp yop)) {
 499                 throw new IllegalArgumentException();
 500             }
 501             if (!(yop.yieldValue() instanceof Result r)) {
 502                 throw new IllegalArgumentException();
 503             }
 504             this.quotedOp = r.op();
 505         }
 506 
 507         @Override
 508         public List<Body> bodies() {
 509             return List.of(quotedBody);
 510         }
 511 
 512         /**
 513          * {@return the quoted operation}
 514          */
 515         public Op quotedOp() {
 516             return quotedOp;
 517         }
 518 
 519         @Override
 520         public Block.Builder lower(Block.Builder b, CodeTransformer _ignore) {
 521             // Isolate body with respect to ancestor transformations
 522             // and copy directly without lowering descendant operations
 523             b.rebind(b.context(), CodeTransformer.COPYING_TRANSFORMER).op(this);
 524             return b;
 525         }
 526 
 527         @Override
 528         public TypeElement resultType() {
 529             return QUOTED_OP_TYPE;
 530         }
 531     }
 532 
 533     /**
 534      * The terminating return operation, that can model the Java language return statement.
 535      * <p>
 536      * This operation exits an isolated body.
 537      */
 538     @OpDeclaration(ReturnOp.NAME)
 539     public static final class ReturnOp extends CoreOp
 540             implements Op.BodyTerminating, JavaOp.JavaStatement {
 541         static final String NAME = "return";
 542 
 543         ReturnOp(ExternalizedOp def) {
 544             if (def.operands().size() > 1) {
 545                 throw new IllegalArgumentException("Operation must have zero or one operand " + def.name());
 546             }
 547 
 548             this(def.operands().isEmpty() ? null : def.operands().get(0));
 549         }
 550 
 551         ReturnOp(ReturnOp that, CodeContext cc) {
 552             super(that, cc);
 553         }
 554 
 555         @Override
 556         public ReturnOp transform(CodeContext cc, CodeTransformer ot) {
 557             return new ReturnOp(this, cc);
 558         }
 559 
 560         ReturnOp(Value operand) {
 561             super(operand == null ? List.of() : List.of(operand));
 562         }
 563 
 564         /**
 565          * {@return the value returned by this return operation, or null if absent}
 566          */
 567         public Value returnValue() {
 568             if (operands().size() == 1) {
 569                 return operands().get(0);
 570             } else {
 571                 // @@@
 572                 return null;
 573             }
 574         }
 575 
 576         @Override
 577         public TypeElement resultType() {
 578             return JavaType.VOID;
 579         }
 580     }
 581 
 582     /**
 583      * The terminating unreachable operation.
 584      * <p>
 585      * This operation models termination that is unreachable.
 586      */
 587     @OpDeclaration(UnreachableOp.NAME)
 588     public static final class UnreachableOp extends CoreOp
 589             implements Op.BodyTerminating {
 590         static final String NAME = "unreachable";
 591 
 592         UnreachableOp(ExternalizedOp def) {
 593             if (!def.operands().isEmpty()) {
 594                 throw new IllegalArgumentException("Operation must zero operands " + def.name());
 595             }
 596 
 597             this();
 598         }
 599 
 600         UnreachableOp(UnreachableOp that, CodeContext cc) {
 601             super(that, cc);
 602         }
 603 
 604         @Override
 605         public UnreachableOp transform(CodeContext cc, CodeTransformer ot) {
 606             return new UnreachableOp(this, cc);
 607         }
 608 
 609         UnreachableOp() {
 610             super(List.of());
 611         }
 612 
 613         @Override
 614         public TypeElement resultType() {
 615             return JavaType.VOID;
 616         }
 617     }
 618 
 619     /**
 620      * The terminating yield operation.
 621      * <p>
 622      * This operation models exits from its parent body, yielding at most one value (zero value for yielding unit
 623      * or void)
 624      */
 625     @OpDeclaration(YieldOp.NAME)
 626     public static final class YieldOp extends CoreOp
 627             implements Op.BodyTerminating {
 628         static final String NAME = "yield";
 629 
 630         YieldOp(ExternalizedOp def) {
 631             if (def.operands().size() > 1) {
 632                 throw new IllegalArgumentException("Operation must have zero or one operand " + def.name());
 633             }
 634 
 635             this(def.operands());
 636         }
 637 
 638         YieldOp(YieldOp that, CodeContext cc) {
 639             super(that, cc);
 640         }
 641 
 642         @Override
 643         public YieldOp transform(CodeContext cc, CodeTransformer ot) {
 644             return new YieldOp(this, cc);
 645         }
 646 
 647         YieldOp() {
 648             super(List.of());
 649         }
 650 
 651         YieldOp(List<Value> operands) {
 652             super(operands);
 653         }
 654 
 655         /**
 656          * {@return the value yielded by this yield operation, or null if absent}
 657          */
 658         public Value yieldValue() {
 659             if (operands().size() == 1) {
 660                 return operands().get(0);
 661             } else {
 662                 // @@@
 663                 return null;
 664             }
 665         }
 666 
 667         @Override
 668         public TypeElement resultType() {
 669             return JavaType.VOID;
 670         }
 671     }
 672 
 673     /**
 674      * The terminating unconditional branch operation.
 675      * <p>
 676      * This operation accepts a successor to the next block to branch to.
 677      */
 678     @OpDeclaration(BranchOp.NAME)
 679     public static final class BranchOp extends CoreOp
 680             implements Op.BlockTerminating {
 681         static final String NAME = "branch";
 682 
 683         final Block.Reference b;
 684 
 685         BranchOp(ExternalizedOp def) {
 686             if (!def.operands().isEmpty() || def.successors().size() != 1) {
 687                 throw new IllegalArgumentException("Operation must have zero arguments and one successor" + def.name());
 688             }
 689 
 690             this(def.successors().get(0));
 691         }
 692 
 693         BranchOp(BranchOp that, CodeContext cc) {
 694             super(that, cc);
 695 
 696             this.b = cc.getSuccessorOrCreate(that.b);
 697         }
 698 
 699         @Override
 700         public BranchOp transform(CodeContext cc, CodeTransformer ot) {
 701             return new BranchOp(this, cc);
 702         }
 703 
 704         BranchOp(Block.Reference successor) {
 705             super(List.of());
 706 
 707             this.b = successor;
 708         }
 709 
 710         @Override
 711         public List<Block.Reference> successors() {
 712             return List.of(b);
 713         }
 714 
 715         /**
 716          * {@return The branch target}
 717          */
 718         public Block.Reference branch() {
 719             return b;
 720         }
 721 
 722         @Override
 723         public TypeElement resultType() {
 724             return JavaType.VOID;
 725         }
 726     }
 727 
 728     /**
 729      * The terminating conditional branch operation.
 730      * <p>
 731      * This operation accepts a boolean operand and two successors, the true successor and false successor.
 732      * When the operand is true the  true successor is selected, otherwise the false successor is selected.
 733      * The selected successor refers to the next block to branch to.
 734      */
 735     @OpDeclaration(ConditionalBranchOp.NAME)
 736     public static final class ConditionalBranchOp extends CoreOp
 737             implements Op.BlockTerminating {
 738         static final String NAME = "cbranch";
 739 
 740         final Block.Reference t;
 741         final Block.Reference f;
 742 
 743         ConditionalBranchOp(ExternalizedOp def) {
 744             if (def.operands().size() != 1 || def.successors().size() != 2) {
 745                 throw new IllegalArgumentException("Operation must one operand and two successors" + def.name());
 746             }
 747 
 748             this(def.operands().getFirst(), def.successors().get(0), def.successors().get(1));
 749         }
 750 
 751         ConditionalBranchOp(ConditionalBranchOp that, CodeContext cc) {
 752             super(that, cc);
 753 
 754             this.t = cc.getSuccessorOrCreate(that.t);
 755             this.f = cc.getSuccessorOrCreate(that.f);
 756         }
 757 
 758         @Override
 759         public ConditionalBranchOp transform(CodeContext cc, CodeTransformer ot) {
 760             return new ConditionalBranchOp(this, cc);
 761         }
 762 
 763         ConditionalBranchOp(Value p, Block.Reference t, Block.Reference f) {
 764             super(List.of(p));
 765 
 766             this.t = t;
 767             this.f = f;
 768         }
 769 
 770         @Override
 771         public List<Block.Reference> successors() {
 772             return List.of(t, f);
 773         }
 774 
 775         /**
 776          * {@return the branch condition}
 777          */
 778         public Value predicate() {
 779             return operands().get(0);
 780         }
 781 
 782         /**
 783          * {@return the branch target when the condition is true}
 784          */
 785         public Block.Reference trueBranch() {
 786             return t;
 787         }
 788 
 789         /**
 790          * {@return the branch target when the condition is false}
 791          */
 792         public Block.Reference falseBranch() {
 793             return f;
 794         }
 795 
 796         @Override
 797         public TypeElement resultType() {
 798             return JavaType.VOID;
 799         }
 800     }
 801 
 802     /**
 803      * The constant operation, that can model Java language literal and constant expressions.
 804      */
 805     @OpDeclaration(ConstantOp.NAME)
 806     public static final class ConstantOp extends CoreOp
 807             implements Op.Pure, JavaOp.JavaExpression {
 808         static final String NAME = "constant";
 809 
 810         /**
 811          * The externalized attribute modelling the constant value
 812          */
 813         public static final String ATTRIBUTE_CONSTANT_VALUE = NAME + ".value";
 814 
 815         final Object value;
 816         final TypeElement type;
 817 
 818         ConstantOp(ExternalizedOp def) {
 819             if (!def.operands().isEmpty()) {
 820                 throw new IllegalArgumentException("Operation must have zero operands");
 821             }
 822 
 823             Object value = def.extractAttributeValue(ATTRIBUTE_CONSTANT_VALUE, true,
 824                     v -> processConstantValue(def.resultType(), v));
 825 
 826             this(def.resultType(), value);
 827         }
 828 
 829         static Object processConstantValue(TypeElement t, Object value) {
 830             if (t.equals(JavaType.BOOLEAN) && value instanceof Boolean) {
 831                 return value;
 832             } else if (t.equals(JavaType.BYTE) && value instanceof Number n) {
 833                 return n.byteValue();
 834             } else if (t.equals(JavaType.SHORT) && value instanceof Number n) {
 835                 return n.shortValue();
 836             } else if (t.equals(JavaType.CHAR) && value instanceof Character) {
 837                 return value;
 838             } else if (t.equals(JavaType.INT) && value instanceof Number n) {
 839                 return n.intValue();
 840             } else if (t.equals(JavaType.LONG) && value instanceof Number n) {
 841                 return n.longValue();
 842             } else if (t.equals(JavaType.FLOAT) && value instanceof Number n) {
 843                 return n.floatValue();
 844             } else if (t.equals(JavaType.DOUBLE) && value instanceof Number n) {
 845                 return n.doubleValue();
 846             } else if (t.equals(JavaType.J_L_STRING)) {
 847                 return value == ExternalizedOp.NULL_ATTRIBUTE_VALUE ?
 848                         null : (String)value;
 849             } else if (t.equals(JavaType.J_L_CLASS)) {
 850                 return value == ExternalizedOp.NULL_ATTRIBUTE_VALUE ?
 851                         null : (TypeElement)value;
 852             } else if (value == ExternalizedOp.NULL_ATTRIBUTE_VALUE) {
 853                 return null; // null constant
 854             }
 855 
 856             throw new UnsupportedOperationException("Unsupported constant type and value: " + t + " " + value);
 857         }
 858 
 859         ConstantOp(ConstantOp that, CodeContext cc) {
 860             super(that, cc);
 861 
 862             this.type = that.type;
 863             this.value = that.value;
 864         }
 865 
 866         @Override
 867         public ConstantOp transform(CodeContext cc, CodeTransformer ot) {
 868             return new ConstantOp(this, cc);
 869         }
 870 
 871         ConstantOp(TypeElement type, Object value) {
 872             super(List.of());
 873 
 874             this.type = type;
 875             this.value = value;
 876         }
 877 
 878         @Override
 879         public Map<String, Object> externalize() {
 880             return Map.of("", value == null ? ExternalizedOp.NULL_ATTRIBUTE_VALUE : value);
 881         }
 882 
 883         /**
 884          * {@return the constant value modeled by this constant operation}
 885          */
 886         public Object value() {
 887             return value;
 888         }
 889 
 890         @Override
 891         public TypeElement resultType() {
 892             return type;
 893         }
 894     }
 895 
 896     /**
 897      * A runtime representation of a variable.
 898      *
 899      * @param <T> the type of the var's value.
 900      * @@@ Ideally should never be exposed
 901      * @@@ Move to interpreter?
 902      */
 903     public interface Var<T> {
 904         /**
 905          * {@return the value of a var}
 906          */
 907         T value();
 908 
 909         /**
 910          * Constructs an instance of a var.
 911          *
 912          * @param value the initial value of the var.
 913          * @param <T>   the type of the var's value.
 914          * @return the var
 915          */
 916         static <T> Var<T> of(T value) {
 917             return () -> value;
 918         }
 919     }
 920 
 921     /**
 922      * The variable operation, that can model declarations of Java language local variables, method parameters, or
 923      * lambda parameters.
 924      */
 925     @OpDeclaration(VarOp.NAME)
 926     public static final class VarOp extends CoreOp
 927             implements JavaOp.JavaStatement {
 928         static final String NAME = "var";
 929 
 930         /**
 931          * The externalized attribute modelling the variable name
 932          */
 933         public static final String ATTRIBUTE_NAME = NAME + ".name";
 934 
 935         final String varName;
 936         final VarType resultType;
 937 
 938         VarOp(ExternalizedOp def) {
 939             if (def.operands().size() > 1) {
 940                 throw new IllegalStateException("Operation must have zero or one operand");
 941             }
 942 
 943             String name = def.extractAttributeValue(ATTRIBUTE_NAME, true,
 944                     v -> switch (v) {
 945                         case String s -> s;
 946                         case null -> "";
 947                         default -> throw new UnsupportedOperationException("Unsupported var name value:" + v);
 948                     });
 949 
 950             // @@@ Cannot use canonical constructor because type is wrapped
 951             super(def.operands());
 952 
 953             this.varName = name;
 954             this.resultType = (VarType) def.resultType();
 955         }
 956 
 957         VarOp(VarOp that, CodeContext cc) {
 958             super(that, cc);
 959 
 960             this.varName = that.varName;
 961             this.resultType = that.isResultTypeOverridable()
 962                     ? CoreType.varType(initOperand().type()) : that.resultType;
 963         }
 964 
 965         boolean isResultTypeOverridable() {
 966             return !isUninitialized() && resultType().valueType().equals(initOperand().type());
 967         }
 968 
 969         @Override
 970         public VarOp transform(CodeContext cc, CodeTransformer ot) {
 971             return new VarOp(this, cc);
 972         }
 973 
 974         VarOp(String varName, TypeElement type, Value init) {
 975             super(init == null ? List.of() : List.of(init));
 976 
 977             this.varName =  varName == null ? "" : varName;
 978             this.resultType = CoreType.varType(type);
 979         }
 980 
 981         @Override
 982         public Map<String, Object> externalize() {
 983             return isUnnamedVariable() ? Map.of() : Map.of("", varName);
 984         }
 985 
 986         /**
 987          * {@return the initial value assigned to this variable}
 988          * @throws IllegalStateException if this variable doesn't have an initial value,
 989          *                               that is, if it models an uninitialized variable
 990          */
 991         public Value initOperand() {
 992             if (operands().isEmpty()) {
 993                 throw new IllegalStateException("Uninitialized variable");
 994             }
 995             return operands().getFirst();
 996         }
 997 
 998         /**
 999          * {@return the variable name}
1000          */
1001         public String varName() {
1002             return varName;
1003         }
1004 
1005         /**
1006          * {@return the variable type}
1007          */
1008         public TypeElement varValueType() {
1009             return resultType.valueType();
1010         }
1011 
1012         @Override
1013         public VarType resultType() {
1014             return resultType;
1015         }
1016 
1017         /**
1018          * {@return true if this variable operation models an unnamed variable}
1019          */
1020         public boolean isUnnamedVariable() {
1021             return varName.isEmpty();
1022         }
1023 
1024         /**
1025          * {@return true if this variable operation models an uninitialized variable}
1026          */
1027         public boolean isUninitialized() {
1028             return operands().isEmpty();
1029         }
1030     }
1031 
1032     /**
1033      * The var access operation, that can model access to Java language local variables, method parameters, or
1034      * lambda parameters.
1035      */
1036     public sealed abstract static class VarAccessOp extends CoreOp
1037             implements JavaOp.AccessOp {
1038         VarAccessOp(VarAccessOp that, CodeContext cc) {
1039             super(that, cc);
1040         }
1041 
1042         VarAccessOp(List<Value> operands) {
1043             super(operands);
1044         }
1045 
1046         /**
1047          * {@return the accessed variable}
1048          */
1049         public Value varOperand() {
1050             return operands().getFirst();
1051         }
1052 
1053         /**
1054          * {@return the type of the accessed variable}
1055          */
1056         public VarType varType() {
1057             return (VarType) varOperand().type();
1058         }
1059 
1060         /**
1061          * {@return the variable operation associated with this access operation}
1062          */
1063         public VarOp varOp() {
1064             if (!(varOperand() instanceof Result varValue)) {
1065                 throw new IllegalStateException("Variable access to block parameter: " + varOperand());
1066             }
1067 
1068             // At a high-level a variable value can be a BlockArgument.
1069             // Lowering should remove such cases and the var declaration should emerge
1070             // This method is primarily used when transforming to pure SSA
1071             return (VarOp) varValue.op();
1072         }
1073 
1074         static void checkIsVarOp(Value varValue) {
1075             if (!(varValue.type() instanceof VarType)) {
1076                 throw new IllegalArgumentException("Value's type is not a variable type: " + varValue);
1077             }
1078         }
1079 
1080         /**
1081          * The variable load operation, that models a reading variable.
1082          */
1083         @OpDeclaration(VarLoadOp.NAME)
1084         public static final class VarLoadOp extends VarAccessOp
1085                 implements JavaOp.JavaExpression {
1086             static final String NAME = "var.load";
1087 
1088             VarLoadOp(ExternalizedOp opdef) {
1089                 if (opdef.operands().size() != 1) {
1090                     throw new IllegalArgumentException("Operation must have one operand");
1091                 }
1092                 checkIsVarOp(opdef.operands().get(0));
1093 
1094                 this(opdef.operands().get(0));
1095             }
1096 
1097             VarLoadOp(VarLoadOp that, CodeContext cc) {
1098                 super(that, cc);
1099             }
1100 
1101             @Override
1102             public VarLoadOp transform(CodeContext cc, CodeTransformer ot) {
1103                 return new VarLoadOp(this, cc);
1104             }
1105 
1106             // (Variable)VarType
1107             VarLoadOp(Value varValue) {
1108                 super(List.of(varValue));
1109             }
1110 
1111             @Override
1112             public TypeElement resultType() {
1113                 return varType().valueType();
1114             }
1115         }
1116 
1117         /**
1118          * The variable store operation, that can model a variable assignment.
1119          */
1120         @OpDeclaration(VarStoreOp.NAME)
1121         public static final class VarStoreOp extends VarAccessOp
1122                 implements JavaOp.JavaExpression, JavaOp.JavaStatement {
1123             static final String NAME = "var.store";
1124 
1125             VarStoreOp(ExternalizedOp opdef) {
1126                 if (opdef.operands().size() != 2) {
1127                     throw new IllegalArgumentException("Operation must have two operands");
1128                 }
1129                 checkIsVarOp(opdef.operands().get(0));
1130 
1131                 this(opdef.operands().get(0), opdef.operands().get(1));
1132             }
1133 
1134             VarStoreOp(VarStoreOp that, CodeContext cc) {
1135                 super(that, cc);
1136             }
1137 
1138             VarStoreOp(List<Value> values) {
1139                 super(values);
1140             }
1141 
1142             @Override
1143             public VarStoreOp transform(CodeContext cc, CodeTransformer ot) {
1144                 return new VarStoreOp(this, cc);
1145             }
1146 
1147             // (Variable, VarType)void
1148             VarStoreOp(Value varValue, Value v) {
1149                 super(List.of(varValue, v));
1150             }
1151 
1152             /**
1153              * {@return the value being stored to the variable}
1154              */
1155             public Value storeOperand() {
1156                 return operands().get(1);
1157             }
1158 
1159             @Override
1160             public TypeElement resultType() {
1161                 return JavaType.VOID;
1162             }
1163         }
1164     }
1165 
1166     // Tuple operations, for modelling return with multiple values
1167 
1168     /**
1169      * The tuple operation. A tuple contain a fixed set of values accessible by their component index.
1170      */
1171     @OpDeclaration(TupleOp.NAME)
1172     public static final class TupleOp extends CoreOp {
1173         static final String NAME = "tuple";
1174 
1175         TupleOp(ExternalizedOp def) {
1176             this(def.operands());
1177         }
1178 
1179         TupleOp(TupleOp that, CodeContext cc) {
1180             super(that, cc);
1181         }
1182 
1183         @Override
1184         public TupleOp transform(CodeContext cc, CodeTransformer ot) {
1185             return new TupleOp(this, cc);
1186         }
1187 
1188         TupleOp(List<? extends Value> componentValues) {
1189             super(componentValues);
1190         }
1191 
1192         @Override
1193         public TypeElement resultType() {
1194             return CoreType.tupleTypeFromValues(operands());
1195         }
1196     }
1197 
1198     /**
1199      * The tuple component load operation, that access the component of a tuple at a given, constant, component index.
1200      */
1201     @OpDeclaration(TupleLoadOp.NAME)
1202     public static final class TupleLoadOp extends CoreOp {
1203         static final String NAME = "tuple.load";
1204 
1205         /**
1206          * The externalized attribute modelling the tuple index
1207          */
1208         public static final String ATTRIBUTE_INDEX = NAME + ".index";
1209 
1210         final int index;
1211 
1212         TupleLoadOp(ExternalizedOp def) {
1213             if (def.operands().size() != 1) {
1214                 throw new IllegalStateException("Operation must have one operand");
1215             }
1216 
1217             int index = def.extractAttributeValue(ATTRIBUTE_INDEX, true,
1218                     v -> switch (v) {
1219                         case Integer i -> i;
1220                         case null, default -> throw new UnsupportedOperationException("Unsupported tuple index value:" + v);
1221                     });
1222 
1223             this(def.operands().get(0), index);
1224         }
1225 
1226         TupleLoadOp(TupleLoadOp that, CodeContext cc) {
1227             super(that, cc);
1228 
1229             this.index = that.index;
1230         }
1231 
1232         @Override
1233         public TupleLoadOp transform(CodeContext cc, CodeTransformer ot) {
1234             return new TupleLoadOp(this, cc);
1235         }
1236 
1237         TupleLoadOp(Value tupleValue, int index) {
1238             super(List.of(tupleValue));
1239 
1240             this.index = index;
1241         }
1242 
1243         @Override
1244         public Map<String, Object> externalize() {
1245             return Map.of("", index);
1246         }
1247 
1248         /**
1249          * {@return the component index of this tuple load operation}
1250          */
1251         public int index() {
1252             return index;
1253         }
1254 
1255         @Override
1256         public TypeElement resultType() {
1257             Value tupleValue = operands().get(0);
1258             TupleType t = (TupleType) tupleValue.type();
1259             return t.componentTypes().get(index);
1260         }
1261     }
1262 
1263     /**
1264      * The tuple component set operation, that access the component of a tuple at a given, constant, component index.
1265      */
1266     @OpDeclaration(TupleWithOp.NAME)
1267     public static final class TupleWithOp extends CoreOp {
1268         static final String NAME = "tuple.with";
1269 
1270         /**
1271          * The externalized attribute modelling the tuple index
1272          */
1273         public static final String ATTRIBUTE_INDEX = NAME + ".index";
1274 
1275         final int index;
1276 
1277         TupleWithOp(ExternalizedOp def) {
1278             if (def.operands().size() != 2) {
1279                 throw new IllegalStateException("Operation must have two operands");
1280             }
1281 
1282             int index = def.extractAttributeValue(ATTRIBUTE_INDEX, true,
1283                     v -> switch (v) {
1284                         case Integer i -> i;
1285                         case null, default -> throw new UnsupportedOperationException("Unsupported tuple index value:" + v);
1286                     });
1287 
1288             this(def.operands().get(0), index, def.operands().get(1));
1289         }
1290 
1291         TupleWithOp(TupleWithOp that, CodeContext cc) {
1292             super(that, cc);
1293 
1294             this.index = that.index;
1295         }
1296 
1297         @Override
1298         public TupleWithOp transform(CodeContext cc, CodeTransformer ot) {
1299             return new TupleWithOp(this, cc);
1300         }
1301 
1302         TupleWithOp(Value tupleValue, int index, Value value) {
1303             super(List.of(tupleValue, value));
1304 
1305             // @@@ Validate tuple type and index
1306             this.index = index;
1307         }
1308 
1309         @Override
1310         public Map<String, Object> externalize() {
1311             return Map.of("", index);
1312         }
1313 
1314         /**
1315          * {@return the component index of this tuple with operation}
1316          */
1317         public int index() {
1318             return index;
1319         }
1320 
1321         @Override
1322         public TypeElement resultType() {
1323             Value tupleValue = operands().get(0);
1324             TupleType tupleType = (TupleType) tupleValue.type();
1325             Value value = operands().get(1);
1326 
1327             List<TypeElement> tupleComponentTypes = new ArrayList<>(tupleType.componentTypes());
1328             tupleComponentTypes.set(index, value.type());
1329             return CoreType.tupleType(tupleComponentTypes);
1330         }
1331     }
1332 
1333     static Op createOp(ExternalizedOp def) {
1334         Op op = switch (def.name()) {
1335             case "branch" -> new BranchOp(def);
1336             case "cbranch" -> new ConditionalBranchOp(def);
1337             case "constant" -> new ConstantOp(def);
1338             case "func" -> new FuncOp(def);
1339             case "func.call" -> new FuncCallOp(def);
1340             case "module" -> new ModuleOp(def);
1341             case "quoted" -> new QuotedOp(def);
1342             case "return" -> new ReturnOp(def);
1343             case "tuple" -> new TupleOp(def);
1344             case "tuple.load" -> new TupleLoadOp(def);
1345             case "tuple.with" -> new TupleWithOp(def);
1346             case "unreachable" -> new UnreachableOp(def);
1347             case "var" -> new VarOp(def);
1348             case "var.load" -> new VarAccessOp.VarLoadOp(def);
1349             case "var.store" -> new VarAccessOp.VarStoreOp(def);
1350             case "yield" -> new YieldOp(def);
1351             default -> null;
1352         };
1353         if (op != null) {
1354             op.setLocation(def.location());
1355         }
1356         return op;
1357     }
1358 
1359     /**
1360      * An operation factory for core operations.
1361      */
1362     public static final OpFactory CORE_OP_FACTORY = CoreOp::createOp;
1363 
1364     /**
1365      * Creates a function operation builder
1366      *
1367      * @param funcName the function name
1368      * @param funcType the function type
1369      * @return the function operation builder
1370      */
1371     public static FuncOp.Builder func(String funcName, FunctionType funcType) {
1372         return new FuncOp.Builder(null, funcName, funcType);
1373     }
1374 
1375     /**
1376      * Creates a function operation
1377      *
1378      * @param funcName the function name
1379      * @param body     the function body
1380      * @return the function operation
1381      */
1382     public static FuncOp func(String funcName, Body.Builder body) {
1383         return new FuncOp(funcName, body);
1384     }
1385 
1386     /**
1387      * Creates a function call operation
1388      *
1389      * @param funcName the name of the function operation
1390      * @param funcType the function type
1391      * @param args     the function arguments
1392      * @return the function call operation
1393      */
1394     public static FuncCallOp funcCall(String funcName, FunctionType funcType, Value... args) {
1395         return funcCall(funcName, funcType, List.of(args));
1396     }
1397 
1398     /**
1399      * Creates a function call operation
1400      *
1401      * @param funcName the name of the function operation
1402      * @param funcType the function type
1403      * @param args     the function arguments
1404      * @return the function call operation
1405      */
1406     public static FuncCallOp funcCall(String funcName, FunctionType funcType, List<Value> args) {
1407         return new FuncCallOp(funcName, funcType.returnType(), args);
1408     }
1409 
1410     /**
1411      * Creates a function call operation
1412      *
1413      * @param func the target function
1414      * @param args the function arguments
1415      * @return the function call operation
1416      */
1417     public static FuncCallOp funcCall(FuncOp func, Value... args) {
1418         return funcCall(func, List.of(args));
1419     }
1420 
1421     /**
1422      * Creates a function call operation
1423      *
1424      * @param func the target function
1425      * @param args the function argments
1426      * @return the function call operation
1427      */
1428     public static FuncCallOp funcCall(FuncOp func, List<Value> args) {
1429         return new FuncCallOp(func.funcName(), func.invokableType().returnType(), args);
1430     }
1431 
1432     /**
1433      * Creates a module operation.
1434      *
1435      * @param functions the functions of the module operation
1436      * @return the module operation
1437      */
1438     public static ModuleOp module(FuncOp... functions) {
1439         return module(List.of(functions));
1440     }
1441 
1442     /**
1443      * Creates a module operation.
1444      *
1445      * @param functions the functions of the module operation
1446      * @return the module operation
1447      */
1448     public static ModuleOp module(List<FuncOp> functions) {
1449         return new ModuleOp(List.copyOf(functions));
1450     }
1451 
1452     /**
1453      * Creates a quoted operation.
1454      *
1455      * @param ancestorBody the ancestor of the body of the quoted operation
1456      * @param opFunc       a function that accepts the body of the quoted operation and returns the operation to be quoted
1457      * @return the quoted operation
1458      */
1459     public static QuotedOp quoted(Body.Builder ancestorBody,
1460                                   Function<Block.Builder, Op> opFunc) {
1461         Body.Builder body = Body.Builder.of(ancestorBody, CoreType.FUNCTION_TYPE_VOID);
1462         Block.Builder block = body.entryBlock();
1463         block.op(core_yield(
1464                 block.op(opFunc.apply(block))));
1465         return new QuotedOp(body);
1466     }
1467 
1468     /**
1469      * Creates a quoted operation.
1470      *
1471      * @param body quoted operation body
1472      * @return the quoted operation
1473      */
1474     public static QuotedOp quoted(Body.Builder body) {
1475         return new QuotedOp(body);
1476     }
1477 
1478     /**
1479      * Creates a return operation.
1480      *
1481      * @return the return operation
1482      */
1483     public static ReturnOp return_() {
1484         return return_(null);
1485     }
1486 
1487     /**
1488      * Creates a return operation.
1489      *
1490      * @param returnValue the return value
1491      * @return the return operation
1492      */
1493     public static ReturnOp return_(Value returnValue) {
1494         return new ReturnOp(returnValue);
1495     }
1496 
1497     /**
1498      * Creates an unreachable operation.
1499      *
1500      * @return the unreachable operation
1501      */
1502     public static UnreachableOp unreachable() {
1503         return new UnreachableOp();
1504     }
1505 
1506     /**
1507      * Creates a yield operation.
1508      *
1509      * @return the yield operation
1510      */
1511     public static YieldOp core_yield() {
1512         return new YieldOp();
1513     }
1514 
1515     /**
1516      * Creates a yield operation.
1517      *
1518      * @param yieldValue the yielded value
1519      * @return the yield operation
1520      */
1521     public static YieldOp core_yield(Value yieldValue) {
1522         return new YieldOp(List.of(yieldValue));
1523     }
1524 
1525     /**
1526      * Creates an unconditional break operation.
1527      *
1528      * @param target the jump target
1529      * @return the unconditional break operation
1530      */
1531     public static BranchOp branch(Block.Reference target) {
1532         return new BranchOp(target);
1533     }
1534 
1535     /**
1536      * Creates a conditional break operation.
1537      *
1538      * @param condValue   the test value of the conditional break operation
1539      * @param trueTarget  the jump target when the test value evaluates to true
1540      * @param falseTarget the jump target when the test value evaluates to false
1541      * @return the conditional break operation
1542      */
1543     public static ConditionalBranchOp conditionalBranch(Value condValue,
1544                                                         Block.Reference trueTarget, Block.Reference falseTarget) {
1545         return new ConditionalBranchOp(condValue, trueTarget, falseTarget);
1546     }
1547 
1548     /**
1549      * Creates a constant operation.
1550      *
1551      * @param type  the constant type
1552      * @param value the constant value
1553      * @return the constant operation
1554      */
1555     public static ConstantOp constant(TypeElement type, Object value) {
1556         return new ConstantOp(type, value);
1557     }
1558 
1559     // @@@ Add field load/store overload with explicit fieldType
1560 
1561     /**
1562      * Creates a var operation modeling an unnamed and uninitialized variable,
1563      * either an unnamed local variable or an unnamed parameter.
1564      *
1565      * @param type the type of the var's value
1566      * @return the var operation
1567      */
1568     public static VarOp var(TypeElement type) {
1569         return var(null, type);
1570     }
1571 
1572     /**
1573      * Creates a var operation modeling an uninitialized variable, either a local variable or a parameter.
1574      *
1575      * @param name the name of the var
1576      * @param type the type of the var's value
1577      * @return the var operation
1578      */
1579     public static VarOp var(String name, TypeElement type) {
1580         return var(name, type, null);
1581     }
1582 
1583     /**
1584      * Creates a var operation modeling an unnamed variable, either an unnamed local variable or an unnamed parameter.
1585      *
1586      * @param init the initial value of the var
1587      * @return the var operation
1588      */
1589     public static VarOp var(Value init) {
1590         return var(null, init);
1591     }
1592 
1593     /**
1594      * Creates a var operation modeling a variable, either a local variable or a parameter.
1595      * <p>
1596      * If the given name is {@code null} or an empty string then the variable is an unnamed variable.
1597      *
1598      * @param name the name of the var
1599      * @param init the initial value of the var
1600      * @return the var operation
1601      */
1602     public static VarOp var(String name, Value init) {
1603         return var(name, init.type(), init);
1604     }
1605 
1606     /**
1607      * Creates a var operation modeling a variable, either a local variable or a parameter.
1608      * <p>
1609      * If the given name is {@code null} or an empty string then the variable is an unnamed variable.
1610      *
1611      * @param name the name of the var
1612      * @param type the type of the var's value
1613      * @param init the initial value of the var
1614      * @return the var operation
1615      */
1616     public static VarOp var(String name, TypeElement type, Value init) {
1617         return new VarOp(name, type, init);
1618     }
1619 
1620     /**
1621      * Creates a var load operation.
1622      *
1623      * @param varValue the var value
1624      * @return the var load operation
1625      */
1626     public static VarAccessOp.VarLoadOp varLoad(Value varValue) {
1627         return new VarAccessOp.VarLoadOp(varValue);
1628     }
1629 
1630     /**
1631      * Creates a var store operation.
1632      *
1633      * @param varValue the var value
1634      * @param v        the value to store in the var
1635      * @return the var store operation
1636      */
1637     public static VarAccessOp.VarStoreOp varStore(Value varValue, Value v) {
1638         return new VarAccessOp.VarStoreOp(varValue, v);
1639     }
1640 
1641     /**
1642      * Creates a tuple operation.
1643      *
1644      * @param componentValues the values of tuple (in order)
1645      * @return the tuple operation
1646      */
1647     public static TupleOp tuple(Value... componentValues) {
1648         return tuple(List.of(componentValues));
1649     }
1650 
1651     /**
1652      * Creates a tuple operation.
1653      *
1654      * @param componentValues the values of tuple (in order)
1655      * @return the tuple operation
1656      */
1657     public static TupleOp tuple(List<? extends Value> componentValues) {
1658         return new TupleOp(componentValues);
1659     }
1660 
1661     /**
1662      * Creates a tuple load operation.
1663      *
1664      * @param tuple the tuple value
1665      * @param index the component index value
1666      * @return the tuple load operation
1667      */
1668     public static TupleLoadOp tupleLoad(Value tuple, int index) {
1669         return new TupleLoadOp(tuple, index);
1670     }
1671 
1672     /**
1673      * Creates a tuple with operation.
1674      *
1675      * @param tuple the tuple value
1676      * @param index the component index value
1677      * @param value the component value
1678      * @return the tuple with operation
1679      */
1680     public static TupleWithOp tupleWith(Value tuple, int index, Value value) {
1681         return new TupleWithOp(tuple, index, value);
1682     }
1683 }