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.internal;
27
28 import jdk.incubator.code.*;
29 import jdk.incubator.code.dialect.core.CoreType;
30 import jdk.incubator.code.dialect.core.FunctionType;
31 import jdk.incubator.code.dialect.java.*;
32 import jdk.incubator.code.extern.*;
33
34 import java.util.*;
35 import java.util.function.Function;
36 import java.util.function.Supplier;
37
38 import static jdk.incubator.code.dialect.core.CoreOp.*;
39 import static jdk.incubator.code.dialect.core.CoreType.functionType;
40 import static jdk.incubator.code.dialect.java.JavaOp.*;
41 import static jdk.incubator.code.dialect.java.JavaType.*;
42
43 /**
44 * A transformer of code models to models that build them.
45 * <p>
46 * A building code model when executed will construct the same code model it was transformed from.
47 * Such a building code model could be transformed to bytecode and stored in class files.
48 */
49 public class OpBuilder {
50
51 static final JavaType J_C_E_EXTERNALIZED_OP = type(ExternalizedOp.class);
52
53 static final MethodRef DIALECT_FACTORY_OP_FACTORY = MethodRef.method(DialectFactory.class, "opFactory",
54 OpFactory.class);
55
56 static final MethodRef DIALECT_FACTORY_TYPE_ELEMENT_FACTORY = MethodRef.method(DialectFactory.class, "typeElementFactory",
57 TypeElementFactory.class);
58
59 static final MethodRef OP_FACTORY_CONSTRUCT = MethodRef.method(OpFactory.class, "constructOp",
60 Op.class, ExternalizedOp.class);
61
62 static final MethodRef TYPE_ELEMENT_FACTORY_CONSTRUCT = MethodRef.method(TypeElementFactory.class, "constructType",
63 TypeElement.class, ExternalizedTypeElement.class);
64
65 static final MethodRef BODY_BUILDER_OF = MethodRef.method(Body.Builder.class, "of",
66 Body.Builder.class, Body.Builder.class, FunctionType.class);
67
68 static final MethodRef BODY_BUILDER_ENTRY_BLOCK = MethodRef.method(Body.Builder.class, "entryBlock",
69 Block.Builder.class);
70
71 // instance varargs
72 static final MethodRef BLOCK_BUILDER_SUCCESSOR = MethodRef.method(Block.Builder.class, "successor",
73 Block.Reference.class, Value[].class);
74
75 static final MethodRef BLOCK_BUILDER_OP = MethodRef.method(Block.Builder.class, "op",
76 Op.Result.class, Op.class);
77
78 // instance varargs
79 static final MethodRef BLOCK_BUILDER_BLOCK = MethodRef.method(Block.Builder.class, "block",
80 Block.Builder.class, TypeElement[].class);
81
82 static final MethodRef BLOCK_BUILDER_PARAMETER = MethodRef.method(Block.Builder.class, "parameter",
83 Block.Parameter.class, TypeElement.class);
84
85 // static varargs
86 static final MethodRef FUNCTION_TYPE_FUNCTION_TYPE = MethodRef.method(CoreType.class, "functionType",
87 FunctionType.class, TypeElement.class, TypeElement[].class);
88
89
90 static final JavaType J_U_LIST = type(List.class);
91
92 static final MethodRef LIST_OF_ARRAY = MethodRef.method(J_U_LIST, "of",
93 J_U_LIST, array(J_L_OBJECT, 1));
94
95 static final JavaType J_U_MAP = type(Map.class);
96
97 static final JavaType J_U_MAP_ENTRY = type(Map.Entry.class);
98
99 static final MethodRef MAP_ENTRY = MethodRef.method(J_U_MAP, "entry",
100 J_U_MAP, J_L_OBJECT, J_L_OBJECT);
101
102 static final MethodRef MAP_OF_ARRAY = MethodRef.method(J_U_MAP, "of",
103 J_U_MAP, array(J_U_MAP_ENTRY, 1));
104
105
106 static final JavaType EX_TYPE_ELEM = type(ExternalizedTypeElement.class);
107
108 static final FunctionType EXTER_TYPE_BUILDER_F_TYPE = functionType(EX_TYPE_ELEM);
109
110 static final MethodRef EX_TYPE_ELEM_OF_ARRAY = MethodRef.method(ExternalizedTypeElement.class, "of",
111 ExternalizedTypeElement.class, String.class, ExternalizedTypeElement[].class);
112
113
114 static final JavaType J_C_LOCATION = type(Location.class);
115
116 static final FunctionType EXTERNALIZED_OP_F_TYPE = functionType(
117 J_C_E_EXTERNALIZED_OP,
118 J_L_STRING,
119 J_C_LOCATION,
120 J_U_LIST,
121 J_U_LIST,
122 type(TypeElement.class),
123 J_U_MAP,
124 J_U_LIST);
125
126 static final FunctionType BUILDER_F_TYPE = functionType(type(Op.class));
127
128 static final MethodRef LIST_EMPTY = MethodRef.method(List.class, "of", List.class);
129 static final MethodRef LIST_OF_OBJECT = MethodRef.method(List.class, "of", List.class, Object.class);
130 static final MethodRef MAP_EMPTY = MethodRef.method(Map.class, "of", Map.class);
131 static final MethodRef MAP_OF_OBJECT_OBJECT = MethodRef.method(Map.class, "of", Map.class, Object.class, Object.class);
132
133 static final String LIST_BUILDER_F_NAME = "$list";
134 static final String MAP_BUILDER_F_NAME = "$map";
135 static final String OP_BUILDER_F_NAME_1 = "$op1";
136 static final String OP_BUILDER_F_NAME_2 = "$op2";
137 static final String OP_BUILDER_F_NAME_3 = "$op3";
138 static final String TYPE_BUILDER_F_NAME = "$type";
139 static final String EXTER_TYPE_BUILDER_F_NAME = "$exterType";
140 static final String JAVA_VERSION_CHECKER_F_NAME = "$checkJavaVersion";
141
142 static final FunctionType LIST_BUILDER_F_TYPE = functionType(
143 J_U_LIST,
144 J_L_OBJECT);
145
146 static final FunctionType MAP_BUILDER_F_TYPE = functionType(
147 J_U_MAP,
148 J_L_OBJECT);
149
150 static final FunctionType OP_BUILDER_F_OVERRIDE_1 = functionType(
151 type(Op.class),
152 J_L_STRING, // op name
153 J_C_LOCATION, // location: Location or null
154 J_L_OBJECT, // operand(s): Value, List<Value> or null
155 J_L_OBJECT, // successor(s): Block.Reference, List<Block.Reference> or null
156 INT, // result type index
157 J_L_OBJECT, // attribute(s): Map<String, Object>, Object or null
158 J_L_OBJECT); // body definition(s): Body.Builder, List<Body.Builder> or null
159
160 static final FunctionType OP_BUILDER_F_OVERRIDE_2 = functionType(
161 type(Op.Result.class),
162 type(Block.Builder.class),
163 J_L_STRING, // op name
164 J_C_LOCATION, // location: Location or null
165 J_L_OBJECT, // operand(s): Value, List<Value> or null
166 J_L_OBJECT, // successor(s): Block.Reference, List<Block.Reference> or null
167 INT, // result type index
168 J_L_OBJECT, // attribute(s): Map<String, Object>, Object or null
169 J_L_OBJECT); // body definition(s): Body.Builder, List<Body.Builder> or null
170
171 static final FunctionType OP_BUILDER_F_OVERRIDE_3 = functionType(
172 type(Op.Result.class),
173 type(Block.Builder.class),
174 J_L_STRING, // op name
175 INT, // location.line
176 INT, // location.columnt
177 J_L_OBJECT, // operand(s): Value, List<Value> or null
178 J_L_OBJECT, // successor(s): Block.Reference, List<Block.Reference> or null
179 INT, // result type index
180 J_L_OBJECT, // attribute(s): Map<String, Object>, Object or null
181 J_L_OBJECT); // body definition(s): Body.Builder, List<Body.Builder> or null
182
183 static final FunctionType TYPE_BUILDER_F_TYPE = functionType(JavaType.type(TypeElement.class), INT);
184
185 final Map<Value, Value> valueMap;
186
187 final Map<Block, Value> blockMap;
188
189 final SequencedMap<ExternalizedTypeElement, List<Integer>> registeredExternalizedTypes;
190
191 final Map<TypeElement, Value> typeElementMap;
192
193 Block.Builder builder;
194
195 final Stack<Block.Builder> lambdaStack = new Stack<>();
196
197 // safe limit of the operations built by a single method/lambda body
198 static final int OP_LIMIT = 1000;
199 // safe limit of types / exterType calls built by a single builder method
200 static final int TYPE_LIMIT = 3000;
201
202 int opCounter = 0;
203
204 /**
205 * Transform the given code model to one that builds it.
206 * <p>
207 * This method initially applies the function {@code dialectFactoryF} to
208 * the block builder that is used to build resulting code model. The result
209 * is a dialect factory value which is subsequently used to build operations
210 * that construct type elements and operations present in the given code model.
211 *
212 * @param ops the named code models.
213 * @param dialectFactoryF a function that builds code items to produce a dialect factory value.
214 * @return the module with building code models and support functions.
215 */
216 public static ModuleOp createBuilderFunctions(SequencedMap<String, ? extends Op> ops, Function<Block.Builder, Value> dialectFactoryF) {
217 List<FuncOp> funcs = new ArrayList<>();
218 SequencedMap<ExternalizedTypeElement, List<Integer>> registeredExternalizedTypes = new LinkedHashMap<>();
219 for (var e : ops.sequencedEntrySet()) {
220 OpBuilder opBuilder = new OpBuilder(registeredExternalizedTypes);
221 funcs.add(opBuilder.build(e.getKey(), e.getValue()));
222 registeredExternalizedTypes = opBuilder.registeredExternalizedTypes;
223 }
224 funcs.addAll(createSupportFunctions(dialectFactoryF));
225 funcs.addAll(createExternTypeHelperFuncs(registeredExternalizedTypes));
226 ModuleOp module = module(funcs);
227 module.seal();
228 return module;
229 }
230
231 static List<FuncOp> createSupportFunctions(Function<Block.Builder, Value> dialectFactoryF) {
232 return List.of(
233 // static List $list(Object o) {
234 // if (o == null) return List.of();
235 // if (o instanceof List) return (List)o;
236 // return List.of(o);
237 // }
238 func(LIST_BUILDER_F_NAME, LIST_BUILDER_F_TYPE).body(bb -> {
239 Block.Builder b0 = bb.entryBlock(), b1 = b0.block(), b2 = b0.block(), b3 = b0.block(), b4 = b0.block();
240 Value arg = b0.parameters().get(0);
241 b0.op(conditionalBranch(b0.op(eq(arg, b0.op(constant(J_L_OBJECT, null)))), b1.successor(), b2.successor()));
242 b1.op(return_(b1.op(invoke(LIST_EMPTY))));
243 b2.op(conditionalBranch(b2.op(instanceOf(J_U_LIST, arg)), b3.successor(), b4.successor()));
244 b3.op(return_(b3.op(cast(J_U_LIST, arg))));
245 b4.op(return_(b4.op(invoke(LIST_OF_OBJECT, arg))));
246 }),
247 // static Map $map(Object o) {
248 // if (o == null) return Map.of();
249 // if (o instanceof Map) return (Map)o;
250 // return Map.of("", o);
251 // }
252 func(MAP_BUILDER_F_NAME, MAP_BUILDER_F_TYPE).body(bb -> {
253 Block.Builder b0 = bb.entryBlock(), b1 = b0.block(), b2 = b0.block(), b3 = b0.block(), b4 = b0.block();
254 Value arg = b0.parameters().get(0);
255 b0.op(conditionalBranch(b0.op(eq(arg, b0.op(constant(J_L_OBJECT, null)))), b1.successor(), b2.successor()));
256 b1.op(return_(b1.op(invoke(MAP_EMPTY))));
257 b2.op(conditionalBranch(b2.op(instanceOf(J_U_MAP, arg)), b3.successor(), b4.successor()));
258 b3.op(return_(b3.op(cast(J_U_MAP, arg))));
259 b4.op(return_(b4.op(invoke(MAP_OF_OBJECT_OBJECT, b4.op(constant(J_L_STRING, "")), arg))));
260 }),
261 // static Op $op1(String name,
262 // Location location,
263 // Object operands,
264 // Object successors,
265 // int resultTypeIndex,
266 // Object attributes,
267 // Object bodyDefinitions) {
268 // return <dialect factory>.opFactory().constructOp(
269 // new ExternalizedOp(name,
270 // location,
271 // $list(operands),
272 // $list(successors),
273 // $type(resultTypeIndex),
274 // $map(attributes),
275 // $list(bodyDefinitions)));
276 // }
277 func(OP_BUILDER_F_NAME_1, OP_BUILDER_F_OVERRIDE_1).body(bb -> {
278 Block.Builder b = bb.entryBlock();
279 List<Block.Parameter> args = b.parameters();
280 b.op(return_(b.op(invoke(OP_FACTORY_CONSTRUCT,
281 b.op(invoke(DIALECT_FACTORY_OP_FACTORY, dialectFactoryF.apply(b))),
282 b.op(new_(MethodRef.constructor(EXTERNALIZED_OP_F_TYPE),
283 args.get(0),
284 args.get(1),
285 b.op(funcCall(LIST_BUILDER_F_NAME, LIST_BUILDER_F_TYPE, args.get(2))),
286 b.op(funcCall(LIST_BUILDER_F_NAME, LIST_BUILDER_F_TYPE, args.get(3))),
287 b.op(funcCall(TYPE_BUILDER_F_NAME, TYPE_BUILDER_F_TYPE, args.get(4))),
288 b.op(funcCall(MAP_BUILDER_F_NAME, MAP_BUILDER_F_TYPE, args.get(5))),
289 b.op(funcCall(LIST_BUILDER_F_NAME, LIST_BUILDER_F_TYPE, args.get(6)))))))));
290 }),
291 // static Op.Result $op2(Block.Builder b,
292 // String name,
293 // Location location,
294 // Object operands,
295 // Object successors,
296 // int resultTypeIndex,
297 // Object attributes,
298 // Object bodyDefinitions) {
299 // return b.op($op1(name,
300 // location,
301 // operands,
302 // successors,
303 // resultType,
304 // attributes,
305 // bodyDefinitions));
306 // }
307 func(OP_BUILDER_F_NAME_2, OP_BUILDER_F_OVERRIDE_2).body(bb -> {
308 Block.Builder b = bb.entryBlock();
309 List<Block.Parameter> args = b.parameters();
310 b.op(return_(b.op(invoke(BLOCK_BUILDER_OP,
311 args.get(0),
312 b.op(funcCall(OP_BUILDER_F_NAME_1, OP_BUILDER_F_OVERRIDE_1,
313 args.get(1),
314 args.get(2),
315 args.get(3),
316 args.get(4),
317 args.get(5),
318 args.get(6),
319 args.get(7)))))));
320 }),
321 // static Op.Result $op3(Block.Builder b,
322 // String name,
323 // int line,
324 // int column,
325 // Object operands,
326 // Object successors,
327 // int resultTypeIndex,
328 // Object attributes,
329 // Object bodyDefinitions) {
330 // return $op2(b,
331 // name,
332 // new Location(line, column),
333 // operands,
334 // successors,
335 // resultType,
336 // attributes,
337 // bodyDefinitions);
338 // }
339 func(OP_BUILDER_F_NAME_3, OP_BUILDER_F_OVERRIDE_3).body(bb -> {
340 Block.Builder b = bb.entryBlock();
341 List<Block.Parameter> args = b.parameters();
342 b.op(return_(b.op(funcCall(OP_BUILDER_F_NAME_2, OP_BUILDER_F_OVERRIDE_2,
343 args.get(0),
344 args.get(1),
345 b.op(new_(MethodRef.constructor(Location.class, int.class, int.class), args.get(2), args.get(3))),
346 args.get(4),
347 args.get(5),
348 args.get(6),
349 args.get(7),
350 args.get(8)))));
351 }),
352 // static private TypeElement $type(int typeIndex) {
353 // return JavaOp.JAVA_DIALECT_FACTORY.typeElementFactory().constructType($exType(typeIndex));
354 // }
355 func(TYPE_BUILDER_F_NAME, CoreType.functionType(type(TypeElement.class))).body(b -> {
356 var i = b.parameter(INT);
357 var typeElementFactory = b.op(invoke(DIALECT_FACTORY_TYPE_ELEMENT_FACTORY, dialectFactoryF.apply(b)));
358 var exterType = b.op(funcCall(EXTER_TYPE_BUILDER_F_NAME, EXTER_TYPE_BUILDER_F_TYPE, i));
359 var typeElement = b.op(invoke(MethodRef.method(TypeElementFactory.class, "constructType", TypeElement.class, ExternalizedTypeElement.class), typeElementFactory, exterType));
360 b.op(return_(typeElement));
361 }),
362 func(JAVA_VERSION_CHECKER_F_NAME, FunctionType.FUNCTION_TYPE_VOID).body(b -> {
363 var compiletimeVersion = Runtime.version().feature();
364 // runtimeVersion = Runtime.version().feature()
365 var version = b.op(invoke(MethodRef.method(Runtime.class, "version", Runtime.Version.class)));
366 var runtimeVersion = b.op(invoke(MethodRef.method(Runtime.Version.class, "feature", int.class), version));
367 IfOp ifop = if_(b.parentBody()).if_(c -> {
368 var p = c.op(neq(runtimeVersion, b.op(constant(INT, compiletimeVersion))));
369 c.op(core_yield(p));
370 }).then(t -> {
371 var s = "The Java version used at compile time to generate and store the code model, Java " + compiletimeVersion +
372 ", is not the same as the Java version used at runtime to load the code model, Java ";
373 var errMessage = t.op(concat(
374 t.op(constant(J_L_STRING, s)),
375 runtimeVersion
376 ));
377 t.op(throw_(
378 t.op(new_(MethodRef.constructor(UnsupportedOperationException.class, String.class), errMessage))
379 ));
380 }).else_();
381 b.op(ifop);
382 b.op(return_());
383 })
384 );
385 }
386
387 private static List<FuncOp> createExternTypeHelperFuncs(SequencedMap<ExternalizedTypeElement, List<Integer>> registeredExterTypes) {
388 /*
389 static private ExternalizedTypeElement $exType(int typeIndex) {
390 return switch(typeIndex) {
391 case 0 -> ExternalizedTypeElement.of("void");
392 case 1 -> ExternalizedTypeElement.of("java.type.primitive", exType(0));
393 default -> throw new IllegalStateException();
394 };
395 }
396 */
397 List<FuncOp> funcs = new ArrayList<>();
398 Iterator<Map.Entry<ExternalizedTypeElement, List<Integer>>> typesEnntryIterator = registeredExterTypes.sequencedEntrySet().iterator();
399 int methodCounter = 0;
400 do {
401 String followUpBuilderName = EXTER_TYPE_BUILDER_F_NAME + (methodCounter + 1);
402 funcs.add(func(EXTER_TYPE_BUILDER_F_NAME + (methodCounter > 0 ? methodCounter : ""), EXTER_TYPE_BUILDER_F_TYPE).body(b -> {
403 Block.Parameter i = b.parameter(INT);
404 List<Body.Builder> swBodies = new ArrayList<>();
405 for (int counter = 0; counter < TYPE_LIMIT && typesEnntryIterator.hasNext();) {
406 Map.Entry<ExternalizedTypeElement, List<Integer>> e = typesEnntryIterator.next();
407 counter += e.getValue().size();
408 Body.Builder l = Body.Builder.of(b.parentBody(), functionType(BOOLEAN));
409 Block.Parameter target = l.entryBlock().parameter(INT);
410 Integer typeIndex = e.getValue().getLast();
411 Result p = l.entryBlock().op(eq(target, l.entryBlock().op(constant(INT, typeIndex))));
412 l.entryBlock().op(core_yield(p));
413
414 Body.Builder expr = Body.Builder.of(b.parentBody(), EXTER_TYPE_BUILDER_F_TYPE);
415 List<Value> args = new ArrayList<>();
416 args.add(expr.entryBlock().op(constant(J_L_STRING, e.getKey().identifier())));
417 for (int j = 0; j < e.getValue().size() - 1; j++) {
418 Value index = expr.entryBlock().op(constant(INT, e.getValue().get(j)));
419 Result opr = expr.entryBlock().op(funcCall(EXTER_TYPE_BUILDER_F_NAME, EXTER_TYPE_BUILDER_F_TYPE, index));
420 args.add(opr);
421 }
422 MethodRef mr;
423 Result type;
424 if (e.getKey().arguments().size() < 5) {
425 List<Class<?>> params = new ArrayList<>();
426 params.add(String.class);
427 params.addAll(Collections.nCopies(e.getKey().arguments().size(), ExternalizedTypeElement.class));
428 mr = MethodRef.method(ExternalizedTypeElement.class, "of", ExternalizedTypeElement.class, params);
429 type = expr.entryBlock().op(invoke(mr, args));
430 } else {
431 type = expr.entryBlock().op(invoke(InvokeOp.InvokeKind.STATIC, true, EX_TYPE_ELEM, EX_TYPE_ELEM_OF_ARRAY, args));
432 }
433 expr.entryBlock().op(core_yield(type));
434
435 swBodies.add(l);
436 swBodies.add(expr);
437 }
438
439 // default case
440 Body.Builder dl = Body.Builder.of(b.parentBody(), functionType(BOOLEAN));
441 dl.entryBlock().parameter(INT);
442 dl.entryBlock().op(core_yield(dl.entryBlock().op(constant(BOOLEAN, true))));
443 Body.Builder de = Body.Builder.of(b.parentBody(), EXTER_TYPE_BUILDER_F_TYPE);
444 if (typesEnntryIterator.hasNext()) {
445 // forward to a follow-up builder method (we are over TYPE_LIMIT)
446 de.entryBlock().op(core_yield(de.entryBlock().op(funcCall(followUpBuilderName, EXTER_TYPE_BUILDER_F_TYPE, i))));
447 } else {
448 // throw
449 de.entryBlock().op(throw_(de.entryBlock().op(new_(MethodRef.constructor(IllegalStateException.class)))));
450 }
451 swBodies.add(dl);
452 swBodies.add(de);
453
454 var r = b.op(switchExpression(i, swBodies));
455 b.op(return_(r));
456 }));
457 methodCounter++;
458 } while (typesEnntryIterator.hasNext());
459 return funcs;
460 }
461
462 OpBuilder(SequencedMap<ExternalizedTypeElement, List<Integer>> registeredExternalizedTypes) {
463 this.valueMap = new HashMap<>();
464 this.blockMap = new HashMap<>();
465 this.typeElementMap = new HashMap<>();
466 this.registeredExternalizedTypes = registeredExternalizedTypes;
467
468 Body.Builder body = Body.Builder.of(null, BUILDER_F_TYPE);
469 this.builder = body.entryBlock();
470 }
471
472 FuncOp build(String name, Op op) {
473 Value ancestorBody = builder.op(constant(type(Body.Builder.class), null));
474 // check if java version at compile time matches the java version at runtime
475 builder.op(funcCall(JAVA_VERSION_CHECKER_F_NAME, FunctionType.FUNCTION_TYPE_VOID));
476 Value result = buildOp(null, ancestorBody, op);
477 // seal op
478 builder.op(invoke(MethodRef.method(Op.class, "seal", void.class), result));
479 builder.op(return_(result));
480
481 // return from lambdas on stack
482 while (!lambdaStack.isEmpty()) {
483 var lambdaBuilder = builder;
484 builder = lambdaStack.pop();
485 var l = builder.op(lambda(JavaType.parameterized(JavaType.type(Supplier.class), JavaType.type(Op.class)), lambdaBuilder.parentBody()));
486 builder.op(return_(builder.op(cast(JavaType.type(Op.class), builder.op(invoke(MethodRef.method(Supplier.class, "get", Object.class), l))))));
487 }
488
489 return func(name, builder.parentBody());
490 }
491
492
493 Value buildOp(Value blockBuilder, Value ancestorBody, Op inputOp) {
494 if (++opCounter == OP_LIMIT) {
495 // continue building in a lambda
496 opCounter = 0;
497 lambdaStack.push(builder);
498 builder = Body.Builder.of(builder.parentBody(), functionType(JavaType.type(Supplier.class))).entryBlock();
499 }
500 List<Value> bodies = new ArrayList<>();
501 for (Body inputBody : inputOp.bodies()) {
502 Value body = buildBody(ancestorBody, inputBody);
503 bodies.add(body);
504 }
505
506 List<Value> operands = new ArrayList<>();
507 for (Value inputOperand : inputOp.operands()) {
508 Value operand = valueMap.get(inputOperand);
509 operands.add(operand);
510 }
511
512 List<Value> successors = new ArrayList<>();
513 for (Block.Reference inputSuccessor : inputOp.successors()) {
514 List<Value> successorArgs = new ArrayList<>();
515 for (Value inputOperand : inputSuccessor.arguments()) {
516 Value operand = valueMap.get(inputOperand);
517 successorArgs.add(operand);
518 }
519 Value referencedBlock = blockMap.get(inputSuccessor.targetBlock());
520
521 List<Value> args = new ArrayList<>();
522 args.add(referencedBlock);
523 args.addAll(successorArgs);
524 Value successor = builder.op(invoke(
525 InvokeOp.InvokeKind.INSTANCE, true,
526 BLOCK_BUILDER_SUCCESSOR.type().returnType(),
527 BLOCK_BUILDER_SUCCESSOR, args));
528 successors.add(successor);
529 }
530
531 return buildOp(
532 blockBuilder,
533 inputOp,
534 inputOp.externalizeOpName(),
535 inputOp.location(),
536 operands,
537 successors,
538 inputOp.resultType(),
539 inputOp.externalize(),
540 bodies);
541 }
542
543 Value buildOp(Value blockBuilder,
544 Op inputOp,
545 String name,
546 Location location,
547 List<Value> operands,
548 List<Value> successors,
549 TypeElement resultType,
550 Map<String, Object> attributes,
551 List<Value> bodies) {
552
553 boolean bb = blockBuilder != null;
554 boolean simpleLoc = bb && location != null && location.sourceRef() == null;
555
556 List<Value> args = new ArrayList<>();
557 if (bb) {
558 args.add(blockBuilder);
559 }
560 args.add(builder.op(constant(J_L_STRING, name)));
561 if (simpleLoc) {
562 args.add(builder.op(constant(INT, location.line())));
563 args.add(builder.op(constant(INT, location.column())));
564 } else {
565 args.add(buildLocation(location));
566 }
567 args.add(buildFlexibleList(type(Value.class), operands));
568 args.add(buildFlexibleList(type(Block.Reference.class), successors));
569 args.add(builder.op(constant(INT, registerType(resultType.externalize()))));
570 args.add(buildAttributeMap(inputOp, attributes));
571 args.add(buildFlexibleList(type(Body.Builder.class), bodies));
572 return builder.op(bb ? simpleLoc ? funcCall(OP_BUILDER_F_NAME_3, OP_BUILDER_F_OVERRIDE_3, args)
573 : funcCall(OP_BUILDER_F_NAME_2, OP_BUILDER_F_OVERRIDE_2, args)
574 : funcCall(OP_BUILDER_F_NAME_1, OP_BUILDER_F_OVERRIDE_1, args));
575 }
576
577 Value buildFlexibleList(JavaType elementType, List<Value> elements) {
578 return switch (elements.size()) {
579 case 0 -> builder.op(constant(elementType, null));
580 case 1 -> elements.getFirst();
581 default -> buildList(elementType, elements);
582 };
583 }
584
585 Value buildLocation(Location l) {
586 if (l == null) {
587 return builder.op(constant(J_C_LOCATION, null));
588 } else {
589 return builder.op(new_(MethodRef.constructor(Location.class, String.class, int.class, int.class),
590 builder.op(constant(J_L_STRING, l.sourceRef())),
591 builder.op(constant(INT, l.line())),
592 builder.op(constant(INT, l.column()))));
593 }
594 }
595
596 Value buildBody(Value ancestorBodyValue, Body inputBody) {
597 Value yieldType = buildType(inputBody.yieldType());
598 Value bodyType = builder.op(invoke(
599 InvokeOp.InvokeKind.STATIC, true,
600 FUNCTION_TYPE_FUNCTION_TYPE.type().returnType(),
601 FUNCTION_TYPE_FUNCTION_TYPE, List.of(yieldType)));
602 Value body = builder.op(invoke(BODY_BUILDER_OF, ancestorBodyValue, bodyType));
603
604 Value entryBlock = null;
605 for (Block inputBlock : inputBody.blocks()) {
606 Value block;
607 if (inputBlock.isEntryBlock()) {
608 block = entryBlock = builder.op(invoke(BODY_BUILDER_ENTRY_BLOCK, body));
609 } else {
610 assert entryBlock != null;
611 block = builder.op(invoke(InvokeOp.InvokeKind.INSTANCE, true,
612 BLOCK_BUILDER_BLOCK.type().returnType(),
613 BLOCK_BUILDER_BLOCK, List.of(entryBlock)));
614 }
615 blockMap.put(inputBlock, block);
616
617 for (Block.Parameter inputP : inputBlock.parameters()) {
618 Value type = buildType(inputP.type());
619 Value blockParameter = builder.op(invoke(BLOCK_BUILDER_PARAMETER, block, type));
620 valueMap.put(inputP, blockParameter);
621 }
622 }
623
624 for (Block inputBlock : inputBody.blocks()) {
625 Value block = blockMap.get(inputBlock);
626 for (Op inputOp : inputBlock.ops()) {
627 valueMap.put(inputOp.result(), buildOp(block, body, inputOp));
628 }
629 }
630
631 return body;
632 }
633
634 private int registerType(ExternalizedTypeElement ete) {
635 if (!registeredExternalizedTypes.containsKey(ete)) {
636 List<Integer> values = new ArrayList<>();
637 for (ExternalizedTypeElement argument : ete.arguments()) {
638 values.add(registerType(argument));
639 }
640 values.add(registeredExternalizedTypes.size()); // insertion order of the new key
641 registeredExternalizedTypes.put(ete, values);
642 }
643 return registeredExternalizedTypes.get(ete).getLast(); // returns the insertion order of the key
644 }
645
646 Value buildType(TypeElement _t) {
647 return typeElementMap.computeIfAbsent(_t, t -> {
648 int typeIndex = registerType(_t.externalize());
649 Op.Result i = builder.op(constant(INT, typeIndex));
650 return builder.op(funcCall(TYPE_BUILDER_F_NAME, CoreType.functionType(type(TypeElement.class)), i));
651 });
652 }
653
654 Value buildAttributeMap(Op inputOp, Map<String, Object> attributes) {
655 if (attributes.isEmpty()) {
656 return builder.op(constant(type(Map.class), null));
657 }
658 if (attributes.size() == 1 && attributes.get("") instanceof Object o) {
659 return buildAttributeValue(o);
660 }
661 List<Value> keysAndValues = new ArrayList<>();
662 for (Map.Entry<String, Object> entry : attributes.entrySet()) {
663 Value key = builder.op(constant(J_L_STRING, entry.getKey()));
664 Value value = buildAttributeValue(entry.getValue());
665 keysAndValues.add(key);
666 keysAndValues.add(value);
667 }
668 return buildMap(J_L_STRING, J_L_OBJECT, keysAndValues);
669 }
670
671 private Value box(TypeElement to, Value v) {
672 return builder.op(invoke(MethodRef.method(to, "valueOf", to, v.type()), v));
673 }
674
675 Value buildAttributeValue(Object value) {
676 return switch (value) {
677 case Boolean _ ->
678 box(J_L_BOOLEAN, builder.op(constant(BOOLEAN, value)));
679 case Byte _ ->
680 box(J_L_BYTE, builder.op(constant(BYTE, value)));
681 case Short _ ->
682 box(J_L_SHORT, builder.op(constant(SHORT, value)));
683 case Character _ ->
684 box(J_L_CHARACTER, builder.op(constant(CHAR, value)));
685 case Integer _ ->
686 box(J_L_INTEGER, builder.op(constant(INT, value)));
687 case Long _ ->
688 box(J_L_LONG, builder.op(constant(LONG, value)));
689 case Float _ ->
690 box(J_L_FLOAT, builder.op(constant(FLOAT, value)));
691 case Double _ ->
692 box(J_L_DOUBLE, builder.op(constant(DOUBLE, value)));
693 case Class<?> v ->
694 buildType(JavaType.type(v));
695 case String s ->
696 builder.op(constant(J_L_STRING, value));
697 case TypeElement f ->
698 buildType(f);
699 case InvokeOp.InvokeKind ik -> {
700 FieldRef enumValueRef = FieldRef.field(InvokeOp.InvokeKind.class, ik.name(), InvokeOp.InvokeKind.class);
701 yield builder.op(fieldLoad(enumValueRef));
702 }
703 case Object o when value == ExternalizedOp.NULL_ATTRIBUTE_VALUE ->
704 builder.op(fieldLoad(FieldRef.field(ExternalizedOp.class,
705 "NULL_ATTRIBUTE_VALUE", Object.class)));
706 default -> {
707 // @@@ use the result of value.toString()?
708 throw new UnsupportedOperationException("Unsupported attribute value: " + value);
709 }
710 };
711 }
712
713
714 Value buildMap(JavaType keyType, JavaType valueType, List<Value> keysAndValues) {
715 JavaType mapType = parameterized(J_U_MAP, keyType, valueType);
716 if (keysAndValues.size() < 21) {
717 MethodRef mapOf = MethodRef.method(J_U_MAP, "of",
718 J_U_MAP, Collections.nCopies(keysAndValues.size(), J_L_OBJECT));
719 return builder.op(invoke(mapType, mapOf, keysAndValues));
720 } else {
721 JavaType mapEntryType = parameterized(J_U_MAP_ENTRY, keyType, valueType);
722 List<Value> elements = new ArrayList<>(keysAndValues.size() / 2);
723 for (int i = 0; i < keysAndValues.size(); i += 2) {
724 Value key = keysAndValues.get(i);
725 Value value = keysAndValues.get(i + 1);
726 Value entry = builder.op(invoke(mapEntryType, MAP_ENTRY, key, value));
727 elements.add(entry);
728 }
729 Value array = buildArray(mapEntryType, elements);
730 return builder.op(invoke(mapType, MAP_OF_ARRAY, array));
731 }
732 }
733
734
735 Value buildList(JavaType elementType, List<Value> elements) {
736 JavaType listType = parameterized(J_U_LIST, elementType);
737 if (elements.size() < 11) {
738 MethodRef listOf = MethodRef.method(J_U_LIST, "of",
739 J_U_LIST, Collections.nCopies(elements.size(), J_L_OBJECT));
740 return builder.op(invoke(listType, listOf, elements));
741 } else {
742 Value array = buildArray(elementType, elements);
743 return builder.op(invoke(listType, LIST_OF_ARRAY, array));
744 }
745 }
746
747
748 Value buildArray(JavaType elementType, List<Value> elements) {
749 Value array = builder.op(newArray(JavaType.array(elementType),
750 builder.op(constant(INT, elements.size()))));
751 for (int i = 0; i < elements.size(); i++) {
752 builder.op(arrayStoreOp(
753 array,
754 builder.op(constant(INT, i)),
755 elements.get(i)));
756 }
757 return array;
758 }
759 }