Skip to content

Commit fa1dfa2

Browse files
committed
[GR-71109] Assign invalid return profile to targets that throw an exception and have uninitialized return profile.
PullRequest: graal/22516
2 parents 7fa1a51 + f86b870 commit fa1dfa2

File tree

3 files changed

+285
-0
lines changed

3 files changed

+285
-0
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.truffle.api.test.polyglot;
42+
43+
import org.graalvm.polyglot.Context;
44+
import org.junit.Test;
45+
46+
import com.oracle.truffle.api.Assumption;
47+
import com.oracle.truffle.api.CallTarget;
48+
import com.oracle.truffle.api.CompilerDirectives;
49+
import com.oracle.truffle.api.frame.VirtualFrame;
50+
import com.oracle.truffle.api.nodes.RootNode;
51+
import com.oracle.truffle.tck.tests.TruffleTestAssumptions;
52+
53+
/**
54+
* Test that {@link CompilerDirectives#transferToInterpreter()} does not trigger deopt cycle
55+
* detection. The test first deopts "deopt cycle detection threshold"-times using
56+
* {@link CompilerDirectives#transferToInterpreterAndInvalidate()}. This does not trigger the
57+
* detection, because it is not enabled. Then it uses
58+
* {@link CompilerDirectives#transferToInterpreter()} to deopt, explicitly invalidates the code and
59+
* deopts repeatedly again. This must not trigger the deopt cycle detection.
60+
*/
61+
public class GR68545Test {
62+
private static final int DEOPT_CYCLE_DETECTION_THRESHOLD = 15;
63+
64+
static class AlwaysDeoptNoInvalidate extends RootNode {
65+
66+
@CompilerDirectives.CompilationFinal private Assumption assumption = Assumption.create("Test assumption");
67+
68+
AlwaysDeoptNoInvalidate() {
69+
super(null);
70+
}
71+
72+
@CompilerDirectives.TruffleBoundary
73+
static void boundaryMethod() {
74+
75+
}
76+
77+
@Override
78+
public Object execute(VirtualFrame frame) {
79+
if (assumption.isValid()) {
80+
int arg = (int) frame.getArguments()[0];
81+
int threshold = 15;
82+
if (arg < threshold) {
83+
CompilerDirectives.transferToInterpreterAndInvalidate();
84+
}
85+
// call boundary method to prevent compiler from moving the following deoptimization
86+
// up
87+
boundaryMethod();
88+
CompilerDirectives.transferToInterpreter();
89+
}
90+
return null;
91+
}
92+
93+
@Override
94+
public String getName() {
95+
return "AlwaysDeoptNoInvalidate";
96+
}
97+
98+
@Override
99+
public String toString() {
100+
return getName();
101+
}
102+
}
103+
104+
@Test
105+
public void testAlwaysDeoptNoInvalidate() {
106+
TruffleTestAssumptions.assumeOptimizingRuntime();
107+
TruffleTestAssumptions.assumeDeoptLoopDetectionAvailable();
108+
try (Context context = Context.newBuilder().allowExperimentalOptions(true) //
109+
.option("engine.CompilationFailureAction", "Throw") //
110+
.option("engine.BackgroundCompilation", "false") //
111+
.option("engine.CompileImmediately", "true") //
112+
.option("compiler.DeoptCycleDetectionThreshold", String.valueOf(DEOPT_CYCLE_DETECTION_THRESHOLD)).build()) {
113+
context.enter();
114+
AlwaysDeoptNoInvalidate alwaysDeoptNoInvalidate = new AlwaysDeoptNoInvalidate();
115+
CallTarget callTarget = alwaysDeoptNoInvalidate.getCallTarget();
116+
for (int i = 0; i < DEOPT_CYCLE_DETECTION_THRESHOLD; i++) {
117+
callTarget.call(i);
118+
}
119+
callTarget.call(DEOPT_CYCLE_DETECTION_THRESHOLD);
120+
alwaysDeoptNoInvalidate.assumption.invalidate("Test reason");
121+
alwaysDeoptNoInvalidate.assumption = Assumption.create();
122+
callTarget.call(DEOPT_CYCLE_DETECTION_THRESHOLD + 1);
123+
for (int i = 0; i < 100; i++) {
124+
callTarget.call(DEOPT_CYCLE_DETECTION_THRESHOLD + 2 + i);
125+
}
126+
}
127+
}
128+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.truffle.api.test.polyglot;
42+
43+
import org.graalvm.polyglot.Context;
44+
import org.junit.Assert;
45+
import org.junit.Test;
46+
47+
import com.oracle.truffle.api.CallTarget;
48+
import com.oracle.truffle.api.CompilerDirectives;
49+
import com.oracle.truffle.api.Truffle;
50+
import com.oracle.truffle.api.frame.VirtualFrame;
51+
import com.oracle.truffle.api.nodes.DirectCallNode;
52+
import com.oracle.truffle.api.nodes.RootNode;
53+
import com.oracle.truffle.tck.tests.TruffleTestAssumptions;
54+
55+
/**
56+
* Test that direct calls to a target that always throws an exception does not cause a
57+
* deoptimization cycle.
58+
*/
59+
public class GR71109Test {
60+
private static final int DEOPT_CYCLE_DETECTION_THRESHOLD = 15;
61+
62+
static class Callee extends RootNode {
63+
64+
Callee() {
65+
super(null);
66+
}
67+
68+
@Override
69+
public Object execute(VirtualFrame frame) {
70+
throw getTestException();
71+
}
72+
73+
@CompilerDirectives.TruffleBoundary
74+
private static RuntimeException getTestException() {
75+
return new RuntimeException("Test exception");
76+
}
77+
78+
@Override
79+
public String getName() {
80+
return "callee";
81+
}
82+
83+
@Override
84+
public String toString() {
85+
return getName();
86+
}
87+
}
88+
89+
static class Caller extends RootNode {
90+
91+
private final DirectCallNode callNode;
92+
93+
Caller(CallTarget callee) {
94+
super(null);
95+
callNode = Truffle.getRuntime().createDirectCallNode(callee);
96+
}
97+
98+
@Override
99+
public Object execute(VirtualFrame frame) {
100+
return callNode.call(frame.getArguments());
101+
}
102+
103+
@Override
104+
public String getName() {
105+
return "caller";
106+
}
107+
108+
@Override
109+
public String toString() {
110+
return getName();
111+
}
112+
}
113+
114+
@Test
115+
public void testDirectCallToAlwaysThrowException() {
116+
TruffleTestAssumptions.assumeOptimizingRuntime();
117+
TruffleTestAssumptions.assumeDeoptLoopDetectionAvailable();
118+
try (Context context = Context.newBuilder().allowExperimentalOptions(true) //
119+
.option("engine.CompilationFailureAction", "Throw") //
120+
.option("engine.BackgroundCompilation", "false") //
121+
.option("engine.CompileImmediately", "true") //
122+
.option("compiler.Inlining", "false") //
123+
.option("engine.CompileOnly", "caller") //
124+
.option("compiler.DeoptCycleDetectionThreshold", String.valueOf(DEOPT_CYCLE_DETECTION_THRESHOLD)) //
125+
.build()) {
126+
context.enter();
127+
/*
128+
* Compilation is limited to the caller, because immediate compilation of the callee
129+
* would initialize its return profile. We want to test that the return profile of the
130+
* callee is also initialized (to invalid return profile) as the result of throwing the
131+
* exception. Initialized return profile of the callee is needed for direct C2C and C2I
132+
* calls on SVM, otherwise the call causes a deopt.
133+
*/
134+
Caller caller = new Caller(new Callee().getCallTarget());
135+
CallTarget callTarget = caller.getCallTarget();
136+
for (int i = 0; i < DEOPT_CYCLE_DETECTION_THRESHOLD + 100; i++) {
137+
try {
138+
callTarget.call(i);
139+
Assert.fail();
140+
} catch (RuntimeException e) {
141+
if (!"Test exception".equals(e.getMessage())) {
142+
throw e;
143+
}
144+
}
145+
}
146+
}
147+
}
148+
149+
}

truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedCallTarget.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,6 +1728,14 @@ private Throwable profileExceptionType(Throwable value) {
17281728
}
17291729
CompilerDirectives.transferToInterpreterAndInvalidate();
17301730
specializeException(value);
1731+
/*
1732+
* If the target throws an exception, we can't leave the return profile uninitialized,
1733+
* because it is needed for direct calls on SVM. If the return profile stays null, it causes
1734+
* a deopt of the caller for direct C2C and C2I calls to the target with the uninitialized
1735+
* return profile. If the callee keeps throwing an exception, the deopt can be repeated
1736+
* unlimited number of times thus causing a deopt cycle for the caller.
1737+
*/
1738+
getInitializedReturnProfile();
17311739
return value;
17321740
}
17331741

0 commit comments

Comments
 (0)