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 }