Implement array get and array put in optimizing.

Also fix a couple of assembler/disassembler issues.

Change-Id: I705c8572988c1a9c4df3172b304678529636d5f6
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 1f0b361..43e6b83 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -425,6 +425,41 @@
   return true;
 }
 
+void HGraphBuilder::BuildArrayAccess(const Instruction& instruction,
+                                     uint32_t dex_offset,
+                                     bool is_put,
+                                     Primitive::Type anticipated_type) {
+  uint8_t source_or_dest_reg = instruction.VRegA_23x();
+  uint8_t array_reg = instruction.VRegB_23x();
+  uint8_t index_reg = instruction.VRegC_23x();
+
+  DCHECK(IsTypeSupported(anticipated_type));
+
+  // We need one temporary for the null check, one for the index, and one for the length.
+  Temporaries temps(graph_, 3);
+
+  HInstruction* object = LoadLocal(array_reg, Primitive::kPrimNot);
+  object = new (arena_) HNullCheck(object, dex_offset);
+  current_block_->AddInstruction(object);
+  temps.Add(object);
+
+  HInstruction* length = new (arena_) HArrayLength(object);
+  current_block_->AddInstruction(length);
+  temps.Add(length);
+  HInstruction* index = LoadLocal(index_reg, Primitive::kPrimInt);
+  index = new (arena_) HBoundsCheck(index, length, dex_offset);
+  current_block_->AddInstruction(index);
+  temps.Add(index);
+  if (is_put) {
+    HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type);
+    // TODO: Insert a type check node if the type is Object.
+    current_block_->AddInstruction(new (arena_) HArraySet(object, index, value, dex_offset));
+  } else {
+    current_block_->AddInstruction(new (arena_) HArrayGet(object, index, anticipated_type));
+    UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
+  }
+}
+
 bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_t dex_offset) {
   if (current_block_ == nullptr) {
     return true;  // Dead code
@@ -697,6 +732,24 @@
       break;
     }
 
+#define ARRAY_XX(kind, anticipated_type)                                          \
+    case Instruction::AGET##kind: {                                               \
+      BuildArrayAccess(instruction, dex_offset, false, anticipated_type);         \
+      break;                                                                      \
+    }                                                                             \
+    case Instruction::APUT##kind: {                                               \
+      BuildArrayAccess(instruction, dex_offset, true, anticipated_type);          \
+      break;                                                                      \
+    }
+
+    ARRAY_XX(, Primitive::kPrimInt);
+    ARRAY_XX(_WIDE, Primitive::kPrimLong);
+    ARRAY_XX(_OBJECT, Primitive::kPrimNot);
+    ARRAY_XX(_BOOLEAN, Primitive::kPrimBoolean);
+    ARRAY_XX(_BYTE, Primitive::kPrimByte);
+    ARRAY_XX(_CHAR, Primitive::kPrimChar);
+    ARRAY_XX(_SHORT, Primitive::kPrimShort);
+
     default:
       return false;
   }
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index f94b8e8..170c427 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -93,6 +93,10 @@
   void BuildReturn(const Instruction& instruction, Primitive::Type type);
 
   bool BuildFieldAccess(const Instruction& instruction, uint32_t dex_offset, bool is_get);
+  void BuildArrayAccess(const Instruction& instruction,
+                        uint32_t dex_offset,
+                        bool is_get,
+                        Primitive::Type anticipated_type);
 
   // Builds an invocation node and returns whether the instruction is supported.
   bool BuildInvoke(const Instruction& instruction,
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 90ec6cf..eccc970 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -39,6 +39,24 @@
 static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2;  // LR, R6, R7
 static constexpr int kCurrentMethodStackOffset = 0;
 
+static Location ArmCoreLocation(Register reg) {
+  return Location::RegisterLocation(ArmManagedRegister::FromCoreRegister(reg));
+}
+
+static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1, R2 };
+static constexpr size_t kRuntimeParameterCoreRegistersLength =
+    arraysize(kRuntimeParameterCoreRegisters);
+
+class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
+ public:
+  InvokeRuntimeCallingConvention()
+      : CallingConvention(kRuntimeParameterCoreRegisters,
+                          kRuntimeParameterCoreRegistersLength) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
+};
+
 #define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())->
 
 class NullCheckSlowPathARM : public SlowPathCode {
@@ -72,6 +90,33 @@
   DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM);
 };
 
+class BoundsCheckSlowPathARM : public SlowPathCode {
+ public:
+  explicit BoundsCheckSlowPathARM(uint32_t dex_pc,
+                                  Location index_location,
+                                  Location length_location)
+      : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    CodeGeneratorARM* arm_codegen = reinterpret_cast<CodeGeneratorARM*>(codegen);
+    __ Bind(GetEntryLabel());
+    InvokeRuntimeCallingConvention calling_convention;
+    arm_codegen->Move32(ArmCoreLocation(calling_convention.GetRegisterAt(0)), index_location_);
+    arm_codegen->Move32(ArmCoreLocation(calling_convention.GetRegisterAt(1)), length_location_);
+    int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowArrayBounds).Int32Value();
+    __ ldr(LR, Address(TR, offset));
+    __ blx(LR);
+    codegen->RecordPcInfo(dex_pc_);
+  }
+
+ private:
+  const uint32_t dex_pc_;
+  const Location index_location_;
+  const Location length_location_;
+
+  DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM);
+};
+
 #undef __
 #define __ reinterpret_cast<ArmAssembler*>(GetAssembler())->
 
@@ -214,10 +259,6 @@
   return kNumberOfRegIds;
 }
 
-static Location ArmCoreLocation(Register reg) {
-  return Location::RegisterLocation(ArmManagedRegister::FromCoreRegister(reg));
-}
-
 InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen)
       : HGraphVisitor(graph),
         assembler_(codegen->GetAssembler()),
@@ -943,20 +984,6 @@
   }
 }
 
-static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1 };
-static constexpr size_t kRuntimeParameterCoreRegistersLength =
-    arraysize(kRuntimeParameterCoreRegisters);
-
-class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
- public:
-  InvokeRuntimeCallingConvention()
-      : CallingConvention(kRuntimeParameterCoreRegisters,
-                          kRuntimeParameterCoreRegistersLength) {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
-};
-
 void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) {
   codegen_->MarkNotLeaf();
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
@@ -1099,24 +1126,15 @@
       break;
     }
 
-    case Primitive::kPrimInt: {
-      Register value = locations->InAt(1).AsArm().AsCoreRegister();
-      __ StoreToOffset(kStoreWord, value, obj, offset);
-      break;
-    }
-
+    case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
       Register value = locations->InAt(1).AsArm().AsCoreRegister();
       __ StoreToOffset(kStoreWord, value, obj, offset);
