diff --git a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/BoxingEliminationTest.java b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/BoxingEliminationTest.java index 63fd7b85b08b..8e0d8de3e274 100644 --- a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/BoxingEliminationTest.java +++ b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/BoxingEliminationTest.java @@ -2183,6 +2183,38 @@ public void testAndSingle() { assertStable(quickenings, node, 1L); } + @Test + public void testNonBEableOperand() { + // return arg0 + arg1 + BoxingEliminationTestRootNode node = (BoxingEliminationTestRootNode) parse(b -> { + b.beginRoot(); + b.beginReturn(); + b.beginAddWithNonBEableOperands(); + b.emitLoadArgument(0); + b.emitLoadArgument(1); + b.endAddWithNonBEableOperands(); + b.endReturn(); + b.endRoot(); + }).getRootNode(); + + assertInstructions(node, + "load.argument", + "load.argument", + "c.AddWithNonBEableOperands", + "return"); + + node.getCallTarget().call(42, 3.14f); + + assertInstructions(node, + "load.argument$Int", + "load.argument", + "c.AddWithNonBEableOperands$IntFloat", + "return"); + + var quickenings = assertQuickenings(node, 3, 1); + assertStable(quickenings, node, 123, 4.56f); + } + @GenerateBytecode(languageClass = BytecodeDSLTestLanguage.class, // enableYield = true, enableSerialization = true, // enableQuickening = true, // @@ -2651,6 +2683,23 @@ public static Object doInt(int constant1, Object dynamic, int constant2, @Cached } } + @Operation + static final class AddWithNonBEableOperands { + /** + * Regression test: the code for boxing elimination would incorrectly try to boxing + * eliminate the float operand because another specialization had a BE-able operand at + * the same operand index. + */ + @Specialization + public static Object doIntFloat(int x, float y) { + return x + y; + } + + @Specialization + public static Object doFloatInt(float x, int y) { + return x + y; + } + } } } 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 dbfa52c5f273..74fb295bb152 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 @@ -143,13 +143,31 @@ public ChildExecutionResult createExecuteChild(FlatNodeGenFactory factory, CodeT b.string(targetValue.getName(), " = "); int index = execution.getIndex(); - boolean throwsUnexpectedResult = buildChildExecution(b, frameState, stackFrame(), index); + boolean throwsUnexpectedResult = buildChildExecution(b, frameState, stackFrame(), index, targetValue.getTypeMirror()); return new ChildExecutionResult(b.build(), throwsUnexpectedResult); } - public boolean canBoxingEliminateType(NodeExecutionData currentExecution, TypeMirror type) { - return model.isBoxingEliminated(type); + public boolean canEliminateTypeGuard(NodeExecutionData currentExecution, TypeMirror type) { + int operandIndex = currentExecution.getIndex() - instruction.signature.constantOperandsBeforeCount; + int operandCount = instruction.signature.dynamicOperandCount; + if (operandIndex < 0) { + // can always be eliminated as they are always precise + return true; + } else if (operandIndex < operandCount) { + if (!instruction.isQuickening()) { + return false; + } + + final TypeMirror instructionType = instruction.signature.getDynamicOperandType(operandIndex); + if (ElementUtils.typeEquals(instructionType, type) || instruction.nodeData.getTypeSystem().lookupCast(instructionType, type) != null) { + return model.isBoxingEliminated(type); + } + return false; + } else { + // can always be eliminated as they are always precise + return true; + } } public void beforeCallSpecialization(FlatNodeGenFactory nodeFactory, CodeTreeBuilder builder, FrameState frameState, @@ -161,8 +179,8 @@ public void beforeCallSpecialization(FlatNodeGenFactory nodeFactory, CodeTreeBui } } - private boolean buildChildExecution(CodeTreeBuilder b, FrameState frameState, String frame, int specializationIndex) { - int operandIndex = specializationIndex; + private boolean buildChildExecution(CodeTreeBuilder b, FrameState frameState, String frame, int index, TypeMirror targetType) { + int operandIndex = index; if (operandIndex < instruction.signature.constantOperandsBeforeCount) { ConstantOperandModel constantOperand = instruction.operation.constantOperands.before().get(operandIndex); InstructionImmediate imm = instruction.constantOperandImmediates.get(constantOperand); @@ -175,80 +193,105 @@ private boolean buildChildExecution(CodeTreeBuilder b, FrameState frameState, St operandIndex -= instruction.signature.constantOperandsBeforeCount; int operandCount = instruction.signature.dynamicOperandCount; if (operandIndex < operandCount) { - TypeMirror specializedType = instruction.signature.getSpecializedType(operandIndex); - TypeMirror genericType = instruction.signature.getGenericType(operandIndex); - TypeMirror specializationTargetType; - TypeMirror expectedType = instruction.isQuickening() ? specializedType : genericType; - - if (instruction.isQuickening()) { - if (instruction.filteredSpecializations != null) { - specializationTargetType = instruction.getSpecializationSignature().signature().getDynamicOperandTypes().get(operandIndex); - } else { - specializationTargetType = specializedType; + return buildOperandExecution(b, frameState, frame, targetType, operandIndex, operandCount); + } + operandIndex -= instruction.signature.dynamicOperandCount; + + int constantOperandAfterCount = instruction.signature.constantOperandsAfterCount; + if (operandIndex < constantOperandAfterCount) { + ConstantOperandModel constantOperand = instruction.operation.constantOperands.after().get(operandIndex); + InstructionImmediate imm = instruction.constantOperandImmediates.get(constantOperand); + if (imm == null) { + throw new AssertionError("Could not find an immediate for constant operand " + constantOperand + " on instruction " + instruction); + } + b.tree(rootNode.readConstantImmediate("$bc", "$bci", "$bytecode", imm, constantOperand.type())); + return false; + } + + throw new AssertionError("index=" + index + ", signature=" + instruction.signature); + } + + private boolean buildOperandExecution(CodeTreeBuilder b, FrameState frameState, String frame, TypeMirror targetType, int operandIndex, int operandCount) throws AssertionError { + final TypeMirror instructionType = instruction.signature.getDynamicOperandType(operandIndex); + String stackIndex = "$sp - " + (operandCount - operandIndex); + + // slow paths never need boxing elimination (e.g. in the uncached interpreter) + final boolean fastPath = frameState.getMode().isFastPath(); + + /* + * We only want boxing elimination in the cached interpreter, when the operand type is + * boxing eliminated and the instruction is a quickening. Without the quickening we cannot + * boxing eliminate as the operands need to be switched as well. + */ + final boolean boxingEliminated = fastPath && model.isBoxingEliminated(instructionType) && instruction.isQuickening(); + + boolean expectOtherTypes = instruction.getQuickeningRoot().needsChildBciForBoxingElimination(model, operandIndex); + if (boxingEliminated) { + if (!expectOtherTypes) { + // Sanity check for the internal consistency of boxing elimination and + // needsChildBciForBoxingElimination. + throw new AssertionError("expectOtherTypes must be true with boxing elimination"); + } + + final ImplicitCastData cast; + if (model.isBoxingEliminated(instructionType) && !ElementUtils.typeEquals(instructionType, targetType) && !ElementUtils.isObject(targetType)) { + cast = instruction.nodeData.getTypeSystem().lookupCast(instructionType, targetType); + if (cast == null) { + throw new AssertionError("Instruction type must match the declared type unless for implicit casts: " + instruction + ": " + instructionType + " -> " + targetType); } - expectedType = specializedType; } else { - specializationTargetType = genericType; - expectedType = genericType; + cast = null; } - String stackIndex = "$sp - " + (operandCount - operandIndex); - ImplicitCastData cast = instruction.nodeData.getTypeSystem().lookupCast(expectedType, specializationTargetType); + if (cast != null) { + b.startStaticCall(cast.getMethod()); + } - if (instruction.getQuickeningRoot().needsBoxingElimination(model, operandIndex)) { - if (frameState.getMode().isFastPath()) { - b.startStatement(); - if (!ElementUtils.typeEquals(expectedType, specializedType)) { - b.startStaticCall(rootNode.lookupExpectMethod(expectedType, specializedType)); - } - if (cast != null) { - b.startStaticCall(cast.getMethod()); - } - - BytecodeRootNodeElement.startExpectFrameUnsafe(b, frame, expectedType); - b.string(stackIndex); - b.end(); + BytecodeRootNodeElement.startExpectFrameUnsafe(b, frame, instructionType); + b.string(stackIndex); + b.end(); - if (cast != null) { - b.end(); - } + if (cast != null) { + b.end(); + } - if (!ElementUtils.typeEquals(expectedType, specializedType)) { - b.end(); - } + return true; // unexpected exception thrown + } else { + // expected type is always Object without boxing elimination + + /* + * This currently only happens for variadic arguments where the target type is Object[] + * but the expected type is Object. If we introduce new types, we might need to expand + * this check. + */ + if (ElementUtils.typeEquals(targetType, context.getType(Object[].class))) { + b.cast(targetType); + } + + if (expectOtherTypes) { + if (fastPath) { + // frame.expectObject(index) + BytecodeRootNodeElement.startExpectFrameUnsafe(b, frame, context.getType(Object.class)); + b.string(stackIndex); b.end(); - return true; + return true; // unexpected exception thrown } else { - if (!ElementUtils.isObject(genericType)) { - b.cast(specializedType); - } + // frame.getValue(index) BytecodeRootNodeElement.startGetFrameUnsafe(b, frame, null); b.string(stackIndex); b.end(); return false; } } else { - if (!ElementUtils.isObject(genericType)) { - b.cast(expectedType); - } + /* + * If boxing elimination is not used in the interpreter version then we can assume + * the frame tag is object. + */ + // frame.getObject(index) b.string(BytecodeRootNodeElement.uncheckedGetFrameObject(frame, stackIndex)); - return false; + return false; // no unexpected exception thrown } } - operandIndex -= instruction.signature.dynamicOperandCount; - - int constantOperandAfterCount = instruction.signature.constantOperandsAfterCount; - if (operandIndex < constantOperandAfterCount) { - ConstantOperandModel constantOperand = instruction.operation.constantOperands.after().get(operandIndex); - InstructionImmediate imm = instruction.constantOperandImmediates.get(constantOperand); - if (imm == null) { - throw new AssertionError("Could not find an immediate for constant operand " + constantOperand + " on instruction " + instruction); - } - b.tree(rootNode.readConstantImmediate("$bc", "$bci", "$bytecode", imm, constantOperand.type())); - return false; - } - - throw new AssertionError("index=" + specializationIndex + ", signature=" + instruction.signature); } public CodeExecutableElement getQuickenMethod() { @@ -334,7 +377,7 @@ private CodeExecutableElement createQuickenMethod(FlatNodeGenFactory factory, Fr for (InstructionModel quickening : relevantQuickenings) { for (int index = 0; index < quickening.signature.dynamicOperandCount; index++) { - if (model.isBoxingEliminated(quickening.signature.getSpecializedType(index))) { + if (model.isBoxingEliminated(quickening.signature.getDynamicOperandType(index))) { boxingEliminated.add(index); } } @@ -387,7 +430,7 @@ private CodeExecutableElement createQuickenMethod(FlatNodeGenFactory factory, Fr List dynamicOperandTypes = specializationSignature.signature().getDynamicOperandTypes(); for (int valueIndex : boxingEliminated) { - TypeMirror specializedType = quickening.signature.getSpecializedType(valueIndex); + TypeMirror specializedType = quickening.signature.getDynamicOperandType(valueIndex); TypeMirror specializationTargetType = dynamicOperandTypes.get(valueIndex); CodeTree check = factory.createIsImplicitTypeStateCheck(frameState, specializedType, specializationTargetType, valueIndex + specializationSignature.signature().constantOperandsBeforeCount); @@ -401,7 +444,7 @@ private CodeExecutableElement createQuickenMethod(FlatNodeGenFactory factory, Fr } for (int valueIndex : boxingEliminated) { - TypeMirror specializedType = quickening.signature.getSpecializedType(valueIndex); + TypeMirror specializedType = quickening.signature.getDynamicOperandType(valueIndex); if (model.isBoxingEliminated(specializedType)) { b.newLine().string(" ", sep, "("); b.string("newOperand" + valueIndex); @@ -414,7 +457,7 @@ private CodeExecutableElement createQuickenMethod(FlatNodeGenFactory factory, Fr b.end().startBlock(); for (int valueIndex : boxingEliminated) { - TypeMirror specializedType = quickening.signature.getSpecializedType(valueIndex); + TypeMirror specializedType = quickening.signature.getDynamicOperandType(valueIndex); if (!model.isBoxingEliminated(specializedType)) { b.startStatement(); b.string("newOperand" + valueIndex, " = undoQuickening(oldOperand" + valueIndex + ")"); diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeRootNodeElement.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeRootNodeElement.java index 4afaab58000b..05b707906054 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeRootNodeElement.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeRootNodeElement.java @@ -6565,7 +6565,7 @@ private CodeExecutableElement createAfterChild() { int immediateIndex = 0; boolean elseIf = false; for (int valueIndex = 0; valueIndex < op.instruction.signature.dynamicOperandCount; valueIndex++) { - if (op.instruction.needsBoxingElimination(model, valueIndex)) { + if (op.instruction.needsChildBciForBoxingElimination(model, valueIndex)) { elseIf = b.startIf(elseIf); b.string("childIndex == " + valueIndex).end().startBlock(); @@ -17398,7 +17398,7 @@ private CodeExecutableElement lookupTagLeave(InstructionModel instr) { } CodeTreeBuilder b = method.createBuilder(); - TypeMirror inputType = instr.specializedType == null ? instr.signature.getSpecializedType(0) : instr.specializedType; + TypeMirror inputType = instr.specializedType == null ? instr.signature.getDynamicOperandType(0) : instr.specializedType; TypeMirror returnType = instr.signature.returnType; boolean isSpecialized = instr.specializedType != null; @@ -17540,7 +17540,7 @@ private CodeExecutableElement lookupDoPop(InstructionModel instr) { } CodeTreeBuilder b = method.createBuilder(); - TypeMirror inputType = instr.signature.getSpecializedType(0); + TypeMirror inputType = instr.signature.getDynamicOperandType(0); boolean isGeneric = ElementUtils.isObject(inputType); @@ -17665,7 +17665,7 @@ private CodeExecutableElement lookupDoBranch(InstructionModel instr) { } CodeTreeBuilder b = method.createBuilder(); - TypeMirror inputType = instr.signature.getSpecializedType(0); + TypeMirror inputType = instr.signature.getDynamicOperandType(0); b.startTryBlock(); b.startDeclaration(type(boolean.class), "result"); @@ -17729,7 +17729,7 @@ private CodeExecutableElement lookupDoSpecializeBranch(InstructionModel instr) { InstructionModel boxedInstruction = null; InstructionModel unboxedInstruction = null; for (InstructionModel quickening : instr.getFlattenedQuickenedInstructions()) { - if (ElementUtils.isObject(quickening.signature.getSpecializedType(0))) { + if (ElementUtils.isObject(quickening.signature.getDynamicOperandType(0))) { boxedInstruction = quickening; } else { unboxedInstruction = quickening; @@ -18016,7 +18016,7 @@ private CodeExecutableElement lookupDoMergeConditional(InstructionModel instr) { method.getParameters().add(0, new CodeVariableElement(abstractBytecodeNode.asType(), "$this")); } - final TypeMirror inputType = instr.signature.getSpecializedType(1); + final TypeMirror inputType = instr.signature.getDynamicOperandType(1); final TypeMirror returnType = instr.signature.returnType; CodeTreeBuilder b = method.createBuilder(); @@ -18214,7 +18214,7 @@ private CodeExecutableElement lookupDoStoreLocal(InstructionModel instr) { method.getParameters().add(0, new CodeVariableElement(types.Frame, "stackFrame")); } - final TypeMirror inputType = instr.signature.getSpecializedType(0); + final TypeMirror inputType = instr.signature.getDynamicOperandType(0); final TypeMirror slotType = instr.specializedType != null ? instr.specializedType : type(Object.class); CodeTreeBuilder b = method.createBuilder(); @@ -18966,7 +18966,7 @@ private void buildCallExecute(CodeTreeBuilder b, InstructionModel instr, String } for (int i = 0; i < instr.signature.dynamicOperandCount; i++) { - TypeMirror targetType = instr.signature.getGenericType(i); + TypeMirror targetType = instr.signature.getDynamicOperandType(i); b.startGroup(); if (!ElementUtils.isObject(targetType)) { b.cast(targetType); diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/InstructionModel.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/InstructionModel.java index e94ea16367b8..acfb3a6679a5 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/InstructionModel.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/InstructionModel.java @@ -750,18 +750,22 @@ public boolean canUseNodeSingleton() { return true; } - public boolean needsBoxingElimination(BytecodeDSLModel model, int valueIndex) { + /** + * Whether the instruction or any of its quickenings needs a child bci immediate in order to + * perform boxing elimination of the given operand. + */ + public boolean needsChildBciForBoxingElimination(BytecodeDSLModel model, int valueIndex) { if (!model.usesBoxingElimination()) { return false; } if (signature.isVariadicParameter(valueIndex)) { return false; } - if (model.isBoxingEliminated(signature.getSpecializedType(valueIndex))) { + if (model.isBoxingEliminated(signature.getDynamicOperandType(valueIndex))) { return true; } for (InstructionModel quickenedInstruction : quickenedInstructions) { - if (quickenedInstruction.needsBoxingElimination(model, valueIndex)) { + if (quickenedInstruction.needsChildBciForBoxingElimination(model, valueIndex)) { return true; } } 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 8d057807aad8..ae8d2735b274 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 @@ -107,47 +107,29 @@ public Signature specializeReturnType(TypeMirror newReturnType) { return new Signature(this, operandTypes, newReturnType); } - public Signature specializeOperandType(int dynamicOperandIndex, TypeMirror newType) { + public Signature specializeDynamicOperandType(int dynamicOperandIndex, TypeMirror newType) { if (dynamicOperandIndex < 0 || dynamicOperandIndex >= dynamicOperandCount) { throw new IllegalArgumentException("Invalid operand index " + dynamicOperandIndex); } - - TypeMirror type = getSpecializedType(dynamicOperandIndex); + int operandIndex = constantOperandsBeforeCount + dynamicOperandIndex; + TypeMirror type = operandTypes.get(operandIndex); if (ElementUtils.typeEquals(type, newType)) { return this; } - List newOperandTypes = new ArrayList<>(operandTypes); - newOperandTypes.set(dynamicOperandIndex, newType); + newOperandTypes.set(operandIndex, newType); return new Signature(this, newOperandTypes, this.returnType); } - public TypeMirror getGenericType(int dynamicOperandIndex) { - if (dynamicOperandIndex < 0 || dynamicOperandIndex >= dynamicOperandCount) { - throw new IllegalArgumentException("Invalid operand index " + dynamicOperandIndex); - } - if (isVariadicParameter(dynamicOperandIndex)) { - return context.getType(Object[].class); - } - return context.getType(Object.class); - } - - public TypeMirror getGenericReturnType() { - if (isVoid) { - return context.getType(void.class); - } else { - return context.getType(Object.class); - } - } - - public TypeMirror getSpecializedType(int dynamicOperandIndex) { + public TypeMirror getDynamicOperandType(int dynamicOperandIndex) { if (dynamicOperandIndex < 0 && dynamicOperandIndex >= dynamicOperandCount) { throw new IllegalArgumentException("Invalid operand index " + dynamicOperandIndex); } - if (isVariadicParameter(dynamicOperandIndex)) { - return context.getType(Object[].class); + TypeMirror type = operandTypes.get(constantOperandsBeforeCount + dynamicOperandIndex); + if (isVariadicParameter(dynamicOperandIndex) && !ElementUtils.typeEquals(type, context.getType(Object[].class))) { + throw new AssertionError("Invalid varargs type."); } - return operandTypes.get(constantOperandsBeforeCount + dynamicOperandIndex); + return type; } public boolean isVariadicParameter(int i) { 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 80a2f01242a1..71a6effdc9b5 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 @@ -895,24 +895,44 @@ private void resolveBoxingElimination(BytecodeDSLModel model, List includedSpecializationElements = includedSpecializations.stream().map(s -> s.getMethod()).toList(); - List includedSpecializationSignatures = CustomOperationParser.parseSignatures(includedSpecializationElements, node, + List includedSpecializationSignatures = CustomOperationParser.parseSignatures( + includedSpecializationElements, node, quickening.operation().constantOperands); - Signature signature = SpecializationSignatureParser.createPolymorphicSignature(includedSpecializationSignatures, + Signature signature = SpecializationSignatureParser.createPolymorphicSignature( + includedSpecializationSignatures, includedSpecializationElements, node); // inject custom signatures. - for (int i = 0; i < quickening.types().size(); i++) { - TypeMirror type = quickening.types().get(i); - if (model.isBoxingEliminated(type)) { - signature = signature.specializeOperandType(i, type); + InstructionModel baseInstruction = quickening.operation().instruction; + List genericSignature = baseInstruction.signature.getDynamicOperandTypes(); + if (quickening.types().size() != genericSignature.size()) { + throw new AssertionError("Invalid signature size in quickening " + quickening.types() + " : " + genericSignature); + } + for (int i = 0; i < genericSignature.size(); i++) { + TypeMirror specialized = quickening.types().get(i); + TypeMirror target; + + if (model.isBoxingEliminated(specialized) && + /* + * Handles the corner case when the set of quickened + * specializations happens to be incompatible with boxing + * elimination of this type. May happen if implicit cast and + * non-implicit casts types of the same specialization argument + * are mixed in the same quickening. The only safe thing to do + * is to not boxing eliminate. + */ + !ElementUtils.isObject(signature.getDynamicOperandType(i))) { + target = specialized; + } else { + target = genericSignature.get(i); } + signature = signature.specializeDynamicOperandType(i, target); } - InstructionModel baseInstruction = quickening.operation().instruction; - InstructionModel quickenedInstruction = model.quickenInstruction(baseInstruction, signature, ElementUtils.firstLetterUpperCase(name)); quickenedInstruction.filteredSpecializations = includedSpecializations; + validateQuickening(model, quickenedInstruction); } } @@ -926,7 +946,7 @@ private void resolveBoxingElimination(BytecodeDSLModel model, List genericTypes = instruction.getQuickeningRoot().signature.operandTypes; + List specializedTypes = instruction.signature.operandTypes; + + for (int i = 0; i < genericTypes.size(); i++) { + if (ElementUtils.typeEquals(genericTypes.get(i), specializedTypes.get(i))) { + continue; + } + if (!model.isBoxingEliminated(specializedTypes.get(i))) { + /* + * We need to double check all specialized types are actually boxing eliminated + * otherwise this would lead to hard to detect deopt loops. + */ + throw new AssertionError("Invalid quickening signature " + instruction); + } + } + } + } + private static void parseDefaultUncachedThreshold(BytecodeDSLModel model, AnnotationMirror generateBytecodeMirror, DSLExpressionResolver resolver) { AnnotationValue explicitValue = ElementUtils.getAnnotationValue(generateBytecodeMirror, "defaultUncachedThreshold", false); String defaultUncachedThreshold; 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 0d367c79856c..3f2747cb405e 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 @@ -964,7 +964,7 @@ private List createNodeChildAnnotations(CustomOperationModel c result.add(createNodeChildAnnotation(operation.getConstantOperandBeforeName(i), before.get(i).type())); } for (int i = 0; i < signature.dynamicOperandCount; i++) { - result.add(createNodeChildAnnotation("child" + i, signature.getGenericType(i))); + result.add(createNodeChildAnnotation("child" + i, signature.getDynamicOperandType(i))); } List after = operation.constantOperands.after(); for (int i = 0; i < after.size(); i++) { @@ -1031,7 +1031,7 @@ private CodeExecutableElement createExecuteMethod(CustomOperationModel customOpe ex.addParameter(new CodeVariableElement(before.get(i).type(), operation.getConstantOperandBeforeName(i))); } for (int i = 0; i < signature.dynamicOperandCount; i++) { - ex.addParameter(new CodeVariableElement(signature.getGenericType(i), "child" + i + "Value")); + ex.addParameter(new CodeVariableElement(signature.getDynamicOperandType(i), "child" + i + "Value")); } List after = operation.constantOperands.after(); for (int i = 0; i < after.size(); i++) { @@ -1052,17 +1052,34 @@ private CodeExecutableElement createExecuteMethod(CustomOperationModel customOpe */ private void createCustomInstruction(CustomOperationModel customOperation, CodeTypeElement generatedNode, Signature signature, String operationName) { + + /* + * We erase all specialized types. Without boxing elimination we cannot specialize any of + * the signature types in the instruction. If we introduce the notion of static types in the + * future we might be able to use more of the type information. + */ + Signature erasedSignature = signature; + for (int i = 0; i < erasedSignature.getDynamicOperandTypes().size(); i++) { + TypeMirror targetType; + if (erasedSignature.isVariadicParameter(i)) { + targetType = context.getType(Object[].class); + } else { + targetType = context.getType(Object.class); + } + erasedSignature = erasedSignature.specializeDynamicOperandType(i, targetType); + } + String instructionName = "c." + operationName; InstructionModel instr; if (customOperation.isEpilogExceptional()) { // We don't emit bytecode for this operation. Allocate an InstructionModel but don't // register it as an instruction. - instr = new InstructionModel(InstructionKind.CUSTOM, instructionName, signature); + instr = new InstructionModel(InstructionKind.CUSTOM, instructionName, erasedSignature); } else { - instr = parent.instruction(InstructionKind.CUSTOM, instructionName, signature); + instr = parent.instruction(InstructionKind.CUSTOM, instructionName, erasedSignature); } instr.nodeType = generatedNode; - instr.nodeData = parseGeneratedNode(customOperation, generatedNode, signature); + instr.nodeData = parseGeneratedNode(customOperation, generatedNode, erasedSignature); customOperation.operation.setInstruction(instr); if (customOperation.operation.variadicReturn) { diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/FlatNodeGenFactory.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/FlatNodeGenFactory.java index 80de294cc52d..a784f5187965 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/FlatNodeGenFactory.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/FlatNodeGenFactory.java @@ -2628,11 +2628,8 @@ private List filterCompatibleExecutableTypes(ExecutableTypeD public CodeExecutableElement createExecuteMethod(CodeTypeElement clazz, CodeExecutableElement baseMethod, List specializations, boolean skipStateChecks) { - final List allSpecializations = specializations; int signatureSize = node.getPolymorphicExecutable().getSignatureParameters().size(); ExecutableTypeData type = new ExecutableTypeData(node, baseMethod, signatureSize, List.of(node.getFrameType()), false, true); - - List implementedSpecializations = allSpecializations; CodeExecutableElement method = createExecuteMethod(type); method.addAnnotationMirror(new CodeAnnotationMirror(types.HostCompilerDirectives_InliningRoot)); FrameState frameState = FrameState.load(this, type, Integer.MAX_VALUE, NodeExecutionMode.FAST_PATH, method); @@ -2643,9 +2640,9 @@ public CodeExecutableElement createExecuteMethod(CodeTypeElement clazz, CodeExec } clazz.add(method); CodeTreeBuilder builder = method.createBuilder(); - SpecializationGroup group = SpecializationGroup.create(implementedSpecializations); + SpecializationGroup group = SpecializationGroup.create(specializations); frameState.setSkipStateChecks(skipStateChecks); - builder.tree(createFastPath(builder, implementedSpecializations, group, type, frameState)); + builder.tree(createFastPath(builder, specializations, group, type, frameState)); return method; } @@ -3619,7 +3616,7 @@ private TypeGuard findBoxingEliminationGuard(SpecializationGroup group, NodeExec return null; } NodeExecutionData currentExecution = node.getChildExecutions().get(guard.getSignatureIndex()); - if (plugs.canBoxingEliminateType(currentExecution, guard.getType())) { + if (plugs.canEliminateTypeGuard(currentExecution, guard.getType())) { return guard; } return null; diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGeneratorPlugs.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGeneratorPlugs.java index cf2597c8691f..d9e13e25b3c3 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGeneratorPlugs.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGeneratorPlugs.java @@ -90,7 +90,7 @@ default String createNodeChildReferenceForException(FlatNodeGenFactory flatNodeG return flatNodeGenFactory.createNodeChildReferenceForException(frameState, execution, child); } - default boolean canBoxingEliminateType(NodeExecutionData currentExecution, TypeMirror type) { + default boolean canEliminateTypeGuard(NodeExecutionData currentExecution, TypeMirror type) { if (!isPrimitive(type)) { return false; }