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