diff --git a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/ConstantOperandTest.java b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/ConstantOperandTest.java index 0f0948554acb6..b5d4c87d845b4 100644 --- a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/ConstantOperandTest.java +++ b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/ConstantOperandTest.java @@ -445,7 +445,7 @@ public static final class DifferentDynamicArgumentCount { public static void doOperation(VirtualFrame frame, int const1, Object dynamic1, int const2) { } - @ExpectError("Error calculating operation signature: all specializations must have the same number of value arguments.") + @ExpectError("Error calculating operation signature: all specializations must have the same number of operands.") @Specialization public static void doOperation2(VirtualFrame frame, int const1, Object dynamic1, Object dynamic2, int const2) { } diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeDSLNodeFactory.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeDSLNodeFactory.java index 6770a208de559..fb4bb44a624e8 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeDSLNodeFactory.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeDSLNodeFactory.java @@ -104,6 +104,7 @@ import com.oracle.truffle.dsl.processor.TruffleTypes; import com.oracle.truffle.dsl.processor.bytecode.model.BytecodeDSLModel; import com.oracle.truffle.dsl.processor.bytecode.model.CustomOperationModel; +import com.oracle.truffle.dsl.processor.bytecode.model.DynamicOperandModel; import com.oracle.truffle.dsl.processor.bytecode.model.InstructionModel; import com.oracle.truffle.dsl.processor.bytecode.model.InstructionModel.ImmediateKind; import com.oracle.truffle.dsl.processor.bytecode.model.InstructionModel.InstructionImmediate; @@ -3214,7 +3215,7 @@ private CodeExecutableElement createFinish() { private CodeExecutableElement createCreateLocal() { CodeExecutableElement ex = new CodeExecutableElement(Set.of(PUBLIC), types.BytecodeLocal, "createLocal"); - addJavadoc(ex, "Creates a new local. Uses default values for the local's slot metadata."); + addJavadoc(ex, "Creates a new local. Uses default values for the local's metadata."); CodeTreeBuilder b = ex.createBuilder(); @@ -3232,10 +3233,12 @@ private CodeExecutableElement createCreateLocalAllParameters() { ex.addParameter(new CodeVariableElement(type(Object.class), "info")); addJavadoc(ex, """ - Creates a new local. Uses the given {@code slotKind}, {@code name}, and {@code info} for its frame slot metadata. + Creates a new local. Uses the given {@code name} and {@code info} in its local metadata. @param name the name assigned to the local's slot. @param info the info assigned to the local's slot. + @see BytecodeNode#getLocalNames + @see BytecodeNode#getLocalInfos """); CodeTreeBuilder b = ex.createBuilder(); @@ -3537,6 +3540,37 @@ private static String getBuilderMethodJavadocHeader(String action, OperationMode return sb.toString(); } + private static String getOperationSignatureJavadoc(OperationModel operation) { + StringBuilder result = new StringBuilder(); + result.append("Signature: "); + result.append(operation.name); + result.append("("); + + boolean first = true; + for (DynamicOperandModel dynamicOperand : operation.dynamicOperands) { + if (!first) { + result.append(", "); + } + first = false; + + boolean firstOperandName = true; + for (String operandName : dynamicOperand.names()) { + if (!firstOperandName) { + result.append("|"); + } + firstOperandName = false; + result.append(operandName); + } + + if (dynamicOperand.isVariadic()) { + result.append("..."); + } + } + result.append(")"); + + return result.toString(); + } + private void addBeginOrEmitOperationDoc(OperationModel operation, CodeExecutableElement ex) { List lines = new ArrayList<>(1); @@ -3546,6 +3580,9 @@ private void addBeginOrEmitOperationDoc(OperationModel operation, CodeExecutable lines.add(getBuilderMethodJavadocHeader("Emits", operation)); } + lines.add("

"); + lines.add(getOperationSignatureJavadoc(operation)); + if (operation.javadoc != null && !operation.javadoc.isBlank()) { lines.add("

"); for (String line : operation.javadoc.strip().split("\n")) { @@ -3573,20 +3610,26 @@ private void addEndOperationDoc(OperationModel operation, CodeExecutableElement List lines = new ArrayList<>(1); lines.add(getBuilderMethodJavadocHeader("Ends", operation)); + lines.add("

"); + lines.add(getOperationSignatureJavadoc(operation)); if (model.epilogReturn != null && operation == model.epilogReturn.operation) { lines.add("

"); lines.add(String.format( "NB: This method does not directly emit %s instructions. Instead, {@link #doEmitReturn} uses the operation stack entry to determine that each Return should be preceded by a %s instruction.", operation.name, operation.name)); + } else if (operation.kind == OperationKind.TAG) { + lines.add("

"); + lines.add("The tags passed to this method should match the ones used in the corresponding {@link #beginTag} call."); } + lines.add(" "); if (operation.operationEndArguments.length != 0) { - lines.add(" "); for (OperationArgument argument : operation.operationEndArguments) { lines.add(argument.toJavadocParam()); } } + lines.add("@see #begin" + operation.name); addJavadoc(ex, lines); } @@ -3597,6 +3640,8 @@ private void addBeginRootOperationDoc(OperationModel rootOperation, CodeExecutab List lines = new ArrayList<>(2); lines.add("Begins a new root node."); lines.add("

"); + lines.add(getOperationSignatureJavadoc(rootOperation)); + lines.add("

"); for (String line : rootOperation.javadoc.strip().split("\n")) { lines.add(line); } @@ -4195,16 +4240,16 @@ private CodeExecutableElement createEnd(OperationModel operation) { emitThrowIllegalStateException(b, "\"Operation " + operation.name + " expected at least " + childString(1) + ", but \" + operation.childCount + \" provided. This is probably a bug in the parser.\""); b.end(); - } else if (operation.isVariadic && operation.numChildren() > 1) { + } else if (operation.isVariadic && operation.numDynamicOperands() > 1) { // The variadic child is included in numChildren, so the operation requires // numChildren - 1 children at minimum. - b.startIf().string("operation.childCount < " + (operation.numChildren() - 1)).end().startBlock(); - emitThrowIllegalStateException(b, "\"Operation " + operation.name + " expected at least " + childString(operation.numChildren() - 1) + + b.startIf().string("operation.childCount < " + (operation.numDynamicOperands() - 1)).end().startBlock(); + emitThrowIllegalStateException(b, "\"Operation " + operation.name + " expected at least " + childString(operation.numDynamicOperands() - 1) + ", but \" + operation.childCount + \" provided. This is probably a bug in the parser.\""); b.end(); } else if (!operation.isVariadic) { - b.startIf().string("operation.childCount != " + operation.numChildren()).end().startBlock(); - emitThrowIllegalStateException(b, "\"Operation " + operation.name + " expected exactly " + childString(operation.numChildren()) + + b.startIf().string("operation.childCount != " + operation.numDynamicOperands()).end().startBlock(); + emitThrowIllegalStateException(b, "\"Operation " + operation.name + " expected exactly " + childString(operation.numDynamicOperands()) + ", but \" + operation.childCount + \" provided. This is probably a bug in the parser.\""); b.end(); } @@ -4473,8 +4518,10 @@ private CodeExecutableElement createEndRoot(OperationModel rootOperation) { CodeExecutableElement ex = new CodeExecutableElement(Set.of(PUBLIC), model.templateType.asType(), "endRoot"); String javadoc = """ Finishes generating bytecode for the current root node. - +

"""; + + javadoc += getOperationSignatureJavadoc(rootOperation) + "\n\n"; if (model.prolog != null) { for (OperationArgument operationArgument : model.prolog.operation.operationEndArguments) { ex.addParameter(operationArgument.toVariableElement()); @@ -5111,8 +5158,8 @@ private List emitConstantBeginOperands(CodeTreeBuilder b, OperationModel if (instruction == null) { return List.of(); } - List constantOperandsBefore = instruction.signature.constantOperandsBefore; - int numConstantOperands = constantOperandsBefore.size(); + + int numConstantOperands = operation.numConstantOperandsBefore(); if (numConstantOperands == 0) { return List.of(); } @@ -5123,9 +5170,9 @@ private List emitConstantBeginOperands(CodeTreeBuilder b, OperationModel * Eagerly allocate space for the constants. Even if the node is not emitted (e.g., * it's a disabled instrumentation), we need the constant pool to be stable. */ - String constantPoolIndex = operation.instruction.signature.constantOperandsBefore.get(i) + "Index"; + String constantPoolIndex = operation.getConstantOperandBeforeName(i) + "Index"; b.startDeclaration(type(int.class), constantPoolIndex); - buildAddArgumentConstant(b, operation.operationBeginArguments[i], operation.getOperationBeginArgumentName(i)); + buildAddArgumentConstant(b, operation.operationBeginArguments[i]); b.end(); result.add(constantPoolIndex); } @@ -5146,43 +5193,42 @@ private List emitConstantOperands(CodeTreeBuilder b, OperationModel oper if (instruction == null) { return List.of(); } - List constantOperandsBefore = instruction.signature.constantOperandsBefore; - List constantOperandAfter = instruction.signature.constantOperandsAfter; - int numConstantOperands = constantOperandsBefore.size() + constantOperandAfter.size(); + int numConstantOperandsBefore = operation.numConstantOperandsBefore(); + int numConstantOperandsAfter = operation.numConstantOperandsAfter(); + int numConstantOperands = numConstantOperandsBefore + numConstantOperandsAfter; if (numConstantOperands == 0) { return List.of(); } boolean inEmit = !operation.hasChildren(); List result = new ArrayList<>(numConstantOperands); - for (int i = 0; i < numConstantOperands; i++) { - if (i < constantOperandsBefore.size()) { - if (inEmit) { - String variable = constantOperandsBefore.get(i) + "Index"; - b.startDeclaration(type(int.class), variable); - buildAddArgumentConstant(b, operation.operationBeginArguments[i], operation.getOperationBeginArgumentName(i)); - b.end(); - result.add(variable); - } else { - result.add("operationData.constants[" + i + "]"); - } + for (int i = 0; i < numConstantOperandsBefore; i++) { + if (inEmit) { + String variable = operation.getConstantOperandBeforeName(i) + "Index"; + b.startDeclaration(type(int.class), variable); + buildAddArgumentConstant(b, operation.operationBeginArguments[i]); + b.end(); + result.add(variable); } else { - if (model.prolog != null && operation == model.prolog.operation) { - /** - * Special case: when emitting the prolog in beginRoot, end constants are - * not yet known. They will be patched in endRoot. - */ - result.add(UNINIT); - } else { - int afterI = i - instruction.signature.getConstantOperandsBeforeCount(); - String variable = instruction.signature.constantOperandsAfter.get(afterI) + "Index"; - b.startDeclaration(type(int.class), variable); - buildAddArgumentConstant(b, operation.operationEndArguments[afterI], operation.getOperationEndArgumentName(afterI)); - b.end(); - result.add(variable); - } + result.add("operationData.constants[" + i + "]"); } } + for (int i = 0; i < numConstantOperandsAfter; i++) { + if (model.prolog != null && operation == model.prolog.operation) { + /** + * Special case: when emitting the prolog in beginRoot, end constants are not + * yet known. They will be patched in endRoot. + */ + result.add(UNINIT); + } else { + String variable = operation.getConstantOperandAfterName(i) + "Index"; + b.startDeclaration(type(int.class), variable); + buildAddArgumentConstant(b, operation.operationEndArguments[i]); + b.end(); + result.add(variable); + } + + } return result; } @@ -5195,7 +5241,7 @@ private String[] buildCustomInitializer(CodeTreeBuilder b, OperationModel operat b.statement("doEmitVariadic(operation.childCount - " + (instruction.signature.dynamicOperandCount - 1) + ")"); } - if (customChildBci != null && operation.numChildren() > 1) { + if (customChildBci != null && operation.numDynamicOperands() > 1) { throw new AssertionError("customChildBci can only be used with a single child."); } @@ -5248,13 +5294,13 @@ private String[] buildCustomInitializer(CodeTreeBuilder b, OperationModel operat return args; } - private void buildAddArgumentConstant(CodeTreeBuilder b, OperationArgument argument, String localName) { + private void buildAddArgumentConstant(CodeTreeBuilder b, OperationArgument argument) { b.startCall("constantPool.addConstant"); if (ElementUtils.typeEquals(argument.builderType(), argument.constantType())) { - b.string(localName); + b.string(argument.name()); } else { b.startStaticCall(argument.constantType(), "constantOf"); - b.string(localName); + b.string(argument.name()); b.end(); } b.end(); @@ -5279,7 +5325,7 @@ private CodeExecutableElement createBeforeChild() { b.startSwitch().string("operationStack[operationSp - 1].operation").end().startBlock(); Map> groupedOperations = model.getOperations().stream().filter(OperationModel::hasChildren).collect(Collectors.groupingBy(op -> { - if (op.isTransparent && (op.isVariadic || op.numChildren() > 1)) { + if (op.isTransparent && (op.isVariadic || op.numDynamicOperands() > 1)) { return BeforeChildKind.TRANSPARENT; } else if (op.kind == OperationKind.CUSTOM_SHORT_CIRCUIT) { return BeforeChildKind.SHORT_CIRCUIT; @@ -13315,7 +13361,7 @@ private void buildCallExecute(CodeTreeBuilder b, InstructionModel instr, String if (evaluatedArg != null) { b.string(evaluatedArg); } else if (tier.isUncached()) { - for (int i = 0; i < instr.signature.getConstantOperandsBeforeCount(); i++) { + for (int i = 0; i < instr.signature.constantOperandsBeforeCount; i++) { TypeMirror constantOperandType = instr.operation.constantOperands.before().get(i).type(); b.startGroup(); if (!ElementUtils.isObject(constantOperandType)) { @@ -13337,7 +13383,7 @@ private void buildCallExecute(CodeTreeBuilder b, InstructionModel instr, String b.end(); } - for (int i = 0; i < instr.signature.getConstantOperandsAfterCount(); i++) { + for (int i = 0; i < instr.signature.constantOperandsAfterCount; i++) { TypeMirror constantOperandType = instr.operation.constantOperands.after().get(i).type(); b.startGroup(); if (!ElementUtils.isObject(constantOperandType)) { diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeDSLNodeGeneratorPlugs.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeDSLNodeGeneratorPlugs.java index aa8226f06492a..a217316f36567 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeDSLNodeGeneratorPlugs.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeDSLNodeGeneratorPlugs.java @@ -130,7 +130,7 @@ public boolean canBoxingEliminateType(NodeExecutionData currentExecution, TypeMi private boolean buildChildExecution(CodeTreeBuilder b, FrameState frameState, String frame, int idx) { int index = idx; - if (index < instruction.signature.getConstantOperandsBeforeCount()) { + if (index < instruction.signature.constantOperandsBeforeCount) { TypeMirror constantOperandType = instruction.operation.constantOperands.before().get(index).type(); if (!ElementUtils.isObject(constantOperandType)) { b.cast(constantOperandType); @@ -141,7 +141,7 @@ private boolean buildChildExecution(CodeTreeBuilder b, FrameState frameState, St return false; } - index -= instruction.signature.getConstantOperandsBeforeCount(); + index -= instruction.signature.constantOperandsBeforeCount; if (index < instruction.signature.dynamicOperandCount) { TypeMirror targetType = instruction.signature.getSpecializedType(index); TypeMirror genericType = instruction.signature.getGenericType(index); @@ -181,18 +181,18 @@ private boolean buildChildExecution(CodeTreeBuilder b, FrameState frameState, St } index -= instruction.signature.dynamicOperandCount; - if (index < instruction.signature.getConstantOperandsAfterCount()) { + if (index < instruction.signature.constantOperandsAfterCount) { TypeMirror constantOperandType = instruction.operation.constantOperands.after().get(index).type(); if (!ElementUtils.isObject(constantOperandType)) { b.cast(constantOperandType); } List imms = instruction.getImmediates(ImmediateKind.CONSTANT); - InstructionImmediate imm = imms.get(instruction.signature.getConstantOperandsBeforeCount() + index); + InstructionImmediate imm = imms.get(instruction.signature.constantOperandsBeforeCount + index); b.string(readConst(readBc("$bc", "$bci + " + imm.offset()), "$bytecode.constants")); return false; } - index -= instruction.signature.getConstantOperandsAfterCount(); + index -= instruction.signature.constantOperandsAfterCount; throw new AssertionError("index=" + index + ", signature=" + instruction.signature); } diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLBuiltins.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLBuiltins.java index 787845c90a98c..e15c41dcc91e9 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLBuiltins.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLBuiltins.java @@ -40,6 +40,8 @@ */ package com.oracle.truffle.dsl.processor.bytecode.model; +import java.util.List; + import com.oracle.truffle.dsl.processor.ProcessorContext; import com.oracle.truffle.dsl.processor.TruffleTypes; import com.oracle.truffle.dsl.processor.bytecode.model.InstructionModel.ImmediateKind; @@ -71,24 +73,24 @@ public static void addBuiltins(BytecodeDSLModel m, TruffleTypes types, Processor m.blockOperation = m.operation(OperationKind.BLOCK, "Block", """ - Block is a grouping operation that executes one or more children sequentially, producing the result of the last child (if any). + Block is a grouping operation that executes each child in its body sequentially, producing the result of the last child (if any). This operation can be used to group multiple operations together in a single operation. - It has a similar role to a block in Java, but it can also produce a value (i.e., blocks can be expressions). + The result of a Block is the result produced by the last child (or void, if no value is produced). """) // .setTransparent(true) // .setVariadic(true) // .setDynamicOperands(transparentOperationChild()); m.rootOperation = m.operation(OperationKind.ROOT, "Root", String.format(""" - Each Root operation defines one function (i.e., a {@link %s}). It takes one or more children, which define the body of the function. + Each Root operation defines one function (i.e., a {@link %s}). It takes one or more children, which define the body of the function that executes when it is invoked.

A root operation is typically the outermost one. That is, a {@link BytecodeParser} should invoke {@link #beginRoot} first before using other builder methods to generate bytecode. The parser should invoke {@link #endRoot} to finish generating the {@link %s}.

This operation *can* be nested in Source and SourceSection operations in order to provide a source location for the entire root node.

- This method can also be called inside of another root operation. Bytecode generation for the first root node suspends until generation for the second root node finishes. - The second root node will be completely independent of the first (i.e., it is not "nested" and can be invoked directly). + This method can also be called inside of another root operation. Bytecode generation for the outer root node suspends until generation for the inner root node finishes. + The inner root node is not lexically nested in the first (you can invoke the inner root node independently), but the inner root *can* manipulate the outer root's locals using materialized local accesses if the outer frame is provided to it. Multiple root nodes can be obtained from the {@link BytecodeNodes} object in the order of their {@link #beginRoot} calls. """, m.templateType.getSimpleName(), m.templateType.getSimpleName())) // @@ -98,105 +100,107 @@ The second root node will be completely independent of the first (i.e., it is no new OperationArgument(types.TruffleLanguage, OperationArgument.Encoding.LANGUAGE, "language", "the language to associate with the root node")) // .setDynamicOperands(transparentOperationChild()); m.ifThenOperation = m.operation(OperationKind.IF_THEN, "IfThen", """ - IfThen implements an if-then statement. It has two children. It evaluates its first child, and if it produces {@code true}, it executes its second child. - This operation does not produce a result. - Note that only Java booleans are accepted as results of the first operation, and all other values produce undefined behaviour. + IfThen implements an if-then statement. It evaluates {@code condition}, which must produce a boolean. If the value is {@code true}, it executes {@code thens}. + This is a void operation; {@code thens} can also be void. """) // .setVoid(true) // .setDynamicOperands(child("condition"), voidableChild("thens")); m.ifThenElseOperation = m.operation(OperationKind.IF_THEN_ELSE, "IfThenElse", """ - IfThenElse implements an if-then-else statement. It has three children. It evaluates its first child, and if it produces {@code true}, it executes its second child; otherwise, it executes its third child. - This operation does not produce a result. - Note that only Java booleans are accepted as results of the first operation, and all other values produce undefined behaviour. + IfThenElse implements an if-then-else statement. It evaluates {@code condition}, which must produce a boolean. If the value is {@code true}, it executes {@code thens}; otherwise, it executes {@code elses}. + This is a void operation; both {@code thens} and {@code elses} can also be void. """) // .setVoid(true) // .setDynamicOperands(child("condition"), voidableChild("thens"), voidableChild("elses")); m.conditionalOperation = m.operation(OperationKind.CONDITIONAL, "Conditional", """ - Conditional implements a conditional expression (e.g., {@code cond ? thens : elses} in Java). It has the same semantics as IfThenElse, except it produces the value of the child that is conditionally executed. + Conditional implements a conditional expression (e.g., {@code condition ? thens : elses} in Java). It has the same semantics as IfThenElse, except it produces the value of the conditionally-executed child. """) // .setDynamicOperands(child("condition"), child("thens"), child("elses")); m.whileOperation = m.operation(OperationKind.WHILE, "While", """ - While implements a while loop. It has two children. It evaluates its first child, and if it produces {@code true}, it executes its second child and then repeats. - Note that only Java booleans are accepted as results of the first operation, and all other values produce undefined behaviour. + While implements a while loop. It evaluates {@code condition}, which must produce a boolean. If the value is {@code true}, it executes {@code body} and repeats. + This is a void operation; {@code body} can also be void. """) // .setVoid(true) // .setDynamicOperands(child("condition"), voidableChild("body")); m.tryCatchOperation = m.operation(OperationKind.TRY_CATCH, "TryCatch", """ - TryCatch implements an exception handler. It has two children: the body and the handler. It executes the body, and if any {@link com.oracle.truffle.api.exception.AbstractTruffleException} occurs during execution, it stores the exception in the given local and executes the handler. + TryCatch implements an exception handler. It executes {@code try}, and if a Truffle exception is thrown, it stores the exception in {@code exceptionLocal} and executes {@code catch}. Unlike a Java try-catch, this operation does not filter the exception based on type. + This is a void operation; both {@code try} and {@code catch} can also be void. """) // .setVoid(true) // .setOperationBeginArguments(new OperationArgument(types.BytecodeLocal, Encoding.LOCAL, "exceptionLocal", "the local to bind the caught exception to")) // .setDynamicOperands(voidableChild("try"), voidableChild("catch")); m.finallyTryOperation = m.operation(OperationKind.FINALLY_TRY, "FinallyTry", """ - FinallyTry implements a finally handler. It takes two children: the handler and the body. It executes the body, and after execution finishes it always executes the handler. - If the body finishes normally, the handler executes and control continues after the FinallyTry operation. - If the body finishes exceptionally, the handler executes and can access the Truffle exception using the given local. The exception is rethrown after the handler. - If the body finishes with a control flow operation, the handler executes and then the control flow operation continues (i.e., a Branch will branch, a Return will return). + FinallyTry implements a finally handler. It executes {@code try}, and after execution finishes it always executes {@code finally}. + If {@code try} finishes normally, {@code finally} executes and control continues after the FinallyTry operation. + If {@code try} finishes exceptionally, the Truffle exception is stored in {@code exceptionLocal} and {@code finally} executes. The exception is rethrown after {@code finally}. + If {@code try} finishes with a control flow operation, {@code finally} executes and then the control flow operation continues (i.e., a Branch will branch, a Return will return). + This is a void operation; both {@code finally} and {@code try} can also be void.

- Note that the first child is the handler and the second child is the body. Specifying the handler first greatly simplifies and speeds up bytecode generation. + Note that the ordering of the children does not match their execution order. This design is intentional to make bytecode generation simpler and faster. """) // .setVoid(true) // .setOperationBeginArguments(new OperationArgument(types.BytecodeLocal, Encoding.LOCAL, "exceptionLocal", "the local to bind a thrown exception to (if available)")) // .setDynamicOperands(voidableChild("finally"), voidableChild("try")); m.operation(OperationKind.FINALLY_TRY_CATCH, "FinallyTryCatch", """ - FinallyTryCatch implements a finally handler that behaves differently when an exception is thrown. It takes three children: the regular handler, the body, and the exceptional handler. It executes the body and then executes one of the handlers. - If the body finishes normally, the regular handler executes and control continues after the FinallyTryCatch operation. - If the body finishes exceptionally, the exceptional handler executes and can access the Truffle exception using the given local. The exception is rethrown after the handler. - If the body finishes with a control flow operation, the regular handler executes and then the control flow operation continues (i.e., a Branch will branch, a Return will return). + FinallyTryCatch implements a finally handler with different behaviour for thrown exceptions. It executes {@code try} and then one of the handlers. + If {@code try} finishes normally, {@code finally} executes and control continues after the FinallyTryCatch operation. + If {@code try} finishes exceptionally, the Truffle exception is stored in {@code exceptionLocal} and {@code catch} executes. The exception is rethrown after {@code catch}. + If {@code try} finishes with a control flow operation, {@code finally} executes and then the control flow operation continues (i.e., a Branch will branch, a Return will return). + This is a void operation; any of {@code finally}, {@code try}, or {@code catch} can be void. """) // .setVoid(true) // .setOperationBeginArguments(new OperationArgument(types.BytecodeLocal, Encoding.LOCAL, "exceptionLocal", "the local to bind a thrown exception to")) // .setDynamicOperands(voidableChild("finally"), voidableChild("try"), voidableChild("catch")); m.operation(OperationKind.LABEL, "Label", """ - Label defines a location in the bytecode that can be used as a forward Branch target. + Label assigns {@code label} the current location in the bytecode (so that it can be used as the target of a Branch). + This is a void operation.

Each {@link BytecodeLabel} must be defined exactly once. It should be defined directly inside the same operation in which it is created (using {@link #createLabel}). """) // .setVoid(true) // .setOperationBeginArguments(new OperationArgument(types.BytecodeLabel, Encoding.LABEL, "label", "the label to define")); m.operation(OperationKind.BRANCH, "Branch", """ - Branch performs a branch to the given label. + Branch performs a branch to {@code label}. This operation only supports unconditional forward branches; use IfThen and While to perform other kinds of branches. """) // .setVoid(true) // .setOperationBeginArguments(new OperationArgument(types.BytecodeLabel, Encoding.LABEL, "label", "the label to branch to")) // .setInstruction(m.branchInstruction); m.loadConstantOperation = m.operation(OperationKind.LOAD_CONSTANT, "LoadConstant", """ - LoadConstant produces the given constant value. The constant should be immutable, since it may be shared across multiple LoadConstant operations. + LoadConstant produces {@code constant}. The constant should be immutable, since it may be shared across multiple LoadConstant operations. """) // .setOperationBeginArguments(new OperationArgument(context.getType(Object.class), Encoding.OBJECT, "constant", "the constant value to load")) // .setInstruction(m.loadConstantInstruction); m.operation(OperationKind.LOAD_ARGUMENT, "LoadArgument", """ - LoadArgument reads an argument from the frame using the given index and produces its value. + LoadArgument reads the argument at {@code index} from the frame. """) // .setOperationBeginArguments(new OperationArgument(context.getType(int.class), Encoding.INTEGER, "index", "the index of the argument to load")) // .setInstruction(m.instruction(InstructionKind.LOAD_ARGUMENT, "load.argument", m.signature(Object.class))// .addImmediate(ImmediateKind.INTEGER, "index")); m.loadLocalOperation = m.operation(OperationKind.LOAD_LOCAL, "LoadLocal", """ - LoadLocal reads the given local from the frame and produces the current value. + LoadLocal reads {@code local} from the current frame. If a value has not been written to the local, LoadLocal produces the default value as defined in the {@link FrameDescriptor} ({@code null} by default). """) // .setOperationBeginArguments(new OperationArgument(types.BytecodeLocal, Encoding.LOCAL, "local", "the local to load")) // .setInstruction(m.instruction(InstructionKind.LOAD_LOCAL, "load.local", m.signature(Object.class)) // .addImmediate(ImmediateKind.LOCAL_OFFSET, "localOffset")); m.loadLocalMaterializedOperation = m.operation(OperationKind.LOAD_LOCAL_MATERIALIZED, "LoadLocalMaterialized", """ - LoadLocalMaterialized reads the given local from the frame produced by its child. - This operation can be used to read locals from materialized frames, including frames of enclosing root nodes. + LoadLocalMaterialized reads {@code local} from the frame produced by {@code frame}. + This operation can be used to read locals from materialized frames. The materialized frame must belong to the same root node or an enclosing root node. """) // .setOperationBeginArguments(new OperationArgument(types.BytecodeLocal, Encoding.LOCAL, "local", "the local to load")) // .setDynamicOperands(child("frame")) // .setInstruction(m.instruction(InstructionKind.LOAD_LOCAL_MATERIALIZED, "load.local.mat", m.signature(Object.class, Object.class)) // .addImmediate(ImmediateKind.LOCAL_OFFSET, "localOffset")); m.storeLocalOperation = m.operation(OperationKind.STORE_LOCAL, "StoreLocal", """ - StoreLocal executes its child and overwrites the given local with the result. + StoreLocal writes the value produced by {@code value} into the {@code local} in the current frame. """) // .setVoid(true) // .setOperationBeginArguments(new OperationArgument(types.BytecodeLocal, Encoding.LOCAL, "local", "the local to store to")) // @@ -204,8 +208,8 @@ If the body finishes with a control flow operation, the regular handler executes .setInstruction(m.instruction(InstructionKind.STORE_LOCAL, "store.local", m.signature(void.class, Object.class)) // .addImmediate(ImmediateKind.LOCAL_OFFSET, "localOffset")); m.storeLocalMaterializedOperation = m.operation(OperationKind.STORE_LOCAL_MATERIALIZED, "StoreLocalMaterialized", """ - StoreLocalMaterialized evaluates its first child to produce a frame, then evaluates its second child and stores the result in the given local. - This operation can be used to store locals into materialized frames, including frames of enclosing root nodes. + StoreLocalMaterialized writes the value produced by {@code value} into the {@code local} in the frame produced by {@code frame}. + This operation can be used to store locals into materialized frames. The materialized frame must belong to the same root node or an enclosing root node. """) // .setVoid(true) // .setOperationBeginArguments(new OperationArgument(types.BytecodeLocal, Encoding.LOCAL, "local", "the local to store to")) // @@ -213,26 +217,27 @@ If the body finishes with a control flow operation, the regular handler executes .setInstruction(m.instruction(InstructionKind.STORE_LOCAL_MATERIALIZED, "store.local.mat", m.signature(void.class, Object.class, Object.class)) // .addImmediate(ImmediateKind.LOCAL_OFFSET, "localOffset")); - m.returnOperation = m.operation(OperationKind.RETURN, "Return", "Return evaluates its child and returns the result.") // + m.returnOperation = m.operation(OperationKind.RETURN, "Return", "Return returns the value produced by {@code result}.") // .setDynamicOperands(child("result")).setInstruction(m.returnInstruction); if (m.enableYield) { m.yieldInstruction = m.instruction(InstructionKind.YIELD, "yield", m.signature(void.class, Object.class)).addImmediate(ImmediateKind.CONSTANT, "location"); m.operation(OperationKind.YIELD, "Yield", """ - Yield executes its child, suspends execution at the given location, and returns a {@link com.oracle.truffle.api.bytecode.ContinuationResult} containing the result. + Yield executes {@code value} and suspends execution at the given location, returning a {@link com.oracle.truffle.api.bytecode.ContinuationResult} containing the result. The caller can resume the continuation, which continues execution after the Yield. When resuming, the caller passes a value that becomes the value produced by the Yield. """) // .setDynamicOperands(child("value")).setInstruction(m.yieldInstruction); } m.sourceOperation = m.operation(OperationKind.SOURCE, "Source", """ - Source associates the enclosed children with the given source object. Together with SourceSection, it encodes source locations for operations in the program. + Source associates the children in its {@code body} with {@code source}. Together with SourceSection, it encodes source locations for operations in the program. """) // .setTransparent(true) // .setVariadic(true) // .setOperationBeginArguments(new OperationArgument(types.Source, Encoding.OBJECT, "source", "the source object to associate with the enclosed operations")) // .setDynamicOperands(transparentOperationChild()); - m.sourceSectionOperation = m.operation(OperationKind.SOURCE_SECTION, "SourceSection", """ - SourceSection associates the enclosed children with the given source character index and length. It must be (directly or indirectly) enclosed within a Source operation. - """) // + m.sourceSectionOperation = m.operation(OperationKind.SOURCE_SECTION, "SourceSection", + """ + SourceSection associates the children in its {@code body} with the source {@code index} and {@code length}. It must be (directly or indirectly) enclosed within a Source operation. + """) // .setTransparent(true) // .setVariadic(true) // .setOperationBeginArguments(new OperationArgument(context.getType(int.class), Encoding.INTEGER, "index", "the starting character index of the source section"), @@ -248,8 +253,8 @@ SourceSection associates the enclosed children with the given source character i m.tagLeaveVoidInstruction.addImmediate(ImmediateKind.TAG_NODE, "tag"); m.tagOperation = m.operation(OperationKind.TAG, "Tag", """ - Tag associates the enclosed children with the given tags. - When the {@link BytecodeConfig} includes one or more of the given tags, the root node will automatically invoke instrumentation probes when entering/leaving the enclosed operations. + Tag associates {@code tagged} with the given tags. + When the {@link BytecodeConfig} includes one or more of the given tags, the interpreter will automatically invoke instrumentation probes when entering/leaving {@code tagged}. """) // .setOperationBeginArgumentVarArgs(true) // .setOperationBeginArguments( @@ -283,14 +288,14 @@ SourceSection associates the enclosed children with the given source character i } private static DynamicOperandModel child(String name) { - return new DynamicOperandModel(name, false, false); + return new DynamicOperandModel(List.of(name), false, false); } private static DynamicOperandModel voidableChild(String name) { - return new DynamicOperandModel(name, true, false); + return new DynamicOperandModel(List.of(name), true, false); } private static DynamicOperandModel transparentOperationChild() { - return new DynamicOperandModel("body", true, true); + return new DynamicOperandModel(List.of("body"), true, true); } } diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLModel.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLModel.java index 584a4ef4989b0..bb0153ce0e50f 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLModel.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLModel.java @@ -300,7 +300,7 @@ public CustomOperationModel customRegularOperation(OperationKind kind, String na } else if (ElementUtils.typeEquals(mirror.getAnnotationType(), types.EpilogReturn)) { op.setInternal(); op.setTransparent(true); - op.setDynamicOperands(new DynamicOperandModel("value", true, false)); + op.setDynamicOperands(new DynamicOperandModel(List.of("value"), true, false)); if (epilogReturn != null) { addError(typeElement, "%s is already annotated with @%s. A Bytecode DSL class can only declare one return epilog.", getSimpleName(epilogReturn.getTemplateType()), getSimpleName(types.EpilogReturn)); diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/DynamicOperandModel.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/DynamicOperandModel.java index 46789e46e01b0..47f8cdc6c0263 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/DynamicOperandModel.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/DynamicOperandModel.java @@ -40,6 +40,8 @@ */ package com.oracle.truffle.dsl.processor.bytecode.model; -public record DynamicOperandModel(String name, boolean voidAllowed, boolean isVariadic) { +import java.util.List; + +public record DynamicOperandModel(List names, boolean voidAllowed, boolean isVariadic) { } diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/OperationModel.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/OperationModel.java index 2cc3ea26afec5..0ba1ad8f2f02b 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/OperationModel.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/OperationModel.java @@ -117,8 +117,8 @@ public enum Encoding { * Models the constant operand data statically declared on the operation using ConstantOperand * annotations. */ - public record ConstantOperands(List before, List after) { - public static final ConstantOperands NONE = new ConstantOperands(List.of(), List.of()); + public record ConstantOperandsModel(List before, List after) { + public static final ConstantOperandsModel NONE = new ConstantOperandsModel(List.of(), List.of()); public boolean hasConstantOperands() { return this != NONE; @@ -149,14 +149,23 @@ public boolean hasConstantOperands() { public boolean isInternal; public InstructionModel instruction; - public ConstantOperands constantOperands = null; + public CustomOperationModel customModel; + + // The constant operands parsed from {@code @ConstantOperand} annotations. + public ConstantOperandsModel constantOperands = null; + + // Dynamic operand data supplied by builtin specs / parsed from operation specializations. public DynamicOperandModel[] dynamicOperands = new DynamicOperandModel[0]; + + // Operand names parsed from operation specializations. + public List constantOperandBeforeNames; + public List constantOperandAfterNames; + public OperationArgument[] operationBeginArguments = EMPTY_ARGUMENTS; public OperationArgument[] operationEndArguments = EMPTY_ARGUMENTS; public boolean operationBeginArgumentVarArgs = false; - public CustomOperationModel customModel; - + // A unique identifier for instrumentation instructions. public int instrumentationIndex; public OperationModel(BytecodeDSLModel parent, int id, OperationKind kind, String name, String javadoc) { @@ -167,12 +176,26 @@ public OperationModel(BytecodeDSLModel parent, int id, OperationKind kind, Strin this.javadoc = javadoc; } - public int numChildren() { + public int numConstantOperandsBefore() { + if (constantOperands == null) { + return 0; + } + return constantOperands.before.size(); + } + + public int numDynamicOperands() { return dynamicOperands.length; } + public int numConstantOperandsAfter() { + if (constantOperands == null) { + return 0; + } + return constantOperands.after.size(); + } + public boolean hasChildren() { - return isVariadic || numChildren() > 0; + return isVariadic || numDynamicOperands() > 0; } public void setInstrumentationIndex(int instrumentationIndex) { @@ -198,6 +221,14 @@ public OperationModel setVoid(boolean isVoid) { return this; } + public String getConstantOperandBeforeName(int i) { + return constantOperandBeforeNames.get(i); + } + + public String getConstantOperandAfterName(int i) { + return constantOperandAfterNames.get(i); + } + public OperationModel setDynamicOperands(DynamicOperandModel... dynamicOperands) { this.dynamicOperands = dynamicOperands; return this; diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/Signature.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/Signature.java index d234ca4169c9f..8db864bef3178 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/Signature.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/Signature.java @@ -56,24 +56,22 @@ public final class Signature { public final List operandTypes; public final boolean isVariadic; public final boolean isVoid; - - public final List constantOperandsBefore; - public final List constantOperandsAfter; + public final int constantOperandsBeforeCount; public final int dynamicOperandCount; + public final int constantOperandsAfterCount; public Signature(TypeMirror returnType, List types) { - this(returnType, types, false, List.of(), List.of()); + this(returnType, types, false, 0, 0); } - public Signature(TypeMirror returnType, List types, boolean isVariadic, List constantOperandsBefore, List constantOperandsAfter) { + public Signature(TypeMirror returnType, List types, boolean isVariadic, int constantOperandsBeforeCount, int constantOperandsAfterCount) { this.returnType = returnType; this.operandTypes = Collections.unmodifiableList(types); this.isVariadic = isVariadic; this.isVoid = ElementUtils.isVoid(returnType); - - this.constantOperandsBefore = Collections.unmodifiableList(constantOperandsBefore); - this.constantOperandsAfter = Collections.unmodifiableList(constantOperandsAfter); - this.dynamicOperandCount = types.size() - constantOperandsBefore.size() - constantOperandsAfter.size(); + this.constantOperandsBeforeCount = constantOperandsBeforeCount; + this.dynamicOperandCount = operandTypes.size() - constantOperandsBeforeCount - constantOperandsAfterCount; + this.constantOperandsAfterCount = constantOperandsAfterCount; } public TypeMirror getGenericType(int i) { @@ -97,7 +95,7 @@ public TypeMirror getSpecializedType(int i) { if (isVariadicParameter(i)) { return context.getType(Object[].class); } - return operandTypes.get(constantOperandsBefore.size() + i); + return operandTypes.get(constantOperandsBeforeCount + i); } public boolean isVariadicParameter(int i) { @@ -111,12 +109,12 @@ public String toString() { sb.append(ElementUtils.getSimpleName(returnType)).append(" "); sb.append("("); - for (int i = 0; i < getConstantOperandsBeforeCount(); i++) { + for (int i = 0; i < constantOperandsBeforeCount; i++) { sb.append(ElementUtils.getSimpleName(operandTypes.get(i))); sb.append(", "); } - int offset = getConstantOperandsBeforeCount(); + int offset = constantOperandsBeforeCount; for (int i = 0; i < dynamicOperandCount; i++) { sb.append(ElementUtils.getSimpleName(operandTypes.get(offset + i))); if (isVariadic && i == dynamicOperandCount - 1) { @@ -126,7 +124,7 @@ public String toString() { } offset += dynamicOperandCount; - for (int i = 0; i < getConstantOperandsAfterCount(); i++) { + for (int i = 0; i < constantOperandsAfterCount; i++) { sb.append(ElementUtils.getSimpleName(operandTypes.get(offset + i))); sb.append(", "); } @@ -139,12 +137,4 @@ public String toString() { return sb.toString(); } - - public int getConstantOperandsBeforeCount() { - return constantOperandsBefore.size(); - } - - public int getConstantOperandsAfterCount() { - return constantOperandsAfter.size(); - } } diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/parser/BytecodeDSLParser.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/parser/BytecodeDSLParser.java index 47e7eafc5fc7f..afc922bc7c5c5 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/parser/BytecodeDSLParser.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/parser/BytecodeDSLParser.java @@ -92,6 +92,7 @@ import com.oracle.truffle.dsl.processor.bytecode.model.OptimizationDecisionsModel.QuickenDecision; import com.oracle.truffle.dsl.processor.bytecode.model.OptimizationDecisionsModel.SuperInstructionDecision; import com.oracle.truffle.dsl.processor.bytecode.model.Signature; +import com.oracle.truffle.dsl.processor.bytecode.parser.SpecializationSignatureParser.SpecializationSignature; import com.oracle.truffle.dsl.processor.java.ElementUtils; import com.oracle.truffle.dsl.processor.java.compiler.CompilerFactory; import com.oracle.truffle.dsl.processor.library.ExportsData; @@ -689,9 +690,9 @@ private void parseBytecodeDSLModel(TypeElement typeElement, BytecodeDSLModel mod name = String.join("#", includedSpecializations.stream().map((s) -> s.getId()).toList()); } List includedSpecializationElements = includedSpecializations.stream().map(s -> s.getMethod()).toList(); - List includedSpecializationSignatures = CustomOperationParser.parseSignatures(includedSpecializationElements, customOperation, operation.constantOperands); + List includedSpecializationSignatures = CustomOperationParser.parseSignatures(includedSpecializationElements, customOperation, operation.constantOperands); assert !customOperation.hasErrors(); - Signature signature = SignatureParser.createPolymorphicSignature(includedSpecializationSignatures, includedSpecializationElements, customOperation); + Signature signature = SpecializationSignatureParser.createPolymorphicSignature(includedSpecializationSignatures, includedSpecializationElements, customOperation); InstructionModel baseInstruction = operation.instruction; InstructionModel quickenedInstruction = model.quickenInstruction(baseInstruction, signature, ElementUtils.firstLetterUpperCase(name)); quickenedInstruction.filteredSpecializations = includedSpecializations; diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/parser/CustomOperationParser.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/parser/CustomOperationParser.java index cce77e853c4d1..eaa6101876b6f 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/parser/CustomOperationParser.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/parser/CustomOperationParser.java @@ -51,7 +51,6 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; -import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -82,9 +81,10 @@ import com.oracle.truffle.dsl.processor.bytecode.model.InstructionModel.ImmediateKind; import com.oracle.truffle.dsl.processor.bytecode.model.InstructionModel.InstructionKind; import com.oracle.truffle.dsl.processor.bytecode.model.OperationModel; -import com.oracle.truffle.dsl.processor.bytecode.model.OperationModel.ConstantOperands; +import com.oracle.truffle.dsl.processor.bytecode.model.OperationModel.ConstantOperandsModel; import com.oracle.truffle.dsl.processor.bytecode.model.OperationModel.OperationArgument; import com.oracle.truffle.dsl.processor.bytecode.model.OperationModel.OperationKind; +import com.oracle.truffle.dsl.processor.bytecode.parser.SpecializationSignatureParser.SpecializationSignature; import com.oracle.truffle.dsl.processor.bytecode.model.ShortCircuitInstructionModel; import com.oracle.truffle.dsl.processor.bytecode.model.Signature; import com.oracle.truffle.dsl.processor.generator.FlatNodeGenFactory; @@ -186,7 +186,7 @@ public CustomOperationModel parseCustomRegularOperation(TypeElement typeElement, OperationModel operation = customOperation.operation; validateCustomOperation(customOperation, typeElement, mirror, name); - ConstantOperands constantOperands = getConstantOperands(customOperation, typeElement, mirror); + ConstantOperandsModel constantOperands = getConstantOperands(customOperation, typeElement, mirror); operation.constantOperands = constantOperands; if (customOperation.hasErrors()) { return customOperation; @@ -199,12 +199,12 @@ public CustomOperationModel parseCustomRegularOperation(TypeElement typeElement, return customOperation; } - List signatures = parseSignatures(specializations, customOperation, constantOperands); + List signatures = parseSignatures(specializations, customOperation, constantOperands); if (customOperation.hasErrors()) { return customOperation; } - Signature signature = SignatureParser.createPolymorphicSignature(signatures, specializations, customOperation); + Signature signature = SpecializationSignatureParser.createPolymorphicSignature(signatures, specializations, customOperation); if (customOperation.hasErrors()) { return customOperation; } @@ -212,7 +212,11 @@ public CustomOperationModel parseCustomRegularOperation(TypeElement typeElement, throw new AssertionError("Signature could not be computed, but no error was reported"); } - produceConstantOperandWarnings(signature, signatures, constantOperands, customOperation, mirror); + produceConstantOperandWarnings(customOperation, signature, mirror); + List constantOperandBeforeNames = mergeConstantOperandNames(customOperation, constantOperands.before(), signatures, 0); + List constantOperandAfterNames = mergeConstantOperandNames(customOperation, constantOperands.after(), signatures, + signature.constantOperandsBeforeCount + signature.dynamicOperandCount); + List> dynamicOperandNames = collectDynamicOperandNames(signatures, signature); if (operation.kind == OperationKind.CUSTOM_INSTRUMENTATION) { validateInstrumentationSignature(customOperation, signature); @@ -253,71 +257,84 @@ public CustomOperationModel parseCustomRegularOperation(TypeElement typeElement, operation.isVariadic = signature.isVariadic || isShortCircuit(); operation.isVoid = signature.isVoid; - operation.operationBeginArguments = createOperationConstantArguments(constantOperands.before(), signature.constantOperandsBefore); - operation.operationEndArguments = createOperationConstantArguments(constantOperands.after(), signature.constantOperandsAfter); + DynamicOperandModel[] dynamicOperands = new DynamicOperandModel[signature.dynamicOperandCount]; for (int i = 0; i < dynamicOperands.length; i++) { - // TODO: infer name from specializations - dynamicOperands[i] = new DynamicOperandModel("child" + i, false, signature.isVariadicParameter(i)); + dynamicOperands[i] = new DynamicOperandModel(dynamicOperandNames.get(i), false, signature.isVariadicParameter(i)); } - operation.setDynamicOperands(dynamicOperands); + operation.dynamicOperands = dynamicOperands; + operation.constantOperandBeforeNames = constantOperandBeforeNames; + operation.constantOperandAfterNames = constantOperandAfterNames; + operation.operationBeginArguments = createOperationConstantArguments(constantOperands.before(), constantOperandBeforeNames); + operation.operationEndArguments = createOperationConstantArguments(constantOperands.after(), constantOperandAfterNames); customOperation.operation.setInstruction(createCustomInstruction(customOperation, typeElement, generatedNode, signature, name)); return customOperation; } - private void produceConstantOperandWarnings(Signature polymorphicSignature, List signatures, ConstantOperands constantOperands, CustomOperationModel customOperation, - AnnotationMirror mirror) { - for (int i = 0; i < constantOperands.before().size(); i++) { - ConstantOperandModel constantOperand = constantOperands.before().get(i); - Collection operandNames = getConstantOperandNamesBefore(signatures, constantOperand, i); + private static List> collectDynamicOperandNames(List signatures, Signature signature) { + List> result = new ArrayList<>(); + for (int i = 0; i < signature.dynamicOperandCount; i++) { + result.add(getDynamicOperandNames(signatures, signature.constantOperandsBeforeCount + i)); + } + return result; + } + + private static List mergeConstantOperandNames(CustomOperationModel customOperation, List constantOperands, List signatures, + int operandOffset) { + List result = new ArrayList<>(); + for (int i = 0; i < constantOperands.size(); i++) { + ConstantOperandModel constantOperand = constantOperands.get(i); + List operandNames = getConstantOperandNames(signatures, constantOperand, operandOffset + i); if (operandNames.size() > 1) { customOperation.addWarning(constantOperand.mirror(), null, "Specializations use multiple different names for this operand (%s). It is recommended to use the same name in each specialization or to explicitly provide a name for the operand.", operandNames); } + // Take the first name. + result.add(operandNames.getFirst()); + } + return result; + } + + private void produceConstantOperandWarnings(CustomOperationModel customOperation, Signature polymorphicSignature, AnnotationMirror mirror) { + ConstantOperandsModel constantOperands = customOperation.operation.constantOperands; + for (ConstantOperandModel constantOperand : constantOperands.before()) { warnIfSpecifyAtEndUnnecessary(polymorphicSignature, constantOperand, customOperation, mirror); } - for (int i = 0; i < constantOperands.after().size(); i++) { - ConstantOperandModel constantOperand = constantOperands.after().get(i); - Collection operandNames = getConstantOperandNamesAfter(signatures, constantOperand, i); - if (operandNames.size() > 1) { - customOperation.addWarning(constantOperand.mirror(), null, - "Specializations use multiple different names for this operand (%s). It is recommended to use the same name in each specialization or to explicitly provide a name for the operand.", - operandNames); - } + for (ConstantOperandModel constantOperand : constantOperands.after()) { warnIfSpecifyAtEndUnnecessary(polymorphicSignature, constantOperand, customOperation, mirror); } } - private static Collection getConstantOperandNamesBefore(List signatures, ConstantOperandModel constantOperand, int operandIndex) { - if (!constantOperand.name().isEmpty()) { - return List.of(constantOperand.name()); - } + private static List getDynamicOperandNames(List signatures, int operandIndex) { LinkedHashSet result = new LinkedHashSet<>(); - for (Signature signature : signatures) { - result.add(signature.constantOperandsBefore.get(operandIndex)); + for (SpecializationSignature signature : signatures) { + result.add(signature.operandNames().get(operandIndex)); } - return result; + return new ArrayList<>(result); } - private static Collection getConstantOperandNamesAfter(List signatures, ConstantOperandModel constantOperand, int operandIndex) { + private static List getConstantOperandNames(List signatures, ConstantOperandModel constantOperand, int operandIndex) { if (!constantOperand.name().isEmpty()) { return List.of(constantOperand.name()); } LinkedHashSet result = new LinkedHashSet<>(); - for (Signature signature : signatures) { - result.add(signature.constantOperandsAfter.get(operandIndex)); + for (SpecializationSignature signature : signatures) { + result.add(signature.operandNames().get(operandIndex)); } - return result; + return new ArrayList<>(result); } private void warnIfSpecifyAtEndUnnecessary(Signature polymorphicSignature, ConstantOperandModel constantOperand, CustomOperationModel customOperation, AnnotationMirror mirror) { if (ElementUtils.typeEquals(mirror.getAnnotationType(), types.Prolog)) { - // This flag affects whether the constant is supplied to beginRoot or endRoot. + /* + * Even though the prolog doesn't take dynamic operands, its constants are supplied via + * beginRoot/endRoot, so the difference is meaningful. + */ return; } @@ -375,7 +392,7 @@ private void validateEpilogReturnSignature(CustomOperationModel customOperation, } - private void validateEpilogExceptionalSignature(CustomOperationModel customOperation, Signature signature, List specializations, List allSignatures) { + private void validateEpilogExceptionalSignature(CustomOperationModel customOperation, Signature signature, List specializations, List signatures) { if (signature.dynamicOperandCount != 1) { customOperation.addError(String.format("An @%s operation must have exactly one dynamic operand for the exception. " + "Update all specializations to take one operand to resolve this.", @@ -383,8 +400,8 @@ private void validateEpilogExceptionalSignature(CustomOperationModel customOpera return; } - for (int i = 0; i < allSignatures.size(); i++) { - Signature individualSignature = allSignatures.get(i); + for (int i = 0; i < signatures.size(); i++) { + Signature individualSignature = signatures.get(i).signature(); TypeMirror argType = individualSignature.operandTypes.get(0); if (!isAssignable(argType, types.AbstractTruffleException)) { customOperation.addError(String.format("The operand type for %s must be %s or a subclass.", @@ -403,12 +420,12 @@ private void validateEpilogExceptionalSignature(CustomOperationModel customOpera } } - private OperationArgument[] createOperationConstantArguments(List operands, List signatureNames) { - assert operands.size() == signatureNames.size(); - OperationArgument[] arguments = new OperationArgument[signatureNames.size()]; - for (int i = 0; i < signatureNames.size(); i++) { + private OperationArgument[] createOperationConstantArguments(List operands, List operandNames) { + assert operands.size() == operandNames.size(); + OperationArgument[] arguments = new OperationArgument[operandNames.size()]; + for (int i = 0; i < operandNames.size(); i++) { ConstantOperandModel constantOperand = operands.get(i); - String argumentName = signatureNames.get(i); + String argumentName = operandNames.get(i); TypeMirror builderType; TypeMirror constantType; OperationArgument.Encoding encoding; @@ -448,7 +465,7 @@ public CustomOperationModel parseCustomShortCircuitOperation(TypeElement typeEle OperationModel operation = customOperation.operation; operation.isVariadic = true; operation.isVoid = false; - operation.setDynamicOperands(new DynamicOperandModel("value", false, false)); + operation.setDynamicOperands(new DynamicOperandModel(List.of("value"), false, false)); /* * NB: This creates a new operation for the boolean converter (or reuses one if such an @@ -594,10 +611,10 @@ private void validateCustomOperation(CustomOperationModel customOperation, TypeE } } - private ConstantOperands getConstantOperands(CustomOperationModel customOperation, TypeElement typeElement, AnnotationMirror mirror) { + private ConstantOperandsModel getConstantOperands(CustomOperationModel customOperation, TypeElement typeElement, AnnotationMirror mirror) { List constantOperands = ElementUtils.getRepeatedAnnotation(typeElement.getAnnotationMirrors(), types.ConstantOperand); if (constantOperands.isEmpty()) { - return ConstantOperands.NONE; + return ConstantOperandsModel.NONE; } if (ElementUtils.typeEqualsAny(mirror.getAnnotationType(), types.EpilogReturn, types.EpilogExceptional)) { @@ -626,7 +643,7 @@ private ConstantOperands getConstantOperands(CustomOperationModel customOperatio after.add(constantOperand); } } - return new ConstantOperands(before, after); + return new ConstantOperandsModel(before, after); } /* @@ -659,11 +676,11 @@ private CodeTypeElement createNodeForCustomInstruction(TypeElement typeElement) * Adds annotations, methods, etc. to the {@link generatedNode} so that the desired code will be * generated by {@link FlatNodeGenFactory} during code generation. */ - private void addCustomInstructionNodeMembers(TypeElement originalTypeElement, CodeTypeElement generatedNode, Signature signature, ConstantOperands constantOperands) { + private void addCustomInstructionNodeMembers(CustomOperationModel customOperation, TypeElement originalTypeElement, CodeTypeElement generatedNode, Signature signature) { if (shouldGenerateUncached(originalTypeElement)) { generatedNode.addAnnotationMirror(new CodeAnnotationMirror(types.GenerateUncached)); } - generatedNode.addAll(createExecuteMethods(signature, originalTypeElement, constantOperands)); + generatedNode.addAll(createExecuteMethods(customOperation, originalTypeElement, signature)); /* * Add @NodeChildren to this node for each argument to the operation. These get used by @@ -672,7 +689,7 @@ private void addCustomInstructionNodeMembers(TypeElement originalTypeElement, Co */ CodeAnnotationMirror nodeChildrenAnnotation = new CodeAnnotationMirror(types.NodeChildren); nodeChildrenAnnotation.setElementValue("value", - new CodeAnnotationValue(createNodeChildAnnotations(signature, constantOperands).stream().map(CodeAnnotationValue::new).collect(Collectors.toList()))); + new CodeAnnotationValue(createNodeChildAnnotations(customOperation, signature).stream().map(CodeAnnotationValue::new).collect(Collectors.toList()))); generatedNode.addAnnotationMirror(nodeChildrenAnnotation); if (parent.enableTracing || parent.enableSpecializationIntrospection) { @@ -688,17 +705,20 @@ private boolean isProxy() { return context.getEnvironment().getTypeUtils().isSameType(annotationType, context.getTypes().OperationProxy_Proxyable); } - private List createNodeChildAnnotations(Signature signature, ConstantOperands constantOperands) { + private List createNodeChildAnnotations(CustomOperationModel customOperation, Signature signature) { List result = new ArrayList<>(); - for (int i = 0; i < signature.getConstantOperandsBeforeCount(); i++) { - result.add(createNodeChildAnnotation(signature.constantOperandsBefore.get(i), constantOperands.before().get(i).type())); + OperationModel operation = customOperation.operation; + ConstantOperandsModel constantOperands = operation.constantOperands; + for (int i = 0; i < operation.numConstantOperandsBefore(); i++) { + result.add(createNodeChildAnnotation(operation.getConstantOperandBeforeName(i), constantOperands.before().get(i).type())); } for (int i = 0; i < signature.dynamicOperandCount; i++) { + // TODO dynamic operand name result.add(createNodeChildAnnotation("child" + i, signature.getGenericType(i))); } - for (int i = 0; i < signature.getConstantOperandsAfterCount(); i++) { - result.add(createNodeChildAnnotation(signature.constantOperandsAfter.get(i), constantOperands.after().get(i).type())); + for (int i = 0; i < operation.numConstantOperandsAfter(); i++) { + result.add(createNodeChildAnnotation(operation.getConstantOperandAfterName(i), constantOperands.after().get(i).type())); } return result; @@ -734,19 +754,19 @@ private CodeExecutableElement createNodeChildExecute(String name, TypeMirror ret return ex; } - private List createExecuteMethods(Signature signature, TypeElement typeElement, ConstantOperands constantOperands) { + private List createExecuteMethods(CustomOperationModel customOperation, TypeElement typeElement, Signature signature) { List result = new ArrayList<>(); - result.add(createExecuteMethod(signature, "executeObject", signature.returnType, constantOperands, false, false)); + result.add(createExecuteMethod(customOperation, signature, "executeObject", signature.returnType, false, false)); if (shouldGenerateUncached(typeElement)) { - result.add(createExecuteMethod(signature, "executeUncached", signature.returnType, constantOperands, false, true)); + result.add(createExecuteMethod(customOperation, signature, "executeUncached", signature.returnType, false, true)); } return result; } - private CodeExecutableElement createExecuteMethod(Signature signature, String name, TypeMirror type, ConstantOperands constantOperands, boolean withUnexpected, boolean uncached) { + private CodeExecutableElement createExecuteMethod(CustomOperationModel customOperation, Signature signature, String name, TypeMirror type, boolean withUnexpected, boolean uncached) { CodeExecutableElement ex = new CodeExecutableElement(Set.of(PUBLIC, ABSTRACT), type, name); if (withUnexpected) { ex.addThrownType(types.UnexpectedResultException); @@ -755,14 +775,17 @@ private CodeExecutableElement createExecuteMethod(Signature signature, String na ex.addParameter(new CodeVariableElement(types.VirtualFrame, "frame")); if (uncached) { - for (int i = 0; i < constantOperands.before().size(); i++) { - ex.addParameter(new CodeVariableElement(constantOperands.before().get(i).type(), signature.constantOperandsBefore.get(i))); + OperationModel operation = customOperation.operation; + ConstantOperandsModel constantOperands = operation.constantOperands; + for (int i = 0; i < operation.numConstantOperandsBefore(); i++) { + ex.addParameter(new CodeVariableElement(constantOperands.before().get(i).type(), operation.getConstantOperandBeforeName(i))); } for (int i = 0; i < signature.dynamicOperandCount; i++) { + // TODO: dynamic operand name ex.addParameter(new CodeVariableElement(signature.getGenericType(i), "child" + i + "Value")); } - for (int i = 0; i < constantOperands.after().size(); i++) { - ex.addParameter(new CodeVariableElement(constantOperands.after().get(i).type(), signature.constantOperandsAfter.get(i))); + for (int i = 0; i < operation.numConstantOperandsAfter(); i++) { + ex.addParameter(new CodeVariableElement(constantOperands.after().get(i).type(), operation.getConstantOperandAfterName(i))); } } @@ -783,12 +806,13 @@ private InstructionModel createCustomInstruction(CustomOperationModel customOper instr.nodeType = generatedNode; instr.nodeData = parseGeneratedNode(customOperation, originalTypeElement, generatedNode, signature); - for (int i = 0; i < signature.getConstantOperandsBeforeCount(); i++) { - instr.addImmediate(ImmediateKind.CONSTANT, signature.constantOperandsBefore.get(i)); + OperationModel operation = customOperation.operation; + for (int i = 0; i < operation.numConstantOperandsBefore(); i++) { + instr.addImmediate(ImmediateKind.CONSTANT, operation.getConstantOperandBeforeName(i)); } - for (int i = 0; i < signature.getConstantOperandsAfterCount(); i++) { - instr.addImmediate(ImmediateKind.CONSTANT, signature.constantOperandsAfter.get(i)); + for (int i = 0; i < operation.numConstantOperandsAfter(); i++) { + instr.addImmediate(ImmediateKind.CONSTANT, operation.getConstantOperandAfterName(i)); } instr.addImmediate(ImmediateKind.NODE_PROFILE, "node"); @@ -814,7 +838,7 @@ private NodeData parseGeneratedNode(CustomOperationModel customOperation, TypeEl } // Add members to the generated node so that the proper node specification is parsed. - addCustomInstructionNodeMembers(originalTypeElement, generatedNode, signature, customOperation.operation.constantOperands); + addCustomInstructionNodeMembers(customOperation, originalTypeElement, generatedNode, signature); NodeData result; try { @@ -845,9 +869,9 @@ private NodeData parseGeneratedNode(CustomOperationModel customOperation, TypeEl * Parses each specialization to a signature. Returns the list of signatures, or null if any of * them had errors. */ - public static List parseSignatures(List specializations, MessageContainer customOperation, ConstantOperands constantOperands) { - List signatures = new ArrayList<>(specializations.size()); - SignatureParser parser = new SignatureParser(ProcessorContext.getInstance()); + public static List parseSignatures(List specializations, MessageContainer customOperation, ConstantOperandsModel constantOperands) { + List signatures = new ArrayList<>(specializations.size()); + SpecializationSignatureParser parser = new SpecializationSignatureParser(ProcessorContext.getInstance()); for (ExecutableElement specialization : specializations) { signatures.add(parser.parse(specialization, customOperation, constantOperands)); } diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/parser/SignatureParser.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/parser/SpecializationSignatureParser.java similarity index 89% rename from truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/parser/SignatureParser.java rename to truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/parser/SpecializationSignatureParser.java index a36add9ed6567..f0641a6f9587b 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/parser/SignatureParser.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/parser/SpecializationSignatureParser.java @@ -59,23 +59,31 @@ import com.oracle.truffle.dsl.processor.ProcessorContext; import com.oracle.truffle.dsl.processor.TruffleTypes; import com.oracle.truffle.dsl.processor.bytecode.model.ConstantOperandModel; -import com.oracle.truffle.dsl.processor.bytecode.model.OperationModel.ConstantOperands; +import com.oracle.truffle.dsl.processor.bytecode.model.OperationModel.ConstantOperandsModel; import com.oracle.truffle.dsl.processor.bytecode.model.Signature; import com.oracle.truffle.dsl.processor.java.ElementUtils; import com.oracle.truffle.dsl.processor.java.model.CodeTypeMirror.ArrayCodeTypeMirror; import com.oracle.truffle.dsl.processor.model.MessageContainer; -public class SignatureParser { +public class SpecializationSignatureParser { + + /** + * Represents a signature parsed from a given specialization of a custom operation. In addition + * to the regular signature information, this record includes the operand names declared by the + * specialization. + */ + record SpecializationSignature(Signature signature, List operandNames) { + } final ProcessorContext context; final TruffleTypes types; - public SignatureParser(ProcessorContext context) { + public SpecializationSignatureParser(ProcessorContext context) { this.context = context; this.types = context.getTypes(); } - public Signature parse(ExecutableElement specialization, MessageContainer errorTarget, ConstantOperands constantOperands) { + public SpecializationSignature parse(ExecutableElement specialization, MessageContainer errorTarget, ConstantOperandsModel constantOperands) { boolean isValid = true; boolean isFallback = ElementUtils.findAnnotationMirror(specialization, types.Fallback) != null; @@ -99,9 +107,8 @@ public Signature parse(ExecutableElement specialization, MessageContainer errorT skipDSLParameters(params); } + List operandNames = new ArrayList<>(operands.size()); int numConstantOperands = constantOperands.before().size() + constantOperands.after().size(); - List constantOperandsBefore = new ArrayList<>(constantOperands.before().size()); - List constantOperandsAfter = new ArrayList<>(constantOperands.after().size()); if (operands.size() < numConstantOperands) { errorTarget.addError(specialization, "Specialization should declare at least %d operand%s (one for each %s).", numConstantOperands, @@ -110,17 +117,17 @@ public Signature parse(ExecutableElement specialization, MessageContainer errorT isValid = false; } else { // Argument layout: [consts_before..., dynamic_params..., consts_after...] + int numDynamicOperands = operands.size() - numConstantOperands; // Process constant operands (before). for (int i = 0; i < constantOperands.before().size(); i++) { VariableElement operand = operands.get(i); ConstantOperandModel constantOperand = constantOperands.before().get(i); isValid = checkConstantOperandParam(operand, constantOperand, errorTarget) && isValid; - constantOperandsBefore.add(constantOperand.getNameOrDefault(operand.getSimpleName().toString())); + operandNames.add(constantOperand.getNameOrDefault(operand.getSimpleName().toString())); } // Process dynamic operands. - int numDynamicOperands = operands.size() - numConstantOperands; int dynamicOffset = constantOperands.before().size(); for (int i = 0; i < numDynamicOperands; i++) { VariableElement dynamicOperand = operands.get(dynamicOffset + i); @@ -157,6 +164,8 @@ public Signature parse(ExecutableElement specialization, MessageContainer errorT isValid = false; } } + + operandNames.add(dynamicOperand.getSimpleName().toString()); } // Process constant operands (after). @@ -165,7 +174,7 @@ public Signature parse(ExecutableElement specialization, MessageContainer errorT VariableElement operand = operands.get(constantAfterOffset + i); ConstantOperandModel constantOperand = constantOperands.after().get(i); isValid = checkConstantOperandParam(operand, constantOperand, errorTarget) && isValid; - constantOperandsAfter.add(constantOperand.getNameOrDefault(operand.getSimpleName().toString())); + operandNames.add(constantOperand.getNameOrDefault(operand.getSimpleName().toString())); } } @@ -205,7 +214,9 @@ public Signature parse(ExecutableElement specialization, MessageContainer errorT if (ElementUtils.canThrowTypeExact(specialization.getThrownTypes(), CustomOperationParser.types().UnexpectedResultException)) { returnType = context.getDeclaredType(Object.class); } - return new Signature(returnType, operandTypes, hasVariadic, constantOperandsBefore, constantOperandsAfter); + Signature signature = new Signature(returnType, operandTypes, hasVariadic, constantOperands.before().size(), constantOperands.after().size()); + + return new SpecializationSignature(signature, operandNames); } private boolean isVariadic(VariableElement param) { @@ -270,12 +281,12 @@ private boolean checkConstantOperandParam(VariableElement constantOperandParam, * Also accumulates individual signatures into the {@code signatures} parameter, so they can be * inspected individually. */ - public static Signature createPolymorphicSignature(List signatures, List specializations, MessageContainer customOperation) { + public static Signature createPolymorphicSignature(List signatures, List specializations, MessageContainer customOperation) { assert !signatures.isEmpty(); assert signatures.size() == specializations.size(); - Signature polymorphicSignature = signatures.get(0); + Signature polymorphicSignature = signatures.get(0).signature(); for (int i = 1; i < signatures.size(); i++) { - polymorphicSignature = mergeSignatures(signatures.get(i), polymorphicSignature, specializations.get(i), customOperation); + polymorphicSignature = mergeSignatures(signatures.get(i).signature(), polymorphicSignature, specializations.get(i), customOperation); if (polymorphicSignature == null) { break; } @@ -297,11 +308,11 @@ private static Signature mergeSignatures(Signature a, Signature b, Element el, M } return null; } - assert a.getConstantOperandsBeforeCount() == b.getConstantOperandsBeforeCount(); - assert a.getConstantOperandsAfterCount() == b.getConstantOperandsAfterCount(); + assert a.constantOperandsBeforeCount == b.constantOperandsBeforeCount; + assert a.constantOperandsAfterCount == b.constantOperandsAfterCount; if (a.dynamicOperandCount != b.dynamicOperandCount) { if (errorTarget != null) { - errorTarget.addError(el, "Error calculating operation signature: all specializations must have the same number of value arguments."); + errorTarget.addError(el, "Error calculating operation signature: all specializations must have the same number of operands."); } return null; } @@ -311,8 +322,7 @@ private static Signature mergeSignatures(Signature a, Signature b, Element el, M for (int i = 0; i < a.operandTypes.size(); i++) { mergedTypes.add(mergeIfPrimitiveType(a.context, a.operandTypes.get(i), b.operandTypes.get(i))); } - return new Signature(newReturnType, mergedTypes, a.isVariadic, - a.constantOperandsBefore, a.constantOperandsAfter); + return new Signature(newReturnType, mergedTypes, a.isVariadic, a.constantOperandsBeforeCount, a.constantOperandsAfterCount); } private static TypeMirror mergeIfPrimitiveType(ProcessorContext context, TypeMirror a, TypeMirror b) {