Improved LSE: Replacing loads with Phis.

Create "Phi placeholders" for tracking heap values that can
merge from different values and try to match existing Phis
or create new Phis to replace loads. For Phi placeholders
from loop headers we do not know whether they are fed by
unknown values through back-edges when processing the loop
header, so we delay processing loads that depend on them
until we walked the entire graph. We then try to match them
with existing instructions (when the location is unchanged
in the loop) or Phis or create new Phis if needed. If we
find a loop Phi placeholder fed with unknown value from a
back-edge, we mark the Phi placeholder unreplaceable and
reprocess loads and stores to propagate the unknown value.
This can sometimes allow other loads to be replaced. At the
end we re-calculate the heap values to find stores that can
be eliminated because they write over the same value.

Golem results:
  art-opt-cc           arm  arm64    x86 x86-64
  CaffeineFloat      +6.7%  +3.0%  +5.9%  +3.8%
  KotlinMicroWhen   +33.7%  +4.8%  +1.8%  +0.6%
  art-opt (more noisy than art-opt-cc)
  CaffeineFloat      +4.1%  +4.4%  +7.8% +10.5%
  KotlinMicroWhen   +33.6%  +2.0%  +1.8%  +1.8%
The MoveLiteralColumn benchmark seems to gain significantly
(up to 22% on art-opt-cc but under 10% on art-opt) but it is
very noisy and the results are therefore unreliable.

Insignificant code size changes for aosp_blueline-userdebug:
  - before:
    arm boot*.oat: 15303468
    arm64 boot*.oat: 18184736
    services.odex: 25195944
    grep -c pAllocObject boot.arm64.oatdump.txt: 27213
    grep -c pAllocArray boot.arm64.oatdump.txt: 3620
  - after:
    arm boot*.oat: 15299524 (-4KiB, -0.03%)
    arm64 boot*.oat: 18176528 (-8KiB, -0.05%)
    services.odex: 25191832 (-4KiB, -0.02%)
    grep -c pAllocObject boot.arm64.oatdump.txt: 27206 (-7)
    grep -c pAllocArray boot.arm64.oatdump.txt: 3615 (-5)

Test: New tests in 530-checker-lse.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Test: blueline-userdebug boots.
Bug: 77906240
Change-Id: Ia9fe0cd3530f9d3941650dfefc00a7f7fd821994
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index 9672d97..34a445f 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -54,6 +54,9 @@
 class TestClass2 {
   int i;
   int j;
+  int k;
+  int l;
+  int m;
 }
 
 class TestClass3 {
@@ -139,6 +142,7 @@
   /// CHECK: InstanceFieldSet
   /// CHECK: InstanceFieldSet
   /// CHECK: InstanceFieldSet
+  /// CHECK: InstanceFieldSet
   /// CHECK: InstanceFieldGet
   /// CHECK: InstanceFieldGet
   /// CHECK: InstanceFieldGet
@@ -151,8 +155,10 @@
   /// CHECK: InstanceFieldSet
   /// CHECK: InstanceFieldSet
   /// CHECK: InstanceFieldSet
+  /// CHECK: InstanceFieldSet
+
+  /// CHECK-START: int Main.test3(TestClass) load_store_elimination (after)
   /// CHECK-NOT: InstanceFieldGet
-  /// CHECK-NOT: StaticFieldGet
 
   // A new allocation (even non-singleton) shouldn't alias with pre-existing values.
   static int test3(TestClass obj) {
@@ -186,6 +192,7 @@
 
   /// CHECK-START: int Main.test4(TestClass, boolean) load_store_elimination (after)
   /// CHECK-NOT: InstanceFieldGet
+  /// CHECK-NOT: Phi
 
   // Set and merge the same value in two branches.
   static int test4(TestClass obj, boolean b) {
@@ -198,16 +205,26 @@
   }
 
   /// CHECK-START: int Main.test5(TestClass, boolean) load_store_elimination (before)
-  /// CHECK-DAG: InstanceFieldSet
-  /// CHECK-DAG: InstanceFieldSet
-  /// CHECK-DAG: InstanceFieldGet
-  /// CHECK-DAG: Return
+  /// 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: InstanceFieldSet
-  /// CHECK-DAG: InstanceFieldSet
-  /// CHECK-DAG: InstanceFieldGet
-  /// CHECK-DAG: Return
+  /// 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) {
@@ -220,18 +237,20 @@
   }
 
   /// CHECK-START: int Main.test6(TestClass, TestClass, boolean) load_store_elimination (before)
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldGet
-  /// CHECK: InstanceFieldGet
+  /// 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: InstanceFieldSet
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldSet
+  /// 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: NullCheck
   /// CHECK-NOT: InstanceFieldGet
 
   // Setting the same value doesn't clear the value for aliased locations.
@@ -336,7 +355,6 @@
   /// CHECK-NOT: InstanceFieldGet
 
   // Loop without heap writes.
-  // obj.i is actually hoisted to the loop pre-header by licm already.
   static int test11(TestClass obj) {
     obj.i = 1;
     int sum = 0;
@@ -411,9 +429,11 @@
   /// CHECK-START: int Main.test15() load_store_elimination (after)
   /// CHECK: <<Const2:i\d+>> IntConstant 2
   /// CHECK: StaticFieldSet
-  /// CHECK-NOT: StaticFieldGet
   /// 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;
@@ -511,23 +531,29 @@
   /// CHECK: InstanceFieldGet
 
   /// CHECK-START: void Main.test21(TestClass) load_store_elimination (after)
-  /// CHECK: NewInstance
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldGet
-  /// CHECK: InstanceFieldGet
+  /// CHECK-DAG: InstanceFieldSet
+  /// CHECK-DAG: Phi
+
+  /// 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";
     }
-    System.out.print(obj0.str.substring(0, 0) + obj.str.substring(0, 0));
+    $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)
@@ -544,12 +570,6 @@
   /// CHECK-START: int Main.test22() load_store_elimination (after)
   /// CHECK-NOT: NewInstance
   /// CHECK-NOT: InstanceFieldSet
