Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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, //
Expand Down Expand Up @@ -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;
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
Expand All @@ -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() {
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -387,7 +430,7 @@ private CodeExecutableElement createQuickenMethod(FlatNodeGenFactory factory, Fr
List<TypeMirror> 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);
Expand All @@ -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);
Expand All @@ -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 + ")");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down
Loading