blob: 875444941d9817c899d7f17aba87bef22e3927b9 [file] [log] [blame]
/*
* 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: <<Int1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Int2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Obj:l\d+>> ParameterValue
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Int1>>]
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Int2>>]
/// CHECK-DAG: <<GetField:i\d+>> InstanceFieldGet [{{l\d+}}]
/// CHECK-DAG: Return [<<GetField>>]
/// CHECK-START: int Main.test5(TestClass, boolean) load_store_elimination (after)
/// CHECK-DAG: <<Int1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Int2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Obj:l\d+>> ParameterValue
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Int1>>]
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Int2>>]
/// CHECK-DAG: <<Phi:i\d+>> Phi [<<Arg1:i\d+>>,<<Arg2:i\d+>>]
/// CHECK-DAG: Return [<<Phi>>]
/// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>"]) == set(["<<Int1>>","<<Int2>>"])
/// 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: <<Const2:i\d+>> IntConstant 2
/// CHECK: StaticFieldSet
/// CHECK: Return [<<Const2>>]
/// 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: <<Const0:i\d+>> IntConstant 0
/// CHECK-NOT: NewInstance
/// CHECK-NOT: InstanceFieldSet
/// CHECK-NOT: InstanceFieldGet
/// CHECK: Return [<<Const0>>]
// 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: <<Int1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Int2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Int3:i\d+>> IntConstant 3
/// CHECK-DAG: <<Obj:l\d+>> NewInstance
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Int3>>]
/// CHECK-DAG: <<Add1:i\d+>> Add [<<Get1:i\d+>>,<<Int1>>]
/// CHECK-DAG: <<Get1>> InstanceFieldGet [<<Obj>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Add1>>]
/// CHECK-DAG: <<Add2:i\d+>> Add [<<Get2:i\d+>>,<<Int2>>]
/// CHECK-DAG: <<Get2>> InstanceFieldGet [<<Obj>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Add2>>]
/// CHECK-DAG: Return [<<Get3:i\d+>>]
/// CHECK-DAG: <<Get3>> InstanceFieldGet [<<Obj>>]
/// CHECK-START: int Main.test23(boolean) load_store_elimination (after)
/// CHECK-DAG: <<Int1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Int2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Int3:i\d+>> IntConstant 3
/// CHECK-DAG: <<Add1:i\d+>> Add [<<Int3>>,<<Int1>>]
/// CHECK-DAG: <<Add2:i\d+>> Add [<<Int3>>,<<Int2>>]
/// CHECK-DAG: <<Phi:i\d+>> Phi [<<Arg1:i\d+>>,<<Arg2:i\d+>>]
/// CHECK-DAG: Return [<<Phi>>]
/// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>"]) == set(["<<Add1>>","<<Add2>>"])
/// 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: <<True:i\d+>> IntConstant 1
/// CHECK-DAG: <<Float8:f\d+>> FloatConstant 8
/// CHECK-DAG: <<Float42:f\d+>> FloatConstant 42
/// CHECK-DAG: <<Obj:l\d+>> NewInstance
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<True>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Float8>>]
/// CHECK-DAG: <<GetTest:z\d+>> InstanceFieldGet [<<Obj>>]
/// CHECK-DAG: <<GetField:f\d+>> InstanceFieldGet [<<Obj>>]
/// CHECK-DAG: <<Select:f\d+>> Select [<<Float42>>,<<GetField>>,<<GetTest>>]
/// CHECK-DAG: Return [<<Select>>]
/// CHECK-START: float Main.test24() load_store_elimination (after)
/// CHECK-DAG: <<True:i\d+>> IntConstant 1
/// CHECK-DAG: <<Float8:f\d+>> FloatConstant 8
/// CHECK-DAG: <<Float42:f\d+>> FloatConstant 42
/// CHECK-DAG: <<Select:f\d+>> Select [<<Float42>>,<<Float8>>,<<True>>]
/// CHECK-DAG: Return [<<Select>>]
/// 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: <<Int1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Int2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Int3:i\d+>> IntConstant 3
/// CHECK-DAG: <<Int5:i\d+>> IntConstant 5
/// CHECK-DAG: <<Int6:i\d+>> IntConstant 6
/// CHECK-DAG: <<Obj:l\d+>> NewInstance
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Int1>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Int2>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Int3>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Int5>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Int6>>]
/// CHECK-DAG: <<GetField:i\d+>> InstanceFieldGet [<<Obj>>]
/// CHECK-DAG: Return [<<GetField>>]
/// CHECK-START: int Main.test25(boolean, boolean, boolean) load_store_elimination (after)
/// CHECK-DAG: <<Int1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Int2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Int3:i\d+>> IntConstant 3
/// CHECK-DAG: <<Int5:i\d+>> IntConstant 5
/// CHECK-DAG: <<Int6:i\d+>> IntConstant 6
/// CHECK-DAG: <<Phi:i\d+>> Phi [<<Arg1:i\d+>>,<<Arg2:i\d+>>,<<Arg3:i\d+>>,<<Arg4:i\d+>>,<<Arg5:i\d+>>]
/// CHECK-DAG: Return [<<Phi>>]
/// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>","<<Arg3>>","<<Arg4>>","<<Arg5>>"]) == set(["<<Int1>>","<<Int2>>","<<Int3>>","<<Int5>>","<<Int6>>"])
/// 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: <<Float0:f\d+>> FloatConstant 0
/// CHECK-DAG: <<Float1:f\d+>> FloatConstant 1
/// CHECK-DAG: <<Float2:f\d+>> FloatConstant 2
/// CHECK-DAG: <<Float3:f\d+>> FloatConstant 3
/// CHECK-DAG: <<Float8:f\d+>> FloatConstant 8
/// CHECK-DAG: <<Obj:l\d+>> NewInstance
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Float8>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Float0>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Float1>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Float2>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Float3>>]
/// CHECK-DAG: <<GetField:f\d+>> InstanceFieldGet [<<Obj>>]
/// CHECK-DAG: Return [<<GetField>>]
/// CHECK-START: float Main.test26(int) load_store_elimination (after)
/// CHECK-DAG: <<Float0:f\d+>> FloatConstant 0
/// CHECK-DAG: <<Float1:f\d+>> FloatConstant 1
/// CHECK-DAG: <<Float2:f\d+>> FloatConstant 2
/// CHECK-DAG: <<Float3:f\d+>> FloatConstant 3
/// CHECK-DAG: <<Float8:f\d+>> FloatConstant 8
/// CHECK-DAG: <<Phi:f\d+>> Phi [<<Arg1:f\d+>>,<<Arg2:f\d+>>,<<Arg3:f\d+>>,<<Arg4:f\d+>>]
/// CHECK-DAG: Return [<<Phi>>]
/// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>","<<Arg3>>","<<Arg4>>"]) == set(["<<Float0>>","<<Float1>>","<<Float2>>","<<Float3>>"])
/// 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: <<Int1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Obj:l\d+>> NewInstance
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Int1>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Int1>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Int1>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Int1>>]
/// CHECK-DAG: <<GetField:i\d+>> InstanceFieldGet [<<Obj>>]
/// CHECK-DAG: Return [<<GetField>>]
/// CHECK-START: int Main.test27(boolean, boolean) load_store_elimination (after)
/// CHECK-DAG: <<Int1:i\d+>> IntConstant 1
/// CHECK-DAG: Return [<<Int1>>]
/// 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: <<Int0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Int5:i\d+>> IntConstant 5
/// CHECK-DAG: <<Int6:i\d+>> IntConstant 6
/// CHECK-DAG: <<Array:l\d+>> NewArray
/// CHECK-DAG: ArraySet [<<Array>>,<<Int0>>,<<Int5>>]
/// CHECK-DAG: ArraySet [<<Array>>,<<Int0>>,<<Int6>>]
/// CHECK-DAG: <<GetIndex:i\d+>> ArrayGet [<<Array>>,<<Int0>>]
/// CHECK-DAG: Return [<<GetIndex>>]
/// CHECK-START: int Main.test28(boolean, boolean) load_store_elimination (after)
/// CHECK-DAG: <<Int0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Int5:i\d+>> IntConstant 5
/// CHECK-DAG: <<Int6:i\d+>> IntConstant 6
/// CHECK-DAG: <<Phi:i\d+>> Phi [<<Arg1:i\d+>>,<<Arg2:i\d+>>,<<Arg3:i\d+>>]
/// CHECK-DAG: Return [<<Phi>>]
/// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>","<<Arg3>>"]) == set(["<<Int0>>","<<Int5>>","<<Int6>>"])
/// 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: <<Float2:f\d+>> FloatConstant 2
/// CHECK-DAG: <<Float5:f\d+>> FloatConstant 5
/// CHECK-DAG: <<Float8:f\d+>> FloatConstant 8
/// CHECK-DAG: <<Obj:l\d+>> NewInstance
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Float8>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Float2>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Float5>>]
/// CHECK-DAG: <<GetField:f\d+>> InstanceFieldGet [<<Obj>>]
/// CHECK-DAG: Return [<<GetField>>]
/// CHECK-START: float Main.test29(boolean) load_store_elimination (after)
/// CHECK-DAG: <<Float2:f\d+>> FloatConstant 2
/// CHECK-DAG: <<Float5:f\d+>> FloatConstant 5
/// CHECK-DAG: <<Float8:f\d+>> FloatConstant 8
/// CHECK-DAG: <<Phi:f\d+>> Phi [<<Arg1:f\d+>>,<<Arg2:f\d+>>]
/// CHECK-DAG: Return [<<Phi>>]
/// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>"]) == set(["<<Float5>>","<<Float2>>"])
/// 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: <<Int1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Int2:i\d+>> IntConstant 2
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Int1>>]
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Int2>>]
/// CHECK-DAG: <<GetField:i\d+>> InstanceFieldGet [{{l\d+}}]
/// CHECK-DAG: Return [<<GetField>>]
/// CHECK-START: int Main.test30(TestClass, boolean) load_store_elimination (after)
/// CHECK-DAG: <<Int1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Int2:i\d+>> IntConstant 2
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Int1>>]
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Int2>>]
/// CHECK-DAG: <<GetField:i\d+>> InstanceFieldGet [{{l\d+}}]
/// CHECK-DAG: Return [<<GetField>>]
/// 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: <<Int2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Int5:i\d+>> IntConstant 5
/// CHECK-DAG: <<Int6:i\d+>> IntConstant 6
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Int5>>] field_name:{{.*TestClass.i}}
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Int6>>] field_name:{{.*TestClass.i}}
/// CHECK-DAG: <<Get1:i\d+>> InstanceFieldGet [{{l\d+}}] field_name:{{.*TestClass.i}}
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Get1>>] field_name:{{.*TestClass.j}}
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Int2>>] field_name:{{.*TestClass.i}}
/// CHECK-DAG: <<Get2:i\d+>> InstanceFieldGet [{{l\d+}}]
/// CHECK-DAG: Return [<<Get2>>]
/// CHECK-START: int Main.test31(boolean, boolean) load_store_elimination (after)
/// CHECK-DAG: <<Int2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Int5:i\d+>> IntConstant 5
/// CHECK-DAG: <<Int6:i\d+>> IntConstant 6
/// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Int5>>,<<Int6>>]
/// CHECK-DAG: <<Phi2:i\d+>> Phi [<<Phi1>>,<<Int2>>]
/// CHECK-DAG: Return [<<Phi2>>]
/// 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: <<Int1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Int10:i\d+>> IntConstant 10
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Int1>>] field_name:{{.*TestClass2.i}}
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Int1>>] field_name:{{.*TestClass2.j}}
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Int1>>] field_name:{{.*TestClass2.k}}
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Int1>>] field_name:{{.*TestClass2.l}}
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Int1>>] field_name:{{.*TestClass2.m}}
/// CHECK-DAG: Return [<<Int10>>]
/// CHECK-START: int Main.test32(int) load_store_elimination (after)
/// CHECK-DAG: <<Int10:i\d+>> IntConstant 10
/// CHECK-DAG: Return [<<Int10>>]
/// 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:i\d+>> Phi
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Phi>>]
/// CHECK-START: int Main.test33(TestClass, boolean) load_store_elimination (after)
/// CHECK-DAG: InstanceFieldSet
/// CHECK-DAG: InstanceFieldSet
/// CHECK-DAG: <<Phi:i\d+>> 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:i\d+>> Phi
/// CHECK-DAG: InstanceFieldSet [{{l\d+}},<<Phi>>]
/// 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<Object> getWeakReference() {
return new java.lang.ref.WeakReference<>(new Object());
}
static void testFinalizableByForcingGc() {
testFinalizable();
java.lang.ref.WeakReference<Object> 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<Object> 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: <<Value:b\d+>> ArrayGet
/// CHECK-DAG: Return [<<Value>>]
/// CHECK-START: int Main.$noinline$testByteArrayDefaultValue() load_store_elimination (after)
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: Return [<<Const0>>]
/// 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: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<A:l\d+>> NewArray
/// CHECK-DAG: ArraySet [<<A>>,<<Const0>>,<<Const0>>]
/// CHECK-DAG: ArraySet [<<A>>,<<Const0>>,<<Const1>>]
/// CHECK-DAG: ArraySet [<<A>>,<<Const0>>,<<Const1>>]
/// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<A>>,<<Const0>>]
/// CHECK-DAG: Return [<<Get>>]
//
/// CHECK-START: int Main.testLocalArrayMerge1(boolean) load_store_elimination (after)
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: Return [<<Const1>>]
//
/// 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: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
/// CHECK-DAG: <<A:l\d+>> NewArray
/// CHECK-DAG: ArraySet [<<A>>,<<Const0>>,<<Const1>>]
/// CHECK-DAG: ArraySet [<<A>>,<<Const0>>,<<Const2>>]
/// CHECK-DAG: ArraySet [<<A>>,<<Const0>>,<<Const3>>]
/// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<A>>,<<Const0>>]
/// CHECK-DAG: Return [<<Get>>]
/// CHECK-START: int Main.testLocalArrayMerge2(boolean) load_store_elimination (after)
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
/// CHECK-DAG: <<Phi:i\d+>> Phi [<<Arg1:i\d+>>,<<Arg2:i\d+>>]
/// CHECK-DAG: Return [<<Phi>>]
/// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>"]) == set(["<<Const2>>","<<Const3>>"])
/// 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: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<A:l\d+>> NewArray
/// CHECK-DAG: ArraySet [<<A>>,<<Const0>>,<<Const1>>]
/// CHECK-DAG: ArraySet [<<A>>,<<Const0>>,<<Const2>>]
/// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<A>>,<<Const0>>]
/// CHECK-DAG: Return [<<Get>>]
/// 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: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<A:l\d+>> NewArray
/// CHECK-DAG: ArraySet [<<A>>,<<Const0>>,<<Const0>>]
/// CHECK-DAG: ArraySet [<<A>>,<<Const0>>,<<Const1>>]
/// CHECK-DAG: ArraySet [<<A>>,<<Const0>>,<<Const1>>]
/// CHECK-DAG: <<Get1:b\d+>> ArrayGet [<<A>>,<<Const0>>]
/// CHECK-DAG: <<Get2:a\d+>> ArrayGet [<<A>>,<<Const0>>]
/// CHECK-DAG: <<Add:i\d+>> Add [<<Get1>>,<<Get2>>]
/// CHECK-DAG: Return [<<Add>>]
//
/// CHECK-START: int Main.testLocalArrayMerge4(boolean) load_store_elimination (after)
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Cnv1:b\d+>> TypeConversion [<<Const1>>]
/// CHECK-DAG: <<Cnv2:a\d+>> TypeConversion [<<Const1>>]
/// CHECK-DAG: <<Add:i\d+>> Add [<<Cnv1>>,<<Cnv2>>]
/// CHECK-DAG: Return [<<Add>>]
//
/// 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: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
/// CHECK-DAG: ArraySet
/// CHECK-DAG: ArraySet
/// CHECK-DAG: <<Phi:i\d+>> Phi [<<Arg1:i\d+>>,<<Arg2:i\d+>>]
/// CHECK-DAG: Return [<<Phi>>]
/// CHECK-DAG: <<Sub:i\d+>> Sub [<<Const3>>,<<Phi>>]
/// CHECK-DAG: Return [<<Sub>>]
/// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>"]) == set(["<<Const1>>","<<Const2>>"])
/// 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: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: ArraySet
/// CHECK-DAG: ArraySet
/// CHECK-DAG: ArraySet
/// CHECK-DAG: ArraySet
/// CHECK-DAG: Return [<<Phi2:i\d+>>]
/// CHECK-DAG: <<Phi2>> Phi [<<Arg3:i\d+>>,<<Arg4:i\d+>>]
/// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Arg1:i\d+>>,<<Arg2:i\d+>>]
/// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>"]) == set(["<<Const1>>","<<Const2>>"])
/// CHECK-EVAL: set(["<<Arg3>>","<<Arg4>>"]) == set(["<<Const0>>","<<Phi1>>"])
/// 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")
//
/// 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")
//
/// 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);
}
}