diff options
Diffstat (limited to 'test')
31 files changed, 1243 insertions, 119 deletions
diff --git a/test/412-new-array/info.txt b/test/412-new-array/info.txt index cb388b6023..b5f834aa31 100644 --- a/test/412-new-array/info.txt +++ b/test/412-new-array/info.txt @@ -1 +1,3 @@ Simple tests for new-array, filled-new-array and fill-array-data. +Regression test for the arm64 mterp miscalculating the fill-array-data-payload +address, zero-extending a register instead of sign-extending. diff --git a/test/412-new-array/smali/fill_array_data.smali b/test/412-new-array/smali/fill_array_data.smali index 34776db5de..2b24e56cef 100644 --- a/test/412-new-array/smali/fill_array_data.smali +++ b/test/412-new-array/smali/fill_array_data.smali @@ -15,6 +15,21 @@ .end method +.method public static intArrayFillInstructionAfterData([I)V + .registers 1 + goto :FillInstruction + +:ArrayData + .array-data 4 + 1 2 3 4 5 + .end array-data + +:FillInstruction + fill-array-data v0, :ArrayData + return-void + +.end method + .method public static shortArray([S)V .registers 1 diff --git a/test/412-new-array/src/Main.java b/test/412-new-array/src/Main.java index b9c2a053e0..d95d2c52f3 100644 --- a/test/412-new-array/src/Main.java +++ b/test/412-new-array/src/Main.java @@ -259,6 +259,45 @@ public class Main extends TestCase { } { + Method m = c.getMethod("intArrayFillInstructionAfterData", int[].class); + int[] array = new int[7]; + Object[] args = { array }; + m.invoke(null, args); + assertEquals(7, array.length); + assertEquals(1, array[0]); + assertEquals(2, array[1]); + assertEquals(3, array[2]); + assertEquals(4, array[3]); + assertEquals(5, array[4]); + assertEquals(0, array[5]); + assertEquals(0, array[6]); + + array = new int[2]; + args[0] = array; + Throwable exception = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof IndexOutOfBoundsException); + } + assertNotNull(exception); + exception = null; + // Test that nothing has been written to the array. + assertEquals(0, array[0]); + assertEquals(0, array[1]); + + args[0] = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof NullPointerException); + } + assertNotNull(exception); + } + + { Method m = c.getMethod("shortArray", short[].class); short[] array = new short[7]; Object[] args = { array }; diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java index 41771b52c4..c125e337cb 100644 --- a/test/449-checker-bce/src/Main.java +++ b/test/449-checker-bce/src/Main.java @@ -1204,9 +1204,6 @@ public class Main { /// CHECK: Deoptimize /// CHECK: Deoptimize /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize /// CHECK-NOT: Deoptimize /// CHECK: Goto /// CHECK: Goto @@ -1217,7 +1214,7 @@ public class Main { for (int i = array.length - 1 ; i >= 0; i--) { array[i] = 1; } - // Several HDeoptimize will be added. Two for each index. + // Three HDeoptimize will be added for the bounds. // The null check is not necessary. for (int i = end - 2 ; i > 0; i--) { if (expectInterpreter) { @@ -1266,20 +1263,12 @@ public class Main { /// CHECK: Deoptimize /// CHECK: Deoptimize /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize /// CHECK-NOT: Deoptimize /// CHECK: Goto /// CHECK: Goto /// CHECK: Goto void foo6(int[] array, int start, int end, boolean expectInterpreter) { - // Several HDeoptimize will be added. for (int i = end; i >= start; i--) { if (expectInterpreter) { assertIsInterpreted(); @@ -1398,8 +1387,8 @@ public class Main { /// CHECK-NOT: Deoptimize void foo9(int[] array, boolean expectInterpreter) { - // Two HDeoptimize will be added. Two for the index - // and one for null check on array. + // Three HDeoptimize will be added. Two for the index and one for null check on array. Then + // simplification removes one redundant HDeoptimize. for (int i = 0 ; i < 10; i++) { if (expectInterpreter) { assertIsInterpreted(); diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java index 359d521ffc..040479e8c5 100644 --- a/test/458-checker-instruction-simplification/src/Main.java +++ b/test/458-checker-instruction-simplification/src/Main.java @@ -1142,7 +1142,13 @@ public class Main { public static boolean $noinline$EqualBoolVsIntConst(boolean arg) { if (doThrow) { throw new Error(); } - return (arg ? 0 : 1) != 2; + // Make calls that will be inlined to make sure the instruction simplifier + // sees the simplification (dead code elimination will also try to simplify it). + return (arg ? $inline$ReturnArg(0) : $inline$ReturnArg(1)) != 2; + } + + public static int $inline$ReturnArg(int arg) { + return arg; } /// CHECK-START: boolean Main.$noinline$NotEqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before) @@ -1161,7 +1167,9 @@ public class Main { public static boolean $noinline$NotEqualBoolVsIntConst(boolean arg) { if (doThrow) { throw new Error(); } - return (arg ? 0 : 1) == 2; + // Make calls that will be inlined to make sure the instruction simplifier + // sees the simplification (dead code elimination will also try to simplify it). + return (arg ? $inline$ReturnArg(0) : $inline$ReturnArg(1)) == 2; } /* diff --git a/test/501-regression-packed-switch/info.txt b/test/501-regression-packed-switch/info.txt index fbd93fa815..988b220a87 100644 --- a/test/501-regression-packed-switch/info.txt +++ b/test/501-regression-packed-switch/info.txt @@ -1,2 +1,4 @@ Regression test for the interpreter and optimizing's builder which used to trip when compiled code contained a packed switch with no targets. +Regression test for the arm64 mterp miscalculating the switch table +address, zero-extending a register instead of sign-extending. diff --git a/test/501-regression-packed-switch/smali/Test.smali b/test/501-regression-packed-switch/smali/Test.smali index 8756ed5f23..5a760c7880 100644 --- a/test/501-regression-packed-switch/smali/Test.smali +++ b/test/501-regression-packed-switch/smali/Test.smali @@ -27,3 +27,28 @@ .packed-switch 0x0 .end packed-switch .end method + +.method public static PackedSwitchAfterData(I)I + .registers 1 + goto :pswitch_instr + + :case0 + const/4 v0, 0x1 + return v0 + + :pswitch_data + .packed-switch 0x0 + :case0 + :case1 + .end packed-switch + + :pswitch_instr + packed-switch v0, :pswitch_data + const/4 v0, 0x7 + return v0 + + :case1 + const/4 v0, 0x4 + return v0 + +.end method diff --git a/test/501-regression-packed-switch/src/Main.java b/test/501-regression-packed-switch/src/Main.java index b80bc62c50..12bc1a8138 100644 --- a/test/501-regression-packed-switch/src/Main.java +++ b/test/501-regression-packed-switch/src/Main.java @@ -29,5 +29,10 @@ public class Main { if (result != 5) { throw new Error("Expected 5, got " + result); } + m = c.getMethod("PackedSwitchAfterData", new Class[] { int.class }); + result = (Integer) m.invoke(null, new Integer(0)); + if (result != 1) { + throw new Error("Expected 1, got " + result); + } } } diff --git a/test/527-checker-array-access-split/src/Main.java b/test/527-checker-array-access-split/src/Main.java index ead94464bf..3366f20cc5 100644 --- a/test/527-checker-array-access-split/src/Main.java +++ b/test/527-checker-array-access-split/src/Main.java @@ -34,9 +34,21 @@ public class Main { /// CHECK-START-ARM64: int Main.constantIndexGet(int[]) instruction_simplifier_arm64 (after) /// CHECK: <<Array:l\d+>> NullCheck /// CHECK: <<Index:i\d+>> BoundsCheck - /// CHECK-NOT: Arm64IntermediateAddress + /// CHECK-NOT: IntermediateAddress /// CHECK: ArrayGet [<<Array>>,<<Index>>] + + /// CHECK-START-ARM: int Main.constantIndexGet(int[]) instruction_simplifier_arm (before) + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: ArrayGet [<<Array>>,<<Index>>] + + /// CHECK-START-ARM: int Main.constantIndexGet(int[]) instruction_simplifier_arm (after) + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK-NOT: IntermediateAddress + /// CHECK: ArrayGet [<<Array>>,<<Index>>] + public static int constantIndexGet(int array[]) { return array[1]; } @@ -55,9 +67,22 @@ public class Main { /// CHECK: <<Const2:i\d+>> IntConstant 2 /// CHECK: <<Array:l\d+>> NullCheck /// CHECK: <<Index:i\d+>> BoundsCheck - /// CHECK-NOT: Arm64IntermediateAddress + /// CHECK-NOT: IntermediateAddress + /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Const2>>] + + + /// CHECK-START-ARM: void Main.constantIndexSet(int[]) instruction_simplifier_arm (before) + /// CHECK: <<Const2:i\d+>> IntConstant 2 + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Const2>>] + /// CHECK-START-ARM: void Main.constantIndexSet(int[]) instruction_simplifier_arm (after) + /// CHECK: <<Const2:i\d+>> IntConstant 2 + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK-NOT: IntermediateAddress + /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Const2>>] public static void constantIndexSet(int array[]) { array[1] = 2; @@ -76,7 +101,20 @@ public class Main { /// CHECK: <<DataOffset:i\d+>> IntConstant /// CHECK: <<Array:l\d+>> NullCheck /// CHECK: <<Index:i\d+>> BoundsCheck - /// CHECK: <<Address:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-NEXT: ArrayGet [<<Address>>,<<Index>>] + + + /// CHECK-START-ARM: int Main.get(int[], int) instruction_simplifier_arm (before) + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: ArrayGet [<<Array>>,<<Index>>] + + /// CHECK-START-ARM: int Main.get(int[], int) instruction_simplifier_arm (after) + /// CHECK: <<DataOffset:i\d+>> IntConstant + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: ArrayGet [<<Address>>,<<Index>>] public static int get(int array[], int index) { @@ -102,7 +140,26 @@ public class Main { /// CHECK: <<DataOffset:i\d+>> IntConstant /// CHECK: <<Array:l\d+>> NullCheck /// CHECK: <<Index:i\d+>> BoundsCheck - /// CHECK: <<Address:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-NEXT: ArraySet [<<Address>>,<<Index>>,<<Arg>>] + + + /// CHECK-START-ARM: void Main.set(int[], int, int) instruction_simplifier_arm (before) + /// CHECK: ParameterValue + /// CHECK: ParameterValue + /// CHECK: <<Arg:i\d+>> ParameterValue + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Arg>>] + + /// CHECK-START-ARM: void Main.set(int[], int, int) instruction_simplifier_arm (after) + /// CHECK: ParameterValue + /// CHECK: ParameterValue + /// CHECK: <<Arg:i\d+>> ParameterValue + /// CHECK: <<DataOffset:i\d+>> IntConstant + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: ArraySet [<<Address>>,<<Index>>,<<Arg>>] public static void set(int array[], int index, int value) { @@ -126,10 +183,10 @@ public class Main { /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant /// CHECK: <<Array:l\d+>> NullCheck /// CHECK: <<Index:i\d+>> BoundsCheck - /// CHECK: <<Address1:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] - /// CHECK: <<Address2:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: ArraySet [<<Address2>>,<<Index>>,<<Add>>] /// CHECK-START-ARM64: void Main.getSet(int[], int) GVN_after_arch (after) @@ -137,12 +194,42 @@ public class Main { /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant /// CHECK: <<Array:l\d+>> NullCheck /// CHECK: <<Index:i\d+>> BoundsCheck - /// CHECK: <<Address:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Address>>,<<Index>>] /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] - /// CHECK-NOT: Arm64IntermediateAddress + /// CHECK-NOT: IntermediateAddress /// CHECK: ArraySet [<<Address>>,<<Index>>,<<Add>>] + + /// CHECK-START-ARM: void Main.getSet(int[], int) instruction_simplifier_arm (before) + /// CHECK: <<Const1:i\d+>> IntConstant 1 + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Array>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Add>>] + + /// CHECK-START-ARM: void Main.getSet(int[], int) instruction_simplifier_arm (after) + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-NEXT: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-NEXT: ArraySet [<<Address2>>,<<Index>>,<<Add>>] + + /// CHECK-START-ARM: void Main.getSet(int[], int) GVN_after_arch (after) + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Address>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK-NOT: IntermediateAddress + /// CHECK: ArraySet [<<Address>>,<<Index>>,<<Add>>] public static void getSet(int array[], int index) { array[index] = array[index] + 1; } @@ -166,11 +253,11 @@ public class Main { /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant /// CHECK: <<Array:l\d+>> NullCheck /// CHECK: <<Index:i\d+>> BoundsCheck - /// CHECK: <<Address1:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] /// CHECK: NewArray - /// CHECK: <<Address2:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: ArraySet [<<Address2>>,<<Index>>,<<Add>>] /// CHECK-START-ARM64: int[] Main.accrossGC(int[], int) GVN_after_arch (after) @@ -178,11 +265,45 @@ public class Main { /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant /// CHECK: <<Array:l\d+>> NullCheck /// CHECK: <<Index:i\d+>> BoundsCheck - /// CHECK: <<Address1:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] /// CHECK: NewArray - /// CHECK: <<Address2:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: ArraySet [<<Address2>>,<<Index>>,<<Add>>] + + + /// CHECK-START-ARM: int[] Main.accrossGC(int[], int) instruction_simplifier_arm (before) + /// CHECK: <<Const1:i\d+>> IntConstant 1 + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Array>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: NewArray + /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Add>>] + + /// CHECK-START-ARM: int[] Main.accrossGC(int[], int) instruction_simplifier_arm (after) + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-NEXT: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: NewArray + /// CHECK: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-NEXT: ArraySet [<<Address2>>,<<Index>>,<<Add>>] + + /// CHECK-START-ARM: int[] Main.accrossGC(int[], int) GVN_after_arch (after) + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: NewArray + /// CHECK: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK: ArraySet [<<Address2>>,<<Index>>,<<Add>>] public static int[] accrossGC(int array[], int index) { @@ -196,6 +317,14 @@ public class Main { * Test that the intermediate address is shared between array accesses after * the bounds check have been removed by BCE. */ + // For checker tests `instruction_simplifier_<arch> (after)` below, by the time we reach + // the architecture-specific instruction simplifier, BCE has removed the bounds checks in + // the loop. + + // Note that we do not care that the `DataOffset` is `12`. But if we do not + // specify it and any other `IntConstant` appears before that instruction, + // checker will match the previous `IntConstant`, and we will thus fail the + // check. /// CHECK-START-ARM64: int Main.canMergeAfterBCE1() instruction_simplifier_arm64 (before) /// CHECK: <<Const1:i\d+>> IntConstant 1 @@ -207,14 +336,6 @@ public class Main { /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Add>>] - // By the time we reach the architecture-specific instruction simplifier, BCE - // has removed the bounds checks in the loop. - - // Note that we do not care that the `DataOffset` is `12`. But if we do not - // specify it and any other `IntConstant` appears before that instruction, - // checker will match the previous `IntConstant`, and we will thus fail the - // check. - /// CHECK-START-ARM64: int Main.canMergeAfterBCE1() instruction_simplifier_arm64 (after) /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant 12 @@ -222,10 +343,10 @@ public class Main { /// CHECK: <<Index:i\d+>> Phi /// CHECK: If // -------------- Loop - /// CHECK: <<Address1:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] - /// CHECK: <<Address2:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: ArraySet [<<Address2>>,<<Index>>,<<Add>>] /// CHECK-START-ARM64: int Main.canMergeAfterBCE1() GVN_after_arch (after) @@ -235,10 +356,47 @@ public class Main { /// CHECK: <<Index:i\d+>> Phi /// CHECK: If // -------------- Loop - /// CHECK: <<Address:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Address>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK-NOT: IntermediateAddress + /// CHECK: ArraySet [<<Address>>,<<Index>>,<<Add>>] + + + /// CHECK-START-ARM: int Main.canMergeAfterBCE1() instruction_simplifier_arm (before) + /// CHECK: <<Const1:i\d+>> IntConstant 1 + /// CHECK: <<Array:l\d+>> NewArray + /// CHECK: <<Index:i\d+>> Phi + /// CHECK: If + // -------------- Loop + /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Array>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Add>>] + + /// CHECK-START-ARM: int Main.canMergeAfterBCE1() instruction_simplifier_arm (after) + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant 12 + /// CHECK: <<Array:l\d+>> NewArray + /// CHECK: <<Index:i\d+>> Phi + /// CHECK: If + // -------------- Loop + /// CHECK: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-NEXT: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-NEXT: ArraySet [<<Address2>>,<<Index>>,<<Add>>] + + /// CHECK-START-ARM: int Main.canMergeAfterBCE1() GVN_after_arch (after) + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant 12 + /// CHECK: <<Array:l\d+>> NewArray + /// CHECK: <<Index:i\d+>> Phi + /// CHECK: If + // -------------- Loop + /// CHECK: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Address>>,<<Index>>] /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] - /// CHECK-NOT: Arm64IntermediateAddress + /// CHECK-NOT: IntermediateAddress /// CHECK: ArraySet [<<Address>>,<<Index>>,<<Add>>] public static int canMergeAfterBCE1() { @@ -279,12 +437,12 @@ public class Main { /// CHECK: If // -------------- Loop /// CHECK-DAG: <<Index1:i\d+>> Add [<<Index>>,<<Const1>>] - /// CHECK-DAG: <<Address1:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-DAG: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-DAG: <<ArrayGetI:i\d+>> ArrayGet [<<Address1>>,<<Index>>] - /// CHECK-DAG: <<Address2:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-DAG: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-DAG: <<ArrayGetI1:i\d+>> ArrayGet [<<Address2>>,<<Index1>>] /// CHECK: <<Add:i\d+>> Add [<<ArrayGetI>>,<<ArrayGetI1>>] - /// CHECK: <<Address3:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address3:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK: ArraySet [<<Address3>>,<<Index1>>,<<Add>>] /// CHECK-START-ARM64: int Main.canMergeAfterBCE2() GVN_after_arch (after) @@ -295,7 +453,7 @@ public class Main { /// CHECK: If // -------------- Loop /// CHECK-DAG: <<Index1:i\d+>> Add [<<Index>>,<<Const1>>] - /// CHECK-DAG: <<Address:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-DAG: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-DAG: <<ArrayGetI:i\d+>> ArrayGet [<<Address>>,<<Index>>] /// CHECK-DAG: <<ArrayGetI1:i\d+>> ArrayGet [<<Address>>,<<Index1>>] /// CHECK: <<Add:i\d+>> Add [<<ArrayGetI>>,<<ArrayGetI1>>] @@ -304,8 +462,55 @@ public class Main { // There should be only one intermediate address computation in the loop. /// CHECK-START-ARM64: int Main.canMergeAfterBCE2() GVN_after_arch (after) - /// CHECK: Arm64IntermediateAddress - /// CHECK-NOT: Arm64IntermediateAddress + /// CHECK: IntermediateAddress + /// CHECK-NOT: IntermediateAddress + + + /// CHECK-START-ARM: int Main.canMergeAfterBCE2() instruction_simplifier_arm (before) + /// CHECK: <<Const1:i\d+>> IntConstant 1 + /// CHECK: <<Array:l\d+>> NewArray + /// CHECK: <<Index:i\d+>> Phi + /// CHECK: If + // -------------- Loop + /// CHECK-DAG: <<Index1:i\d+>> Add [<<Index>>,<<Const1>>] + /// CHECK-DAG: <<ArrayGetI:i\d+>> ArrayGet [<<Array>>,<<Index>>] + /// CHECK-DAG: <<ArrayGetI1:i\d+>> ArrayGet [<<Array>>,<<Index1>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGetI>>,<<ArrayGetI1>>] + /// CHECK: ArraySet [<<Array>>,<<Index1>>,<<Add>>] + + /// CHECK-START-ARM: int Main.canMergeAfterBCE2() instruction_simplifier_arm (after) + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant 12 + /// CHECK: <<Array:l\d+>> NewArray + /// CHECK: <<Index:i\d+>> Phi + /// CHECK: If + // -------------- Loop + /// CHECK-DAG: <<Index1:i\d+>> Add [<<Index>>,<<Const1>>] + /// CHECK-DAG: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-DAG: <<ArrayGetI:i\d+>> ArrayGet [<<Address1>>,<<Index>>] + /// CHECK-DAG: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-DAG: <<ArrayGetI1:i\d+>> ArrayGet [<<Address2>>,<<Index1>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGetI>>,<<ArrayGetI1>>] + /// CHECK: <<Address3:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: ArraySet [<<Address3>>,<<Index1>>,<<Add>>] + + /// CHECK-START-ARM: int Main.canMergeAfterBCE2() GVN_after_arch (after) + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant 12 + /// CHECK: <<Array:l\d+>> NewArray + /// CHECK: <<Index:i\d+>> Phi + /// CHECK: If + // -------------- Loop + /// CHECK-DAG: <<Index1:i\d+>> Add [<<Index>>,<<Const1>>] + /// CHECK-DAG: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-DAG: <<ArrayGetI:i\d+>> ArrayGet [<<Address>>,<<Index>>] + /// CHECK-DAG: <<ArrayGetI1:i\d+>> ArrayGet [<<Address>>,<<Index1>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGetI>>,<<ArrayGetI1>>] + /// CHECK: ArraySet [<<Address>>,<<Index1>>,<<Add>>] + + /// CHECK-START-ARM: int Main.canMergeAfterBCE2() GVN_after_arch (after) + /// CHECK: IntermediateAddress + /// CHECK-NOT: IntermediateAddress public static int canMergeAfterBCE2() { int[] array = {0, 1, 2, 3}; @@ -315,6 +520,37 @@ public class Main { return array[array.length - 1]; } + /// CHECK-START-ARM: int Main.checkLongFloatDouble() instruction_simplifier_arm (before) + /// CHECK-DAG: <<Array1:l\d+>> NewArray + /// CHECK-DAG: <<Array2:l\d+>> NewArray + /// CHECK-DAG: <<Array3:l\d+>> NewArray + /// CHECK-DAG: <<Index:i\d+>> Phi + /// CHECK-DAG: ArrayGet [<<Array1>>,<<Index>>] + /// CHECK-DAG: ArrayGet [<<Array2>>,<<Index>>] + /// CHECK-DAG: ArrayGet [<<Array3>>,<<Index>>] + + /// CHECK-START-ARM: int Main.checkLongFloatDouble() instruction_simplifier_arm (after) + /// CHECK-DAG: <<Array1:l\d+>> NewArray + /// CHECK-DAG: <<Array2:l\d+>> NewArray + /// CHECK-DAG: <<Array3:l\d+>> NewArray + /// CHECK-DAG: <<Index:i\d+>> Phi + /// CHECK-DAG: ArrayGet [<<Array1>>,<<Index>>] + /// CHECK-DAG: ArrayGet [<<Array2>>,<<Index>>] + /// CHECK-DAG: ArrayGet [<<Array3>>,<<Index>>] + + /// CHECK-START-ARM: int Main.checkLongFloatDouble() instruction_simplifier_arm (after) + /// CHECK-NOT: IntermediateAddress + public static int checkLongFloatDouble() { + long[] array_long = {0, 1, 2, 3}; + float[] array_float = {(float)0.0, (float)1.0, (float)2.0, (float)3.0}; + double[] array_double = {0.0, 1.0, 2.0, 3.0}; + double s = 0.0; + + for (int i = 0; i < 4; i++) { + s += (double)array_long[i] + (double)array_float[i] + array_double[i]; + } + return (int)s; + } public static void main(String[] args) { int[] array = {123, 456, 789}; @@ -337,5 +573,7 @@ public class Main { assertIntEquals(4, canMergeAfterBCE1()); assertIntEquals(6, canMergeAfterBCE2()); + + assertIntEquals(18, checkLongFloatDouble()); } } diff --git a/test/529-checker-unresolved/src/Main.java b/test/529-checker-unresolved/src/Main.java index 5a36ba5d9c..7b5cbc1504 100644 --- a/test/529-checker-unresolved/src/Main.java +++ b/test/529-checker-unresolved/src/Main.java @@ -77,6 +77,16 @@ public class Main extends UnresolvedSuperClass { expectEquals(123456789123456789f, UnresolvedClass.staticFloat); expectEquals(123456789123456789d, UnresolvedClass.staticDouble); expectEquals(o, UnresolvedClass.staticObject); + + // Check "large" values. + + UnresolvedClass.staticByte = (byte)-1; + UnresolvedClass.staticChar = (char)32768; + UnresolvedClass.staticInt = -1; + + expectEquals((byte)-1, UnresolvedClass.staticByte); + expectEquals((char)32768, UnresolvedClass.staticChar); + expectEquals(-1, UnresolvedClass.staticInt); } /// CHECK-START: void Main.callUnresolvedInstanceFieldAccess(UnresolvedClass) register (before) diff --git a/test/530-checker-loops3/expected.txt b/test/530-checker-loops3/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/530-checker-loops3/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/530-checker-loops3/info.txt b/test/530-checker-loops3/info.txt new file mode 100644 index 0000000000..07d99a3e55 --- /dev/null +++ b/test/530-checker-loops3/info.txt @@ -0,0 +1 @@ +Test on loop optimizations, in particular loop-based dynamic bce. diff --git a/test/530-checker-loops3/src/Main.java b/test/530-checker-loops3/src/Main.java new file mode 100644 index 0000000000..5ffcbe964d --- /dev/null +++ b/test/530-checker-loops3/src/Main.java @@ -0,0 +1,327 @@ +/* + * 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. + */ + +// +// Test on loop optimizations, in particular dynamic BCE. In all cases, +// bounds check on a[] is resolved statically. Bounds checks on b[] +// exercise various different scenarios. In all cases, loop-based +// dynamic BCE is better than the dominator-based BCE, since it +// generates the test outside the loop. +// +public class Main { + + /// CHECK-START: void Main.oneConstantIndex(int[], int[]) BCE (before) + /// CHECK-DAG: BoundsCheck loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: void Main.oneConstantIndex(int[], int[]) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-NOT: Deoptimize + // + /// CHECK-START: void Main.oneConstantIndex(int[], int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + public static void oneConstantIndex(int[] a, int[] b) { + // Dynamic bce on b requires two deopts: one null and one bound. + for (int i = 0; i < a.length; i++) { + a[i] = b[1]; + } + } + + /// CHECK-START: void Main.multipleConstantIndices(int[], int[]) BCE (before) + /// CHECK-DAG: BoundsCheck loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: void Main.multipleConstantIndices(int[], int[]) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-NOT: Deoptimize + // + /// CHECK-START: void Main.multipleConstantIndices(int[], int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + public static void multipleConstantIndices(int[] a, int[] b) { + // Dynamic bce on b requires two deopts: one null and one bound. + for (int i = 0; i < a.length; i++) { + a[i] = b[0] + b[1] + b[2]; + } + } + + /// CHECK-START: void Main.oneInvariantIndex(int[], int[], int) BCE (before) + /// CHECK-DAG: BoundsCheck loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: void Main.oneInvariantIndex(int[], int[], int) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-NOT: Deoptimize + // + /// CHECK-START: void Main.oneInvariantIndex(int[], int[], int) BCE (after) + /// CHECK-NOT: BoundsCheck + public static void oneInvariantIndex(int[] a, int[] b, int c) { + // Dynamic bce on b requires two deopts: one null and one bound. + for (int i = 0; i < a.length; i++) { + a[i] = b[c]; + } + } + + /// CHECK-START: void Main.multipleInvariantIndices(int[], int[], int) BCE (before) + /// CHECK-DAG: BoundsCheck loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: void Main.multipleInvariantIndices(int[], int[], int) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-NOT: Deoptimize + // + /// CHECK-START: void Main.multipleInvariantIndices(int[], int[], int) BCE (after) + /// CHECK-NOT: BoundsCheck + public static void multipleInvariantIndices(int[] a, int[] b, int c) { + // Dynamic bce on b requires three deopts: one null and two bounds. + for (int i = 0; i < a.length; i++) { + a[i] = b[c-1] + b[c] + b[c+1]; + } + } + + /// CHECK-START: void Main.oneUnitStride(int[], int[]) BCE (before) + /// CHECK-DAG: BoundsCheck loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: void Main.oneUnitStride(int[], int[]) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-NOT: Deoptimize + // + /// CHECK-START: void Main.oneUnitStride(int[], int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + public static void oneUnitStride(int[] a, int[] b) { + // Dynamic bce on b requires three deopts: one null and two bounds. + for (int i = 0; i < a.length; i++) { + a[i] = b[i]; + } + } + + /// CHECK-START: void Main.multipleUnitStrides(int[], int[]) BCE (before) + /// CHECK-DAG: BoundsCheck loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: void Main.multipleUnitStrides(int[], int[]) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-NOT: Deoptimize + // + /// CHECK-START: void Main.multipleUnitStrides(int[], int[]) instruction_simplifier_after_bce (after) + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-NOT: Deoptimize + // + /// CHECK-START: void Main.multipleUnitStrides(int[], int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + public static void multipleUnitStrides(int[] a, int[] b) { + // Dynamic bce on b requires four deopts: one null and three bounds. + // One redundant deopt is removed by simplifier. + // TODO: range information could remove another + for (int i = 1; i < a.length - 1; i++) { + a[i] = b[i-1] + b[i] + b[i+1]; + } + } + + /// CHECK-START: void Main.multipleUnitStridesConditional(int[], int[]) BCE (before) + /// CHECK-DAG: BoundsCheck loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: void Main.multipleUnitStridesConditional(int[], int[]) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-NOT: Deoptimize + // + /// CHECK-START: void Main.multipleUnitStridesConditional(int[], int[]) instruction_simplifier_after_bce (after) + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-NOT: Deoptimize + // + /// CHECK-START: void Main.multipleUnitStridesConditional(int[], int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + public static void multipleUnitStridesConditional(int[] a, int[] b) { + // Dynamic bce on b requires four deopts: one null and three bounds. + // The two conditional references may be included, since they are in range. + // One redundant deopt is removed by simplifier. + for (int i = 2; i < a.length - 2; i++) { + int t = b[i-2] + b[i] + b[i+2] + (((i & 1) == 0) ? b[i+1] : b[i-1]); + a[i] = t; + } + } + + /// CHECK-START: void Main.shifter(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: void Main.shifter(int[]) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-NOT: Deoptimize + // + /// CHECK-START: void Main.shifter(int[]) instruction_simplifier_after_bce (after) + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-NOT: Deoptimize + // + /// CHECK-START: void Main.shifter(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + public static void shifter(int[] x) { + // Real-life example: should have four deopts: one null and three bounds. + // Two redundant deopts are removed by simplifier. + for (int i = 16; i < 80; i++) { + int t = x[i - 3] ^ x[i - 8] ^ x[i - 14] ^ x[i - 16]; + x[i] = t << 1 | t >>> 31; + } + } + + /// CHECK-START: void Main.stencil(int[], int, int) BCE (before) + /// CHECK-DAG: BoundsCheck loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: void Main.stencil(int[], int, int) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-NOT: Deoptimize + // + /// CHECK-START: void Main.stencil(int[], int, int) BCE (after) + /// CHECK-NOT: BoundsCheck + public static void stencil(int[] array, int start, int end) { + // Real-life example: should have four deopts: one null and three bounds. + for (int i = end; i >= start; i--) { + array[i] = (array[i-2] + array[i-1] + array[i] + array[i+1] + array[i+2]) / 5; + } + } + + // + // Verifier. + // + + public static void main(String[] args) { + int[] a = new int[10]; + int b[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int b1[] = { 100 }; + + oneConstantIndex(a, b); + for (int i = 0; i < a.length; i++) { + expectEquals(2, a[i]);; + } + try { + oneConstantIndex(a, b1); + throw new Error("Should throw AIOOBE"); + } catch (ArrayIndexOutOfBoundsException e) { + } + + multipleConstantIndices(a, b); + for (int i = 0; i < a.length; i++) { + expectEquals(6, a[i]);; + } + try { + multipleConstantIndices(a, b1); + throw new Error("Should throw AIOOBE"); + } catch (ArrayIndexOutOfBoundsException e) { + } + + oneInvariantIndex(a, b, 1); + for (int i = 0; i < a.length; i++) { + expectEquals(2, a[i]);; + } + try { + oneInvariantIndex(a, b1, 1); + throw new Error("Should throw AIOOBE"); + } catch (ArrayIndexOutOfBoundsException e) { + } + + multipleInvariantIndices(a, b, 1); + for (int i = 0; i < a.length; i++) { + expectEquals(6, a[i]);; + } + try { + multipleInvariantIndices(a, b1, 1); + throw new Error("Should throw AIOOBE"); + } catch (ArrayIndexOutOfBoundsException e) { + } + + oneUnitStride(a, b); + for (int i = 0; i < a.length; i++) { + expectEquals(i + 1, a[i]);; + } + try { + oneUnitStride(a, b1); + throw new Error("Should throw AIOOBE"); + } catch (ArrayIndexOutOfBoundsException e) { + expectEquals(100, a[0]);; + } + + multipleUnitStrides(a, b); + for (int i = 1; i < a.length - 1; i++) { + expectEquals(3 * i + 3, a[i]);; + } + try { + multipleUnitStrides(a, b1); + throw new Error("Should throw AIOOBE"); + } catch (ArrayIndexOutOfBoundsException e) { + } + + multipleUnitStridesConditional(a, b); + for (int i = 2; i < a.length - 2; i++) { + int e = 3 * i + 3 + (((i & 1) == 0) ? i + 2 : i); + expectEquals(e, a[i]);; + } + try { + multipleUnitStridesConditional(a, b1); + throw new Error("Should throw AIOOBE"); + } catch (ArrayIndexOutOfBoundsException e) { + } + + System.out.println("passed"); + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} diff --git a/test/566-polymorphic-inlining/polymorphic_inline.cc b/test/566-polymorphic-inlining/polymorphic_inline.cc index c0d93dd8a1..9f4c6c91f8 100644 --- a/test/566-polymorphic-inlining/polymorphic_inline.cc +++ b/test/566-polymorphic-inlining/polymorphic_inline.cc @@ -81,6 +81,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_ensureJittedAndPolymorphicInline566( do_checks(cls, "testInvokeVirtual"); do_checks(cls, "testInvokeInterface"); + do_checks(cls, "testInvokeInterface2"); do_checks(cls, "$noinline$testInlineToSameTarget"); } diff --git a/test/566-polymorphic-inlining/src/Main.java b/test/566-polymorphic-inlining/src/Main.java index d39e6ed57b..53852a417c 100644 --- a/test/566-polymorphic-inlining/src/Main.java +++ b/test/566-polymorphic-inlining/src/Main.java @@ -16,6 +16,8 @@ interface Itf { public Class sameInvokeInterface(); + public Class sameInvokeInterface2(); + public Class sameInvokeInterface3(); } public class Main implements Itf { @@ -50,6 +52,8 @@ public class Main implements Itf { testInvokeVirtual(mains[1]); testInvokeInterface(itfs[0]); testInvokeInterface(itfs[1]); + testInvokeInterface2(itfs[0]); + testInvokeInterface2(itfs[1]); $noinline$testInlineToSameTarget(mains[0]); $noinline$testInlineToSameTarget(mains[1]); } @@ -64,9 +68,13 @@ public class Main implements Itf { assertEquals(Itf.class, testInvokeInterface(itfs[0])); assertEquals(Itf.class, testInvokeInterface(itfs[1])); + assertEquals(Itf.class, testInvokeInterface2(itfs[0])); + assertEquals(Itf.class, testInvokeInterface2(itfs[1])); + // This will trigger a deoptimization of the compiled code. assertEquals(OtherSubclass.class, testInvokeVirtual(mains[2])); assertEquals(OtherSubclass.class, testInvokeInterface(itfs[2])); + assertEquals(null, testInvokeInterface2(itfs[2])); // Run this once to make sure we execute the JITted code. $noinline$testInlineToSameTarget(mains[0]); @@ -83,10 +91,28 @@ public class Main implements Itf { return Itf.class; } + public Class sameInvokeInterface2() { + field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo. + return Itf.class; + } + + public Class sameInvokeInterface3() { + field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo. + return Itf.class; + } + public static Class testInvokeInterface(Itf i) { return i.sameInvokeInterface(); } + public static Class testInvokeInterface2(Itf i) { + // Make three interface calls that will do a ClassTableGet to ensure bogus code + // generation of ClassTableGet will crash. + i.sameInvokeInterface(); + i.sameInvokeInterface2(); + return i.sameInvokeInterface3(); + } + public static Class testInvokeVirtual(Main m) { return m.sameInvokeVirtual(); } @@ -120,4 +146,11 @@ class OtherSubclass extends Main { public Class sameInvokeInterface() { return OtherSubclass.class; } + + public Class sameInvokeInterface2() { + return null; + } + public Class sameInvokeInterface3() { + return null; + } } diff --git a/test/604-hot-static-interface/hot_static_interface.cc b/test/604-hot-static-interface/hot_static_interface.cc deleted file mode 100644 index 475a11d351..0000000000 --- a/test/604-hot-static-interface/hot_static_interface.cc +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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. - */ - -#include "art_method.h" -#include "jit/jit.h" -#include "jit/jit_code_cache.h" -#include "jit/profiling_info.h" -#include "oat_quick_method_header.h" -#include "scoped_thread_state_change.h" -#include "ScopedUtfChars.h" -#include "stack_map.h" - -namespace art { - -extern "C" JNIEXPORT void JNICALL Java_Main_waitUntilJitted(JNIEnv* env, - jclass, - jclass itf, - jstring method_name) { - jit::Jit* jit = Runtime::Current()->GetJit(); - if (jit == nullptr) { - return; - } - - ScopedObjectAccess soa(Thread::Current()); - - ScopedUtfChars chars(env, method_name); - CHECK(chars.c_str() != nullptr); - - mirror::Class* klass = soa.Decode<mirror::Class*>(itf); - ArtMethod* method = klass->FindDeclaredDirectMethodByName(chars.c_str(), sizeof(void*)); - - jit::JitCodeCache* code_cache = jit->GetCodeCache(); - OatQuickMethodHeader* header = nullptr; - // Make sure there is a profiling info, required by the compiler. - ProfilingInfo::Create(soa.Self(), method, /* retry_allocation */ true); - while (true) { - header = OatQuickMethodHeader::FromEntryPoint(method->GetEntryPointFromQuickCompiledCode()); - if (code_cache->ContainsPc(header->GetCode())) { - break; - } else { - // Sleep to yield to the compiler thread. - usleep(1000); - // Will either ensure it's compiled or do the compilation itself. - jit->CompileMethod(method, soa.Self(), /* osr */ false); - } - } -} - -} // namespace art diff --git a/test/604-hot-static-interface/src/Main.java b/test/604-hot-static-interface/src/Main.java index 559f15d380..04d7cd6567 100644 --- a/test/604-hot-static-interface/src/Main.java +++ b/test/604-hot-static-interface/src/Main.java @@ -22,14 +22,14 @@ public class Main { Itf.foo(new Object()); } - waitUntilJitted(Itf.class, "foo"); + ensureJitCompiled(Itf.class, "foo"); if (!Itf.foo(new Object())) { throw new Error("Unexpected result"); } } - private static native void waitUntilJitted(Class itf, String method_name); + private static native void ensureJitCompiled(Class itf, String method_name); } interface Itf { diff --git a/test/611-checker-simplify-if/expected.txt b/test/611-checker-simplify-if/expected.txt new file mode 100644 index 0000000000..3083c4c528 --- /dev/null +++ b/test/611-checker-simplify-if/expected.txt @@ -0,0 +1,7 @@ +54 +54 +54 +12 +12 +12 +33 diff --git a/test/611-checker-simplify-if/info.txt b/test/611-checker-simplify-if/info.txt new file mode 100644 index 0000000000..b090db816b --- /dev/null +++ b/test/611-checker-simplify-if/info.txt @@ -0,0 +1 @@ +Checker tests for the 'if' simplification in the compiler. diff --git a/test/611-checker-simplify-if/src/Main.java b/test/611-checker-simplify-if/src/Main.java new file mode 100644 index 0000000000..21f4115c55 --- /dev/null +++ b/test/611-checker-simplify-if/src/Main.java @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + + public static void main(String[] args) { + testNoInline(args); + System.out.println(staticField); + testInline(args); + System.out.println(staticField); + testNonConstantInputs(args); + System.out.println(staticField); + testNonConstantEqual(args); + System.out.println(staticField); + testGreaterCondition(args); + System.out.println(staticField); + testSwitch(args); + System.out.println(staticField); + testFP(args); + System.out.println(staticField); + } + + // Test when a condition is the input of the if. + + /// CHECK-START: void Main.testNoInline(java.lang.String[]) dead_code_elimination (before) + /// CHECK: <<Const0:i\d+>> IntConstant 0 + /// CHECK: If + /// CHECK: <<Phi:i\d+>> Phi + /// CHECK: <<Equal:z\d+>> Equal [<<Phi>>,<<Const0>>] + /// CHECK: If [<<Equal>>] + + /// CHECK-START: void Main.testNoInline(java.lang.String[]) dead_code_elimination (after) + /// CHECK: If + /// CHECK-NOT: Phi + /// CHECK-NOT: Equal + /// CHECK-NOT: If + public static void testNoInline(String[] args) { + boolean myVar = false; + if (args.length == 42) { + myVar = true; + } else { + staticField = 32; + myVar = false; + } + if (myVar) { + staticField = 12; + } else { + staticField = 54; + } + } + + // Test when the phi is the input of the if. + + /// CHECK-START: void Main.testInline(java.lang.String[]) dead_code_elimination_final (before) + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + /// CHECK-DAG: If + /// CHECK-DAG: <<Phi:i\d+>> Phi + /// CHECK-DAG: If [<<Phi>>] + + /// CHECK-START: void Main.testInline(java.lang.String[]) dead_code_elimination_final (after) + /// CHECK: If + /// CHECK-NOT: Phi + /// CHECK-NOT: If + public static void testInline(String[] args) { + boolean myVar = $inline$doTest(args); + if (myVar) { + staticField = 12; + } else { + staticField = 54; + } + } + + public static boolean $inline$doTest(String[] args) { + boolean myVar; + if (args.length == 42) { + myVar = true; + } else { + staticField = 32; + myVar = false; + } + return myVar; + } + + // Test when one input is not a constant. We can only optimize the constant input. + + /// CHECK-START: void Main.testNonConstantInputs(java.lang.String[]) dead_code_elimination (before) + /// CHECK-DAG: <<Const34:i\d+>> IntConstant 34 + /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 + /// CHECK-DAG: If + /// CHECK-DAG: <<StaticFieldGet:i\d+>> StaticFieldGet + /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const34>>,<<StaticFieldGet>>] + /// CHECK-DAG: <<NotEqual:z\d+>> NotEqual [<<Phi>>,<<Const42>>] + /// CHECK-DAG: If [<<NotEqual>>] + + /// CHECK-START: void Main.testNonConstantInputs(java.lang.String[]) dead_code_elimination (after) + /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 + /// CHECK-DAG: If + /// CHECK-DAG: <<StaticFieldGet:i\d+>> StaticFieldGet + /// CHECK-NOT: Phi + /// CHECK-DAG: <<NotEqual:z\d+>> NotEqual [<<StaticFieldGet>>,<<Const42>>] + /// CHECK-DAG: If [<<NotEqual>>] + public static void testNonConstantInputs(String[] args) { + int a = 42; + if (args.length == 42) { + a = 34; + } else { + staticField = 32; + a = otherStaticField; + } + if (a == 42) { + staticField = 12; + } else { + staticField = 54; + } + } + + // Test with a condition. + + /// CHECK-START: void Main.testGreaterCondition(java.lang.String[]) dead_code_elimination (before) + /// CHECK-DAG: <<Const34:i\d+>> IntConstant 34 + /// CHECK-DAG: <<Const22:i\d+>> IntConstant 22 + /// CHECK-DAG: <<Const25:i\d+>> IntConstant 25 + /// CHECK-DAG: If + /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const34>>,<<Const22>>] + /// CHECK-DAG: <<GE:z\d+>> GreaterThanOrEqual [<<Phi>>,<<Const25>>] + /// CHECK-DAG: If [<<GE>>] + + /// CHECK-START: void Main.testGreaterCondition(java.lang.String[]) dead_code_elimination (after) + /// CHECK-DAG: If + /// CHECK-NOT: Phi + /// CHECK-NOT: GreaterThanOrEqual + /// CHECK-NOT: If + public static void testGreaterCondition(String[] args) { + int a = 42;; + if (args.length == 42) { + a = 34; + } else { + staticField = 32; + a = 22; + } + if (a < 25) { + staticField = 12; + } else { + staticField = 54; + } + } + + // Test when comparing non constants. + + /// CHECK-START: void Main.testNonConstantEqual(java.lang.String[]) dead_code_elimination (before) + /// CHECK-DAG: <<Const34:i\d+>> IntConstant 34 + /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 + /// CHECK-DAG: If + /// CHECK-DAG: <<StaticFieldGet:i\d+>> StaticFieldGet + /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const34>>,<<StaticFieldGet>>] + /// CHECK-DAG: <<NotEqual:z\d+>> NotEqual [<<Phi>>,<<StaticFieldGet>>] + /// CHECK-DAG: If [<<NotEqual>>] + + /// CHECK-START: void Main.testNonConstantEqual(java.lang.String[]) dead_code_elimination (after) + /// CHECK-DAG: <<Const34:i\d+>> IntConstant 34 + /// CHECK-DAG: If + /// CHECK-DAG: <<StaticFieldGet:i\d+>> StaticFieldGet + /// CHECK-NOT: Phi + /// CHECK-DAG: <<NotEqual:z\d+>> NotEqual [<<Const34>>,<<StaticFieldGet>>] + /// CHECK-DAG: If [<<NotEqual>>] + public static void testNonConstantEqual(String[] args) { + int a = 42; + int b = otherStaticField; + if (args.length == 42) { + a = 34; + } else { + staticField = 32; + a = b; + } + if (a == b) { + staticField = 12; + } else { + staticField = 54; + } + } + + // Make sure we don't "simplify" a loop and potentially turn it into + // an irreducible loop. The suspend check at the loop header prevents + // us from doing the simplification. + + /// CHECK-START: void Main.testLoop(boolean) disassembly (after) + /// CHECK-DAG: SuspendCheck + /// CHECK: irreducible:false + /// CHECK-NOT: irreducible:true + public static void testLoop(boolean c) { + while (true) { + if (c) { + if ($noinline$foo()) return; + c = false; + } else { + $noinline$foo(); + c = true; + } + } + } + + static boolean $noinline$foo() { + if (doThrow) throw new Error(""); + return true; + } + + /// CHECK-START: void Main.testSwitch(java.lang.String[]) dead_code_elimination (before) + /// CHECK: If + /// CHECK: If + /// CHECK: If + + /// CHECK-START: void Main.testSwitch(java.lang.String[]) dead_code_elimination (after) + /// CHECK: If + /// CHECK: If + /// CHECK-NOT: If + public static void testSwitch(String[] args) { + boolean cond = false; + switch (args.length) { + case 42: + staticField = 11; + cond = true; + break; + case 43: + staticField = 33; + cond = true; + break; + default: + cond = false; + break; + } + if (cond) { + // Redirect case 42 and 43 here. + staticField = 2; + } + // Redirect default here. + } + + /// CHECK-START: void Main.testFP(java.lang.String[]) dead_code_elimination (before) + /// CHECK: If + /// CHECK: If + + /// CHECK-START: void Main.testFP(java.lang.String[]) dead_code_elimination (after) + /// CHECK: If + /// CHECK: If + public static void testFP(String[] args) { + float f = 2.2f; + float nan = $noinline$getNaN(); + if (args.length == 42) { + f = 4.3f; + } else { + staticField = 33; + f = nan; + } + if (f == nan) { + staticField = 5; + } + } + + // No inline variant to avoid having the compiler see it's a NaN. + static float $noinline$getNaN() { + if (doThrow) throw new Error(""); + return Float.NaN; + } + + static boolean doThrow; + static int staticField; + static int otherStaticField; +} diff --git a/test/612-jit-dex-cache/expected.txt b/test/612-jit-dex-cache/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/612-jit-dex-cache/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/612-jit-dex-cache/info.txt b/test/612-jit-dex-cache/info.txt new file mode 100644 index 0000000000..e80f642f3e --- /dev/null +++ b/test/612-jit-dex-cache/info.txt @@ -0,0 +1,2 @@ +Regression test for the JIT compiler which used to +wrongly update the dex cache of a class loader. diff --git a/test/612-jit-dex-cache/src-ex/B.java b/test/612-jit-dex-cache/src-ex/B.java new file mode 100644 index 0000000000..4da9a1da6b --- /dev/null +++ b/test/612-jit-dex-cache/src-ex/B.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class B { +} diff --git a/test/612-jit-dex-cache/src-ex/LoadedByAppClassLoader.java b/test/612-jit-dex-cache/src-ex/LoadedByAppClassLoader.java new file mode 100644 index 0000000000..1d6158a593 --- /dev/null +++ b/test/612-jit-dex-cache/src-ex/LoadedByAppClassLoader.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class LoadedByAppClassLoader { + public static void letMeInlineYou(A a) { + a.foo(); + } + + public static ClassLoader areYouB() { + // Ensure letMeInlineYou is JITted and tries to do inlining of A.foo. + // The compiler used to wrongly update the dex cache of letMeInlineYou's + // class loader. + Main.ensureJitCompiled(LoadedByAppClassLoader.class, "letMeInlineYou"); + return OtherClass.getB().getClassLoader(); + } +} + +class OtherClass { + public static Class getB() { + // This used to return the B class of another class loader. + return B.class; + } +} diff --git a/test/612-jit-dex-cache/src/A.java b/test/612-jit-dex-cache/src/A.java new file mode 100644 index 0000000000..415c712477 --- /dev/null +++ b/test/612-jit-dex-cache/src/A.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class A { + public int foo() { + return 42; + } +} diff --git a/test/612-jit-dex-cache/src/B.java b/test/612-jit-dex-cache/src/B.java new file mode 100644 index 0000000000..46c878b572 --- /dev/null +++ b/test/612-jit-dex-cache/src/B.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class B extends A { +} diff --git a/test/612-jit-dex-cache/src/Main.java b/test/612-jit-dex-cache/src/Main.java new file mode 100644 index 0000000000..0e4bd2245d --- /dev/null +++ b/test/612-jit-dex-cache/src/Main.java @@ -0,0 +1,67 @@ +/* + * 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.lang.reflect.InvocationTargetException; + +import dalvik.system.PathClassLoader; + +// ClassLoader not delegating for non java. packages. +class DelegateLastPathClassLoader extends PathClassLoader { + + public DelegateLastPathClassLoader(String dexPath, ClassLoader parent) { + super(dexPath, parent); + } + + @Override + protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (!name.startsWith("java.")) { + try { + return findClass(name); + } catch (ClassNotFoundException ignore) { + // Ignore and fall through to parent class loader. + } + } + return super.loadClass(name, resolve); + } +} + +public class Main { + + private static Class classFromDifferentLoader() throws Exception { + final String DEX_FILE = System.getenv("DEX_LOCATION") + "/612-jit-dex-cache-ex.jar"; + ClassLoader loader = new DelegateLastPathClassLoader(DEX_FILE, Main.class.getClassLoader()); + return loader.loadClass("LoadedByAppClassLoader"); + } + + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + Class cls = classFromDifferentLoader(); + Method m = cls.getDeclaredMethod("letMeInlineYou", A.class); + B b = new B(); + // Invoke the method enough times to get an inline cache and get JITted. + for (int i = 0; i < 10000; ++i) { + m.invoke(null, b); + } + m = cls.getDeclaredMethod("areYouB", null); + ClassLoader loader = (ClassLoader) m.invoke(null); + if (loader != cls.getClassLoader()) { + throw new Error("Wrong class loader"); + } + } + + public static native void ensureJitCompiled(Class cls, String method_name); +} diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk index 75e74eca3a..7813d16657 100644 --- a/test/Android.libarttest.mk +++ b/test/Android.libarttest.mk @@ -47,8 +47,7 @@ LIBARTTEST_COMMON_SRC_FILES := \ 570-checker-osr/osr.cc \ 595-profile-saving/profile-saving.cc \ 596-app-images/app_images.cc \ - 597-deopt-new-string/deopt.cc \ - 604-hot-static-interface/hot_static_interface.cc + 597-deopt-new-string/deopt.cc ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index dd6b6f3fbc..8f8b667429 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -527,7 +527,7 @@ TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS := # Tests that should fail in the read barrier configuration with the Optimizing compiler (AOT). # 484: Baker's fast path based read barrier compiler instrumentation generates code containing # more parallel moves on x86, thus some Checker assertions may fail. -# 527: On ARM64, the read barrier instrumentation does not support the HArm64IntermediateAddress +# 527: On ARM64 and ARM, the read barrier instrumentation does not support the HIntermediateAddress # instruction yet (b/26601270). # 537: Expects an array copy to be intrinsified on x86-64, but calling-on-slowpath intrinsics are # not yet handled in the read barrier configuration. diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index fd41fd281f..e70a95cbb5 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -18,10 +18,14 @@ #include "base/logging.h" #include "dex_file-inl.h" +#include "jit/jit.h" +#include "jit/jit_code_cache.h" #include "mirror/class-inl.h" #include "nth_caller_visitor.h" +#include "oat_quick_method_header.h" #include "runtime.h" #include "scoped_thread_state_change.h" +#include "ScopedUtfChars.h" #include "stack.h" #include "thread-inl.h" @@ -116,4 +120,38 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_compiledWithOptimizing(JNIEnv* e return JNI_TRUE; } +extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env, + jclass, + jclass cls, + jstring method_name) { + jit::Jit* jit = Runtime::Current()->GetJit(); + if (jit == nullptr) { + return; + } + + ScopedObjectAccess soa(Thread::Current()); + + ScopedUtfChars chars(env, method_name); + CHECK(chars.c_str() != nullptr); + + mirror::Class* klass = soa.Decode<mirror::Class*>(cls); + ArtMethod* method = klass->FindDeclaredDirectMethodByName(chars.c_str(), sizeof(void*)); + + jit::JitCodeCache* code_cache = jit->GetCodeCache(); + OatQuickMethodHeader* header = nullptr; + // Make sure there is a profiling info, required by the compiler. + ProfilingInfo::Create(soa.Self(), method, /* retry_allocation */ true); + while (true) { + header = OatQuickMethodHeader::FromEntryPoint(method->GetEntryPointFromQuickCompiledCode()); + if (code_cache->ContainsPc(header->GetCode())) { + break; + } else { + // Sleep to yield to the compiler thread. + usleep(1000); + // Will either ensure it's compiled or do the compilation itself. + jit->CompileMethod(method, soa.Self(), /* osr */ false); + } + } +} + } // namespace art diff --git a/test/run-test b/test/run-test index 3ae063abca..1ef5428726 100755 --- a/test/run-test +++ b/test/run-test @@ -37,7 +37,7 @@ test_dir="test-$$" if [ -z "$TMPDIR" ]; then tmp_dir="/tmp/$USER/${test_dir}" else - tmp_dir="${TMPDIR}/$USER/${test_dir}" + tmp_dir="${TMPDIR}/${test_dir}" fi checker="${progdir}/../tools/checker/checker.py" export JAVA="java" @@ -78,9 +78,14 @@ if [ -z "$ANDROID_BUILD_TOP" ]; then export ANDROID_BUILD_TOP=$oldwd fi +# ANDROID_HOST_OUT is not set in a build environment. +if [ -z "$ANDROID_HOST_OUT" ]; then + export ANDROID_HOST_OUT=${OUT_DIR:-$ANDROID_BUILD_TOP/out/}host/linux-x86 +fi + # If JACK_CLASSPATH is not set, assume it only contains core-libart. if [ -z "$JACK_CLASSPATH" ]; then - export JACK_CLASSPATH="${OUT_DIR:-$ANDROID_BUILD_TOP/out}/host/common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:${OUT_DIR:-$ANDROID_BUILD_TOP/out}/host/common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack" + export JACK_CLASSPATH="${ANDROID_HOST_OUT}/../common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:${ANDROID_HOST_OUT}/../common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack" fi export JACK="$JACK -g -cp $JACK_CLASSPATH" @@ -462,10 +467,6 @@ if [ "$runtime" = "dalvik" ]; then fi elif [ "$runtime" = "art" ]; then if [ "$target_mode" = "no" ]; then - # ANDROID_HOST_OUT is not set in a build environment. - if [ -z "$ANDROID_HOST_OUT" ]; then - export ANDROID_HOST_OUT=${OUT_DIR:-$ANDROID_BUILD_TOP/out/}host/linux-x86 - fi guess_host_arch_name run_args="${run_args} --boot ${ANDROID_HOST_OUT}/framework/core${image_suffix}${pic_image_suffix}${multi_image_suffix}.art" run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}" |