-
-      Register temp = locations->GetTemp(0).AsArm().AsCoreRegister();
-      Register card = locations->GetTemp(1).AsArm().AsCoreRegister();
-      Label is_null;
-      __ CompareAndBranchIfZero(value, &is_null);
-      __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value());
-      __ Lsr(temp, obj, gc::accounting::CardTable::kCardShift);
-      __ strb(card, Address(card, temp));
-      __ Bind(&is_null);
+      if (field_type == Primitive::kPrimNot) {
+        Register temp = locations->GetTemp(0).AsArm().AsCoreRegister();
+        Register card = locations->GetTemp(1).AsArm().AsCoreRegister();
+        codegen_->MarkGCCard(temp, card, obj, value);
+      }
       break;
     }
 
@@ -1218,6 +1236,251 @@
   __ b(slow_path->GetEntryLabel(), EQ);
 }
 
+void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+  locations->SetOut(Location::RequiresRegister());
+  instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Register obj = locations->InAt(0).AsArm().AsCoreRegister();
+  Location index = locations->InAt(1);
+
+  switch (instruction->GetType()) {
+    case Primitive::kPrimBoolean: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
+      Register out = locations->Out().AsArm().AsCoreRegister();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
+        __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset);
+      } else {
+        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister()));
+        __ LoadFromOffset(kLoadUnsignedByte, out, IP, data_offset);
+      }
+      break;
+    }
+
+    case Primitive::kPrimByte: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
+      Register out = locations->Out().AsArm().AsCoreRegister();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
+        __ LoadFromOffset(kLoadSignedByte, out, obj, offset);
+      } else {
+        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister()));
+        __ LoadFromOffset(kLoadSignedByte, out, IP, data_offset);
+      }
+      break;
+    }
+
+    case Primitive::kPrimShort: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
+      Register out = locations->Out().AsArm().AsCoreRegister();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
+        __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset);
+      } else {
+        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2));
+        __ LoadFromOffset(kLoadSignedHalfword, out, IP, data_offset);
+      }
+      break;
+    }
+
+    case Primitive::kPrimChar: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
+      Register out = locations->Out().AsArm().AsCoreRegister();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
+        __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset);
+      } else {
+        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2));
+        __ LoadFromOffset(kLoadUnsignedHalfword, out, IP, data_offset);
+      }
+      break;
+    }
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot: {
+      DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t));
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+      Register out = locations->Out().AsArm().AsCoreRegister();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+        __ LoadFromOffset(kLoadWord, out, obj, offset);
+      } else {
+        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_4));
+        __ LoadFromOffset(kLoadWord, out, IP, data_offset);
+      }
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
+      ArmManagedRegister out = locations->Out().AsArm();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+        __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), obj, offset);
+      } else {
+        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_8));
+        __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), IP, data_offset);
+      }
+      break;
+    }
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << instruction->GetType();
+  }
+}
+
+void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  Primitive::Type value_type = instruction->InputAt(2)->GetType();
+  if (value_type == Primitive::kPrimNot) {
+    InvokeRuntimeCallingConvention calling_convention;
+    locations->SetInAt(0, ArmCoreLocation(calling_convention.GetRegisterAt(0)));
+    locations->SetInAt(1, ArmCoreLocation(calling_convention.GetRegisterAt(1)));
+    locations->SetInAt(2, ArmCoreLocation(calling_convention.GetRegisterAt(2)));
+    codegen_->MarkNotLeaf();
+  } else {
+    locations->SetInAt(0, Location::RequiresRegister());
+    locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+    locations->SetInAt(2, Location::RequiresRegister());
+  }
+  instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Register obj = locations->InAt(0).AsArm().AsCoreRegister();
+  Location index = locations->InAt(1);
+  Primitive::Type value_type = instruction->InputAt(2)->GetType();
+
+  switch (value_type) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
+      Register value = locations->InAt(2).AsArm().AsCoreRegister();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
+        __ StoreToOffset(kStoreByte, value, obj, offset);
+      } else {
+        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister()));
+        __ StoreToOffset(kStoreByte, value, IP, data_offset);
+      }
+      break;
+    }
+
+    case Primitive::kPrimShort:
+    case Primitive::kPrimChar: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
+      Register value = locations->InAt(2).AsArm().AsCoreRegister();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
+        __ StoreToOffset(kStoreHalfword, value, obj, offset);
+      } else {
+        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2));
+        __ StoreToOffset(kStoreHalfword, value, IP, data_offset);
+      }
+      break;
+    }
+
+    case Primitive::kPrimInt: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+      Register value = locations->InAt(2).AsArm().AsCoreRegister();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+        __ StoreToOffset(kStoreWord, value, obj, offset);
+      } else {
+        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_4));
+        __ StoreToOffset(kStoreWord, value, IP, data_offset);
+      }
+      break;
+    }
+
+    case Primitive::kPrimNot: {
+      int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAputObject).Int32Value();
+      __ ldr(LR, Address(TR, offset));
+      __ blx(LR);
+      codegen_->RecordPcInfo(instruction->GetDexPc());
+      DCHECK(!codegen_->IsLeafMethod());
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
+      ArmManagedRegister value = locations->InAt(2).AsArm();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+        __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), obj, offset);
+      } else {
+        __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_8));
+        __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), IP, data_offset);
+      }
+      break;
+    }
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << instruction->GetType();
+  }
+}
+
+void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister());
+  instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  uint32_t offset = mirror::Array::LengthOffset().Uint32Value();
+  Register obj = locations->InAt(0).AsArm().AsCoreRegister();
+  Register out = locations->Out().AsArm().AsCoreRegister();
+  __ LoadFromOffset(kLoadWord, out, obj, offset);
+}
+
+void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  // TODO: Have a normalization phase that makes this instruction never used.
+  locations->SetOut(Location::SameAsFirstInput());
+  instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(
+      instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+  codegen_->AddSlowPath(slow_path);
+
+  Register index = locations->InAt(0).AsArm().AsCoreRegister();
+  Register length = locations->InAt(1).AsArm().AsCoreRegister();
+
+  __ cmp(index, ShifterOperand(length));
+  __ b(slow_path->GetEntryLabel(), CS);
+}
+
+void CodeGeneratorARM::MarkGCCard(Register temp, Register card, Register object, Register value) {
+  Label is_null;
+  __ CompareAndBranchIfZero(value, &is_null);
+  __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value());
+  __ Lsr(temp, object, gc::accounting::CardTable::kCardShift);
+  __ strb(card, Address(card, temp));
+  __ Bind(&is_null);
+}
+
 void LocationsBuilderARM::VisitTemporary(HTemporary* temp) {
   temp->SetLocations(nullptr);
 }
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 85ab22b..610625c 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -175,12 +175,15 @@
     return InstructionSet::kThumb2;
   }
 
- private:
   // Helper method to move a 32bits value between two locations.
   void Move32(Location destination, Location source);
   // Helper method to move a 64bits value between two locations.
   void Move64(Location destination, Location source);
 
