Skip to content

Commit 462a5b3

Browse files
committed
[GR-66112] [GR-66823] Improve documentation concerning optimizing of Truffle interpreters
PullRequest: graal/21301
2 parents 3fa330a + 1c32b0d commit 462a5b3

File tree

4 files changed

+575
-38
lines changed

4 files changed

+575
-38
lines changed

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

Lines changed: 180 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,15 @@
2828
import static org.junit.Assert.assertNotNull;
2929
import static org.junit.Assert.assertTrue;
3030

31+
import java.io.IOException;
32+
import java.util.Map;
3133
import java.util.concurrent.atomic.AtomicReference;
3234
import java.util.function.Consumer;
3335
import java.util.function.Supplier;
3436

3537
import org.graalvm.polyglot.Context;
38+
import org.graalvm.polyglot.Engine;
39+
import org.graalvm.polyglot.Source;
3640
import org.junit.After;
3741
import org.junit.Assert;
3842
import org.junit.Assume;
@@ -44,18 +48,26 @@
4448
import com.oracle.truffle.api.CompilerDirectives;
4549
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
4650
import com.oracle.truffle.api.Truffle;
51+
import com.oracle.truffle.api.TruffleLanguage;
4752
import com.oracle.truffle.api.frame.VirtualFrame;
53+
import com.oracle.truffle.api.nodes.IndirectCallNode;
4854
import com.oracle.truffle.api.nodes.RootNode;
55+
import com.oracle.truffle.api.test.common.NullObject;
4956
import com.oracle.truffle.compiler.TruffleCompilerListener;
5057
import com.oracle.truffle.runtime.AbstractCompilationTask;
5158
import com.oracle.truffle.runtime.OptimizedCallTarget;
5259
import com.oracle.truffle.runtime.OptimizedTruffleRuntime;
5360
import com.oracle.truffle.runtime.OptimizedTruffleRuntimeListener;
5461

5562
import jdk.graal.compiler.truffle.TruffleCompilerOptions;
63+
import jdk.graal.compiler.util.CollectionsUtil;
5664

5765
public class DeoptLoopDetectionTest {
5866

67+
private static final Map<String, String> ENGINE_OPTIONS = CollectionsUtil.mapOf(
68+
"engine.CompilationFailureAction", "Silent", //
69+
"engine.BackgroundCompilation", "false", //
70+
"engine.CompileImmediately", "true");
5971
private final AtomicReference<CallTarget> callTargetFilter = new AtomicReference<>();
6072
private final AtomicReference<Boolean> compilationResult = new AtomicReference<>();
6173
private final AtomicReference<String> compilationFailedReason = new AtomicReference<>();
@@ -80,10 +92,7 @@ public void onCompilationFailed(OptimizedCallTarget target, String reason, boole
8092

8193
@Before
8294
public void setup() {
83-
context = Context.newBuilder().//
84-
option("engine.CompilationFailureAction", "Silent").//
85-
option("engine.BackgroundCompilation", "false").//
86-
option("engine.CompileImmediately", "true").build();
95+
context = Context.newBuilder().options(ENGINE_OPTIONS).build();
8796
context.enter();
8897
Assume.assumeTrue(Truffle.getRuntime() instanceof OptimizedTruffleRuntime);
8998
((OptimizedTruffleRuntime) Truffle.getRuntime()).addListener(listener);
@@ -104,7 +113,7 @@ public Object execute(VirtualFrame frame) {
104113
CompilerDirectives.transferToInterpreterAndInvalidate();
105114
return null;
106115
}
107-
}, "alwaysDeopt", CallTarget::call, 1);
116+
}, "alwaysDeopt", CallTarget::call, 0, 1);
108117
}
109118

110119
@Test
@@ -145,19 +154,19 @@ public void accept(CallTarget callTarget) {
145154
((OptimizedCallTarget) callTarget).invalidate("Force one more recompile");
146155
}
147156
}
148-
}, 1));
157+
}, 0, 1));
149158
Assert.assertEquals("No deopt loop detected after " + MAX_EXECUTIONS + " executions", expectedError.getMessage());
150159
}
151160

