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