+  // Emit a write barrier.
+  void MarkGCCard(Register temp, Register card, Register object, Register value);
+
+ private:
   LocationsBuilderARM location_builder_;
   InstructionCodeGeneratorARM instruction_visitor_;
   ParallelMoveResolverARM move_resolver_;
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 251a2ad..2264638 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -39,6 +39,24 @@
 static constexpr int kNumberOfPushedRegistersAtEntry = 1;
 static constexpr int kCurrentMethodStackOffset = 0;
 
+static Location X86CpuLocation(Register reg) {
+  return Location::RegisterLocation(X86ManagedRegister::FromCpuRegister(reg));
+}
+
+static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX };
+static constexpr size_t kRuntimeParameterCoreRegistersLength =
+    arraysize(kRuntimeParameterCoreRegisters);
+
+class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
+ public:
+  InvokeRuntimeCallingConvention()
+      : CallingConvention(kRuntimeParameterCoreRegisters,
+                          kRuntimeParameterCoreRegistersLength) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
+};
+
 #define __ reinterpret_cast<X86Assembler*>(codegen->GetAssembler())->
 
 class NullCheckSlowPathX86 : public SlowPathCode {
@@ -71,6 +89,31 @@
   DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86);
 };
 
+class BoundsCheckSlowPathX86 : public SlowPathCode {
+ public:
+  explicit BoundsCheckSlowPathX86(uint32_t dex_pc,
+                                  Location index_location,
+                                  Location length_location)
+      : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    CodeGeneratorX86* x86_codegen = reinterpret_cast<CodeGeneratorX86*>(codegen);
+    __ Bind(GetEntryLabel());
+    InvokeRuntimeCallingConvention calling_convention;
+    x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(0)), index_location_);
+    x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(1)), length_location_);
+    __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowArrayBounds)));
+    codegen->RecordPcInfo(dex_pc_);
+  }
+
+ private:
+  const uint32_t dex_pc_;
+  const Location index_location_;
+  const Location length_location_;
+
+  DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86);
+};
+
 #undef __
 #define __ reinterpret_cast<X86Assembler*>(GetAssembler())->
 
@@ -188,10 +231,6 @@
   return kNumberOfRegIds;
 }
 
-static Location X86CpuLocation(Register reg) {
-  return Location::RegisterLocation(X86ManagedRegister::FromCpuRegister(reg));
-}
-
 InstructionCodeGeneratorX86::InstructionCodeGeneratorX86(HGraph* graph, CodeGeneratorX86* codegen)
       : HGraphVisitor(graph),
         assembler_(codegen->GetAssembler()),
@@ -260,20 +299,6 @@
   return Location();
 }
 
-static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX };
-static constexpr size_t kRuntimeParameterCoreRegistersLength =
-    arraysize(kRuntimeParameterCoreRegisters);
-
-class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
- public:
-  InvokeRuntimeCallingConvention()
-      : CallingConvention(kRuntimeParameterCoreRegisters,
-                          kRuntimeParameterCoreRegistersLength) {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
-};
-
 Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) {
   switch (type) {
     case Primitive::kPrimBoolean:
@@ -1048,7 +1073,7 @@
     locations->SetInAt(1, Location::RequiresRegister());
   }
   // Temporary registers for the write barrier.
-  if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
+  if (field_type == Primitive::kPrimNot) {
     locations->AddTemp(Location::RequiresRegister());
     // Ensure the card is in a byte register.
     locations->AddTemp(X86CpuLocation(ECX));
@@ -1077,25 +1102,16 @@
       break;
     }
 
-    case Primitive::kPrimInt: {
-      Register value = locations->InAt(1).AsX86().AsCpuRegister();
-      __ movl(Address(obj, offset), value);
-      break;
-    }
-
+    case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
       Register value = locations->InAt(1).AsX86().AsCpuRegister();
       __ movl(Address(obj, offset), value);
-      Label is_null;
-      Register temp = locations->GetTemp(0).AsX86().AsCpuRegister();
-      Register card = locations->GetTemp(1).AsX86().AsCpuRegister();
-      __ testl(value, value);
-      __ j(kEqual, &is_null);
-      __ fs()->movl(card, Address::Absolute(Thread::CardTableOffset<kX86WordSize>().Int32Value()));
-      __ movl(temp, obj);
-      __ shrl(temp, Immediate(gc::accounting::CardTable::kCardShift));
-      __ movb(Address(temp, card, TIMES_1, 0),  locations->GetTemp(1).AsX86().AsByteRegister());
-      __ Bind(&is_null);
+
+      if (field_type == Primitive::kPrimNot) {
+        Register temp = locations->GetTemp(0).AsX86().AsCpuRegister();
+        Register card = locations->GetTemp(1).AsX86().AsCpuRegister();
+        codegen_->MarkGCCard(temp, card, obj, value);
+      }
       break;
     }
 
@@ -1115,6 +1131,18 @@
   }
 }
 
+void CodeGeneratorX86::MarkGCCard(Register temp, Register card, Register object, Register value) {
+  Label is_null;
+  __ testl(value, value);
+  __ j(kEqual, &is_null);
+  __ fs()->movl(card, Address::Absolute(Thread::CardTableOffset<kX86WordSize>().Int32Value()));
+  __ movl(temp, object);
+  __ shrl(temp, Immediate(gc::accounting::CardTable::kCardShift));
+  __ movb(Address(temp, card, TIMES_1, 0),
+          X86ManagedRegister::FromCpuRegister(card).AsByteRegister());
+  __ Bind(&is_null);
+}
+
 void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
   locations->SetInAt(0, Location::RequiresRegister());
@@ -1202,6 +1230,243 @@
   __ j(kEqual, slow_path->GetEntryLabel());
 }
 
