/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ public class Main { public static void main(String[] args) { System.loadLibrary(args[0]); Thread testThread = new Thread() { public void run() { performTest(); } }; testThread.start(); try { testThread.join(20 * 1000); // 20s timeout. } catch (InterruptedException ie) { System.out.println("Interrupted."); System.exit(1); } Thread.State state = testThread.getState(); if (state != Thread.State.TERMINATED) { System.out.println("Test timed out, current state: " + state); System.exit(1); } } public static void performTest() { new SubMain(); if ($noinline$returnInt() != 53) { throw new Error("Unexpected return value"); } if ($noinline$returnFloat() != 42.2f) { throw new Error("Unexpected return value"); } if ($noinline$returnDouble() != Double.longBitsToDouble(0xF000000000001111L)) { throw new Error("Unexpected return value "); } if ($noinline$returnLong() != 0xFFFF000000001111L) { throw new Error("Unexpected return value"); } try { $noinline$deopt(); } catch (Exception e) {} DeoptimizationController.stopDeoptimization(); $noinline$inlineCache(new Main(), /* isSecondInvocation */ false); if ($noinline$inlineCache(new SubMain(), /* isSecondInvocation */ true) != SubMain.class) { throw new Error("Unexpected return value"); } $noinline$inlineCache2(new Main(), /* isSecondInvocation */ false); if ($noinline$inlineCache2(new SubMain(), /* isSecondInvocation */ true) != SubMain.class) { throw new Error("Unexpected return value"); } // Test polymorphic inline cache to the same target (inlineCache3). $noinline$inlineCache3(new Main(), /* isSecondInvocation */ false); $noinline$inlineCache3(new SubMain(), /* isSecondInvocation */ false); if ($noinline$inlineCache3(new SubMain(), /* isSecondInvocation */ true) != null) { throw new Error("Unexpected return value"); } $noinline$stackOverflow(new Main(), /* isSecondInvocation */ false); $noinline$stackOverflow(new SubMain(), /* isSecondInvocation */ true); $opt$noinline$testOsrInlineLoop(null); System.out.println("b28210356 passed."); } public static int $noinline$returnInt() { if (doThrow) throw new Error(""); int i = 0; for (; i < 100000; ++i) { } while (!isInOsrCode("$noinline$returnInt")) {} System.out.println(i); return 53; } public static float $noinline$returnFloat() { if (doThrow) throw new Error(""); int i = 0; for (; i < 200000; ++i) { } while (!isInOsrCode("$noinline$returnFloat")) {} System.out.println(i); return 42.2f; } public static double $noinline$returnDouble() { if (doThrow) throw new Error(""); int i = 0; for (; i < 300000; ++i) { } while (!isInOsrCode("$noinline$returnDouble")) {} System.out.println(i); return Double.longBitsToDouble(0xF000000000001111L); } public static long $noinline$returnLong() { if (doThrow) throw new Error(""); int i = 0; for (; i < 400000; ++i) { } while (!isInOsrCode("$noinline$returnLong")) {} System.out.println(i); return 0xFFFF000000001111L; } public static void $noinline$deopt() { if (doThrow) throw new Error(""); int i = 0; for (; i < 100000; ++i) { } while (!isInOsrCode("$noinline$deopt")) {} DeoptimizationController.startDeoptimization(); } public static Class $noinline$inlineCache(Main m, boolean isSecondInvocation) { // If we are running in non-JIT mode, or were unlucky enough to get this method // already JITted, just return the expected value. if (!isInInterpreter("$noinline$inlineCache")) { return SubMain.class; } ensureHasProfilingInfo("$noinline$inlineCache"); // Ensure that we have OSR code to jump to. if (isSecondInvocation) { ensureHasOsrCode("$noinline$inlineCache"); } // This call will be optimized in the OSR compiled code // to check and deoptimize if m is not of type 'Main'. Main other = m.inlineCache(); // Jump to OSR compiled code. The second run // of this method will have 'm' as a SubMain, and the compiled // code we are jumping to will have wrongly optimize other as being a // 'Main'. if (isSecondInvocation) { while (!isInOsrCode("$noinline$inlineCache")) {} } // We used to wrongly optimize this call and assume 'other' was a 'Main'. return other.returnClass(); } public static Class $noinline$inlineCache2(Main m, boolean isSecondInvocation) { // If we are running in non-JIT mode, or were unlucky enough to get this method // already JITted, just return the expected value. if (!isInInterpreter("$noinline$inlineCache2")) { return SubMain.class; } ensureHasProfilingInfo("$noinline$inlineCache2"); // Ensure that we have OSR code to jump to. if (isSecondInvocation) { ensureHasOsrCode("$noinline$inlineCache2"); } // This call will be optimized in the OSR compiled code // to check and deoptimize if m is not of type 'Main'. Main other = m.inlineCache2(); // Jump to OSR compiled code. The second run // of this method will have 'm' as a SubMain, and the compiled // code we are jumping to will have wrongly optimize other as being null. if (isSecondInvocation) { while (!isInOsrCode("$noinline$inlineCache2")) {} } // We used to wrongly optimize this code and assume 'other' was always null. return (other == null) ? null : other.returnClass(); } public static Class $noinline$inlineCache3(Main m, boolean isSecondInvocation) { // If we are running in non-JIT mode, or were unlucky enough to get this method // already JITted, just return the expected value. if (!isInInterpreter("$noinline$inlineCache3")) { return null; } ensureHasProfilingInfo("$noinline$inlineCache3"); // Ensure that we have OSR code to jump to. if (isSecondInvocation) { ensureHasOsrCode("$noinline$inlineCache3"); } // This call will be optimized in the OSR compiled code // to check and deoptimize if m is not of type 'Main'. Main other = m.inlineCache3(); // Jump to OSR compiled code. The second run // of this method will have 'm' as a SubMain, and the compiled // code we are jumping to will have wrongly optimize other as being null. if (isSecondInvocation) { while (!isInOsrCode("$noinline$inlineCache3")) {} } // We used to wrongly optimize this code and assume 'other' was always null. return (other == null) ? null : other.returnClass(); } public Main inlineCache() { return new Main(); } public Main inlineCache2() { return null; } public Main inlineCache3() { return null; } public Class returnClass() { return Main.class; } public void otherInlineCache() { return; } public static void $noinline$stackOverflow(Main m, boolean isSecondInvocation) { // If we are running in non-JIT mode, or were unlucky enough to get this method // already JITted, just return the expected value. if (!isInInterpreter("$noinline$stackOverflow")) { return; } // We need a ProfilingInfo object to populate the 'otherInlineCache' call. ensureHasProfilingInfo("$noinline$stackOverflow"); if (isSecondInvocation) { // Ensure we have an OSR code and we jump to it. while (!isInOsrCode("$noinline$stackOverflow")) {} } for (int i = 0; i < (isSecondInvocation ? 10000000 : 1); ++i) { // The first invocation of $noinline$stackOverflow will populate the inline // cache with Main. The second invocation of the method, will see a SubMain // and will therefore trigger deoptimization. m.otherInlineCache(); } } public static void $opt$noinline$testOsrInlineLoop(String[] args) { // Regression test for inlining a method with a loop to a method without a loop in OSR mode. if (doThrow) throw new Error(); assertIntEquals(12, $opt$inline$testRemoveSuspendCheck(12, 5)); // Since we cannot have a loop directly in this method, we need to force the OSR // compilation from native code. ensureHasProfilingInfo("$opt$noinline$testOsrInlineLoop"); ensureHasOsrCode("$opt$noinline$testOsrInlineLoop"); } public static int $opt$inline$testRemoveSuspendCheck(int x, int y) { // For this test we need an inlined loop and have DCE re-run loop analysis // after inlining. while (y > 0) { while ($opt$inline$inlineFalse() || !$opt$inline$inlineTrue()) { x++; } y--; } return x; } public static boolean $opt$inline$inlineTrue() { return true; } public static boolean $opt$inline$inlineFalse() { return false; } public static void assertIntEquals(int expected, int result) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); } } public static native boolean isInOsrCode(String methodName); public static native boolean isInInterpreter(String methodName); public static native void ensureHasProfilingInfo(String methodName); public static native void ensureHasOsrCode(String methodName); public static boolean doThrow = false; } class SubMain extends Main { public Class returnClass() { return SubMain.class; } public Main inlineCache() { return new SubMain(); } public Main inlineCache2() { return new SubMain(); } public void otherInlineCache() { return; } }