diff options
| -rw-r--r-- | test/602-deoptimizeable/info.txt | 7 | ||||
| -rw-r--r-- | test/602-deoptimizeable/src/Main.java | 8 | ||||
| -rw-r--r-- | test/685-deoptimizeable/expected.txt | 2 | ||||
| -rw-r--r-- | test/685-deoptimizeable/info.txt | 1 | ||||
| -rw-r--r-- | test/685-deoptimizeable/src/Main.java | 252 | ||||
| -rw-r--r-- | test/knownfailures.json | 4 |
6 files changed, 269 insertions, 5 deletions
diff --git a/test/602-deoptimizeable/info.txt b/test/602-deoptimizeable/info.txt index d0952f903b..4b6147f7f1 100644 --- a/test/602-deoptimizeable/info.txt +++ b/test/602-deoptimizeable/info.txt @@ -1 +1,8 @@ Test various cases for full/partial-fragment deoptimization. + +TODO: we should remove this test as its expectations at point of +writing was that debuggable apps could run un-deoptimizeable frames +from the boot image. Today, we deoptimize the boot image as soon as +we see the app being debuggable. Test 685-deoptimizeable is the proper +version of this test, but we currently keep the 602 version around to +try diagnosing a gcstress issue. diff --git a/test/602-deoptimizeable/src/Main.java b/test/602-deoptimizeable/src/Main.java index 3d45b861c9..7a3285d793 100644 --- a/test/602-deoptimizeable/src/Main.java +++ b/test/602-deoptimizeable/src/Main.java @@ -62,10 +62,10 @@ public class Main { public static void main(String[] args) throws Exception { System.loadLibrary(args[0]); - // Only test stack frames in compiled mode. - if (!hasOatFile() || isInterpreted()) { - disableStackFrameAsserts(); - } + // TODO: Stack frame assertions are irrelevant in this test as we now + // always run JIT with debuggable. 685-deoptimizeable is the proper version + // of this test, but we keep this version around to diagnose a gcstress issue. + disableStackFrameAsserts(); final HashMap<DummyObject, Long> map = new HashMap<DummyObject, Long>(); // Single-frame deoptimization that covers partial fragment. diff --git a/test/685-deoptimizeable/expected.txt b/test/685-deoptimizeable/expected.txt new file mode 100644 index 0000000000..f993efcdad --- /dev/null +++ b/test/685-deoptimizeable/expected.txt @@ -0,0 +1,2 @@ +JNI_OnLoad called +Finishing diff --git a/test/685-deoptimizeable/info.txt b/test/685-deoptimizeable/info.txt new file mode 100644 index 0000000000..d0952f903b --- /dev/null +++ b/test/685-deoptimizeable/info.txt @@ -0,0 +1 @@ +Test various cases for full/partial-fragment deoptimization. diff --git a/test/685-deoptimizeable/src/Main.java b/test/685-deoptimizeable/src/Main.java new file mode 100644 index 0000000000..fc7fdea2aa --- /dev/null +++ b/test/685-deoptimizeable/src/Main.java @@ -0,0 +1,252 @@ +/* + * 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. + */ + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; + +class DummyObject { + public static boolean sHashCodeInvoked = false; + private int i; + + public DummyObject(int i) { + this.i = i; + } + + public boolean equals(Object obj) { + return (obj instanceof DummyObject) && (i == ((DummyObject)obj).i); + } + + public int hashCode() { + sHashCodeInvoked = true; + Main.assertIsManaged(); + Main.deoptimizeAll(); + Main.assertIsInterpreted(); + return i % 64; + } +} + +public class Main { + static boolean sFlag = false; + + public static native void deoptimizeAll(); + public static native void undeoptimizeAll(); + public static native void assertIsInterpreted(); + public static native void assertIsManaged(); + public static native void assertCallerIsInterpreted(); + public static native void disableStackFrameAsserts(); + public static native boolean hasJit(); + private static native void ensureJitCompiled(Class<?> itf, String method_name); + + public static void execute(Runnable runnable) throws Exception { + Thread t = new Thread(runnable); + t.start(); + t.join(); + } + + public static void ensureAllJitCompiled() { + ensureJitCompiled(HashMap.class, "hash"); + ensureJitCompiled(Main.class, "$noinline$run1"); + ensureJitCompiled(Main.class, "$noinline$run2"); + ensureJitCompiled(Main.class, "$noinline$run3A"); + ensureJitCompiled(Main.class, "$noinline$run3B"); + ensureJitCompiled(DummyObject.class, "hashCode"); + } + + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + // Only test stack frames in compiled mode. + if (!hasJit()) { + disableStackFrameAsserts(); + } + + ensureAllJitCompiled(); + + final HashMap<DummyObject, Long> map = new HashMap<DummyObject, Long>(); + + // Single-frame deoptimization that covers partial fragment. + execute(new Runnable() { + public void run() { + ensureJitCompiled(this.getClass(), "runInternal"); + runInternal(); + } + + public void runInternal() { + int[] arr = new int[3]; + assertIsManaged(); + int res = $noinline$run1(arr); + assertIsManaged(); // Only single frame is deoptimized. + if (res != 79) { + System.out.println("Failure 1!"); + System.exit(0); + } + } + }); + + // Single-frame deoptimization that covers a full fragment. + execute(new Runnable() { + public void run() { + ensureJitCompiled(this.getClass(), "runInternal"); + runInternal(); + } + + public void runInternal() { + try { + int[] arr = new int[3]; + assertIsManaged(); + // Use reflection to call $noinline$run2 so that it does + // full-fragment deoptimization since that is an upcall. + Class<?> cls = Class.forName("Main"); + Method method = cls.getDeclaredMethod("$noinline$run2", int[].class); + double res = (double)method.invoke(Main.class, arr); + assertIsManaged(); // Only single frame is deoptimized. + if (res != 79.3d) { + System.out.println("Failure 2!"); + System.exit(0); + } + } catch (Exception e) { + e.printStackTrace(System.out); + } + } + }); + + // Full-fragment deoptimization. + execute(new Runnable() { + public void run() { + ensureJitCompiled(this.getClass(), "runInternal"); + runInternal(); + } + + public void runInternal() { + assertIsManaged(); + float res = $noinline$run3B(); + assertIsInterpreted(); // Every deoptimizeable method is deoptimized. + if (res != 0.034f) { + System.out.println("Failure 3!"); + System.exit(0); + } + } + }); + + undeoptimizeAll(); // Make compiled code useable again. + ensureAllJitCompiled(); + + // Partial-fragment deoptimization. + execute(new Runnable() { + public void run() { + ensureJitCompiled(this.getClass(), "runInternal"); + ensureJitCompiled(HashMap.class, "hash"); + runInternal(); + } + + public void runInternal() { + try { + assertIsManaged(); + map.put(new DummyObject(10), Long.valueOf(100)); + assertIsInterpreted(); // Every deoptimizeable method is deoptimized. + } catch (Exception e) { + e.printStackTrace(System.out); + } + } + }); + + undeoptimizeAll(); // Make compiled code useable again. + ensureAllJitCompiled(); + + if (!DummyObject.sHashCodeInvoked) { + System.out.println("hashCode() method not invoked!"); + } + if (map.get(new DummyObject(10)) != 100) { + System.out.println("Wrong hashmap value!"); + } + System.out.println("Finishing"); + } + + public static int $noinline$run1(int[] arr) { + assertIsManaged(); + // Prevent inlining. + if (sFlag) { + throw new Error(); + } + boolean caught = false; + // BCE will use deoptimization for the code below. + try { + arr[0] = 1; + arr[1] = 1; + arr[2] = 1; + // This causes AIOOBE and triggers deoptimization from compiled code. + arr[3] = 1; + } catch (ArrayIndexOutOfBoundsException e) { + assertIsInterpreted(); // Single-frame deoptimization triggered. + caught = true; + } + if (!caught) { + System.out.println("Expected exception"); + } + assertIsInterpreted(); + return 79; + } + + public static double $noinline$run2(int[] arr) { + assertIsManaged(); + // Prevent inlining. + if (sFlag) { + throw new Error(); + } + boolean caught = false; + // BCE will use deoptimization for the code below. + try { + arr[0] = 1; + arr[1] = 1; + arr[2] = 1; + // This causes AIOOBE and triggers deoptimization from compiled code. + arr[3] = 1; + } catch (ArrayIndexOutOfBoundsException e) { + assertIsInterpreted(); // Single-frame deoptimization triggered. + caught = true; + } + if (!caught) { + System.out.println("Expected exception"); + } + assertIsInterpreted(); + return 79.3d; + } + + public static float $noinline$run3A() { + assertIsManaged(); + // Prevent inlining. + if (sFlag) { + throw new Error(); + } + // Deoptimize callers. + deoptimizeAll(); + assertIsInterpreted(); + assertCallerIsInterpreted(); // $noinline$run3B is deoptimizeable. + return 0.034f; + } + + public static float $noinline$run3B() { + assertIsManaged(); + // Prevent inlining. + if (sFlag) { + throw new Error(); + } + float res = $noinline$run3A(); + assertIsInterpreted(); + return res; + } +} diff --git a/test/knownfailures.json b/test/knownfailures.json index 6eb4707ea4..a7fb189e97 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -291,7 +291,8 @@ { "tests": ["454-get-vreg", "457-regs", - "602-deoptimizeable"], + "602-deoptimizeable", + "685-deoptimizeable"], "description": ["Tests that should fail when the optimizing compiler ", "compiles them non-debuggable."], "variant": "optimizing & ndebuggable | regalloc_gc & ndebuggable | speed-profile & ndebuggable | jit & ndebuggable" @@ -944,6 +945,7 @@ "675-checker-unverified-method", "676-proxy-jit-at-first-use", "676-resolve-field-type", + "685-deoptimizeable", "706-checker-scheduler", "707-checker-invalid-profile", "714-invoke-custom-lambda-metafactory", |