Skip to content

Commit 0fbbfe1

Browse files
committed
[GR-69649] Inline primitive constant operands in Bytecode DSL interpreters.
PullRequest: graal/22279
2 parents 6ef47fb + 90d42e7 commit 0fbbfe1

File tree

16 files changed

+1273
-233
lines changed

16 files changed

+1273
-233
lines changed

truffle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan
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.
2525
* GR-61293: Bytecode DSL: Specialization state is now inlined into the bytecode array, which reduces memory footprint and interpreter execution time.
26+
* GR-69649: Bytecode DSL now encodes primitive constant operands directly in the bytecode, reducing memory indirections in the interpreter. This optimization is enabled by default; you can configure it with `@GenerateBytecode(inlinePrimitiveConstants = true)`.
2627

2728
## Version 25.0
2829
* 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/ConstantOperandTest.java

Lines changed: 549 additions & 16 deletions
Large diffs are not rendered by default.

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,40 @@ public void testImplicitTagInstrumentation() {
652652
});
653653
}
654654

655+
@Test
656+
public void testYieldQuickeningRegressionTest() {
657+
/*
658+
* Regression test for a quickening bug. The yield's childBci calculation did not account
659+
* for tag.resume instructions, and under the right circumstances, the parent operation
660+
* would "quicken" an instruction operand, leading to unexpected results.
661+
*/
662+
runInstrumentationTest((context, instrumenter) -> {
663+
BytecodeRootNodes<ComplexCustomYieldTestRootNode> nodes = ComplexCustomYieldTestRootNodeGen.create(BytecodeDSLTestLanguage.REF.get(null), BytecodeConfig.DEFAULT, b -> {
664+
b.beginRoot();
665+
b.emitLoadConstant(42);
666+
b.beginReturn();
667+
b.beginAddConstantsYield(1);
668+
b.emitLoadArgument(0);
669+
b.endAddConstantsYield(1);
670+
b.endReturn();
671+
b.endRoot();
672+
});
673+
ComplexCustomYieldTestRootNode root = nodes.getNode(0);
674+
root.getBytecodeNode().setUncachedThreshold(0);
675+
List<Object> yieldValues = new ArrayList<>();
676+
AtomicInteger resumeCount = new AtomicInteger();
677+
instrumenter.attachExecutionEventFactory(SourceSectionFilter.newBuilder().tagIs(StatementTag.class).build(), createFactory(yieldValues, resumeCount));
678+
679+
ContinuationResult cont = (ContinuationResult) root.getCallTarget().call(0);
680+
assertEquals(2, cont.getResult());
681+
assertEquals(123, cont.continueWith(123));
682+
683+
cont = (ContinuationResult) root.getCallTarget().call(0);
684+
assertEquals(2, cont.getResult());
685+
assertEquals(123, cont.continueWith(123));
686+
});
687+
}
688+
655689
private static void runInstrumentationTest(BiConsumer<Context, Instrumenter> test) {
656690
Context context = Context.create(BytecodeDSLTestLanguage.ID);
657691
try {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ meth public abstract !hasdefault boolean enableSpecializationIntrospection()
317317
meth public abstract !hasdefault boolean enableTagInstrumentation()
318318
meth public abstract !hasdefault boolean enableUncachedInterpreter()
319319
meth public abstract !hasdefault boolean enableYield()
320+
meth public abstract !hasdefault boolean inlinePrimitiveConstants()
320321
meth public abstract !hasdefault boolean storeBytecodeIndexInFrame()
321322
meth public abstract !hasdefault java.lang.Class<?> tagTreeNodeLibrary()
322323
meth public abstract !hasdefault java.lang.Class<?>[] boxingEliminationTypes()

truffle/src/com.oracle.truffle.api.bytecode/src/com/oracle/truffle/api/bytecode/GenerateBytecode.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,16 @@
469469
*/
470470
Class<?>[] boxingEliminationTypes() default {};
471471

472+
/**
473+
* Whether constant operands of primitive type should be encoded directly in the bytecode.
474+
* Inlining can reduce the number of memory reads required to access constants.
475+
* <p>
476+
* Currently, inlining is only supported for {@link ConstantOperand}s.
477+
*
478+
* @since 25.1
479+
*/
480+
boolean inlinePrimitiveConstants() default true;
481+
472482
/**
473483
* Whether to generate introspection data for specializations. The data is accessible using
474484
* {@link com.oracle.truffle.api.bytecode.Instruction.Argument#getSpecializationInfo()}.

truffle/src/com.oracle.truffle.api.bytecode/src/com/oracle/truffle/api/bytecode/serialization/BytecodeSerializer.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import java.io.IOException;
4545

4646
import com.oracle.truffle.api.bytecode.BytecodeRootNode;
47+
import com.oracle.truffle.api.bytecode.GenerateBytecode;
4748

4849
/**
4950
* Represents a class that can serialize constants in a bytecode interpreter.
@@ -71,13 +72,14 @@
7172
* A serializer is responsible for encoding:
7273
* <ul>
7374
* <li>objects used as constants in the bytecode (e.g., objects passed to {@code emitLoadConstant}
74-
* or constant operands)</li>
75+
* or constant operands, except primitive ones that are
76+
* {@link GenerateBytecode#inlinePrimitiveConstants inlined})</li>
7577
* <li>objects stored in non-{@code transient} fields of the root node</li>
7678
* <li>{@link com.oracle.truffle.api.source.Source} objects passed in builder calls (i.e., sources
7779
* passed to {@code beginSource})</li>
7880
* </ul>
7981
*
80-
* @see com.oracle.truffle.api.bytecode.GenerateBytecode#enableSerialization
82+
* @see GenerateBytecode#enableSerialization
8183
* @since 24.2
8284
*/
8385
@FunctionalInterface

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

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import com.oracle.truffle.dsl.processor.TruffleTypes;
5757
import com.oracle.truffle.dsl.processor.bytecode.generator.BytecodeRootNodeElement.InterpreterTier;
5858
import com.oracle.truffle.dsl.processor.bytecode.model.BytecodeDSLModel;
59+
import com.oracle.truffle.dsl.processor.bytecode.model.ConstantOperandModel;
5960
import com.oracle.truffle.dsl.processor.bytecode.model.InstructionModel;
6061
import com.oracle.truffle.dsl.processor.bytecode.model.InstructionModel.ImmediateKind;
6162
import com.oracle.truffle.dsl.processor.bytecode.model.InstructionModel.InstructionImmediate;
@@ -163,10 +164,12 @@ public void beforeCallSpecialization(FlatNodeGenFactory nodeFactory, CodeTreeBui
163164
private boolean buildChildExecution(CodeTreeBuilder b, FrameState frameState, String frame, int specializationIndex) {
164165
int operandIndex = specializationIndex;
165166
if (operandIndex < instruction.signature.constantOperandsBeforeCount) {
166-
TypeMirror constantOperandType = instruction.operation.constantOperands.before().get(operandIndex).type();
167-
List<InstructionImmediate> imms = instruction.getImmediates(ImmediateKind.CONSTANT);
168-
InstructionImmediate imm = imms.get(operandIndex);
169-
b.tree(readConstFastPath(imm, constantOperandType));
167+
ConstantOperandModel constantOperand = instruction.operation.constantOperands.before().get(operandIndex);
168+
InstructionImmediate imm = instruction.constantOperandImmediates.get(constantOperand);
169+
if (imm == null) {
170+
throw new AssertionError("Could not find an immediate for constant operand " + constantOperand + " on instruction " + instruction);
171+
}
172+
b.tree(rootNode.readConstantImmediate("$bc", "$bci", "$bytecode", imm, constantOperand.type()));
170173
return false;
171174
}
172175
operandIndex -= instruction.signature.constantOperandsBeforeCount;
@@ -236,20 +239,18 @@ private boolean buildChildExecution(CodeTreeBuilder b, FrameState frameState, St
236239

237240
int constantOperandAfterCount = instruction.signature.constantOperandsAfterCount;
238241
if (operandIndex < constantOperandAfterCount) {
239-
TypeMirror constantOperandType = instruction.operation.constantOperands.after().get(operandIndex).type();
240-
List<InstructionImmediate> imms = instruction.getImmediates(ImmediateKind.CONSTANT);
241-
InstructionImmediate imm = imms.get(instruction.signature.constantOperandsBeforeCount + operandIndex);
242-
b.tree(rootNode.readConstFastPath(readImmediate("$bc", "$bci", imm), "$bytecode.constants", constantOperandType));
242+
ConstantOperandModel constantOperand = instruction.operation.constantOperands.after().get(operandIndex);
243+
InstructionImmediate imm = instruction.constantOperandImmediates.get(constantOperand);
244+
if (imm == null) {
245+
throw new AssertionError("Could not find an immediate for constant operand " + constantOperand + " on instruction " + instruction);
246+
}
247+
b.tree(rootNode.readConstantImmediate("$bc", "$bci", "$bytecode", imm, constantOperand.type()));
243248
return false;
244249
}
245250

246251
throw new AssertionError("index=" + specializationIndex + ", signature=" + instruction.signature);
247252
}
248253

249-
private CodeTree readConstFastPath(InstructionImmediate imm, TypeMirror immediateType) {
250-
return rootNode.readConstFastPath(readImmediate("$bc", "$bci", imm), "$bytecode.constants", immediateType);
251-
}
252-
253254
public CodeExecutableElement getQuickenMethod() {
254255
return quickenMethod;
255256
}
@@ -307,7 +308,7 @@ public CodeTree bindExpressionValue(FrameState frameState, Variable variable) {
307308
return CodeTreeBuilder.singleString("$bci");
308309
case BytecodeDSLParser.SYMBOL_CONTINUATION_ROOT:
309310
InstructionImmediate continuationIndex = instruction.getImmediates(ImmediateKind.CONSTANT).getLast();
310-
return CodeTreeBuilder.createBuilder().tree(readConstFastPath(continuationIndex, rootNode.getContinuationRootNodeImpl().asType())).build();
311+
return CodeTreeBuilder.createBuilder().tree(rootNode.readConstantImmediate("$bc", "$bci", "$bytecode", continuationIndex, rootNode.getContinuationRootNodeImpl().asType())).build();
311312
default:
312313
return NodeGeneratorPlugs.super.bindExpressionValue(frameState, variable);
313314

0 commit comments

Comments
 (0)