Skip to content

Commit 56e3cc4

Browse files
committed
[GR-61293] Bytecode-DSL: Inline state bits into the bytecode array whenever possible.
PullRequest: graal/22240
2 parents d8ce19c + 75f7748 commit 56e3cc4

File tree

25 files changed

+1341
-659
lines changed

25 files changed

+1341
-659
lines changed

truffle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan
2222
* GR-8251: Added `DebuggerSession.disposeStepping(Thread)` to the debugger API to allow disposal of any pending step on a thread.
2323
* GR-8251: Pending steps are no longer removed when no debugging action is prepared on a `SuspendedEvent`. In practice, this means that the lifecycle of steps is now independent of breakpoint hits.
2424
* GR-8251: `DebuggerSession.resumeThread(Thread)` no longer cancels ongoing step operations. Stepping is now independent of other debugger actions to enhance flexibility.
25+
* GR-61293: Bytecode DSL: Specialization state is now inlined into the bytecode array, which reduces memory footprint and interpreter execution time.
2526

2627
## Version 25.0
2728
* GR-31495 Added ability to specify language and instrument specific options using `Source.Builder.option(String, String)`. Languages may describe available source options by implementing `TruffleLanguage.getSourceOptionDescriptors()` and `TruffleInstrument.getSourceOptionDescriptors()` respectively.

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
package com.oracle.truffle.api.bytecode.test;
4242

4343
import static org.junit.Assert.assertEquals;
44+
import static org.junit.Assert.assertFalse;
45+
import static org.junit.Assert.assertTrue;
4446
import static org.junit.Assert.fail;
4547

4648
import java.util.List;
@@ -49,6 +51,7 @@
4951

5052
import com.oracle.truffle.api.bytecode.BytecodeRootNode;
5153
import com.oracle.truffle.api.bytecode.Instruction;
54+
import com.oracle.truffle.api.bytecode.Instruction.Argument.Kind;
5255

5356
public class AbstractInstructionTest {
5457

@@ -151,4 +154,21 @@ public static <T> void assertFails(Callable<?> callable, Class<T> exceptionType,
151154
fail("expected " + exceptionType.getName() + " but no exception was thrown");
152155
}
153156

157+
public static void assertSingletonNode(Instruction.Argument nodeArgument) {
158+
assertFalse(nodeArgument.asCachedNode().isAdoptable());
159+
}
160+
161+
public static void assertInstanceNode(Instruction.Argument nodeArgument) {
162+
assertTrue(nodeArgument.asCachedNode().isAdoptable());
163+
}
164+
165+
public static Instruction.Argument findNodeArgument(Instruction i) {
166+
for (Instruction.Argument arg : i.getArguments()) {
167+
if (arg.getKind() == Kind.NODE_PROFILE) {
168+
return arg;
169+
}
170+
}
171+
return null;
172+
}
173+
154174
}

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

Lines changed: 93 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,18 @@
4141
package com.oracle.truffle.api.bytecode.test;
4242

4343
import static org.junit.Assert.assertEquals;
44-
import static org.junit.Assert.fail;
44+
import static org.junit.Assert.assertNotNull;
45+
import static org.junit.Assert.assertTrue;
4546

4647
import org.junit.Test;
4748

4849
import com.oracle.truffle.api.bytecode.BytecodeConfig;
50+
import com.oracle.truffle.api.bytecode.BytecodeNode;
4951
import com.oracle.truffle.api.bytecode.BytecodeParser;
5052
import com.oracle.truffle.api.bytecode.BytecodeRootNode;
5153
import com.oracle.truffle.api.bytecode.BytecodeRootNodes;
5254
import com.oracle.truffle.api.bytecode.GenerateBytecode;
5355
import com.oracle.truffle.api.bytecode.Instruction;
54-
import com.oracle.truffle.api.bytecode.Instruction.Argument.Kind;
5556
import com.oracle.truffle.api.bytecode.Operation;
5657
import com.oracle.truffle.api.dsl.Bind;
5758
import com.oracle.truffle.api.dsl.Specialization;
@@ -76,11 +77,13 @@ public void testSingleSpecialization() {
7677
b.endRoot();
7778
}).getRootNode();
7879

