28
28
import static org .junit .Assert .assertNotNull ;
29
29
import static org .junit .Assert .assertTrue ;
30
30
31
+ import java .io .IOException ;
32
+ import java .util .Map ;
31
33
import java .util .concurrent .atomic .AtomicReference ;
32
34
import java .util .function .Consumer ;
33
35
import java .util .function .Supplier ;
34
36
35
37
import org .graalvm .polyglot .Context ;
38
+ import org .graalvm .polyglot .Engine ;
39
+ import org .graalvm .polyglot .Source ;
36
40
import org .junit .After ;
37
41
import org .junit .Assert ;
38
42
import org .junit .Assume ;
44
48
import com .oracle .truffle .api .CompilerDirectives ;
45
49
import com .oracle .truffle .api .CompilerDirectives .CompilationFinal ;
46
50
import com .oracle .truffle .api .Truffle ;
51
+ import com .oracle .truffle .api .TruffleLanguage ;
47
52
import com .oracle .truffle .api .frame .VirtualFrame ;
53
+ import com .oracle .truffle .api .nodes .IndirectCallNode ;
48
54
import com .oracle .truffle .api .nodes .RootNode ;
55
+ import com .oracle .truffle .api .test .common .NullObject ;
49
56
import com .oracle .truffle .compiler .TruffleCompilerListener ;
50
57
import com .oracle .truffle .runtime .AbstractCompilationTask ;
51
58
import com .oracle .truffle .runtime .OptimizedCallTarget ;
52
59
import com .oracle .truffle .runtime .OptimizedTruffleRuntime ;
53
60
import com .oracle .truffle .runtime .OptimizedTruffleRuntimeListener ;
54
61
55
62
import jdk .graal .compiler .truffle .TruffleCompilerOptions ;
63
+ import jdk .graal .compiler .util .CollectionsUtil ;
56
64
57
65
public class DeoptLoopDetectionTest {
58
66
67
+ private static final Map <String , String > ENGINE_OPTIONS = CollectionsUtil .mapOf (
68
+ "engine.CompilationFailureAction" , "Silent" , //
69
+ "engine.BackgroundCompilation" , "false" , //
70
+ "engine.CompileImmediately" , "true" );
59
71
private final AtomicReference <CallTarget > callTargetFilter = new AtomicReference <>();
60
72
private final AtomicReference <Boolean > compilationResult = new AtomicReference <>();
61
73
private final AtomicReference <String > compilationFailedReason = new AtomicReference <>();
@@ -80,10 +92,7 @@ public void onCompilationFailed(OptimizedCallTarget target, String reason, boole
80
92
81
93
@ Before
82
94
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 ();
87
96
context .enter ();
88
97
Assume .assumeTrue (Truffle .getRuntime () instanceof OptimizedTruffleRuntime );
89
98
((OptimizedTruffleRuntime ) Truffle .getRuntime ()).addListener (listener );
@@ -104,7 +113,7 @@ public Object execute(VirtualFrame frame) {
104
113
CompilerDirectives .transferToInterpreterAndInvalidate ();
105
114
return null ;
106
115
}
107
- }, "alwaysDeopt" , CallTarget ::call , 1 );
116
+ }, "alwaysDeopt" , CallTarget ::call , 0 , 1 );
108
117
}
109
118
110
119
@ Test
@@ -145,19 +154,19 @@ public void accept(CallTarget callTarget) {
145
154
((OptimizedCallTarget ) callTarget ).invalidate ("Force one more recompile" );
146
155
}
147
156
}
148
- }, 1 ));
157
+ }, 0 , 1 ));
149
158
Assert .assertEquals ("No deopt loop detected after " + MAX_EXECUTIONS + " executions" , expectedError .getMessage ());
150
159
}
151
160
152
161
@ Test
153
162
public void testLocalDeopt () {
154
163
assertDeoptLoop (new BaseRootNode () {
155
164
156
- @ CompilationFinal boolean cachedValue ;
165
+ @ CompilationFinal int cachedValue ;
157
166
158
167
@ Override
159
168
public Object execute (VirtualFrame frame ) {
160
- boolean arg = (boolean ) frame .getArguments ()[0 ];
169
+ int arg = (int ) frame .getArguments ()[0 ];
161
170
if (this .cachedValue != arg ) {
162
171
CompilerDirectives .transferToInterpreterAndInvalidate ();
163
172
this .cachedValue = arg ;
@@ -166,9 +175,9 @@ public Object execute(VirtualFrame frame) {
166
175
}
167
176
168
177
}, "localDeopt" , (target ) -> {
169
- target .call (true );
170
- target .call (false );
171
- }, 2 );
178
+ target .call (0 );
179
+ target .call (1 );
180
+ }, 0 , 2 );
172
181
}
173
182
174
183
@ Test
@@ -190,7 +199,7 @@ public Object execute(VirtualFrame frame) {
190
199
191
200
}, "globalDeopt" , (target ) -> {
192
201
target .call (true );
193
- }, 1 );
202
+ }, 0 , 1 );
194
203
}
195
204
196
205
static class StaticAssumptionRootNode extends BaseRootNode {
@@ -215,7 +224,7 @@ public void testStaticAssumptionNoDeopt() {
215
224
StaticAssumptionRootNode .assumption = Assumption .create ();
216
225
firstExecution [0 ] = false ;
217
226
}
218
- }, 1 ));
227
+ }, 0 , 1 ));
219
228
Assert .assertEquals ("No deopt loop detected after " + MAX_EXECUTIONS + " executions" , expectedError .getMessage ());
220
229
}
221
230
@@ -243,7 +252,7 @@ public Object execute(VirtualFrame frame) {
243
252
244
253
}, "localLoopDeoptwithChangedCode" , (target ) -> {
245
254
target .call (1 );
246
- }, 1 );
255
+ }, 0 , 1 );
247
256
}
248
257
249
258
@ Test
@@ -289,12 +298,166 @@ public Object execute(VirtualFrame frame) {
289
298
public void accept (CallTarget target ) {
290
299
target .call (input ++);
291
300
}
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 );
293
456
}
294
457
295
458
private static final int MAX_EXECUTIONS = 1024 ;
296
459
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 ) {
298
461
root .name = name ;
299
462
CallTarget callTarget = root .getCallTarget ();
300
463
callTargetFilter .set (callTarget );
@@ -313,7 +476,7 @@ private void assertDeoptLoop(BaseRootNode root, String name, Consumer<CallTarget
313
476
iterationCounter ++;
314
477
}
315
478
316
- assertTrue (iterationCounter * compilationsPerIteration > TruffleCompilerOptions .DeoptCycleDetectionThreshold .getDefaultValue ());
479
+ assertTrue (previousCompilations + iterationCounter * compilationsPerIteration > TruffleCompilerOptions .DeoptCycleDetectionThreshold .getDefaultValue ());
317
480
assertEquals (Boolean .FALSE , compilationResult .get ());
318
481
String failedReason = compilationFailedReason .get ();
319
482
assertNotNull (failedReason );
0 commit comments