152161
@Test
153162
public void testLocalDeopt() {
154163
assertDeoptLoop(new BaseRootNode() {
155164

156-
@CompilationFinal boolean cachedValue;
165+
@CompilationFinal int cachedValue;
157166

158167
@Override
159168
public Object execute(VirtualFrame frame) {
160-
boolean arg = (boolean) frame.getArguments()[0];
169+
int arg = (int) frame.getArguments()[0];
161170
if (this.cachedValue != arg) {
162171
CompilerDirectives.transferToInterpreterAndInvalidate();
163172
this.cachedValue = arg;
@@ -166,9 +175,9 @@ public Object execute(VirtualFrame frame) {
166175
}
167176

168177
}, "localDeopt", (target) -> {
169-
target.call(true);
170-
target.call(false);
171-
}, 2);
178+
target.call(0);
179+
target.call(1);
180+
}, 0, 2);
172181
}
173182

174183
@Test
@@ -190,7 +199,7 @@ public Object execute(VirtualFrame frame) {
190199

191200
}, "globalDeopt", (target) -> {
192201
target.call(true);
193-
}, 1);
202+
}, 0, 1);
194203
}
195204

196205
static class StaticAssumptionRootNode extends BaseRootNode {
@@ -215,7 +224,7 @@ public void testStaticAssumptionNoDeopt() {
215224
StaticAssumptionRootNode.assumption = Assumption.create();
216225
firstExecution[0] = false;
217226
}
218-
}, 1));
227+
}, 0, 1));
219228
Assert.assertEquals("No deopt loop detected after " + MAX_EXECUTIONS + " executions", expectedError.getMessage());
220229
}
221230

@@ -243,7 +252,7 @@ public Object execute(VirtualFrame frame) {
243252

244253
}, "localLoopDeoptwithChangedCode", (target) -> {
245254
target.call(1);
246-
}, 1);
255+
}, 0, 1);
247256
}
248257

249258
@Test
@@ -289,12 +298,166 @@ public Object execute(VirtualFrame frame) {
289298
public void accept(CallTarget target) {
290299
target.call(input++);
291300
}
292-
}, 1);
301+
}, 0, 1);
302+
}
303+
304+
@TruffleLanguage.Registration(id = NonConstantLanguageContextTestLanguage.ID, contextPolicy = TruffleLanguage.ContextPolicy.SHARED)
305+
static class NonConstantLanguageContextTestLanguage extends TruffleLanguage<NonConstantLanguageContextTestLanguage.LanguageContext> {
306+
static final String ID = "NonConstantLanguageContextTestLanguage";
307+
static final String ROOT_NODE_NAME = "languageContextFallacy";
308+
309+
static class LanguageContext {
310+
private final Env env;
311+
312+
@CompilationFinal boolean initialized;
313+
314+
LanguageContext(Env env) {
315+
this.env = env;
316+
}
317+
318+
void ensureInitialized() {
319+
if (!initialized) {
320+
CompilerDirectives.transferToInterpreterAndInvalidate();
321+
initialize();
322+
}
323+
}
324+
325+
private void initialize() {
326+
// perform initialization
327+
initialized = true;
328+
}
329+
330+
static final ContextReference<LanguageContext> REF = ContextReference.create(NonConstantLanguageContextTestLanguage.class);
331+
}
332+
333+
@Override
334+
protected LanguageContext createContext(Env env) {
335+
return new LanguageContext(env);
336+
}
337+
338+
@Override
339+
protected CallTarget parse(ParsingRequest request) throws Exception {
340+
BaseRootNode rootNode = new BaseRootNode() {
341+
@Override
342+
public Object execute(VirtualFrame frame) {
343+
LanguageContext languageContext = LanguageContext.REF.get(this);
344+
languageContext.ensureInitialized();
345+
// ...
346+
return NullObject.SINGLETON;
347+
}
348+
};
349+
rootNode.name = ROOT_NODE_NAME;
350+
return rootNode.getCallTarget();
351+
}
352+
}
353+
354+
@Test
355+
public void testNonConstantLanguageContext() throws IOException {
356+
try (Engine engine = Engine.newBuilder().options(ENGINE_OPTIONS).build()) {
357+
Source source = Source.newBuilder(NonConstantLanguageContextTestLanguage.ID, "", "TestSource").build();
358+
AtomicReference<OptimizedCallTarget> captureTargetReference = new AtomicReference<>();
359+
final OptimizedTruffleRuntimeListener listener2 = new OptimizedTruffleRuntimeListener() {
360+
@Override
361+
public void onCompilationSuccess(OptimizedCallTarget target, AbstractCompilationTask task, TruffleCompilerListener.GraphInfo graph,
362+
TruffleCompilerListener.CompilationResultInfo result) {
363+
OptimizedTruffleRuntimeListener.super.onCompilationSuccess(target, task, graph, result);
364+
captureTargetReference.set(target);
365+
}
366+
};
367+
((OptimizedTruffleRuntime) Truffle.getRuntime()).addListener(listener2);
368+
try {
369+
try (Context context1 = Context.newBuilder().engine(engine).build()) {
370+
context1.eval(source);
371+
}
372+
} finally {
373+
((OptimizedTruffleRuntime) Truffle.getRuntime()).removeListener(listener2);
374+
}
375+
assertDeoptLoop((BaseRootNode) captureTargetReference.get().getRootNode(), NonConstantLanguageContextTestLanguage.ROOT_NODE_NAME, new Consumer<>() {
376+
377+
int calleeIndex = 0;
378+
379+
@Override
380+
public void accept(CallTarget target) {
381+
try (Context context2 = Context.newBuilder().engine(engine).build()) {
382+
context2.eval(source);
383+
}
384+
}
385+
}, 1, 1);
386+
}
387+
}
388+
389+
static class MyFunction {
390+
private final RootNode root;
391+
392+
MyFunction(int id) {
393+
this.root = new RootNode(null) {
394+
@Override
395+
public Object execute(VirtualFrame frame) {
396+
return NullObject.SINGLETON;
397+
}
398+
399+
@Override
400+
public String getName() {
401+
return "callee" + id;
402+
}
403+
404+
@Override
405+
public String toString() {
406+
return getName();
407+
}
408+
};
409+
}
410+
}
411+
412+
@Test
413+
public void testLookupAndDispatch() {
414+
assertDeoptLoop(new BaseRootNode() {
415+
416+
@Child IndirectCallNode callNode = IndirectCallNode.create();
417+
418+
@Override
419+
public Object execute(VirtualFrame frame) {
420+
MyFunction function = (MyFunction) frame.getArguments()[0];
421+
CallTarget target = function.root.getCallTarget();
422+
return callNode.call(target);
423+
}
424+
425+
}, "tooBroadDispatch", new Consumer<>() {
426+
427+
int calleeIndex = 0;
428+
429+
@Override
430+
public void accept(CallTarget target) {
431+
target.call(new MyFunction(calleeIndex++));
432+
}
433+
}, 0, 1);
434+
}
435+
436+
@Test
437+
public void testSkippedException() {
438+
assertDeoptLoop(new BaseRootNode() {
439+
440+
@Override
441+
public Object execute(VirtualFrame frame) {
442+
try {
443+
throw new IndexOutOfBoundsException();
444+
} catch (RuntimeException e) {
445+
//
446+
}
447+
return null;
448+
}
449+
450+
}, "skippedException", new Consumer<>() {
451+
@Override
452+
public void accept(CallTarget target) {
453+
target.call();
454+
}
455+
}, 0, 1);
293456
}
294457