79-
Instruction singleSpecializationNode = node.getBytecodeNode().getInstructionsAsList().get(1);
80-
assertEquals("c.SingleSpecialization", singleSpecializationNode.getName());
81-
assertNoNode(singleSpecializationNode);
82-
8380
assertEquals(42, node.getCallTarget().call(42));
81+
82+
Instruction instruction = node.getBytecodeNode().getInstructionsAsList().get(1);
83+
assertEquals("c.SingleSpecialization", instruction.getName());
84+
Instruction.Argument nodeArgument = findNodeArgument(instruction);
85+
assertNotNull(nodeArgument);
86+
assertSingletonNode(nodeArgument);
8487
}
8588

8689
@Test
@@ -90,35 +93,72 @@ public void testBoundNode() {
9093
b.beginRoot();
9194

9295
b.beginReturn();
93-
b.beginBound();
96+
b.beginBoundNode();
9497
b.emitLoadArgument(0);
95-
b.endBound();
98+
b.endBoundNode();
9699
b.endReturn();
97100
b.endRoot();
98101
}).getRootNode();
99102

100-
Instruction singleSpecializationNode = node.getBytecodeNode().getInstructionsAsList().get(1);
101-
assertEquals("c.Bound", singleSpecializationNode.getName());
102-
assertNode(singleSpecializationNode);
103-
104103
assertEquals(42, node.getCallTarget().call(42));
104+
105+
Instruction instruction = node.getBytecodeNode().getInstructionsAsList().get(1);
106+
Instruction.Argument nodeArgument = findNodeArgument(instruction);
107+
assertEquals("c.BoundNode", instruction.getName());
108+
assertNotNull(nodeArgument);
109+
110+
assertInstanceNode(nodeArgument);
111+
assertTrue(nodeArgument.getSpecializationInfo().get(0).isActive());
105112
}
106113

