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.writer;
 27 
 28 import jdk.incubator.code.*;
 29 import jdk.incubator.code.op.OpFactory;
 30 import jdk.incubator.code.op.ExternalizableOp;
 31 import jdk.incubator.code.type.*;
 32 import java.util.*;
 33 import java.util.stream.Stream;
 34 
 35 import static jdk.incubator.code.op.CoreOp.*;
 36 import static jdk.incubator.code.type.FunctionType.functionType;
 37 import static jdk.incubator.code.type.JavaType.*;
 38 
 39 /**
 40  * A transformer of code models to models that build them.
 41  * <p>
 42  * A building code model when executed will construct the same code model it was transformed from.
 43  * Such a building code model could be transformed to bytecode and stored in class files.
 44  */
 45 public class OpBuilder {
 46 
 47     static final JavaType J_C_O_EXTERNALIZED_OP = type(ExternalizableOp.ExternalizedOp.class);
 48 
 49     static final MethodRef OP_FACTORY_CONSTRUCT = MethodRef.method(OpFactory.class, "constructOp",
 50             Op.class, ExternalizableOp.ExternalizedOp.class);
 51 
 52     static final MethodRef TYPE_ELEMENT_FACTORY_CONSTRUCT = MethodRef.method(TypeElementFactory.class, "constructType",
 53             TypeElement.class, ExternalizedTypeElement.class);
 54 
 55     static final MethodRef BODY_BUILDER_OF = MethodRef.method(Body.Builder.class, "of",
 56             Body.Builder.class, Body.Builder.class, FunctionType.class);
 57 
 58     static final MethodRef BODY_BUILDER_ENTRY_BLOCK = MethodRef.method(Body.Builder.class, "entryBlock",
 59             Block.Builder.class);
 60 
 61     // instance varargs
 62     static final MethodRef BLOCK_BUILDER_SUCCESSOR = MethodRef.method(Block.Builder.class, "successor",
 63             Block.Reference.class, Value[].class);
 64 
 65     static final MethodRef BLOCK_BUILDER_OP = MethodRef.method(Block.Builder.class, "op",
 66             Op.Result.class, Op.class);
 67 
 68     // instance varargs
 69     static final MethodRef BLOCK_BUILDER_BLOCK = MethodRef.method(Block.Builder.class, "block",
 70             Block.Builder.class, TypeElement[].class);
 71 
 72     static final MethodRef BLOCK_BUILDER_PARAMETER = MethodRef.method(Block.Builder.class, "parameter",
 73             Block.Parameter.class, TypeElement.class);
 74 
 75     // static varargs
 76     static final MethodRef FUNCTION_TYPE_FUNCTION_TYPE = MethodRef.method(FunctionType.class, "functionType",
 77             FunctionType.class, TypeElement.class, TypeElement[].class);
 78 
 79 
 80     static final JavaType J_U_LIST = type(List.class);
 81 
 82     static final MethodRef LIST_OF_ARRAY = MethodRef.method(J_U_LIST, "of",
 83             J_U_LIST, array(J_L_OBJECT, 1));
 84 
 85     static final JavaType J_U_MAP = type(Map.class);
 86 
 87     static final JavaType J_U_MAP_ENTRY = type(Map.Entry.class);
 88 
 89     static final MethodRef MAP_ENTRY = MethodRef.method(J_U_MAP, "entry",
 90             J_U_MAP, J_L_OBJECT, J_L_OBJECT);
 91 
 92     static final MethodRef MAP_OF_ARRAY = MethodRef.method(J_U_MAP, "of",
 93             J_U_MAP, array(J_U_MAP_ENTRY, 1));
 94 
 95 
 96     static final JavaType EX_TYPE_ELEM = type(ExternalizedTypeElement.class);
 97 
 98     static final MethodRef EX_TYPE_ELEM_OF_LIST = MethodRef.method(EX_TYPE_ELEM, "of",
 99             EX_TYPE_ELEM, J_L_STRING, J_U_LIST);
100 
101 
102     static final FunctionType EXTERNALIZED_OP_F_TYPE = functionType(
103             J_C_O_EXTERNALIZED_OP,
104             J_L_STRING,
105             J_U_LIST,
106             J_U_LIST,
107             type(TypeElement.class),
108             J_U_MAP,
109             J_U_LIST);
110 
111     static final FunctionType BUILDER_F_TYPE = functionType(type(Op.class),
112             type(OpFactory.class),
113             type(TypeElementFactory.class));
114 
115 
116     Map<Value, Value> valueMap;
117 
118     Map<Block, Value> blockMap;
119 
120     Map<ExternalizedTypeElement, Value> exTypeElementMap;
121 
122     Map<TypeElement, Value> typeElementMap;
123 
124     Block.Builder builder;
125 
126     Value opFactory;
127 
128     Value typeElementFactory;
129 
130     /**
131      * Transform the given code model to one that builds it.
132      *
133      * @param op the code model
134      * @return the building code model.
135      */
136     public static FuncOp createBuilderFunction(Op op) {
137         return new OpBuilder().build(op);
138     }
139 
140     OpBuilder() {
141         this.valueMap = new HashMap<>();
142         this.blockMap = new HashMap<>();
143         this.exTypeElementMap = new HashMap<>();
144         this.typeElementMap = new HashMap<>();
145     }
146 
147     FuncOp build(Op op) {
148         Body.Builder body = Body.Builder.of(null, BUILDER_F_TYPE);
149 
150         builder = body.entryBlock();
151         opFactory = builder.parameters().get(0);
152         typeElementFactory = builder.parameters().get(1);
153 
154         Value ancestorBody = builder.op(constant(type(Body.Builder.class), null));
155         Value result = buildOp(ancestorBody, op);
156         builder.op(_return(result));
157 
158         return func("builder." + op.opName(), body);
159     }
160 
161 
162     Value buildOp(Value ancestorBody, Op inputOp) {
163         List<Value> bodies = new ArrayList<>();
164         for (Body inputBody : inputOp.bodies()) {
165             Value body = buildBody(ancestorBody, inputBody);
166             bodies.add(body);
167         }
168 
169         List<Value> operands = new ArrayList<>();
170         for (Value inputOperand : inputOp.operands()) {
171             Value operand = valueMap.get(inputOperand);
172             operands.add(operand);
173         }
174 
175         List<Value> successors = new ArrayList<>();
176         for (Block.Reference inputSuccessor : inputOp.successors()) {
177             List<Value> successorArgs = new ArrayList<>();
178             for (Value inputOperand : inputSuccessor.arguments()) {
179                 Value operand = valueMap.get(inputOperand);
180                 successorArgs.add(operand);
181             }
182             Value referencedBlock = blockMap.get(inputSuccessor.targetBlock());
183 
184             List<Value> args = new ArrayList<>();
185             args.add(referencedBlock);
186             args.addAll(successorArgs);
187             Value successor = builder.op(invoke(
188                     InvokeOp.InvokeKind.INSTANCE, true,
189                     BLOCK_BUILDER_SUCCESSOR.type().returnType(),
190                     BLOCK_BUILDER_SUCCESSOR, args));
191             successors.add(successor);
192         }
193 
194         Value opDef = buildOpDefinition(
195                 inputOp.opName(),
196                 operands,
197                 successors,
198                 inputOp.resultType(),
199                 inputOp instanceof ExternalizableOp exop ? exop.attributes() : Map.of(),
200                 bodies);
201         return builder.op(invoke(OP_FACTORY_CONSTRUCT, opFactory, opDef));
202     }
203 
204 
205     Value buildOpDefinition(String name,
206                             List<Value> operands,
207                             List<Value> successors,
208                             TypeElement resultType,
209                             Map<String, Object> attributes,
210                             List<Value> bodies) {
211         List<Value> args = List.of(
212                 builder.op(constant(J_L_STRING, name)),
213                 buildList(type(Value.class), operands),
214                 buildList(type(Block.Reference.class), successors),
215                 buildType(resultType),
216                 buildAttributeMap(attributes),
217                 buildList(type(Body.Builder.class), bodies));
218         return builder.op(_new(ConstructorRef.constructor(EXTERNALIZED_OP_F_TYPE), args));
219     }
220 
221     Value buildBody(Value ancestorBodyValue, Body inputBody) {
222         Value yieldType = buildType(inputBody.yieldType());
223         Value bodyType = builder.op(invoke(
224                 InvokeOp.InvokeKind.STATIC, true,
225                 FUNCTION_TYPE_FUNCTION_TYPE.type().returnType(),
226                 FUNCTION_TYPE_FUNCTION_TYPE, List.of(yieldType)));
227         Value body = builder.op(invoke(BODY_BUILDER_OF, ancestorBodyValue, bodyType));
228 
229         Value entryBlock = null;
230         for (Block inputBlock : inputBody.blocks()) {
231             Value block;
232             if (inputBlock.isEntryBlock()) {
233                 block = entryBlock = builder.op(invoke(BODY_BUILDER_ENTRY_BLOCK, body));
234             } else {
235                 assert entryBlock != null;
236                 block = builder.op(invoke(InvokeOp.InvokeKind.INSTANCE, true,
237                         BLOCK_BUILDER_BLOCK.type().returnType(),
238                         BLOCK_BUILDER_BLOCK, List.of(entryBlock)));
239             }
240             blockMap.put(inputBlock, block);
241 
242             for (Block.Parameter inputP : inputBlock.parameters()) {
243                 Value type = buildType(inputP.type());
244                 Value blockParameter = builder.op(invoke(BLOCK_BUILDER_PARAMETER, block, type));
245                 valueMap.put(inputP, blockParameter);
246             }
247         }
248 
249         for (Block inputBlock : inputBody.blocks()) {
250             Value block = blockMap.get(inputBlock);
251             for (Op inputOp : inputBlock.ops()) {
252                 Value op = buildOp(body, inputOp);
253                 Value result = builder.op(invoke(BLOCK_BUILDER_OP, block, op));
254                 valueMap.put(inputOp.result(), result);
255             }
256         }
257 
258         return body;
259     }
260 
261     Value buildType(TypeElement _t) {
262         return typeElementMap.computeIfAbsent(_t, t -> {
263             Value exTypeElem = buildExternalizedType(t.externalize());
264             return builder.op(invoke(TYPE_ELEMENT_FACTORY_CONSTRUCT, typeElementFactory, exTypeElem));
265         });
266     }
267 
268     Value buildExternalizedType(ExternalizedTypeElement e) {
269         // Cannot use computeIfAbsent due to recursion
270         if (exTypeElementMap.get(e) instanceof Value v) {
271             return v;
272         }
273 
274         List<Value> arguments = new ArrayList<>();
275         for (ExternalizedTypeElement a : e.arguments()) {
276             arguments.add(buildExternalizedType(a));
277         }
278 
279         Value identifier = builder.op(constant(J_L_STRING, e.identifier()));
280         Value ve;
281         if (e.arguments().size() < 5) {
282             MethodRef elemOf = MethodRef.method(EX_TYPE_ELEM, "of",
283                     EX_TYPE_ELEM, Stream.concat(Stream.of(J_L_STRING),
284                             Collections.nCopies(e.arguments().size(), EX_TYPE_ELEM).stream()).toList());
285             arguments.addFirst(identifier);
286             ve = builder.op(invoke(elemOf, arguments));
287         } else {
288             Value list = buildList(EX_TYPE_ELEM, arguments);
289             ve = builder.op(invoke(EX_TYPE_ELEM_OF_LIST, identifier, list));
290         }
291 
292         exTypeElementMap.put(e, ve);
293         return ve;
294     }
295 
296     Value buildAttributeMap(Map<String, Object> attributes) {
297         List<Value> keysAndValues = new ArrayList<>();
298         for (Map.Entry<String, Object> entry : attributes.entrySet()) {
299             Value key = builder.op(constant(J_L_STRING, entry.getKey()));
300             Value value = buildAttributeValue(entry.getValue());
301             keysAndValues.add(key);
302             keysAndValues.add(value);
303         }
304         return buildMap(J_L_STRING, J_L_OBJECT, keysAndValues);
305     }
306 
307     Value buildAttributeValue(Object value) {
308         return switch (value) {
309             case Boolean v -> {
310                 yield builder.op(constant(BOOLEAN, value));
311             }
312             case Byte v -> {
313                 yield builder.op(constant(BYTE, value));
314             }
315             case Short v -> {
316                 yield builder.op(constant(SHORT, value));
317             }
318             case Character v -> {
319                 yield builder.op(constant(CHAR, value));
320             }
321             case Integer v -> {
322                 yield builder.op(constant(INT, value));
323             }
324             case Long v -> {
325                 yield builder.op(constant(LONG, value));
326             }
327             case Float v -> {
328                 yield builder.op(constant(FLOAT, value));
329             }
330             case Double v -> {
331                 yield builder.op(constant(DOUBLE, value));
332             }
333             case Class<?> v -> {
334                 yield buildType(JavaType.type(v));
335             }
336             case String s -> {
337                 yield builder.op(constant(J_L_STRING, value));
338             }
339             case TypeElement f -> {
340                 yield buildType(f);
341             }
342             case Location l -> {
343                 // @@@ Construct location explicitly
344                 yield builder.op(constant(J_L_STRING, l.toString()));
345             }
346             case InvokeOp.InvokeKind ik -> {
347                 FieldRef enumValueRef = FieldRef.field(InvokeOp.InvokeKind.class, ik.name(), InvokeOp.InvokeKind.class);
348                 yield builder.op(fieldLoad(enumValueRef));
349             }
350             case Object o when value == ExternalizableOp.NULL_ATTRIBUTE_VALUE -> {
351                 yield builder.op(fieldLoad(FieldRef.field(ExternalizableOp.class,
352                         "NULL_ATTRIBUTE_VALUE", Object.class)));
353             }
354             default -> {
355                 // @@@ use the result of value.toString()?
356                 throw new UnsupportedOperationException("Unsupported attribute value: " + value);
357             }
358         };
359     }
360 
361 
362     Value buildMap(JavaType keyType, JavaType valueType, List<Value> keysAndValues) {
363         JavaType mapType = parameterized(J_U_MAP, keyType, valueType);
364         if (keysAndValues.size() < 21) {
365             MethodRef mapOf = MethodRef.method(J_U_MAP, "of",
366                     J_U_MAP, Collections.nCopies(keysAndValues.size(), J_L_OBJECT));
367             return builder.op(invoke(mapType, mapOf, keysAndValues));
368         } else {
369             JavaType mapEntryType = parameterized(J_U_MAP_ENTRY, keyType, valueType);
370             List<Value> elements = new ArrayList<>(keysAndValues.size() / 2);
371             for (int i = 0; i < keysAndValues.size(); i += 2) {
372                 Value key = keysAndValues.get(i);
373                 Value value = keysAndValues.get(i + 1);
374                 Value entry = builder.op(invoke(mapEntryType, MAP_ENTRY, key, value));
375                 elements.add(entry);
376             }
377             Value array = buildArray(mapEntryType, elements);
378             return builder.op(invoke(mapType, MAP_OF_ARRAY, array));
379         }
380     }
381 
382 
383     Value buildList(JavaType elementType, List<Value> elements) {
384         JavaType listType = parameterized(J_U_LIST, elementType);
385         if (elements.size() < 11) {
386             MethodRef listOf = MethodRef.method(J_U_LIST, "of",
387                     J_U_LIST, Collections.nCopies(elements.size(), J_L_OBJECT));
388             return builder.op(invoke(listType, listOf, elements));
389         } else {
390             Value array = buildArray(elementType, elements);
391             return builder.op(invoke(listType, LIST_OF_ARRAY, array));
392         }
393     }
394 
395 
396     Value buildArray(JavaType elementType, List<Value> elements) {
397         Value array = builder.op(newArray(JavaType.array(elementType),
398                 builder.op(constant(INT, elements.size()))));
399         for (int i = 0; i < elements.size(); i++) {
400             builder.op(arrayStoreOp(
401                     array,
402                     builder.op(constant(INT, i)),
403                     elements.get(i)));
404         }
405         return array;
406     }
407 }