Merge "ART: Make GVN work with BoundType."
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index 4863718..e6b6326 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -479,7 +479,10 @@
     HInstruction* next = current->GetNext();
     // Do not kill the set with the side effects of the instruction just now: if
     // the instruction is GVN'ed, we don't need to kill.
-    if (current->CanBeMoved()) {
+    //
+    // BoundType is a special case example of an instruction which shouldn't be moved but can be
+    // GVN'ed.
+    if (current->CanBeMoved() || current->IsBoundType()) {
       if (current->IsBinaryOperation() && current->AsBinaryOperation()->IsCommutative()) {
         // For commutative ops, (x op y) will be treated the same as (y op x)
         // after fixed ordering.
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index ef8a757..661f66a 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -2786,6 +2786,14 @@
   SetPackedFlag<kFlagReferenceTypeIsExact>(rti.IsExact());
 }
 
+bool HBoundType::InstructionDataEquals(const HInstruction* other) const {
+  const HBoundType* other_bt = other->AsBoundType();
+  ScopedObjectAccess soa(Thread::Current());
+  return GetUpperBound().IsEqual(other_bt->GetUpperBound()) &&
+         GetUpperCanBeNull() == other_bt->GetUpperCanBeNull() &&
+         CanBeNull() == other_bt->CanBeNull();
+}
+
 void HBoundType::SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be_null) {
   if (kIsDebugBuild) {
     ScopedObjectAccess soa(Thread::Current());
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 2037879..975ad1c 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -7142,6 +7142,7 @@
     SetRawInputAt(0, input);
   }
 
+  bool InstructionDataEquals(const HInstruction* other) const OVERRIDE;
   bool IsClonable() const OVERRIDE { return true; }
 
   // {Get,Set}Upper* should only be used in reference type propagation.
diff --git a/test/477-checker-bound-type/src/Main.java b/test/477-checker-bound-type/src/Main.java
index 2504ab2..237e4da 100644
--- a/test/477-checker-bound-type/src/Main.java
+++ b/test/477-checker-bound-type/src/Main.java
@@ -57,5 +57,79 @@
     }
   }
 
-  public static void main(String[] args) {  }
+  /// CHECK-START: void Main.boundTypeInLoop(int[]) licm (before)
+  /// CHECK-DAG: <<Param:l\d+>>     ParameterValue                        loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>       Phi                                   loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<BoundT:l\d+>>    BoundType [<<Param>>]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                    ArrayLength [<<BoundT>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                    ArrayGet                              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                    ArraySet                              loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START: void Main.boundTypeInLoop(int[]) licm (after)
+  /// CHECK-DAG: <<Param:l\d+>>     ParameterValue                        loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>       Phi                                   loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<BoundT:l\d+>>    BoundType [<<Param>>]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                    ArrayLength [<<BoundT>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                    ArrayGet                              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                    ArraySet                              loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-NOT:                    BoundType
+
+  /// CHECK-START: void Main.boundTypeInLoop(int[]) loop_optimization (after)
+  /// CHECK-DAG: <<Param:l\d+>>     ParameterValue                        loop:none
+  /// CHECK-DAG: <<BoundTA:l\d+>>   BoundType [<<Param>>]                 loop:none
+  /// CHECK-DAG:                    ArrayLength [<<BoundTA>>]             loop:none
+  /// CHECK-DAG:                    ArrayGet                              loop:none
+  /// CHECK-DAG:                    ArraySet                              loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>       Phi                                   loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<BoundT:l\d+>>    BoundType [<<Param>>]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                    ArrayLength [<<BoundT>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                    ArrayGet                              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                    ArraySet                              loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START: void Main.boundTypeInLoop(int[]) GVN$after_arch (after)
+  /// CHECK-DAG: <<Param:l\d+>>     ParameterValue                        loop:none
+  /// CHECK-DAG: <<BoundTA:l\d+>>   BoundType [<<Param>>]                 loop:none
+  /// CHECK-DAG:                    ArrayLength [<<BoundTA>>]             loop:none
+  /// CHECK-DAG:                    ArrayGet                              loop:none
+  /// CHECK-DAG:                    ArraySet                              loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>       Phi                                   loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:                    ArrayGet                              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                    ArraySet                              loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-NOT:                    BoundType
+  /// CHECK-NOT:                    ArrayLength
+  private static void boundTypeInLoop(int[] a) {
+    for (int i = 0; a != null && i < a.length; i++) {
+      a[i] += 1;
+    }
+  }
+
+  //  BoundType must not be hoisted by LICM, in this example it leads to ArrayLength being
+  //  hoisted as well which is invalid.
+  //
+  /// CHECK-START: void Main.BoundTypeNoLICM(java.lang.Object) licm (before)
+  /// CHECK-DAG: <<Param:l\d+>>   ParameterValue             loop:none
+  /// CHECK-DAG:                  SuspendCheck               loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Bound1:l\d+>>  BoundType [<<Param>>]      loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Bound2:l\d+>>  BoundType [<<Bound1>>]     loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:                  ArrayLength [<<Bound2>>]   loop:<<Loop>> outer_loop:none
+  //
+  /// CHECK-START: void Main.BoundTypeNoLICM(java.lang.Object) licm (after)
+  /// CHECK-DAG: <<Param:l\d+>>   ParameterValue             loop:none
+  /// CHECK-DAG:                  SuspendCheck               loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Bound1:l\d+>>  BoundType [<<Param>>]      loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG: <<Bound2:l\d+>>  BoundType [<<Bound1>>]     loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:                  ArrayLength [<<Bound2>>]   loop:<<Loop>> outer_loop:none
+  //
+  /// CHECK-NOT:                  BoundType                  loop:none
+  private static void BoundTypeNoLICM(Object obj) {
+    int i = 0;
+    while (obj instanceof int[]) {
+      int[] a = (int[])obj;
+      a[0] = 1;
+    }
+  }
+
+  public static void main(String[] args) { }
 }