107-
private static void assertNoNode(Instruction i) {
108-
for (Instruction.Argument arg : i.getArguments()) {
109-
if (arg.getKind() == Kind.NODE_PROFILE) {
110-
fail("No node profile expected but found in " + i);
111-
}
112-
}
114+
@Test
115+
public void testBoundBytecodeNode() {
116+
// return - (arg0)
117+
NodeOptimizationTestNode node = (NodeOptimizationTestNode) parse(b -> {
118+
b.beginRoot();
119+
120+
b.beginReturn();
121+
b.beginBoundBytecodeNode();
122+
b.emitLoadArgument(0);
123+
b.endBoundBytecodeNode();
124+
b.endReturn();
125+
b.endRoot();
126+
}).getRootNode();
127+
128+
assertEquals(42, node.getCallTarget().call(42));
129+
130+
Instruction instruction = node.getBytecodeNode().getInstructionsAsList().get(1);
131+
Instruction.Argument nodeArgument = findNodeArgument(instruction);
132+
assertEquals("c.BoundBytecodeNode", instruction.getName());
133+
assertNotNull(nodeArgument);
134+
135+
assertSingletonNode(nodeArgument);
136+
assertTrue(nodeArgument.getSpecializationInfo().get(0).isActive());
113137
}
114138

115-
private static void assertNode(Instruction i) {
116-
for (Instruction.Argument arg : i.getArguments()) {
117-
if (arg.getKind() == Kind.NODE_PROFILE) {
118-
return;
119-
}
120-
}
121-
fail("No node profile found but expected in " + i);
139+
@Test
140+
public void testSingletonForSpecialization() {
141+
// return - (arg0)
142+
NodeOptimizationTestNode node = (NodeOptimizationTestNode) parse(b -> {
143+
b.beginRoot();
144+
145+
b.beginReturn();
146+
b.beginSpecializationTest();
147+
b.emitLoadArgument(0);
148+
b.endSpecializationTest();
149+
b.endReturn();
150+
b.endRoot();
151+
}).getRootNode();
152+
153+
assertEquals(42, node.getCallTarget().call(42));
154+
155+
Instruction instruction = node.getBytecodeNode().getInstructionsAsList().get(1);
156+
assertEquals("c.SpecializationTest$Int", instruction.getName());
157+
Instruction.Argument nodeArgument = findNodeArgument(instruction);
158+
assertNotNull(nodeArgument);
159+
160+
assertSingletonNode(nodeArgument);
161+
assertTrue(nodeArgument.getSpecializationInfo().get(0).isActive());
122162
}
123163

124164
private static NodeOptimizationTestNode parse(BytecodeParser<NodeOptimizationTestNodeGen.Builder> builder) {
@@ -128,7 +168,7 @@ private static NodeOptimizationTestNode parse(BytecodeParser<NodeOptimizationTes
128168

129169
@GenerateBytecode(languageClass = BytecodeDSLTestLanguage.class, //
130170
enableYield = true, enableSerialization = true, //
131-
enableQuickening = true, //
171+
enableSpecializationIntrospection = true, enableQuickening = true, //
132172
boxingEliminationTypes = {long.class, int.class, boolean.class})
133173
public abstract static class NodeOptimizationTestNode extends DebugBytecodeRootNode implements BytecodeRootNode {
134174

@@ -137,6 +177,24 @@ protected NodeOptimizationTestNode(BytecodeDSLTestLanguage language,
137177
super(language, frameDescriptor.build());
138178
}
139179

180+
@Operation
181+
static final class SpecializationTest {
182+
@Specialization
183+
public static int doInt(int a) {
184+
return a;
185+
}
186+
187+
@Specialization
188+
public static long doLong(long a) {
189+
return a;
190+
}
191+
192+
@Specialization
193+
public static boolean doBoolean(boolean a) {
194+
return a;
195+
}
196+
}
197+
140198
@Operation
141199
static final class SingleSpecialization {
142200
@Specialization
@@ -146,13 +204,21 @@ public static Object doDefault(Object v) {
146204
}
147205

148206
@Operation
149-
static final class Bound {
207+
static final class BoundNode {
150208
@Specialization
151209
public static Object doDefault(Object v, @SuppressWarnings("unused") @Bind Node node) {
152210
return v;
153211
}
154212
}
155213

214+
@Operation
215+
static final class BoundBytecodeNode {
216+
@Specialization
217+
public static Object doDefault(Object v, @SuppressWarnings("unused") @Bind BytecodeNode node) {
218+
return v;
219+
}
220+
}
221+
156222
}
157223

158224
}

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

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
package com.oracle.truffle.api.bytecode.test;
4242

4343
import static org.junit.Assert.assertEquals;
44+
import static org.junit.Assert.assertNotNull;
45+
import static org.junit.Assert.assertTrue;
4446

4547
import java.lang.reflect.Field;
4648

@@ -50,6 +52,7 @@
5052
import com.oracle.truffle.api.bytecode.BytecodeParser;
5153
import com.oracle.truffle.api.bytecode.BytecodeRootNode;
5254
import com.oracle.truffle.api.bytecode.GenerateBytecode;
55+
import com.oracle.truffle.api.bytecode.Instruction;
5356
import com.oracle.truffle.api.bytecode.Instrumentation;
5457
import com.oracle.truffle.api.bytecode.Operation;
5558
import com.oracle.truffle.api.bytecode.OperationProxy;
@@ -65,7 +68,7 @@
6568
import com.oracle.truffle.api.nodes.Node;
6669
import com.oracle.truffle.api.nodes.RootNode;
6770

68-
public class StoreBytecodeEliminationTest {
71+
public class StoreBytecodeEliminationTest extends AbstractInstructionTest {
6972

7073
@SuppressWarnings("unchecked")
7174
public static StoreBytecodeEliminationRootNode parseNode(BytecodeParser<?> builder) {
@@ -128,7 +131,32 @@ public void testExplicit2() {
128131
assertEquals(-1, root.getCallTarget().call(1));
129132
}
130133

131-
@GenerateBytecode(languageClass = BytecodeDSLTestLanguage.class, enableQuickening = true, enableUncachedInterpreter = true, storeBytecodeIndexInFrame = true)
134+
@Test
135+
public void testExplicitUsesSingleton() {
136+
StoreBytecodeEliminationRootNode root = parseNode((StoreBytecodeEliminationRootNodeGen.Builder b) -> {
137+
b.beginRoot();
138+
b.beginReturn();
139+
b.beginExplicitTest();
140+
b.emitLoadArgument(0);
141+
b.endExplicitTest();
142+
b.endReturn();
143+
b.endRoot();
144+
});
145+
146+
root.getBytecodeNode().setUncachedThreshold(0);
147+
148+
assertEquals(-1, root.getCallTarget().call(0));
149+
150+
Instruction instruction = root.getBytecodeNode().getInstructionsAsList().get(1);
151+
assertEquals("c.ExplicitTest", instruction.getName());
152+
Instruction.Argument nodeArgument = findNodeArgument(instruction);
153+
assertNotNull(nodeArgument);
154+
155+
assertSingletonNode(nodeArgument);
156+
assertTrue(nodeArgument.getSpecializationInfo().get(0).isActive());
157+
}
158+
159+
@GenerateBytecode(languageClass = BytecodeDSLTestLanguage.class, enableQuickening = true, enableUncachedInterpreter = true, storeBytecodeIndexInFrame = true, enableSpecializationIntrospection = true)
132160
abstract static class StoreBytecodeEliminationRootNode extends RootNode implements BytecodeRootNode {
133161

134162
private static final int BCI_INDEX;

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

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -766,8 +766,8 @@ public void testRootExceptionHandler() {
766766
assertFails(() -> node.getCallTarget().call(), TestException.class);
767767
assertEvents(node,
768768
events,
769-
new Event(EventKind.ENTER, 0x0000, 0x001e, null, RootTag.class),
770-
new Event(EventKind.EXCEPTIONAL, 0x0000, 0x001e, TestException.class, RootTag.class));
769+
new Event(EventKind.ENTER, 0x0000, 0x0020, null, RootTag.class),
770+
new Event(EventKind.EXCEPTIONAL, 0x0000, 0x0020, TestException.class, RootTag.class));
771771
}
772772

773773
@Test
@@ -803,8 +803,8 @@ public void testRootExceptionHandlerReturnValue() {
803803
assertEquals(42, node.getCallTarget().call());
804804
assertEvents(node,
805805
events,
806-
new Event(EventKind.ENTER, 0x0000, 0x0028, null, RootTag.class),
807-
new Event(EventKind.RETURN_VALUE, 0x0000, 0x0028, Integer.class, RootTag.class));
806+
new Event(EventKind.ENTER, 0x0000, 0x002a, null, RootTag.class),
807+
new Event(EventKind.RETURN_VALUE, 0x0000, 0x002a, Integer.class, RootTag.class));
808808

809809
}
810810

@@ -1057,8 +1057,8 @@ public void testImplicitCustomTag() {
10571057

10581058
assertEvents(node,
10591059
events,
1060-
new Event(EventKind.ENTER, 0x0000, 0x001c, null, ExpressionTag.class),
1061-
new Event(EventKind.RETURN_VALUE, 0x0000, 0x001c, 42, ExpressionTag.class));
1060+
new Event(EventKind.ENTER, 0x0000, 0x001e, null, ExpressionTag.class),
1061+
new Event(EventKind.RETURN_VALUE, 0x0000, 0x001e, 42, ExpressionTag.class));
10621062

10631063
}
10641064

@@ -1095,8 +1095,8 @@ public void testImplicitCustomProxyTag() {
10951095

10961096
assertEvents(node,
10971097
events,
1098-
new Event(EventKind.ENTER, 0x0000, 0x001c, null, ExpressionTag.class),
1099-
new Event(EventKind.RETURN_VALUE, 0x0000, 0x001c, 42, ExpressionTag.class));
1098+
new Event(EventKind.ENTER, 0x0000, 0x001e, null, ExpressionTag.class),
1099+
new Event(EventKind.RETURN_VALUE, 0x0000, 0x001e, 42, ExpressionTag.class));
11001100

11011101
}
11021102

@@ -1212,8 +1212,8 @@ public void testImplicitRootTagProlog() {
12121212

12131213
assertEvents(node,
12141214
events,
1215-
new Event(0, EventKind.ENTER, 0x0000, 0x0028, null, RootTag.class),
1216-
new Event(3, EventKind.RETURN_VALUE, 0x0000, 0x0028, 42, RootTag.class));
1215+
new Event(0, EventKind.ENTER, 0x0000, 0x002a, null, RootTag.class),
1216+
new Event(3, EventKind.RETURN_VALUE, 0x0000, 0x002a, 42, RootTag.class));
12171217

12181218
}
12191219

@@ -1264,8 +1264,8 @@ public void testImplicitRootBodyTagProlog() {
12641264

12651265
assertEvents(node,
12661266
events,
1267-
new Event(1, EventKind.ENTER, 0x0006, 0x0028, null, RootBodyTag.class),
1268-
new Event(2, EventKind.RETURN_VALUE, 0x0006, 0x0028, 42, RootBodyTag.class));
1267+
new Event(1, EventKind.ENTER, 0x0006, 0x002a, null, RootBodyTag.class),
1268+
new Event(2, EventKind.RETURN_VALUE, 0x0006, 0x002a, 42, RootBodyTag.class));
12691269
}
12701270

12711271
@Test
@@ -1317,10 +1317,10 @@ public void testImplicitRootTagsProlog() {
13171317

13181318
assertEvents(node,
13191319
events,
1320-
new Event(0, EventKind.ENTER, 0x0000, 0x004c, null, RootTag.class),
1321-
new Event(2, EventKind.ENTER, 0x000c, 0x0038, null, RootBodyTag.class),
1322-
new Event(3, EventKind.RETURN_VALUE, 0x000c, 0x0038, 42, RootBodyTag.class),
1323-
new Event(5, EventKind.RETURN_VALUE, 0x0000, 0x004c, 42, RootTag.class));
1320+
new Event(0, EventKind.ENTER, 0x0000, 0x0050, null, RootTag.class),
1321+
new Event(2, EventKind.ENTER, 0x000c, 0x003a, null, RootBodyTag.class),
1322+
new Event(3, EventKind.RETURN_VALUE, 0x000c, 0x003a, 42, RootBodyTag.class),
1323+
new Event(5, EventKind.RETURN_VALUE, 0x0000, 0x0050, 42, RootTag.class));
13241324

13251325
}
13261326

truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/AbstractBasicInterpreterTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,9 @@ protected static void testIntrospectionInvariants(BytecodeNode bytecode) {
382382
Node node = arg.asCachedNode();
383383
if (bytecode.getTier() == BytecodeTier.CACHED) {
384384
assertNotNull(node);
385-
assertSame(bytecode, node.getParent());
385+
if (node.isAdoptable()) {
386+
assertSame(bytecode, node.getParent());
387+
}
386388
assertNotNull(arg.getSpecializationInfo());
387389
} else {
388390
assertNull(node);

truffle/src/com.oracle.truffle.api.bytecode/snapshot.sigtest

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ CLSS public abstract static com.oracle.truffle.api.bytecode.Instruction$Argument
366366
cons protected init(java.lang.Object)
367367
innr public final static !enum Kind
368368
innr public final static BranchProfile
369+
meth protected java.util.List<com.oracle.truffle.api.dsl.Introspection$SpecializationInfo> getSpecializationInfoInternal()
369370
meth public abstract com.oracle.truffle.api.bytecode.Instruction$Argument$Kind getKind()
370371
meth public abstract java.lang.String getName()
371372
meth public com.oracle.truffle.api.bytecode.Instruction$Argument$BranchProfile asBranchProfile()

0 commit comments

Comments
 (0)