Merge "Revert "Revert "Use LIRSlowPath for throwing ArrayOutOfBoundsException."""
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index 8a88d61..6f4fa3a 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -323,8 +323,6 @@
 std::ostream& operator<<(std::ostream& os, const X86ConditionCode& kind);
 
 enum ThrowKind {
-  kThrowArrayBounds,
-  kThrowConstantArrayBounds,
   kThrowNoSuchMethod,
 };
 
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index c876b3a..d5b34a5 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -1167,9 +1167,9 @@
 
     if (needs_range_check) {
       if (constant_index) {
-        GenImmedCheck(kCondLs, reg_len, mir_graph_->ConstantValue(rl_index), kThrowConstantArrayBounds);
+        GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len);
       } else {
-        GenRegRegCheck(kCondLs, reg_len, rl_index.reg, kThrowArrayBounds);
+        GenArrayBoundsCheck(rl_index.reg, reg_len);
       }
       FreeTemp(reg_len);
     }
@@ -1196,7 +1196,7 @@
     rl_result = EvalLoc(rl_dest, reg_class, true);
 
     if (needs_range_check) {
-      GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds);
+      GenArrayBoundsCheck(rl_index.reg, reg_len);
       FreeTemp(reg_len);
     }
     LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size);
@@ -1271,9 +1271,9 @@
     }
     if (needs_range_check) {
       if (constant_index) {
-        GenImmedCheck(kCondLs, reg_len, mir_graph_->ConstantValue(rl_index), kThrowConstantArrayBounds);
+        GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len);
       } else {
-        GenRegRegCheck(kCondLs, reg_len, rl_index.reg, kThrowArrayBounds);
+        GenArrayBoundsCheck(rl_index.reg, reg_len);
       }
       FreeTemp(reg_len);
     }
@@ -1289,7 +1289,7 @@
     OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
     rl_src = LoadValue(rl_src, reg_class);
     if (needs_range_check) {
-      GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds);
+      GenArrayBoundsCheck(rl_index.reg, reg_len);
       FreeTemp(reg_len);
     }
     StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size);
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index 1847921..6781a9b 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -91,6 +91,59 @@
   AddSlowPath(new (arena_) DivZeroCheckSlowPath(this, branch));
 }
 
