Skip to content

Commit 2d63370

Browse files
Automatic merge of master into galahad
2 parents 2ace28a + 205b4d0 commit 2d63370

File tree

10 files changed

+314
-4
lines changed

10 files changed

+314
-4
lines changed

compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/truffle/HSTruffleCompilable.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ final class HSTruffleCompilable extends HSObject implements TruffleCompilable {
7373
private static volatile JNIMethod prepareForCompilationNewMethod;
7474
private static volatile JNIMethod getSuccessfulCompilationCountMethod;
7575
private static volatile JNIMethod onCompilationSuccessMethod;
76+
private static volatile JNIMethod canBeInlinedMethod;
7677

7778
private final TruffleFromLibGraalCalls calls;
7879
/**
@@ -175,6 +176,31 @@ private int callGetSuccessfulCompilationCountMethod(JNIMethod method, JNIEnv env
175176
return calls.getJNICalls().callStaticInt(env, calls.getPeer(), method, args);
176177
}
177178

179+
@Override
180+
public boolean canBeInlined() {
181+
JNIEnv env = JNIMethodScope.env();
182+
JNIMethod method = findCanBeInlinedMethod(env);
183+
if (method != null) {
184+
return callCanBeInlinedMethod(method, env, getHandle());
185+
}
186+
return true;
187+
}
188+
189+
private JNIMethod findCanBeInlinedMethod(JNIEnv env) {
190+
JNIMethod res = canBeInlinedMethod;
191+
if (res == null) {
192+
res = calls.findJNIMethod(env, "canBeInlined", boolean.class, Object.class);
193+
canBeInlinedMethod = res;
194+
}
195+
return res.getJMethodID().isNonNull() ? res : null;
196+
}
197+
198+
private boolean callCanBeInlinedMethod(JNIMethod method, JNIEnv env, JObject p0) {
199+
JNI.JValue args = StackValue.get(1, JNI.JValue.class);
200+
args.addressOf(0).setJObject(p0);
201+
return calls.getJNICalls().callStaticBoolean(env, calls.getPeer(), method, args);
202+
}
203+
178204
@TruffleFromLibGraal(IsTrivial)
179205
@Override
180206
public boolean isTrivial() {

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ public void testDeoptLoopDetected() {
173173
CountDownLatch calleeCompilationStartLatch = new CountDownLatch(1);
174174
CountDownLatch calleeCompilationFinishedLatch = new CountDownLatch(1);
175175
AtomicBoolean intCallerCompilationFailed = new AtomicBoolean();
176-
optimizedTruffleRuntime.addListener(new OptimizedTruffleRuntimeListener() {
176+
OptimizedTruffleRuntimeListener listener = new OptimizedTruffleRuntimeListener() {
177177
@Override
178178
public void onCompilationSuccess(OptimizedCallTarget target, AbstractCompilationTask task, TruffleCompilerListener.GraphInfo graph, TruffleCompilerListener.CompilationResultInfo result) {
179179
if (target == calleeRef.get()) {
@@ -198,7 +198,8 @@ public void onCompilationFailed(OptimizedCallTarget target, String reason, boole
198198
intCallerCompilationFailed.set(true);
199199
}
200200
}
201-
});
201+
};
202+
optimizedTruffleRuntime.addListener(listener);
202203
try (Context context = Context.newBuilder().allowExperimentalOptions(true).option("engine.CompileImmediately", "false").option("engine.BackgroundCompilation", "true").option(
203204
"engine.DynamicCompilationThresholds", "false").option("engine.MultiTier", "false").option("engine.Splitting", "false").option("engine.SingleTierCompilationThreshold",
204205
"10").option("engine.CompilationFailureAction", "Silent").build()) {
@@ -242,6 +243,8 @@ public void onCompilationFailed(OptimizedCallTarget target, String reason, boole
242243
intCaller.call(42, Integer.class);
243244
}
244245
Assert.assertTrue(intCallerCompilationFailed.get());
246+
} finally {
247+
optimizedTruffleRuntime.removeListener(listener);
245248
}
246249
}
247250
}
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
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+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package jdk.graal.compiler.truffle.test;
26+
27+
import java.io.IOException;
28+
import java.util.Map;
29+
import java.util.concurrent.atomic.AtomicInteger;
30+
import java.util.concurrent.atomic.AtomicReference;
31+
import java.util.function.Function;
32+
import java.util.function.Supplier;
33+
34+
import org.graalvm.polyglot.Context;
35+
import org.junit.Assert;
36+
import org.junit.Assume;
37+
import org.junit.Test;
38+
39+
import com.oracle.truffle.api.CallTarget;
40+
import com.oracle.truffle.api.CompilerDirectives;
41+
import com.oracle.truffle.api.Truffle;
42+
import com.oracle.truffle.api.frame.VirtualFrame;
43+
import com.oracle.truffle.api.nodes.DirectCallNode;
44+
import com.oracle.truffle.api.nodes.ExplodeLoop;
45+
import com.oracle.truffle.api.nodes.RootNode;
46+
import com.oracle.truffle.api.test.SubprocessTestUtils;
47+
import com.oracle.truffle.api.test.common.NullObject;
48+
import com.oracle.truffle.compiler.TruffleCompilerListener;
49+
import com.oracle.truffle.runtime.AbstractCompilationTask;
50+
import com.oracle.truffle.runtime.OptimizedCallTarget;
51+
import com.oracle.truffle.runtime.OptimizedTruffleRuntime;
52+
import com.oracle.truffle.runtime.OptimizedTruffleRuntimeListener;
53+
54+
import jdk.graal.compiler.util.CollectionsUtil;
55+
56+
public class NeverInlineFailedTest {
57+
58+
static class Caller extends RootNode {
59+
@Child private DirectCallNode callNode;
60+
61+
Caller(CallTarget target) {
62+
super(null);
63+
this.callNode = Truffle.getRuntime().createDirectCallNode(target);
64+
}
65+
66+
@Override
67+
public Object execute(VirtualFrame frame) {
68+
return callNode.call(frame.getArguments());
69+
}
70+
71+
@Override
72+
public String getName() {
73+
return "caller";
74+
}
75+
76+
@Override
77+
public String toString() {
78+
return getName();
79+
}
80+
}
81+
82+
static class Callee extends RootNode {
83+
84+
Callee() {
85+
super(null);
86+
}
87+
88+
@Override
89+
public Object execute(VirtualFrame frame) {
90+
return NullObject.SINGLETON;
91+
}
92+
93+
@Override
94+
protected boolean prepareForCompilation(boolean rootCompilation, int compilationTier, boolean lastTier) {
95+
throw new RuntimeException("Intentionally fail compilation");
96+
}
97+
98+
@Override
99+
public String getName() {
100+
return "callee";
101+
}
102+
103+
@Override
104+
public String toString() {
105+
return getName();
106+
}
107+
}
108+
109+
static class Callee2 extends RootNode {
110+
111+
private static class MyClass {
112+
int[] array = new int[100];
113+
114+
MyClass() {
115+
for (int i = 0; i < array.length; i++) {
116+
array[i] = value();
117+
}
118+
}
119+
120+
@CompilerDirectives.TruffleBoundary
121+
private static int value() {
122+
return 1;
123+
}
124+
125+
public int sum() {
126+
int sum = 0;
127+
for (int i : array) {
128+
sum += i;
129+
}
130+
return sum;
131+
}
132+
}
133+
134+
@SuppressWarnings("unused") private int global;
135+
@SuppressWarnings("unused") private int globalI;
136+
137+
Callee2() {
138+
super(null);
139+
}
140+
141+
@Override
142+
public Object execute(VirtualFrame frame) {
143+
recurse();
144+
foo();
145+
return null;
146+
}
147+
148+
@ExplodeLoop
149+
private void recurse() {
150+
for (int i = 0; i < 1000; i++) {
151+
getF().apply(0);
152+
}
153+
}
154+
155+
private Function<Integer, Integer> getF() {
156+
return new Function<>() {
157+
@Override
158+
public Integer apply(Integer integer) {
159+
return integer < 500 ? getF().apply(integer + 1) : 0;
160+
}
161+
};
162+
}
163+
164+
protected void foo() {
165+
for (; globalI < 1000; globalI++) {
166+
global += new MyClass().sum();
167+
}
168+
}
169+
};
170+
171+
@Test
172+
public void testNeverInlineFailed() throws IOException, InterruptedException {
173+
testNeverInlineFailedImpl(Callee::new, CollectionsUtil.mapOfEntries(), "java.lang.RuntimeException: Intentionally fail compilation");
174+
}
175+
176+
@Test
177+
public void testNeverInlineFailed2() throws IOException, InterruptedException {
178+
testNeverInlineFailedImpl(Callee2::new, CollectionsUtil.mapOf("compiler.CompilationTimeout", "1"), "jdk.graal.compiler.core.common.PermanentBailoutException: Compilation exceeded");
179+
}
180+
181+
private static void testNeverInlineFailedImpl(Supplier<RootNode> calleeSupplier, Map<String, String> extraOptions, String reasonContains) throws IOException, InterruptedException {
182+
Assume.assumeTrue(Truffle.getRuntime() instanceof OptimizedTruffleRuntime);
183+
Runnable test = () -> {
184+
OptimizedTruffleRuntime optimizedTruffleRuntime = (OptimizedTruffleRuntime) Truffle.getRuntime();
185+
AtomicReference<CallTarget> calleeRef = new AtomicReference<>();
186+
AtomicReference<CallTarget> callerRef = new AtomicReference<>();
187+
AtomicReference<String> calleeCompilationFailure = new AtomicReference<>();
188+
AtomicInteger callerCompilationInlinedCalls = new AtomicInteger(-1);
189+
OptimizedTruffleRuntimeListener listener = new OptimizedTruffleRuntimeListener() {
190+
@Override
191+
public void onCompilationFailed(OptimizedCallTarget target, String reason, boolean bailout, boolean permanentBailout, int tier, Supplier<String> lazyStackTrace) {
192+
if (target == calleeRef.get()) {
193+
calleeCompilationFailure.set(reason);
194+
}
195+
}
196+
197+
@Override
198+
public void onCompilationSuccess(OptimizedCallTarget target, AbstractCompilationTask task, TruffleCompilerListener.GraphInfo graph,
199+
TruffleCompilerListener.CompilationResultInfo result) {
200+
if (target == callerRef.get()) {
201+
callerCompilationInlinedCalls.set(task.countInlinedCalls());
202+
}
203+
}
204+
};
205+
optimizedTruffleRuntime.addListener(listener);
206+
try (Context context = Context.newBuilder().allowExperimentalOptions(true).option("engine.CompileImmediately", "true").option("engine.BackgroundCompilation", "false").option(
207+
"engine.CompilationFailureAction", "Silent").options(extraOptions).build()) {
208+
context.enter();
209+
CallTarget calleeTarget = calleeSupplier.get().getCallTarget();
210+
calleeRef.set(calleeTarget);
211+
calleeTarget.call();
212+
Assert.assertNotNull(calleeCompilationFailure.get());
213+
Assert.assertTrue("Unexpected compilation failure reason: " + calleeCompilationFailure.get(),
214+
calleeCompilationFailure.get().contains(reasonContains));
215+
CallTarget callerTarget = new Caller(calleeTarget).getCallTarget();
216+
callerRef.set(callerTarget);
217+
callerTarget.call();
218+
Assert.assertEquals(0, callerCompilationInlinedCalls.get());
219+
Assert.assertTrue(((OptimizedCallTarget) callerTarget).isValid());
220+
} finally {
221+
optimizedTruffleRuntime.removeListener(listener);
222+
}
223+
};
224+
SubprocessTestUtils.newBuilder(NeverInlineFailedTest.class, test).postfixVmOption("-Djdk.graal.CompilationFailureAction=Silent").run();
225+
}
226+
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/phases/inlining/CallNode.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,10 @@ public void expand() {
285285
*/
286286
return;
287287
}
288+
if (getDirectCallTarget() != null && !getDirectCallTarget().canBeInlined()) {
289+
state = State.BailedOut;
290+
return;
291+
}
288292
assert state == State.Cutoff : "Cannot expand a non-cutoff node. Node is " + state;
289293
assert getParent() != null;
290294
state = State.Expanded;

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/phases/inlining/InliningPolicy.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Map;
3030

