Do a parallel move in BoundsCheckSlowPath.

The two locations of the index and length could overlap,
so we need a parallel move. Also factorize the code for
doing a parallel move based on two locations.

Change-Id: Iee8b3459e2eed6704d45e9a564fb2cd050741ea4
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 9d17263..6b5ec1d 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -632,4 +632,13 @@
   }
 }
 
+void CodeGenerator::EmitParallelMoves(Location from1, Location to1, Location from2, Location to2) {
+  MoveOperands move1(from1, to1, nullptr);
+  MoveOperands move2(from2, to2, nullptr);
+  HParallelMove parallel_move(GetGraph()->GetArena());
+  parallel_move.AddMove(&move1);
+  parallel_move.AddMove(&move2);
+  GetMoveResolver()->EmitNativeCode(&parallel_move);
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index fc4ea4b..ac4fc67 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -33,6 +33,7 @@
 class Assembler;
 class CodeGenerator;
 class DexCompilationUnit;
+class ParallelMoveResolver;
 class SrcMap;
 
 class CodeAllocator {
@@ -165,6 +166,8 @@
   // of the architecture.
   static size_t GetCacheOffset(uint32_t index);
 
+  void EmitParallelMoves(Location from1, Location to1, Location from2, Location to2);
+
  protected:
   CodeGenerator(HGraph* graph,
                 size_t number_of_core_registers,
@@ -197,6 +200,8 @@
 
   virtual Location GetStackLocation(HLoadLocal* load) const = 0;
 
+  virtual ParallelMoveResolver* GetMoveResolver() = 0;
+
   // Frame size required for this method.
   uint32_t frame_size_;
   uint32_t core_spill_mask_;
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index c2e9a2e..b336c31 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -169,11 +169,14 @@
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
     __ Bind(GetEntryLabel());
+    // We're moving two locations to locations that could overlap, so we need a parallel
+    // move resolver.
     InvokeRuntimeCallingConvention calling_convention;
-    arm_codegen->Move32(
-        Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_);
-    arm_codegen->Move32(
-        Location::RegisterLocation(calling_convention.GetRegisterAt(1)), length_location_);
+    codegen->EmitParallelMoves(
+        index_location_,
+        Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+        length_location_,
+        Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
     arm_codegen->InvokeRuntime(
         QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc());
   }
@@ -290,16 +293,11 @@
     // We're moving two locations to locations that could overlap, so we need a parallel
     // move resolver.
     InvokeRuntimeCallingConvention calling_convention;
-    MoveOperands move1(class_to_check_,
-                       Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
-                       nullptr);
-    MoveOperands move2(object_class_,
-                       Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
-                       nullptr);
-    HParallelMove parallel_move(codegen->GetGraph()->GetArena());
-    parallel_move.AddMove(&move1);
-    parallel_move.AddMove(&move2);
-    arm_codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
+    codegen->EmitParallelMoves(
+        class_to_check_,
+        Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+        object_class_,
+        Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
 
     if (instruction_->IsInstanceOf()) {
       arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial), instruction_, dex_pc_);
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 5d51993..e3a13a2 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -186,7 +186,7 @@
   // Blocks all register pairs made out of blocked core registers.
   void UpdateBlockedPairRegisters() const;
 
-  ParallelMoveResolverARM* GetMoveResolver() {
+  ParallelMoveResolverARM* GetMoveResolver() OVERRIDE {
     return &move_resolver_;
   }
 
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index f2ead21..54e87f4 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -230,6 +230,11 @@
   void Load(Primitive::Type type, vixl::Register dst, const vixl::MemOperand& src);
   void Store(Primitive::Type type, vixl::Register rt, const vixl::MemOperand& dst);
 
+  ParallelMoveResolver* GetMoveResolver() OVERRIDE {
+    UNIMPLEMENTED(INFO) << "TODO: MoveResolver";
+    return nullptr;
+  }
+
  private:
   // Labels for each block that will be compiled.
   vixl::Label* block_labels_;
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index bed44b2..d537b17 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -140,9 +140,14 @@
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
     __ Bind(GetEntryLabel());
+    // We're moving two locations to locations that could overlap, so we need a parallel
+    // move resolver.
     InvokeRuntimeCallingConvention calling_convention;
-    x86_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_);
-    x86_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(1)), length_location_);
+    x86_codegen->EmitParallelMoves(
+        index_location_,
+        Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+        length_location_,
+        Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
     __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowArrayBounds)));
     codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
   }
