Skip to content

Commit 1d80be5

Browse files
DSouzaMansalond
authored andcommitted
[GR-60366] Backport to 24.2: Bytecode DSL annotation processor NPE with boxing variant fallback instruction.
PullRequest: graal/19567
2 parents 1effa8a + 39b4e14 commit 1d80be5

File tree

4 files changed

+174
-15
lines changed

4 files changed

+174
-15
lines changed

truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/BoxingEliminationTest.java

+118
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,11 @@
6666
import com.oracle.truffle.api.dsl.Bind;
6767
import com.oracle.truffle.api.dsl.Cached;
6868
import com.oracle.truffle.api.dsl.Specialization;
69+
import com.oracle.truffle.api.dsl.Cached.Shared;
6970
import com.oracle.truffle.api.frame.FrameDescriptor;
7071
import com.oracle.truffle.api.frame.FrameSlotTypeException;
7172
import com.oracle.truffle.api.frame.VirtualFrame;
73+
import com.oracle.truffle.api.nodes.Node;
7274
import com.oracle.truffle.api.nodes.UnexpectedResultException;
7375

7476
public class BoxingEliminationTest extends AbstractInstructionTest {
@@ -1515,6 +1517,61 @@ public void testRewriteCastLong() {
15151517
assertStable(quickenings, node, "");
15161518
}
15171519

1520+
@Test
1521+
public void testTimesTwo() {
1522+
BoxingEliminationTestRootNode node = parse(b -> {
1523+
b.beginRoot();
1524+
b.beginReturn();
1525+
b.beginAbs();
1526+
b.beginTimesTwo();
1527+
b.emitLoadArgument(0);
1528+
b.endTimesTwo();
1529+
b.endAbs();
1530+
b.endReturn();
1531+
b.endRoot();
1532+
});
1533+
1534+
assertQuickenings(node, 0, 0);
1535+
assertInstructions(node,
1536+
"load.argument",
1537+
"c.TimesTwo",
1538+
"c.Abs",
1539+
"return");
1540+
assertEquals(42L, node.getCallTarget().call(-21L));
1541+
assertInstructions(node,
1542+
"load.argument",
1543+
"c.TimesTwo$long",
1544+
"c.Abs$LessThanZero",
1545+
"return");
1546+
1547+
assertEquals(42L, node.getCallTarget().call(21L));
1548+
1549+
assertInstructions(node,
1550+
"load.argument",
1551+
"c.TimesTwo$long",
1552+
"c.Abs$GreaterZero#LessThanZero",
1553+
"return");
1554+
1555+
assertEquals(42, node.getCallTarget().call(-21));
1556+
assertInstructions(node,
1557+
"load.argument",
1558+
"c.TimesTwo$Generic",
1559+
"c.Abs",
1560+
"return");
1561+
1562+
assertEquals("lala", node.getCallTarget().call("la"));
1563+
assertInstructions(node,
1564+
"load.argument",
1565+
"c.TimesTwo$Generic",
1566+
"c.Abs",
1567+
"return");
1568+
1569+
var quickenings = assertQuickenings(node, 9, 5);
1570+
assertStable(quickenings, node, 42);
1571+
assertStable(quickenings, node, 42L);
1572+
assertStable(quickenings, node, "la");
1573+
}
1574+
15181575
@Test
15191576
public void testBinarySubscriptInt() {
15201577
BoxingEliminationTestRootNode node = parse(b -> {
@@ -2016,6 +2073,67 @@ public static Object doObject(Object obj) {
20162073
}
20172074
}
20182075

2076+
@Operation
2077+
public static final class TimesTwo {
2078+
2079+
@Specialization(rewriteOn = UnexpectedResultException.class)
2080+
public static int doInt(Object obj, @Cached @Shared TimesTwoNode doubleNode) throws UnexpectedResultException {
2081+
return doubleNode.executeInt(obj);
2082+
}
2083+
2084+
@Specialization(rewriteOn = UnexpectedResultException.class)
2085+
public static long doLong(Object obj, @Cached @Shared TimesTwoNode doubleNode) throws UnexpectedResultException {
2086+
return doubleNode.executeLong(obj);
2087+
}
2088+
2089+
@Specialization(replaces = {"doInt", "doLong"})
2090+
public static Object doObject(Object obj, @Cached @Shared TimesTwoNode doubleNode) {
2091+
return doubleNode.executeObject(obj);
2092+
}
2093+
2094+
@SuppressWarnings("truffle-inlining")
2095+
abstract static class TimesTwoNode extends Node {
2096+
2097+
int executeInt(Object x) throws UnexpectedResultException {
2098+
Object result = executeObject(x);
2099+
if (result instanceof Integer i) {
2100+
return i;
2101+
}
2102+
CompilerDirectives.transferToInterpreterAndInvalidate();
2103+
throw new UnexpectedResultException(result);
2104+
}
2105+
2106+
long executeLong(Object x) throws UnexpectedResultException {
2107+
Object result = executeObject(x);
2108+
if (result instanceof Long l) {
2109+
return l;
2110+
}
2111+
CompilerDirectives.transferToInterpreterAndInvalidate();
2112+
throw new UnexpectedResultException(result);
2113+
}
2114+
2115+
abstract Object executeObject(Object x);
2116+
2117+
@Specialization
2118+
public int doInt(int x) {
2119+
return x + x;
2120+
}
2121+
2122+
@Specialization
2123+
public long doLong(long x) {
2124+
return x + x;
2125+
}
2126+
2127+
@Specialization
2128+
@TruffleBoundary
2129+
public Object doObject(Object x) {
2130+
String asString = x.toString();
2131+
return asString + asString;
2132+
}
2133+
}
2134+
2135+
}
2136+
20192137
@Operation
20202138
@ConstantOperand(type = LocalAccessor.class)
20212139
public static final class LoadLocalCustom {

truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeDSLNodeGeneratorPlugs.java

+7-11
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ public void notifySpecialize(FlatNodeGenFactory nodeFactory, CodeTreeBuilder bui
232232
rootNode.emitOnSpecialize(builder, "$bytecode", "$bci", BytecodeRootNodeElement.readInstruction("$bc", "$bci"), specialization.getNode().getNodeId() + "$" + specialization.getId());
233233
}
234234

235-
if (instruction.getQuickeningRoot().hasQuickenings()) {
235+
if (instruction.getQuickeningRoot().hasSpecializedQuickenings()) {
236236
if (quickenMethod == null) {
237237
quickenMethod = createQuickenMethod(nodeFactory, frameState);
238238
}
@@ -284,11 +284,11 @@ private CodeExecutableElement createQuickenMethod(FlatNodeGenFactory factory, Fr
284284
CodeTreeBuilder b = method.createBuilder();
285285
b.declaration(context.getType(short.class), "newInstruction");
286286
Set<Integer> boxingEliminated = new TreeSet<>();
287-
for (InstructionModel quickening : instruction.quickenedInstructions) {
288-
if (quickening.isReturnTypeQuickening()) {
289-
// not a valid target instruction -> selected only by parent
290-
continue;
291-
}
287+
List<InstructionModel> relevantQuickenings = instruction.quickenedInstructions.stream() //
288+
.filter(q -> !q.isReturnTypeQuickening() /* selected only by parent */ && q.filteredSpecializations != null) //
289+
.toList();
290+
291+
for (InstructionModel quickening : relevantQuickenings) {
292292
for (int index = 0; index < quickening.signature.dynamicOperandCount; index++) {
293293
if (model.isBoxingEliminated(quickening.signature.getSpecializedType(index))) {
294294
boxingEliminated.add(index);
@@ -333,11 +333,7 @@ private CodeExecutableElement createQuickenMethod(FlatNodeGenFactory factory, Fr
333333
}
334334

335335
boolean elseIf = false;
336-
for (InstructionModel quickening : instruction.quickenedInstructions) {
337-
if (quickening.isReturnTypeQuickening()) {
338-
// not a valid target instruction -> selected only by parent
339-
continue;
340-
}
336+
for (InstructionModel quickening : relevantQuickenings) {
341337
elseIf = b.startIf(elseIf);
342338
CodeTree activeCheck = factory.createOnlyActive(frameState, quickening.filteredSpecializations, instruction.nodeData.getReachableSpecializations());
343339
b.tree(activeCheck);

truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/InstructionModel.java

+41-2
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ public int compareTo(InstructionEncoding other) {
264264
*/
265265
public boolean returnTypeQuickening;
266266

267+
public boolean generic;
268+
267269
/*
268270
* Alternative argument specialization type for builtin quickenings. E.g. for loadLocal
269271
* parameter types.
@@ -370,6 +372,19 @@ public boolean hasQuickenings() {
370372
return !quickenedInstructions.isEmpty();
371373
}
372374

375+
public boolean isSpecializedQuickening() {
376+
return quickeningBase != null && !returnTypeQuickening && !generic;
377+
}
378+
379+
public boolean hasSpecializedQuickenings() {
380+
for (InstructionModel instr : quickenedInstructions) {
381+
if (instr.isSpecializedQuickening()) {
382+
return true;
383+
}
384+
}
385+
return false;
386+
}
387+
373388
public boolean isQuickening() {
374389
return quickeningBase != null;
375390
}
@@ -389,6 +404,25 @@ public void pp(PrettyPrinter printer) {
389404
if (signature != null) {
390405
printer.field("signature", signature);
391406
}
407+
408+
if (getQuickeningRoot().hasQuickenings()) {
409+
String quickenKind;
410+
if (quickeningBase == null) {
411+
quickenKind = "base";
412+
} else {
413+
if (isReturnTypeQuickening()) {
414+
quickenKind = "return-type";
415+
} else {
416+
if (generic) {
417+
quickenKind = "generic";
418+
} else {
419+
quickenKind = "specialized";
420+
}
421+
}
422+
}
423+
printer.field("quicken-kind", quickenKind);
424+
}
425+
392426
}
393427

394428
public boolean isTagInstrumentation() {
@@ -598,15 +632,20 @@ public boolean needsBoxingElimination(BytecodeDSLModel model, int valueIndex) {
598632

599633
public InstructionModel findSpecializedInstruction(TypeMirror type) {
600634
for (InstructionModel specialization : quickenedInstructions) {
601-
if (ElementUtils.typeEquals(type, specialization.specializedType)) {
635+
if (!specialization.generic && ElementUtils.typeEquals(type, specialization.specializedType)) {
602636
return specialization;
603637
}
604638
}
605639
return null;
606640
}
607641

608642
public InstructionModel findGenericInstruction() {
609-
return findSpecializedInstruction(null);
643+
for (InstructionModel specialization : quickenedInstructions) {
644+
if (specialization.generic) {
645+
return specialization;
646+
}
647+
}
648+
return null;
610649
}
611650

612651
public void validateAlignment() {

truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/parser/BytecodeDSLParser.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ private void parseBytecodeDSLModel(TypeElement typeElement, BytecodeDSLModel mod
655655
break;
656656
}
657657
}
658-
if (isBoxingEliminatedOverload && operation.instruction.nodeData.needsRewrites(context)) {
658+
if (isBoxingEliminatedOverload && operation.instruction.nodeData.getReachableSpecializations().size() > 1) {
659659
for (List<TypeMirror> signature : expandedSignatures) {
660660
List<TypeMirror> parameterTypes = signature.subList(1, signature.size());
661661
boxingEliminationQuickenings.add(new ResolvedQuickenDecision(operation, List.of(specialization), parameterTypes));
@@ -775,6 +775,7 @@ private void parseBytecodeDSLModel(TypeElement typeElement, BytecodeDSLModel mod
775775
if (!overloadedTypes.isEmpty()) {
776776
InstructionModel specialization = model.quickenInstruction(instruction, instruction.signature, "Generic");
777777
specialization.returnTypeQuickening = false;
778+
specialization.generic = true;
778779
}
779780

780781
}
@@ -809,6 +810,7 @@ private void parseBytecodeDSLModel(TypeElement typeElement, BytecodeDSLModel mod
809810
InstructionModel specialization = model.quickenInstruction(instruction,
810811
new Signature(context.getType(void.class), List.of(context.getType(Object.class))),
811812
"Generic");
813+
specialization.generic = true;
812814
specialization.returnTypeQuickening = false;
813815

814816
InstructionModel returnTypeQuickening = model.quickenInstruction(instruction,
@@ -837,6 +839,7 @@ private void parseBytecodeDSLModel(TypeElement typeElement, BytecodeDSLModel mod
837839
InstructionModel genericQuickening = model.quickenInstruction(instruction,
838840
instruction.signature,
839841
"generic");
842+
genericQuickening.generic = true;
840843
genericQuickening.returnTypeQuickening = false;
841844
genericQuickening.specializedType = null;
842845
break;
@@ -854,6 +857,7 @@ private void parseBytecodeDSLModel(TypeElement typeElement, BytecodeDSLModel mod
854857

855858
genericQuickening = model.quickenInstruction(instruction,
856859
instruction.signature, "generic");
860+
genericQuickening.generic = true;
857861
genericQuickening.returnTypeQuickening = false;
858862
genericQuickening.specializedType = null;
859863
break;
@@ -882,6 +886,7 @@ private void parseBytecodeDSLModel(TypeElement typeElement, BytecodeDSLModel mod
882886

883887
genericQuickening = model.quickenInstruction(instruction,
884888
instruction.signature, "generic");
889+
genericQuickening.generic = true;
885890
genericQuickening.returnTypeQuickening = false;
886891
genericQuickening.specializedType = null;
887892
break;
@@ -909,6 +914,7 @@ private void parseBytecodeDSLModel(TypeElement typeElement, BytecodeDSLModel mod
909914
genericQuickening = model.quickenInstruction(instruction,
910915
instruction.signature,
911916
"generic");
917+
genericQuickening.generic = true;
912918
genericQuickening.returnTypeQuickening = false;
913919
genericQuickening.specializedType = null;
914920

@@ -936,9 +942,9 @@ private void parseBytecodeDSLModel(TypeElement typeElement, BytecodeDSLModel mod
936942
genericQuickening = model.quickenInstruction(instruction,
937943
instruction.signature,
938944
"generic");
945+
genericQuickening.generic = true;
939946
genericQuickening.returnTypeQuickening = false;
940947
genericQuickening.specializedType = null;
941-
942948
break;
943949
}
944950

0 commit comments

Comments
 (0)