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 FIELD_REF_OF_STRING = MethodRef.method(FieldRef.class, "ofString", 85 FieldRef.class, String.class); 86 87 static final MethodRef RECORD_TYPE_REF_OF_STRING = MethodRef.method(RecordTypeRef.class, "ofString", 88 RecordTypeRef.class, String.class); 89 90 91 static final JavaType J_U_LIST = type(List.class); 92 93 static final MethodRef LIST_OF_ARRAY = MethodRef.method(J_U_LIST, "of", 94 J_U_LIST, array(J_L_OBJECT, 1)); 95 96 static final JavaType J_U_MAP = type(Map.class); 97 98 static final JavaType J_U_HASH_MAP = type(HashMap.class); 99 100 static final JavaType J_U_MAP_ENTRY = type(Map.Entry.class); 101 102 static final MethodRef MAP_ENTRY = MethodRef.method(J_U_MAP, "entry", 103 J_U_MAP, J_L_OBJECT, J_L_OBJECT); 104 105 static final MethodRef MAP_OF = MethodRef.method(J_U_MAP, "of", 106 J_U_MAP); 107 108 static final MethodRef MAP_OF_ARRAY = MethodRef.method(J_U_MAP, "of", 109 J_U_MAP, array(J_U_MAP_ENTRY, 1)); 110 111 static final MethodRef MAP_PUT = MethodRef.method(J_U_MAP, "put", 112 J_L_OBJECT, J_L_OBJECT, J_L_OBJECT); 113 114 115 static final FunctionType EXTERNALIZED_OP_F_TYPE = functionType( 116 J_C_O_EXTERNALIZED_OP, 117 J_L_STRING, 118 J_U_LIST, 119 J_U_LIST, 120 type(TypeElement.class), 121 J_U_MAP, 122 J_U_LIST); 123 124 static final FunctionType BUILDER_F_TYPE = functionType(type(Op.class), 125 type(OpFactory.class), 126 type(TypeElementFactory.class)); 127 128 129 Map<Value, Value> valueMap; 130 131 Map<Block, Value> blockMap; 132 133 Block.Builder builder; 134 135 Value opFactory; 136 137 Value typeElementFactory; 138 139 /** 140 * Transform the given code model to one that builds it. 141 * 142 * @param op the code model 143 * @return the building code model. 144 */ 145 public static FuncOp createBuilderFunction(Op op) { 146 return new OpBuilder().build(op); 147 } 148 149 OpBuilder() { 150 this.valueMap = new HashMap<>(); 151 this.blockMap = new HashMap<>(); 152 } 153 154 FuncOp build(Op op) { 155 Body.Builder body = Body.Builder.of(null, BUILDER_F_TYPE); 156 157 builder = body.entryBlock(); 158 opFactory = builder.parameters().get(0); 159 typeElementFactory = builder.parameters().get(1); 160 161 Value ancestorBody = builder.op(constant(type(Body.Builder.class), null)); 162 Value result = buildOp(ancestorBody, op); 163 builder.op(_return(result)); 164 165 return func("builder." + op.opName(), body); 166 } 167 168 169 Value buildOp(Value ancestorBody, Op inputOp) { 170 List<Value> bodies = new ArrayList<>(); 171 for (Body inputBody : inputOp.bodies()) { 172 Value body = buildBody(ancestorBody, inputBody); 173 bodies.add(body); 174 } 175 176 List<Value> operands = new ArrayList<>(); 177 for (Value inputOperand : inputOp.operands()) { 178 Value operand = valueMap.get(inputOperand); 179 operands.add(operand); 180 } 181 182 List<Value> successors = new ArrayList<>(); 183 for (Block.Reference inputSuccessor : inputOp.successors()) { 184 List<Value> successorArgs = new ArrayList<>(); 185 for (Value inputOperand : inputSuccessor.arguments()) { 186 Value operand = valueMap.get(inputOperand); 187 successorArgs.add(operand); 188 } 189 Value referencedBlock = blockMap.get(inputSuccessor.targetBlock()); 190 191 List<Value> args = new ArrayList<>(); 192 args.add(referencedBlock); 193 args.addAll(successorArgs); 194 Value successor = builder.op(invoke( 195 InvokeOp.InvokeKind.INSTANCE, true, 196 BLOCK_BUILDER_SUCCESSOR.type().returnType(), 197 BLOCK_BUILDER_SUCCESSOR, args)); 198 successors.add(successor); 199 } 200 201 Value opDef = buildOpDefinition( 202 inputOp.opName(), 203 operands, 204 successors, 205 inputOp.resultType(), 206 inputOp instanceof ExternalizableOp exop ? exop.attributes() : Map.of(), 207 bodies); 208 return builder.op(invoke(OP_FACTORY_CONSTRUCT, opFactory, opDef)); 209 } 210 211 212 Value buildOpDefinition(String name, 213 List<Value> operands, 214 List<Value> successors, 215 TypeElement resultType, 216 Map<String, Object> attributes, 217 List<Value> bodies) { 218 List<Value> args = List.of( 219 builder.op(constant(J_L_STRING, name)), 220 buildList(type(Value.class), operands), 221 buildList(type(Block.Reference.class), successors), 222 buildType(resultType), 223 buildAttributeMap(attributes), 224 buildList(type(Body.Builder.class), bodies)); 225 return builder.op(_new(EXTERNALIZED_OP_F_TYPE, args)); 226 } 227 228 Value buildBody(Value ancestorBodyValue, Body inputBody) { 229 Value yieldType = buildType(inputBody.yieldType()); 230 Value bodyType = builder.op(invoke( 231 InvokeOp.InvokeKind.STATIC, true, 232 FUNCTION_TYPE_FUNCTION_TYPE.type().returnType(), 233 FUNCTION_TYPE_FUNCTION_TYPE, List.of(yieldType))); 234 Value body = builder.op(invoke(BODY_BUILDER_OF, ancestorBodyValue, bodyType)); 235 236 Value entryBlock = null; 237 for (Block inputBlock : inputBody.blocks()) { 238 Value block; 239 if (inputBlock.isEntryBlock()) { 240 block = entryBlock = builder.op(invoke(BODY_BUILDER_ENTRY_BLOCK, body)); 241 } else { 242 assert entryBlock != null; 243 block = builder.op(invoke(InvokeOp.InvokeKind.INSTANCE, true, 244 BLOCK_BUILDER_BLOCK.type().returnType(), 245 BLOCK_BUILDER_BLOCK, List.of(entryBlock))); 246 } 247 blockMap.put(inputBlock, block); 248 249 for (Block.Parameter inputP : inputBlock.parameters()) { 250 Value type = buildType(inputP.type()); 251 Value blockParameter = builder.op(invoke(BLOCK_BUILDER_PARAMETER, block, type)); 252 valueMap.put(inputP, blockParameter); 253 } 254 } 255 256 for (Block inputBlock : inputBody.blocks()) { 257 Value block = blockMap.get(inputBlock); 258 for (Op inputOp : inputBlock.ops()) { 259 Value op = buildOp(body, inputOp); 260 Value result = builder.op(invoke(BLOCK_BUILDER_OP, block, op)); 261 valueMap.put(inputOp.result(), result); 262 } 263 } 264 265 return body; 266 } 267 268 Value buildType(TypeElement t) { 269 Value typeString = builder.op(constant(J_L_STRING, t.toString())); 270 Value exTypeElem = builder.op(invoke(EX_TYPE_ELEMENT_OF_STRING, typeString)); 271 return builder.op(invoke(TYPE_ELEMENT_FACTORY_CONSTRUCT, typeElementFactory, exTypeElem)); 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 MethodRef r -> { 318 Value string = builder.op(constant(J_L_STRING, value.toString())); 319 yield builder.op(invoke(METHOD_REF_OF_STRING, string)); 320 } 321 case FieldRef r -> { 322 Value string = builder.op(constant(J_L_STRING, value.toString())); 323 yield builder.op(invoke(FIELD_REF_OF_STRING, string)); 324 } 325 case RecordTypeRef r -> { 326 Value string = builder.op(constant(J_L_STRING, value.toString())); 327 yield builder.op(invoke(RECORD_TYPE_REF_OF_STRING, string)); 328 } 329 case TypeElement f -> { 330 yield buildType(f); 331 } 332 case Location l -> { 333 // @@@ Construct location explicitly 334 yield builder.op(constant(J_L_STRING, l.toString())); 335 } 336 case InvokeOp.InvokeKind ik -> { 337 FieldRef enumValueRef = FieldRef.field(InvokeOp.InvokeKind.class, ik.name(), InvokeOp.InvokeKind.class); 338 yield builder.op(fieldLoad(enumValueRef)); 339 } 340 case Object o when value == ExternalizableOp.NULL_ATTRIBUTE_VALUE -> { 341 yield builder.op(fieldLoad(FieldRef.field(ExternalizableOp.class, 342 "NULL_ATTRIBUTE_VALUE", Object.class))); 343 } 344 default -> { 345 // @@@ use the result of value.toString()? 346 throw new UnsupportedOperationException("Unsupported attribute value: " + value); 347 } 348 }; 349 } 350 351 352 Value buildMap(JavaType keyType, JavaType valueType, List<Value> keysAndValues) { 353 JavaType mapType = parameterized(J_U_MAP, keyType, valueType); 354 if (keysAndValues.isEmpty()) { 355 return builder.op(invoke(MAP_OF)); 356 } else { 357 Value map = builder.op(_new(mapType, functionType(J_U_HASH_MAP))); 358 for (int i = 0; i < keysAndValues.size(); i += 2) { 359 Value key = keysAndValues.get(i); 360 Value value = keysAndValues.get(i + 1); 361 builder.op(invoke(MAP_PUT, map, key, value)); 362 } 363 return map; 364 } 365 } 366 367 368 Value buildList(JavaType elementType, List<Value> elements) { 369 JavaType listType = parameterized(J_U_LIST, elementType); 370 if (elements.size() < 11) { 371 MethodRef listOf = MethodRef.method(J_U_LIST, "of", 372 J_U_LIST, Collections.nCopies(elements.size(), J_L_OBJECT)); 373 return builder.op(invoke(listType, listOf, elements)); 374 } else { 375 Value array = buildArray(elementType, elements); 376 return builder.op(invoke(listType, LIST_OF_ARRAY, array)); 377 } 378 } 379 380 381 Value buildArray(JavaType elementType, List<Value> elements) { 382 Value array = builder.op(newArray(elementType, 383 builder.op(constant(INT, elements.size())))); 384 for (int i = 0; i < elements.size(); i++) { 385 builder.op(arrayStoreOp(array, elements.get(i), 386 builder.op(constant(INT, i)))); 387 } 388 return array; 389 } 390 }