/*
* 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;
}
}