@@ -107,6 +107,48 @@ public Object execute(VirtualFrame frame) {
107
107
}, "alwaysDeopt" , CallTarget ::call , 1 );
108
108
}
109
109
110
+ @ Test
111
+ public void testAlwaysDeoptNoInvalidate () {
112
+ AssertionError expectedError = Assert .assertThrows (AssertionError .class , () -> assertDeoptLoop (new BaseRootNode () {
113
+ @ CompilerDirectives .TruffleBoundary
114
+ static void boundaryMethod () {
115
+
116
+ }
117
+
118
+ @ Override
119
+ public Object execute (VirtualFrame frame ) {
120
+ int arg = (int ) frame .getArguments ()[0 ];
121
+ int threshold = TruffleCompilerOptions .DeoptCycleDetectionThreshold .getDefaultValue ();
122
+ if (arg < threshold ) {
123
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
124
+ }
125
+ // call boundary method to prevent compiler from moving the following deoptimization
126
+ // up
127
+ boundaryMethod ();
128
+ CompilerDirectives .transferToInterpreter ();
129
+ return null ;
130
+ }
131
+ }, "alwaysDeoptNoInvalidate" , new Consumer <CallTarget >() {
132
+ int i ;
133
+
134
+ @ Override
135
+ public void accept (CallTarget callTarget ) {
136
+ callTarget .call (i ++);
137
+ if (i == TruffleCompilerOptions .DeoptCycleDetectionThreshold .getDefaultValue () + 1 ) {
138
+ /*
139
+ * Invalidate the target that was just deoptimized (but not invalidated) by
140
+ * transferToInterpreter. The exact same compilation is then repeated in the
141
+ * next iteration, but because deoptimize nodes with deoptimization action
142
+ * "None" (like the one used for transferToInterpreter) don't trigger deopt loop
143
+ * detection, no deopt loop should be detected.
144
+ */
145
+ ((OptimizedCallTarget ) callTarget ).invalidate ("Force one more recompile" );
146
+ }
147
+ }
148
+ }, 1 ));
149
+ Assert .assertEquals ("No deopt loop detected after " + MAX_EXECUTIONS + " executions" , expectedError .getMessage ());
150
+ }
151
+
110
152
@ Test
111
153
public void testLocalDeopt () {
112
154
assertDeoptLoop (new BaseRootNode () {
0 commit comments