1 /*
  2  * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package jdk.incubator.code.internal;
 27 
 28 import jdk.incubator.code.*;
 29 import jdk.incubator.code.dialect.core.CoreType;
 30 import jdk.incubator.code.dialect.core.FunctionType;
 31 import jdk.incubator.code.dialect.java.*;
 32 import jdk.incubator.code.extern.*;
 33 
 34 import java.util.*;
 35 import java.util.function.Function;
 36 import java.util.stream.Stream;
 37 
 38 import static jdk.incubator.code.dialect.core.CoreOp.*;
 39 import static jdk.incubator.code.dialect.core.CoreType.functionType;
 40 import static jdk.incubator.code.dialect.java.JavaOp.*;
 41 import static jdk.incubator.code.dialect.java.JavaType.*;
 42 
 43 /**
 44  * A transformer of code models to models that build them.
 45  * <p>
 46  * A building code model when executed will construct the same code model it was transformed from.
 47  * Such a building code model could be transformed to bytecode and stored in class files.
 48  */
 49 public class OpBuilder {
 50 
 51     static final JavaType J_C_E_EXTERNALIZED_OP = type(ExternalizedOp.class);
 52 
 53     static final MethodRef DIALECT_FACTORY_OP_FACTORY = MethodRef.method(DialectFactory.class, "opFactory",
 54             OpFactory.class);
 55 
 56     static final MethodRef DIALECT_FACTORY_TYPE_ELEMENT_FACTORY = MethodRef.method(DialectFactory.class, "typeElementFactory",
 57             TypeElementFactory.class);
 58 
 59     static final MethodRef OP_FACTORY_CONSTRUCT = MethodRef.method(OpFactory.class, "constructOp",
 60             Op.class, ExternalizedOp.class);
 61 
 62     static final MethodRef TYPE_ELEMENT_FACTORY_CONSTRUCT = MethodRef.method(TypeElementFactory.class, "constructType",
 63             TypeElement.class, ExternalizedTypeElement.class);
 64 
 65     static final MethodRef BODY_BUILDER_OF = MethodRef.method(Body.Builder.class, "of",
 66             Body.Builder.class, Body.Builder.class, FunctionType.class);
 67 
 68     static final MethodRef BODY_BUILDER_ENTRY_BLOCK = MethodRef.method(Body.Builder.class, "entryBlock",
 69             Block.Builder.class);
 70 
 71     // instance varargs
 72     static final MethodRef BLOCK_BUILDER_SUCCESSOR = MethodRef.method(Block.Builder.class, "successor",
 73             Block.Reference.class, Value[].class);
 74 
 75     static final MethodRef BLOCK_BUILDER_OP = MethodRef.method(Block.Builder.class, "op",
 76             Op.Result.class, Op.class);
 77 
 78     // instance varargs
 79     static final MethodRef BLOCK_BUILDER_BLOCK = MethodRef.method(Block.Builder.class, "block",
 80             Block.Builder.class, TypeElement[].class);
 81 
 82     static final MethodRef BLOCK_BUILDER_PARAMETER = MethodRef.method(Block.Builder.class, "parameter",
 83             Block.Parameter.class, TypeElement.class);
 84 
 85     // static varargs
 86     static final MethodRef FUNCTION_TYPE_FUNCTION_TYPE = MethodRef.method(CoreType.class, "functionType",
 87             FunctionType.class, TypeElement.class, TypeElement[].class);
 88 
 89 
 90     static final JavaType J_U_LIST = type(List.class);
 91 
 92     static final MethodRef LIST_OF_ARRAY = MethodRef.method(J_U_LIST, "of",
 93             J_U_LIST, array(J_L_OBJECT, 1));
 94 
 95     static final JavaType J_U_MAP = type(Map.class);
 96 
 97     static final JavaType J_U_MAP_ENTRY = type(Map.Entry.class);
 98 
 99     static final MethodRef MAP_ENTRY = MethodRef.method(J_U_MAP, "entry",
100             J_U_MAP, J_L_OBJECT, J_L_OBJECT);
101 
102     static final MethodRef MAP_OF_ARRAY = MethodRef.method(J_U_MAP, "of",
103             J_U_MAP, array(J_U_MAP_ENTRY, 1));
104 
105 
106     static final JavaType EX_TYPE_ELEM = type(ExternalizedTypeElement.class);
107 
108     static final MethodRef EX_TYPE_ELEM_OF_LIST = MethodRef.method(EX_TYPE_ELEM, "of",
109             EX_TYPE_ELEM, J_L_STRING, J_U_LIST);
110 
111 
112     static final JavaType J_C_LOCATION = type(Location.class);
113 
114     static final MethodRef LOCATION_FROM_STRING = MethodRef.method(J_C_LOCATION, "fromString",
115             J_C_LOCATION, J_L_STRING);
116 
117     static final FunctionType EXTERNALIZED_OP_F_TYPE = functionType(
118             J_C_E_EXTERNALIZED_OP,
119             J_L_STRING,
120             J_C_LOCATION,
121             J_U_LIST,
122             J_U_LIST,
123             type(TypeElement.class),
124             J_U_MAP,
125             J_U_LIST);
126 
127     static final FunctionType BUILDER_F_TYPE = functionType(type(Op.class));
128 
129 
130     final Map<Value, Value> valueMap;
131 
132     final Map<Block, Value> blockMap;
133 
134     final Map<ExternalizedTypeElement, Value> exTypeElementMap;
135 
136     final Map<TypeElement, Value> typeElementMap;
137 
138     final Block.Builder builder;
139 
140     final Value dialectFactory;
141 
142     final Value opFactory;
143 
144     final Value typeElementFactory;
145 
146     /**
147      * Transform the given code model to one that builds it.
148      * <p>
149      * This method initially applies the function {@code dialectFactoryF} to
150      * the block builder that is used to build resulting code model. The result
151      * is a dialect factory value which is subsequently used to build operations
152      * that construct type elements and operations present in the given code model.
153      *
154      * @param op the code model.
155      * @param dialectFactoryF a function that builds code items to produce a dialect factory value.
156      * @return the building code model.
157      */
158     public static FuncOp createBuilderFunction(Op op, Function<Block.Builder, Value> dialectFactoryF) {
159         return new OpBuilder(dialectFactoryF).build(op);
160     }
161 
162     OpBuilder(Function<Block.Builder, Value> dialectFactoryF) {
163         this.valueMap = new HashMap<>();
164         this.blockMap = new HashMap<>();
165         this.exTypeElementMap = new HashMap<>();
166         this.typeElementMap = new HashMap<>();
167 
168         Body.Builder body = Body.Builder.of(null, BUILDER_F_TYPE);
169         this.builder = body.entryBlock();
170         this.dialectFactory = dialectFactoryF.apply(builder);
171         this.opFactory = builder.op(invoke(DIALECT_FACTORY_OP_FACTORY, dialectFactory));
172         this.typeElementFactory = builder.op(invoke(DIALECT_FACTORY_TYPE_ELEMENT_FACTORY, dialectFactory));
173     }
174 
175     FuncOp build(Op op) {
176         Value ancestorBody = builder.op(constant(type(Body.Builder.class), null));
177         Value result = buildOp(ancestorBody, op);
178         // seal op
179         builder.op(invoke(MethodRef.method(Op.class, "seal", void.class), result));
180         builder.op(return_(result));
181 
182         // @@@ avoid use of opName
183         return func("builder." + op.getClass().getName(), builder.parentBody());
184     }
185 
186 
187     Value buildOp(Value ancestorBody, Op inputOp) {
188         List<Value> bodies = new ArrayList<>();
189         for (Body inputBody : inputOp.bodies()) {
190             Value body = buildBody(ancestorBody, inputBody);
191             bodies.add(body);
192         }
193 
194         List<Value> operands = new ArrayList<>();
195         for (Value inputOperand : inputOp.operands()) {
196             Value operand = valueMap.get(inputOperand);
197             operands.add(operand);
198         }
199 
200         List<Value> successors = new ArrayList<>();
201         for (Block.Reference inputSuccessor : inputOp.successors()) {
202             List<Value> successorArgs = new ArrayList<>();
203             for (Value inputOperand : inputSuccessor.arguments()) {
204                 Value operand = valueMap.get(inputOperand);
205                 successorArgs.add(operand);
206             }
207             Value referencedBlock = blockMap.get(inputSuccessor.targetBlock());
208 
209             List<Value> args = new ArrayList<>();
210             args.add(referencedBlock);
211             args.addAll(successorArgs);
212             Value successor = builder.op(invoke(
213                     InvokeOp.InvokeKind.INSTANCE, true,
214                     BLOCK_BUILDER_SUCCESSOR.type().returnType(),
215                     BLOCK_BUILDER_SUCCESSOR, args));
216             successors.add(successor);
217         }
218 
219         Value opDef = buildOpDefinition(
220                 inputOp,
221                 inputOp.externalizeOpName(),
222                 inputOp.location(),
223                 operands,
224                 successors,
225                 inputOp.resultType(),
226                 inputOp.externalize(),
227                 bodies);
228         return builder.op(invoke(OP_FACTORY_CONSTRUCT, opFactory, opDef));
229     }
230 
231 
232     Value buildOpDefinition(Op inputOp,
233                             String name,
234                             Location location,
235                             List<Value> operands,
236                             List<Value> successors,
237                             TypeElement resultType,
238                             Map<String, Object> attributes,
239                             List<Value> bodies) {
240         List<Value> args = List.of(
241                 builder.op(constant(J_L_STRING, name)),
242                 buildLocation(location),
243                 buildList(type(Value.class), operands),
244                 buildList(type(Block.Reference.class), successors),
245                 buildType(resultType),
246                 buildAttributeMap(inputOp, attributes),
247                 buildList(type(Body.Builder.class), bodies));
248         return builder.op(new_(ConstructorRef.constructor(EXTERNALIZED_OP_F_TYPE), args));
249     }
250 
251     Value buildLocation(Location l) {
252         if (l == null) {
253             return builder.op(constant(J_C_LOCATION, null));
254         } else {
255             return builder.op(invoke(LOCATION_FROM_STRING,
256                     builder.op(constant(J_L_STRING, l.toString()))));
257         }
258     }
259 
260     Value buildBody(Value ancestorBodyValue, Body inputBody) {
261         Value yieldType = buildType(inputBody.yieldType());
262         Value bodyType = builder.op(invoke(
263                 InvokeOp.InvokeKind.STATIC, true,
264                 FUNCTION_TYPE_FUNCTION_TYPE.type().returnType(),
265                 FUNCTION_TYPE_FUNCTION_TYPE, List.of(yieldType)));
266         Value body = builder.op(invoke(BODY_BUILDER_OF, ancestorBodyValue, bodyType));
267 
268         Value entryBlock = null;
269         for (Block inputBlock : inputBody.blocks()) {
270             Value block;
271             if (inputBlock.isEntryBlock()) {
272                 block = entryBlock = builder.op(invoke(BODY_BUILDER_ENTRY_BLOCK, body));
273             } else {
274                 assert entryBlock != null;
275                 block = builder.op(invoke(InvokeOp.InvokeKind.INSTANCE, true,
276                         BLOCK_BUILDER_BLOCK.type().returnType(),
277                         BLOCK_BUILDER_BLOCK, List.of(entryBlock)));
278             }
279             blockMap.put(inputBlock, block);
280 
281             for (Block.Parameter inputP : inputBlock.parameters()) {
282                 Value type = buildType(inputP.type());
283                 Value blockParameter = builder.op(invoke(BLOCK_BUILDER_PARAMETER, block, type));
284                 valueMap.put(inputP, blockParameter);
285             }
286         }
287 
288         for (Block inputBlock : inputBody.blocks()) {
289             Value block = blockMap.get(inputBlock);
290             for (Op inputOp : inputBlock.ops()) {
291                 Value op = buildOp(body, inputOp);
292                 Value result = builder.op(invoke(BLOCK_BUILDER_OP, block, op));
293                 valueMap.put(inputOp.result(), result);
294             }
295         }
296 
297         return body;
298     }
299 
300     Value buildType(TypeElement _t) {
301         return typeElementMap.computeIfAbsent(_t, t -> {
302             Value exTypeElem = buildExternalizedType(t.externalize());
303             return builder.op(invoke(TYPE_ELEMENT_FACTORY_CONSTRUCT, typeElementFactory, exTypeElem));
304         });
305     }
306 
307     Value buildExternalizedType(ExternalizedTypeElement e) {
308         // Cannot use computeIfAbsent due to recursion
309         if (exTypeElementMap.get(e) instanceof Value v) {
310             return v;
311         }
312 
313         List<Value> arguments = new ArrayList<>();
314         for (ExternalizedTypeElement a : e.arguments()) {
315             arguments.add(buildExternalizedType(a));
316         }
317 
318         Value identifier = builder.op(constant(J_L_STRING, e.identifier()));
319         Value ve;
320         if (e.arguments().size() < 5) {
321             MethodRef elemOf = MethodRef.method(EX_TYPE_ELEM, "of",
322                     EX_TYPE_ELEM, Stream.concat(Stream.of(J_L_STRING),
323                             Collections.nCopies(e.arguments().size(), EX_TYPE_ELEM).stream()).toList());
324             arguments.addFirst(identifier);
325             ve = builder.op(invoke(elemOf, arguments));
326         } else {
327             Value list = buildList(EX_TYPE_ELEM, arguments);
328             ve = builder.op(invoke(EX_TYPE_ELEM_OF_LIST, identifier, list));
329         }
330 
331         exTypeElementMap.put(e, ve);
332         return ve;
333     }
334 
335     Value buildAttributeMap(Op inputOp, Map<String, Object> attributes) {
336         List<Value> keysAndValues = new ArrayList<>();
337         for (Map.Entry<String, Object> entry : attributes.entrySet()) {
338             Value key = builder.op(constant(J_L_STRING, entry.getKey()));
339             Value value = buildAttributeValue(entry.getValue());
340             keysAndValues.add(key);
341             keysAndValues.add(value);
342         }
343         return buildMap(J_L_STRING, J_L_OBJECT, keysAndValues);
344     }
345 
346     Value buildAttributeValue(Object value) {
347         return switch (value) {
348             case Boolean v -> {
349                 yield builder.op(constant(BOOLEAN, value));
350             }
351             case Byte v -> {
352                 yield builder.op(constant(BYTE, value));
353             }
354             case Short v -> {
355                 yield builder.op(constant(SHORT, value));
356             }
357             case Character v -> {
358                 yield builder.op(constant(CHAR, value));
359             }
360             case Integer v -> {
361                 yield builder.op(constant(INT, value));
362             }
363             case Long v -> {
364                 yield builder.op(constant(LONG, value));
365             }
366             case Float v -> {
367                 yield builder.op(constant(FLOAT, value));
368             }
369             case Double v -> {
370                 yield builder.op(constant(DOUBLE, value));
371             }
372             case Class<?> v -> {
373                 yield buildType(JavaType.type(v));
374             }
375             case String s -> {
376                 yield builder.op(constant(J_L_STRING, value));
377             }
378             case TypeElement f -> {
379                 yield buildType(f);
380             }
381             case InvokeOp.InvokeKind ik -> {
382                 FieldRef enumValueRef = FieldRef.field(InvokeOp.InvokeKind.class, ik.name(), InvokeOp.InvokeKind.class);
383                 yield builder.op(fieldLoad(enumValueRef));
384             }
385             case Object o when value == ExternalizedOp.NULL_ATTRIBUTE_VALUE -> {
386                 yield builder.op(fieldLoad(FieldRef.field(ExternalizedOp.class,
387                         "NULL_ATTRIBUTE_VALUE", Object.class)));
388             }
389             default -> {
390                 // @@@ use the result of value.toString()?
391                 throw new UnsupportedOperationException("Unsupported attribute value: " + value);
392             }
393         };
394     }
395 
396 
397     Value buildMap(JavaType keyType, JavaType valueType, List<Value> keysAndValues) {
398         JavaType mapType = parameterized(J_U_MAP, keyType, valueType);
399         if (keysAndValues.size() < 21) {
400             MethodRef mapOf = MethodRef.method(J_U_MAP, "of",
401                     J_U_MAP, Collections.nCopies(keysAndValues.size(), J_L_OBJECT));
402             return builder.op(invoke(mapType, mapOf, keysAndValues));
403         } else {
404             JavaType mapEntryType = parameterized(J_U_MAP_ENTRY, keyType, valueType);
405             List<Value> elements = new ArrayList<>(keysAndValues.size() / 2);
406             for (int i = 0; i < keysAndValues.size(); i += 2) {
407                 Value key = keysAndValues.get(i);
408                 Value value = keysAndValues.get(i + 1);
409                 Value entry = builder.op(invoke(mapEntryType, MAP_ENTRY, key, value));
410                 elements.add(entry);
411             }
412             Value array = buildArray(mapEntryType, elements);
413             return builder.op(invoke(mapType, MAP_OF_ARRAY, array));
414         }
415     }
416 
417 
418     Value buildList(JavaType elementType, List<Value> elements) {
419         JavaType listType = parameterized(J_U_LIST, elementType);
420         if (elements.size() < 11) {
421             MethodRef listOf = MethodRef.method(J_U_LIST, "of",
422                     J_U_LIST, Collections.nCopies(elements.size(), J_L_OBJECT));
423             return builder.op(invoke(listType, listOf, elements));
424         } else {
425             Value array = buildArray(elementType, elements);
426             return builder.op(invoke(listType, LIST_OF_ARRAY, array));
427         }
428     }
429 
430 
431     Value buildArray(JavaType elementType, List<Value> elements) {
432         Value array = builder.op(newArray(JavaType.array(elementType),
433                 builder.op(constant(INT, elements.size()))));
434         for (int i = 0; i < elements.size(); i++) {
435             builder.op(arrayStoreOp(
436                     array,
437                     builder.op(constant(INT, i)),
438                     elements.get(i)));
439         }
440         return array;
441     }
442 }