Skip to content

Commit 40812b3

Browse files
committed
[GR-64533] Fix uncached should include all specializations that do not have multiple instances, to avoid bad stack values produced by uncached interpreters.
PullRequest: graal/20634
2 parents 55b63eb + 066d8ea commit 40812b3

File tree

21 files changed

+334
-82
lines changed

21 files changed

+334
-82
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ protected DynamicallyDispatchedObject(Shape shape) {
135135
@SuppressWarnings("unused")
136136
@ExportMessage
137137
static class Accepts {
138-
@Specialization(guards = "cachedShape == receiver.getShape()")
138+
@Specialization(guards = "cachedShape == receiver.getShape()", excludeForUncached = true)
139139
@SuppressWarnings("unused")
140140
static boolean doCachedShape(DynamicallyDispatchedObject receiver,
141141
@Shared("cachedShape") @Cached("receiver.getShape()") Shape cachedShape,
@@ -153,7 +153,7 @@ static boolean doCachedTypeClass(DynamicallyDispatchedObject receiver,
153153
@SuppressWarnings("unused")
154154
@ExportMessage
155155
static class Dispatch {
156-
@Specialization(guards = "cachedShape == receiver.getShape()")
156+
@Specialization(guards = "cachedShape == receiver.getShape()", excludeForUncached = true)
157157
static Class<?> doCachedShape(DynamicallyDispatchedObject receiver,
158158
@Shared("cachedShape") @Cached("receiver.getShape()") Shape cachedShape,
159159
@Shared("cachedTypeClass") @Cached(value = "receiver.getShape().getDynamicType().getClass()", allowUncached = true) Class<? extends Object> typeClass) {

truffle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan
1414
* GR-63201 Added `TruffleLanguage.Registration.optionalResources` and `TruffleInstrument.Registration.optionalResources` attributes to support optional resources which implementations are not available at the runtime. Optional resources, if omitted at runtime, can still be used as long as their resource path is specified via the `polyglot.engine.resourcePath.<componentId>` system property.
1515
* GR-61282 Bytecode DSL: Bytecode builders now also allow emitting source sections using the start and length source indices in the end method in addition to the begin method. This was added to support linear parsing without look-ahead.
1616
* GR-61282 Bytecode DSL: (breaking) If multiple source sections were specified around root operations, only the innermost source section directly encapsulating the root will be accessible. Other encapsulating source sections will be discarded for outer most root operations.
17+
* GR-64533 By default every specialization is now included for {@link GenerateUncached}, except specializations that require a {@link Specialization#limit() limit} and are replaced, those are excluded by default. By setting `@Specialization(excludeForUncached=..)` explicitly the default behavior can be overridden, e.g. to include or exclude a specialization for uncached. Specializations which are no longer compatible with uncached will produce a warning instead of an error for compatibility reasons until all languages are migrated. It is therefore expected that language implementations may see new warnings with this version.
1718

1819
## Version 24.2.0
1920
* GR-60636 Truffle now stops compiling when the code cache fills up on HotSpot. A warning is printed when that happens.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ private void enableTraceFun() {
214214
@Operation
215215
@ConstantOperand(type = SetTraceFuncRootNode.class)
216216
static final class Invoke {
217-
@Specialization
217+
@Specialization(excludeForUncached = true)
218218
public static void doInvoke(@SuppressWarnings("unused") SetTraceFuncRootNode callee,
219219
@Cached("create(callee.getCallTarget())") DirectCallNode callNode) {
220220
callNode.call();

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ protected static boolean callTargetMatches(CallTarget left, CallTarget right) {
543543

544544
@Operation
545545
public static final class InvokeRecursive {
546-
@Specialization(guards = "true")
546+
@Specialization(guards = "true", excludeForUncached = true)
547547
public static Object doRootNode(@Variadic Object[] args, @Cached("create($rootNode.getCallTarget())") DirectCallNode callNode) {
548548
return callNode.call(args);
549549
}

truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CachedNodeTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,8 @@ public abstract static class ErrorNode4 extends Node {
377377
@Specialization
378378
static Object s0(Object arg0,
379379
@ExpectError("Failed to generate code for @GenerateUncached: The specialization uses @Cached without valid uncached expression. " +
380-
"Error parsing expression 'getUncached()': The method getUncached is undefined for the enclosing scope.. " +
381-
"To resolve this specify the uncached or allowUncached attribute in @Cached.") //
380+
"Error parsing expression 'getUncached()': The method getUncached is undefined for the enclosing scope. " +
381+
"To resolve this specify the uncached or allowUncached attribute in @Cached or exclude the specialization from @GenerateUncached using @Specialization(excludeForUncached=true).") //
382382
@Cached InvalidNode4 foo) {
383383
return arg0;
384384
}

truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/GenerateUncachedTest.java

Lines changed: 193 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,30 @@
5050
import com.oracle.truffle.api.Truffle;
5151
import com.oracle.truffle.api.dsl.Cached;
5252
import com.oracle.truffle.api.dsl.Fallback;
53+
import com.oracle.truffle.api.dsl.GenerateInline;
5354
import com.oracle.truffle.api.dsl.GenerateUncached;
5455
import com.oracle.truffle.api.dsl.ImplicitCast;
5556
import com.oracle.truffle.api.dsl.NodeChild;
5657
import com.oracle.truffle.api.dsl.Specialization;
5758
import com.oracle.truffle.api.dsl.TypeSystem;
5859
import com.oracle.truffle.api.dsl.TypeSystemReference;
5960
import com.oracle.truffle.api.dsl.UnsupportedSpecializationException;
61+
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.CachedLegacyTestNodeGen;
6062
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.NodeChildTest0NodeGen;
6163
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.NodeChildTest1NodeGen;
6264
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.TestNonStaticSpecializationNodeGen;
65+
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached10NodeGen;
66+
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached11NodeGen;
67+
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached12NodeGen;
6368
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached1NodeGen;
6469
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached2NodeGen;
6570
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached3NodeGen;
6671
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached4NodeGen;
6772
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached5NodeGen;
6873
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached6NodeGen;
6974
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached7NodeGen;
75+
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached8NodeGen;
76+
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached9NodeGen;
7077
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.UncachedTrivial1NodeGen;
7178
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.UncachedTrivial2NodeGen;
7279
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.UncachedTrivial3NodeGen;
@@ -273,6 +280,135 @@ public void testUncached7() {
273280
assertEquals("fallback", node.execute(42f));
274281
}
275282

283+
@GenerateUncached
284+
abstract static class Uncached8Node extends Node {
285+
286+
abstract Object execute(Object arg);
287+
288+
@Specialization(guards = "v == cachedV", limit = "3", excludeForUncached = false)
289+
static String s1(int v, @Cached("v") int cachedV) {
290+
return "s1";
291+
}
292+
293+
@Specialization
294+
static String s2(int v) { // effectively unreachable for uncached
295+
return "s2";
296+
}
297+
298+
}
299+
300+
@Test
301+
public void testUncached8() {
302+
Uncached8Node node = Uncached8NodeGen.getUncached();
303+
assertEquals("s1", node.execute(42));
304+
assertEquals("s1", node.execute(44));
305+
assertEquals("s1", node.execute(45));
306+
assertEquals("s1", node.execute(46));
307+
}
308+
309+
@GenerateUncached
310+
abstract static class Uncached9Node extends Node {
311+
312+
abstract String execute();
313+
314+
static Assumption getAssumption() {
315+
return Assumption.ALWAYS_VALID;
316+
}
317+
318+
@Specialization(assumptions = "getAssumption()")
319+
static String s0() {
320+
return "s0";
321+
}
322+
323+
@Specialization(replaces = "s0")
324+
static String s1() throws ArithmeticException {
325+
return "s1";
326+
}
327+
328+
}
329+
330+
@Test
331+
public void testUncached9() {
332+
Uncached9Node node = Uncached9NodeGen.getUncached();
333+
assertEquals("s0", node.execute());
334+
}
335+
336+
@GenerateUncached
337+
abstract static class Uncached10Node extends Node {
338+
339+
abstract String execute();
340+
341+
static Assumption getAssumption() {
342+
return Assumption.NEVER_VALID;
343+
}
344+
345+
@Specialization(assumptions = "getAssumption()")
346+
static String s0() {
347+
return "s0";
348+
}
349+
350+
@Specialization(replaces = "s0")
351+
static String s1() throws ArithmeticException {
352+
return "s1";
353+
}
354+
}
355+
356+
@Test
357+
public void testUncached10() {
358+
Uncached10Node node = Uncached10NodeGen.getUncached();
359+
assertEquals("s1", node.execute());
360+
}
361+
362+
@GenerateUncached
363+
abstract static class Uncached11Node extends Node {
364+
365+
abstract String execute();
366+
367+
@Specialization(excludeForUncached = true)
368+
static String s0() {
369+
return "s0";
370+
}
371+
372+
@Specialization(replaces = "s0")
373+
static String s1() {
374+
return "s1";
375+
}
376+
}
377+
378+
@Test
379+
public void testUncached11() {
380+
Uncached11Node node = Uncached11NodeGen.getUncached();
381+
assertEquals("s1", node.execute());
382+
}
383+
384+
@GenerateUncached
385+
abstract static class Uncached12Node extends Node {
386+
387+
abstract String execute(Object arg);
388+
389+
@Specialization
390+
static String s0(int arg) {
391+
return "s0";
392+
}
393+
394+
@Specialization(replaces = "s0", excludeForUncached = true)
395+
static String s1(int arg) {
396+
return "s1";
397+
}
398+
399+
// specialization to make AlwaysGenerateOnlySlowPath happy
400+
@Specialization
401+
static String s2(double b) {
402+
return "s2";
403+
}
404+
}
405+
406+
@Test
407+
public void testUncached12() {
408+
Uncached12Node node = Uncached12NodeGen.getUncached();
409+
assertEquals("s0", node.execute(42));
410+
}
411+
276412
@GenerateUncached
277413
abstract static class UncachedTrivial1Node extends Node {
278414

@@ -482,8 +618,8 @@ abstract static class ErrorNode2 extends Node {
482618
@Specialization
483619
static int f0(int v,
484620
@ExpectError("Failed to generate code for @GenerateUncached: The specialization uses @Cached without valid uncached expression. " +
485-
"Error parsing expression 'getUncached()': The method getUncached is undefined for the enclosing scope.. " +
486-
"To resolve this specify the uncached or allowUncached attribute in @Cached.") //
621+
"Error parsing expression 'getUncached()': The method getUncached is undefined for the enclosing scope. " +
622+
"To resolve this specify the uncached or allowUncached attribute in @Cached or exclude the specialization from @GenerateUncached using @Specialization(excludeForUncached=true).") //
487623
@Cached("nonTrivialCache(v)") int cachedV) {
488624
return v;
489625
}
@@ -533,23 +669,6 @@ static int f0(int v) {
533669

534670
}
535671

536-
@GenerateUncached
537-
abstract static class ErrorNode7 extends Node {
538-
539-
abstract Object execute(Object arg);
540-
541-
@ExpectError("Failed to generate code for @GenerateUncached: The specialization rewrites on exceptions and there is no specialization that replaces it. Add a replaces=\"s1\" class to specialization below to resolve this problem.")
542-
@Specialization(rewriteOn = ArithmeticException.class)
543-
static String s1(int v) {
544-
return "s1";
545-
}
546-
547-
@Specialization
548-
static String s2(int v) {
549-
return "s2";
550-
}
551-
}
552-
553672
@GenerateUncached
554673
abstract static class ErrorNonTrivialNode1 extends Node {
555674

@@ -558,8 +677,8 @@ abstract static class ErrorNonTrivialNode1 extends Node {
558677
@Specialization
559678
static String s1(Object v,
560679
@ExpectError("Failed to generate code for @GenerateUncached: The specialization uses @Cached without valid uncached expression. " +
561-
"Error parsing expression 'getUncached()': The method getUncached is undefined for the enclosing scope.. " +
562-
"To resolve this specify the uncached or allowUncached attribute in @Cached.")//
680+
"Error parsing expression 'getUncached()': The method getUncached is undefined for the enclosing scope. " +
681+
"To resolve this specify the uncached or allowUncached attribute in @Cached or exclude the specialization from @GenerateUncached using @Specialization(excludeForUncached=true).") //
563682
@Cached("foo(v)") Object cached) {
564683
return "s1";
565684
}
@@ -768,4 +887,57 @@ public void testNodeChild1() {
768887
assertEquals("s0", NodeChildTest1NodeGen.getUncached().execute(0, 0));
769888
assertEquals("s1", NodeChildTest1NodeGen.getUncached().execute(42, 42));
770889
}
890+
891+
@GenerateUncached
892+
@GenerateInline(false)
893+
abstract static class CachedLegacyTestNode extends Node {
894+
895+
abstract String execute();
896+
897+
@Specialization
898+
static String s0(
899+
// actually a warning
900+
@ExpectError("Failed to generate code for @GenerateUncached: The specialization uses @Cached without valid uncached expression%") //
901+
@Cached("create()") Assumption assumption) {
902+
return "s0";
903+
}
904+
905+
@Specialization(replaces = "s0")
906+
static String s1() throws ArithmeticException {
907+
return "s1";
908+
}
909+
}
910+
911+
@Test
912+
public void testCachedLegacyTestNode() {
913+
// still executable as the error is only a warning
914+
assertEquals("s1", CachedLegacyTestNodeGen.getUncached().execute());
915+
}
916+
917+
abstract static class WarningUnusedNode extends Node {
918+
919+
abstract String execute();
920+
921+
@ExpectError("The attribute excludeForUncached has no effect as the node is not configured for uncached generation.%")
922+
@Specialization(excludeForUncached = true)
923+
static String s0() {
924+
return "s0";
925+
}
926+
927+
}
928+
929+
@GenerateUncached
930+
@ExpectError("All specializations were excluded for uncached. At least one specialization must remain included. " +
931+
"Set the excludeForUncached attribute to false for at least one specialization to resolve this problem.")
932+
abstract static class ErrorAllExcludedNode extends Node {
933+
934+
abstract String execute();
935+
936+
@Specialization(excludeForUncached = true)
937+
static String s0() {
938+
return "s0";
939+
}
940+
941+
}
942+
771943
}

truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ReachabilityTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ int foo() {
318318

319319
@GenerateUncached
320320
abstract static class ReachabilityUncached1 extends ReachabilityUncached {
321-
@Specialization
321+
@Specialization(excludeForUncached = true)
322322
int doCached(@Cached("foo()") int cached) {
323323
return cached;
324324
}

truffle/src/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SpecializationStatisticsTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,10 @@ public class SpecializationStatisticsTest {
7474
"| SpecializationStatisticTestNodeGen.Uncached 1 (25%) 1 (6%) Min= 1 Avg= 1.00 Max= 1 MaxNode= N/A %n" +
7575
"| s0 0 (0%) 0 (0%) Min= 0 Avg= 0.00 Max= 0 MaxNode= - %n" +
7676
"| s1 0 (0%) 0 (0%) Min= 0 Avg= 0.00 Max= 0 MaxNode= - %n" +
77-
"| s2 0 (0%) 0 (0%) Min= 0 Avg= 0.00 Max= 0 MaxNode= - %n" +
78-
"| s3 <String> 1 (100%) 1 (100%) Min= 1 Avg= 1.00 Max= 1 MaxNode= N/A %n" +
77+
"| s2 <String> 1 (100%) 1 (100%) Min= 1 Avg= 1.00 Max= 1 MaxNode= N/A %n" +
78+
"| s3 0 (0%) 0 (0%) Min= 0 Avg= 0.00 Max= 0 MaxNode= - %n" +
7979
"| --------------------------------------------------------------------------------------------------------------------------------------------------%n" +
80-
"| [s3] 1 (100%) 1 (100%) Min= 1 Avg= 1.00 Max= 1 MaxNode= N/A %n" +
80+
"| [s2] 1 (100%) 1 (100%) Min= 1 Avg= 1.00 Max= 1 MaxNode= N/A %n" +
8181
" -----------------------------------------------------------------------------------------------------------------------------------------------------%n" +
8282
"| Name Instances Executions Executions per instance %n" +
8383
" -----------------------------------------------------------------------------------------------------------------------------------------------------%n" +

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ CLSS public abstract interface !annotation com.oracle.truffle.api.dsl.Specializa
426426
anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
427427
anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[METHOD])
428428
intf java.lang.annotation.Annotation
429+
meth public abstract !hasdefault boolean excludeForUncached()
429430
meth public abstract !hasdefault int unroll()
430431
meth public abstract !hasdefault java.lang.Class<? extends java.lang.Throwable>[] rewriteOn()
431432
meth public abstract !hasdefault java.lang.String insertBefore()
@@ -557,7 +558,7 @@ meth public void printStackTrace(java.io.PrintStream)
557558
meth public void printStackTrace(java.io.PrintWriter)
558559
meth public void setStackTrace(java.lang.StackTraceElement[])
559560
supr java.lang.Object
560-
hfds CAUSE_CAPTION,EMPTY_THROWABLE_ARRAY,NULL_CAUSE_MESSAGE,SELF_SUPPRESSION_MESSAGE,SUPPRESSED_CAPTION,SUPPRESSED_SENTINEL,UNASSIGNED_STACK,backtrace,cause,depth,detailMessage,serialVersionUID,stackTrace,suppressedExceptions
561+
hfds CAUSE_CAPTION,EMPTY_THROWABLE_ARRAY,NULL_CAUSE_MESSAGE,SELF_SUPPRESSION_MESSAGE,SUPPRESSED_CAPTION,SUPPRESSED_SENTINEL,UNASSIGNED_STACK,backtrace,cause,depth,detailMessage,jfrTracing,serialVersionUID,stackTrace,suppressedExceptions
561562
hcls PrintStreamOrWriter,SentinelHolder,WrappedPrintStream,WrappedPrintWriter
562563

563564
CLSS public abstract interface java.lang.annotation.Annotation

0 commit comments

Comments
 (0)