+void Mir2Lir::GenArrayBoundsCheck(RegStorage index, RegStorage length) {
+  class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath {
+   public:
+    ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch, RegStorage index, RegStorage length)
+        : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch),
+          index_(index), length_(length) {
+    }
+
+    void Compile() OVERRIDE {
+      m2l_->ResetRegPool();
+      m2l_->ResetDefTracking();
+      GenerateTargetLabel();
+      m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds),
+                                    index_, length_, true);
+    }
+
+   private:
+    const RegStorage index_;
+    const RegStorage length_;
+  };
+
+  LIR* branch = OpCmpBranch(kCondUge, index, length, nullptr);
+  AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch, index, length));
+}
+
+void Mir2Lir::GenArrayBoundsCheck(int index, RegStorage length) {
+  class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath {
+   public:
+    ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch, int index, RegStorage length)
+        : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch),
+          index_(index), length_(length) {
+    }
+
+    void Compile() OVERRIDE {
+      m2l_->ResetRegPool();
+      m2l_->ResetDefTracking();
+      GenerateTargetLabel();
+
+      m2l_->OpRegCopy(m2l_->TargetReg(kArg1), length_);
+      m2l_->LoadConstant(m2l_->TargetReg(kArg0), index_);
+      m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds),
+                                    m2l_->TargetReg(kArg0), m2l_->TargetReg(kArg1), true);
+    }
+
+   private:
+    const int32_t index_;
+    const RegStorage length_;
+  };
+
+  LIR* branch = OpCmpImmBranch(kCondLs, length, index, nullptr);
+  AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch, index, length));
+}
+
 LIR* Mir2Lir::GenNullCheck(RegStorage reg) {
   class NullCheckSlowPath : public Mir2Lir::LIRSlowPath {
    public:
@@ -685,58 +738,7 @@
     AppendLIR(lab);
     ThreadOffset<4> func_offset(-1);
     int v1 = lab->operands[2];
-    int v2 = lab->operands[3];
-    const bool target_x86 = cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64;
     switch (lab->operands[0]) {
-      case kThrowConstantArrayBounds:  // v1 is length reg (for Arm/Mips), v2 constant index
-        // v1 holds the constant array index.  Mips/Arm uses v2 for length, x86 reloads.
-        if (target_x86) {
-          OpRegMem(kOpMov, TargetReg(kArg1), RegStorage::Solo32(v1),
-                   mirror::Array::LengthOffset().Int32Value());
-        } else {
-          OpRegCopy(TargetReg(kArg1), RegStorage::Solo32(v1));
-        }
-        // Make sure the following LoadConstant doesn't mess with kArg1.
-        LockTemp(TargetReg(kArg1));
-        LoadConstant(TargetReg(kArg0), v2);
-        func_offset = QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds);
-        break;
-      case kThrowArrayBounds:
-        // Move v1 (array index) to kArg0 and v2 (array length) to kArg1
-        if (v2 != TargetReg(kArg0).GetReg()) {
-          OpRegCopy(TargetReg(kArg0), RegStorage::Solo32(v1));
-          if (target_x86) {
-            // x86 leaves the array pointer in v2, so load the array length that the handler expects
-            OpRegMem(kOpMov, TargetReg(kArg1), RegStorage::Solo32(v2),
-                     mirror::Array::LengthOffset().Int32Value());
-          } else {
-            OpRegCopy(TargetReg(kArg1), RegStorage::Solo32(v2));
-          }
-        } else {
-          if (v1 == TargetReg(kArg1).GetReg()) {
-            // Swap v1 and v2, using kArg2 as a temp
-            OpRegCopy(TargetReg(kArg2), RegStorage::Solo32(v1));
-            if (target_x86) {
-              // x86 leaves the array pointer in v2; load the array length that the handler expects
-              OpRegMem(kOpMov, TargetReg(kArg1), RegStorage::Solo32(v2),
-                       mirror::Array::LengthOffset().Int32Value());
-            } else {
-              OpRegCopy(TargetReg(kArg1), RegStorage::Solo32(v2));
-            }
-            OpRegCopy(TargetReg(kArg0), TargetReg(kArg2));
-          } else {
-            if (target_x86) {
-              // x86 leaves the array pointer in v2; load the array length that the handler expects
-              OpRegMem(kOpMov, TargetReg(kArg1), RegStorage::Solo32(v2),
-                       mirror::Array::LengthOffset().Int32Value());
-            } else {
-              OpRegCopy(TargetReg(kArg1), RegStorage::Solo32(v2));
-            }
-            OpRegCopy(TargetReg(kArg0), RegStorage::Solo32(v1));
-          }
-        }
-        func_offset = QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds);
-        break;
       case kThrowNoSuchMethod:
         OpRegCopy(TargetReg(kArg0), RegStorage::Solo32(v1));
         func_offset =
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 4aae16d..758096b 100644
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -255,12 +255,27 @@
   CallHelper(r_tgt, helper_offset, safepoint_pc);
 }
 
+void Mir2Lir::CopyToArgumentRegs(RegStorage arg0, RegStorage arg1) {
+  if (arg1.GetReg() == TargetReg(kArg0).GetReg()) {
+    if (arg0.GetReg() == TargetReg(kArg1).GetReg()) {
+      // Swap kArg0 and kArg1 with kArg2 as temp.
+      OpRegCopy(TargetReg(kArg2), arg1);
+      OpRegCopy(TargetReg(kArg0), arg0);
+      OpRegCopy(TargetReg(kArg1), TargetReg(kArg2));
+    } else {
+      OpRegCopy(TargetReg(kArg1), arg1);
+      OpRegCopy(TargetReg(kArg0), arg0);
+    }
+  } else {
+    OpRegCopy(TargetReg(kArg0), arg0);
+    OpRegCopy(TargetReg(kArg1), arg1);
+  }
+}
+
 void Mir2Lir::CallRuntimeHelperRegReg(ThreadOffset<4> helper_offset, RegStorage arg0,
                                       RegStorage arg1, bool safepoint_pc) {
   RegStorage r_tgt = CallHelperSetup(helper_offset);
-  DCHECK_NE(TargetReg(kArg0).GetReg(), arg1.GetReg());  // check copy into arg0 won't clobber arg1
-  OpRegCopy(TargetReg(kArg0), arg0);
-  OpRegCopy(TargetReg(kArg1), arg1);
+  CopyToArgumentRegs(arg0, arg1);
   ClobberCallerSave();
   CallHelper(r_tgt, helper_offset, safepoint_pc);
 }