295458
private static final int MAX_EXECUTIONS = 1024;
296459

297-
private void assertDeoptLoop(BaseRootNode root, String name, Consumer<CallTarget> callStrategy, int compilationsPerIteration) {
460+
private void assertDeoptLoop(BaseRootNode root, String name, Consumer<CallTarget> callStrategy, int previousCompilations, int compilationsPerIteration) {
298461
root.name = name;
299462
CallTarget callTarget = root.getCallTarget();
300463
callTargetFilter.set(callTarget);
@@ -313,7 +476,7 @@ private void assertDeoptLoop(BaseRootNode root, String name, Consumer<CallTarget
313476
iterationCounter++;
314477
}
315478

316-
assertTrue(iterationCounter * compilationsPerIteration > TruffleCompilerOptions.DeoptCycleDetectionThreshold.getDefaultValue());
479+
assertTrue(previousCompilations + iterationCounter * compilationsPerIteration > TruffleCompilerOptions.DeoptCycleDetectionThreshold.getDefaultValue());
317480
assertEquals(Boolean.FALSE, compilationResult.get());
318481
String failedReason = compilationFailedReason.get();
319482
assertNotNull(failedReason);

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/KnownTruffleTypes.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ private static ResolvedJavaField getThrowableJFRTracingField(MetaAccessProvider
231231
}
232232

233233
private ResolvedJavaType[] createSkippedExceptionTypes() {
234+
// keep in sync with truffle/docs/DeoptCyclePatterns.md
234235
List<ResolvedJavaType> types = new ArrayList<>(16);
235236
types.add(UnexpectedResultException);
236237
types.add(SlowPathException);

0 commit comments

Comments
 (0)