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