@@ -268,9 +283,7 @@
 void Mir2Lir::CallRuntimeHelperRegRegImm(ThreadOffset<4> helper_offset, RegStorage arg0,
                                          RegStorage arg1, int arg2, bool safepoint_pc) {
   RegStorage r_tgt = CallHelperSetup(helper_offset);
-  DCHECK_NE(TargetReg(kArg0).GetReg(), arg1.GetReg());  // check copy into arg0 won't clobber arg1
-  OpRegCopy(TargetReg(kArg0), arg0);
-  OpRegCopy(TargetReg(kArg1), arg1);
+  CopyToArgumentRegs(arg0, arg1);
   LoadConstant(TargetReg(kArg2), arg2);
   ClobberCallerSave();
   CallHelper(r_tgt, helper_offset, safepoint_pc);
diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc
index 60358b4..2375720 100644
--- a/compiler/dex/quick/mips/int_mips.cc
+++ b/compiler/dex/quick/mips/int_mips.cc
@@ -513,7 +513,7 @@
     rl_result = EvalLoc(rl_dest, reg_class, true);
 
     if (needs_range_check) {
-      GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds);
+      GenArrayBoundsCheck(rl_index.reg, reg_len);
       FreeTemp(reg_len);
     }
     LoadBaseDispWide(reg_ptr, 0, rl_result.reg, INVALID_SREG);
@@ -524,7 +524,7 @@
     rl_result = EvalLoc(rl_dest, reg_class, true);
 
     if (needs_range_check) {
-      GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds);
+      GenArrayBoundsCheck(rl_index.reg, reg_len);
       FreeTemp(reg_len);
     }
     LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size);
@@ -590,7 +590,7 @@
     rl_src = LoadValueWide(rl_src, reg_class);
 
     if (needs_range_check) {
-      GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds);
+      GenArrayBoundsCheck(rl_index.reg, reg_len);
       FreeTemp(reg_len);
     }
 
@@ -598,7 +598,7 @@
   } else {
     rl_src = LoadValue(rl_src, reg_class);
     if (needs_range_check) {
-      GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds);
+       GenArrayBoundsCheck(rl_index.reg, reg_len);
       FreeTemp(reg_len);
     }
     StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size);
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 65910e9..1f69eb5 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -567,6 +567,8 @@
     void GenDivZeroCheck(ConditionCode c_code);
     // reg holds divisor.
     void GenDivZeroCheck(RegStorage reg);
+    void GenArrayBoundsCheck(RegStorage index, RegStorage length);
+    void GenArrayBoundsCheck(int32_t index, RegStorage length);
     LIR* GenNullCheck(RegStorage reg);
     void MarkPossibleNullPointerException(int opt_flags);
     void MarkPossibleStackOverflowException();
@@ -1227,6 +1229,10 @@
 
     void AddDivZeroCheckSlowPath(LIR* branch);
 
+    // Copy arg0 and arg1 to kArg0 and kArg1 safely, possibly using
+    // kArg2 as temp.
+    void CopyToArgumentRegs(RegStorage arg0, RegStorage arg1);
+
   public:
     // TODO: add accessors for these.
     LIR* literal_list_;                        // Constants.
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index 0b9823d..b802591 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -136,6 +136,8 @@
     RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div);
     void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
     void GenDivZeroCheckWide(RegStorage reg);
+    void GenArrayBoundsCheck(RegStorage index, RegStorage array_base, int32_t len_offset);
+    void GenArrayBoundsCheck(int32_t index, RegStorage array_base, int32_t len_offset);
     void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method);
     void GenExitSequence();
     void GenSpecialExitSequence();
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 4ffb9a4..84325b4 100644
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -891,6 +891,86 @@
   FreeTemp(t_reg);
 }
 
