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 }