Skip to content

[GR-64533] Fix uncached should include all specializations that do not have multiple instances, to avoid bad stack values produced by uncached interpreters. #11084

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ protected DynamicallyDispatchedObject(Shape shape) {
@SuppressWarnings("unused")
@ExportMessage
static class Accepts {
@Specialization(guards = "cachedShape == receiver.getShape()")
@Specialization(guards = "cachedShape == receiver.getShape()", excludeForUncached = true)
@SuppressWarnings("unused")
static boolean doCachedShape(DynamicallyDispatchedObject receiver,
@Shared("cachedShape") @Cached("receiver.getShape()") Shape cachedShape,
Expand All @@ -153,7 +153,7 @@ static boolean doCachedTypeClass(DynamicallyDispatchedObject receiver,
@SuppressWarnings("unused")
@ExportMessage
static class Dispatch {
@Specialization(guards = "cachedShape == receiver.getShape()")
@Specialization(guards = "cachedShape == receiver.getShape()", excludeForUncached = true)
static Class<?> doCachedShape(DynamicallyDispatchedObject receiver,
@Shared("cachedShape") @Cached("receiver.getShape()") Shape cachedShape,
@Shared("cachedTypeClass") @Cached(value = "receiver.getShape().getDynamicType().getClass()", allowUncached = true) Class<? extends Object> typeClass) {
Expand Down
1 change: 1 addition & 0 deletions truffle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan
* 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.
* 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.
* 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.
* 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.

## Version 24.2.0
* GR-60636 Truffle now stops compiling when the code cache fills up on HotSpot. A warning is printed when that happens.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ private void enableTraceFun() {
@Operation
@ConstantOperand(type = SetTraceFuncRootNode.class)
static final class Invoke {
@Specialization
@Specialization(excludeForUncached = true)
public static void doInvoke(@SuppressWarnings("unused") SetTraceFuncRootNode callee,
@Cached("create(callee.getCallTarget())") DirectCallNode callNode) {
callNode.call();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ protected static boolean callTargetMatches(CallTarget left, CallTarget right) {

@Operation
public static final class InvokeRecursive {
@Specialization(guards = "true")
@Specialization(guards = "true", excludeForUncached = true)
public static Object doRootNode(@Variadic Object[] args, @Cached("create($rootNode.getCallTarget())") DirectCallNode callNode) {
return callNode.call(args);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,8 +377,8 @@ public abstract static class ErrorNode4 extends Node {
@Specialization
static Object s0(Object arg0,
@ExpectError("Failed to generate code for @GenerateUncached: The specialization uses @Cached without valid uncached expression. " +
"Error parsing expression 'getUncached()': The method getUncached is undefined for the enclosing scope.. " +
"To resolve this specify the uncached or allowUncached attribute in @Cached.") //
"Error parsing expression 'getUncached()': The method getUncached is undefined for the enclosing scope. " +
"To resolve this specify the uncached or allowUncached attribute in @Cached or exclude the specialization from @GenerateUncached using @Specialization(excludeForUncached=true).") //
@Cached InvalidNode4 foo) {
return arg0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,23 +50,30 @@
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.GenerateInline;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.ImplicitCast;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.dsl.TypeSystem;
import com.oracle.truffle.api.dsl.TypeSystemReference;
import com.oracle.truffle.api.dsl.UnsupportedSpecializationException;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.CachedLegacyTestNodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.NodeChildTest0NodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.NodeChildTest1NodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.TestNonStaticSpecializationNodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached10NodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached11NodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached12NodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached1NodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached2NodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached3NodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached4NodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached5NodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached6NodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached7NodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached8NodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.Uncached9NodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.UncachedTrivial1NodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.UncachedTrivial2NodeGen;
import com.oracle.truffle.api.dsl.test.GenerateUncachedTestFactory.UncachedTrivial3NodeGen;
Expand Down Expand Up @@ -273,6 +280,135 @@ public void testUncached7() {
assertEquals("fallback", node.execute(42f));
}

@GenerateUncached
abstract static class Uncached8Node extends Node {

abstract Object execute(Object arg);

@Specialization(guards = "v == cachedV", limit = "3", excludeForUncached = false)
static String s1(int v, @Cached("v") int cachedV) {
return "s1";
}

@Specialization
static String s2(int v) { // effectively unreachable for uncached
return "s2";
}

}

@Test
public void testUncached8() {
Uncached8Node node = Uncached8NodeGen.getUncached();
assertEquals("s1", node.execute(42));
assertEquals("s1", node.execute(44));
assertEquals("s1", node.execute(45));
assertEquals("s1", node.execute(46));
}

@GenerateUncached
abstract static class Uncached9Node extends Node {

abstract String execute();

static Assumption getAssumption() {
return Assumption.ALWAYS_VALID;
}

@Specialization(assumptions = "getAssumption()")
static String s0() {
return "s0";
}

@Specialization(replaces = "s0")
static String s1() throws ArithmeticException {
return "s1";
}

}

@Test
public void testUncached9() {
Uncached9Node node = Uncached9NodeGen.getUncached();
assertEquals("s0", node.execute());
}

@GenerateUncached
abstract static class Uncached10Node extends Node {

abstract String execute();

static Assumption getAssumption() {
return Assumption.NEVER_VALID;
}

@Specialization(assumptions = "getAssumption()")
static String s0() {
return "s0";
}

@Specialization(replaces = "s0")
static String s1() throws ArithmeticException {
return "s1";
}
}

@Test
public void testUncached10() {
Uncached10Node node = Uncached10NodeGen.getUncached();
assertEquals("s1", node.execute());
}

@GenerateUncached
abstract static class Uncached11Node extends Node {

abstract String execute();

@Specialization(excludeForUncached = true)
static String s0() {
return "s0";
}

@Specialization(replaces = "s0")
static String s1() {
return "s1";
}
}

@Test
public void testUncached11() {
Uncached11Node node = Uncached11NodeGen.getUncached();
assertEquals("s1", node.execute());
}

@GenerateUncached
abstract static class Uncached12Node extends Node {

abstract String execute(Object arg);

@Specialization
static String s0(int arg) {
return "s0";
}

@Specialization(replaces = "s0", excludeForUncached = true)
static String s1(int arg) {
return "s1";
}

// specialization to make AlwaysGenerateOnlySlowPath happy
@Specialization
static String s2(double b) {
return "s2";
}
}

@Test
public void testUncached12() {
Uncached12Node node = Uncached12NodeGen.getUncached();
assertEquals("s0", node.execute(42));
}

@GenerateUncached
abstract static class UncachedTrivial1Node extends Node {

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

}

@GenerateUncached
abstract static class ErrorNode7 extends Node {

abstract Object execute(Object arg);

@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.")
@Specialization(rewriteOn = ArithmeticException.class)
static String s1(int v) {
return "s1";
}

@Specialization
static String s2(int v) {
return "s2";
}
}

@GenerateUncached
abstract static class ErrorNonTrivialNode1 extends Node {

Expand All @@ -558,8 +677,8 @@ abstract static class ErrorNonTrivialNode1 extends Node {
@Specialization
static String s1(Object v,
@ExpectError("Failed to generate code for @GenerateUncached: The specialization uses @Cached without valid uncached expression. " +
"Error parsing expression 'getUncached()': The method getUncached is undefined for the enclosing scope.. " +
"To resolve this specify the uncached or allowUncached attribute in @Cached.")//
"Error parsing expression 'getUncached()': The method getUncached is undefined for the enclosing scope. " +
"To resolve this specify the uncached or allowUncached attribute in @Cached or exclude the specialization from @GenerateUncached using @Specialization(excludeForUncached=true).") //
@Cached("foo(v)") Object cached) {
return "s1";
}
Expand Down Expand Up @@ -768,4 +887,57 @@ public void testNodeChild1() {
assertEquals("s0", NodeChildTest1NodeGen.getUncached().execute(0, 0));
assertEquals("s1", NodeChildTest1NodeGen.getUncached().execute(42, 42));
}

@GenerateUncached
@GenerateInline(false)
abstract static class CachedLegacyTestNode extends Node {

abstract String execute();

@Specialization
static String s0(
// actually a warning
@ExpectError("Failed to generate code for @GenerateUncached: The specialization uses @Cached without valid uncached expression%") //
@Cached("create()") Assumption assumption) {
return "s0";
}

@Specialization(replaces = "s0")
static String s1() throws ArithmeticException {
return "s1";
}
}

@Test
public void testCachedLegacyTestNode() {
// still executable as the error is only a warning
assertEquals("s1", CachedLegacyTestNodeGen.getUncached().execute());
}

abstract static class WarningUnusedNode extends Node {

abstract String execute();

@ExpectError("The attribute excludeForUncached has no effect as the node is not configured for uncached generation.%")
@Specialization(excludeForUncached = true)
static String s0() {
return "s0";
}

}

@GenerateUncached
@ExpectError("All specializations were excluded for uncached. At least one specialization must remain included. " +
"Set the excludeForUncached attribute to false for at least one specialization to resolve this problem.")
abstract static class ErrorAllExcludedNode extends Node {

abstract String execute();

@Specialization(excludeForUncached = true)
static String s0() {
return "s0";
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ int foo() {

@GenerateUncached
abstract static class ReachabilityUncached1 extends ReachabilityUncached {
@Specialization
@Specialization(excludeForUncached = true)
int doCached(@Cached("foo()") int cached) {
return cached;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ public class SpecializationStatisticsTest {
"| SpecializationStatisticTestNodeGen.Uncached 1 (25%) 1 (6%) Min= 1 Avg= 1.00 Max= 1 MaxNode= N/A %n" +
"| s0 0 (0%) 0 (0%) Min= 0 Avg= 0.00 Max= 0 MaxNode= - %n" +
"| s1 0 (0%) 0 (0%) Min= 0 Avg= 0.00 Max= 0 MaxNode= - %n" +
"| s2 0 (0%) 0 (0%) Min= 0 Avg= 0.00 Max= 0 MaxNode= - %n" +
"| s3 <String> 1 (100%) 1 (100%) Min= 1 Avg= 1.00 Max= 1 MaxNode= N/A %n" +
"| s2 <String> 1 (100%) 1 (100%) Min= 1 Avg= 1.00 Max= 1 MaxNode= N/A %n" +
"| s3 0 (0%) 0 (0%) Min= 0 Avg= 0.00 Max= 0 MaxNode= - %n" +
"| --------------------------------------------------------------------------------------------------------------------------------------------------%n" +
"| [s3] 1 (100%) 1 (100%) Min= 1 Avg= 1.00 Max= 1 MaxNode= N/A %n" +
"| [s2] 1 (100%) 1 (100%) Min= 1 Avg= 1.00 Max= 1 MaxNode= N/A %n" +
" -----------------------------------------------------------------------------------------------------------------------------------------------------%n" +
"| Name Instances Executions Executions per instance %n" +
" -----------------------------------------------------------------------------------------------------------------------------------------------------%n" +
Expand Down
3 changes: 2 additions & 1 deletion truffle/src/com.oracle.truffle.api.dsl/snapshot.sigtest
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ CLSS public abstract interface !annotation com.oracle.truffle.api.dsl.Specializa
anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[METHOD])
intf java.lang.annotation.Annotation
meth public abstract !hasdefault boolean excludeForUncached()
meth public abstract !hasdefault int unroll()
meth public abstract !hasdefault java.lang.Class<? extends java.lang.Throwable>[] rewriteOn()
meth public abstract !hasdefault java.lang.String insertBefore()
Expand Down Expand Up @@ -557,7 +558,7 @@ meth public void printStackTrace(java.io.PrintStream)
meth public void printStackTrace(java.io.PrintWriter)
meth public void setStackTrace(java.lang.StackTraceElement[])
supr java.lang.Object
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
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
hcls PrintStreamOrWriter,SentinelHolder,WrappedPrintStream,WrappedPrintWriter

CLSS public abstract interface java.lang.annotation.Annotation
Expand Down
Loading
Loading