-  /// CHECK-NOT: NewInstance
-  /// CHECK-NOT: InstanceFieldSet
-  /// CHECK-NOT: InstanceFieldGet
-  /// CHECK-NOT: NewInstance
-  /// CHECK-NOT: InstanceFieldSet
-  /// CHECK-NOT: InstanceFieldGet
   /// CHECK-NOT: InstanceFieldGet
 
   // For a singleton, loop side effects can kill its field values only if:
@@ -571,45 +591,45 @@
   }
 
   /// CHECK-START: int Main.test23(boolean) load_store_elimination (before)
-  /// CHECK-DAG: NewInstance
-  /// CHECK-DAG: InstanceFieldSet
-  /// CHECK-DAG: InstanceFieldGet
-  /// CHECK-DAG: InstanceFieldSet
-  /// CHECK-DAG: InstanceFieldGet
-  /// CHECK-DAG: InstanceFieldSet
-  /// CHECK-DAG: InstanceFieldGet
-  /// CHECK-DAG: Return
+  /// 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: NewInstance
-  /// CHECK-DAG: InstanceFieldSet
-  /// CHECK-DAG: InstanceFieldSet
-  /// CHECK-DAG: InstanceFieldGet
-  /// CHECK-DAG: Return
+  /// 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:     InstanceFieldSet
-  /// CHECK:     InstanceFieldSet
+  /// CHECK-NOT: NewInstance
   /// CHECK-NOT: InstanceFieldSet
-
-  /// CHECK-START: int Main.test23(boolean) load_store_elimination (after)
-  /// CHECK: InstanceFieldGet
   /// CHECK-NOT: InstanceFieldGet
 
-  /// CHECK-START: int Main.test23(boolean) load_store_elimination (after)
-  /// CHECK-DAG: <<Get:i\d+>> InstanceFieldGet
-  /// CHECK-DAG: Return [<<Get>>]
-
-  // Test store elimination on merging.
+  // 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 cannot be eliminated due to the merge later.
+      obj.i += 1;   // This store can be eliminated after replacing the load below with a Phi.
     } else {
-      obj.i += 2;   // This store cannot be eliminated due to the merge later.
+      obj.i += 2;   // This store can be eliminated after replacing the load below with a Phi.
     }
-    return obj.i;
+    return obj.i;   // This load is eliminated by creating a Phi.
   }
 
   /// CHECK-START: float Main.test24() load_store_elimination (before)
@@ -631,6 +651,9 @@
   /// 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();
@@ -640,6 +663,529 @@
     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:                     Phi
+
+  /// CHECK-START: int Main.test33(TestClass, boolean) load_store_elimination (after)
+  /// CHECK:                         InstanceFieldSet
+  /// CHECK-NOT:                     InstanceFieldSet
+
+  // Test eliminating non-observable stores.
+  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;
+  }
+
   /// CHECK-START: int Main.$noinline$testConversion1(TestClass, int) load_store_elimination (before)
   /// CHECK-DAG:                     InstanceFieldSet
   /// CHECK-DAG:                     InstanceFieldSet
@@ -652,7 +1198,14 @@
   /// CHECK-DAG:                     InstanceFieldSet
   /// CHECK-DAG:                     TypeConversion
   /// CHECK-DAG:                     InstanceFieldSet
-  /// CHECK-DAG:                     InstanceFieldGet
+  /// 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) {
@@ -667,6 +1220,8 @@
   /// 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)
@@ -674,11 +1229,22 @@
   /// CHECK-DAG:                     InstanceFieldSet
   /// CHECK-DAG:                     TypeConversion
   /// CHECK-DAG:                     InstanceFieldSet
-  /// CHECK-DAG:                     InstanceFieldGet
+  /// 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;
@@ -697,6 +1263,86 @@
     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