3131
import org.graalvm.collections.Pair;
32+
3233
import jdk.graal.compiler.truffle.TruffleCompilerOptions;
3334

3435
@SuppressWarnings("unused")

substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
import org.graalvm.nativeimage.AnnotationAccess;
105105
import org.graalvm.nativeimage.ImageSingletons;
106106
import org.graalvm.nativeimage.hosted.Feature;
107+
import org.graalvm.nativeimage.hosted.FieldValueTransformer;
107108

108109
import com.oracle.graal.pointsto.meta.AnalysisField;
109110
import com.oracle.graal.pointsto.meta.AnalysisMethod;
@@ -1076,9 +1077,21 @@ final class Target_com_oracle_truffle_runtime_OptimizedCallTarget {
10761077
*/
10771078
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) //
10781079
boolean compilationFailed;
1080+
/*
1081+
* Re-enable the target for inlining when it was disabled during image generation.
1082+
*/
1083+
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = TransformToTrue.class) //
1084+
boolean canBeInlined = true;
10791085
/*
10801086
* The initialized time stamp is not useful when collected during image generation.
10811087
*/
10821088
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) //
10831089
long initializedTimestamp;
1090+
1091+
private static final class TransformToTrue implements FieldValueTransformer {
1092+
@Override
1093+
public Object transform(Object receiver, Object originalValue) {
1094+
return true;
1095+
}
1096+
}
10841097
}

substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/isolated/IsolatedCompilableTruffleAST.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,11 @@ public int getSuccessfulCompilationCount() {
171171
return getSuccessfulCompilationCount0(IsolatedCompileContext.get().getClient(), handle);
172172
}
173173

174+
@Override
175+
public boolean canBeInlined() {
176+
return canBeInlined0(IsolatedCompileContext.get().getClient(), handle);
177+
}
178+
174179
@CEntryPoint(exceptionHandler = IsolatedCompileClient.WordExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = CEntryPoint.Publish.NotPublished)
175180
@CEntryPointOptions(callerEpilogue = IsolatedCompileClient.ExceptionRethrowCallerEpilogue.class)
176181
private static ClientHandle<SpeculationLog> getCompilationSpeculationLog0(@SuppressWarnings("unused") ClientIsolateThread client, ClientHandle<SubstrateCompilableTruffleAST> compilableHandle) {
@@ -306,6 +311,13 @@ private static int getSuccessfulCompilationCount0(@SuppressWarnings("unused") Cl
306311
return compilable.getSuccessfulCompilationCount();
307312
}
308313

314+
@CEntryPoint(exceptionHandler = IsolatedCompileClient.BooleanExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = CEntryPoint.Publish.NotPublished)
315+
@CEntryPointOptions(callerEpilogue = IsolatedCompileClient.ExceptionRethrowCallerEpilogue.class)
316+
private static boolean canBeInlined0(@SuppressWarnings("unused") ClientIsolateThread client, ClientHandle<SubstrateCompilableTruffleAST> handle) {
317+
SubstrateCompilableTruffleAST compilable = IsolatedCompileClient.get().unhand(handle);
318+
return compilable.canBeInlined();
319+
}
320+
309321
private static Map<String, String> readCompilerOptions(Map<String, String> map, BinaryInput in) {
310322
int size = in.readInt();
311323
for (int i = 0; i < size; i++) {

truffle/src/com.oracle.truffle.compiler/src/com/oracle/truffle/compiler/TruffleCompilable.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,4 +221,16 @@ static String serializeException(Throwable e) {
221221
default int getSuccessfulCompilationCount() {
222222
return 0;
223223
}
224+
225+
/**
226+
* Returns <code>true</code> if this compilable can be inlined. Please note that this does not
227+
* mean it will always be inlined, as inlining is a complex process that takes many factors into
228+
* account. If this method returns <code>false</code>, it will never be inlined. This typically
229+
* means that compilation with this compilable as the root has failed.
230+
*
231+
*
232+
*/
233+
default boolean canBeInlined() {
234+
return true;
235+
}
224236
}

0 commit comments

Comments
 (0)