Skip to content

Commit 665d8cb

Browse files
committed
Fix: generic boxing overload should not be targeted by quicken
1 parent 6146963 commit 665d8cb

File tree

2 files changed

+124
-10
lines changed

2 files changed

+124
-10
lines changed

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

Lines changed: 118 additions & 0 deletions
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

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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);

0 commit comments

Comments
 (0)