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