@@ -759,9 +1405,6 @@
 
   // Test that HSelect creates alias.
   static int $noinline$testHSelect(boolean b) {
-    if (sFlag) {
-      throw new Error();
-    }
     TestClass obj = new TestClass();
     TestClass obj2 = null;
     obj.i = 0xdead;
@@ -782,11 +1425,11 @@
   }
 
   /// CHECK-START: int Main.sumWithinRange(int[], int, int) load_store_elimination (before)
-  /// CHECK: NewInstance
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldGet
-  /// CHECK: InstanceFieldGet
+  /// 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
@@ -882,6 +1525,9 @@
   /// 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.
@@ -1150,20 +1796,24 @@
   }
 
   /// CHECK-START: int Main.testExitMerge(boolean) load_store_elimination (before)
-  /// CHECK: NewInstance
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldGet
-  /// CHECK: Return
-  /// CHECK: InstanceFieldSet
-  /// CHECK: Throw
+  /// CHECK-DAG: NewInstance
+  /// CHECK-DAG: InstanceFieldSet
+  /// CHECK-DAG: InstanceFieldGet
+  /// CHECK-DAG: Return
+  /// CHECK-DAG: InstanceFieldSet
+  /// CHECK-DAG: Throw
 
   /// CHECK-START: int Main.testExitMerge(boolean) load_store_elimination (after)
-  /// CHECK-NOT: NewInstance
+  /// CHECK-DAG: Return
+  /// CHECK-DAG: Throw
+
+  /// CHECK-START: int Main.testExitMerge(boolean) load_store_elimination (after)
   /// CHECK-NOT: InstanceFieldSet
   /// CHECK-NOT: InstanceFieldGet
-  /// CHECK: Return
-  /// CHECK-NOT: InstanceFieldSet
-  /// CHECK: Throw
+
+  /// 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) {
@@ -1171,16 +1821,16 @@
       return obj.i + 1;
     } else {
       obj.i = 2;
-      throw new Error();
+      throw new Error();  // Note: We have a NewInstance here.
     }
   }
 
   /// CHECK-START: int Main.testExitMerge2(boolean) load_store_elimination (before)
-  /// CHECK: NewInstance
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldGet
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldGet
+  /// 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
@@ -1212,7 +1862,7 @@
     Object[] array = new Object[2];
     sArray = array;
     Object obj = array[0];
-    array[1] = obj;    // store the same value as the defaut value.
+    array[1] = obj;    // Store the same value as the default value.
   }
 
   /// CHECK-START: int Main.$noinline$testByteArrayDefaultValue() load_store_elimination (before)
@@ -1279,22 +1929,24 @@
   /// 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: <<Const0:i\d+>> IntConstant 0
-  /// CHECK-DAG: <<A:l\d+>>      NewArray
-  /// CHECK-DAG: <<Get:i\d+>>    ArrayGet [<<A>>,<<Const0>>]
-  /// CHECK-DAG:                 Return [<<Get>>]
-  //
+  /// 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-DAG:                 ArraySet
-  /// CHECK-DAG:                 ArraySet
+  /// 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 diamond pattern stores/load remain.
+    // The load after the diamond pattern is eliminated and replaced with a Phi,
+    // stores are then also eliminated.
     if (x) {
       a[0] = 2;
     } else {
@@ -1303,7 +1955,7 @@
     return a[0];
   }
 
-  /// CHECK-START: int Main.testLocalArrayMerge3(boolean) load_store_elimination (after)
+  /// 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
@@ -1312,8 +1964,12 @@
   /// 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) {
-    // All stores/load remain.
     int[] a = { 1 };
     if (x) {
       a[0] = 2;
@@ -1356,6 +2012,110 @@
     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: void Main.$noinline$testThrowingArraySet(java.lang.Object[], java.lang.Object) load_store_elimination (before)
   /// CHECK-DAG:                 ArrayGet
   /// CHECK-DAG:                 ArraySet
@@ -1377,6 +2137,1467 @@
     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-DAG:                 ArraySet
+  /// CHECK-DAG:                 ArrayGet
+
+  /// CHECK-START: int Main.testLoop15(int) load_store_elimination (after)
+  /// CHECK-DAG:                 NewArray
+  /// CHECK-DAG:                 ArraySet
+  /// 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.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;
+  }
+
+  private static void $noinline$clobberObservables() {}
+
   static void assertIntEquals(int result, int expected) {
     if (expected != result) {
       throw new Error("Expected: " + expected + ", found: " + result);
@@ -1435,6 +3656,41 @@
     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);
+
     testFinalizableByForcingGc();
     assertIntEquals($noinline$testHSelect(true), 0xdead);
     int[] array = {2, 5, 9, -1, -3, 10, 8, 4};
@@ -1448,6 +3704,14 @@
     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};
@@ -1527,6 +3791,16 @@
     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);
 
     TestClass[] tca = new TestClass[] { new TestClass(), null };
     try {
@@ -1539,7 +3813,171 @@
         throw new Error("tca[1] is null");
       }
     }
-  }
 
-  static boolean sFlag;
+    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(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);
+  }
 }