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