+void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+  locations->SetOut(Location::RequiresRegister());
+  instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Register obj = locations->InAt(0).AsX86().AsCpuRegister();
+  Location index = locations->InAt(1);
+
+  switch (instruction->GetType()) {
+    case Primitive::kPrimBoolean: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
+      Register out = locations->Out().AsX86().AsCpuRegister();
+      if (index.IsConstant()) {
+        __ movzxb(out, Address(obj,
+            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset));
+      } else {
+        __ movzxb(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_1, data_offset));
+      }
+      break;
+    }
+
+    case Primitive::kPrimByte: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
+      Register out = locations->Out().AsX86().AsCpuRegister();
+      if (index.IsConstant()) {
+        __ movsxb(out, Address(obj,
+            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset));
+      } else {
+        __ movsxb(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_1, data_offset));
+      }
+      break;
+    }
+
+    case Primitive::kPrimShort: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
+      Register out = locations->Out().AsX86().AsCpuRegister();
+      if (index.IsConstant()) {
+        __ movsxw(out, Address(obj,
+            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset));
+      } else {
+        __ movsxw(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_2, data_offset));
+      }
+      break;
+    }
+
+    case Primitive::kPrimChar: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
+      Register out = locations->Out().AsX86().AsCpuRegister();
+      if (index.IsConstant()) {
+        __ movzxw(out, Address(obj,
+            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset));
+      } else {
+        __ movzxw(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_2, data_offset));
+      }
+      break;
+    }
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+      Register out = locations->Out().AsX86().AsCpuRegister();
+      if (index.IsConstant()) {
+        __ movl(out, Address(obj,
+            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset));
+      } else {
+        __ movl(out, Address(obj, index.AsX86().AsCpuRegister(), TIMES_4, data_offset));
+      }
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
+      X86ManagedRegister out = locations->Out().AsX86();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+        __ movl(out.AsRegisterPairLow(), Address(obj, offset));
+        __ movl(out.AsRegisterPairHigh(), Address(obj, offset + kX86WordSize));
+      } else {
+        __ movl(out.AsRegisterPairLow(),
+                Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset));
+        __ movl(out.AsRegisterPairHigh(),
+                Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset + kX86WordSize));
+      }
+      break;
+    }
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << instruction->GetType();
+  }
+}
+
+void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  Primitive::Type value_type = instruction->InputAt(2)->GetType();
+  if (value_type == Primitive::kPrimNot) {
+    InvokeRuntimeCallingConvention calling_convention;
+    locations->SetInAt(0, X86CpuLocation(calling_convention.GetRegisterAt(0)));
+    locations->SetInAt(1, X86CpuLocation(calling_convention.GetRegisterAt(1)));
+    locations->SetInAt(2, X86CpuLocation(calling_convention.GetRegisterAt(2)));
+    codegen_->MarkNotLeaf();
+  } else {
+    locations->SetInAt(0, Location::RequiresRegister());
+    locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+    if (value_type == Primitive::kPrimBoolean || value_type == Primitive::kPrimByte) {
+      // Ensure the value is in a byte register.
+      locations->SetInAt(2, X86CpuLocation(EAX));
+    } else {
+      locations->SetInAt(2, Location::RequiresRegister());
+    }
+  }
+
+  instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Register obj = locations->InAt(0).AsX86().AsCpuRegister();
+  Location index = locations->InAt(1);
+  Primitive::Type value_type = instruction->InputAt(2)->GetType();
+
+  switch (value_type) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
+      ByteRegister value = locations->InAt(2).AsX86().AsByteRegister();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
+        __ movb(Address(obj, offset), value);
+      } else {
+        __ movb(Address(obj, index.AsX86().AsCpuRegister(), TIMES_1, data_offset), value);
+      }
+      break;
+    }
+
+    case Primitive::kPrimShort:
+    case Primitive::kPrimChar: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
+      Register value = locations->InAt(2).AsX86().AsCpuRegister();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
+        __ movw(Address(obj, offset), value);
+      } else {
+        __ movw(Address(obj, index.AsX86().AsCpuRegister(), TIMES_2, data_offset), value);
+      }
+      break;
+    }
+
+    case Primitive::kPrimInt: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+      Register value = locations->InAt(2).AsX86().AsCpuRegister();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+        __ movl(Address(obj, offset), value);
+      } else {
+        __ movl(Address(obj, index.AsX86().AsCpuRegister(), TIMES_4, data_offset), value);
+      }
+      break;
+    }
+
+    case Primitive::kPrimNot: {
+      DCHECK(!codegen_->IsLeafMethod());
+      __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAputObject)));
+      codegen_->RecordPcInfo(instruction->GetDexPc());
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
+      X86ManagedRegister value = locations->InAt(2).AsX86();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+        __ movl(Address(obj, offset), value.AsRegisterPairLow());
+        __ movl(Address(obj, offset + kX86WordSize), value.AsRegisterPairHigh());
+      } else {
+        __ movl(Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset),
+                value.AsRegisterPairLow());
+        __ movl(Address(obj, index.AsX86().AsCpuRegister(), TIMES_8, data_offset + kX86WordSize),
+                value.AsRegisterPairHigh());
+      }
+      break;
+    }
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << instruction->GetType();
+  }
+}
+
+void LocationsBuilderX86::VisitArrayLength(HArrayLength* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister());
+  instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86::VisitArrayLength(HArrayLength* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  uint32_t offset = mirror::Array::LengthOffset().Uint32Value();
+  Register obj = locations->InAt(0).AsX86().AsCpuRegister();
+  Register out = locations->Out().AsX86().AsCpuRegister();
+  __ movl(out, Address(obj, offset));
+}
+
+void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  // TODO: Have a normalization phase that makes this instruction never used.
+  locations->SetOut(Location::SameAsFirstInput());
+  instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86::VisitBoundsCheck(HBoundsCheck* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86(
+      instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+  codegen_->AddSlowPath(slow_path);
+
+  Register index = locations->InAt(0).AsX86().AsCpuRegister();
+  Register length = locations->InAt(1).AsX86().AsCpuRegister();
+
+  __ cmpl(index, length);
+  __ j(kAboveEqual, slow_path->GetEntryLabel());
+}
+
 void LocationsBuilderX86::VisitTemporary(HTemporary* temp) {
   temp->SetLocations(nullptr);
 }
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index b7c2ad8..7c50204 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -177,12 +177,15 @@
     return InstructionSet::kX86;
   }
 
- private:
   // Helper method to move a 32bits value between two locations.
   void Move32(Location destination, Location source);
   // Helper method to move a 64bits value between two locations.
   void Move64(Location destination, Location source);
 
+  // Emit a write barrier.
+  void MarkGCCard(Register temp, Register card, Register object, Register value);
+
+ private:
   LocationsBuilderX86 location_builder_;
   InstructionCodeGeneratorX86 instruction_visitor_;
   ParallelMoveResolverX86 move_resolver_;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 641e8e1..2ff2a17 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -43,6 +43,23 @@
 static constexpr int kNumberOfPushedRegistersAtEntry = 1;
 static constexpr int kCurrentMethodStackOffset = 0;
 
+static Location X86_64CpuLocation(Register reg) {
+  return Location::RegisterLocation(X86_64ManagedRegister::FromCpuRegister(reg));
+}
+
+static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX };
+static constexpr size_t kRuntimeParameterCoreRegistersLength =
+    arraysize(kRuntimeParameterCoreRegisters);
+
+class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
+ public:
+  InvokeRuntimeCallingConvention()
+      : CallingConvention(kRuntimeParameterCoreRegisters,
+                          kRuntimeParameterCoreRegistersLength) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
+};
 
 #define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())->
 
@@ -78,6 +95,32 @@
   DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86_64);
 };
 
