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