+void X86Mir2Lir::GenArrayBoundsCheck(RegStorage index,
+                                     RegStorage array_base,
+                                     int len_offset) {
+  class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath {
+   public:
+    ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch,
+                             RegStorage index, RegStorage array_base, int32_t len_offset)
+        : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch),
+          index_(index), array_base_(array_base), len_offset_(len_offset) {
+    }
+
+    void Compile() OVERRIDE {
+      m2l_->ResetRegPool();
+      m2l_->ResetDefTracking();
+      GenerateTargetLabel();
+
+      RegStorage new_index = index_;
+      // Move index out of kArg1, either directly to kArg0, or to kArg2.
+      if (index_.GetReg() == m2l_->TargetReg(kArg1).GetReg()) {
+        if (array_base_.GetReg() == m2l_->TargetReg(kArg0).GetReg()) {
+          m2l_->OpRegCopy(m2l_->TargetReg(kArg2), index_);
+          new_index = m2l_->TargetReg(kArg2);
+        } else {
+          m2l_->OpRegCopy(m2l_->TargetReg(kArg0), index_);
+          new_index = m2l_->TargetReg(kArg0);
+        }
+      }
+      // Load array length to kArg1.
+      m2l_->OpRegMem(kOpMov, m2l_->TargetReg(kArg1), array_base_, len_offset_);
+      m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds),
+                                    new_index, m2l_->TargetReg(kArg1), true);
+    }
+
+   private:
+    const RegStorage index_;
+    const RegStorage array_base_;
+    const int32_t len_offset_;
+  };
+
+  OpRegMem(kOpCmp, index, array_base, len_offset);
+  LIR* branch = OpCondBranch(kCondUge, nullptr);
+  AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch,
+                                                    index, array_base, len_offset));
+}
+
+void X86Mir2Lir::GenArrayBoundsCheck(int32_t index,
+                                     RegStorage array_base,
+                                     int32_t len_offset) {
+  class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath {
+   public:
+    ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch,
+                             int32_t index, RegStorage array_base, int32_t len_offset)
+        : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch),
+          index_(index), array_base_(array_base), len_offset_(len_offset) {
+    }
+
+    void Compile() OVERRIDE {
+      m2l_->ResetRegPool();
+      m2l_->ResetDefTracking();
+      GenerateTargetLabel();
+
+      // Load array length to kArg1.
+      m2l_->OpRegMem(kOpMov, m2l_->TargetReg(kArg1), array_base_, len_offset_);
+      m2l_->LoadConstant(m2l_->TargetReg(kArg0), index_);
+      m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds),
+                                    m2l_->TargetReg(kArg0), m2l_->TargetReg(kArg1), true);
+    }
+
+   private:
+    const int32_t index_;
+    const RegStorage array_base_;
+    const int32_t len_offset_;
+  };
+
+  NewLIR3(IS_SIMM8(index) ? kX86Cmp32MI8 : kX86Cmp32MI, array_base.GetReg(), len_offset, index);
+  LIR* branch = OpCondBranch(kCondLs, nullptr);
+  AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch,
+                                                    index, array_base, len_offset));
+}
+
 // Test suspend flag, return target of taken suspend branch
 LIR* X86Mir2Lir::OpTestSuspend(LIR* target) {
   OpTlsCmp(Thread::ThreadFlagsOffset<4>(), 0);
@@ -1348,10 +1428,9 @@
 
   if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
     if (constant_index) {
-      GenMemImmedCheck(kCondLs, rl_array.reg, len_offset,
-                       constant_index_value, kThrowConstantArrayBounds);
+      GenArrayBoundsCheck(constant_index_value, rl_array.reg, len_offset);
     } else {
-      GenRegMemCheck(kCondUge, rl_index.reg, rl_array.reg, len_offset, kThrowArrayBounds);
+      GenArrayBoundsCheck(rl_index.reg, rl_array.reg, len_offset);
     }
   }
   rl_result = EvalLoc(rl_dest, reg_class, true);
@@ -1400,10 +1479,9 @@
 
   if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
     if (constant_index) {
-      GenMemImmedCheck(kCondLs, rl_array.reg, len_offset,
-                       constant_index_value, kThrowConstantArrayBounds);
+      GenArrayBoundsCheck(constant_index_value, rl_array.reg, len_offset);
     } else {
-      GenRegMemCheck(kCondUge, rl_index.reg, rl_array.reg, len_offset, kThrowArrayBounds);
+      GenArrayBoundsCheck(rl_index.reg, rl_array.reg, len_offset);
     }
   }
   if ((size == kLong) || (size == kDouble)) {