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 }