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