+class BoundsCheckSlowPathX86_64 : public SlowPathCode {
+ public:
+  explicit BoundsCheckSlowPathX86_64(uint32_t dex_pc,
+                                     Location index_location,
+                                     Location length_location)
+      : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+
+  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    CodeGeneratorX86_64* x64_codegen = reinterpret_cast<CodeGeneratorX86_64*>(codegen);
+    __ Bind(GetEntryLabel());
+    InvokeRuntimeCallingConvention calling_convention;
+    x64_codegen->Move(X86_64CpuLocation(calling_convention.GetRegisterAt(0)), index_location_);
+    x64_codegen->Move(X86_64CpuLocation(calling_convention.GetRegisterAt(1)), length_location_);
+    __ gs()->call(Address::Absolute(
+        QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowArrayBounds), true));
+    codegen->RecordPcInfo(dex_pc_);
+  }
+
+ private:
+  const uint32_t dex_pc_;
+  const Location index_location_;
+  const Location length_location_;
+
+  DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86_64);
+};
+
 #undef __
 #define __ reinterpret_cast<X86_64Assembler*>(GetAssembler())->
 
@@ -103,10 +146,6 @@
   stream << X86_64ManagedRegister::FromXmmRegister(FloatRegister(reg));
 }
 
-static Location X86_64CpuLocation(Register reg) {
-  return Location::RegisterLocation(X86_64ManagedRegister::FromCpuRegister(reg));
-}
-
 CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph)
       : CodeGenerator(graph, kNumberOfRegIds),
         location_builder_(graph, this),
@@ -117,7 +156,8 @@
   return kNumberOfPushedRegistersAtEntry * kX86_64WordSize;
 }
 
-InstructionCodeGeneratorX86_64::InstructionCodeGeneratorX86_64(HGraph* graph, CodeGeneratorX86_64* codegen)
+InstructionCodeGeneratorX86_64::InstructionCodeGeneratorX86_64(HGraph* graph,
+                                                               CodeGeneratorX86_64* codegen)
       : HGraphVisitor(graph),
         assembler_(codegen->GetAssembler()),
         codegen_(codegen) {}
@@ -603,20 +643,6 @@
   __ ret();
 }
 
-static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX };
-static constexpr size_t kRuntimeParameterCoreRegistersLength =
-    arraysize(kRuntimeParameterCoreRegisters);
-
-class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
- public:
-  InvokeRuntimeCallingConvention()
-      : CallingConvention(kRuntimeParameterCoreRegisters,
-                          kRuntimeParameterCoreRegistersLength) {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
-};
-
 Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) {
   switch (type) {
     case Primitive::kPrimBoolean:
@@ -944,24 +970,14 @@
       break;
     }
 
-    case Primitive::kPrimInt: {
-      __ movl(Address(obj, offset), value);
-      break;
-    }
-
+    case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
       __ movl(Address(obj, offset), value);
-      Label is_null;
-      CpuRegister temp = locations->GetTemp(0).AsX86_64().AsCpuRegister();
-      CpuRegister card = locations->GetTemp(1).AsX86_64().AsCpuRegister();
-      __ testl(value, value);
-      __ j(kEqual, &is_null);
-      __ gs()->movq(card, Address::Absolute(
-          Thread::CardTableOffset<kX86_64WordSize>().Int32Value(), true));
-      __ movq(temp, obj);
-      __ shrq(temp, Immediate(gc::accounting::CardTable::kCardShift));
-      __ movb(Address(temp, card, TIMES_1, 0),  card);
-      __ Bind(&is_null);
+      if (field_type == Primitive::kPrimNot) {
+        CpuRegister temp = locations->GetTemp(0).AsX86_64().AsCpuRegister();
+        CpuRegister card = locations->GetTemp(1).AsX86_64().AsCpuRegister();
+        codegen_->MarkGCCard(temp, card, obj, value);
+      }
       break;
     }
 
@@ -1059,6 +1075,245 @@
   __ j(kEqual, slow_path->GetEntryLabel());
 }
 
+void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+  locations->SetOut(Location::RequiresRegister());
+  instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
+  Location index = locations->InAt(1);
+
+  switch (instruction->GetType()) {
+    case Primitive::kPrimBoolean: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
+      CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+      if (index.IsConstant()) {
+        __ movzxb(out, Address(obj,
+            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset));
+      } else {
+        __ movzxb(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_1, data_offset));
+      }
+      break;
+    }
+
+    case Primitive::kPrimByte: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
+      CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+      if (index.IsConstant()) {
+        __ movsxb(out, Address(obj,
+            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset));
+      } else {
+        __ movsxb(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_1, data_offset));
+      }
+      break;
+    }
+
+    case Primitive::kPrimShort: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
+      CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+      if (index.IsConstant()) {
+        __ movsxw(out, Address(obj,
+            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset));
+      } else {
+        __ movsxw(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_2, data_offset));
+      }
+      break;
+    }
+
+    case Primitive::kPrimChar: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
+      CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+      if (index.IsConstant()) {
+        __ movzxw(out, Address(obj,
+            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset));
+      } else {
+        __ movzxw(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_2, data_offset));
+      }
+      break;
+    }
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot: {
+      DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t));
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+      CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+      if (index.IsConstant()) {
+        __ movl(out, Address(obj,
+            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset));
+      } else {
+        __ movl(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_4, data_offset));
+      }
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
+      CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+      if (index.IsConstant()) {
+        __ movq(out, Address(obj,
+            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset));
+      } else {
+        __ movq(out, Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_8, data_offset));
+      }
+      break;
+    }
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << instruction->GetType();
+  }
+}
+
+void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  Primitive::Type value_type = instruction->InputAt(2)->GetType();
+  if (value_type == Primitive::kPrimNot) {
+    InvokeRuntimeCallingConvention calling_convention;
+    locations->SetInAt(0, X86_64CpuLocation(calling_convention.GetRegisterAt(0)));
+    locations->SetInAt(1, X86_64CpuLocation(calling_convention.GetRegisterAt(1)));
+    locations->SetInAt(2, X86_64CpuLocation(calling_convention.GetRegisterAt(2)));
+    codegen_->MarkNotLeaf();
+  } else {
+    locations->SetInAt(0, Location::RequiresRegister());
+    locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+    locations->SetInAt(2, Location::RequiresRegister());
+  }
+  instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
+  Location index = locations->InAt(1);
+  Primitive::Type value_type = instruction->InputAt(2)->GetType();
+
+  switch (value_type) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
+      CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
+        __ movb(Address(obj, offset), value);
+      } else {
+        __ movb(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_1, data_offset), value);
+      }
+      break;
+    }
+
+    case Primitive::kPrimShort:
+    case Primitive::kPrimChar: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
+      CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
+        __ movw(Address(obj, offset), value);
+      } else {
+        __ movw(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_2, data_offset), value);
+      }
+      break;
+    }
+
+    case Primitive::kPrimInt: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+      CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+        __ movl(Address(obj, offset), value);
+      } else {
+        __ movl(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_4, data_offset), value);
+      }
+      break;
+    }
+
+    case Primitive::kPrimNot: {
+      __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAputObject), true));
+      DCHECK(!codegen_->IsLeafMethod());
+      codegen_->RecordPcInfo(instruction->GetDexPc());
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
+      CpuRegister value = locations->InAt(2).AsX86_64().AsCpuRegister();
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+        __ movq(Address(obj, offset), value);
+      } else {
+        __ movq(Address(obj, index.AsX86_64().AsCpuRegister(), TIMES_8, data_offset), value);
+      }
+      break;
+    }
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << instruction->GetType();
+  }
+}
+
+void LocationsBuilderX86_64::VisitArrayLength(HArrayLength* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister());
+  instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  uint32_t offset = mirror::Array::LengthOffset().Uint32Value();
+  CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
+  CpuRegister out = locations->Out().AsX86_64().AsCpuRegister();
+  __ movl(out, Address(obj, offset));
+}
+
+void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  // TODO: Have a normalization phase that makes this instruction never used.
+  locations->SetOut(Location::SameAsFirstInput());
+  instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64(
+      instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+  codegen_->AddSlowPath(slow_path);
+
+  CpuRegister index = locations->InAt(0).AsX86_64().AsCpuRegister();
+  CpuRegister length = locations->InAt(1).AsX86_64().AsCpuRegister();
+
+  __ cmpl(index, length);
+  __ j(kAboveEqual, slow_path->GetEntryLabel());
+}
+
+void CodeGeneratorX86_64::MarkGCCard(CpuRegister temp,
+                                     CpuRegister card,
+                                     CpuRegister object,
+                                     CpuRegister value) {
+  Label is_null;
+  __ testl(value, value);
+  __ j(kEqual, &is_null);
+  __ gs()->movq(card, Address::Absolute(
+      Thread::CardTableOffset<kX86_64WordSize>().Int32Value(), true));
+  __ movq(temp, object);
+  __ shrq(temp, Immediate(gc::accounting::CardTable::kCardShift));
+  __ movb(Address(temp, card, TIMES_1, 0),  card);
+  __ Bind(&is_null);
+}
+
 void LocationsBuilderX86_64::VisitTemporary(HTemporary* temp) {
   temp->SetLocations(nullptr);
 }
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index a20ca3f..44552ea 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -176,10 +176,13 @@
     return InstructionSet::kX86_64;
   }
 
