Skip to content

Commit 9461021

Browse files
committed
[GR-61578] [GR-61663] Bytecode DSL fixes: uncached transitions and variadic operands to fallback specializations.
PullRequest: graal/19915
2 parents 251fa9b + d514762 commit 9461021

File tree

7 files changed

+143
-47
lines changed

7 files changed

+143
-47
lines changed

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/BytecodeDSLPartialEvaluationTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,12 +384,12 @@ public void testVariadicLength() {
384384
b.beginBlock();
385385

386386
b.beginReturn();
387-
b.beginVeryComplexOperation();
387+
b.beginVariadicOperation();
388388
b.emitLoadConstant(3L);
389389
for (int i = 0; i < numVariadic; i++) {
390390
b.emitLoadNull();
391391
}
392-
b.endVeryComplexOperation();
392+
b.endVariadicOperation();
393393
b.endReturn();
394394

395395
b.endBlock();

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,11 +323,16 @@ public static String addStrings(String lhs, long constantRhs) {
323323
}
324324

325325
@Operation
326-
static final class VeryComplexOperation {
326+
static final class VariadicOperation {
327327
@Specialization
328-
public static long bla(long a1, @Variadic Object[] a2) {
328+
public static long doLong(long a1, @Variadic Object[] a2) {
329329
return a1 + a2.length;
330330
}
331+
332+
@Fallback
333+
public static long doOther(@SuppressWarnings("unused") Object a1, @Variadic Object[] a2) {
334+
return a2.length;
335+
}
331336
}
332337

333338
@Operation
@@ -524,6 +529,19 @@ protected static boolean callTargetMatches(CallTarget left, CallTarget right) {
524529
}
525530
}
526531

532+
@Operation
533+
public static final class InvokeRecursive {
534+
@Specialization(guards = "true")
535+
public static Object doRootNode(@Variadic Object[] args, @Cached("create($rootNode.getCallTarget())") DirectCallNode callNode) {
536+
return callNode.call(args);
537+
}
538+
539+
@Specialization(replaces = {"doRootNode"})
540+
public static Object doRootNodeUncached(@Variadic Object[] args, @Bind BasicInterpreter root, @Shared @Cached IndirectCallNode callNode) {
541+
return callNode.call(root.getCallTarget(), args);
542+
}
543+
}
544+
527545
@Operation
528546
public static final class MaterializeFrame {
529547
@Specialization

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

Lines changed: 99 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,15 +1857,15 @@ public void testLocalsNonlocalDifferentFrameSizes() {
18571857

18581858
@Test
18591859
public void testVariadicZeroVarargs() {
1860-
// return veryComplex(7);
1860+
// return variadicOperation(7);
18611861

18621862
RootCallTarget root = parse("variadicZeroVarargs", b -> {
18631863
b.beginRoot();
18641864

18651865
b.beginReturn();
1866-
b.beginVeryComplexOperation();
1866+
b.beginVariadicOperation();
18671867
b.emitLoadConstant(7L);
1868-
b.endVeryComplexOperation();
1868+
b.endVariadicOperation();
18691869
b.endReturn();
18701870

18711871
b.endRoot();
@@ -1876,16 +1876,16 @@ public void testVariadicZeroVarargs() {
18761876

18771877
@Test
18781878
public void testVariadicOneVarargs() {
1879-
// return veryComplex(7, "foo");
1879+
// return variadicOperation(7, "foo");
18801880

18811881
RootCallTarget root = parse("variadicOneVarargs", b -> {
18821882
b.beginRoot();
18831883

18841884
b.beginReturn();
1885-
b.beginVeryComplexOperation();
1885+
b.beginVariadicOperation();
18861886
b.emitLoadConstant(7L);
18871887
b.emitLoadConstant("foo");
1888-
b.endVeryComplexOperation();
1888+
b.endVariadicOperation();
18891889
b.endReturn();
18901890

18911891
b.endRoot();
@@ -1896,18 +1896,18 @@ public void testVariadicOneVarargs() {
18961896

18971897
@Test
18981898
public void testVariadicFewVarargs() {
1899-
// return veryComplex(7, "foo", "bar", "baz");
1899+
// return variadicOperation(7, "foo", "bar", "baz");
19001900

19011901
RootCallTarget root = parse("variadicFewVarargs", b -> {
19021902
b.beginRoot();
19031903

19041904
b.beginReturn();
1905-
b.beginVeryComplexOperation();
1905+
b.beginVariadicOperation();
19061906
b.emitLoadConstant(7L);
19071907
b.emitLoadConstant("foo");
19081908
b.emitLoadConstant("bar");
19091909
b.emitLoadConstant("baz");
1910-
b.endVeryComplexOperation();
1910+
b.endVariadicOperation();
19111911
b.endReturn();
19121912

19131913
b.endRoot();
@@ -1918,18 +1918,18 @@ public void testVariadicFewVarargs() {
19181918

19191919
@Test
19201920
public void testVariadicManyVarargs() {
1921-
// return veryComplex(7, [1330 args]);
1921+
// return variadicOperation(7, [1330 args]);
19221922

19231923
RootCallTarget root = parse("variadicManyVarArgs", b -> {
19241924
b.beginRoot();
19251925

19261926
b.beginReturn();
1927-
b.beginVeryComplexOperation();
1927+
b.beginVariadicOperation();
19281928
b.emitLoadConstant(7L);
19291929
for (int i = 0; i < 1330; i++) {
19301930
b.emitLoadConstant("test");
19311931
}
1932-
b.endVeryComplexOperation();
1932+
b.endVariadicOperation();
19331933
b.endReturn();
19341934

19351935
b.endRoot();
@@ -1938,15 +1938,36 @@ public void testVariadicManyVarargs() {
19381938
assertEquals(1337L, root.call());
19391939
}
19401940

1941+
public void testVariadicFallback() {
1942+
// return variadicOperation(arg0, arg1, arg2);
1943+
1944+
RootCallTarget root = parse("variadicFallback", b -> {
1945+
b.beginRoot();
1946+
1947+
b.beginReturn();
1948+
b.beginVariadicOperation();
1949+
b.emitLoadArgument(0);
1950+
b.emitLoadArgument(1);
1951+
b.emitLoadArgument(2);
1952+
b.endVariadicOperation();
1953+
b.endReturn();
1954+
1955+
b.endRoot();
1956+
});
1957+
1958+
assertEquals(42L, root.call(40L, "foo", "bar"));
1959+
assertEquals(2L, root.call("foo", "bar", "baz"));
1960+
}
1961+
19411962
@Test
19421963
public void testVariadicTooFewArguments() {
1943-
assertThrowsWithMessage("Operation VeryComplexOperation expected at least 1 child, but 0 provided. This is probably a bug in the parser.", IllegalStateException.class, () -> {
1964+
assertThrowsWithMessage("Operation VariadicOperation expected at least 1 child, but 0 provided. This is probably a bug in the parser.", IllegalStateException.class, () -> {
19441965
parse("variadicTooFewArguments", b -> {
19451966
b.beginRoot();
19461967

19471968
b.beginReturn();
1948-
b.beginVeryComplexOperation();
1949-
b.endVeryComplexOperation();
1969+
b.beginVariadicOperation();
1970+
b.endVariadicOperation();
19501971
b.endReturn();
19511972

19521973
b.endRoot();
@@ -2561,6 +2582,69 @@ public void testTransitionToCachedLoop() {
25612582
assertEquals(24L, node.getCallTarget().call(24L));
25622583
}
25632584

2585+
@Test
2586+
public void testTransitionToCachedRecursive() {
2587+
assumeTrue(run.hasUncachedInterpreter());
2588+
BasicInterpreter node = parseNode("transitionToCachedRecursive", b -> {
2589+
// function f(x) { return 0 < x ? x + f(x-1) : 0 }
2590+
b.beginRoot();
2591+
b.beginIfThenElse();
2592+
b.beginLess();
2593+
b.emitLoadConstant(0L);
2594+
b.emitLoadArgument(0);
2595+
b.endLess();
2596+
2597+
b.beginReturn();
2598+
b.beginAdd();
2599+
b.emitLoadArgument(0);
2600+
b.beginInvokeRecursive();
2601+
b.beginAddConstantOperation(-1L);
2602+
b.emitLoadArgument(0);
2603+
b.endAddConstantOperation();
2604+
b.endInvokeRecursive();
2605+
b.endAdd();
2606+
b.endReturn();
2607+
2608+
b.beginReturn();
2609+
b.emitLoadConstant(0L);
2610+
b.endReturn();
2611+
2612+
b.endIfThenElse();
2613+
b.endRoot();
2614+
});
2615+
2616+
node.getBytecodeNode().setUncachedThreshold(22);
2617+
assertEquals(BytecodeTier.UNCACHED, node.getBytecodeNode().getTier());
2618+
assertEquals(20 * 21 / 2L, node.getCallTarget().call(20L)); // 21 calls
2619+
assertEquals(BytecodeTier.UNCACHED, node.getBytecodeNode().getTier());
2620+
node.getBytecodeNode().setUncachedThreshold(21);
2621+
assertEquals(20 * 21 / 2L, node.getCallTarget().call(20L)); // 21 calls
2622+
assertEquals(BytecodeTier.CACHED, node.getBytecodeNode().getTier());
2623+
}
2624+
2625+
@Test
2626+
public void testTransitionToCachedYield() {
2627+
assumeTrue(run.hasUncachedInterpreter());
2628+
BasicInterpreter node = parseNode("transitionToCachedYield", b -> {
2629+
b.beginRoot();
2630+
for (int i = 0; i < 20; i++) {
2631+
b.beginYield();
2632+
b.emitLoadNull();
2633+
b.endYield();
2634+
}
2635+
b.endRoot();
2636+
});
2637+
2638+
node.getBytecodeNode().setUncachedThreshold(16);
2639+
assertEquals(BytecodeTier.UNCACHED, node.getBytecodeNode().getTier());
2640+
ContinuationResult cont = (ContinuationResult) node.getCallTarget().call();
2641+
for (int i = 1; i < 16; i++) {
2642+
assertEquals(BytecodeTier.UNCACHED, node.getBytecodeNode().getTier());
2643+
cont = (ContinuationResult) cont.continueWith(null);
2644+
}
2645+
assertEquals(BytecodeTier.CACHED, node.getBytecodeNode().getTier());
2646+
}
2647+
25642648
@Test
25652649
public void testInvalidDefaultUncachedThreshold() {
25662650
assumeTrue(run.hasUncachedInterpreter());

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@
7070
* <p>
7171
* The {@link #getTier() tier} of a bytecode node initially always starts out as
7272
* {@link BytecodeTier#UNCACHED}. This means that no cached nodes were created yet. The
73-
* {@link #setUncachedThreshold(int) uncached threshold} determines how many calls, back-edges, and
74-
* yields are necessary for the node to transition to the cached tier. By default the uncached
73+
* {@link #setUncachedThreshold(int) uncached threshold} determines how many calls/resumes and
74+
* back-edges are necessary for the node to transition to the cached tier. By default the uncached
7575
* threshold is 16 if the {@link GenerateBytecode#enableUncachedInterpreter() uncached interpreter}
7676
* is enabled, and 0 if not (i.e., it will transition to cached on the first execution). The
7777
* intention of the uncached bytecode tier is to reduce the footprint of root nodes that are
@@ -937,7 +937,7 @@ protected void setLocalValueInternalDouble(Frame frame, int localOffset, int loc
937937
public abstract List<LocalVariable> getLocals();
938938

939939
/**
940-
* Sets the number of times the uncached interpreter must return, branch backwards, or yield
940+
* Sets the number of times the uncached interpreter must be invoked/resumed or branch backwards
941941
* before transitioning to cached. See {@link GenerateBytecode#defaultUncachedThreshold} for
942942
* information about the default threshold and the meaning of different {@code threshold}
943943
* values.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@
125125
boolean enableUncachedInterpreter() default false;
126126

127127
/**
128-
* Sets the default number of times an uncached interpreter must return, branch backwards, or
129-
* yield before transitioning to cached.
128+
* Sets the default number of times an uncached interpreter must be invoked/resumed or branch
129+
* backwards before transitioning to cached.
130130
* <p>
131131
* The default uncached threshold expression supports a subset of Java (see the
132132
* {@link com.oracle.truffle.api.dsl.Cached Cached} documentation). It should evaluate to an

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

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13122,10 +13122,13 @@ private List<CodeExecutableElement> createContinueAt() {
1312213122
b.startDeclaration(types.Node, "prev").startCall("encapsulatingNode", "set").string("this").end().end();
1312313123
b.startTryBlock();
1312413124

13125-
b.statement("int uncachedExecuteCount = this.uncachedExecuteCount_");
13126-
b.startIf().string("uncachedExecuteCount <= 0 && uncachedExecuteCount != ", FORCE_UNCACHED_THRESHOLD).end().startBlock();
13125+
b.startIf().string("uncachedExecuteCount_ <= 1").end().startBlock();
13126+
b.startIf().string("uncachedExecuteCount_ != " + FORCE_UNCACHED_THRESHOLD).end().startBlock();
1312713127
b.statement("$root.transitionToCached(frame, 0)");
1312813128
b.startReturn().string("startState").end();
13129+
b.end(2);
13130+
b.startElseBlock();
13131+
b.statement("uncachedExecuteCount_--");
1312913132
b.end();
1313013133
}
1313113134

@@ -13551,14 +13554,19 @@ private void buildInstructionCaseBlock(CodeTreeBuilder b, InstructionModel instr
1355113554
case BRANCH_BACKWARD:
1355213555
if (tier.isUncached()) {
1355313556
b.statement("bci = " + readImmediate("bc", "bci", instr.getImmediate(ImmediateKind.BYTECODE_INDEX)));
13554-
b.startIf().string("uncachedExecuteCount <= 1").end().startBlock();
13555-
b.startIf().string("uncachedExecuteCount != ", FORCE_UNCACHED_THRESHOLD).end().startBlock();
13557+
13558+
b.startIf().string("uncachedExecuteCount_ <= 1").end().startBlock();
13559+
/*
13560+
* The force uncached check is put in here so that we don't need to check it
13561+
* in the common case (the else branch where we just decrement).
13562+
*/
13563+
b.startIf().string("uncachedExecuteCount_ != ", FORCE_UNCACHED_THRESHOLD).end().startBlock();
1355613564
b.tree(GeneratorUtils.createTransferToInterpreterAndInvalidate());
1355713565
b.statement("$root.transitionToCached(frame, bci)");
1355813566
b.statement("return ", encodeState("bci", "sp"));
13559-
b.end();
13560-
b.end().startElseBlock();
13561-
b.statement("uncachedExecuteCount--");
13567+
b.end(2);
13568+
b.startElseBlock();
13569+
b.statement("uncachedExecuteCount_--");
1356213570
b.end();
1356313571
} else {
1356413572
emitReportLoopCount(b, CodeTreeBuilder.createBuilder().string("++loopCounter.value >= ").staticReference(loopCounter.asType(), "REPORT_LOOP_STRIDE").build(), true);
@@ -16202,21 +16210,7 @@ private static void emitReturnTopOfStack(CodeTreeBuilder b) {
1620216210
}
1620316211

1620416212
private void emitBeforeReturnProfiling(CodeTreeBuilder b) {
16205-
if (tier.isUncached()) {
16206-
b.startIf().string("uncachedExecuteCount <= 1").end().startBlock();
16207-
/*
16208-
* The force uncached check is put in here so that we don't need to check it in the
16209-
* common case (the else branch where we just decrement).
16210-
*/
16211-
b.startIf().string("uncachedExecuteCount != ", FORCE_UNCACHED_THRESHOLD).end().startBlock();
16212-
b.tree(GeneratorUtils.createTransferToInterpreterAndInvalidate());
16213-
b.statement("$root.transitionToCached(frame, bci)");
16214-
b.end();
16215-
b.end().startElseBlock();
16216-
b.statement("uncachedExecuteCount--");
16217-
b.statement("this.uncachedExecuteCount_ = uncachedExecuteCount");
16218-
b.end();
16219-
} else {
16213+
if (tier.isCached()) {
1622016214
emitReportLoopCount(b, CodeTreeBuilder.singleString("loopCounter.value > 0"), false);
1622116215
}
1622216216
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ public SpecializationSignature parse(ExecutableElement specialization, MessageCo
156156
* synthesize our own execute method that only takes Object arguments, fallback
157157
* specializations with non-Object parameters are unsupported.
158158
*/
159-
if (!isObject(dynamicOperand.asType())) {
159+
if (!isObject(dynamicOperand.asType()) && !isVariadic(dynamicOperand)) {
160160
if (errorTarget != null) {
161161
errorTarget.addError(dynamicOperand, "Operands to @%s specializations of Operation nodes must have type %s.",
162162
getSimpleName(types.Fallback),

0 commit comments

Comments
 (0)