/* * 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 Circle { Circle(double radius) { this.radius = radius; } public double getRadius() { return radius; } public double getArea() { return radius * radius * Math.PI; } private double radius; } class TestClass { static { sTestClassObj = new TestClass(-1, -2); } TestClass() { } TestClass(int i, int j) { this.i = i; this.j = j; } int i; int j; volatile int k; TestClass next; String str; byte b; static int si; static TestClass sTestClassObj; } class SubTestClass extends TestClass { int k; } class TestClass2 { int i; int j; int k; int l; int m; } class TestClass3 { float floatField = 8.0f; boolean test1 = true; } // Chosen to have different values with (x + 1) * 10 and (x - 1) * 10. This // means we can easily make sure that different code is in fact executed on // escape and non-escape paths. // Negative so that high-bits will be set for all the 64-bit values allowing us // to easily check for truncation. class TestClass4 { float floatField = -3.0f; double doubleField = -3.0d; short shortField = -3; int intField = -3; byte byteField = -3; long longField = -3l; } class Finalizable { static boolean sVisited = false; static final int VALUE1 = 0xbeef; static final int VALUE2 = 0xcafe; int i; protected void finalize() { if (i != VALUE1) { System.out.println("Where is the beef?"); } sVisited = true; } } interface Filter { public boolean isValid(int i); } public class Main { static void $noinline$Escape4(TestClass4 o) { o.floatField += 1.0f; o.doubleField += 1.0d; o.byteField += 1; o.shortField += 1; o.intField += 1; o.longField += 1; } static Object ESCAPE = null; static void $noinline$Escape(TestClass o) { if (o == null) { return; } ESCAPE = o; o.next.i++; } /// CHECK-START: double Main.calcCircleArea(double) load_store_elimination (before) /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK-START: double Main.calcCircleArea(double) load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: InstanceFieldGet static double calcCircleArea(double radius) { return new Circle(radius).getArea(); } /// CHECK-START: int Main.test1(TestClass, TestClass) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK: InstanceFieldGet /// CHECK-START: int Main.test1(TestClass, TestClass) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-NOT: NullCheck /// CHECK-NOT: InstanceFieldGet // Different fields shouldn't alias. static int test1(TestClass obj1, TestClass obj2) { obj1.i = 1; obj2.j = 2; return obj1.i + obj2.j; } /// CHECK-START: int Main.test2(TestClass) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK-START: int Main.test2(TestClass) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK-NOT: NullCheck /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: InstanceFieldGet // Redundant store of the same value. static int test2(TestClass obj) { obj.j = 1; obj.j = 1; return obj.j; } /// CHECK-START: int Main.test3(TestClass) load_store_elimination (before) /// CHECK: StaticFieldGet /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK: InstanceFieldGet /// CHECK: InstanceFieldGet /// CHECK: InstanceFieldGet /// CHECK-START: int Main.test3(TestClass) load_store_elimination (after) /// CHECK: StaticFieldGet /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-START: int Main.test3(TestClass) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet // A new allocation (even non-singleton) shouldn't alias with pre-existing values. static int test3(TestClass obj) { TestClass obj1 = TestClass.sTestClassObj; TestClass obj2 = new TestClass(); // Cannot alias with obj or obj1 which pre-exist. obj.next = obj2; // Make obj2 a non-singleton. // All stores below need to stay since obj/obj1/obj2 are not singletons. obj.i = 1; obj1.j = 2; // Following stores won't kill values of obj.i and obj1.j. obj2.i = 3; obj2.j = 4; return obj.i + obj1.j + obj2.i + obj2.j; } /// CHECK-START: int Main.test4(TestClass, boolean) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: Return /// CHECK-START: int Main.test4(TestClass, boolean) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Return /// CHECK-START: int Main.test4(TestClass, boolean) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet /// CHECK-START: int Main.test4(TestClass, boolean) load_store_elimination (after) /// CHECK-NOT: Phi // Set and merge the same value in two branches. static int test4(TestClass obj, boolean b) { if (b) { obj.i = 1; } else { obj.i = 1; } return obj.i; } /// CHECK-START: int Main.test5(TestClass, boolean) load_store_elimination (before) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] /// CHECK-DAG: <> InstanceFieldGet [{{l\d+}}] /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.test5(TestClass, boolean) load_store_elimination (after) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] /// CHECK-DAG: <> Phi [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-EVAL: set(["<>","<>"]) == set(["<>","<>"]) /// CHECK-START: int Main.test5(TestClass, boolean) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet // Set and merge different values in two branches. static int test5(TestClass obj, boolean b) { if (b) { obj.i = 1; } else { obj.i = 2; } return obj.i; } /// CHECK-START: int Main.test6(TestClass, TestClass, boolean) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.test6(TestClass, TestClass, boolean) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.test6(TestClass, TestClass, boolean) load_store_elimination (after) /// CHECK: InstanceFieldGet /// CHECK-NOT: InstanceFieldGet // Setting the same value doesn't clear the value for aliased locations. static int test6(TestClass obj1, TestClass obj2, boolean b) { obj1.i = 1; obj1.j = 2; if (b) { obj2.j = 2; } return obj1.j + obj2.j; } /// CHECK-START: int Main.test7(TestClass) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK-START: int Main.test7(TestClass) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet // Invocation should kill values in non-singleton heap locations. static int test7(TestClass obj) { obj.i = 1; System.out.print(""); return obj.i; } /// CHECK-START: int Main.test8() load_store_elimination (before) /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: InvokeVirtual /// CHECK: InstanceFieldGet /// CHECK-START: int Main.test8() load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldSet /// CHECK: InvokeVirtual /// CHECK-NOT: NullCheck /// CHECK-NOT: InstanceFieldGet // Invocation should not kill values in singleton heap locations. static int test8() { TestClass obj = new TestClass(); obj.i = 1; System.out.print(""); return obj.i; } /// CHECK-START: int Main.test9(TestClass) load_store_elimination (before) /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK-START: int Main.test9(TestClass) load_store_elimination (after) /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet // Invocation should kill values in non-singleton heap locations. static int test9(TestClass obj) { TestClass obj2 = new TestClass(); obj2.i = 1; obj.next = obj2; System.out.print(""); return obj2.i; } /// CHECK-START: int Main.test10(TestClass) load_store_elimination (before) /// CHECK-DAG: StaticFieldGet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: StaticFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.test10(TestClass) load_store_elimination (after) /// CHECK-DAG: StaticFieldGet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: StaticFieldSet /// CHECK-START: int Main.test10(TestClass) load_store_elimination (after) /// CHECK: NullCheck /// CHECK-NOT: NullCheck /// CHECK-START: int Main.test10(TestClass) load_store_elimination (after) /// CHECK: InstanceFieldGet /// CHECK-NOT: InstanceFieldGet // Static fields shouldn't alias with instance fields. static int test10(TestClass obj) { TestClass.si += obj.i; return obj.i; } /// CHECK-START: int Main.test11(TestClass) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK-START: int Main.test11(TestClass) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK-NOT: NullCheck /// CHECK-NOT: InstanceFieldGet // Loop without heap writes. static int test11(TestClass obj) { obj.i = 1; int sum = 0; for (int i = 0; i < 10; i++) { sum += obj.i; } return sum; } /// CHECK-START: int Main.test12(TestClass, TestClass) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK: InstanceFieldSet /// CHECK-START: int Main.test12(TestClass, TestClass) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK: InstanceFieldSet // Loop with heap writes. static int test12(TestClass obj1, TestClass obj2) { obj1.i = 1; int sum = 0; for (int i = 0; i < 10; i++) { sum += obj1.i; obj2.i = sum; } return sum; } /// CHECK-START: int Main.test13(TestClass, TestClass2) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK: InstanceFieldGet /// CHECK-START: int Main.test13(TestClass, TestClass2) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-NOT: NullCheck /// CHECK-NOT: InstanceFieldGet // Different classes shouldn't alias. static int test13(TestClass obj1, TestClass2 obj2) { obj1.i = 1; obj2.i = 2; return obj1.i + obj2.i; } /// CHECK-START: int Main.test14(TestClass, SubTestClass) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK-START: int Main.test14(TestClass, SubTestClass) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet // Subclass may alias with super class. static int test14(TestClass obj1, SubTestClass obj2) { obj1.i = 1; obj2.i = 2; return obj1.i; } /// CHECK-START: int Main.test15() load_store_elimination (before) /// CHECK: StaticFieldSet /// CHECK: StaticFieldSet /// CHECK: StaticFieldGet /// CHECK-START: int Main.test15() load_store_elimination (after) /// CHECK: <> IntConstant 2 /// CHECK: StaticFieldSet /// CHECK: Return [<>] /// CHECK-START: int Main.test15() load_store_elimination (after) /// CHECK-NOT: StaticFieldGet // Static field access from subclass's name. static int test15() { TestClass.si = 1; SubTestClass.si = 2; return TestClass.si; } /// CHECK-START: int Main.test16() load_store_elimination (before) /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK: InstanceFieldGet /// CHECK-START: int Main.test16() load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: InstanceFieldGet // Test inlined constructor. static int test16() { TestClass obj = new TestClass(1, 2); return obj.i + obj.j; } /// CHECK-START: int Main.test17() load_store_elimination (before) /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK-START: int Main.test17() load_store_elimination (after) /// CHECK: <> IntConstant 0 /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: InstanceFieldGet /// CHECK: Return [<>] // Test getting default value. static int test17() { TestClass obj = new TestClass(); obj.j = 1; return obj.i; } /// CHECK-START: int Main.test18(TestClass) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK-START: int Main.test18(TestClass) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet // Volatile field load/store shouldn't be eliminated. static int test18(TestClass obj) { obj.k = 1; return obj.k; } /// CHECK-START: float Main.test19(float[], float[]) load_store_elimination (before) /// CHECK: {{f\d+}} ArrayGet /// CHECK: {{f\d+}} ArrayGet /// CHECK-START: float Main.test19(float[], float[]) load_store_elimination (after) /// CHECK: {{f\d+}} ArrayGet /// CHECK-NOT: {{f\d+}} ArrayGet // I/F, J/D aliasing should not happen any more and LSE should eliminate the load. static float test19(float[] fa1, float[] fa2) { fa1[0] = fa2[0]; return fa1[0]; } /// CHECK-START: TestClass Main.test20() load_store_elimination (before) /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK-START: TestClass Main.test20() load_store_elimination (after) /// CHECK: NewInstance /// CHECK-NOT: InstanceFieldSet // Storing default heap value is redundant if the heap location has the // default heap value. static TestClass test20() { TestClass obj = new TestClass(); obj.i = 0; return obj; } /// CHECK-START: void Main.test21(TestClass) load_store_elimination (before) /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: void Main.test21(TestClass) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-START: void Main.test21(TestClass) load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldGet // Loop side effects can kill heap values, stores need to be kept in that case. static void test21(TestClass obj0) { TestClass obj = new TestClass(); obj0.str = "abc"; obj.str = "abc"; // Note: This loop is transformed by the loop optimization pass, therefore we // are not checking the exact number of InstanceFieldSet and Phi instructions. for (int i = 0; i < 2; i++) { // Generate some loop side effect that writes into obj. obj.str = "def"; } $noinline$printSubstrings00(obj0.str, obj.str); } static void $noinline$printSubstrings00(String str1, String str2) { System.out.print(str1.substring(0, 0) + str2.substring(0, 0)); } /// CHECK-START: int Main.test22() load_store_elimination (before) /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK: InstanceFieldGet /// CHECK-START: int Main.test22() load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: InstanceFieldGet // For a singleton, loop side effects can kill its field values only if: // (1) it dominiates the loop header, and // (2) its fields are stored into inside a loop. static int test22() { int sum = 0; TestClass obj1 = new TestClass(); obj1.i = 2; // This store can be eliminated since obj1 is never stored into inside a loop. for (int i = 0; i < 2; i++) { TestClass obj2 = new TestClass(); obj2.i = 3; // This store can be eliminated since the singleton is inside the loop. sum += obj2.i; } TestClass obj3 = new TestClass(); obj3.i = 5; // This store can be eliminated since the singleton is created after the loop. sum += obj1.i + obj3.i; return sum; } /// CHECK-START: int Main.test23(boolean) load_store_elimination (before) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> NewInstance /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: <> InstanceFieldGet [<>] /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: <> InstanceFieldGet [<>] /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-DAG: <> InstanceFieldGet [<>] /// CHECK-START: int Main.test23(boolean) load_store_elimination (after) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: <> Phi [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-EVAL: set(["<>","<>"]) == set(["<>","<>"]) /// CHECK-START: int Main.test23(boolean) load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: InstanceFieldGet // Test heap value merging from multiple branches. static int test23(boolean b) { TestClass obj = new TestClass(); obj.i = 3; // This store can be eliminated since the value flows into each branch. if (b) { obj.i += 1; // This store can be eliminated after replacing the load below with a Phi. } else { obj.i += 2; // This store can be eliminated after replacing the load below with a Phi. } return obj.i; // This load is eliminated by creating a Phi. } /// CHECK-START: float Main.test24() load_store_elimination (before) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> FloatConstant 8 /// CHECK-DAG: <> FloatConstant 42 /// CHECK-DAG: <> NewInstance /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: <> InstanceFieldGet [<>] /// CHECK-DAG: <> InstanceFieldGet [<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-START: float Main.test24() load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldGet static float test24() { float a = 42.0f; TestClass3 obj = new TestClass3(); if (obj.test1) { a = obj.floatField; } return a; } /// CHECK-START: int Main.test25(boolean, boolean, boolean) load_store_elimination (before) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> IntConstant 5 /// CHECK-DAG: <> IntConstant 6 /// CHECK-DAG: <> NewInstance /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: <> InstanceFieldGet [<>] /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.test25(boolean, boolean, boolean) load_store_elimination (after) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> IntConstant 5 /// CHECK-DAG: <> IntConstant 6 /// CHECK-DAG: <> Phi [<>,<>,<>,<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-EVAL: set(["<>","<>","<>","<>","<>"]) == set(["<>","<>","<>","<>","<>"]) /// CHECK-START: int Main.test25(boolean, boolean, boolean) load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: InstanceFieldGet // Test heap value merging from nested branches. static int test25(boolean b, boolean c, boolean d) { TestClass obj = new TestClass(); if (b) { if (c) { obj.i = 1; } else { if (d) { obj.i = 2; } else { obj.i = 3; } } } else { if (c) { obj.i = 5; } else { obj.i = 6; } } return obj.i; } /// CHECK-START: float Main.test26(int) load_store_elimination (before) /// CHECK-DAG: <> FloatConstant 0 /// CHECK-DAG: <> FloatConstant 1 /// CHECK-DAG: <> FloatConstant 2 /// CHECK-DAG: <> FloatConstant 3 /// CHECK-DAG: <> FloatConstant 8 /// CHECK-DAG: <> NewInstance /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: <> InstanceFieldGet [<>] /// CHECK-DAG: Return [<>] /// CHECK-START: float Main.test26(int) load_store_elimination (after) /// CHECK-DAG: <> FloatConstant 0 /// CHECK-DAG: <> FloatConstant 1 /// CHECK-DAG: <> FloatConstant 2 /// CHECK-DAG: <> FloatConstant 3 /// CHECK-DAG: <> FloatConstant 8 /// CHECK-DAG: <> Phi [<>,<>,<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-EVAL: set(["<>","<>","<>","<>"]) == set(["<>","<>","<>","<>"]) /// CHECK-START: float Main.test26(int) load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: InstanceFieldGet // Test heap value merging from switch statement. static float test26(int b) { TestClass3 obj = new TestClass3(); switch (b) { case 1: obj.floatField = 3.0f; break; case 2: obj.floatField = 2.0f; break; case 3: obj.floatField = 1.0f; break; default: obj.floatField = 0.0f; break; } return obj.floatField; } /// CHECK-START: int Main.test27(boolean, boolean) load_store_elimination (before) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> NewInstance /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: <> InstanceFieldGet [<>] /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.test27(boolean, boolean) load_store_elimination (after) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.test27(boolean, boolean) load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: InstanceFieldGet /// CHECK-NOT: Phi // Test merging same value from nested branches. static int test27(boolean b, boolean c) { TestClass obj = new TestClass(); if (b) { if (c) { obj.i = 1; } else { obj.i = 1; } } else { if (c) { obj.i = 1; } else { obj.i = 1; } } return obj.i; } /// CHECK-START: int Main.test28(boolean, boolean) load_store_elimination (before) /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> IntConstant 5 /// CHECK-DAG: <> IntConstant 6 /// CHECK-DAG: <> NewArray /// CHECK-DAG: ArraySet [<>,<>,<>] /// CHECK-DAG: ArraySet [<>,<>,<>] /// CHECK-DAG: <> ArrayGet [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.test28(boolean, boolean) load_store_elimination (after) /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> IntConstant 5 /// CHECK-DAG: <> IntConstant 6 /// CHECK-DAG: <> Phi [<>,<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-EVAL: set(["<>","<>","<>"]) == set(["<>","<>","<>"]) /// CHECK-START: int Main.test28(boolean, boolean) load_store_elimination (after) /// CHECK-NOT: NewArray /// CHECK-NOT: ArraySet /// CHECK-NOT: ArrayGet // Test merging array stores in branches. static int test28(boolean b, boolean c) { int[] array = new int[1]; if (b) { if (c) { array[0] = 5; } else { array[0] = 6; } } else { /* Default value: 0. */ } return array[0]; } /// CHECK-START: float Main.test29(boolean) load_store_elimination (before) /// CHECK-DAG: <> FloatConstant 2 /// CHECK-DAG: <> FloatConstant 5 /// CHECK-DAG: <> FloatConstant 8 /// CHECK-DAG: <> NewInstance /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: InstanceFieldSet [<>,<>] /// CHECK-DAG: <> InstanceFieldGet [<>] /// CHECK-DAG: Return [<>] /// CHECK-START: float Main.test29(boolean) load_store_elimination (after) /// CHECK-DAG: <> FloatConstant 2 /// CHECK-DAG: <> FloatConstant 5 /// CHECK-DAG: <> FloatConstant 8 /// CHECK-DAG: <> Phi [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-EVAL: set(["<>","<>"]) == set(["<>","<>"]) /// CHECK-START: float Main.test29(boolean) load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: InstanceFieldGet // Test implicit type conversion in branches. static float test29(boolean b) { TestClass3 obj = new TestClass3(); if (b) { obj.floatField = 5; // Int } else { obj.floatField = 2L; // Long } return obj.floatField; } /// CHECK-START: int Main.test30(TestClass, boolean) load_store_elimination (before) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] /// CHECK-DAG: <> InstanceFieldGet [{{l\d+}}] /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.test30(TestClass, boolean) load_store_elimination (after) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] /// CHECK-DAG: <> InstanceFieldGet [{{l\d+}}] /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.test30(TestClass, boolean) load_store_elimination (after) /// CHECK-NOT: Phi // Don't merge different values in two branches for different variables. static int test30(TestClass obj, boolean b) { if (b) { obj.i = 1; } else { obj.j = 2; } return obj.i; } /// CHECK-START: int Main.test31(boolean, boolean) load_store_elimination (before) /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> IntConstant 5 /// CHECK-DAG: <> IntConstant 6 /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] field_name:{{.*TestClass.i}} /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] field_name:{{.*TestClass.i}} /// CHECK-DAG: <> InstanceFieldGet [{{l\d+}}] field_name:{{.*TestClass.i}} /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] field_name:{{.*TestClass.j}} /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] field_name:{{.*TestClass.i}} /// CHECK-DAG: <> InstanceFieldGet [{{l\d+}}] /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.test31(boolean, boolean) load_store_elimination (after) /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> IntConstant 5 /// CHECK-DAG: <> IntConstant 6 /// CHECK-DAG: <> Phi [<>,<>] /// CHECK-DAG: <> Phi [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.test31(boolean, boolean) load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: InstanceFieldGet // Test nested branches that can't be flattened. static int test31(boolean b, boolean c) { TestClass obj = new TestClass(); if (b) { if (c) { obj.i = 5; } else { obj.i = 6; } obj.j = obj.i; } else { obj.i = 2; } return obj.i; } /// CHECK-START: int Main.test32(int) load_store_elimination (before) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 10 /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] field_name:{{.*TestClass2.i}} /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] field_name:{{.*TestClass2.j}} /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] field_name:{{.*TestClass2.k}} /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] field_name:{{.*TestClass2.l}} /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] field_name:{{.*TestClass2.m}} /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.test32(int) load_store_elimination (after) /// CHECK-DAG: <> IntConstant 10 /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.test32(int) load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldGet /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: Phi // Test no unused Phi instructions are created. static int test32(int i) { TestClass2 obj = new TestClass2(); // By default, i/j/k/l/m are initialized to 0. switch (i) { case 1: obj.i = 1; break; case 2: obj.j = 1; break; case 3: obj.k = 1; break; case 4: obj.l = 1; break; case 5: obj.m = 1; break; } // So here, each variable has value Phi [0,1,1,1,1,1]. // But since no heap values are used, we should not be creating these Phis. return 10; } /// CHECK-START: int Main.test33(TestClass, boolean) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: <> Phi /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] /// CHECK-START: int Main.test33(TestClass, boolean) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: <> Phi /// CHECK-START: int Main.test33(TestClass, boolean) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet // Test that we are not eliminating the if/else sets to `obj.i`. We have `NullCheck`s on `obj` // when doing `obj.i`. Since `NullCheck` can throw, we save the stores. // The 3rd `obj.i` set is redundant and can be eliminated. It will have the same value and it is // not needed. static int test33(TestClass obj, boolean x) { int phi; if (x) { obj.i = 1; phi = 1; } else { obj.i = 2; phi = 2; } obj.i = phi; return phi; } /// CHECK-START: int Main.test34(TestClass, boolean, boolean) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: <> Phi /// CHECK-DAG: InstanceFieldSet [{{l\d+}},<>] /// CHECK-START: int Main.test34(TestClass, boolean, boolean) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-START: int Main.test34(TestClass, boolean, boolean) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet // Test eliminating a store that writes a Phi equivalent to merged // heap values of observable stores. static int test34(TestClass obj, boolean x, boolean y) { int phi; if (x) { obj.i = 1; phi = 1; if (y) { return 3; } } else { obj.i = 2; phi = 2; if (y) { return 4; } } obj.i = phi; return phi; } /// CHECK-START: int Main.test35(TestClass, boolean, boolean) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.test35(TestClass, boolean, boolean) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-START: int Main.test35(TestClass, boolean, boolean) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet // Test Phi creation for load elimination. static int test35(TestClass obj, boolean x, boolean y) { if (x) { obj.i = 1; } else { obj.i = 2; } if (y) { if (x) { obj.i = 3; } obj.j = 5; } return obj.i; } /// CHECK-START: int Main.test36(TestClass, boolean) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.test36(TestClass, boolean) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-START: int Main.test36(TestClass, boolean) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet /// CHECK-START: int Main.test36(TestClass, boolean) load_store_elimination (after) /// CHECK: Phi /// CHECK-NOT: Phi // Test Phi matching for load elimination. static int test36(TestClass obj, boolean x) { int phi; if (x) { obj.i = 1; phi = 1; } else { obj.i = 2; phi = 2; } // The load is replaced by the existing Phi instead of constructing a new one. return obj.i + phi; } /// CHECK-START: int Main.test37(TestClass, boolean) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.test37(TestClass, boolean) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet // Test preserving observable stores. static int test37(TestClass obj, boolean x) { if (x) { obj.i = 1; } int tmp = obj.i; // The store above must be kept. obj.i = 2; return tmp; } /// CHECK-START: int Main.test38(TestClass, boolean) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.test38(TestClass, boolean) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet // Test eliminating store of the same value after eliminating non-observable stores. static int test38(TestClass obj, boolean x) { obj.i = 1; if (x) { return 1; // The store above must be kept. } obj.i = 2; // Not observable, shall be eliminated. obj.i = 3; // Not observable, shall be eliminated. obj.i = 1; // After eliminating the non-observable stores above, this stores the // same value that is already stored in `obj.i` and shall be eliminated. return 2; } /// CHECK-START: int Main.test39(TestClass, boolean) load_store_elimination (before) /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.test39(TestClass, boolean) load_store_elimination (after) /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.test39(TestClass, boolean) load_store_elimination (after) /// CHECK: InstanceFieldGet /// CHECK-NOT: InstanceFieldGet // Test creating a reference Phi for load elimination. static int test39(TestClass obj, boolean x) { obj.next = new TestClass(1, 2); if (x) { obj.next = new SubTestClass(); } return obj.next.i; } private static int test40() { int[] array = new int[1]; try { $noinline$fillArrayTest40(array, 100, 0); System.out.println("UNREACHABLE"); } catch (Throwable expected) { } assertIntEquals(array[0], 1); try { $noinline$fillArrayTest40(array, 100, 1); System.out.println("UNREACHABLE"); } catch (Throwable expected) { } assertIntEquals(array[0], 2); $noinline$fillArrayTest40(array, 100, 2); assertIntEquals(array[0], 150); return array[0]; } /// CHECK-START: void Main.$noinline$fillArrayTest40(int[], int, int) load_store_elimination (before) /// CHECK: ArraySet /// CHECK: DivZeroCheck /// CHECK: ArraySet /// CHECK: DivZeroCheck /// CHECK: ArraySet /// CHECK-START: void Main.$noinline$fillArrayTest40(int[], int, int) load_store_elimination (after) /// CHECK: ArraySet /// CHECK: DivZeroCheck /// CHECK: ArraySet /// CHECK: DivZeroCheck /// CHECK: ArraySet // Check that the stores to array[0] are not eliminated since we can throw in between the stores. private static void $noinline$fillArrayTest40(int[] array, int a, int b) { array[0] = 1; int x = a / b; array[0] = 2; int y = a / (b - 1); array[0] = x + y; } /// CHECK-START: int Main.$noinline$testConversion1(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.$noinline$testConversion1(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: TypeConversion /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-START: int Main.$noinline$testConversion1(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK-NOT: Phi // Test tracking values containing type conversion. // Regression test for b/161521389 . static int $noinline$testConversion1(TestClass obj, int x) { obj.i = x; if ((x & 1) != 0) { obj.b = (byte) x; obj.i = obj.b; } return obj.i; } /// CHECK-START: int Main.$noinline$testConversion2(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: TypeConversion /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.$noinline$testConversion2(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: TypeConversion /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-START: int Main.$noinline$testConversion2(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK: Phi /// CHECK-NOT: Phi /// CHECK-START: int Main.$noinline$testConversion2(TestClass, int) load_store_elimination (after) /// CHECK: TypeConversion /// CHECK-NOT: TypeConversion /// CHECK-START: int Main.$noinline$testConversion2(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet // Test moving type conversion when needed. static int $noinline$testConversion2(TestClass obj, int x) { int tmp = 0; obj.i = x; if ((x & 1) != 0) { // The instruction simplifier can remove this TypeConversion if there are // no environment uses. Currently, there is an environment use in NullCheck, // so this TypeConversion remains and GVN removes the second TypeConversion // below. Since we really want to test that the TypeConversion from below // can be moved and used for the load of `obj.b`, we have a similar test // written in smali in 530-checker-lse3, StoreLoad.test3(int), except that // it's using static fields (which would not help with the environment use). obj.b = (byte) x; obj.i = obj.b; tmp = (byte) x; } return obj.i + tmp; } /// CHECK-START: int Main.$noinline$testConversion3(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.$noinline$testConversion3(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: TypeConversion /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.$noinline$testConversion3(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK: Phi /// CHECK-NOT: Phi /// CHECK-START: int Main.$noinline$testConversion3(TestClass, int) load_store_elimination (after) /// CHECK: TypeConversion /// CHECK-NOT: TypeConversion /// CHECK-START: int Main.$noinline$testConversion3(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet // Test tracking values containing type conversion with loop. static int $noinline$testConversion3(TestClass obj, int x) { obj.i = x; for (int i = 0; i < x; ++i) { obj.b = (byte) i; obj.i = obj.b; } return obj.i; } /// CHECK-START: int Main.$noinline$testConversion4(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: TypeConversion /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.$noinline$testConversion4(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: TypeConversion /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.$noinline$testConversion4(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK: Phi /// CHECK-NOT: Phi /// CHECK-START: int Main.$noinline$testConversion4(TestClass, int) load_store_elimination (after) /// CHECK: TypeConversion /// CHECK-NOT: TypeConversion /// CHECK-START: int Main.$noinline$testConversion4(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet // Test moving type conversion when needed with loop. static int $noinline$testConversion4(TestClass obj, int x) { int tmp = x; obj.i = x; for (int i = 0; i < x; ++i) { obj.b = (byte) i; obj.i = obj.b; tmp = (byte) i; } return obj.i + tmp; } /// CHECK-START: void Main.testFinalizable() load_store_elimination (before) /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-START: void Main.testFinalizable() load_store_elimination (after) /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet // Allocations of finalizable objects cannot be eliminated. static void testFinalizable() { Finalizable finalizable = new Finalizable(); finalizable.i = Finalizable.VALUE2; finalizable.i = Finalizable.VALUE1; } static java.lang.ref.WeakReference getWeakReference() { return new java.lang.ref.WeakReference<>(new Object()); } static void testFinalizableByForcingGc() { testFinalizable(); java.lang.ref.WeakReference reference = getWeakReference(); Runtime runtime = Runtime.getRuntime(); for (int i = 0; i < 20; ++i) { runtime.gc(); System.runFinalization(); try { Thread.sleep(1); } catch (InterruptedException e) { throw new AssertionError(e); } // Check to see if the weak reference has been garbage collected. if (reference.get() == null) { // A little bit more sleep time to make sure. try { Thread.sleep(100); } catch (InterruptedException e) { throw new AssertionError(e); } if (!Finalizable.sVisited) { System.out.println("finalize() not called."); } return; } } System.out.println("testFinalizableByForcingGc() failed to force gc."); } /// CHECK-START: void Main.testFinalizable() load_store_elimination (before) /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-START: void Main.testFinalizableWithLoop() load_store_elimination (after) /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet // Allocations of finalizable objects cannot be eliminated. static void testFinalizableWithLoop() { for (int i = 0; i < 1000; ++i) { Finalizable finalizable = new Finalizable(); finalizable.i = Finalizable.VALUE2; finalizable.i = Finalizable.VALUE1; } } static void testFinalizableWithLoopByForcingGc() { testFinalizableWithLoop(); java.lang.ref.WeakReference reference = getWeakReference(); Runtime runtime = Runtime.getRuntime(); for (int i = 0; i < 20; ++i) { runtime.gc(); System.runFinalization(); try { Thread.sleep(1); } catch (InterruptedException e) { throw new AssertionError(e); } // Check to see if the weak reference has been garbage collected. if (reference.get() == null) { // A little bit more sleep time to make sure. try { Thread.sleep(100); } catch (InterruptedException e) { throw new AssertionError(e); } if (!Finalizable.sVisited) { System.out.println("finalize() not called."); } return; } } System.out.println("testFinalizableWithLoopByForcingGc() failed to force gc."); } /// CHECK-START: int Main.$noinline$testHSelect(boolean) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: Select /// CHECK-START: int Main.$noinline$testHSelect(boolean) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: Select // Test that HSelect creates alias. static int $noinline$testHSelect(boolean b) { TestClass obj = new TestClass(); TestClass obj2 = null; obj.i = 0xdead; if (b) { obj2 = obj; } return obj2.i; } static int sumWithFilter(int[] array, Filter f) { int sum = 0; for (int i = 0; i < array.length; i++) { if (f.isValid(array[i])) { sum += array[i]; } } return sum; } /// CHECK-START: int Main.sumWithinRange(int[], int, int) load_store_elimination (before) /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.sumWithinRange(int[], int, int) load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: InstanceFieldGet // A lambda-style allocation can be eliminated after inlining. static int sumWithinRange(int[] array, final int low, final int high) { Filter filter = new Filter() { public boolean isValid(int i) { return (i >= low) && (i <= high); } }; return sumWithFilter(array, filter); } private static int mI = 0; private static float mF = 0f; /// CHECK-START: float Main.testAllocationEliminationWithLoops() load_store_elimination (before) /// CHECK: NewInstance /// CHECK: NewInstance /// CHECK: NewInstance /// CHECK-START: float Main.testAllocationEliminationWithLoops() load_store_elimination (after) /// CHECK-NOT: NewInstance private static float testAllocationEliminationWithLoops() { for (int i0 = 0; i0 < 5; i0++) { for (int i1 = 0; i1 < 5; i1++) { for (int i2 = 0; i2 < 5; i2++) { int lI0 = ((int) new Integer(((int) new Integer(mI)))); if (((boolean) new Boolean(false))) { for (int i3 = 576 - 1; i3 >= 0; i3--) { mF -= 976981405.0f; } } } } } return 1.0f; } /// CHECK-START: TestClass2 Main.testStoreStore() load_store_elimination (before) /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-START: TestClass2 Main.testStoreStore() load_store_elimination (after) /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet private static TestClass2 testStoreStore() { TestClass2 obj = new TestClass2(); obj.i = 41; obj.j = 42; obj.i = 41; obj.j = 43; return obj; } /// CHECK-START: void Main.testStoreStore2(TestClass2) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-START: void Main.testStoreStore2(TestClass2) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet private static void testStoreStore2(TestClass2 obj) { obj.i = 41; obj.j = 42; obj.i = 43; obj.j = 44; } /// CHECK-START: void Main.testStoreStore3(TestClass2, boolean) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-START: void Main.testStoreStore3(TestClass2, boolean) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet /// CHECK-START: void Main.testStoreStore3(TestClass2, boolean) load_store_elimination (after) /// CHECK-NOT: Phi private static void testStoreStore3(TestClass2 obj, boolean flag) { obj.i = 41; obj.j = 42; // redundant since it's overwritten in both branches below. if (flag) { obj.j = 43; } else { obj.j = 44; } } /// CHECK-START: void Main.testStoreStore4() load_store_elimination (before) /// CHECK: StaticFieldSet /// CHECK: StaticFieldSet /// CHECK-START: void Main.testStoreStore4() load_store_elimination (after) /// CHECK: StaticFieldSet /// CHECK-NOT: StaticFieldSet private static void testStoreStore4() { TestClass.si = 61; TestClass.si = 62; } /// CHECK-START: int Main.testStoreStore5(TestClass2, TestClass2) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK: InstanceFieldSet /// CHECK-START: int Main.testStoreStore5(TestClass2, TestClass2) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK: InstanceFieldSet private static int testStoreStore5(TestClass2 obj1, TestClass2 obj2) { obj1.i = 71; // This store is needed since obj2.i may load from it. int i = obj2.i; obj1.i = 72; return i; } /// CHECK-START: int Main.testStoreStore6(TestClass2, TestClass2) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK: InstanceFieldSet /// CHECK-START: int Main.testStoreStore6(TestClass2, TestClass2) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldGet /// CHECK: InstanceFieldSet private static int testStoreStore6(TestClass2 obj1, TestClass2 obj2) { obj1.i = 81; // Even though the value in `obj1.i` will be overridden below, this store is needed // since obj2.j has a NullCheck and can throw. int j = obj2.j; obj1.i = 82; return j; } /// CHECK-START: int Main.testNoSideEffects(int[]) load_store_elimination (before) /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK: ArrayGet /// CHECK-START: int Main.testNoSideEffects(int[]) load_store_elimination (after) /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK-NOT: ArraySet /// CHECK-NOT: ArrayGet private static int testNoSideEffects(int[] array) { array[0] = 101; array[1] = 102; int bitCount = Integer.bitCount(0x3456); array[1] = 103; return array[0] + bitCount; } /// CHECK-START: void Main.testThrow(TestClass2, java.lang.Exception) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: Throw /// CHECK-START: void Main.testThrow(TestClass2, java.lang.Exception) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: Throw // Make sure throw keeps the store. private static void testThrow(TestClass2 obj, Exception e) throws Exception { obj.i = 55; throw e; } /// CHECK-START: int Main.testStoreStoreWithDeoptimize(int[]) load_store_elimination (before) /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: Deoptimize /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK: ArrayGet /// CHECK: ArrayGet /// CHECK: ArrayGet /// CHECK: ArrayGet /// CHECK-START: int Main.testStoreStoreWithDeoptimize(int[]) load_store_elimination (after) /// CHECK: NewInstance /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet /// CHECK: Deoptimize /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK-NOT: ArrayGet private static int testStoreStoreWithDeoptimize(int[] arr) { TestClass2 obj = new TestClass2(); obj.i = 41; obj.j = 42; obj.i = 41; obj.j = 43; arr[0] = 1; // One HDeoptimize here. arr[1] = 1; arr[2] = 1; arr[3] = 1; return arr[0] + arr[1] + arr[2] + arr[3]; } /// CHECK-START: double Main.getCircleArea(double, boolean) load_store_elimination (before) /// CHECK: NewInstance /// CHECK-START: double Main.getCircleArea(double, boolean) load_store_elimination (after) /// CHECK-NOT: NewInstance private static double getCircleArea(double radius, boolean b) { double area = 0d; if (b) { area = new Circle(radius).getArea(); } return area; } /// CHECK-START: double Main.testDeoptimize(int[], double[], double) load_store_elimination (before) /// CHECK: Deoptimize /// CHECK: NewInstance /// CHECK: Deoptimize /// CHECK: NewInstance /// CHECK-START: double Main.testDeoptimize(int[], double[], double) load_store_elimination (after) /// CHECK: Deoptimize /// CHECK: NewInstance /// CHECK: Deoptimize /// CHECK-NOT: NewInstance private static double testDeoptimize(int[] iarr, double[] darr, double radius) { iarr[0] = 1; // One HDeoptimize here. Not triggered. iarr[1] = 1; Circle circle1 = new Circle(radius); iarr[2] = 1; darr[0] = circle1.getRadius(); // One HDeoptimize here, which holds circle1 live. Triggered. darr[1] = circle1.getRadius(); darr[2] = circle1.getRadius(); darr[3] = circle1.getRadius(); return new Circle(Math.PI).getArea(); } /// CHECK-START: int Main.testAllocationEliminationOfArray1() load_store_elimination (before) /// CHECK: NewArray /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK: ArrayGet /// CHECK: ArrayGet /// CHECK: ArrayGet /// CHECK: ArrayGet /// CHECK-START: int Main.testAllocationEliminationOfArray1() load_store_elimination (after) /// CHECK-NOT: NewArray /// CHECK-NOT: ArraySet /// CHECK-NOT: ArrayGet private static int testAllocationEliminationOfArray1() { int[] array = new int[4]; array[2] = 4; array[3] = 7; return array[0] + array[1] + array[2] + array[3]; } /// CHECK-START: int Main.testAllocationEliminationOfArray2() load_store_elimination (before) /// CHECK: NewArray /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK: ArrayGet /// CHECK-START-{ARM64,X86,X86_64}: int Main.testAllocationEliminationOfArray2() load_store_elimination (after) /// CHECK-NOT: NewArray /// CHECK-NOT: ArraySet /// CHECK-NOT: ArraySet /// CHECK-NOT: ArrayGet // The loop optimization doesn't happen in ARM which leads to LSE not being able to optimize this // case. /// CHECK-START-ARM: int Main.testAllocationEliminationOfArray2() load_store_elimination (after) /// CHECK: NewArray /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK: ArrayGet private static int testAllocationEliminationOfArray2() { // Array can be eliminated because LSE can reduce the array accesses into // integer constants. int[] array = new int[3]; array[1] = 4; array[2] = 7; int sum = 0; for (int e : array) { sum += e; } return sum; } /// CHECK-START: int Main.testAllocationEliminationOfArray3(int) load_store_elimination (before) /// CHECK: NewArray /// CHECK: ArraySet /// CHECK: ArrayGet /// CHECK-START: int Main.testAllocationEliminationOfArray3(int) load_store_elimination (after) /// CHECK-NOT: NewArray /// CHECK-NOT: ArraySet /// CHECK-NOT: ArrayGet private static int testAllocationEliminationOfArray3(int i) { int[] array = new int[4]; array[i] = 4; return array[i]; } /// CHECK-START: int Main.testAllocationEliminationOfArray4(int) load_store_elimination (before) /// CHECK: NewArray /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK: ArrayGet /// CHECK: ArrayGet /// CHECK-START: int Main.testAllocationEliminationOfArray4(int) load_store_elimination (after) /// CHECK: NewArray /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK: ArrayGet /// CHECK-NOT: ArrayGet private static int testAllocationEliminationOfArray4(int i) { // Cannot eliminate array allocation due to index aliasing between 1 and i. int[] array = new int[4]; array[1] = 2; array[i] = 4; return array[1] + array[i]; } /// CHECK-START: int Main.testAllocationEliminationOfArray5(int) load_store_elimination (before) /// CHECK: NewArray /// CHECK: ArraySet /// CHECK: ArrayGet /// CHECK-START: int Main.testAllocationEliminationOfArray5(int) load_store_elimination (after) /// CHECK: NewArray /// CHECK-NOT: ArraySet /// CHECK-NOT: ArrayGet private static int testAllocationEliminationOfArray5(int i) { // Cannot eliminate array allocation due to unknown i that may // cause NegativeArraySizeException. int[] array = new int[i]; array[1] = 12; return array[1]; } /// CHECK-START: int Main.testAllocationEliminationOfArray6(boolean) load_store_elimination (before) /// CHECK: NewArray /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK: ArrayGet /// CHECK-START: int Main.testAllocationEliminationOfArray6(boolean) load_store_elimination (after) /// CHECK: NewArray /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK: ArrayGet private static int testAllocationEliminationOfArray6(boolean prevent_loop_opt) { // Cannot eliminate array allocation since array is accessed with non-constant // index (only 3 elements to prevent vectorization of the reduction). int[] array = new int[3]; array[1] = 4; array[2] = 7; int sum = 0; for (int e : array) { sum += e; // Prevent the loop from being optimized away before LSE. This should // never be false. if (!prevent_loop_opt) { return -1; } } return sum; } /// CHECK-START: int Main.testExitMerge(boolean) load_store_elimination (before) /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet field_name:TestClass.i /// CHECK-DAG: InstanceFieldGet field_name:TestClass.i /// CHECK-DAG: Return /// CHECK-DAG: InstanceFieldSet field_name:TestClass.i /// CHECK-DAG: Throw /// CHECK-START: int Main.testExitMerge(boolean) load_store_elimination (after) /// CHECK-DAG: Return /// CHECK-DAG: Throw /// CHECK-START: int Main.testExitMerge(boolean) load_store_elimination (after) /// CHECK-NOT: InstanceFieldSet field_name:TestClass.i /// CHECK-NOT: InstanceFieldGet field_name:TestClass.i /// CHECK-START: int Main.testExitMerge(boolean) load_store_elimination (after) /// CHECK: NewInstance /// CHECK-NOT: NewInstance private static int testExitMerge(boolean cond) { TestClass obj = new TestClass(); if (cond) { obj.i = 1; return obj.i + 1; } else { obj.i = 2; throw new Error(); // Note: We have a NewInstance here. } } /// CHECK-START: int Main.testExitMerge2(boolean) load_store_elimination (before) /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testExitMerge2(boolean) load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: InstanceFieldGet private static int testExitMerge2(boolean cond) { TestClass obj = new TestClass(); int res; if (cond) { obj.i = 1; res = obj.i + 1; } else { obj.i = 2; res = obj.j + 2; } return res; } /// CHECK-START: void Main.testStoreSameValue() load_store_elimination (before) /// CHECK: NewArray /// CHECK: ArrayGet /// CHECK: ArraySet /// CHECK-START: void Main.testStoreSameValue() load_store_elimination (after) /// CHECK: NewArray /// CHECK-NOT: ArrayGet /// CHECK-NOT: ArraySet private static void testStoreSameValue() { Object[] array = new Object[2]; sArray = array; Object obj = array[0]; array[1] = obj; // Store the same value as the default value. } /// CHECK-START: int Main.$noinline$testByteArrayDefaultValue() load_store_elimination (before) /// CHECK-DAG: NewArray /// CHECK-DAG: <> ArrayGet /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.$noinline$testByteArrayDefaultValue() load_store_elimination (after) /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.$noinline$testByteArrayDefaultValue() load_store_elimination (after) /// CHECK-NOT: NewArray /// CHECK-NOT: ArrayGet /// CHECK-NOT: TypeConversion private static int $noinline$testByteArrayDefaultValue() { byte[] array = new byte[2]; array[1] = 1; // FIXME: Without any stores, LSA tells LSE not to run. return array[0]; } static Object[] sArray; /// CHECK-START: int Main.testLocalArrayMerge1(boolean) load_store_elimination (before) /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> NewArray /// CHECK-DAG: ArraySet [<>,<>,<>] /// CHECK-DAG: ArraySet [<>,<>,<>] /// CHECK-DAG: ArraySet [<>,<>,<>] /// CHECK-DAG: <> ArrayGet [<>,<>] /// CHECK-DAG: Return [<>] // /// CHECK-START: int Main.testLocalArrayMerge1(boolean) load_store_elimination (after) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: Return [<>] // /// CHECK-START: int Main.testLocalArrayMerge1(boolean) load_store_elimination (after) /// CHECK-NOT: NewArray /// CHECK-NOT: ArraySet /// CHECK-NOT: ArrayGet private static int testLocalArrayMerge1(boolean x) { // The explicit store can be removed right away // since it is equivalent to the default. int[] a = { 0 }; // The diamond pattern stores/load can be replaced // by the direct value. if (x) { a[0] = 1; } else { a[0] = 1; } return a[0]; } /// CHECK-START: int Main.testLocalArrayMerge2(boolean) load_store_elimination (before) /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> NewArray /// CHECK-DAG: ArraySet [<>,<>,<>] /// CHECK-DAG: ArraySet [<>,<>,<>] /// CHECK-DAG: ArraySet [<>,<>,<>] /// CHECK-DAG: <> ArrayGet [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.testLocalArrayMerge2(boolean) load_store_elimination (after) /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> Phi [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-EVAL: set(["<>","<>"]) == set(["<>","<>"]) /// CHECK-START: int Main.testLocalArrayMerge2(boolean) load_store_elimination (after) /// CHECK-NOT: NewArray /// CHECK-NOT: ArraySet /// CHECK-NOT: ArrayGet private static int testLocalArrayMerge2(boolean x) { // The explicit store can be removed eventually even // though it is not equivalent to the default. int[] a = { 1 }; // The load after the diamond pattern is eliminated and replaced with a Phi, // stores are then also eliminated. if (x) { a[0] = 2; } else { a[0] = 3; } return a[0]; } /// CHECK-START: int Main.testLocalArrayMerge3(boolean) load_store_elimination (before) /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> NewArray /// CHECK-DAG: ArraySet [<>,<>,<>] /// CHECK-DAG: ArraySet [<>,<>,<>] /// CHECK-DAG: <> ArrayGet [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.testLocalArrayMerge3(boolean) load_store_elimination (after) /// CHECK-NOT: NewArray /// CHECK-NOT: ArraySet /// CHECK-NOT: ArrayGet private static int testLocalArrayMerge3(boolean x) { int[] a = { 1 }; if (x) { a[0] = 2; } return a[0]; } /// CHECK-START: int Main.testLocalArrayMerge4(boolean) load_store_elimination (before) /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> NewArray /// CHECK-DAG: ArraySet [<>,<>,<>] /// CHECK-DAG: ArraySet [<>,<>,<>] /// CHECK-DAG: ArraySet [<>,<>,<>] /// CHECK-DAG: <> ArrayGet [<>,<>] /// CHECK-DAG: <> ArrayGet [<>,<>] /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: Return [<>] // /// CHECK-START: int Main.testLocalArrayMerge4(boolean) load_store_elimination (after) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: Return [<>] // /// CHECK-START: int Main.testLocalArrayMerge4(boolean) load_store_elimination (after) /// CHECK-NOT: NewArray /// CHECK-NOT: ArraySet /// CHECK-NOT: ArrayGet private static int testLocalArrayMerge4(boolean x) { byte[] a = { 0 }; if (x) { a[0] = 1; } else { a[0] = 1; } // Differently typed (signed vs unsigned), // but same reference. return a[0] + (a[0] & 0xff); } /// CHECK-START: int Main.testLocalArrayMerge5(int[], boolean) load_store_elimination (before) /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK-START: int Main.testLocalArrayMerge5(int[], boolean) load_store_elimination (after) /// CHECK-NOT: ArraySet // Test eliminating store of the same value after eliminating non-observable stores. private static int testLocalArrayMerge5(int[] a, boolean x) { int old = a[0]; if (x) { a[0] = 1; } else { a[0] = 1; } // This store makes the stores above dead and they will be eliminated. // That makes this store unnecessary as we're storing the same value already // present in this location, so it shall also be eliminated. a[0] = old; return old; } /// CHECK-START: int Main.testLocalArrayMerge6(int[], boolean, boolean) load_store_elimination (before) /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-START: int Main.testLocalArrayMerge6(int[], boolean, boolean) load_store_elimination (after) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-DAG: <> Phi [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-DAG: <> Sub [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-EVAL: set(["<>","<>"]) == set(["<>","<>"]) /// CHECK-START: int Main.testLocalArrayMerge6(int[], boolean, boolean) load_store_elimination (after) /// CHECK: Phi /// CHECK-NOT: Phi /// CHECK-START: int Main.testLocalArrayMerge6(int[], boolean, boolean) load_store_elimination (after) /// CHECK-NOT: ArrayGet // Test that we create a single Phi for eliminating two loads in different blocks. private static int testLocalArrayMerge6(int[] a, boolean x, boolean y) { a[0] = 0; if (x) { a[0] = 1; } else { a[0] = 2; } // Phi for load elimination is created here. if (y) { return a[0]; } else { return 3 - a[0]; } } /// CHECK-START: int Main.testLocalArrayMerge7(int[], boolean, boolean) load_store_elimination (before) /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArrayGet /// CHECK-START: int Main.testLocalArrayMerge7(int[], boolean, boolean) load_store_elimination (after) /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-DAG: Return [<>] /// CHECK-DAG: <> Phi [<>,<>] /// CHECK-DAG: <> Phi [<>,<>] /// CHECK-EVAL: set(["<>","<>"]) == set(["<>","<>"]) /// CHECK-EVAL: set(["<>","<>"]) == set(["<>","<>"]) /// CHECK-START: int Main.testLocalArrayMerge7(int[], boolean, boolean) load_store_elimination (after) /// CHECK-NOT: ArrayGet // Test Phi creation for load elimination. private static int testLocalArrayMerge7(int[] a, boolean x, boolean y) { a[1] = 0; if (x) { if (y) { a[0] = 1; } else { a[0] = 2; } a[1] = a[0]; } return a[1]; } /// CHECK-START: int Main.testLocalArrayMerge8(boolean) load_store_elimination (before) /// CHECK-DAG: NewArray /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-START: int Main.testLocalArrayMerge8(boolean) load_store_elimination (after) /// CHECK-NOT: NewArray /// CHECK-NOT: ArraySet /// CHECK-NOT: ArrayGet // Test Merging default value and an identical value. private static int testLocalArrayMerge8(boolean x) { int[] a = new int[2]; if (x) { a[0] = 1; // Make sure the store below is not eliminated immediately as // storing the same value already present in the heap location. a[0] = 0; // Store the same value as default value to test merging with // the default value from else-block. } else { // Do the same as then-block for a different heap location to avoid // relying on block ordering. (Test both `default+0` and `0+default`.) a[1] = 1; a[1] = 0; } return a[0] + a[1]; } /// CHECK-START: void Main.$noinline$testThrowingArraySet(java.lang.Object[], java.lang.Object) load_store_elimination (before) /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-START: void Main.$noinline$testThrowingArraySet(java.lang.Object[], java.lang.Object) load_store_elimination (after) /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet private static void $noinline$testThrowingArraySet(Object[] a, Object o) { Object olda0 = a[0]; a[0] = null; a[1] = olda0; a[0] = o; a[1] = null; } /// CHECK-START: int Main.testLoop1(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: Phi /// CHECK-START: int Main.testLoop1(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-START: int Main.testLoop1(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet // Test Phi creation for load elimination with loop. private static int testLoop1(TestClass obj, int n) { obj.i = 0; for (int i = 0; i < n; ++i) { obj.i = i; } return obj.i; } /// CHECK-START: int Main.testLoop2(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: Phi /// CHECK-START: int Main.testLoop2(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop2(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet /// CHECK-START: int Main.testLoop2(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK-NOT: Phi // Test that we do not create any Phis for load elimination when // the heap value was not modified in the loop. private static int testLoop2(TestClass obj, int n) { obj.i = 1; for (int i = 0; i < n; ++i) { obj.j = i; } return obj.i; } /// CHECK-START: int Main.testLoop3(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop3(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet /// CHECK-START: int Main.testLoop3(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet // Test elimination of a store in the loop that stores the same value that was already // stored before the loop and eliminating the load of that value after the loop. private static int testLoop3(TestClass obj, int n) { obj.i = 1; for (int i = 0; i < n; ++i) { obj.i = 1; } return obj.i; } /// CHECK-START: int Main.testLoop4(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop4(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet /// CHECK-START: int Main.testLoop4(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet // Test store elimination in the loop that stores the same value that was already // stored before the loop, without any loads of that value. private static int testLoop4(TestClass obj, int n) { obj.i = 1; for (int i = 0; i < n; ++i) { obj.i = 1; } return n; } /// CHECK-START: int Main.testLoop5(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop5(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet /// CHECK-START: int Main.testLoop5(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet // Test eliminating loads and stores that just shuffle the same value between // different heap locations. private static int testLoop5(TestClass obj, int n) { // Initialize both `obj.i` and `obj.j` to the same value and then swap these values // in the loop. We should be able to determine that the values are always the same. obj.i = n; obj.j = n; for (int i = 0; i < n; ++i) { if ((i & 1) != 0) { int tmp = obj.i; obj.i = obj.j; obj.j = tmp; } } return obj.i; } /// CHECK-START: int Main.testLoop6(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop6(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet /// CHECK-START: int Main.testLoop6(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet // Test eliminating loads and stores that just shuffle the same value between // different heap locations, or store the same value. private static int testLoop6(TestClass obj, int n) { // Initialize both `obj.i` and `obj.j` to the same value and then swap these values // in the loop or set `obj.i` to the same value. We should be able to determine // that the values are always the same. obj.i = n; obj.j = n; for (int i = 0; i < n; ++i) { if ((i & 1) != 0) { int tmp = obj.i; obj.i = obj.j; obj.j = tmp; } else { obj.i = n; } } return obj.i; } /// CHECK-START: int Main.testLoop7(int) load_store_elimination (before) /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop7(int) load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: InstanceFieldGet // Test eliminating loads and stores that just shuffle the default value between // different heap locations, or store the same value. private static int testLoop7(int n) { // Leave both `obj.i` and `obj.j` initialized to the default value and then // swap these values in the loop or set some to the identical value 0. // We should be able to determine that the values are always the same. TestClass obj = new TestClass(); for (int i = 0; i < n; ++i) { if ((i & 1) != 0) { int tmp = obj.i; obj.i = obj.j; obj.j = tmp; } else { obj.i = 0; } } return obj.i; } /// CHECK-START: int Main.testLoop8(int) load_store_elimination (before) /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop8(int) load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: InstanceFieldGet /// CHECK-START: int Main.testLoop8(int) load_store_elimination (after) /// CHECK: Phi /// CHECK: Phi /// CHECK-NOT: Phi // Test eliminating loads and stores that just shuffle the same value between // different heap locations, or store the same value. The value is loaded // after conditionally setting a different value after the loop to test that // this does not cause creation of excessive Phis. private static int testLoop8(int n) { // Leave both `obj.i` and `obj.j` initialized to the default value and then // swap these values in the loop or set some to the identical value 0. // We should be able to determine that the values are always the same. TestClass obj = new TestClass(); for (int i = 0; i < n; ++i) { if ((i & 1) != 0) { int tmp = obj.i; obj.i = obj.j; obj.j = tmp; } else { obj.i = 0; } } // Up to this point, `obj.i` is always 0 but the Phi placeholder below // must not be included in that determination despite using lazy search // for Phi placeholders triggered by the `obj.i` load below. if ((n & 1) == 0) { obj.i = 1; } return obj.i; } /// CHECK-START: int Main.testLoop9(TestClass, int) load_store_elimination (before) /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InvokeStaticOrDirect /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop9(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-START: int Main.testLoop9(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet /// CHECK-START: int Main.testLoop9(TestClass, int) load_store_elimination (after) /// CHECK-NOT: NewInstance /// CHECK-START: int Main.testLoop9(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldGet /// CHECK-NOT: InstanceFieldGet /// CHECK-START: int Main.testLoop9(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK: Phi /// CHECK: Phi /// CHECK-NOT: Phi // Test that unknown value flowing through a loop back-edge prevents // elimination of a load but that load can be used as an input to a Phi // created to eliminate another load. private static int testLoop9(TestClass obj, int n) { TestClass obj0 = new TestClass(); // Initialize both `obj.i` and `obj0.i` to the same value and then swap these values // in the loop or clobber `obj.i`. We should determine that the `obj.i` load in the // loop must be kept but the `obj0.i` load can be replaced by a Phi chain. obj0.i = n; obj.i = n; for (int i = 0; i < n; ++i) { if ((i & 1) != 0) { int tmp = obj0.i; obj0.i = obj.i; // Load cannot be eliminated. obj.i = tmp; } else { $noinline$clobberObservables(); // Makes obj.i unknown. } } return obj0.i; } /// CHECK-START: int Main.testLoop10(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop10(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop10(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldGet /// CHECK-NOT: InstanceFieldGet // Test load elimination after finding a non-eliminated load depending // on loop Phi placeholder. private static int testLoop10(TestClass obj, int n) { obj.i = 1; for (int i = 0; i < n; ++i) { $noinline$clobberObservables(); } int i1 = obj.i; obj.j = 2; // Use write side effects to stop GVN from eliminating the load below. int i2 = obj.i; return i1 + i2; } /// CHECK-START: int Main.testLoop11(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop11(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-START: int Main.testLoop11(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK: Phi /// CHECK: Phi /// CHECK-NOT: Phi /// CHECK-START: int Main.testLoop11(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet // Test load elimination creating two Phis that depend on each other. private static int testLoop11(TestClass obj, int n) { obj.i = 1; for (int i = 0; i < n; ++i) { if ((i & 1) != 0) { obj.i = 2; } else { obj.i = 3; } // There shall be a Phi created here for `obj.i` before the "++i". // This Phi and the loop Phi that shall be created for `obj.i` depend on each other. } return obj.i; } /// CHECK-START: int Main.testLoop12(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop12(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop12(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK: Phi /// CHECK-NOT: Phi /// CHECK-START: int Main.testLoop12(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet // Test load elimination creating a single Phi with more than 2 inputs. private static int testLoop12(TestClass obj, int n) { obj.i = 1; for (int i = 0; i < n; ) { // Do the loop variable increment first, so that there are back-edges // directly from the "then" and "else" blocks below. ++i; if ((i & 1) != 0) { obj.i = 2; } else { obj.i = 3; } } return obj.i; } /// CHECK-START: int Main.testLoop13(TestClass, int) load_store_elimination (before) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArrayGet /// CHECK-START: int Main.testLoop13(TestClass, int) load_store_elimination (after) /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop13(TestClass, int) load_store_elimination (after) /// CHECK-NOT: NewArray /// CHECK-NOT: ArrayGet /// CHECK-NOT: ArraySet /// CHECK-START: int Main.testLoop13(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK: Phi /// CHECK: Phi /// CHECK: Phi /// CHECK-NOT: Phi // Test eliminating array allocation, loads and stores and creating loop Phis. private static int testLoop13(TestClass obj, int n) { int[] a = new int[3]; for (int i = 0; i < n; ++i) { a[0] = a[1]; a[1] = a[2]; a[2] = obj.i; } return a[0]; } /// CHECK-START: int Main.testLoop14(TestClass2, int) load_store_elimination (before) /// CHECK-DAG: NewArray /// CHECK-DAG: InstanceFieldSet field_name:TestClass2.i /// CHECK-DAG: Phi /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-DAG: InstanceFieldGet field_name:TestClass2.i /// CHECK-DAG: InstanceFieldSet field_name:TestClass2.j /// CHECK-DAG: InstanceFieldGet field_name:TestClass2.i /// CHECK-DAG: ArraySet /// CHECK-DAG: InstanceFieldSet field_name:TestClass2.k /// CHECK-DAG: InstanceFieldSet field_name:TestClass2.j /// CHECK-DAG: InstanceFieldGet field_name:TestClass2.i /// CHECK-DAG: InstanceFieldSet field_name:TestClass2.k /// CHECK-DAG: ArrayGet /// CHECK-START: int Main.testLoop14(TestClass2, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet field_name:TestClass2.i /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet field_name:TestClass2.i /// CHECK-DAG: InstanceFieldSet field_name:TestClass2.j /// CHECK-DAG: InstanceFieldSet field_name:TestClass2.k /// CHECK-DAG: InstanceFieldSet field_name:TestClass2.j /// CHECK-DAG: InstanceFieldSet field_name:TestClass2.k /// CHECK-START: int Main.testLoop14(TestClass2, int) load_store_elimination (after) /// CHECK-NOT: NewArray /// CHECK-START: int Main.testLoop14(TestClass2, int) load_store_elimination (after) /// CHECK: InstanceFieldGet field_name:TestClass2.i /// CHECK-NOT: InstanceFieldGet field_name:TestClass2.i /// CHECK-START: int Main.testLoop14(TestClass2, int) load_store_elimination (after) /// CHECK: Phi /// CHECK: Phi /// CHECK: Phi /// CHECK: Phi /// CHECK-NOT: Phi // Test load elimination in a loop after determing that the first field load // (depending on loop Phi placeholder) cannot be eliminated. private static int testLoop14(TestClass2 obj, int n) { int[] a = new int[3]; obj.i = 1; for (int i = 0; i < n; ++i) { a[0] = a[1]; a[1] = a[2]; int i1 = obj.i; obj.j = 2; // Use write side effects to stop GVN from eliminating the load below. int i2 = obj.i; a[2] = i1; if ((i & 2) != 0) { obj.k = i2; } else { obj.j = 3; // Use write side effects to stop GVN from eliminating the load below. obj.k = obj.i; $noinline$clobberObservables(); // Make obj.i unknown. } } return a[0]; } /// CHECK-START: int Main.testLoop15(int) load_store_elimination (before) /// CHECK-DAG: NewArray /// CHECK-IF: hasIsaFeature("sve") and os.environ.get('ART_FORCE_TRY_PREDICATED_SIMD') == 'true' // /// CHECK-DAG: VecPredWhile /// CHECK-DAG: VecStore // /// CHECK-ELSE: // /// CHECK-DAG: ArraySet // /// CHECK-FI: // /// CHECK-DAG: ArrayGet /// CHECK-START: int Main.testLoop15(int) load_store_elimination (after) /// CHECK-DAG: NewArray /// CHECK-IF: hasIsaFeature("sve") and os.environ.get('ART_FORCE_TRY_PREDICATED_SIMD') == 'true' // /// CHECK-DAG: VecPredWhile /// CHECK-DAG: VecStore // /// CHECK-ELSE: // /// CHECK-DAG: ArraySet // /// CHECK-FI: // /// CHECK-DAG: ArrayGet // Test that aliasing array store in the loop is not eliminated // when a loop Phi placeholder is marked for keeping. private static int testLoop15(int n) { int[] a = new int[n + 1]; for (int i = 0; i < n; ++i) { a[i] = 1; // Cannot be eliminated due to aliasing. } return a[0]; } /// CHECK-START: int Main.testLoop16(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop16(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop16(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet /// CHECK-START: int Main.testLoop16(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK-NOT: Phi // Test that we match an existing loop Phi for eliminating a load. static int testLoop16(TestClass obj, int n) { obj.i = 0; for (int i = 0; i < n; ) { ++i; obj.i = i; } // The load is replaced by the existing Phi instead of constructing a new one. return obj.i; } /// CHECK-START: int Main.testLoop17(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop17(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-START: int Main.testLoop17(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet /// CHECK-START: int Main.testLoop17(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK: Phi /// CHECK-NOT: Phi // Test that we match an existing non-loop Phi for eliminating a load, // one input of the Phi being invariant across a preceding loop. static int testLoop17(TestClass obj, int n) { obj.i = 1; int phi = 1; for (int i = 0; i < n; ++i) { obj.j = 2; // Unrelated. } if ((n & 1) != 0) { obj.i = 2; phi = 2; } // The load is replaced by the existing Phi instead of constructing a new one. return obj.i + phi; } /// CHECK-START: int Main.testLoop18(TestClass, int) load_store_elimination (before) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: ArrayGet /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop18(TestClass, int) load_store_elimination (after) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop18(TestClass, int) load_store_elimination (after) /// CHECK-NOT: ArrayGet // Test eliminating a load of the default value in a loop // with the array index being defined inside the loop. static int testLoop18(TestClass obj, int n) { // The NewArray is kept as it may throw for negative n. // TODO: Eliminate constructor fence even though the NewArray is kept. int[] a0 = new int[n]; for (int i = 0; i < n; ++i) { obj.i = a0[i]; } return n; } /// CHECK-START: int Main.testLoop19(TestClass, int) load_store_elimination (before) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: ArrayGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: ArraySet /// CHECK-START: int Main.testLoop19(TestClass, int) load_store_elimination (after) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop19(TestClass, int) load_store_elimination (after) /// CHECK-NOT: ArrayGet /// CHECK-NOT: ArraySet // Test eliminating a load of the default value and store of an identical value // in a loop with the array index being defined inside the loop. static int testLoop19(TestClass obj, int n) { // The NewArray is kept as it may throw for negative n. // TODO: Eliminate constructor fence even though the NewArray is kept. int[] a0 = new int[n]; for (int i = 0; i < n; ++i) { obj.i = a0[i]; a0[i] = 0; // Store the same value as default. } return n; } /// CHECK-START: int Main.testLoop20(TestClass, int) load_store_elimination (before) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: ArrayGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: ArraySet /// CHECK-START: int Main.testLoop20(TestClass, int) load_store_elimination (after) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop20(TestClass, int) load_store_elimination (after) /// CHECK-NOT: ArrayGet /// CHECK-NOT: ArraySet // Test eliminating a load of the default value and a conditional store of an // identical value in a loop with the array index being defined inside the loop. static int testLoop20(TestClass obj, int n) { // The NewArray is kept as it may throw for negative n. // TODO: Eliminate constructor fence even though the NewArray is kept. int[] a0 = new int[n]; for (int i = 0; i < n; ++i) { obj.i = a0[i]; if ((i & 1) != 0) { a0[i] = 0; // Store the same value as default. } } return n; } /// CHECK-START: int Main.testLoop21(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop21(TestClass, int) load_store_elimination (before) /// CHECK-NOT: Phi /// CHECK-START: int Main.testLoop21(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet /// CHECK-START: int Main.testLoop21(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet /// CHECK-START: int Main.testLoop21(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK-NOT: Phi // Test load elimination when an instance field is used as the loop variable. static int testLoop21(TestClass obj, int n) { for (obj.i = 0; obj.i < n; ++obj.i) { obj.j = 0; // Use write side effects to stop GVN from eliminating the load below. obj.j = obj.i; } return n; } /// CHECK-START: int Main.testLoop22(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop22(TestClass, int) load_store_elimination (before) /// CHECK-NOT: Phi /// CHECK-START: int Main.testLoop22(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet /// CHECK-START: int Main.testLoop22(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet /// CHECK-START: int Main.testLoop22(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK-NOT: Phi // Test load and store elimination when an instance field is used as the loop // variable and then overwritten after the loop. static int testLoop22(TestClass obj, int n) { for (obj.i = 0; obj.i < n; ++obj.i) { obj.j = 0; // Use write side effects to stop GVN from eliminating the load below. obj.j = obj.i; } obj.i = 0; return n; } /// CHECK-START: int Main.testLoop23(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop23(TestClass, int) load_store_elimination (after) /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop23(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet // Test elimination of non-observable stores. static int testLoop23(TestClass obj, int n) { obj.i = -1; int phi = -1; for (int i = 0; i < n; ++i) { obj.i = i; phi = i; } if ((n & 1) != 0) { obj.i = 2; phi = 2; } obj.i = phi; // This store shall be kept, the stores above shall be eliminated. return phi; } /// CHECK-START: int Main.testLoop24(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop24(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-START: int Main.testLoop24(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet // Test matching Phis for store elimination. static int testLoop24(TestClass obj, int n) { obj.i = -1; int phi = -1; for (int i = 0; i < n; ++i) { obj.i = i; phi = i; } if ((n & 1) != 0) { obj.i = 2; phi = 2; } if (n == 3) { return -2; // Make the above stores observable. } // As the stores above are observable and kept, we match the merged // heap value with existing Phis and determine that we're storing // the same value that's already there, so we eliminate this store. obj.i = phi; return phi; } /// CHECK-START: int Main.testLoop25(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop25(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop25(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet // Test that we do not match multiple dependent Phis for load and store elimination. static int testLoop25(TestClass obj, int n) { obj.i = 1; int phi = 1; for (int i = 0; i < n; ++i) { if ((i & 1) != 0) { obj.i = 2; phi = 2; } // There is a Phi here for the variable `phi` before the "++i". // This Phi and the loop Phi for `phi` depend on each other. } if (n == 3) { return -1; // Make above stores observable. } // We're not matching multiple Phi placeholders to existing Phis. Therefore the load // below requires 2 extra Phis to be created and the store below shall not be eliminated // even though it stores the same value that's already present in the heap location. int tmp = obj.i; obj.i = phi; return tmp + phi; } /// CHECK-START: int Main.testLoop26(TestClass, int) load_store_elimination (before) /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop26(TestClass, int) load_store_elimination (after) /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop26(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldGet /// CHECK-NOT: InstanceFieldGet // Test load elimination creating a reference Phi. static int testLoop26(TestClass obj, int n) { obj.next = new TestClass(1, 2); for (int i = 0; i < n; ++i) { obj.next = new SubTestClass(); } return obj.next.i; } /// CHECK-START: int Main.testLoop27(TestClass, int) load_store_elimination (before) /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop27(TestClass, int) load_store_elimination (after) /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop27(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldGet /// CHECK-NOT: InstanceFieldGet // Test load elimination creating two reference Phis that depend on each other. static int testLoop27(TestClass obj, int n) { obj.next = new TestClass(1, 2); for (int i = 0; i < n; ++i) { if ((i & 1) != 0) { obj.next = new SubTestClass(); } // There shall be a Phi created here for `obj.next` before the "++i". // This Phi and the loop Phi that shall be created for `obj.next` depend on each other. } return obj.next.i; } /// CHECK-START: int Main.testLoop28(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArrayGet /// CHECK-START: int Main.testLoop28(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testLoop28(TestClass, int) load_store_elimination (after) /// CHECK-NOT: NewArray /// CHECK-NOT: ArrayGet /// CHECK-NOT: ArraySet /// CHECK-START: int Main.testLoop28(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK: Phi /// CHECK: Phi /// CHECK: Phi /// CHECK-NOT: Phi // Test eliminating array allocation, loads and stores and creating loop Phis // after determining that a field load depending on loop Phi placeholder cannot // be eliminated. private static int testLoop28(TestClass obj, int n) { obj.i = 1; int[] a = new int[3]; for (int i = 0; i < n; ++i) { a[0] = a[1]; a[1] = a[2]; a[2] = obj.i; $noinline$clobberObservables(); } return a[0]; } /// CHECK-START: int Main.testLoop29(int) load_store_elimination (before) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-START: int Main.testLoop29(int) load_store_elimination (after) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet // Test that ArraySet with non-default value prevents matching ArrayGet for // the same array to default value even when the ArraySet is using an index // offset by one, making LSA declare that the two heap locations do not alias. private static int testLoop29(int n) { int[] a = new int[4]; int sum = 0; for (int i = 0; i < n; ) { int value = a[i] + 1; sum += value; ++i; a[i] = value; } return sum; } /// CHECK-START: int Main.testLoop30(int) load_store_elimination (before) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-START: int Main.testLoop30(int) load_store_elimination (after) /// CHECK-NOT: ArrayGet /// CHECK-NOT: ArraySet // Test that ArraySet with default value does not prevent matching ArrayGet // for the same array to the default value. private static int testLoop30(int n) { int[] a = new int[4]; // NewArray is kept due to environment use by Deoptimize. int sum = 0; for (int i = 0; i < n; ) { int value = a[i] + 1; sum += value; ++i; a[i] = 0; } return sum; } /// CHECK-START: int Main.testLoop31(int) load_store_elimination (before) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-START: int Main.testLoop31(int) load_store_elimination (after) /// CHECK-NOT: ArrayGet /// CHECK-NOT: ArraySet // Test that ArraySet with default value read from the array does not // prevent matching ArrayGet for the same array to the default value. private static int testLoop31(int n) { int[] a = new int[4]; // NewArray is kept due to environment use by Deoptimize. int sum = 0; for (int i = 0; i < n; ) { int value = a[i]; sum += value; ++i; a[i] = value; } return sum; } /// CHECK-START: int Main.testLoop32(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testLoop32(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-START: int Main.testLoop32(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet // Test matching Phis for store elimination. static int testLoop32(TestClass obj, int n) { obj.i = -1; int phi = -1; for (int i = 0; i < n; ) { ++i; if ((i & 1) != 0) { obj.i = i; phi = i; } } if ((n & 1) != 0) { obj.i = 2; phi = 2; } if (n == 3) { return -2; // Make the above stores observable. } // As the stores above are observable and kept, we match the merged // heap value with existing Phis and determine that we're storing // the same value that's already there, so we eliminate this store. obj.i = phi; return phi; } // CHECK-START: int Main.testLoop33(TestClass, int) load_store_elimination (before) // CHECK-DAG: InstanceFieldSet // CHECK-DAG: NewArray // CHECK-DAG: Phi // CHECK-DAG: ArrayGet // CHECK-DAG: InstanceFieldSet // CHECK-DAG: Phi // CHECK-DAG: ArrayGet // CHECK-DAG: InstanceFieldGet // CHECK-DAG: InstanceFieldSet // CHECK-DAG: InstanceFieldGet // CHECK-START: int Main.testLoop33(TestClass, int) load_store_elimination (after) // CHECK-DAG: InstanceFieldSet // CHECK-DAG: Phi // CHECK-DAG: InstanceFieldSet // CHECK-DAG: Phi // CHECK-DAG: InstanceFieldGet // CHECK-DAG: InstanceFieldSet // CHECK-DAG: InstanceFieldGet // CHECK-START: int Main.testLoop33(TestClass, int) load_store_elimination (after) // CHECK-NOT: ArrayGet // Test that when processing Phi placeholder with unknown input, we allow materialized // default value in pre-header for array location with index defined in the loop. static int testLoop33(TestClass obj, int n) { obj.i = 0; int[] a0 = new int[n]; for (int i = 0; i < n; ++i) { obj.i = a0[i]; $noinline$clobberObservables(); // Make `obj.i` unknown. } for (int i = 0; i < n; ++i) { int zero = a0[i]; int unknown = obj.i; obj.j += zero + unknown; } return obj.j; } /// CHECK-START: int Main.testLoop34(int) load_store_elimination (before) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-START: int Main.testLoop34(int) load_store_elimination (after) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet // Test that ArraySet with non-default value prevents matching ArrayGet for // the same array to default value even when the ArraySet is using an index // offset by one, making LSA declare that the two heap locations do not alias. // Also test that the ArraySet is not eliminated. private static int testLoop34(int n) { int[] a = new int[n + 1]; int sum = 0; for (int i = 0; i < n; ) { int value = a[i] + 1; sum += value; ++i; a[i] = value; } return sum; } /// CHECK-START: int Main.testLoop35(int) load_store_elimination (before) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArraySet /// CHECK-START: int Main.testLoop35(int) load_store_elimination (after) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-START: int Main.testLoop35(int) load_store_elimination (after) /// CHECK: ArraySet /// CHECK-NOT: ArraySet // Test that ArraySet with non-default value prevents matching ArrayGet for // the same array to default value even when the ArraySet is using an index // offset by one, making LSA declare that the two heap locations do not alias. // Also test that the ArraySet is not eliminated and that a store after the // loop is eliminated. private static int testLoop35(int n) { int[] a = new int[n + 1]; int sum = 0; for (int i = 0; i < n; ) { int value = a[i] + 1; sum += value; ++i; a[i] = value; } a[0] = 1; return sum; } /// CHECK-START: int Main.testLoop36(int) load_store_elimination (before) /// CHECK-DAG: ArraySet /// CHECK-DAG: Deoptimize /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-START: int Main.testLoop36(int) load_store_elimination (before) /// CHECK-NOT: BoundsCheck /// CHECK-START: int Main.testLoop36(int) load_store_elimination (after) /// CHECK-DAG: ArraySet /// CHECK-DAG: Deoptimize /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet // Regression test for b/187487955. // We previously failed a DCHECK() during the search for kept stores when // we encountered two array locations for the same array and considered // non-aliasing by LSA when only one of the array locations had index // defined inside the loop. Note that this situation requires that BCE // eliminates BoundsCheck instructions, otherwise LSA considers those // locations aliasing. private static int testLoop36(int n) { int[] a = new int[n]; int zero = 0; int i = 0; for (; i < n; ++i) { a[i] = i; // Extra instructions to avoid loop unrolling. zero = (((zero ^ 1) + 2) ^ 1) - 2; zero = (((zero ^ 4) + 8) ^ 4) - 8; } // Use 4 loads with consecutive fixed offsets from the loop Phi for `i`. // BCE shall replace BoundsChecks with Deoptimize, so that indexes here are // the Phi plus/minus a constant, something that LSA considers non-aliasing // with the Phi (LSA does not take different loop iterations into account) // but LSE must consider aliasing across dfferent loop iterations. return a[i - 1] + a[i - 2] + a[i - 3] + a[i - 4] + zero; } /// CHECK-START: int Main.testLoop37(int) load_store_elimination (before) /// CHECK-DAG: ArraySet /// CHECK-DAG: Deoptimize /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-START: int Main.testLoop37(int) load_store_elimination (before) /// CHECK-NOT: BoundsCheck /// CHECK-START: int Main.testLoop37(int) load_store_elimination (after) /// CHECK-DAG: ArraySet /// CHECK-DAG: Deoptimize /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet // Similar to testLoop36 but the writes are done via a different reference to the same array. // We previously used a reference comparison for back-edge aliasing analysis but this test // has different references and therefore needs `HeapLocationCollector::CanReferencesAlias()`. private static int testLoop37(int n) { int[] a = new int[n]; int[] b = $noinline$returnArg(a); int zero = 0; int i = 0; for (; i < n; ++i) { b[i] = i; // Extra instructions to avoid loop unrolling. zero = (((zero ^ 1) + 2) ^ 1) - 2; zero = (((zero ^ 4) + 8) ^ 4) - 8; } // Use 4 loads with consecutive fixed offsets from the loop Phi for `i`. // BCE shall replace BoundsChecks with Deoptimize, so that indexes here are // the Phi plus/minus a constant, something that LSA considers non-aliasing // with the Phi (LSA does not take different loop iterations into account) // but LSE must consider aliasing across dfferent loop iterations. return a[i - 1] + a[i - 2] + a[i - 3] + a[i - 4] + zero; } private static int[] $noinline$returnArg(int[] a) { return a; } /// CHECK-START: int Main.testLoop38(int, int[]) load_store_elimination (before) /// CHECK-DAG: ArraySet /// CHECK-DAG: Deoptimize /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-START: int Main.testLoop38(int, int[]) load_store_elimination (before) /// CHECK-NOT: BoundsCheck /// CHECK-START: int Main.testLoop38(int, int[]) load_store_elimination (after) /// CHECK-DAG: ArraySet /// CHECK-DAG: Deoptimize /// CHECK-START: int Main.testLoop38(int, int[]) load_store_elimination (after) /// CHECK-NOT: ArrayGet // Similar to testLoop37 but writing to a different array that exists before allocating `a`, // so that `HeapLocationCollector::CanReferencesAlias()` returns false and all the ArrayGet // instructions are actually eliminated. private static int testLoop38(int n, int[] b) { int[] a = new int[n]; int zero = 0; int i = 0; for (; i < n; ++i) { b[i] = i; // Extra instructions to avoid loop unrolling. zero = (((zero ^ 1) + 2) ^ 1) - 2; zero = (((zero ^ 4) + 8) ^ 4) - 8; } // Use 4 loads with consecutive fixed offsets from the loop Phi for `i`. // BCE shall replace BoundsChecks with Deoptimize, so that indexes here are // the Phi plus/minus a constant, something that LSA considers non-aliasing // with the Phi (LSA does not take different loop iterations into account) // but LSE must consider aliasing across dfferent loop iterations. return a[i - 1] + a[i - 2] + a[i - 3] + a[i - 4] + zero; } /// CHECK-START: int Main.testNestedLoop1(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testNestedLoop1(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet // Test heap value clobbering in nested loop. private static int testNestedLoop1(TestClass obj, int n) { obj.i = 1; for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) { $noinline$clobberObservables(); } } return obj.i; } /// CHECK-START: int Main.testNestedLoop2(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testNestedLoop2(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testNestedLoop2(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldGet /// CHECK-NOT: InstanceFieldGet /// CHECK-START: int Main.testNestedLoop2(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK: Phi /// CHECK: Phi /// CHECK-NOT: Phi // Test heap value clobbering in the nested loop and load elimination for a heap // location then set to known value before the end of the outer loop. private static int testNestedLoop2(TestClass obj, int n) { obj.i = 1; obj.j = 2; for (int i = 0; i < n; ++i) { int tmp = obj.j; for (int j = i + 1; j < n; ++j) { $noinline$clobberObservables(); } obj.i = tmp; } return obj.i; } /// CHECK-START: int Main.testNestedLoop3(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testNestedLoop3(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testNestedLoop3(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldGet /// CHECK-NOT: InstanceFieldGet /// CHECK-START: int Main.testNestedLoop3(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK: Phi /// CHECK: Phi /// CHECK-NOT: Phi // Test heap value clobbering in the nested loop and load elimination for a heap // location then set to known value before the end of the outer loop. private static int testNestedLoop3(TestClass obj, int n) { obj.i = 1; for (int i = 0; i < n; ++i) { obj.j = 2; for (int j = i + 1; j < n; ++j) { $noinline$clobberObservables(); } obj.i = obj.j; } return obj.i; } /// CHECK-START: int Main.testNestedLoop4(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testNestedLoop4(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testNestedLoop4(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet /// CHECK-START: int Main.testNestedLoop4(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK: Phi /// CHECK: Phi /// CHECK: Phi /// CHECK-NOT: Phi // Test creating loop Phis for both inner and outer loop to eliminate a load. private static int testNestedLoop4(TestClass obj, int n) { obj.i = 1; for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) { obj.i = 2; } } return obj.i; } /// CHECK-START: int Main.testNestedLoop5(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testNestedLoop5(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testNestedLoop5(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet /// CHECK-START: int Main.testNestedLoop5(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK: Phi /// CHECK: Phi /// CHECK-NOT: Phi // Test creating a loop Phi for outer loop to eliminate a load. private static int testNestedLoop5(TestClass obj, int n) { obj.i = 1; for (int i = 0; i < n; ++i) { obj.i = 2; for (int j = i + 1; j < n; ++j) { obj.j = 3; // Unrelated. } } return obj.i; } /// CHECK-START: int Main.testNestedLoop6(TestClass, int) load_store_elimination (before) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testNestedLoop6(TestClass, int) load_store_elimination (after) /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testNestedLoop6(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldGet /// CHECK-NOT: InstanceFieldGet /// CHECK-START: int Main.testNestedLoop6(TestClass, int) load_store_elimination (after) /// CHECK: Phi /// CHECK: Phi /// CHECK: Phi /// CHECK: Phi /// CHECK-NOT: Phi // Test heap value clobbering in the nested loop and load elimination for a heap // location then set to known value before the end of that inner loop. private static int testNestedLoop6(TestClass obj, int n) { obj.i = 1; obj.j = 2; for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) { int tmp = obj.j; $noinline$clobberObservables(); obj.i = tmp; } } return obj.i; } /// CHECK-START: int Main.testNestedLoop7(TestClass, int) load_store_elimination (before) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: ArrayGet /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testNestedLoop7(TestClass, int) load_store_elimination (after) /// CHECK-DAG: NewArray /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: InstanceFieldSet /// CHECK-START: int Main.testNestedLoop7(TestClass, int) load_store_elimination (after) /// CHECK-NOT: ArrayGet // Test load elimination in inner loop reading default value that is loop invariant // with an index defined inside the inner loop. private static int testNestedLoop7(TestClass obj, int n) { // The NewArray is kept as it may throw for negative n. // TODO: Eliminate constructor fence even though the NewArray is kept. int[] a0 = new int[n]; for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) { obj.i = a0[j]; } } return n; } /// CHECK-START: int Main.testNestedLoop8(TestClass, int) load_store_elimination (before) /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testNestedLoop8(TestClass, int) load_store_elimination (after) /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: Phi /// CHECK-DAG: NewInstance /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-START: int Main.testNestedLoop8(TestClass, int) load_store_elimination (after) /// CHECK: InstanceFieldGet /// CHECK-NOT: InstanceFieldGet // Test reference type propagation for Phis created for outer and inner loop. private static int testNestedLoop8(TestClass obj, int n) { obj.next = new SubTestClass(); for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) { obj.next = new TestClass(); } } // The Phis created in both loop headers for replacing `obj.next` depend on each other. return obj.next.i; } /// CHECK-START: long Main.testOverlapLoop(int) load_store_elimination (before) /// CHECK-DAG: NewArray /// CHECK-DAG: ArraySet /// CHECK-DAG: If /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-DAG: ArrayGet /// CHECK-DAG: Goto /// CHECK-START: long Main.testOverlapLoop(int) load_store_elimination (after) /// CHECK-DAG: NewArray /// CHECK-DAG: ArraySet /// CHECK-DAG: If /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArrayGet /// CHECK-DAG: ArraySet /// CHECK-DAG: Goto /// CHECK-NOT: ArrayGet // Test that we don't incorrectly remove writes needed by later loop iterations // NB This is fibonacci numbers private static long testOverlapLoop(int cnt) { long[] w = new long[cnt]; w[1] = 1; long t = 1; for (int i = 2; i < cnt; ++i) { w[i] = w[i - 1] + w[i - 2]; t = w[i]; } return t; } private static boolean $noinline$getBoolean(boolean val) { return val; } /// CHECK-START: int Main.$noinline$testPartialEscape1(TestClass, boolean) load_store_elimination (before) /// CHECK-DAG: ParameterValue /// CHECK-DAG: NewInstance /// CHECK-DAG: InvokeStaticOrDirect /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InvokeStaticOrDirect /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldSet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: InstanceFieldGet /// CHECK-DAG: Phi // /// CHECK-NOT: NewInstance /// CHECK-NOT: InvokeStaticOrDirect /// CHECK-NOT: InstanceFieldSet /// CHECK-NOT: InstanceFieldGet // /// CHECK-START: int Main.$noinline$testPartialEscape1(TestClass, boolean) load_store_elimination (after) /// CHECK-DAG: ParameterValue /// CHECK-DAG: NewInstance /// CHECK-DAG: Phi // /// CHECK-START: int Main.$noinline$testPartialEscape1(TestClass, boolean) load_store_elimination (after) /// CHECK: InvokeStaticOrDirect /// CHECK: InvokeStaticOrDirect // /// CHECK-NOT: InvokeStaticOrDirect /// CHECK-START: int Main.$noinline$testPartialEscape1(TestClass, boolean) load_store_elimination (after) /// CHECK: InstanceFieldSet // // TODO: We should be able to remove this setter by realizing `i` only escapes in a branch. /// CHECK: InstanceFieldSet /// CHECK-NOT: InstanceFieldSet // /// CHECK-START: int Main.$noinline$testPartialEscape1(TestClass, boolean) load_store_elimination (after) /// CHECK: InstanceFieldGet /// CHECK: InstanceFieldGet /// CHECK: InstanceFieldGet // /// CHECK-NOT: InstanceFieldGet private static int $noinline$testPartialEscape1(TestClass obj, boolean escape) { TestClass i = new SubTestClass(); int res; if ($noinline$getBoolean(escape)) { i.next = obj; $noinline$Escape(i); res = i.next.i; } else { i.next = obj; res = i.next.i; } return res; } private static void $noinline$clobberObservables() {} static void assertLongEquals(long result, long expected) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); } } static void assertIntEquals(int result, int expected) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); } } static void assertFloatEquals(float result, float expected) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); } } static void assertDoubleEquals(double result, double expected) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); } } public static void main(String[] args) { assertDoubleEquals(Math.PI * Math.PI * Math.PI, calcCircleArea(Math.PI)); assertIntEquals(test1(new TestClass(), new TestClass()), 3); assertIntEquals(test2(new TestClass()), 1); TestClass obj1 = new TestClass(); TestClass obj2 = new TestClass(); obj1.next = obj2; assertIntEquals(test3(obj1), 10); assertIntEquals(test4(new TestClass(), true), 1); assertIntEquals(test4(new TestClass(), false), 1); assertIntEquals(test5(new TestClass(), true), 1); assertIntEquals(test5(new TestClass(), false), 2); assertIntEquals(test6(new TestClass(), new TestClass(), true), 4); assertIntEquals(test6(new TestClass(), new TestClass(), false), 2); assertIntEquals(test7(new TestClass()), 1); assertIntEquals(test8(), 1); obj1 = new TestClass(); obj2 = new TestClass(); obj1.next = obj2; assertIntEquals(test9(new TestClass()), 1); assertIntEquals(test10(new TestClass(3, 4)), 3); assertIntEquals(TestClass.si, 3); assertIntEquals(test11(new TestClass()), 10); assertIntEquals(test12(new TestClass(), new TestClass()), 10); assertIntEquals(test13(new TestClass(), new TestClass2()), 3); SubTestClass obj3 = new SubTestClass(); assertIntEquals(test14(obj3, obj3), 2); assertIntEquals(test15(), 2); assertIntEquals(test16(), 3); assertIntEquals(test17(), 0); assertIntEquals(test18(new TestClass()), 1); float[] fa1 = { 0.8f }; float[] fa2 = { 1.8f }; assertFloatEquals(test19(fa1, fa2), 1.8f); assertFloatEquals(test20().i, 0); test21(new TestClass()); assertIntEquals(test22(), 13); assertIntEquals(test23(true), 4); assertIntEquals(test23(false), 5); assertFloatEquals(test24(), 8.0f); assertIntEquals(test25(false, true, true), 5); assertIntEquals(test25(true, false, true), 2); assertFloatEquals(test26(5), 0.0f); assertFloatEquals(test26(3), 1.0f); assertIntEquals(test27(false, true), 1); assertIntEquals(test27(true, false), 1); assertIntEquals(test28(false, true), 0); assertIntEquals(test28(true, true), 5); assertFloatEquals(test29(true), 5.0f); assertFloatEquals(test29(false), 2.0f); assertIntEquals(test30(new TestClass(), true), 1); assertIntEquals(test30(new TestClass(), false), 0); assertIntEquals(test31(true, true), 5); assertIntEquals(test31(true, false), 6); assertIntEquals(test32(1), 10); assertIntEquals(test32(2), 10); assertIntEquals(test33(new TestClass(), true), 1); assertIntEquals(test33(new TestClass(), false), 2); assertIntEquals(test34(new TestClass(), true, true), 3); assertIntEquals(test34(new TestClass(), false, true), 4); assertIntEquals(test34(new TestClass(), true, false), 1); assertIntEquals(test34(new TestClass(), false, false), 2); assertIntEquals(test35(new TestClass(), true, true), 3); assertIntEquals(test35(new TestClass(), false, true), 2); assertIntEquals(test35(new TestClass(), true, false), 1); assertIntEquals(test35(new TestClass(), false, false), 2); assertIntEquals(test36(new TestClass(), true), 2); assertIntEquals(test36(new TestClass(), false), 4); assertIntEquals(test37(new TestClass(), true), 1); assertIntEquals(test37(new TestClass(), false), 0); assertIntEquals(test38(new TestClass(), true), 1); assertIntEquals(test38(new TestClass(), false), 2); assertIntEquals(test39(new TestClass(), true), 0); assertIntEquals(test39(new TestClass(), false), 1); assertIntEquals(test40(), 150); testFinalizableByForcingGc(); testFinalizableWithLoopByForcingGc(); assertIntEquals($noinline$testHSelect(true), 0xdead); int[] array = {2, 5, 9, -1, -3, 10, 8, 4}; assertIntEquals(sumWithinRange(array, 1, 5), 11); assertFloatEquals(testAllocationEliminationWithLoops(), 1.0f); assertFloatEquals(mF, 0f); assertDoubleEquals(Math.PI * Math.PI * Math.PI, getCircleArea(Math.PI, true)); assertDoubleEquals(0d, getCircleArea(Math.PI, false)); assertIntEquals($noinline$testConversion1(new TestClass(), 300), 300); assertIntEquals($noinline$testConversion1(new TestClass(), 301), 45); assertIntEquals($noinline$testConversion2(new TestClass(), 300), 300); assertIntEquals($noinline$testConversion2(new TestClass(), 301), 90); assertIntEquals($noinline$testConversion3(new TestClass(), 0), 0); assertIntEquals($noinline$testConversion3(new TestClass(), 1), 0); assertIntEquals($noinline$testConversion3(new TestClass(), 128), 127); assertIntEquals($noinline$testConversion3(new TestClass(), 129), -128); assertIntEquals($noinline$testConversion4(new TestClass(), 0), 0); assertIntEquals($noinline$testConversion4(new TestClass(), 1), 0); assertIntEquals($noinline$testConversion4(new TestClass(), 128), 254); assertIntEquals($noinline$testConversion4(new TestClass(), 129), -256); int[] iarray = {0, 0, 0}; double[] darray = {0d, 0d, 0d}; try { assertDoubleEquals(Math.PI * Math.PI * Math.PI, testDeoptimize(iarray, darray, Math.PI)); } catch (Exception e) { System.out.println(e.getClass().getName()); } assertIntEquals(iarray[0], 1); assertIntEquals(iarray[1], 1); assertIntEquals(iarray[2], 1); assertDoubleEquals(darray[0], Math.PI); assertDoubleEquals(darray[1], Math.PI); assertDoubleEquals(darray[2], Math.PI); assertIntEquals(testAllocationEliminationOfArray1(), 11); assertIntEquals(testAllocationEliminationOfArray2(), 11); assertIntEquals(testAllocationEliminationOfArray3(2), 4); assertIntEquals(testAllocationEliminationOfArray4(2), 6); assertIntEquals(testAllocationEliminationOfArray5(2), 12); try { testAllocationEliminationOfArray5(-2); } catch (NegativeArraySizeException e) { System.out.println("Got NegativeArraySizeException."); } assertIntEquals(testAllocationEliminationOfArray6(true), 11); assertIntEquals(testStoreStore().i, 41); assertIntEquals(testStoreStore().j, 43); assertIntEquals(testExitMerge(true), 2); assertIntEquals(testExitMerge2(true), 2); assertIntEquals(testExitMerge2(false), 2); TestClass2 testclass2 = new TestClass2(); testStoreStore2(testclass2); assertIntEquals(testclass2.i, 43); assertIntEquals(testclass2.j, 44); testStoreStore3(testclass2, true); assertIntEquals(testclass2.i, 41); assertIntEquals(testclass2.j, 43); testStoreStore3(testclass2, false); assertIntEquals(testclass2.i, 41); assertIntEquals(testclass2.j, 44); testStoreStore4(); assertIntEquals(TestClass.si, 62); int ret = testStoreStore5(testclass2, testclass2); assertIntEquals(testclass2.i, 72); assertIntEquals(ret, 71); testclass2.j = 88; ret = testStoreStore6(testclass2, testclass2); assertIntEquals(testclass2.i, 82); assertIntEquals(ret, 88); ret = testNoSideEffects(iarray); assertIntEquals(iarray[0], 101); assertIntEquals(iarray[1], 103); assertIntEquals(ret, 108); try { testThrow(testclass2, new Exception()); } catch (Exception e) {} assertIntEquals(testclass2.i, 55); assertIntEquals(testStoreStoreWithDeoptimize(new int[4]), 4); assertIntEquals($noinline$testByteArrayDefaultValue(), 0); assertIntEquals(testLocalArrayMerge1(true), 1); assertIntEquals(testLocalArrayMerge1(false), 1); assertIntEquals(testLocalArrayMerge2(true), 2); assertIntEquals(testLocalArrayMerge2(false), 3); assertIntEquals(testLocalArrayMerge3(true), 2); assertIntEquals(testLocalArrayMerge3(false), 1); assertIntEquals(testLocalArrayMerge4(true), 2); assertIntEquals(testLocalArrayMerge4(false), 2); assertIntEquals(testLocalArrayMerge5(new int[]{ 7 }, true), 7); assertIntEquals(testLocalArrayMerge5(new int[]{ 9 }, false), 9); assertIntEquals(testLocalArrayMerge6(new int[1], true, true), 1); assertIntEquals(testLocalArrayMerge6(new int[1], true, false), 2); assertIntEquals(testLocalArrayMerge6(new int[1], false, true), 2); assertIntEquals(testLocalArrayMerge6(new int[1], false, false), 1); assertIntEquals(testLocalArrayMerge7(new int[2], true, true), 1); assertIntEquals(testLocalArrayMerge7(new int[2], true, false), 2); assertIntEquals(testLocalArrayMerge7(new int[2], false, true), 0); assertIntEquals(testLocalArrayMerge7(new int[2], false, false), 0); assertIntEquals(testLocalArrayMerge8(true), 0); assertIntEquals(testLocalArrayMerge8(false), 0); TestClass[] tca = new TestClass[] { new TestClass(), null }; try { $noinline$testThrowingArraySet(tca, new TestClass2()); } catch (ArrayStoreException expected) { if (tca[0] != null) { throw new Error("tca[0] is not null"); } if (tca[1] == null) { throw new Error("tca[1] is null"); } } assertIntEquals(testLoop1(new TestClass(), 0), 0); assertIntEquals(testLoop1(new TestClass(), 1), 0); assertIntEquals(testLoop1(new TestClass(), 2), 1); assertIntEquals(testLoop1(new TestClass(), 3), 2); assertIntEquals(testLoop2(new TestClass(), 0), 1); assertIntEquals(testLoop2(new TestClass(), 1), 1); assertIntEquals(testLoop2(new TestClass(), 2), 1); assertIntEquals(testLoop2(new TestClass(), 3), 1); assertIntEquals(testLoop3(new TestClass(), 0), 1); assertIntEquals(testLoop3(new TestClass(), 1), 1); assertIntEquals(testLoop3(new TestClass(), 2), 1); assertIntEquals(testLoop3(new TestClass(), 3), 1); assertIntEquals(testLoop4(new TestClass(), 0), 0); assertIntEquals(testLoop4(new TestClass(), 1), 1); assertIntEquals(testLoop4(new TestClass(), 2), 2); assertIntEquals(testLoop4(new TestClass(), 3), 3); assertIntEquals(testLoop5(new TestClass(), 0), 0); assertIntEquals(testLoop5(new TestClass(), 1), 1); assertIntEquals(testLoop5(new TestClass(), 2), 2); assertIntEquals(testLoop5(new TestClass(), 3), 3); assertIntEquals(testLoop6(new TestClass(), 0), 0); assertIntEquals(testLoop6(new TestClass(), 1), 1); assertIntEquals(testLoop6(new TestClass(), 2), 2); assertIntEquals(testLoop6(new TestClass(), 3), 3); assertIntEquals(testLoop7(0), 0); assertIntEquals(testLoop7(1), 0); assertIntEquals(testLoop7(2), 0); assertIntEquals(testLoop7(3), 0); assertIntEquals(testLoop8(0), 1); assertIntEquals(testLoop8(1), 0); assertIntEquals(testLoop8(2), 1); assertIntEquals(testLoop8(3), 0); assertIntEquals(testLoop9(new TestClass(), 0), 0); assertIntEquals(testLoop9(new TestClass(), 1), 1); assertIntEquals(testLoop9(new TestClass(), 2), 2); assertIntEquals(testLoop9(new TestClass(), 3), 3); assertIntEquals(testLoop10(new TestClass(), 0), 2); assertIntEquals(testLoop10(new TestClass(), 1), 2); assertIntEquals(testLoop10(new TestClass(), 2), 2); assertIntEquals(testLoop10(new TestClass(), 3), 2); assertIntEquals(testLoop11(new TestClass(), 0), 1); assertIntEquals(testLoop11(new TestClass(), 1), 3); assertIntEquals(testLoop11(new TestClass(), 2), 2); assertIntEquals(testLoop11(new TestClass(), 3), 3); assertIntEquals(testLoop12(new TestClass(), 0), 1); assertIntEquals(testLoop12(new TestClass(), 1), 2); assertIntEquals(testLoop12(new TestClass(), 2), 3); assertIntEquals(testLoop12(new TestClass(), 3), 2); assertIntEquals(testLoop13(new TestClass(1, 2), 0), 0); assertIntEquals(testLoop13(new TestClass(1, 2), 1), 0); assertIntEquals(testLoop13(new TestClass(1, 2), 2), 0); assertIntEquals(testLoop13(new TestClass(1, 2), 3), 1); assertIntEquals(testLoop14(new TestClass2(), 0), 0); assertIntEquals(testLoop14(new TestClass2(), 1), 0); assertIntEquals(testLoop14(new TestClass2(), 2), 0); assertIntEquals(testLoop14(new TestClass2(), 3), 1); assertIntEquals(testLoop15(0), 0); assertIntEquals(testLoop15(1), 1); assertIntEquals(testLoop15(2), 1); assertIntEquals(testLoop15(3), 1); assertIntEquals(testLoop16(new TestClass(), 0), 0); assertIntEquals(testLoop16(new TestClass(), 1), 1); assertIntEquals(testLoop16(new TestClass(), 2), 2); assertIntEquals(testLoop16(new TestClass(), 3), 3); assertIntEquals(testLoop17(new TestClass(), 0), 2); assertIntEquals(testLoop17(new TestClass(), 1), 4); assertIntEquals(testLoop17(new TestClass(), 2), 2); assertIntEquals(testLoop17(new TestClass(), 3), 4); assertIntEquals(testLoop18(new TestClass(), 0), 0); assertIntEquals(testLoop18(new TestClass(), 1), 1); assertIntEquals(testLoop18(new TestClass(), 2), 2); assertIntEquals(testLoop18(new TestClass(), 3), 3); assertIntEquals(testLoop19(new TestClass(), 0), 0); assertIntEquals(testLoop19(new TestClass(), 1), 1); assertIntEquals(testLoop19(new TestClass(), 2), 2); assertIntEquals(testLoop19(new TestClass(), 3), 3); assertIntEquals(testLoop20(new TestClass(), 0), 0); assertIntEquals(testLoop20(new TestClass(), 1), 1); assertIntEquals(testLoop20(new TestClass(), 2), 2); assertIntEquals(testLoop20(new TestClass(), 3), 3); assertIntEquals(testLoop21(new TestClass(), 0), 0); assertIntEquals(testLoop21(new TestClass(), 1), 1); assertIntEquals(testLoop21(new TestClass(), 2), 2); assertIntEquals(testLoop21(new TestClass(), 3), 3); assertIntEquals(testLoop22(new TestClass(), 0), 0); assertIntEquals(testLoop22(new TestClass(), 1), 1); assertIntEquals(testLoop22(new TestClass(), 2), 2); assertIntEquals(testLoop22(new TestClass(), 3), 3); assertIntEquals(testLoop23(new TestClass(), 0), -1); assertIntEquals(testLoop23(new TestClass(), 1), 2); assertIntEquals(testLoop23(new TestClass(), 2), 1); assertIntEquals(testLoop23(new TestClass(), 3), 2); assertIntEquals(testLoop24(new TestClass(), 0), -1); assertIntEquals(testLoop24(new TestClass(), 1), 2); assertIntEquals(testLoop24(new TestClass(), 2), 1); assertIntEquals(testLoop24(new TestClass(), 3), -2); assertIntEquals(testLoop25(new TestClass(), 0), 2); assertIntEquals(testLoop25(new TestClass(), 1), 2); assertIntEquals(testLoop25(new TestClass(), 2), 4); assertIntEquals(testLoop25(new TestClass(), 3), -1); assertIntEquals(testLoop26(new TestClass(), 0), 1); assertIntEquals(testLoop26(new TestClass(), 1), 0); assertIntEquals(testLoop26(new TestClass(), 2), 0); assertIntEquals(testLoop26(new TestClass(), 3), 0); assertIntEquals(testLoop27(new TestClass(), 0), 1); assertIntEquals(testLoop27(new TestClass(), 1), 1); assertIntEquals(testLoop27(new TestClass(), 2), 0); assertIntEquals(testLoop27(new TestClass(), 3), 0); assertIntEquals(testLoop28(new TestClass(1, 2), 0), 0); assertIntEquals(testLoop28(new TestClass(1, 2), 1), 0); assertIntEquals(testLoop28(new TestClass(1, 2), 2), 0); assertIntEquals(testLoop28(new TestClass(1, 2), 3), 1); assertIntEquals(testLoop29(0), 0); assertIntEquals(testLoop29(1), 1); assertIntEquals(testLoop29(2), 3); assertIntEquals(testLoop29(3), 6); assertIntEquals(testLoop30(0), 0); assertIntEquals(testLoop30(1), 1); assertIntEquals(testLoop30(2), 2); assertIntEquals(testLoop30(3), 3); assertIntEquals(testLoop31(0), 0); assertIntEquals(testLoop31(1), 0); assertIntEquals(testLoop31(2), 0); assertIntEquals(testLoop31(3), 0); assertIntEquals(testLoop32(new TestClass(), 0), -1); assertIntEquals(testLoop32(new TestClass(), 1), 2); assertIntEquals(testLoop32(new TestClass(), 2), 1); assertIntEquals(testLoop32(new TestClass(), 3), -2); assertIntEquals(testLoop33(new TestClass(), 0), 0); assertIntEquals(testLoop33(new TestClass(), 1), 0); assertIntEquals(testLoop33(new TestClass(), 2), 0); assertIntEquals(testLoop33(new TestClass(), 3), 0); assertIntEquals(testLoop34(0), 0); assertIntEquals(testLoop34(1), 1); assertIntEquals(testLoop34(2), 3); assertIntEquals(testLoop34(3), 6); assertIntEquals(testLoop35(0), 0); assertIntEquals(testLoop35(1), 1); assertIntEquals(testLoop35(2), 3); assertIntEquals(testLoop35(3), 6); assertIntEquals(testLoop36(4), 6); assertIntEquals(testLoop37(4), 6); assertIntEquals(testLoop38(4, new int[4]), 0); assertIntEquals(testNestedLoop1(new TestClass(), 0), 1); assertIntEquals(testNestedLoop1(new TestClass(), 1), 1); assertIntEquals(testNestedLoop1(new TestClass(), 2), 1); assertIntEquals(testNestedLoop1(new TestClass(), 3), 1); assertIntEquals(testNestedLoop2(new TestClass(), 0), 1); assertIntEquals(testNestedLoop2(new TestClass(), 1), 2); assertIntEquals(testNestedLoop2(new TestClass(), 2), 2); assertIntEquals(testNestedLoop2(new TestClass(), 3), 2); assertIntEquals(testNestedLoop3(new TestClass(), 0), 1); assertIntEquals(testNestedLoop3(new TestClass(), 1), 2); assertIntEquals(testNestedLoop3(new TestClass(), 2), 2); assertIntEquals(testNestedLoop3(new TestClass(), 3), 2); assertIntEquals(testNestedLoop4(new TestClass(), 0), 1); assertIntEquals(testNestedLoop4(new TestClass(), 1), 1); assertIntEquals(testNestedLoop4(new TestClass(), 2), 2); assertIntEquals(testNestedLoop4(new TestClass(), 3), 2); assertIntEquals(testNestedLoop5(new TestClass(), 0), 1); assertIntEquals(testNestedLoop5(new TestClass(), 1), 2); assertIntEquals(testNestedLoop5(new TestClass(), 2), 2); assertIntEquals(testNestedLoop5(new TestClass(), 3), 2); assertIntEquals(testNestedLoop6(new TestClass(), 0), 1); assertIntEquals(testNestedLoop6(new TestClass(), 1), 1); assertIntEquals(testNestedLoop6(new TestClass(), 2), 2); assertIntEquals(testNestedLoop6(new TestClass(), 3), 2); assertIntEquals(testNestedLoop7(new TestClass(), 0), 0); assertIntEquals(testNestedLoop7(new TestClass(), 1), 1); assertIntEquals(testNestedLoop7(new TestClass(), 2), 2); assertIntEquals(testNestedLoop7(new TestClass(), 3), 3); assertIntEquals(testNestedLoop8(new TestClass(), 0), 0); assertIntEquals(testNestedLoop8(new TestClass(), 1), 0); assertIntEquals(testNestedLoop8(new TestClass(), 2), 0); assertIntEquals(testNestedLoop8(new TestClass(), 3), 0); assertLongEquals(testOverlapLoop(10), 34l); assertLongEquals(testOverlapLoop(50), 7778742049l); assertIntEquals($noinline$testPartialEscape1(new TestClass(), true), 1); assertIntEquals($noinline$testPartialEscape1(new TestClass(), false), 0); TypeConversions.main(); } }