- private:
+  // Emit a write barrier.
+  void MarkGCCard(CpuRegister temp, CpuRegister card, CpuRegister object, CpuRegister value);
+
   // Helper method to move a value between two locations.
   void Move(Location destination, Location source);
 
+ private:
   LocationsBuilderX86_64 location_builder_;
   InstructionCodeGeneratorX86_64 instruction_visitor_;
   ParallelMoveResolverX86_64 move_resolver_;
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 61a6f6b..cb3dd0f 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -437,6 +437,10 @@
   M(Compare)                                               \
   M(InstanceFieldGet)                                      \
   M(InstanceFieldSet)                                      \
+  M(ArrayGet)                                              \
+  M(ArraySet)                                              \
+  M(ArrayLength)                                           \
+  M(BoundsCheck)                                           \
   M(NullCheck)                                             \
   M(Temporary)                                             \
 
@@ -1367,6 +1371,80 @@
   DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet);
 };
 
+class HArrayGet : public HExpression<2> {
+ public:
+  HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type)
+      : HExpression(type) {
+    SetRawInputAt(0, array);
+    SetRawInputAt(1, index);
+  }
+
+  DECLARE_INSTRUCTION(ArrayGet);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HArrayGet);
+};
+
+class HArraySet : public HTemplateInstruction<3> {
+ public:
+  HArraySet(HInstruction* array,
+            HInstruction* index,
+            HInstruction* value,
+            uint32_t dex_pc) : dex_pc_(dex_pc) {
+    SetRawInputAt(0, array);
+    SetRawInputAt(1, index);
+    SetRawInputAt(2, value);
+  }
+
+  virtual bool NeedsEnvironment() const {
+    // We currently always call a runtime method to catch array store
+    // exceptions.
+    return InputAt(2)->GetType() == Primitive::kPrimNot;
+  }
+
+  uint32_t GetDexPc() const { return dex_pc_; }
+
+  DECLARE_INSTRUCTION(ArraySet);
+
+ private:
+  const uint32_t dex_pc_;
+
+  DISALLOW_COPY_AND_ASSIGN(HArraySet);
+};
+
+class HArrayLength : public HExpression<1> {
+ public:
+  explicit HArrayLength(HInstruction* array) : HExpression(Primitive::kPrimInt) {
+    SetRawInputAt(0, array);
+  }
+
+  DECLARE_INSTRUCTION(ArrayLength);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HArrayLength);
+};
+
+class HBoundsCheck : public HExpression<2> {
+ public:
+  HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc)
+      : HExpression(index->GetType()), dex_pc_(dex_pc) {
+    DCHECK(index->GetType() == Primitive::kPrimInt);
+    SetRawInputAt(0, index);
+    SetRawInputAt(1, length);
+  }
+
+  virtual bool NeedsEnvironment() const { return true; }
+
+  uint32_t GetDexPc() const { return dex_pc_; }
+
+  DECLARE_INSTRUCTION(BoundsCheck);
+
+ private:
+  const uint32_t dex_pc_;
+
+  DISALLOW_COPY_AND_ASSIGN(HBoundsCheck);
+};
+
 /**
  * Some DEX instructions are folded into multiple HInstructions that need
  * to stay live until the last HInstruction. This class
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 78ff31a..4904428 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -659,6 +659,10 @@
     if (so.IsImmediate()) {
       return true;
     }
+
+    if (!can_contain_high_register) {
+      return true;
+    }
   }
 
   if (so.IsRegister() && IsHighRegister(so.GetRegister()) && !can_contain_high_register) {
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 3312f8f..891a287 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -312,6 +312,9 @@
   __ movs(R0, ShifterOperand(R1));
   __ mvns(R0, ShifterOperand(R1));
 
+  // 32 bit variants.
+  __ add(R12, R1, ShifterOperand(R0));
+
   size_t cs = __ CodeSize();
   std::vector<uint8_t> managed_code(cs);
   MemoryRegion code(&managed_code[0], managed_code.size());
@@ -868,6 +871,9 @@
 
   __ StoreToOffset(kStoreWord, R2, R4, 12);     // Simple
   __ StoreToOffset(kStoreWord, R2, R4, 0x2000);     // Offset too big.
+  __ StoreToOffset(kStoreWord, R0, R12, 12);
+  __ StoreToOffset(kStoreHalfword, R0, R12, 12);
+  __ StoreToOffset(kStoreByte, R2, R12, 12);
 
   size_t cs = __ CodeSize();
   std::vector<uint8_t> managed_code(cs);
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 18035f3..3f2641c 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -43,6 +43,7 @@
   "  3e:	42c8      	cmn	r0, r1\n",
   "  40:	0008      	movs	r0, r1\n",
   "  42:	43c8      	mvns	r0, r1\n",
+  "  44:	eb01 0c00   add.w	ip, r1, r0\n",
   nullptr
 };
 const char* DataProcessingImmediateResults[] = {
@@ -355,6 +356,9 @@
   "   2:	f44f 5c00 	mov.w	ip, #8192	; 0x2000\n",
   "   6:	44a4      	add	ip, r4\n",
   "   8:	f8cc 2000 	str.w	r2, [ip]\n",
+  "   c:	f8cc 000c 	str.w	r0, [ip, #12]\n",
+  "   10:	f8ac 000c 	strh.w	r0, [ip, #12]\n",
+  "   14:	f88c 200c 	strb.w	r2, [ip, #12]\n",
   nullptr
 };
 const char* IfThenResults[] = {
diff --git a/disassembler/disassembler.h b/disassembler/disassembler.h
index 7547ab7..183e692 100644
--- a/disassembler/disassembler.h
+++ b/disassembler/disassembler.h
@@ -43,6 +43,10 @@
   DISALLOW_COPY_AND_ASSIGN(Disassembler);
 };
 
+static inline bool HasBitSet(uint32_t value, uint32_t bit) {
+  return (value & (1 << bit)) != 0;
+}
+
 }  // namespace art
 
 #endif  // ART_DISASSEMBLER_DISASSEMBLER_H_
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index 1f565e5..56023c1 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -1262,7 +1262,7 @@
     case 3:
       switch (op2) {
         case 0x00: case 0x02: case 0x04: case 0x06:  // 000xxx0
-        case 0x08: case 0x0A: case 0x0C: case 0x0E: {
+        case 0x08: case 0x09: case 0x0A: case 0x0C: case 0x0E: {
           // Store single data item
           // |111|11|100|000|0|0000|1111|110000|000000|
           // |5 3|21|098|765|4|3  0|5  2|10   6|5    0|
@@ -1275,12 +1275,40 @@
           // uint32_t op4 = (instr >> 6) & 0x3F;
           switch (op3) {
             case 0x0: case 0x4: {
-              // STRB Rt,[Rn,#+/-imm8]     - 111 11 00 0 0 00 0 nnnn tttt 1 PUWii ii iiii
-              // STRB Rt,[Rn,Rm,lsl #imm2] - 111 11 00 0 0 00 0 nnnn tttt 0 00000 ii mmmm
+              // {ST,LD}RB Rt,[Rn,#+/-imm12]    - 111 11 00 0 1 00 0 nnnn tttt 1 PUWii ii iiii
+              // {ST,LD}RB Rt,[Rn,#+/-imm8]     - 111 11 00 0 0 00 0 nnnn tttt 1 PUWii ii iiii
+              // {ST,LD}RB Rt,[Rn,Rm,lsl #imm2] - 111 11 00 0 0 00 0 nnnn tttt 0 00000 ii mmmm
               ArmRegister Rn(instr, 16);
               ArmRegister Rt(instr, 12);
-              opcode << "strb";
-              if ((instr & 0x800) != 0) {
+              opcode << (HasBitSet(instr, 20) ? "ldrb" : "strb");
+              if (HasBitSet(instr, 23)) {
+                uint32_t imm12 = instr & 0xFFF;
+                args << Rt << ", [" << Rn << ",#" << imm12 << "]";
+              } else if ((instr & 0x800) != 0) {
+                uint32_t imm8 = instr & 0xFF;
+                args << Rt << ", [" << Rn << ",#" << imm8 << "]";
+              } else {
+                uint32_t imm2 = (instr >> 4) & 3;
+                ArmRegister Rm(instr, 0);
+                args << Rt << ", [" << Rn << ", " << Rm;
+                if (imm2 != 0) {
+                  args << ", " << "lsl #" << imm2;
+                }
+                args << "]";
+              }
+              break;
+            }
+            case 0x1: case 0x5: {
+              // STRH Rt,[Rn,#+/-imm12]    - 111 11 00 0 1 01 0 nnnn tttt 1 PUWii ii iiii
+              // STRH Rt,[Rn,#+/-imm8]     - 111 11 00 0 0 01 0 nnnn tttt 1 PUWii ii iiii
+              // STRH Rt,[Rn,Rm,lsl #imm2] - 111 11 00 0 0 01 0 nnnn tttt 0 00000 ii mmmm
+              ArmRegister Rn(instr, 16);
+              ArmRegister Rt(instr, 12);
+              opcode << "strh";
+              if (HasBitSet(instr, 23)) {
+                uint32_t imm12 = instr & 0xFFF;
+                args << Rt << ", [" << Rn << ",#" << imm12 << "]";
+              } else if ((instr & 0x800) != 0) {
                 uint32_t imm8 = instr & 0xFF;
                 args << Rt << ", [" << Rn << ",#" << imm8 << "]";
               } else {
@@ -1351,8 +1379,8 @@
 
           break;
         }
-        case 0x03: case 0x0B: case 0x13: case 0x1B: {  // 00xx011
-          // Load halfword
+        case 0x03: case 0x0B: case 0x11: case 0x13: case 0x19: case 0x1B: {  // 00xx011
+          // Load byte/halfword
           // |111|11|10|0 0|00|0|0000|1111|110000|000000|
           // |5 3|21|09|8 7|65|4|3  0|5  2|10   6|5    0|
           // |---|--|--|---|--|-|----|----|------|------|
@@ -1380,8 +1408,9 @@
               }
             } else if (op3 == 3) {
               // LDRSH.W Rt, [Rn, #imm12]      - 111 11 00 11 011 nnnn tttt iiiiiiiiiiii
+              // LDRSB.W Rt, [Rn, #imm12]      - 111 11 00 11 001 nnnn tttt iiiiiiiiiiii
               uint32_t imm12 = instr & 0xFFF;
-              opcode << "ldrsh.w";
+              opcode << (HasBitSet(instr, 20) ? "ldrsb.w" : "ldrsh.w");
               args << Rt << ", [" << Rn << ", #" << imm12 << "]";
               if (Rn.r == 9) {
                 args << "  ; ";
@@ -1575,6 +1604,9 @@
     opcode << it_conditions_.back();
     it_conditions_.pop_back();
   }
+  if (opcode.str().size() == 0) {
+    opcode << "UNKNOWN " << op2;
+  }
 
   os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instr, opcode.str().c_str()) << args.str() << '\n';
   return 4;
diff --git a/test/407-arrays/expected.txt b/test/407-arrays/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/407-arrays/expected.txt
diff --git a/test/407-arrays/info.txt b/test/407-arrays/info.txt
new file mode 100644
index 0000000..f30b667
--- /dev/null
+++ b/test/407-arrays/info.txt
@@ -0,0 +1 @@
+Simple tests for array accesses.
diff --git a/test/407-arrays/src/Main.java b/test/407-arrays/src/Main.java
new file mode 100644
index 0000000..5d27e6d
--- /dev/null
+++ b/test/407-arrays/src/Main.java
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+// Simple test for array accesses.
+
+public class Main extends TestCase {
+  public static void main(String[] args) {
+    $opt$testReads(new boolean[1], new byte[1], new char[1], new short[1],
+                   new int[1], new Object[1], new long[1], 0);
+    $opt$testWrites(new boolean[2], new byte[2], new char[2], new short[2],
+                    new int[2], new Object[2], new long[2], 1);
+    ensureThrows(new boolean[2], 2);
+    ensureThrows(new boolean[2], 4);
+    ensureThrows(new boolean[2], -1);
+    ensureThrows(new boolean[2], Integer.MIN_VALUE);
+    ensureThrows(new boolean[2], Integer.MAX_VALUE);
+  }
+
+  static void $opt$testReads(boolean[] bools, byte[] bytes, char[] chars, short[] shorts,
+                             int[] ints, Object[] objects, long[] longs, int index) {
+    assertEquals(false, bools[0]);
+    assertEquals(false, bools[index]);
+
+    assertEquals(0, bytes[0]);
+    assertEquals(0, bytes[index]);
+
+    assertEquals(0, chars[0]);
+    assertEquals(0, chars[index]);
+
+    assertEquals(0, shorts[0]);
+    assertEquals(0, shorts[index]);
+
+    assertEquals(0, ints[0]);
+    assertEquals(0, ints[index]);
+
+    assertNull(objects[0]);
+    assertNull(objects[index]);
+
+    assertEquals(0, longs[0]);
+    assertEquals(0, longs[index]);
+  }
+
+  static void $opt$testWrites(boolean[] bools, byte[] bytes, char[] chars, short[] shorts,
+                              int[] ints, Object[] objects, long[] longs, int index) {
+    bools[0] = true;
+    assertEquals(true, bools[0]);
+    bools[1] = true;
+    assertEquals(true, bools[index]);
+
+    bytes[0] = -4;
+    assertEquals(-4, bytes[0]);
+    bytes[index] = -8;
+    assertEquals(-8, bytes[index]);
+
+    chars[0] = 'c';
+    assertEquals('c', chars[0]);
+    chars[index] = 'd';
+    assertEquals('d', chars[index]);
+
+    shorts[0] = -42;
+    assertEquals(-42, shorts[0]);
+    shorts[index] = -84;
+    assertEquals(-84, shorts[index]);
+
+    ints[0] = -32;
+    assertEquals(-32, ints[0]);
+    ints[index] = -64;
+    assertEquals(-64, ints[index]);
+
+    Object o1 = new Object();
+    objects[0] = o1;
+    assertEquals(o1, objects[0]);
+    Object o2 = new Object();
+    objects[index] = o2;
+    assertEquals(o2, objects[index]);
+
+    long l = -21876876876876876L;
+    longs[0] = l;
+    assertEquals(l, longs[0]);
+    l = -21876876876876877L;
+    longs[index] = l;
+    assertEquals(l, longs[index]);
+  }
+
+  public static void ensureThrows(boolean[] array, int index) {
+    ArrayIndexOutOfBoundsException exception = null;
+    try {
+      $opt$doArrayLoad(array, index);
+    } catch (ArrayIndexOutOfBoundsException e) {
+      exception = e;
+    }
+
+    assertNotNull(exception);
+    assertTrue(exception.toString().contains(Integer.toString(index)));
+
+    exception = null;
+    try {
+      $opt$doArrayStore(array, index);
+    } catch (ArrayIndexOutOfBoundsException e) {
+      exception = e;
+    }
+
+    assertNotNull(exception);
+    assertTrue(exception.toString().contains(Integer.toString(index)));
+  }
+
+  public static void $opt$doArrayLoad(boolean[] array, int index) {
+    boolean res = array[index];
+  }
+
+  public static void $opt$doArrayStore(boolean[] array, int index) {
+    array[index] = false;
+  }
+}
diff --git a/test/407-arrays/src/TestCase.java b/test/407-arrays/src/TestCase.java
new file mode 100644
index 0000000..ef77f71
--- /dev/null
+++ b/test/407-arrays/src/TestCase.java
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+/**
+ * Common superclass for test cases.
+ */
+
+import java.util.Arrays;
+
+public abstract class TestCase {
+  public static void assertSame(Object expected, Object value) {
+    if (expected != value) {
+      throw new AssertionError("Objects are not the same: expected " +
+          String.valueOf(expected) + ", got " + String.valueOf(value));
+    }
+  }
+
+  public static void assertNotSame(Object expected, Object value) {
+    if (expected == value) {
+      throw new AssertionError(
+          "Objects are the same: " + String.valueOf(expected));
+    }
+  }
+
+  public static void assertEquals(String message, int expected, int actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(int expected, int actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertTrue(String message, boolean condition) {
+    if (!condition) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertTrue(boolean condition) {
+    assertTrue("Expected true", condition);
+  }
+
+  public static void assertFalse(String message, boolean condition) {
+    if (condition) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertFalse(boolean condition) {
+    assertFalse("Expected false", condition);
+  }
+
+  public static void assertEquals(Object expected, Object actual) {
+    if (!expected.equals(actual)) {
+      String msg = "Expected \"" + expected + "\" but got \"" + actual + "\"";
+      throw new AssertionError(msg);
+    }
+  }
+
+  public static void assertNotEquals(int expected, int actual) {
+    if (expected == actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertNotEquals(Object expected, Object actual) {
+    if (expected.equals(actual)) {
+      String msg = "Objects are the same: " + String.valueOf(expected);
+      throw new AssertionError(msg);
+    }
+  }
+
+  public static <T> void assertArrayEquals(T[] actual, T... expected) {
+      assertTrue(Arrays.equals(expected, actual));
+  }
+
+  public static void assertEquals(
+      String message, Object expected, Object actual) {
+    if (!expected.equals(actual)) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(
+      String message, long expected, long actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(long expected, long actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertEquals(
+      String message, boolean expected, boolean actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(boolean expected, boolean actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertEquals(
+      String message, float expected, float actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(float expected, float actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertEquals(float expected, float actual,
+                                  float tolerance) {
+    if ((actual < expected - tolerance) || (expected + tolerance < actual)) {
+      throw new AssertionError("Expected " + expected + " got " + actual +
+          " tolerance " + tolerance);
+    }
+  }
+
+  public static void assertEquals(
+      String message, double expected, double actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertEquals(double expected, double actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+  public static void assertEquals(double expected, double actual,
+                                  double tolerance) {
+    if ((actual < expected - tolerance) || (expected + tolerance < actual)) {
+      throw new AssertionError("Expected " + expected + " got " + actual +
+          " tolerance " + tolerance);
+    }
+  }
+
+  public static void assertSame(
+      String message, Object expected, Object actual) {
+    if (expected != actual) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertNull(String message, Object object) {
+    if (object != null) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertNull(Object object) {
+    assertNull("Expected null", object);
+  }
+
+  public static void assertNotNull(String message, Object object) {
+    if (object == null) {
+      throw new AssertionError(message);
+    }
+  }
+
+  public static void assertNotNull(Object object) {
+    assertNotNull("Expected non-null", object);
+  }
+
+  public static void fail(String msg) {
+    throw new AssertionError(msg);
+  }
+}