diff options
Diffstat (limited to 'test')
68 files changed, 1749 insertions, 76 deletions
diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java index 708f61f028..2554b76f73 100644 --- a/test/004-UnsafeTest/src/Main.java +++ b/test/004-UnsafeTest/src/Main.java @@ -36,6 +36,13 @@ public class Main { } } + private static void check(Object actual, Object expected, String msg) { + if (actual != expected) { + System.out.println(msg + " : " + actual + " != " + expected); + System.exit(1); + } + } + private static Unsafe getUnsafe() throws Exception { Class<?> unsafeClass = Class.forName("sun.misc.Unsafe"); Field f = unsafeClass.getDeclaredField("theUnsafe"); @@ -80,6 +87,7 @@ public class Main { "Unsafe.arrayIndexScale(Object[])"); TestClass t = new TestClass(); + int intValue = 12345678; Field intField = TestClass.class.getDeclaredField("intVar"); long intOffset = unsafe.objectFieldOffset(intField); @@ -87,14 +95,23 @@ public class Main { unsafe.putInt(t, intOffset, intValue); check(t.intVar, intValue, "Unsafe.putInt(Object, long, int)"); check(unsafe.getInt(t, intOffset), intValue, "Unsafe.getInt(Object, long)"); + + long longValue = 1234567887654321L; Field longField = TestClass.class.getDeclaredField("longVar"); long longOffset = unsafe.objectFieldOffset(longField); - long longValue = 1234567887654321L; check(unsafe.getLong(t, longOffset), 0, "Unsafe.getLong(Object, long) - initial"); unsafe.putLong(t, longOffset, longValue); check(t.longVar, longValue, "Unsafe.putLong(Object, long, long)"); check(unsafe.getLong(t, longOffset), longValue, "Unsafe.getLong(Object, long)"); + Object objectValue = new Object(); + Field objectField = TestClass.class.getDeclaredField("objectVar"); + long objectOffset = unsafe.objectFieldOffset(objectField); + check(unsafe.getObject(t, objectOffset), null, "Unsafe.getObject(Object, long) - initial"); + unsafe.putObject(t, objectOffset, objectValue); + check(t.objectVar, objectValue, "Unsafe.putObject(Object, long, Object)"); + check(unsafe.getObject(t, objectOffset), objectValue, "Unsafe.getObject(Object, long)"); + if (unsafe.compareAndSwapInt(t, intOffset, 0, 1)) { System.out.println("Unexpectedly succeeding compareAndSwap..."); } @@ -119,6 +136,7 @@ public class Main { private static class TestClass { public int intVar = 0; public long longVar = 0; + public Object objectVar = null; } private static native int vmArrayBaseOffset(Class clazz); diff --git a/test/098-ddmc/src/Main.java b/test/098-ddmc/src/Main.java index f41ff2a94a..4914ba2289 100644 --- a/test/098-ddmc/src/Main.java +++ b/test/098-ddmc/src/Main.java @@ -43,14 +43,24 @@ public class Main { System.out.println("Confirm when we overflow, we don't roll over to zero. b/17392248"); final int overflowAllocations = 64 * 1024; // Won't fit in unsigned 16-bit value. + // TODO: Temporary fix. Keep the new objects live so they are not garbage collected. + // This will cause OOM exception for GC stress tests. The root cause is changed behaviour of + // getRecentAllocations(). Working on restoring its old behaviour. b/20037135 + Object[] objects = new Object[overflowAllocations]; for (int i = 0; i < overflowAllocations; i++) { - new Object(); + objects[i] = new Object(); } Allocations after = new Allocations(DdmVmInternal.getRecentAllocations()); System.out.println("before < overflowAllocations=" + (before.numberOfEntries < overflowAllocations)); System.out.println("after > before=" + (after.numberOfEntries > before.numberOfEntries)); System.out.println("after.numberOfEntries=" + after.numberOfEntries); + // TODO: Temporary fix as above. b/20037135 + objects = null; + Runtime.getRuntime().gc(); + final int fillerStrings = 16 * 1024; + String[] strings = new String[fillerStrings]; + System.out.println("Disable and confirm back to empty"); DdmVmInternal.enableRecentAllocations(false); System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus()); @@ -66,8 +76,8 @@ public class Main { System.out.println("Confirm we can reenable twice in a row without losing allocations"); DdmVmInternal.enableRecentAllocations(true); System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus()); - for (int i = 0; i < 16 * 1024; i++) { - new String("fnord"); + for (int i = 0; i < fillerStrings; i++) { + strings[i] = new String("fnord"); } Allocations first = new Allocations(DdmVmInternal.getRecentAllocations()); DdmVmInternal.enableRecentAllocations(true); diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index b2d7e55214..59722ad00d 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -29,6 +29,9 @@ #include "base/logging.h" #include "base/macros.h" +#include "gc/heap.h" +#include "gc/space/image_space.h" +#include "oat_file.h" #include "utils.h" namespace art { @@ -73,18 +76,45 @@ static bool CheckStack(Backtrace* bt, const std::vector<std::string>& seq) { } } + printf("Can not find %s in backtrace:\n", seq[cur_search_index].c_str()); + for (Backtrace::const_iterator it = bt->begin(); it != bt->end(); ++it) { + if (BacktraceMap::IsValid(it->map)) { + printf(" %s\n", it->func_name.c_str()); + } + } + return false; } #endif +// Currently we have to fall back to our own loader for the boot image when it's compiled PIC +// because its base is zero. Thus in-process unwinding through it won't work. This is a helper +// detecting this. +#if __linux__ +static bool IsPicImage() { + gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace(); + CHECK(image_space != nullptr); // We should be running with an image. + const OatFile* oat_file = image_space->GetOatFile(); + CHECK(oat_file != nullptr); // We should have an oat file to go with the image. + return oat_file->IsPic(); +} +#endif + extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(JNIEnv*, jobject, jint, jboolean) { #if __linux__ + if (IsPicImage()) { + LOG(INFO) << "Image is pic, in-process unwinding check bypassed."; + return JNI_TRUE; + } + // TODO: What to do on Valgrind? std::unique_ptr<Backtrace> bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid())); if (!bt->Unwind(0, nullptr)) { + printf("Can not unwind in process.\n"); return JNI_FALSE; } else if (bt->NumFrames() == 0) { + printf("No frames for unwind in process.\n"); return JNI_FALSE; } @@ -94,6 +124,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(JNIEnv*, jobject std::vector<std::string> seq = { "Java_Main_unwindInProcess", // This function. "boolean Main.unwindInProcess(int, boolean)", // The corresponding Java native method frame. + "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method. "void Main.main(java.lang.String[])" // The Java entry method. }; @@ -155,8 +186,9 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(JNIEnv*, jobj if (ptrace(PTRACE_ATTACH, pid, 0, 0)) { // Were not able to attach, bad. + printf("Failed to attach to other process.\n"); PLOG(ERROR) << "Failed to attach."; - kill(pid, SIGCONT); + kill(pid, SIGKILL); return JNI_FALSE; } @@ -172,8 +204,10 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(JNIEnv*, jobj std::unique_ptr<Backtrace> bt(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD)); bool result = true; if (!bt->Unwind(0, nullptr)) { + printf("Can not unwind other process.\n"); result = false; } else if (bt->NumFrames() == 0) { + printf("No frames for unwind of other process.\n"); result = false; } @@ -185,6 +219,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(JNIEnv*, jobj // Note: For some reason, the name isn't // resolved, so don't look for it right now. "boolean Main.sleep(int, boolean, double)", // The corresponding Java native method frame. + "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method. "void Main.main(java.lang.String[])" // The Java entry method. }; @@ -195,8 +230,8 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(JNIEnv*, jobj PLOG(ERROR) << "Detach failed"; } - // Continue the process so we can kill it on the Java side. - kill(pid, SIGCONT); + // Kill the other process once we are done with it. + kill(pid, SIGKILL); return result ? JNI_TRUE : JNI_FALSE; #else diff --git a/test/137-cfi/run b/test/137-cfi/run new file mode 100755 index 0000000000..78cf2aaf8d --- /dev/null +++ b/test/137-cfi/run @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Copyright (C) 2008 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. + +# Temporarily disable address space layout randomization (ASLR). +# This is need on host so that the linker loads core.oat at fixed address. +export LD_USE_LOAD_BIAS=1 + +exec ${RUN} "$@" diff --git a/test/137-cfi/src/Main.java b/test/137-cfi/src/Main.java index e184e66e6f..6cd187a033 100644 --- a/test/137-cfi/src/Main.java +++ b/test/137-cfi/src/Main.java @@ -20,11 +20,13 @@ import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Comparator; -public class Main { +public class Main implements Comparator<Main> { // Whether to test local unwinding. Libunwind uses linker info to find executables. As we do // not dlopen at the moment, this doesn't work, so keep it off for now. - public final static boolean TEST_LOCAL_UNWINDING = false; + public final static boolean TEST_LOCAL_UNWINDING = true; // Unwinding another process, modelling debuggerd. This doesn't use the linker, so should work // no matter whether we're using dlopen or not. @@ -32,6 +34,8 @@ public class Main { private boolean secondary; + private boolean passed; + public Main(boolean secondary) { this.secondary = secondary; } @@ -60,13 +64,13 @@ public class Main { } private void runSecondary() { - foo(true); + foo(); throw new RuntimeException("Didn't expect to get back..."); } private void runPrimary() { // First do the in-process unwinding. - if (TEST_LOCAL_UNWINDING && !foo(false)) { + if (TEST_LOCAL_UNWINDING && !foo()) { System.out.println("Unwinding self failed."); } @@ -99,7 +103,7 @@ public class Main { System.out.println("Unwinding other process failed."); } } finally { - // Kill the forked process. + // Kill the forked process if it is not already dead. p.destroy(); } } @@ -134,8 +138,19 @@ public class Main { } } - public boolean foo(boolean b) { - return bar(b); + public boolean foo() { + // Call bar via Arrays.binarySearch. + // This tests that we can unwind from framework code. + Main[] array = { this, this, this }; + Arrays.binarySearch(array, 0, 3, this /* value */, this /* comparator */); + return passed; + } + + public int compare(Main lhs, Main rhs) { + passed = bar(secondary); + // Returning "equal" ensures that we terminate search + // after first item and thus call bar() only once. + return 0; } public boolean bar(boolean b) { diff --git a/test/444-checker-nce/src/Main.java b/test/444-checker-nce/src/Main.java index 6ac0cad7e8..32122e4dcd 100644 --- a/test/444-checker-nce/src/Main.java +++ b/test/444-checker-nce/src/Main.java @@ -27,37 +27,37 @@ public class Main { return m.g(); } - /// CHECK-START: Main Main.thisTest() instruction_simplifier (before) + /// CHECK-START: Main Main.thisTest() ssa_builder (after) /// CHECK: NullCheck /// CHECK: InvokeStaticOrDirect - /// CHECK-START: Main Main.thisTest() instruction_simplifier (after) + /// CHECK-START: Main Main.thisTest() instruction_simplifier_after_types (after) /// CHECK-NOT: NullCheck /// CHECK: InvokeStaticOrDirect public Main thisTest() { return g(); } - /// CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier (before) + /// CHECK-START: Main Main.newInstanceRemoveTest() ssa_builder (after) /// CHECK: NewInstance /// CHECK: NullCheck /// CHECK: InvokeStaticOrDirect /// CHECK: NullCheck /// CHECK: InvokeStaticOrDirect - /// CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier (after) + /// CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier_after_types (after) /// CHECK-NOT: NullCheck public Main newInstanceRemoveTest() { Main m = new Main(); return m.g(); } - /// CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier (before) + /// CHECK-START: Main Main.newArrayRemoveTest() ssa_builder (after) /// CHECK: NewArray /// CHECK: NullCheck /// CHECK: ArrayGet - /// CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier (after) + /// CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier_after_types (after) /// CHECK: NewArray /// CHECK-NOT: NullCheck /// CHECK: ArrayGet @@ -178,10 +178,10 @@ public class Main { return n.g(); } - /// CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier (before) + /// CHECK-START: Main Main.scopeRemoveTest(int, Main) ssa_builder (after) /// CHECK: NullCheck - /// CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier (after) + /// CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier_after_types (after) /// CHECK-NOT: NullCheck public Main scopeRemoveTest(int count, Main a) { Main m = null; diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java index 8960df896b..ed6fc1ee2b 100644 --- a/test/449-checker-bce/src/Main.java +++ b/test/449-checker-bce/src/Main.java @@ -617,15 +617,21 @@ public class Main { /// CHECK: ArrayGet /// CHECK-START: void Main.foo1(int[], int, int) BCE (after) - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK-NOT: Deoptimize /// CHECK: Phi /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet /// CHECK-NOT: BoundsCheck /// CHECK: ArrayGet + // Added blocks for deoptimization. + /// CHECK: If + /// CHECK: Goto + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK-NOT: Deoptimize + /// CHECK: Goto + /// CHECK: Phi + /// CHECK: Goto void foo1(int[] array, int start, int end) { // Three HDeoptimize will be added. One for @@ -646,15 +652,21 @@ public class Main { /// CHECK: ArrayGet /// CHECK-START: void Main.foo2(int[], int, int) BCE (after) - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK-NOT: Deoptimize /// CHECK: Phi /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet /// CHECK-NOT: BoundsCheck /// CHECK: ArrayGet + // Added blocks for deoptimization. + /// CHECK: If + /// CHECK: Goto + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK-NOT: Deoptimize + /// CHECK: Goto + /// CHECK: Phi + /// CHECK: Goto void foo2(int[] array, int start, int end) { // Three HDeoptimize will be added. One for @@ -675,14 +687,20 @@ public class Main { /// CHECK: ArrayGet /// CHECK-START: void Main.foo3(int[], int) BCE (after) - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK-NOT: Deoptimize /// CHECK: Phi /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet /// CHECK-NOT: BoundsCheck /// CHECK: ArrayGet + // Added blocks for deoptimization. + /// CHECK: If + /// CHECK: Goto + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK-NOT: Deoptimize + /// CHECK: Goto + /// CHECK: Phi + /// CHECK: Goto void foo3(int[] array, int end) { // Two HDeoptimize will be added. One for end < array.length, @@ -694,6 +712,7 @@ public class Main { } } + /// CHECK-START: void Main.foo4(int[], int) BCE (before) /// CHECK: BoundsCheck /// CHECK: ArraySet @@ -701,14 +720,20 @@ public class Main { /// CHECK: ArrayGet /// CHECK-START: void Main.foo4(int[], int) BCE (after) - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK-NOT: Deoptimize /// CHECK: Phi /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet /// CHECK-NOT: BoundsCheck /// CHECK: ArrayGet + // Added blocks for deoptimization. + /// CHECK: If + /// CHECK: Goto + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK-NOT: Deoptimize + /// CHECK: Goto + /// CHECK: Phi + /// CHECK: Goto void foo4(int[] array, int end) { // Two HDeoptimize will be added. One for end <= array.length, @@ -734,8 +759,6 @@ public class Main { /// CHECK-START: void Main.foo5(int[], int) BCE (after) /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet - /// CHECK: Deoptimize - /// CHECK-NOT: Deoptimize /// CHECK: Phi /// CHECK-NOT: BoundsCheck /// CHECK: ArrayGet @@ -743,6 +766,15 @@ public class Main { /// CHECK: ArrayGet /// CHECK-NOT: BoundsCheck /// CHECK: ArrayGet + // Added blocks for deoptimization. + /// CHECK: If + /// CHECK: Goto + /// CHECK: Deoptimize + /// CHECK-NOT: Deoptimize + /// CHECK: Goto + // array.length is defined before the loop header so no phi is needed. + /// CHECK-NOT: Phi + /// CHECK: Goto void foo5(int[] array, int end) { // Bounds check in this loop can be eliminated without deoptimization. @@ -774,10 +806,6 @@ public class Main { /// CHECK: ArraySet /// CHECK-START: void Main.foo6(int[], int, int) BCE (after) - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK-NOT: Deoptimize /// CHECK: Phi /// CHECK-NOT: BoundsCheck /// CHECK: ArrayGet @@ -791,6 +819,17 @@ public class Main { /// CHECK: ArrayGet /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet + // Added blocks for deoptimization. + /// CHECK: If + /// CHECK: Goto + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK-NOT: Deoptimize + /// CHECK: Goto + /// CHECK: Phi + /// CHECK: Goto + /// CHECK-NOT: Deoptimize void foo6(int[] array, int start, int end) { // Three HDeoptimize will be added. One for @@ -810,15 +849,21 @@ public class Main { /// CHECK: ArrayGet /// CHECK-START: void Main.foo7(int[], int, int, boolean) BCE (after) - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK-NOT: Deoptimize /// CHECK: Phi /// CHECK: BoundsCheck /// CHECK: ArrayGet /// CHECK-NOT: BoundsCheck /// CHECK: ArrayGet + // Added blocks for deoptimization. + /// CHECK: If + /// CHECK: Goto + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK-NOT: Deoptimize + /// CHECK: Goto + /// CHECK: Phi + /// CHECK: Goto void foo7(int[] array, int start, int end, boolean lowEnd) { // Three HDeoptimize will be added. One for @@ -837,6 +882,73 @@ public class Main { } + /// CHECK-START: void Main.foo8(int[][], int, int) BCE (before) + /// CHECK: BoundsCheck + /// CHECK: ArrayGet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + + /// CHECK-START: void Main.foo8(int[][], int, int) BCE (after) + /// CHECK: Phi + /// CHECK-NOT: BoundsCheck + /// CHECK: ArrayGet + /// CHECK: Phi + /// CHECK-NOT: BoundsCheck + /// CHECK: ArraySet + // Added blocks for deoptimization. + /// CHECK: If + /// CHECK: Goto + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK-NOT: Deoptimize + /// CHECK: Goto + /// CHECK: Phi + /// CHECK: Goto + + void foo8(int[][] matrix, int start, int end) { + // Three HDeoptimize will be added for the outer loop. + // start >= 0, end <= matrix.length, and null check on matrix. + // Three HDeoptimize will be added for the inner loop + // start >= 0 (TODO: this may be optimized away), + // end <= row.length, and null check on row. + for (int i = start; i < end; i++) { + int[] row = matrix[i]; + for (int j = start; j < end; j++) { + row[j] = 1; + } + } + } + + + /// CHECK-START: void Main.foo9(int[]) BCE (before) + /// CHECK: NullCheck + /// CHECK: BoundsCheck + /// CHECK: ArrayGet + + /// CHECK-START: void Main.foo9(int[]) BCE (after) + // The loop is guaranteed to be entered. No need to transform the + // loop for loop body entry test. + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK-NOT: Deoptimize + /// CHECK: Phi + /// CHECK-NOT: NullCheck + /// CHECK-NOT: BoundsCheck + /// CHECK: ArrayGet + + void foo9(int[] array) { + // Two HDeoptimize will be added. One for + // 10 <= array.length, and one for null check on array. + for (int i = 0 ; i < 10; i++) { + sum += array[i]; + } + } + + /// CHECK-START: void Main.partialLooping(int[], int, int) BCE (before) /// CHECK: BoundsCheck /// CHECK: ArraySet @@ -951,6 +1063,13 @@ public class Main { main.foo6(new int[10], 2, 7); main = new Main(); + int[] array9 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + main.foo9(array9); + if (main.sum != 45) { + System.out.println("foo9 failed!"); + } + + main = new Main(); int[] array = new int[4]; main.partialLooping(new int[3], 0, 4); if ((array[0] != 1) && (array[1] != 1) && diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java index 4056275d3d..9070627f1c 100644 --- a/test/450-checker-types/src/Main.java +++ b/test/450-checker-types/src/Main.java @@ -364,6 +364,37 @@ public class Main { ((SubclassA)b).$noinline$g(); } + public SubclassA $noinline$getSubclass() { throw new RuntimeException(); } + + /// CHECK-START: void Main.testArraySimpleRemove() instruction_simplifier_after_types (before) + /// CHECK: CheckCast + + /// CHECK-START: void Main.testArraySimpleRemove() instruction_simplifier_after_types (after) + /// CHECK-NOT: CheckCast + public void testArraySimpleRemove() { + Super[] b = new SubclassA[10]; + SubclassA[] c = (SubclassA[])b; + } + + /// CHECK-START: void Main.testInvokeSimpleRemove() instruction_simplifier_after_types (before) + /// CHECK: CheckCast + + /// CHECK-START: void Main.testInvokeSimpleRemove() instruction_simplifier_after_types (after) + /// CHECK-NOT: CheckCast + public void testInvokeSimpleRemove() { + Super b = $noinline$getSubclass(); + ((SubclassA)b).$noinline$g(); + } + /// CHECK-START: void Main.testArrayGetSimpleRemove() instruction_simplifier_after_types (before) + /// CHECK: CheckCast + + /// CHECK-START: void Main.testArrayGetSimpleRemove() instruction_simplifier_after_types (after) + /// CHECK-NOT: CheckCast + public void testArrayGetSimpleRemove() { + Super[] a = new SubclassA[10]; + ((SubclassA)a[0]).$noinline$g(); + } + public static void main(String[] args) { } } diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java index ad5fc8ef93..3c3b939aeb 100644 --- a/test/458-checker-instruction-simplification/src/Main.java +++ b/test/458-checker-instruction-simplification/src/Main.java @@ -933,18 +933,18 @@ public class Main { * remove the second. */ - /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_types (before) + /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_bce (before) /// CHECK-DAG: <<Arg:z\d+>> ParameterValue /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>] /// CHECK-DAG: <<NotNotArg:z\d+>> BooleanNot [<<NotArg>>] /// CHECK-DAG: Return [<<NotNotArg>>] - /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_types (after) + /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_bce (after) /// CHECK-DAG: <<Arg:z\d+>> ParameterValue /// CHECK-DAG: BooleanNot [<<Arg>>] /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_types (after) + /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_bce (after) /// CHECK: BooleanNot /// CHECK-NOT: BooleanNot diff --git a/test/482-checker-loop-back-edge-use/src/Main.java b/test/482-checker-loop-back-edge-use/src/Main.java index 5754723d8a..a4280de749 100644 --- a/test/482-checker-loop-back-edge-use/src/Main.java +++ b/test/482-checker-loop-back-edge-use/src/Main.java @@ -36,8 +36,8 @@ public class Main { } /// CHECK-START: void Main.loop3(boolean) liveness (after) - /// CHECK: ParameterValue liveness:4 ranges:{[4,62)} uses:[58,62] - /// CHECK: Goto liveness:60 + /// CHECK: ParameterValue liveness:4 ranges:{[4,64)} uses:[60,64] + /// CHECK: Goto liveness:62 /// CHECK-START: void Main.loop3(boolean) liveness (after) /// CHECK-NOT: Goto liveness:56 @@ -63,9 +63,9 @@ public class Main { } /// CHECK-START: void Main.loop5(boolean) liveness (after) - /// CHECK: ParameterValue liveness:4 ranges:{[4,52)} uses:[35,44,48,52] - /// CHECK: Goto liveness:46 - /// CHECK: Goto liveness:50 + /// CHECK: ParameterValue liveness:4 ranges:{[4,54)} uses:[37,46,50,54] + /// CHECK: Goto liveness:48 + /// CHECK: Goto liveness:52 public static void loop5(boolean incoming) { // 'incoming' must have a use at both back edges. while (Runtime.getRuntime() != null) { @@ -76,8 +76,8 @@ public class Main { } /// CHECK-START: void Main.loop6(boolean) liveness (after) - /// CHECK: ParameterValue liveness:4 ranges:{[4,48)} uses:[26,48] - /// CHECK: Goto liveness:46 + /// CHECK: ParameterValue liveness:4 ranges:{[4,50)} uses:[26,50] + /// CHECK: Goto liveness:48 /// CHECK-START: void Main.loop6(boolean) liveness (after) /// CHECK-NOT: Goto liveness:24 @@ -90,9 +90,9 @@ public class Main { } /// CHECK-START: void Main.loop7(boolean) liveness (after) - /// CHECK: ParameterValue liveness:4 ranges:{[4,52)} uses:[34,43,48,52] - /// CHECK: Goto liveness:46 - /// CHECK: Goto liveness:50 + /// CHECK: ParameterValue liveness:4 ranges:{[4,54)} uses:[36,45,50,54] + /// CHECK: Goto liveness:48 + /// CHECK: Goto liveness:52 public static void loop7(boolean incoming) { // 'incoming' must have a use at both back edges. while (Runtime.getRuntime() != null) { @@ -102,9 +102,9 @@ public class Main { } /// CHECK-START: void Main.loop8() liveness (after) - /// CHECK: StaticFieldGet liveness:14 ranges:{[14,46)} uses:[37,42,46] - /// CHECK: Goto liveness:40 - /// CHECK: Goto liveness:44 + /// CHECK: StaticFieldGet liveness:14 ranges:{[14,48)} uses:[39,44,48] + /// CHECK: Goto liveness:42 + /// CHECK: Goto liveness:46 public static void loop8() { // 'incoming' must have a use at both back edges. boolean incoming = field; @@ -114,8 +114,8 @@ public class Main { } /// CHECK-START: void Main.loop9() liveness (after) - /// CHECK: StaticFieldGet liveness:24 ranges:{[24,38)} uses:[33,38] - /// CHECK: Goto liveness:40 + /// CHECK: StaticFieldGet liveness:26 ranges:{[26,40)} uses:[35,40] + /// CHECK: Goto liveness:42 public static void loop9() { while (Runtime.getRuntime() != null) { // 'incoming' must only have a use in the inner loop. diff --git a/test/490-checker-inline/expected.txt b/test/490-checker-inline/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/490-checker-inline/expected.txt diff --git a/test/490-checker-inline/info.txt b/test/490-checker-inline/info.txt new file mode 100644 index 0000000000..0e42d771fe --- /dev/null +++ b/test/490-checker-inline/info.txt @@ -0,0 +1 @@ +Check that we inline virtual and interface calls. diff --git a/test/490-checker-inline/src/Main.java b/test/490-checker-inline/src/Main.java new file mode 100644 index 0000000000..21a01897e2 --- /dev/null +++ b/test/490-checker-inline/src/Main.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 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. + */ + +interface Itf { + public void invokeInterface(); +} + +public class Main implements Itf { + + public void invokeInterface () { + } + + public void invokeVirtual() { + } + + public static Main createMain() { + return new Main(); + } + + public static Itf createItf() { + return new Main(); + } + + /// CHECK-START: void Main.testMethod() inliner (before) + /// CHECK-DAG: InvokeVirtual + /// CHECK-DAG: InvokeInterface + + /// CHECK-START: void Main.testMethod() inliner (after) + /// CHECK-NOT: Invoke{{.*}} + + public static void testMethod() { + createMain().invokeVirtual(); + createItf().invokeInterface(); + } + + public static void main(String[] args) { + testMethod(); + } +} diff --git a/test/493-checker-inline-invoke-interface/expected.txt b/test/493-checker-inline-invoke-interface/expected.txt new file mode 100644 index 0000000000..93620a6fb5 --- /dev/null +++ b/test/493-checker-inline-invoke-interface/expected.txt @@ -0,0 +1,5 @@ +Hello from clinit +java.lang.Exception + at ForceStatic.<clinit>(Main.java:24) + at Main.foo(Main.java:31) + at Main.main(Main.java:42) diff --git a/test/493-checker-inline-invoke-interface/info.txt b/test/493-checker-inline-invoke-interface/info.txt new file mode 100644 index 0000000000..bac9c82c9d --- /dev/null +++ b/test/493-checker-inline-invoke-interface/info.txt @@ -0,0 +1,2 @@ +Check that we can optimize interface calls without +requiring the verifier to sharpen them. diff --git a/test/493-checker-inline-invoke-interface/src/Main.java b/test/493-checker-inline-invoke-interface/src/Main.java new file mode 100644 index 0000000000..44b727fe55 --- /dev/null +++ b/test/493-checker-inline-invoke-interface/src/Main.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 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. + */ + +interface Itf { + public void foo(); +} + +class ForceStatic { + static { + System.out.println("Hello from clinit"); + new Exception().printStackTrace(); + } + static int field; +} + +public class Main implements Itf { + public void foo() { + int a = ForceStatic.field; + } + + /// CHECK-START: void Main.main(java.lang.String[]) inliner (before) + /// CHECK: InvokeStaticOrDirect + /// CHECK: InvokeInterface + + /// CHECK-START: void Main.main(java.lang.String[]) inliner (after) + /// CHECK-NOT: Invoke{{.*}} + public static void main(String[] args) { + Itf itf = bar(); + itf.foo(); + } + + public static Itf bar() { + return new Main(); + } +} diff --git a/test/494-checker-instanceof-tests/expected.txt b/test/494-checker-instanceof-tests/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/494-checker-instanceof-tests/expected.txt diff --git a/test/494-checker-instanceof-tests/info.txt b/test/494-checker-instanceof-tests/info.txt new file mode 100644 index 0000000000..59e20bd6a9 --- /dev/null +++ b/test/494-checker-instanceof-tests/info.txt @@ -0,0 +1 @@ +Checker test for optimizations on instanceof. diff --git a/test/494-checker-instanceof-tests/src/Main.java b/test/494-checker-instanceof-tests/src/Main.java new file mode 100644 index 0000000000..bff9c72ded --- /dev/null +++ b/test/494-checker-instanceof-tests/src/Main.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2015 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 boolean $inline$classTypeTest(Object o) { + return o instanceof SubMain; + } + + public static boolean $inline$interfaceTypeTest(Object o) { + return o instanceof Itf; + } + + public static SubMain subMain; + public static Main mainField; + public static Unrelated unrelatedField; + public static FinalUnrelated finalUnrelatedField; + + /// CHECK-START: boolean Main.classTypeTestNull() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 0 + /// CHECK-DAG: Return [<<Const>>] + public static boolean classTypeTestNull() { + return $inline$classTypeTest(null); + } + + /// CHECK-START: boolean Main.classTypeTestExactMain() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 0 + /// CHECK-DAG: Return [<<Const>>] + public static boolean classTypeTestExactMain() { + return $inline$classTypeTest(new Main()); + } + + /// CHECK-START: boolean Main.classTypeTestExactSubMain() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 1 + /// CHECK-DAG: Return [<<Const>>] + public static boolean classTypeTestExactSubMain() { + return $inline$classTypeTest(new SubMain()); + } + + /// CHECK-START: boolean Main.classTypeTestSubMainOrNull() register (after) + /// CHECK-DAG: <<Value:z\d+>> NotEqual + /// CHECK-DAG: Return [<<Value>>] + public static boolean classTypeTestSubMainOrNull() { + return $inline$classTypeTest(subMain); + } + + /// CHECK-START: boolean Main.classTypeTestMainOrNull() register (after) + /// CHECK-DAG: <<Value:z\d+>> InstanceOf + /// CHECK-DAG: Return [<<Value>>] + public static boolean classTypeTestMainOrNull() { + return $inline$classTypeTest(mainField); + } + + /// CHECK-START: boolean Main.classTypeTestUnrelated() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 0 + /// CHECK-DAG: Return [<<Const>>] + public static boolean classTypeTestUnrelated() { + return $inline$classTypeTest(unrelatedField); + } + + /// CHECK-START: boolean Main.classTypeTestFinalUnrelated() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 0 + /// CHECK-DAG: Return [<<Const>>] + public static boolean classTypeTestFinalUnrelated() { + return $inline$classTypeTest(finalUnrelatedField); + } + + /// CHECK-START: boolean Main.interfaceTypeTestNull() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 0 + /// CHECK-DAG: Return [<<Const>>] + public static boolean interfaceTypeTestNull() { + return $inline$interfaceTypeTest(null); + } + + /// CHECK-START: boolean Main.interfaceTypeTestExactMain() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 0 + /// CHECK-DAG: Return [<<Const>>] + public static boolean interfaceTypeTestExactMain() { + return $inline$interfaceTypeTest(new Main()); + } + + /// CHECK-START: boolean Main.interfaceTypeTestExactSubMain() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 1 + /// CHECK-DAG: Return [<<Const>>] + public static boolean interfaceTypeTestExactSubMain() { + return $inline$interfaceTypeTest(new SubMain()); + } + + /// CHECK-START: boolean Main.interfaceTypeTestSubMainOrNull() register (after) + /// CHECK-DAG: <<Value:z\d+>> NotEqual + /// CHECK-DAG: Return [<<Value>>] + public static boolean interfaceTypeTestSubMainOrNull() { + return $inline$interfaceTypeTest(subMain); + } + + /// CHECK-START: boolean Main.interfaceTypeTestMainOrNull() register (after) + /// CHECK-DAG: <<Value:z\d+>> InstanceOf + /// CHECK-DAG: Return [<<Value>>] + public static boolean interfaceTypeTestMainOrNull() { + return $inline$interfaceTypeTest(mainField); + } + + /// CHECK-START: boolean Main.interfaceTypeTestUnrelated() register (after) + /// CHECK-DAG: <<Value:z\d+>> InstanceOf + /// CHECK-DAG: Return [<<Value>>] + public static boolean interfaceTypeTestUnrelated() { + // This method is the main difference between doing an instanceof on an interface + // or a class. We have to keep the instanceof in case a subclass of Unrelated + // implements the interface. + return $inline$interfaceTypeTest(unrelatedField); + } + + /// CHECK-START: boolean Main.interfaceTypeTestFinalUnrelated() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 0 + /// CHECK-DAG: Return [<<Const>>] + public static boolean interfaceTypeTestFinalUnrelated() { + return $inline$interfaceTypeTest(finalUnrelatedField); + } + + public static void expect(boolean expected, boolean actual) { + if (expected != actual) { + throw new Error("Unexpected result"); + } + } + + public static void main(String[] args) { + expect(false, classTypeTestNull()); + expect(false, classTypeTestExactMain()); + expect(true, classTypeTestExactSubMain()); + + subMain = null; + expect(false, classTypeTestSubMainOrNull()); + subMain = new SubMain(); + expect(true, classTypeTestSubMainOrNull()); + + mainField = null; + expect(false, classTypeTestMainOrNull()); + mainField = new Main(); + expect(false, classTypeTestMainOrNull()); + mainField = new SubMain(); + expect(true, classTypeTestMainOrNull()); + + unrelatedField = null; + expect(false, classTypeTestUnrelated()); + unrelatedField = new Unrelated(); + expect(false, classTypeTestUnrelated()); + + finalUnrelatedField = null; + expect(false, classTypeTestFinalUnrelated()); + finalUnrelatedField = new FinalUnrelated(); + expect(false, classTypeTestFinalUnrelated()); + + expect(false, interfaceTypeTestNull()); + expect(false, interfaceTypeTestExactMain()); + expect(true, interfaceTypeTestExactSubMain()); + + subMain = null; + expect(false, interfaceTypeTestSubMainOrNull()); + subMain = new SubMain(); + expect(true, interfaceTypeTestSubMainOrNull()); + + mainField = null; + expect(false, interfaceTypeTestMainOrNull()); + mainField = new Main(); + expect(false, interfaceTypeTestMainOrNull()); + mainField = new SubMain(); + expect(true, interfaceTypeTestMainOrNull()); + + unrelatedField = null; + expect(false, interfaceTypeTestUnrelated()); + unrelatedField = new Unrelated(); + expect(false, interfaceTypeTestUnrelated()); + + finalUnrelatedField = null; + expect(false, interfaceTypeTestFinalUnrelated()); + finalUnrelatedField = new FinalUnrelated(); + expect(false, interfaceTypeTestFinalUnrelated()); + } +} + +interface Itf { +} + +class SubMain extends Main implements Itf { +} + +class Unrelated { +} + +final class FinalUnrelated { +} diff --git a/test/495-checker-checkcast-tests/expected.txt b/test/495-checker-checkcast-tests/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/495-checker-checkcast-tests/expected.txt diff --git a/test/495-checker-checkcast-tests/info.txt b/test/495-checker-checkcast-tests/info.txt new file mode 100644 index 0000000000..4517b22c69 --- /dev/null +++ b/test/495-checker-checkcast-tests/info.txt @@ -0,0 +1 @@ +Checker tests for optimizations on checkcast. diff --git a/test/495-checker-checkcast-tests/src/Main.java b/test/495-checker-checkcast-tests/src/Main.java new file mode 100644 index 0000000000..aa6d5a75f7 --- /dev/null +++ b/test/495-checker-checkcast-tests/src/Main.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2015 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 boolean $inline$classTypeTest(Object o) { + return ((SubMain)o) == o; + } + + public static boolean $inline$interfaceTypeTest(Object o) { + return ((Itf)o) == o; + } + + public static SubMain subMain; + public static Main mainField; + public static Unrelated unrelatedField; + public static FinalUnrelated finalUnrelatedField; + + /// CHECK-START: boolean Main.classTypeTestNull() register (after) + /// CHECK-NOT: CheckCast + public static boolean classTypeTestNull() { + return $inline$classTypeTest(null); + } + + /// CHECK-START: boolean Main.classTypeTestExactMain() register (after) + /// CHECK: CheckCast + public static boolean classTypeTestExactMain() { + return $inline$classTypeTest(new Main()); + } + + /// CHECK-START: boolean Main.classTypeTestExactSubMain() register (after) + /// CHECK-NOT: CheckCast + public static boolean classTypeTestExactSubMain() { + return $inline$classTypeTest(new SubMain()); + } + + /// CHECK-START: boolean Main.classTypeTestSubMainOrNull() register (after) + /// CHECK-NOT: CheckCast + public static boolean classTypeTestSubMainOrNull() { + return $inline$classTypeTest(subMain); + } + + /// CHECK-START: boolean Main.classTypeTestMainOrNull() register (after) + /// CHECK: CheckCast + public static boolean classTypeTestMainOrNull() { + return $inline$classTypeTest(mainField); + } + + /// CHECK-START: boolean Main.classTypeTestUnrelated() register (after) + /// CHECK: CheckCast + public static boolean classTypeTestUnrelated() { + return $inline$classTypeTest(unrelatedField); + } + + /// CHECK-START: boolean Main.classTypeTestFinalUnrelated() register (after) + /// CHECK: CheckCast + public static boolean classTypeTestFinalUnrelated() { + return $inline$classTypeTest(finalUnrelatedField); + } + + /// CHECK-START: boolean Main.interfaceTypeTestNull() register (after) + /// CHECK-NOT: CheckCast + public static boolean interfaceTypeTestNull() { + return $inline$interfaceTypeTest(null); + } + + /// CHECK-START: boolean Main.interfaceTypeTestExactMain() register (after) + /// CHECK: CheckCast + public static boolean interfaceTypeTestExactMain() { + return $inline$interfaceTypeTest(new Main()); + } + + /// CHECK-START: boolean Main.interfaceTypeTestExactSubMain() register (after) + /// CHECK-NOT: CheckCast + public static boolean interfaceTypeTestExactSubMain() { + return $inline$interfaceTypeTest(new SubMain()); + } + + /// CHECK-START: boolean Main.interfaceTypeTestSubMainOrNull() register (after) + /// CHECK-NOT: CheckCast + public static boolean interfaceTypeTestSubMainOrNull() { + return $inline$interfaceTypeTest(subMain); + } + + /// CHECK-START: boolean Main.interfaceTypeTestMainOrNull() register (after) + /// CHECK: CheckCast + public static boolean interfaceTypeTestMainOrNull() { + return $inline$interfaceTypeTest(mainField); + } + + /// CHECK-START: boolean Main.interfaceTypeTestUnrelated() register (after) + /// CHECK: CheckCast + public static boolean interfaceTypeTestUnrelated() { + return $inline$interfaceTypeTest(unrelatedField); + } + + /// CHECK-START: boolean Main.interfaceTypeTestFinalUnrelated() register (after) + /// CHECK: CheckCast + public static boolean interfaceTypeTestFinalUnrelated() { + return $inline$interfaceTypeTest(finalUnrelatedField); + } + + public static void main(String[] args) { + classTypeTestNull(); + try { + classTypeTestExactMain(); + throw new Error("ClassCastException expected"); + } catch (ClassCastException e) {} + classTypeTestExactSubMain(); + + subMain = null; + classTypeTestSubMainOrNull(); + subMain = new SubMain(); + classTypeTestSubMainOrNull(); + + mainField = null; + classTypeTestMainOrNull(); + mainField = new Main(); + try { + classTypeTestMainOrNull(); + throw new Error("ClassCastException expected"); + } catch (ClassCastException e) {} + mainField = new SubMain(); + classTypeTestMainOrNull(); + + unrelatedField = null; + classTypeTestUnrelated(); + unrelatedField = new Unrelated(); + try { + classTypeTestUnrelated(); + throw new Error("ClassCastException expected"); + } catch (ClassCastException e) {} + + finalUnrelatedField = null; + classTypeTestFinalUnrelated(); + finalUnrelatedField = new FinalUnrelated(); + try { + classTypeTestFinalUnrelated(); + throw new Error("ClassCastException expected"); + } catch (ClassCastException e) {} + + interfaceTypeTestNull(); + try { + interfaceTypeTestExactMain(); + throw new Error("ClassCastException expected"); + } catch (ClassCastException e) {} + interfaceTypeTestExactSubMain(); + + subMain = null; + interfaceTypeTestSubMainOrNull(); + subMain = new SubMain(); + interfaceTypeTestSubMainOrNull(); + + mainField = null; + interfaceTypeTestMainOrNull(); + mainField = new Main(); + try { + interfaceTypeTestMainOrNull(); + throw new Error("ClassCastException expected"); + } catch (ClassCastException e) {} + mainField = new SubMain(); + interfaceTypeTestMainOrNull(); + + unrelatedField = null; + interfaceTypeTestUnrelated(); + unrelatedField = new Unrelated(); + try { + interfaceTypeTestUnrelated(); + throw new Error("ClassCastException expected"); + } catch (ClassCastException e) {} + + finalUnrelatedField = null; + interfaceTypeTestFinalUnrelated(); + finalUnrelatedField = new FinalUnrelated(); + try { + interfaceTypeTestFinalUnrelated(); + throw new Error("ClassCastException expected"); + } catch (ClassCastException e) {} + } +} + +interface Itf { +} + +class SubMain extends Main implements Itf { +} + +class Unrelated { +} + +final class FinalUnrelated { +} diff --git a/test/496-checker-inlining-and-class-loader/expected.txt b/test/496-checker-inlining-and-class-loader/expected.txt new file mode 100644 index 0000000000..c6fcb51ecf --- /dev/null +++ b/test/496-checker-inlining-and-class-loader/expected.txt @@ -0,0 +1,4 @@ +Request for LoadedByMyClassLoader +Request for Main +In between the two calls. +In $noinline$bar diff --git a/test/496-checker-inlining-and-class-loader/info.txt b/test/496-checker-inlining-and-class-loader/info.txt new file mode 100644 index 0000000000..aa4b256207 --- /dev/null +++ b/test/496-checker-inlining-and-class-loader/info.txt @@ -0,0 +1,2 @@ +Regression test to ensure compilers preserve JLS +semantics of class loading. diff --git a/test/496-checker-inlining-and-class-loader/src/Main.java b/test/496-checker-inlining-and-class-loader/src/Main.java new file mode 100644 index 0000000000..f6d0b41a58 --- /dev/null +++ b/test/496-checker-inlining-and-class-loader/src/Main.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2015 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.Field; +import java.lang.reflect.Method; +import java.util.List; + +class MyClassLoader extends ClassLoader { + MyClassLoader() throws Exception { + super(MyClassLoader.class.getClassLoader()); + + // Some magic to get access to the pathList field of BaseDexClassLoader. + ClassLoader loader = getClass().getClassLoader(); + Class<?> baseDexClassLoader = loader.getClass().getSuperclass(); + Field f = baseDexClassLoader.getDeclaredField("pathList"); + f.setAccessible(true); + Object pathList = f.get(loader); + + // Some magic to get access to the dexField field of pathList. + f = pathList.getClass().getDeclaredField("dexElements"); + f.setAccessible(true); + dexElements = (Object[]) f.get(pathList); + dexFileField = dexElements[0].getClass().getDeclaredField("dexFile"); + dexFileField.setAccessible(true); + } + + Object[] dexElements; + Field dexFileField; + + protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { + System.out.println("Request for " + className); + + // We're only going to handle LoadedByMyClassLoader. + if (className != "LoadedByMyClassLoader") { + return getParent().loadClass(className); + } + + // Mimic what DexPathList.findClass is doing. + try { + for (Object element : dexElements) { + Object dex = dexFileField.get(element); + Method method = dex.getClass().getDeclaredMethod( + "loadClassBinaryName", String.class, ClassLoader.class, List.class); + + if (dex != null) { + Class clazz = (Class)method.invoke(dex, className, this, null); + if (clazz != null) { + return clazz; + } + } + } + } catch (Exception e) { /* Ignore */ } + return null; + } +} + +class LoadedByMyClassLoader { + /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (before) + /// CHECK: LoadClass + /// CHECK-NEXT: ClinitCheck + /// CHECK-NEXT: InvokeStaticOrDirect + /// CHECK-NEXT: LoadClass + /// CHECK-NEXT: ClinitCheck + /// CHECK-NEXT: StaticFieldGet + /// CHECK-NEXT: LoadString + /// CHECK-NEXT: NullCheck + /// CHECK-NEXT: InvokeVirtual + + /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (after) + /// CHECK: LoadClass + /// CHECK-NEXT: ClinitCheck + /* We inlined Main.$inline$bar */ + /// CHECK-NEXT: LoadClass + /// CHECK-NEXT: ClinitCheck + /// CHECK-NEXT: StaticFieldGet + /// CHECK-NEXT: LoadString + /// CHECK-NEXT: NullCheck + /// CHECK-NEXT: InvokeVirtual + + /// CHECK-START: void LoadedByMyClassLoader.bar() register (before) + /* Load and initialize Main */ + /// CHECK: LoadClass gen_clinit_check:true + /* Load and initialize System */ + /// CHECK-NEXT: LoadClass gen_clinit_check:true + /// CHECK-NEXT: StaticFieldGet + /// CHECK-NEXT: LoadString + /// CHECK-NEXT: NullCheck + /// CHECK-NEXT: InvokeVirtual + public static void bar() { + Main.$inline$bar(); + System.out.println("In between the two calls."); + Main.$noinline$bar(); + } +} + +class Main { + public static void main(String[] args) throws Exception { + MyClassLoader o = new MyClassLoader(); + Class foo = o.loadClass("LoadedByMyClassLoader"); + Method m = foo.getDeclaredMethod("bar"); + m.invoke(null); + } + + public static void $inline$bar() { + } + + public static void $noinline$bar() { + try { + System.out.println("In $noinline$bar"); + } catch (Throwable t) { /* Ignore */ } + } +} diff --git a/test/497-inlining-and-class-loader/clear_dex_cache.cc b/test/497-inlining-and-class-loader/clear_dex_cache.cc new file mode 100644 index 0000000000..f9b33a2874 --- /dev/null +++ b/test/497-inlining-and-class-loader/clear_dex_cache.cc @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 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. + */ + +#include "art_method-inl.h" +#include "jni.h" +#include "scoped_thread_state_change.h" +#include "stack.h" +#include "thread.h" + +namespace art { + +namespace { + +extern "C" JNIEXPORT jobject JNICALL Java_Main_cloneResolvedMethods(JNIEnv*, jclass, jclass cls) { + ScopedObjectAccess soa(Thread::Current()); + return soa.Vm()->AddGlobalRef( + soa.Self(), + soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetResolvedMethods()->Clone(soa.Self())); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_restoreResolvedMethods( + JNIEnv*, jclass, jclass cls, jobject old_cache) { + ScopedObjectAccess soa(Thread::Current()); + mirror::PointerArray* now = soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetResolvedMethods(); + mirror::PointerArray* old = soa.Decode<mirror::PointerArray*>(old_cache); + for (size_t i = 0, e = old->GetLength(); i < e; ++i) { + now->SetElementPtrSize(i, old->GetElementPtrSize<void*>(i, sizeof(void*)), sizeof(void*)); + } +} + +} // namespace + +} // namespace art diff --git a/test/497-inlining-and-class-loader/expected.txt b/test/497-inlining-and-class-loader/expected.txt new file mode 100644 index 0000000000..3e1d85e309 --- /dev/null +++ b/test/497-inlining-and-class-loader/expected.txt @@ -0,0 +1,7 @@ +java.lang.Exception + at Main.$noinline$bar(Main.java:127) + at Level2.$inline$bar(Level1.java:25) + at Level1.$inline$bar(Level1.java:19) + at LoadedByMyClassLoader.bar(Main.java:82) + at java.lang.reflect.Method.invoke(Native Method) + at Main.main(Main.java:101) diff --git a/test/497-inlining-and-class-loader/info.txt b/test/497-inlining-and-class-loader/info.txt new file mode 100644 index 0000000000..e7f02aaf34 --- /dev/null +++ b/test/497-inlining-and-class-loader/info.txt @@ -0,0 +1,2 @@ +Regression test for optimizing to ensure it is using +the correct class loader when walking inlined frames. diff --git a/test/497-inlining-and-class-loader/src/Level1.java b/test/497-inlining-and-class-loader/src/Level1.java new file mode 100644 index 0000000000..977af8321e --- /dev/null +++ b/test/497-inlining-and-class-loader/src/Level1.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 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 Level1 { + public static void $inline$bar() { + Level2.$inline$bar(); + } +} + +class Level2 { + public static void $inline$bar() { + Main.$noinline$bar(); + } +} diff --git a/test/497-inlining-and-class-loader/src/Main.java b/test/497-inlining-and-class-loader/src/Main.java new file mode 100644 index 0000000000..0f7eb599cb --- /dev/null +++ b/test/497-inlining-and-class-loader/src/Main.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2015 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.Field; +import java.lang.reflect.Method; +import java.util.List; + +class MyClassLoader extends ClassLoader { + MyClassLoader() throws Exception { + super(MyClassLoader.class.getClassLoader()); + + // Some magic to get access to the pathList field of BaseDexClassLoader. + ClassLoader loader = getClass().getClassLoader(); + Class<?> baseDexClassLoader = loader.getClass().getSuperclass(); + Field f = baseDexClassLoader.getDeclaredField("pathList"); + f.setAccessible(true); + Object pathList = f.get(loader); + + // Some magic to get access to the dexField field of pathList. + f = pathList.getClass().getDeclaredField("dexElements"); + f.setAccessible(true); + dexElements = (Object[]) f.get(pathList); + dexFileField = dexElements[0].getClass().getDeclaredField("dexFile"); + dexFileField.setAccessible(true); + } + + Object[] dexElements; + Field dexFileField; + + static ClassLoader level1ClassLoader; + + protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { + if (this != level1ClassLoader) { + if (className.equals("Level1")) { + return level1ClassLoader.loadClass(className); + } else if (className.equals("Level2")) { + throw new ClassNotFoundException("None of my methods require Level2!"); + } else if (!className.equals("LoadedByMyClassLoader")) { + // We're only going to handle LoadedByMyClassLoader. + return getParent().loadClass(className); + } + } else { + if (className != "Level1" && className != "Level2") { + return getParent().loadClass(className); + } + } + + // Mimic what DexPathList.findClass is doing. + try { + for (Object element : dexElements) { + Object dex = dexFileField.get(element); + Method method = dex.getClass().getDeclaredMethod( + "loadClassBinaryName", String.class, ClassLoader.class, List.class); + + if (dex != null) { + Class clazz = (Class)method.invoke(dex, className, this, null); + if (clazz != null) { + return clazz; + } + } + } + } catch (Exception e) { /* Ignore */ } + return null; + } +} + +class LoadedByMyClassLoader { + public static void bar() { + Level1.$inline$bar(); + } +} + +class Main { + static { + System.loadLibrary("arttest"); + } + + public static void main(String[] args) throws Exception { + // Clone resolved methods, to restore the original version just + // before we walk the stack in $noinline$bar. + savedResolvedMethods = cloneResolvedMethods(Main.class); + + MyClassLoader o = new MyClassLoader(); + MyClassLoader.level1ClassLoader = new MyClassLoader(); + Class foo = o.loadClass("LoadedByMyClassLoader"); + Method m = foo.getDeclaredMethod("bar"); + try { + m.invoke(null); + } catch (Error e) { /* Ignore */ } + } + + public static void $inline$bar() { + } + + public static void $noinline$bar() { + try { + // Be evil and clear all dex cache entries. + Field f = Class.class.getDeclaredField("dexCache"); + f.setAccessible(true); + Object dexCache = f.get(Main.class); + f = dexCache.getClass().getDeclaredField("resolvedTypes"); + f.setAccessible(true); + Object[] array = (Object[]) f.get(dexCache); + for (int i = 0; i < array.length; i++) { + array[i] = null; + } + restoreResolvedMethods(Main.class, savedResolvedMethods); + } catch (Throwable t) { /* Ignore */ } + + // This will walk the stack, trying to resolve methods in it. + // Because we cleared dex cache entries, we will have to find + // classes again, which require to use the correct class loader + // in the presence of inlining. + new Exception().printStackTrace(); + } + static Object savedResolvedMethods; + + static native Object cloneResolvedMethods(Class<?> cls); + static native void restoreResolvedMethods(Class<?> cls, Object saved); +} diff --git a/test/498-type-propagation/expected.txt b/test/498-type-propagation/expected.txt new file mode 100644 index 0000000000..ccaf6f8f0f --- /dev/null +++ b/test/498-type-propagation/expected.txt @@ -0,0 +1 @@ +Enter diff --git a/test/498-type-propagation/info.txt b/test/498-type-propagation/info.txt new file mode 100644 index 0000000000..b895e91f9d --- /dev/null +++ b/test/498-type-propagation/info.txt @@ -0,0 +1,2 @@ +Regression test for the SSA building of the optimizing +compiler. See comment in smali file. diff --git a/test/498-type-propagation/smali/TypePropagation.smali b/test/498-type-propagation/smali/TypePropagation.smali new file mode 100644 index 0000000000..088ca89985 --- /dev/null +++ b/test/498-type-propagation/smali/TypePropagation.smali @@ -0,0 +1,30 @@ +# Copyright (C) 2015 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. + +.class public LTypePropagation; + +.super Ljava/lang/Object; + +.method public static method([I)V + .registers 2 + const/4 v0, 0 + # When building the SSA graph, we will create a phi for v0, which will be of type + # integer. Only when we get rid of that phi in the redundant phi elimination will + # we realize it's just null. + :start + if-eq v1, v0, :end + if-eq v1, v0, :start + :end + return-void +.end method diff --git a/test/498-type-propagation/src/Main.java b/test/498-type-propagation/src/Main.java new file mode 100644 index 0000000000..7a14172bf5 --- /dev/null +++ b/test/498-type-propagation/src/Main.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 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; + +public class Main { + public static void main(String[] args) throws Exception { + // Workaround for b/18051191. + System.out.println("Enter"); + Class<?> c = Class.forName("TypePropagation"); + Method m = c.getMethod("method", int[].class); + int[] array = new int[7]; + Object[] arguments = { array }; + m.invoke(null, arguments); + } +} diff --git a/test/499-bce-phi-array-length/expected.txt b/test/499-bce-phi-array-length/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/499-bce-phi-array-length/expected.txt diff --git a/test/499-bce-phi-array-length/info.txt b/test/499-bce-phi-array-length/info.txt new file mode 100644 index 0000000000..ee52699579 --- /dev/null +++ b/test/499-bce-phi-array-length/info.txt @@ -0,0 +1,2 @@ +Regression test for BCE phase of optimizing. See Main.java +for a description of the bug. diff --git a/test/499-bce-phi-array-length/src/Main.java b/test/499-bce-phi-array-length/src/Main.java new file mode 100644 index 0000000000..c8c84a1a8f --- /dev/null +++ b/test/499-bce-phi-array-length/src/Main.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 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 int foo(int start, int[] array) { + int result = 0; + // We will create HDeoptimize nodes for this first loop, and a phi + // for the array length which will only be used within the loop. + for (int i = start; i < 3; i++) { + result += array[i]; + for (int j = 0; j < 2; ++j) { + // The HBoundsCheck for this array access will be updated to access + // the array length phi created for the deoptimization checks of the + // first loop. This crashed the compiler which used to DCHECK an array + // length in a bounds check cannot be a phi. + result += array[j]; + } + } + return result; + } + + public static void main(String[] args) { + int[] a = new int[] { 1, 2, 3, 4, 5 }; + int result = foo(1, a); + if (result != 11) { + throw new Error("Got " + result + ", expected " + 11); + } + } +} diff --git a/test/500-instanceof/expected.txt b/test/500-instanceof/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/500-instanceof/expected.txt diff --git a/test/500-instanceof/info.txt b/test/500-instanceof/info.txt new file mode 100644 index 0000000000..b28756aa34 --- /dev/null +++ b/test/500-instanceof/info.txt @@ -0,0 +1,2 @@ +Regression test for optimizing which used to be +too aggresive in removing instanceof checks. diff --git a/test/500-instanceof/src/Main.java b/test/500-instanceof/src/Main.java new file mode 100644 index 0000000000..80fdb0288d --- /dev/null +++ b/test/500-instanceof/src/Main.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015 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. + */ + +interface Itf { +} + +class Foo { +} + +class Main extends Foo implements Itf { + public static void main(String[] args) { + Itf parent = getParent(); + if (!(parent instanceof Foo)) { + throw new Error("Instanceof should have succeeded"); + } + } + + static Itf getParent() { return new Main(); } +} diff --git a/test/501-null-constant-dce/expected.txt b/test/501-null-constant-dce/expected.txt new file mode 100644 index 0000000000..ccaf6f8f0f --- /dev/null +++ b/test/501-null-constant-dce/expected.txt @@ -0,0 +1 @@ +Enter diff --git a/test/501-null-constant-dce/info.txt b/test/501-null-constant-dce/info.txt new file mode 100644 index 0000000000..2c4a686b5f --- /dev/null +++ b/test/501-null-constant-dce/info.txt @@ -0,0 +1 @@ +Regression test for the optimizing compiler. See comment in smali file. diff --git a/test/501-null-constant-dce/smali/DCE.smali b/test/501-null-constant-dce/smali/DCE.smali new file mode 100644 index 0000000000..4a1765e796 --- /dev/null +++ b/test/501-null-constant-dce/smali/DCE.smali @@ -0,0 +1,37 @@ +# Copyright (C) 2015 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. + +.class public LDCE; + +.super Ljava/lang/Object; + +.method public static method([I)LDCE; + .registers 2 + const/4 v0, 0 + # Jump over the code that requires the null constant + # so that the compiler sees the null constant as dead code. + if-eq v0, v0, :end + invoke-static {v0}, LDCE;->method([I)LDCE; + :end + invoke-static {}, LDCE;->$inline$returnNull()LDCE; + move-result-object v0 + return-object v0 +.end method + +.method public static $inline$returnNull()LDCE; + .registers 2 + const/4 v0, 0 + # Return null to make `method` call GetConstantNull again. + return-object v0 +.end method diff --git a/test/501-null-constant-dce/src/Main.java b/test/501-null-constant-dce/src/Main.java new file mode 100644 index 0000000000..3a2d491b15 --- /dev/null +++ b/test/501-null-constant-dce/src/Main.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015 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; + +public class Main { + public static void main(String[] args) throws Exception { + // Workaround for b/18051191. + System.out.println("Enter"); + Class<?> c = Class.forName("DCE"); + Method m = c.getMethod("method", int[].class); + int[] array = new int[7]; + Object[] arguments = { array }; + Object result = m.invoke(null, arguments); + if (result != null) { + throw new Error("Expected null, got " + result); + } + } +} diff --git a/test/501-regression-packed-switch/expected.txt b/test/501-regression-packed-switch/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/501-regression-packed-switch/expected.txt diff --git a/test/501-regression-packed-switch/info.txt b/test/501-regression-packed-switch/info.txt new file mode 100644 index 0000000000..fbd93fa815 --- /dev/null +++ b/test/501-regression-packed-switch/info.txt @@ -0,0 +1,2 @@ +Regression test for the interpreter and optimizing's builder which used +to trip when compiled code contained a packed switch with no targets. diff --git a/test/501-regression-packed-switch/smali/Test.smali b/test/501-regression-packed-switch/smali/Test.smali new file mode 100644 index 0000000000..8756ed5f23 --- /dev/null +++ b/test/501-regression-packed-switch/smali/Test.smali @@ -0,0 +1,29 @@ +# +# Copyright (C) 2015 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. + +.class public LTest; + +.super Ljava/lang/Object; + +.method public static EmptyPackedSwitch(I)I + .registers 1 + packed-switch v0, :pswitch_data_6a + const/4 v0, 0x5 + return v0 + + :pswitch_data_6a + .packed-switch 0x0 + .end packed-switch +.end method diff --git a/test/501-regression-packed-switch/src/Main.java b/test/501-regression-packed-switch/src/Main.java new file mode 100644 index 0000000000..b80bc62c50 --- /dev/null +++ b/test/501-regression-packed-switch/src/Main.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 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.lang.reflect.Type; + +public class Main { + + // Workaround for b/18051191. + class InnerClass {} + + public static void main(String args[]) throws Exception { + Class<?> c = Class.forName("Test"); + Method m = c.getMethod("EmptyPackedSwitch", new Class[] { int.class }); + Integer result = (Integer) m.invoke(null, new Integer(42)); + if (result != 5) { + throw new Error("Expected 5, got " + result); + } + } +} diff --git a/test/503-dead-instructions/expected.txt b/test/503-dead-instructions/expected.txt new file mode 100644 index 0000000000..ccaf6f8f0f --- /dev/null +++ b/test/503-dead-instructions/expected.txt @@ -0,0 +1 @@ +Enter diff --git a/test/503-dead-instructions/info.txt b/test/503-dead-instructions/info.txt new file mode 100644 index 0000000000..7e3f1aba34 --- /dev/null +++ b/test/503-dead-instructions/info.txt @@ -0,0 +1,2 @@ +Regression test for the building phase of the optimizing +compiler. See comment in smali file. diff --git a/test/503-dead-instructions/smali/DeadInstructions.smali b/test/503-dead-instructions/smali/DeadInstructions.smali new file mode 100644 index 0000000000..9f6c5653fa --- /dev/null +++ b/test/503-dead-instructions/smali/DeadInstructions.smali @@ -0,0 +1,63 @@ +# Copyright (C) 2015 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. + +.class public LDeadInstructions; + +.super Ljava/lang/Object; + +.method public static method1()V + .registers 2 + return-void + # Create a label and a branch to that label to trick the + # optimizing compiler into thinking the invoke is live. + :start + const/4 v0, 0 + const/4 v1, 0 + # Provide more arguments than we should. Because this is dead + # code, the verifier will not check the argument count. So + # the compilers must do the same. + invoke-static {v0, v1}, LDeadInstructions;->method1()V + goto :start +.end method + +.method public static method2(J)V + .registers 3 + return-void + :start + const/4 v0, 0 + const/4 v1, 0 + const/4 v2, 0 + # Give a non-sequential pair for the long argument. + invoke-static {v0, v2}, LDeadInstructions;->method2(J)V + goto :start +.end method + +.method public static method3()V + .registers 1 + return-void + :start + const/4 v0, 0 + # Give one half of a pair. + invoke-static {v0}, LDeadInstructions;->method2(J)V + goto :start +.end method + +.method public static method4()V + .registers 2 + return-void + :start + # Provide less arguments than we should. + invoke-static {}, LDeadInstructions;->method3(J)V + goto :start +.end method diff --git a/test/503-dead-instructions/src/Main.java b/test/503-dead-instructions/src/Main.java new file mode 100644 index 0000000000..6249dc79fa --- /dev/null +++ b/test/503-dead-instructions/src/Main.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 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; + +public class Main { + public static void main(String[] args) throws Exception { + // Workaround for b/18051191. + System.out.println("Enter"); + Class<?> c = Class.forName("DeadInstructions"); + Method m = c.getMethod("method1"); + Object[] arguments1 = { }; + m.invoke(null, arguments1); + + Object[] arguments2 = { (long)4 }; + m = c.getMethod("method2", long.class); + m.invoke(null, arguments2); + + Object[] arguments3 = { }; + m = c.getMethod("method3"); + m.invoke(null, arguments3); + + Object[] arguments4 = { }; + m = c.getMethod("method4"); + m.invoke(null, arguments4); + } +} diff --git a/test/504-regression-baseline-entry/expected.txt b/test/504-regression-baseline-entry/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/504-regression-baseline-entry/expected.txt diff --git a/test/504-regression-baseline-entry/info.txt b/test/504-regression-baseline-entry/info.txt new file mode 100644 index 0000000000..26cc9ce75b --- /dev/null +++ b/test/504-regression-baseline-entry/info.txt @@ -0,0 +1,2 @@ +Regression test for the baseline compiler which required the entry block to fall +through to the next block.
\ No newline at end of file diff --git a/test/504-regression-baseline-entry/smali/Test.smali b/test/504-regression-baseline-entry/smali/Test.smali new file mode 100644 index 0000000000..06412e7618 --- /dev/null +++ b/test/504-regression-baseline-entry/smali/Test.smali @@ -0,0 +1,30 @@ +# +# Copyright (C) 2015 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. + +.class public LTest; + +.super Ljava/lang/Object; + +.method public static SingleGotoStart()I + .registers 1 + goto :second + + :first + return v0 + + :second + const/4 v0, 0x5 + goto :first +.end method diff --git a/test/504-regression-baseline-entry/src/Main.java b/test/504-regression-baseline-entry/src/Main.java new file mode 100644 index 0000000000..2c9df28342 --- /dev/null +++ b/test/504-regression-baseline-entry/src/Main.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 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.lang.reflect.Type; + +public class Main { + + // Workaround for b/18051191. + class InnerClass {} + + public static void main(String args[]) throws Exception { + Class<?> c = Class.forName("Test"); + Method m = c.getMethod("SingleGotoStart", (Class[]) null); + Integer result = (Integer) m.invoke(null); + if (result != 5) { + throw new Error("Expected 5, got " + result); + } + } +} diff --git a/test/505-simplifier-type-propagation/expected.txt b/test/505-simplifier-type-propagation/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/505-simplifier-type-propagation/expected.txt diff --git a/test/505-simplifier-type-propagation/info.txt b/test/505-simplifier-type-propagation/info.txt new file mode 100644 index 0000000000..cd84432ff1 --- /dev/null +++ b/test/505-simplifier-type-propagation/info.txt @@ -0,0 +1,3 @@ +Regression test for the optimizing compiler, where +the code generators did not expect type conversion +instructions from one type to the same type. diff --git a/test/505-simplifier-type-propagation/src/Main.java b/test/505-simplifier-type-propagation/src/Main.java new file mode 100644 index 0000000000..780cb3469e --- /dev/null +++ b/test/505-simplifier-type-propagation/src/Main.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 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. + */ + +class Main { + public static void main(String[] args) { + byte result = bar((byte)2); + if (result != 2) { + throw new Error("Expected 2, got " + result); + } + } + + public static byte bar(byte myByte) { + int a = 0; + // The following call will be inlined, which will make + // the type conversion below from byte to byte. + if ($inline$foo()) { + a = myByte; + } + return (byte)a; + } + + public static boolean $inline$foo() { + return true; + } +} diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt index a6b216bf3a..3c6506b6b4 100644 --- a/test/800-smali/expected.txt +++ b/test/800-smali/expected.txt @@ -16,4 +16,7 @@ MoveExc MoveExceptionOnEntry EmptySparseSwitch b/20224106 +b/17410612 +b/21865464 +b/21873167 Done! diff --git a/test/800-smali/smali/b_17410612.smali b/test/800-smali/smali/b_17410612.smali new file mode 100644 index 0000000000..17718cbf60 --- /dev/null +++ b/test/800-smali/smali/b_17410612.smali @@ -0,0 +1,14 @@ +.class public LB17410612; + +# Test that an invoke with a long parameter has the long parameter in +# a pair. This should fail in the verifier and not an abort in the compiler. + +.super Ljava/lang/Object; + +.method public static run()V + .registers 4 + const-wide v0, 0 # Make (v0, v1) a long + const-wide v2, 0 # Make (v2, v3) a long + invoke-static {v0, v3}, Ljava/lang/Long;->valueOf(J)Ljava/lang/Long; + return-void +.end method diff --git a/test/800-smali/smali/b_21865464.smali b/test/800-smali/smali/b_21865464.smali new file mode 100644 index 0000000000..df56a54aaa --- /dev/null +++ b/test/800-smali/smali/b_21865464.smali @@ -0,0 +1,29 @@ +# Copyright (C) 2015 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. + +.class public LB21865464; + +.super Ljava/lang/Object; + +.method public static run()V + .registers 2 + return-void + goto :start + :start + # The following is dead code but used to crash the compiler. + const/4 v0, 0 + return-wide v0 + return v0 + return-object v0 +.end method diff --git a/test/800-smali/smali/b_21873167.smali b/test/800-smali/smali/b_21873167.smali new file mode 100644 index 0000000000..c0c09cbbf2 --- /dev/null +++ b/test/800-smali/smali/b_21873167.smali @@ -0,0 +1,18 @@ +.class public LB21873167; +.super Ljava/lang/Object; + +.method public constructor <init>()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + return-void +.end method + +.method public test()V + .registers 1 + :start + monitor-enter p0 + monitor-exit p0 + :end + return-void + .catchall {:start .. :end} :end +.end method diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java index 3e88364089..d1c275cfef 100644 --- a/test/800-smali/src/Main.java +++ b/test/800-smali/src/Main.java @@ -81,6 +81,11 @@ public class Main { null)); testCases.add(new TestCase("b/20224106", "B20224106", "run", null, new VerifyError(), 0)); + testCases.add(new TestCase("b/17410612", "B17410612", "run", null, new VerifyError(), + 0)); + testCases.add(new TestCase("b/21865464", "B21865464", "run", null, null, + null)); + testCases.add(new TestCase("b/21873167", "B21873167", "test", null, null, null)); } public void runTests() { diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk index 847ad0d2f7..fcb9f8a779 100644 --- a/test/Android.libarttest.mk +++ b/test/Android.libarttest.mk @@ -34,7 +34,8 @@ LIBARTTEST_COMMON_SRC_FILES := \ 455-set-vreg/set_vreg_jni.cc \ 457-regs/regs_jni.cc \ 461-get-reference-vreg/get_reference_vreg_jni.cc \ - 466-get-live-vreg/get_live_vreg_jni.cc + 466-get-live-vreg/get_live_vreg_jni.cc \ + 497-inlining-and-class-loader/clear_dex_cache.cc ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so ifdef TARGET_2ND_ARCH diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index e95f147e65..c2380cc668 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -229,16 +229,10 @@ endif TEST_ART_BROKEN_NO_RELOCATE_TESTS := -# Tests that are broken with GC stress. -TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := - -ifneq (,$(filter gcstress,$(GC_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),gcstress,$(JNI_TYPES), \ - $(IMAGE_TYPES), $(PICTEST_TYPES), $(DBEUGGABLE_TYPES), $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(ALL_ADDRESS_SIZES)) -endif - -TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := +# 098-ddmc is broken until we restore the old behavior of getRecentAllocation() of DDMS. b/20037135 +ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ + $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ + $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), 098-ddmc, $(ALL_ADDRESS_SIZES)) # 115-native-bridge setup is complicated. Need to implement it correctly for the target. ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES),$(COMPILER_TYPES), \ @@ -344,6 +338,7 @@ TEST_ART_BROKEN_NDEBUG_TESTS := \ 457-regs \ 461-get-reference-vreg \ 466-get-live-vreg \ + 497-inlining-and-class-loader \ ifneq (,$(filter ndebug,$(RUN_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),ndebug,$(PREBUILD_TYPES), \ diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 240ed41ff2..09841bfcec 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -296,6 +296,10 @@ if [ "$RELOCATE" = "y" ]; then else FLAGS="$FLAGS -Xnorelocate" COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xnorelocate" + if [ "$HOST" = "y" ]; then + # Increase ulimit to 64MB in case we are running hprof test. + ulimit -S 64000 || exit 1 + fi fi if [ "$HOST" = "n" ]; then diff --git a/test/run-test b/test/run-test index ed3309923b..ed033217b8 100755 --- a/test/run-test +++ b/test/run-test @@ -96,6 +96,7 @@ basic_verify="false" gc_verify="false" gc_stress="false" always_clean="no" +never_clean="no" have_dex2oat="yes" have_patchoat="yes" have_image="yes" @@ -270,6 +271,9 @@ while true; do elif [ "x$1" = "x--always-clean" ]; then always_clean="yes" shift + elif [ "x$1" = "x--never-clean" ]; then + never_clean="yes" + shift elif [ "x$1" = "x--dex2oat-swap" ]; then run_args="${run_args} --dex2oat-swap" shift @@ -472,6 +476,7 @@ if [ "$usage" = "yes" ]; then echo " --gcstress Run with gc stress testing" echo " --gcverify Run with gc verification" echo " --always-clean Delete the test files even if the test fails." + echo " --never-clean Keep the test files even if the test succeeds." echo " --android-root [path] The path on target for the android root. (/system by default)." echo " --dex2oat-swap Use a dex2oat swap file." ) 1>&2 @@ -668,7 +673,7 @@ fi ) 1>&2 # Clean up test files. -if [ "$always_clean" = "yes" -o "$good" = "yes" ]; then +if [ "$always_clean" = "yes" -o "$good" = "yes" ] && [ "$never_clean" = "no" ]; then cd "$oldwd" rm -rf "$tmp_dir" if [ "$target_mode" = "yes" -a "$build_exit" = "0" ]; then |