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 }