// 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 { staticboolean sVisited = false; staticfinalint VALUE1 = 0xbeef; staticfinalint VALUE2 = 0xcafe; int i;
protectedvoid finalize() { if (i != VALUE1) {
System.out.println("Where is the beef?");
}
sVisited = true;
}
}
/// 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. staticint 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.test5(TestClass, boolean) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet
// Set and merge different values in two branches. staticint test5(TestClass obj, boolean b) { if (b) {
obj.i = 1;
} else {
obj.i = 2;
} return obj.i;
}
// Setting the same value doesn't clear the value for aliased locations. staticint test6(TestClass obj1, TestClass obj2, boolean b) {
obj1.i = 1;
obj1.j = 2; if (b) {
obj2.j = 2;
} return obj1.j + obj2.j;
}
// I/F, J/D aliasing should not happen any more and LSE should eliminate the load. staticfloat test19(float[] fa1, float[] fa2) {
fa1[0] = fa2[0]; return fa1[0];
}
// 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;
}
// Loop side effects can kill heap values, stores need to be kept in that case. staticvoid 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);
}
// 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. staticint 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;
}
// Test heap value merging from multiple branches. staticint 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.
}
// Test implicit type conversion in branches. staticfloat 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 (after) /// CHECK-NOT: Phi
// Don't merge different values in two branches for different variables. staticint test30(TestClass obj, boolean b) { if (b) {
obj.i = 1;
} else {
obj.j = 2;
} return obj.i;
}
// Test no unused Phi instructions are created. staticint test32(int i) {
TestClass2 obj = new TestClass2(); // By default, i/j/k/l/m are initialized to 0. switch (i) { case1: obj.i = 1; break; case2: obj.j = 1; break; case3: obj.k = 1; break; case4: obj.l = 1; break; case5: 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. return10;
}
// 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. staticint 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;
}
// Test Phi matching for load elimination. staticint 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;
}
// Test preserving observable stores. staticint 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;
}
// Test eliminating store of the same value after eliminating non-observable stores. staticint test38(TestClass obj, boolean x) {
obj.i = 1; if (x) { return1; // 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. return2;
}
// Check that the stores to array[0] are not eliminated since we can throw in between the stores. privatestaticvoid $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$testConversion2(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet
// Test moving type conversion when needed. staticint $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;
}
Runtime runtime = Runtime.getRuntime(); for (int i = 0; i < 20; ++i) {
runtime.gc();
System.runFinalization(); try { Thread.sleep(1);
} catch (InterruptedException e) { thrownew 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) { thrownew AssertionError(e);
} if (!Finalizable.sVisited) {
System.out.println("finalize() not called.");
} return;
}
}
System.out.println("testFinalizableByForcingGc() failed to force gc.");
}
Runtime runtime = Runtime.getRuntime(); for (int i = 0; i < 20; ++i) {
runtime.gc();
System.runFinalization(); try { Thread.sleep(1);
} catch (InterruptedException e) { thrownew 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) { thrownew AssertionError(e);
} if (!Finalizable.sVisited) {
System.out.println("finalize() not called.");
} return;
}
}
System.out.println("testFinalizableWithLoopByForcingGc() failed to force gc.");
}
// Test that HSelect creates alias. staticint $noinline$testHSelect(boolean b) {
TestClass obj = new TestClass();
TestClass obj2 = null;
obj.i = 0xdead; if (b) {
obj2 = obj;
} return obj2.i;
}
staticint 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;
}
privatestaticint 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;
}
privatestaticint 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;
}
// 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 privatestaticint testAllocationEliminationOfArray2() { // Array can be eliminated because LSE can reduce the array accesses into // integer constants. int[] array = newint[3];
array[1] = 4;
array[2] = 7; int sum = 0; for (int e : array) {
sum += e;
} return sum;
}
/// CHECK-START: int Main.testAllocationEliminationOfArray6(boolean) load_store_elimination (after) /// CHECK: NewArray /// CHECK: ArraySet /// CHECK: ArraySet /// CHECK: ArrayGet privatestaticint 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 = newint[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.testLocalArrayMerge2(boolean) load_store_elimination (after) /// CHECK-NOT: NewArray /// CHECK-NOT: ArraySet /// CHECK-NOT: ArrayGet privatestaticint 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.testLocalArrayMerge5(int[], boolean) load_store_elimination (after) /// CHECK-NOT: ArraySet
// Test eliminating store of the same value after eliminating non-observable stores. privatestaticint 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 (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: <<Sub:i\d+>> Sub [<<Const3>>,<<Phi:i\d+>>] /// CHECK-DAG: <<Phi>> Phi [<<PhiArg1:i\d+>>,<<PhiArg2:i\d+>>] // TODO: Due to missing flagging support in checker, this checker test is disabled. // Enable it when we implement flagging support or during the flag cleanup for // `com::android::art::rw::flags::packed_switch_simplification()`. // CHECK-DAG: <<Phi2:i\d+>> Phi [<<Phi2Arg1:i\d+>>,<<Phi2Arg2:i\d+>>] // CHECK-EVAL: set(["<<PhiArg1>>","<<PhiArg2>>"]) == set(["<<Const1>>","<<Const2>>"]) // CHECK-EVAL: set(["<<Phi2Arg1>>","<<Phi2Arg2>>"]) == set(["<<Phi>>","<<Sub>>"])
/// CHECK-START: int Main.testLocalArrayMerge6(int[], boolean, boolean) load_store_elimination (after) /// CHECK: Phi // TODO: Due to missing flagging support in checker, this checker test is disabled. // Enable it when we implement flagging support or during the flag cleanup for // `com::android::art::rw::flags::packed_switch_simplification()`. // CHECK: Phi // CHECK-NOT: Phi
// Test that we create a single Phi for eliminating two loads in different blocks. privatestaticint 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 { return3 - a[0];
}
}
// Test Merging default value and an identical value. privatestaticint testLocalArrayMerge8(boolean x) { int[] a = newint[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: int Main.testLoop1(TestClass, int) load_store_elimination (after) /// CHECK-NOT: InstanceFieldGet
// Test Phi creation for load elimination with loop. privatestaticint testLoop1(TestClass obj, int n) {
obj.i = 0; for (int i = 0; i < n; ++i) {
obj.i = i;
} return obj.i;
}
// Test that we do not create any Phis for load elimination when // the heap value was not modified in the loop. privatestaticint 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 (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. privatestaticint 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 (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. privatestaticint 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 (after) /// CHECK-NOT: InstanceFieldGet
// Test eliminating loads and stores that just shuffle the same value between // different heap locations. privatestaticint 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 (after) /// CHECK-NOT: InstanceFieldGet
// Test eliminating loads and stores that just shuffle the same value between // different heap locations, or store the same value. privatestaticint 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;
}
// Test eliminating loads and stores that just shuffle the default value between // different heap locations, or store the same value. privatestaticint 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;
}
// 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. privatestaticint 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;
}
// 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. privatestaticint 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;
}
// Test load elimination after finding a non-eliminated load depending // on loop Phi placeholder. privatestaticint 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 (after) /// CHECK-NOT: InstanceFieldGet
// Test load elimination creating two Phis that depend on each other. privatestaticint 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 (after) /// CHECK-NOT: InstanceFieldGet
// Test load elimination creating a single Phi with more than 2 inputs. privatestaticint 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;
}
// Test load elimination in a loop after determing that the first field load // (depending on loop Phi placeholder) cannot be eliminated. privatestaticint testLoop14(TestClass2 obj, int n) { int[] a = newint[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 (after) /// CHECK-DAG: NewArray /// CHECK-IF: hasIsaFeature("sve") and os.environ.get('ART_FORCE_TRY_PREDICATED_SIMD') == 'true' // /// CHECK-DAG: VecPredWhile /// CHECK-DAG: VecStore // /// CHECK-ELSE: // /// CHECK-DAG: ArraySet // /// CHECK-FI: // /// CHECK-DAG: ArrayGet // Test that aliasing array store in the loop is not eliminated // when a loop Phi placeholder is marked for keeping. privatestaticint testLoop15(int n) { int[] a = newint[n + 1]; for (int i = 0; i < n; ++i) {
a[i] = 1; // Cannot be eliminated due to aliasing.
} return a[0];
}
// Test that we match an existing loop Phi for eliminating a load. staticint 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;
}
// Test that we match an existing non-loop Phi for eliminating a load, // one input of the Phi being invariant across a preceding loop. staticint 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 (after) /// CHECK-NOT: ArrayGet
// Test eliminating a load of the default value in a loop // with the array index being defined inside the loop. staticint 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 = newint[n]; for (int i = 0; i < n; ++i) {
obj.i = a0[i];
} return n;
}
// 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. staticint 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 = newint[n]; for (int i = 0; i < n; ++i) {
obj.i = a0[i];
a0[i] = 0; // Store the same value as default.
} return n;
}
// 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. staticint 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 = newint[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;
}
// Test load elimination when an instance field is used as the loop variable. staticint 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;
}
// Test load and store elimination when an instance field is used as the loop // variable and then overwritten after the loop. staticint 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;
}
// Test matching Phis for store elimination. staticint 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 (after) /// CHECK-NOT: InstanceFieldGet
// Test that we do not match multiple dependent Phis for load and store elimination. staticint 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;
}
// Test load elimination creating a reference Phi. staticint 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;
}
// Test load elimination creating two reference Phis that depend on each other. staticint 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;
}
// 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. privatestaticint testLoop28(TestClass obj, int n) {
obj.i = 1; int[] a = newint[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];
}
// 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. privatestaticint testLoop29(int n) { int[] a = newint[4]; int sum = 0; for (int i = 0; i < n; ) { int value = a[i] + 1;
sum += value;
++i;
a[i] = value;
} return sum;
}
// Test that ArraySet with default value does not prevent matching ArrayGet // for the same array to the default value. privatestaticint testLoop30(int n) { int[] a = newint[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;
}
// Test that ArraySet with default value read from the array does not // prevent matching ArrayGet for the same array to the default value. privatestaticint testLoop31(int n) { int[] a = newint[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;
}
// Test matching Phis for store elimination. staticint 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 (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. staticint testLoop33(TestClass obj, int n) {
obj.i = 0; int[] a0 = newint[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;
}
// 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. privatestaticint testLoop34(int n) { int[] a = newint[n + 1]; int sum = 0; for (int i = 0; i < n; ) { int value = a[i] + 1;
sum += value;
++i;
a[i] = value;
} return sum;
}
// 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. privatestaticint testLoop35(int n) { int[] a = newint[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;
}
// 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. privatestaticint testLoop36(int n) { int[] a = newint[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;
}
// 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()`. privatestaticint testLoop37(int n) { int[] a = newint[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;
}
privatestaticint[] $noinline$returnArg(int[] a) { return a;
}
/// 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. privatestaticint testLoop38(int n, int[] b) { int[] a = newint[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;
}
// Test heap value clobbering in nested loop. privatestaticint 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;
}
// 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. privatestaticint 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;
}
// 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. privatestaticint 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;
}
// Test creating loop Phis for both inner and outer loop to eliminate a load. privatestaticint 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;
}
// 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. privatestaticint 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 (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. privatestaticint 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 = newint[n]; for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) {
obj.i = a0[j];
}
} return n;
}
// Test reference type propagation for Phis created for outer and inner loop. privatestaticint 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;
}
// Test that we don't incorrectly remove writes needed by later loop iterations // NB This is fibonacci numbers privatestaticlong testOverlapLoop(int cnt) { long[] w = newlong[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;
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.94Bemerkung:
(vorverarbeitet am 2026-06-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.