ART: Make GVN work with BoundType.

Support BoundType instruction treatment in GVN.
Note: BoundType must not be a subject to LICM as it must not be
moved from more control dependent basic blocks to less control
dependent (e.g. hoisted out from the loop) due to semantics of
bounding the type.

Test: 477-checker-bound-type.
Test: test-art-target, test-art-host.

Change-Id: I64263d6ec7d9ad75d1fb07d3a89e9973be67682b
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) { }
 }