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 }