1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17public class Main {
18  public static void main(String[] args) {
19    System.loadLibrary(args[0]);
20    Thread testThread = new Thread() {
21      public void run() {
22        performTest();
23      }
24    };
25    testThread.start();
26    try {
27      testThread.join(20 * 1000);  // 20s timeout.
28    } catch (InterruptedException ie) {
29      System.out.println("Interrupted.");
30      System.exit(1);
31    }
32    Thread.State state = testThread.getState();
33    if (state != Thread.State.TERMINATED) {
34      System.out.println("Test timed out, current state: " + state);
35      System.exit(1);
36    }
37  }
38
39  public static void performTest() {
40    new SubMain();
41    if ($noinline$returnInt() != 53) {
42      throw new Error("Unexpected return value");
43    }
44    if ($noinline$returnFloat() != 42.2f) {
45      throw new Error("Unexpected return value");
46    }
47    if ($noinline$returnDouble() != Double.longBitsToDouble(0xF000000000001111L)) {
48      throw new Error("Unexpected return value ");
49    }
50    if ($noinline$returnLong() != 0xFFFF000000001111L) {
51      throw new Error("Unexpected return value");
52    }
53
54    try {
55      $noinline$deopt();
56    } catch (Exception e) {}
57    DeoptimizationController.stopDeoptimization();
58
59    $noinline$inlineCache(new Main(), /* isSecondInvocation */ false);
60    if ($noinline$inlineCache(new SubMain(), /* isSecondInvocation */ true) != SubMain.class) {
61      throw new Error("Unexpected return value");
62    }
63
64    $noinline$inlineCache2(new Main(), /* isSecondInvocation */ false);
65    if ($noinline$inlineCache2(new SubMain(), /* isSecondInvocation */ true) != SubMain.class) {
66      throw new Error("Unexpected return value");
67    }
68
69    // Test polymorphic inline cache to the same target (inlineCache3).
70    $noinline$inlineCache3(new Main(), /* isSecondInvocation */ false);
71    $noinline$inlineCache3(new SubMain(), /* isSecondInvocation */ false);
72    if ($noinline$inlineCache3(new SubMain(), /* isSecondInvocation */ true) != null) {
73      throw new Error("Unexpected return value");
74    }
75
76    $noinline$stackOverflow(new Main(), /* isSecondInvocation */ false);
77    $noinline$stackOverflow(new SubMain(), /* isSecondInvocation */ true);
78
79    $opt$noinline$testOsrInlineLoop(null);
80    System.out.println("b28210356 passed.");
81  }
82
83  public static int $noinline$returnInt() {
84    if (doThrow) throw new Error("");
85    int i = 0;
86    for (; i < 100000; ++i) {
87    }
88    while (!isInOsrCode("$noinline$returnInt")) {}
89    System.out.println(i);
90    return 53;
91  }
92
93  public static float $noinline$returnFloat() {
94    if (doThrow) throw new Error("");
95    int i = 0;
96    for (; i < 200000; ++i) {
97    }
98    while (!isInOsrCode("$noinline$returnFloat")) {}
99    System.out.println(i);
100    return 42.2f;
101  }
102
103  public static double $noinline$returnDouble() {
104    if (doThrow) throw new Error("");
105    int i = 0;
106    for (; i < 300000; ++i) {
107    }
108    while (!isInOsrCode("$noinline$returnDouble")) {}
109    System.out.println(i);
110    return Double.longBitsToDouble(0xF000000000001111L);
111  }
112
113  public static long $noinline$returnLong() {
114    if (doThrow) throw new Error("");
115    int i = 0;
116    for (; i < 400000; ++i) {
117    }
118    while (!isInOsrCode("$noinline$returnLong")) {}
119    System.out.println(i);
120    return 0xFFFF000000001111L;
121  }
122
123  public static void $noinline$deopt() {
124    if (doThrow) throw new Error("");
125    int i = 0;
126    for (; i < 100000; ++i) {
127    }
128    while (!isInOsrCode("$noinline$deopt")) {}
129    DeoptimizationController.startDeoptimization();
130  }
131
132  public static Class $noinline$inlineCache(Main m, boolean isSecondInvocation) {
133    // If we are running in non-JIT mode, or were unlucky enough to get this method
134    // already JITted, just return the expected value.
135    if (!isInInterpreter("$noinline$inlineCache")) {
136      return SubMain.class;
137    }
138
139    ensureHasProfilingInfo("$noinline$inlineCache");
140
141    // Ensure that we have OSR code to jump to.
142    if (isSecondInvocation) {
143      ensureHasOsrCode("$noinline$inlineCache");
144    }
145
146    // This call will be optimized in the OSR compiled code
147    // to check and deoptimize if m is not of type 'Main'.
148    Main other = m.inlineCache();
149
150    // Jump to OSR compiled code. The second run
151    // of this method will have 'm' as a SubMain, and the compiled
152    // code we are jumping to will have wrongly optimize other as being a
153    // 'Main'.
154    if (isSecondInvocation) {
155      while (!isInOsrCode("$noinline$inlineCache")) {}
156    }
157
158    // We used to wrongly optimize this call and assume 'other' was a 'Main'.
159    return other.returnClass();
160  }
161
162  public static Class $noinline$inlineCache2(Main m, boolean isSecondInvocation) {
163    // If we are running in non-JIT mode, or were unlucky enough to get this method
164    // already JITted, just return the expected value.
165    if (!isInInterpreter("$noinline$inlineCache2")) {
166      return SubMain.class;
167    }
168
169    ensureHasProfilingInfo("$noinline$inlineCache2");
170
171    // Ensure that we have OSR code to jump to.
172    if (isSecondInvocation) {
173      ensureHasOsrCode("$noinline$inlineCache2");
174    }
175
176    // This call will be optimized in the OSR compiled code
177    // to check and deoptimize if m is not of type 'Main'.
178    Main other = m.inlineCache2();
179
180    // Jump to OSR compiled code. The second run
181    // of this method will have 'm' as a SubMain, and the compiled
182    // code we are jumping to will have wrongly optimize other as being null.
183    if (isSecondInvocation) {
184      while (!isInOsrCode("$noinline$inlineCache2")) {}
185    }
186
187    // We used to wrongly optimize this code and assume 'other' was always null.
188    return (other == null) ? null : other.returnClass();
189  }
190
191  public static Class $noinline$inlineCache3(Main m, boolean isSecondInvocation) {
192    // If we are running in non-JIT mode, or were unlucky enough to get this method
193    // already JITted, just return the expected value.
194    if (!isInInterpreter("$noinline$inlineCache3")) {
195      return null;
196    }
197
198    ensureHasProfilingInfo("$noinline$inlineCache3");
199
200    // Ensure that we have OSR code to jump to.
201    if (isSecondInvocation) {
202      ensureHasOsrCode("$noinline$inlineCache3");
203    }
204
205    // This call will be optimized in the OSR compiled code
206    // to check and deoptimize if m is not of type 'Main'.
207    Main other = m.inlineCache3();
208
209    // Jump to OSR compiled code. The second run
210    // of this method will have 'm' as a SubMain, and the compiled
211    // code we are jumping to will have wrongly optimize other as being null.
212    if (isSecondInvocation) {
213      while (!isInOsrCode("$noinline$inlineCache3")) {}
214    }
215
216    // We used to wrongly optimize this code and assume 'other' was always null.
217    return (other == null) ? null : other.returnClass();
218  }
219
220  public Main inlineCache() {
221    return new Main();
222  }
223
224  public Main inlineCache2() {
225    return null;
226  }
227
228  public Main inlineCache3() {
229    return null;
230  }
231
232  public Class returnClass() {
233    return Main.class;
234  }
235
236  public void otherInlineCache() {
237    return;
238  }
239
240  public static void $noinline$stackOverflow(Main m, boolean isSecondInvocation) {
241    // If we are running in non-JIT mode, or were unlucky enough to get this method
242    // already JITted, just return the expected value.
243    if (!isInInterpreter("$noinline$stackOverflow")) {
244      return;
245    }
246
247    // We need a ProfilingInfo object to populate the 'otherInlineCache' call.
248    ensureHasProfilingInfo("$noinline$stackOverflow");
249
250    if (isSecondInvocation) {
251      // Ensure we have an OSR code and we jump to it.
252      while (!isInOsrCode("$noinline$stackOverflow")) {}
253    }
254
255    for (int i = 0; i < (isSecondInvocation ? 10000000 : 1); ++i) {
256      // The first invocation of $noinline$stackOverflow will populate the inline
257      // cache with Main. The second invocation of the method, will see a SubMain
258      // and will therefore trigger deoptimization.
259      m.otherInlineCache();
260    }
261  }
262
263  public static void $opt$noinline$testOsrInlineLoop(String[] args) {
264    // Regression test for inlining a method with a loop to a method without a loop in OSR mode.
265    if (doThrow) throw new Error();
266    assertIntEquals(12, $opt$inline$testRemoveSuspendCheck(12, 5));
267    // Since we cannot have a loop directly in this method, we need to force the OSR
268    // compilation from native code.
269    ensureHasProfilingInfo("$opt$noinline$testOsrInlineLoop");
270    ensureHasOsrCode("$opt$noinline$testOsrInlineLoop");
271  }
272
273  public static int $opt$inline$testRemoveSuspendCheck(int x, int y) {
274    // For this test we need an inlined loop and have DCE re-run loop analysis
275    // after inlining.
276    while (y > 0) {
277      while ($opt$inline$inlineFalse() || !$opt$inline$inlineTrue()) {
278        x++;
279      }
280      y--;
281    }
282    return x;
283  }
284
285  public static boolean $opt$inline$inlineTrue() {
286    return true;
287  }
288
289  public static boolean $opt$inline$inlineFalse() {
290    return false;
291  }
292
293  public static void assertIntEquals(int expected, int result) {
294    if (expected != result) {
295      throw new Error("Expected: " + expected + ", found: " + result);
296    }
297  }
298
299  public static native boolean isInOsrCode(String methodName);
300  public static native boolean isInInterpreter(String methodName);
301  public static native void ensureHasProfilingInfo(String methodName);
302  public static native void ensureHasOsrCode(String methodName);
303
304  public static boolean doThrow = false;
305}
306
307class SubMain extends Main {
308  public Class returnClass() {
309    return SubMain.class;
310  }
311
312  public Main inlineCache() {
313    return new SubMain();
314  }
315
316  public Main inlineCache2() {
317    return new SubMain();
318  }
319
320  public void otherInlineCache() {
321    return;
322  }
323}
324