@@ -291,16 +296,11 @@
     // We're moving two locations to locations that could overlap, so we need a parallel
     // move resolver.
     InvokeRuntimeCallingConvention calling_convention;
-    MoveOperands move1(class_to_check_,
-                       Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
-                       nullptr);
-    MoveOperands move2(object_class_,
-                       Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
-                       nullptr);
-    HParallelMove parallel_move(codegen->GetGraph()->GetArena());
-    parallel_move.AddMove(&move1);
-    parallel_move.AddMove(&move2);
-    x86_codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
+    x86_codegen->EmitParallelMoves(
+        class_to_check_,
+        Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+        object_class_,
+        Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
 
     if (instruction_->IsInstanceOf()) {
       __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pInstanceofNonTrivial)));
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 85fe21c..5ba05af 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -181,7 +181,7 @@
   // Blocks all register pairs made out of blocked core registers.
   void UpdateBlockedPairRegisters() const;
 
-  ParallelMoveResolverX86* GetMoveResolver() {
+  ParallelMoveResolverX86* GetMoveResolver() OVERRIDE {
     return &move_resolver_;
   }
 
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 112c179..7545e4d 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -179,13 +179,15 @@
         length_location_(length_location) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
     __ Bind(GetEntryLabel());
+    // We're moving two locations to locations that could overlap, so we need a parallel
+    // move resolver.
     InvokeRuntimeCallingConvention calling_convention;
-    x64_codegen->Move(
-        Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_);
-    x64_codegen->Move(
-        Location::RegisterLocation(calling_convention.GetRegisterAt(1)), length_location_);
+    codegen->EmitParallelMoves(
+        index_location_,
+        Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+        length_location_,
+        Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
     __ gs()->call(Address::Absolute(
         QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowArrayBounds), true));
     codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
@@ -305,16 +307,11 @@
     // We're moving two locations to locations that could overlap, so we need a parallel
     // move resolver.
     InvokeRuntimeCallingConvention calling_convention;
-    MoveOperands move1(class_to_check_,
-                       Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
-                       nullptr);
-    MoveOperands move2(object_class_,
-                       Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
-                       nullptr);
-    HParallelMove parallel_move(codegen->GetGraph()->GetArena());
-    parallel_move.AddMove(&move1);
-    parallel_move.AddMove(&move2);
-    x64_codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
+    codegen->EmitParallelMoves(
+        class_to_check_,
+        Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+        object_class_,
+        Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
 
     if (instruction_->IsInstanceOf()) {
       __ gs()->call(
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 9565b6f..6112530 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -171,7 +171,7 @@
     return &assembler_;
   }
 
-  ParallelMoveResolverX86_64* GetMoveResolver() {
+  ParallelMoveResolverX86_64* GetMoveResolver() OVERRIDE {
     return &move_resolver_;
   }
 
diff --git a/test/427-bounds/expected.txt b/test/427-bounds/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/427-bounds/expected.txt
diff --git a/test/427-bounds/info.txt b/test/427-bounds/info.txt
new file mode 100644
index 0000000..8b8b957
--- /dev/null
+++ b/test/427-bounds/info.txt
@@ -0,0 +1,2 @@
+Regression test for the optimizing compiler that used to incorrectly pass
+index and/or length to the pThrowArrayBounds entrypoint.
diff --git a/test/427-bounds/src/Main.java b/test/427-bounds/src/Main.java
new file mode 100644
index 0000000..a2d84d2
--- /dev/null
+++ b/test/427-bounds/src/Main.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    Exception exception = null;
+    try {
+      $opt$Throw(new int[1]);
+    } catch (ArrayIndexOutOfBoundsException e) {
+      exception = e;
+    }
+
+    String exceptionMessage = exception.getMessage();
+
+    // Note that it's ART specific to emit the length.
+    if (exceptionMessage.contains("length")) {
+      if (!exceptionMessage.contains("length=1")) {
+        throw new Error("Wrong length in exception message");
+      }
+    }
+
+    // Note that it's ART specific to emit the index.
+    if (exceptionMessage.contains("index")) {
+      if (!exceptionMessage.contains("index=2")) {
+        throw new Error("Wrong index in exception message");
+      }
+    }
+  }
+
+  static void $opt$Throw(int[] array) {
+    // We fetch the length first, to ensure it is in EAX (on x86).
+    // The pThrowArrayBounds entrypoint expects the index in EAX and the
+    // length in ECX, and the optimizing compiler used to write to EAX
+    // before putting the length in ECX.
+    int length = array.length;
+    array[2] = 42;
+  }
+}