Merge "ART: Start RuntimeCallbacks"
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 5da59f3..faf8b41 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1060,13 +1060,13 @@
   virtual bool operator()(ObjPtr<mirror::Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
     const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
     for (auto& m : c->GetMethods(pointer_size)) {
-      ResolveExceptionsForMethod(&m, pointer_size);
+      ResolveExceptionsForMethod(&m);
     }
     return true;
   }
 
  private:
-  void ResolveExceptionsForMethod(ArtMethod* method_handle, PointerSize pointer_size)
+  void ResolveExceptionsForMethod(ArtMethod* method_handle)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     const DexFile::CodeItem* code_item = method_handle->GetCodeItem();
     if (code_item == nullptr) {
@@ -1088,8 +1088,7 @@
         dex::TypeIndex encoded_catch_handler_handlers_type_idx =
             dex::TypeIndex(DecodeUnsignedLeb128(&encoded_catch_handler_list));
         // Add to set of types to resolve if not already in the dex cache resolved types
-        if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx,
-                                              pointer_size)) {
+        if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
           exceptions_to_resolve_.emplace(encoded_catch_handler_handlers_type_idx,
                                          method_handle->GetDexFile());
         }
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 86d92ff..c69ed31 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -487,7 +487,7 @@
   EXPECT_EQ(72U, sizeof(OatHeader));
   EXPECT_EQ(4U, sizeof(OatMethodOffsets));
   EXPECT_EQ(20U, sizeof(OatQuickMethodHeader));
-  EXPECT_EQ(164 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
+  EXPECT_EQ(159 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
             sizeof(QuickEntryPoints));
 }
 
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 7dc094b..2ee4db9 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -153,21 +153,6 @@
     return instruction_ == bound.instruction_ && constant_ == bound.constant_;
   }
 
-  /*
-   * Hunt "under the hood" of array lengths (leading to array references),
-   * null checks (also leading to array references), and new arrays
-   * (leading to the actual length). This makes it more likely related
-   * instructions become actually comparable.
-   */
-  static HInstruction* HuntForDeclaration(HInstruction* instruction) {
-    while (instruction->IsArrayLength() ||
-           instruction->IsNullCheck() ||
-           instruction->IsNewArray()) {
-      instruction = instruction->InputAt(0);
-    }
-    return instruction;
-  }
-
   static bool Equal(HInstruction* instruction1, HInstruction* instruction2) {
     if (instruction1 == instruction2) {
       return true;
@@ -1136,7 +1121,7 @@
   }
 
   void VisitNewArray(HNewArray* new_array) OVERRIDE {
-    HInstruction* len = new_array->InputAt(0);
+    HInstruction* len = new_array->GetLength();
     if (!len->IsIntConstant()) {
       HInstruction *left;
       int32_t right_const;
@@ -1324,7 +1309,7 @@
     InductionVarRange::Value v2;
     bool needs_finite_test = false;
     HInstruction* index = context->InputAt(0);
-    HInstruction* hint = ValueBound::HuntForDeclaration(context->InputAt(1));
+    HInstruction* hint = HuntForDeclaration(context->InputAt(1));
     if (induction_range_.GetInductionRange(context, index, hint, &v1, &v2, &needs_finite_test)) {
       if (v1.is_known && (v1.a_constant == 0 || v1.a_constant == 1) &&
           v2.is_known && (v2.a_constant == 0 || v2.a_constant == 1)) {
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc
index dfa1504..5d58207 100644
--- a/compiler/optimizing/bounds_check_elimination_test.cc
+++ b/compiler/optimizing/bounds_check_elimination_test.cc
@@ -596,13 +596,11 @@
   HBasicBlock* block = new (allocator) HBasicBlock(graph);
   graph->AddBlock(block);
   entry->AddSuccessor(block);
+  // We pass a bogus constant for the class to avoid mocking one.
   HInstruction* new_array = new (allocator) HNewArray(
       constant_10,
-      graph->GetCurrentMethod(),
-      0,
-      dex::TypeIndex(static_cast<uint16_t>(Primitive::kPrimInt)),
-      graph->GetDexFile(),
-      kQuickAllocArray);
+      constant_10,
+      0);
   block->AddInstruction(new_array);
   block->AddInstruction(new (allocator) HGoto());
 
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 07b1746..9c9c604 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -3984,19 +3984,16 @@
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
   InvokeRuntimeCallingConvention calling_convention;
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
   locations->SetOut(Location::RegisterLocation(R0));
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
-  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
 }
 
 void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) {
-  InvokeRuntimeCallingConvention calling_convention;
-  __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_);
   // Note: if heap poisoning is enabled, the entry point takes cares
   // of poisoning the reference.
-  codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
-  CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
+  codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc());
+  CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
 }
 
 void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index b094e54..68d0b86 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -4747,22 +4747,16 @@
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
   InvokeRuntimeCallingConvention calling_convention;
-  locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
   locations->SetOut(LocationFrom(x0));
-  locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
-  locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(2)));
+  locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
+  locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
 }
 
 void InstructionCodeGeneratorARM64::VisitNewArray(HNewArray* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  InvokeRuntimeCallingConvention calling_convention;
-  Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt);
-  DCHECK(type_index.Is(w0));
-  __ Mov(type_index, instruction->GetTypeIndex().index_);
   // Note: if heap poisoning is enabled, the entry point takes cares
   // of poisoning the reference.
-  codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
-  CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
+  codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc());
+  CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
 }
 
 void LocationsBuilderARM64::VisitNewInstance(HNewInstance* instruction) {
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 05a76e1..592ee5a 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -431,10 +431,12 @@
       // TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to
       // kSaveEverything and use a temporary for the .bss entry address in the fast path,
       // so that we can avoid another calculation here.
+      UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
+      vixl32::Register temp = temps.Acquire();
       CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
           arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
-      arm_codegen->EmitMovwMovtPlaceholder(labels, ip);
-      __ Str(OutputRegister(cls_), MemOperand(ip));
+      arm_codegen->EmitMovwMovtPlaceholder(labels, temp);
+      __ Str(OutputRegister(cls_), MemOperand(temp));
     }
     __ B(GetExitLabel());
   }
@@ -3995,19 +3997,16 @@
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
   InvokeRuntimeCallingConventionARMVIXL calling_convention;
-  locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
   locations->SetOut(LocationFrom(r0));
   locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
   locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(2)));
 }
 
 void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
-  InvokeRuntimeCallingConventionARMVIXL calling_convention;
-  __ Mov(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_);
   // Note: if heap poisoning is enabled, the entry point takes cares
   // of poisoning the reference.
-  codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
-  CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
+  codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc());
+  CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
 }
 
 void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 24234e1..a038382 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -5897,21 +5897,14 @@
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
   InvokeRuntimeCallingConvention calling_convention;
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
   locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
 }
 
 void InstructionCodeGeneratorMIPS::VisitNewArray(HNewArray* instruction) {
-  InvokeRuntimeCallingConvention calling_convention;
-  Register current_method_register = calling_convention.GetRegisterAt(2);
-  __ Lw(current_method_register, SP, kCurrentMethodStackOffset);
-  // Move an uint16_t value to a register.
-  __ LoadConst32(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_);
-  codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
-  CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck,
-                       void*, uint32_t, int32_t, ArtMethod*>();
+  codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc());
+  CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
 }
 
 void LocationsBuilderMIPS::VisitNewInstance(HNewInstance* instruction) {
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index fc8fb7a..446dea6 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -3841,19 +3841,14 @@
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
   InvokeRuntimeCallingConvention calling_convention;
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
   locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
-  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
 }
 
 void InstructionCodeGeneratorMIPS64::VisitNewArray(HNewArray* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  // Move an uint16_t value to a register.
-  __ LoadConst32(locations->GetTemp(0).AsRegister<GpuRegister>(),
-                 instruction->GetTypeIndex().index_);
-  codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
-  CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
+  codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc());
+  CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
 }
 
 void LocationsBuilderMIPS64::VisitNewInstance(HNewInstance* instruction) {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index cc727d2..853c91f 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -4192,18 +4192,15 @@
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
   locations->SetOut(Location::RegisterLocation(EAX));
   InvokeRuntimeCallingConvention calling_convention;
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
-  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
 }
 
 void InstructionCodeGeneratorX86::VisitNewArray(HNewArray* instruction) {
-  InvokeRuntimeCallingConvention calling_convention;
-  __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex().index_));
   // Note: if heap poisoning is enabled, the entry point takes cares
   // of poisoning the reference.
-  codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
-  CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
+  codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc());
+  CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
   DCHECK(!codegen_->IsLeafMethod());
 }
 
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 9adedab..74c71cc 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -4088,21 +4088,16 @@
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
   InvokeRuntimeCallingConvention calling_convention;
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
   locations->SetOut(Location::RegisterLocation(RAX));
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
-  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
 }
 
 void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) {
-  InvokeRuntimeCallingConvention calling_convention;
-  codegen_->Load64BitValue(CpuRegister(calling_convention.GetRegisterAt(0)),
-                           instruction->GetTypeIndex().index_);
   // Note: if heap poisoning is enabled, the entry point takes cares
   // of poisoning the reference.
-  codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
-  CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
-
+  codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc());
+  CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
   DCHECK(!codegen_->IsLeafMethod());
 }
 
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index 6d8ae75..3973985 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -114,12 +114,7 @@
     }
   } else {
     *suitable = instruction;
-    while (instruction->IsArrayLength() ||
-           instruction->IsNullCheck() ||
-           instruction->IsNewArray()) {
-      instruction = instruction->InputAt(0);
-    }
-    return instruction == hint;
+    return HuntForDeclaration(instruction) == hint;
   }
   return false;
 }
@@ -629,7 +624,7 @@
     if (chase_hint_ == nullptr) {
       return is_min ? Value(0) : Value(std::numeric_limits<int32_t>::max());
     } else if (instruction->InputAt(0)->IsNewArray()) {
-      return GetFetch(instruction->InputAt(0)->InputAt(0), trip, in_body, is_min);
+      return GetFetch(instruction->InputAt(0)->AsNewArray()->GetLength(), trip, in_body, is_min);
     }
   } else if (instruction->IsTypeConversion()) {
     // Since analysis is 32-bit (or narrower), chase beyond widening along the path.
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index aa3e1aa..d81817f 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -697,13 +697,8 @@
 }
 
 TEST_F(InductionVarRangeTest, ArrayLengthAndHints) {
-  HInstruction* new_array = new (&allocator_)
-      HNewArray(x_,
-                graph_->GetCurrentMethod(),
-                0,
-                dex::TypeIndex(Primitive::kPrimInt),
-                graph_->GetDexFile(),
-                kQuickAllocArray);
+  // We pass a bogus constant for the class to avoid mocking one.
+  HInstruction* new_array = new (&allocator_) HNewArray(x_, x_, 0);
   entry_block_->AddInstruction(new_array);
   HInstruction* array_length = new (&allocator_) HArrayLength(new_array, 0);
   entry_block_->AddInstruction(array_length);
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 50aa442..5d40f75 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1429,15 +1429,6 @@
         return false;
       }
 
-      if (current->IsNewArray() &&
-          (current->AsNewArray()->GetEntrypoint() == kQuickAllocArrayWithAccessCheck)) {
-        VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
-                       << " could not be inlined because it is using an entrypoint"
-                       << " with access checks";
-        // Allocation entrypoint does not handle inlined frames.
-        return false;
-      }
-
       if (current->IsUnresolvedStaticFieldGet() ||
           current->IsUnresolvedInstanceFieldGet() ||
           current->IsUnresolvedStaticFieldSet() ||
@@ -1538,8 +1529,6 @@
     }
   }
 
-  PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-
   // Iterate over the list of parameter types and test whether any of the
   // actual inputs has a more specific reference type than the type declared in
   // the signature.
@@ -1551,9 +1540,9 @@
        ++param_idx, ++input_idx) {
     HInstruction* input = invoke_instruction->InputAt(input_idx);
     if (input->GetType() == Primitive::kPrimNot) {
-      mirror::Class* param_cls = resolved_method->GetDexCacheResolvedType(
+      mirror::Class* param_cls = resolved_method->GetClassFromTypeIndex(
           param_list->GetTypeItem(param_idx).type_idx_,
-          pointer_size);
+          /* resolve */ false);
       if (IsReferenceTypeRefinement(GetClassRTI(param_cls),
                                     /* declared_can_be_null */ true,
                                     input)) {
@@ -1602,8 +1591,7 @@
         // TODO: we could be more precise by merging the phi inputs but that requires
         // some functionality from the reference type propagation.
         DCHECK(return_replacement->IsPhi());
-        PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-        mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */, pointer_size);
+        mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */);
         return_replacement->SetReferenceTypeInfo(GetClassRTI(cls));
       }
     }
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 8ed0e7f..48653cf 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -1498,16 +1498,8 @@
                                               uint32_t* args,
                                               uint32_t register_index) {
   HInstruction* length = graph_->GetIntConstant(number_of_vreg_arguments, dex_pc);
-  bool finalizable;
-  QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable)
-      ? kQuickAllocArrayWithAccessCheck
-      : kQuickAllocArray;
-  HInstruction* object = new (arena_) HNewArray(length,
-                                                graph_->GetCurrentMethod(),
-                                                dex_pc,
-                                                type_index,
-                                                *dex_compilation_unit_->GetDexFile(),
-                                                entrypoint);
+  HLoadClass* cls = BuildLoadClass(type_index, dex_pc, /* check_access */ true);
+  HInstruction* object = new (arena_) HNewArray(cls, length, dex_pc);
   AppendInstruction(object);
 
   const char* descriptor = dex_file_->StringByTypeIdx(type_index);
@@ -2503,16 +2495,8 @@
     case Instruction::NEW_ARRAY: {
       dex::TypeIndex type_index(instruction.VRegC_22c());
       HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt);
-      bool finalizable;
-      QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable)
-          ? kQuickAllocArrayWithAccessCheck
-          : kQuickAllocArray;
-      AppendInstruction(new (arena_) HNewArray(length,
-                                               graph_->GetCurrentMethod(),
-                                               dex_pc,
-                                               type_index,
-                                               *dex_compilation_unit_->GetDexFile(),
-                                               entrypoint));
+      HLoadClass* cls = BuildLoadClass(type_index, dex_pc, /* check_access */ true);
+      AppendInstruction(new (arena_) HNewArray(cls, length, dex_pc));
       UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction());
       break;
     }
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 911bfb9..35f59cb 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -777,7 +777,7 @@
   // If the array is a NewArray with constant size, replace the array length
   // with the constant instruction. This helps the bounds check elimination phase.
   if (input->IsNewArray()) {
-    input = input->InputAt(0);
+    input = input->AsNewArray()->GetLength();
     if (input->IsIntConstant()) {
       instruction->ReplaceWith(input);
     }
@@ -1774,7 +1774,7 @@
   }
 
   if (potential_array->IsNewArray()) {
-    return potential_array->InputAt(0) == potential_length;
+    return potential_array->AsNewArray()->GetLength() == potential_length;
   }
 
   return false;
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 53b0fdd..a2980dc 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -3801,6 +3801,15 @@
     entrypoint_ = entrypoint;
   }
 
+  HLoadClass* GetLoadClass() const {
+    HInstruction* input = InputAt(0);
+    if (input->IsClinitCheck()) {
+      input = input->InputAt(0);
+    }
+    DCHECK(input->IsLoadClass());
+    return input->AsLoadClass();
+  }
+
   bool IsStringAlloc() const;
 
   DECLARE_INSTRUCTION(NewInstance);
@@ -4355,23 +4364,12 @@
 
 class HNewArray FINAL : public HExpression<2> {
  public:
-  HNewArray(HInstruction* length,
-            HCurrentMethod* current_method,
-            uint32_t dex_pc,
-            dex::TypeIndex type_index,
-            const DexFile& dex_file,
-            QuickEntrypointEnum entrypoint)
-      : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc),
-        type_index_(type_index),
-        dex_file_(dex_file),
-        entrypoint_(entrypoint) {
-    SetRawInputAt(0, length);
-    SetRawInputAt(1, current_method);
+  HNewArray(HInstruction* cls, HInstruction* length, uint32_t dex_pc)
+      : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc) {
+    SetRawInputAt(0, cls);
+    SetRawInputAt(1, length);
   }
 
-  dex::TypeIndex GetTypeIndex() const { return type_index_; }
-  const DexFile& GetDexFile() const { return dex_file_; }
-
   // Calls runtime so needs an environment.
   bool NeedsEnvironment() const OVERRIDE { return true; }
 
@@ -4380,15 +4378,18 @@
 
   bool CanBeNull() const OVERRIDE { return false; }
 
-  QuickEntrypointEnum GetEntrypoint() const { return entrypoint_; }
+  HLoadClass* GetLoadClass() const {
+    DCHECK(InputAt(0)->IsLoadClass());
+    return InputAt(0)->AsLoadClass();
+  }
+
+  HInstruction* GetLength() const {
+    return InputAt(1);
+  }
 
   DECLARE_INSTRUCTION(NewArray);
 
  private:
-  const dex::TypeIndex type_index_;
-  const DexFile& dex_file_;
-  const QuickEntrypointEnum entrypoint_;
-
   DISALLOW_COPY_AND_ASSIGN(HNewArray);
 };
 
@@ -5891,7 +5892,10 @@
 
   bool CanThrow() const OVERRIDE { return true; }
 
-  HLoadClass* GetLoadClass() const { return InputAt(0)->AsLoadClass(); }
+  HLoadClass* GetLoadClass() const {
+    DCHECK(InputAt(0)->IsLoadClass());
+    return InputAt(0)->AsLoadClass();
+  }
 
   DECLARE_INSTRUCTION(ClinitCheck);
 
@@ -6757,6 +6761,23 @@
   std::copy_backward(blocks->begin() + after + 1u, blocks->begin() + old_size, blocks->end());
 }
 
+/*
+ * Hunt "under the hood" of array lengths (leading to array references),
+ * null checks (also leading to array references), and new arrays
+ * (leading to the actual length). This makes it more likely related
+ * instructions become actually comparable.
+ */
+inline HInstruction* HuntForDeclaration(HInstruction* instruction) {
+  while (instruction->IsArrayLength() ||
+         instruction->IsNullCheck() ||
+         instruction->IsNewArray()) {
+    instruction = instruction->IsNewArray()
+        ? instruction->AsNewArray()->GetLength()
+        : instruction->InputAt(0);
+  }
+  return instruction;
+}
+
 }  // namespace art
 
 #endif  // ART_COMPILER_OPTIMIZING_NODES_H_
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index a4d59ab..b02f250 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -548,11 +548,13 @@
 }
 
 void ReferenceTypePropagation::RTPVisitor::VisitNewInstance(HNewInstance* instr) {
-  UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true);
+  ScopedObjectAccess soa(Thread::Current());
+  SetClassAsTypeInfo(instr, instr->GetLoadClass()->GetClass().Get(), /* is_exact */ true);
 }
 
 void ReferenceTypePropagation::RTPVisitor::VisitNewArray(HNewArray* instr) {
-  UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true);
+  ScopedObjectAccess soa(Thread::Current());
+  SetClassAsTypeInfo(instr, instr->GetLoadClass()->GetClass().Get(), /* is_exact */ true);
 }
 
 static mirror::Class* GetClassFromDexCache(Thread* self,
@@ -840,10 +842,8 @@
   }
 
   ScopedObjectAccess soa(Thread::Current());
-  ClassLinker* cl = Runtime::Current()->GetClassLinker();
-  PointerSize pointer_size = cl->GetImagePointerSize();
   ArtMethod* method = instr->GetResolvedMethod();
-  mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false, pointer_size);
+  mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(/* resolve */ false);
   SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
 }
 
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 5e55210..a3fce02 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -5610,7 +5610,7 @@
   " 214:	ecbd 8a10 	vpop	{s16-s31}\n",
   " 218:	e8bd 8de0 	ldmia.w	sp!, {r5, r6, r7, r8, sl, fp, pc}\n",
   " 21c:	4660      	mov	r0, ip\n",
-  " 21e:	f8d9 c2c0 	ldr.w	ip, [r9, #704]	; 0x2c0\n",
+  " 21e:	f8d9 c2ac 	ldr.w	ip, [r9, #684]	; 0x2ac\n",
   " 222:	47e0      	blx	ip\n",
   nullptr
 };
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 102c313..db1cad6 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -856,27 +856,6 @@
 #endif  // USE_READ_BARRIER
 .endm
 
-    /*
-     * Entry from managed code for array put operations of objects where the value being stored
-     * needs to be checked for compatibility.
-     * r0 = array, r1 = index, r2 = value
-     */
-ENTRY art_quick_aput_obj_with_null_and_bound_check
-    tst r0, r0
-    bne art_quick_aput_obj_with_bound_check
-    b art_quick_throw_null_pointer_exception
-END art_quick_aput_obj_with_null_and_bound_check
-
-    .hidden art_quick_aput_obj_with_bound_check
-ENTRY art_quick_aput_obj_with_bound_check
-    ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET]
-    cmp r3, r1
-    bhi art_quick_aput_obj
-    mov r0, r1
-    mov r1, r3
-    b art_quick_throw_array_bounds
-END art_quick_aput_obj_with_bound_check
-
 #ifdef USE_READ_BARRIER
     .extern artReadBarrierSlow
 #endif
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 3b3783c..436d331 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1404,33 +1404,6 @@
 #endif  // USE_READ_BARRIER
 .endm
 
-    /*
-     * Entry from managed code for array put operations of objects where the value being stored
-     * needs to be checked for compatibility.
-     * x0 = array, x1 = index, x2 = value
-     *
-     * Currently all values should fit into w0/w1/w2, and w1 always will as indices are 32b. We
-     * assume, though, that the upper 32b are zeroed out. At least for x1/w1 we can do better by
-     * using index-zero-extension in load/stores.
-     *
-     * Temporaries: x3, x4
-     * TODO: x4 OK? ip seems wrong here.
-     */
-ENTRY art_quick_aput_obj_with_null_and_bound_check
-    tst x0, x0
-    bne art_quick_aput_obj_with_bound_check
-    b art_quick_throw_null_pointer_exception
-END art_quick_aput_obj_with_null_and_bound_check
-
-ENTRY art_quick_aput_obj_with_bound_check
-    ldr w3, [x0, #MIRROR_ARRAY_LENGTH_OFFSET]
-    cmp w3, w1
-    bhi art_quick_aput_obj
-    mov x0, x1
-    mov x1, x3
-    b art_quick_throw_array_bounds
-END art_quick_aput_obj_with_bound_check
-
 #ifdef USE_READ_BARRIER
     .extern artReadBarrierSlow
 #endif
@@ -1672,11 +1645,9 @@
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
-// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB) implemented in asm
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
@@ -1764,13 +1735,6 @@
 
 
 // The common fast path code for art_quick_alloc_array_region_tlab.
-.macro ALLOC_ARRAY_TLAB_FAST_PATH slowPathLabel, xClass, wClass, xCount, wCount, xTemp0, wTemp0, xTemp1, wTemp1, xTemp2, wTemp2
-    // Check null class
-    cbz    \wClass, \slowPathLabel
-    ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED \slowPathLabel, \xClass, \wClass, \xCount, \wCount, \xTemp0, \wTemp0, \xTemp1, \wTemp1, \xTemp2, \wTemp2
-.endm
-
-// The common fast path code for art_quick_alloc_array_region_tlab.
 .macro ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED slowPathLabel, xClass, wClass, xCount, wCount, xTemp0, wTemp0, xTemp1, wTemp1, xTemp2, wTemp2
     // Array classes are never finalizable or uninitialized, no need to check.
     ldr    \wTemp0, [\xClass, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET] // Load component type
@@ -1907,64 +1871,31 @@
 // TODO: We could use this macro for the normal tlab allocator too.
 
 // The common code for art_quick_alloc_array_*region_tlab
-.macro GENERATE_ALLOC_ARRAY_REGION_TLAB name, entrypoint, fast_path, is_resolved
+.macro GENERATE_ALLOC_ARRAY_REGION_TLAB name, entrypoint, fast_path
 ENTRY \name
     // Fast path array allocation for region tlab allocation.
-    // x0: uint32_t type_idx
+    // x0: mirror::Class* type
     // x1: int32_t component_count
-    // x2: ArtMethod* method
-    // x3-x7: free.
+    // x2-x7: free.
 #if !defined(USE_READ_BARRIER)
     mvn    x0, xzr                                            // Read barrier must be enabled here.
     ret                                                       // Return -1.
 #endif
-.if \is_resolved
     mov    x3, x0
-    // If already resolved, class is stored in x0
-.else
-    ldr    x3, [x2, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_64]    // Load dex cache resolved types array
-                                                              // Load the class (x2)
-    ldr    w3, [x3, x0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT]
-.endif
-    // Most common case: GC is not marking.
-    ldr    w4, [xSELF, #THREAD_IS_GC_MARKING_OFFSET]
-    cbnz   x4, .Lmarking\name
-.Ldo_allocation\name:
     \fast_path .Lslow_path\name, x3, w3, x1, w1, x4, w4, x5, w5, x6, w6
-.Lmarking\name:
-    // GC is marking, check the lock word of the class for the mark bit.
-    // If the class is null, go slow path. The check is required to read the lock word.
-    cbz    w3, .Lslow_path\name
-    // Class is not null, check mark bit in lock word.
-    ldr    w4, [x3, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
-    // If the bit is not zero, do the allocation.
-    tbnz   w4, #LOCK_WORD_MARK_BIT_SHIFT, .Ldo_allocation\name
-                                                              // The read barrier slow path. Mark
-                                                              // the class.
-    stp    x0, x1, [sp, #-32]!                                // Save registers (x0, x1, x2, lr).
-    stp    x2, xLR, [sp, #16]
-    mov    x0, x3                                             // Pass the class as the first param.
-    bl     artReadBarrierMark
-    mov    x3, x0                                             // Get the (marked) class back.
-    ldp    x2, xLR, [sp, #16]
-    ldp    x0, x1, [sp], #32                                  // Restore registers.
-    b      .Ldo_allocation\name
 .Lslow_path\name:
-    // x0: uint32_t type_idx / mirror::Class* klass (if resolved)
+    // x0: mirror::Class* klass
     // x1: int32_t component_count
-    // x2: ArtMethod* method
-    // x3: Thread* self
+    // x2: Thread* self
     SETUP_SAVE_REFS_ONLY_FRAME        // save callee saves in case of GC
-    mov    x3, xSELF                  // pass Thread::Current
+    mov    x2, xSELF                  // pass Thread::Current
     bl     \entrypoint
     RESTORE_SAVE_REFS_ONLY_FRAME
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 END \name
 .endm
 
-GENERATE_ALLOC_ARRAY_REGION_TLAB art_quick_alloc_array_region_tlab, artAllocArrayFromCodeRegionTLAB, ALLOC_ARRAY_TLAB_FAST_PATH, 0
-// TODO: art_quick_alloc_array_resolved_region_tlab seems to not get called. Investigate compiler.
-GENERATE_ALLOC_ARRAY_REGION_TLAB art_quick_alloc_array_resolved_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED, 1
+GENERATE_ALLOC_ARRAY_REGION_TLAB art_quick_alloc_array_resolved_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED
 
     /*
      * Called by managed code when the thread has been asked to suspend.
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 5c56923..36f9ea7 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -142,16 +142,8 @@
   static_assert(!IsDirectEntrypoint(kQuickGetObjStatic), "Non-direct C stub marked direct.");
 
   // Array
-  qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
-  static_assert(!IsDirectEntrypoint(kQuickAputObjectWithNullAndBoundCheck),
-                "Non-direct C stub marked direct.");
-  qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
-  static_assert(!IsDirectEntrypoint(kQuickAputObjectWithBoundCheck),
-                "Non-direct C stub marked direct.");
   qpoints->pAputObject = art_quick_aput_obj;
   static_assert(!IsDirectEntrypoint(kQuickAputObject), "Non-direct C stub marked direct.");
-  qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
-  static_assert(!IsDirectEntrypoint(kQuickHandleFillArrayData), "Non-direct C stub marked direct.");
 
   // JNI
   qpoints->pJniMethodStart = JniMethodStart;
@@ -262,6 +254,7 @@
       art_quick_invoke_virtual_trampoline_with_access_check;
   static_assert(!IsDirectEntrypoint(kQuickInvokeVirtualTrampolineWithAccessCheck),
                 "Non-direct C stub marked direct.");
+  qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic;
 
   // Thread
   qpoints->pTestSuspend = art_quick_test_suspend;
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 3acc0a9..76218fb 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1389,28 +1389,6 @@
 #endif  // USE_READ_BARRIER
 .endm
 
-    /*
-     * Entry from managed code for array put operations of objects where the value being stored
-     * needs to be checked for compatibility.
-     * a0 = array, a1 = index, a2 = value
-     */
-ENTRY art_quick_aput_obj_with_null_and_bound_check
-    bnez    $a0, .Lart_quick_aput_obj_with_bound_check_gp_set
-    nop
-    b art_quick_throw_null_pointer_exception
-    nop
-END art_quick_aput_obj_with_null_and_bound_check
-
-ENTRY art_quick_aput_obj_with_bound_check
-    lw $t0, MIRROR_ARRAY_LENGTH_OFFSET($a0)
-    sltu $t1, $a1, $t0
-    bnez $t1, .Lart_quick_aput_obj_gp_set
-    nop
-    move $a0, $a1
-    b art_quick_throw_array_bounds
-    move $a1, $t0
-END art_quick_aput_obj_with_bound_check
-
 #ifdef USE_READ_BARRIER
     .extern artReadBarrierSlow
 #endif
@@ -2285,15 +2263,12 @@
 ENTRY art_quick_invoke_polymorphic
     SETUP_SAVE_REFS_AND_ARGS_FRAME
     move  $a2, rSELF                          # Make $a2 an alias for the current Thread.
-    move  $a3, $sp                            # Make $a3 a pointer to the saved frame context.
-    addiu $sp, $sp, -24                       # Reserve space for JValue result and 4 words for callee.
-    .cfi_adjust_cfa_offset 24
+    addiu $a3, $sp, ARG_SLOT_SIZE             # Make $a3 a pointer to the saved frame context.
     sw    $zero, 20($sp)                      # Initialize JValue result.
     sw    $zero, 16($sp)
-    addiu $a0, $sp, 16                        # Make $a0 a pointer to the JValue result
     la    $t9, artInvokePolymorphic
     jalr  $t9                                 # (result, receiver, Thread*, context)
-    nop
+    addiu $a0, $sp, 16                        # Make $a0 a pointer to the JValue result
 .macro MATCH_RETURN_TYPE c, handler
     li    $t0, \c
     beq   $v0, $t0, \handler
@@ -2307,18 +2282,17 @@
     MATCH_RETURN_TYPE 'D', .Lstore_double_result
     MATCH_RETURN_TYPE 'F', .Lstore_float_result
     MATCH_RETURN_TYPE 'S', .Lstore_int_result
+    MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result
 .purgem MATCH_RETURN_TYPE
     nop
     b .Lcleanup_and_return
     nop
 .Lstore_boolean_result:
+    b .Lcleanup_and_return
     lbu   $v0, 16($sp)                        # Move byte from JValue result to return value register.
-    b .Lcleanup_and_return
-    nop
 .Lstore_char_result:
-    lhu   $v0, 16($sp)                        # Move char from JValue result to return value register.
     b .Lcleanup_and_return
-    nop
+    lhu   $v0, 16($sp)                        # Move char from JValue result to return value register.
 .Lstore_double_result:
 .Lstore_float_result:
     LDu   $f0, $f1, 16, $sp, $t0              # Move double/float from JValue result to return value register.
@@ -2331,8 +2305,6 @@
     lw    $v0, 16($sp)                        # Move lower bits from JValue result to return value register.
     // Fall-through to clean up and return.
 .Lcleanup_and_return:
-    addiu $sp, $sp, 24                        # Remove space for JValue result and the 4 words for the callee.
-    .cfi_adjust_cfa_offset -24
     lw    $t7, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
     bnez  $t7, 1f                             # Success if no exception is pending.
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index ae786fe..b53fd10 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1360,29 +1360,6 @@
 #endif  // USE_READ_BARRIER
 .endm
 
-    /*
-     * Entry from managed code for array put operations of objects where the value being stored
-     * needs to be checked for compatibility.
-     * a0 = array, a1 = index, a2 = value
-     */
-ENTRY art_quick_aput_obj_with_null_and_bound_check
-    bne    $a0, $zero, .Lart_quick_aput_obj_with_bound_check_gp_set
-    nop
-    b art_quick_throw_null_pointer_exception
-    .cpreturn                       # Restore gp from t8 in branch delay slot.
-END art_quick_aput_obj_with_null_and_bound_check
-
-ENTRY art_quick_aput_obj_with_bound_check
-    lwu  $t0, MIRROR_ARRAY_LENGTH_OFFSET($a0)
-    sltu $t1, $a1, $t0
-    bne  $t1, $zero, .Lart_quick_aput_obj_gp_set
-    nop
-    move $a0, $a1
-    move $a1, $t0
-    b art_quick_throw_array_bounds
-    .cpreturn                       # Restore gp from t8 in branch delay slot.
-END art_quick_aput_obj_with_bound_check
-
 ENTRY art_quick_aput_obj
     beq  $a2, $zero, .Ldo_aput_null
     nop
@@ -2132,9 +2109,8 @@
     daddiu $sp, $sp, -8                        # Reserve space for JValue result.
     .cfi_adjust_cfa_offset 8
     sd     $zero, 0($sp)                       # Initialize JValue result.
-    move   $a0, $sp                            # Make $a0 a pointer to the JValue result
     jal    artInvokePolymorphic                # (result, receiver, Thread*, context)
-    nop
+    move   $a0, $sp                            # Make $a0 a pointer to the JValue result
 .macro MATCH_RETURN_TYPE c, handler
     li     $t0, \c
     beq    $v0, $t0, \handler
@@ -2148,27 +2124,24 @@
     MATCH_RETURN_TYPE 'D', .Lstore_double_result
     MATCH_RETURN_TYPE 'F', .Lstore_float_result
     MATCH_RETURN_TYPE 'S', .Lstore_long_result
+    MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result
 .purgem MATCH_RETURN_TYPE
     nop
     b .Lcleanup_and_return
     nop
 .Lstore_boolean_result:
+    b      .Lcleanup_and_return
     lbu    $v0, 0($sp)                         # Move byte from JValue result to return value register.
-    b      .Lcleanup_and_return
-    nop
 .Lstore_char_result:
-    lhu    $v0, 0($sp)                         # Move char from JValue result to return value register.
     b      .Lcleanup_and_return
-    nop
+    lhu    $v0, 0($sp)                         # Move char from JValue result to return value register.
 .Lstore_double_result:
 .Lstore_float_result:
+    b      .Lcleanup_and_return
     l.d    $f0, 0($sp)                         # Move double/float from JValue result to return value register.
-    b      .Lcleanup_and_return
-    nop
 .Lstore_ref_result:
-    lwu    $v0, 0($sp)                         # Move zero extended lower 32-bits to return value register.
     b      .Lcleanup_and_return
-    nop
+    lwu    $v0, 0($sp)                         # Move zero extended lower 32-bits to return value register.
 .Lstore_long_result:
     ld     $v0, 0($sp)                         # Move long from JValue result to return value register.
     // Fall-through to clean up and return.
diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S
index abd9046..cd6424a 100644
--- a/runtime/arch/quick_alloc_entrypoints.S
+++ b/runtime/arch/quick_alloc_entrypoints.S
@@ -25,14 +25,10 @@
 // Called by managed code to allocate an array.
 THREE_ARG_DOWNCALL art_quick_alloc_array\c_suffix, artAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate an array of a resolve class.
-THREE_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+TWO_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate an array when the caller doesn't know whether it has access
 // to the created type.
 THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check\c_suffix, artAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
-THREE_ARG_DOWNCALL art_quick_check_and_alloc_array\c_suffix, artCheckAndAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
-THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check\c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate a string from bytes
 FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes\c_suffix, artAllocStringFromBytesFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate a string from chars
@@ -68,13 +64,9 @@
 #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(c_suffix, cxx_suffix) \
   THREE_ARG_DOWNCALL art_quick_alloc_array ## c_suffix, artAllocArrayFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(c_suffix, cxx_suffix) \
-  THREE_ARG_DOWNCALL art_quick_alloc_array_resolved ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+  TWO_ARG_DOWNCALL art_quick_alloc_array_resolved ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \
   THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check ## c_suffix, artAllocArrayFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-#define GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(c_suffix, cxx_suffix) \
-  THREE_ARG_DOWNCALL art_quick_check_and_alloc_array ## c_suffix, artCheckAndAllocArrayFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-#define GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \
-  THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check ## c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(c_suffix, cxx_suffix) \
   FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes ## c_suffix, artAllocStringFromBytesFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(c_suffix, cxx_suffix) \
@@ -95,8 +87,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
@@ -110,8 +100,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB)
@@ -129,8 +117,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_dlmalloc, DlMalloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc, DlMalloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc, DlMalloc)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc, DlMalloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc, DlMalloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc, DlMalloc)
@@ -141,8 +127,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_dlmalloc_instrumented, DlMallocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc_instrumented, DlMallocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc_instrumented, DlMallocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc_instrumented, DlMallocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc_instrumented, DlMallocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc_instrumented, DlMallocInstrumented)
@@ -154,8 +138,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_rosalloc, RosAlloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc, RosAlloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc, RosAlloc)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc, RosAlloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc, RosAlloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc, RosAlloc)
@@ -166,8 +148,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_rosalloc_instrumented, RosAllocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc_instrumented, RosAllocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc_instrumented, RosAllocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc_instrumented, RosAllocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc_instrumented, RosAllocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc_instrumented, RosAllocInstrumented)
@@ -178,8 +158,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_bump_pointer, BumpPointer)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer, BumpPointer)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer, BumpPointer)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer, BumpPointer)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer, BumpPointer)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer, BumpPointer)
@@ -190,8 +168,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_bump_pointer_instrumented, BumpPointerInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer_instrumented, BumpPointerInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer_instrumented, BumpPointerInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer_instrumented, BumpPointerInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer_instrumented, BumpPointerInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer_instrumented, BumpPointerInstrumented)
@@ -202,8 +178,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab_instrumented, TLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab_instrumented, TLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab_instrumented, TLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab_instrumented, TLABInstrumented)
@@ -214,8 +188,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region, Region)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region, Region)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region, Region)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region, Region)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region, Region)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region, Region)
@@ -226,8 +198,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_instrumented, RegionInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_instrumented, RegionInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_instrumented, RegionInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_instrumented, RegionInstrumented)
@@ -238,8 +208,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab_instrumented, RegionTLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab_instrumented, RegionTLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab_instrumented, RegionTLABInstrumented)
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index ee65fa8..393dfe6 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -908,139 +908,6 @@
 #endif
 }
 
-
-TEST_F(StubTest, APutObj) {
-#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
-    (defined(__x86_64__) && !defined(__APPLE__))
-  Thread* self = Thread::Current();
-
-  // Do not check non-checked ones, we'd need handlers and stuff...
-  const uintptr_t art_quick_aput_obj_with_null_and_bound_check =
-      StubTest::GetEntrypoint(self, kQuickAputObjectWithNullAndBoundCheck);
-
-  // Create an object
-  ScopedObjectAccess soa(self);
-  // garbage is created during ClassLinker::Init
-
-  StackHandleScope<5> hs(soa.Self());
-  Handle<mirror::Class> c(
-      hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
-  Handle<mirror::Class> ca(
-      hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;")));
-
-  // Build a string array of size 1
-  Handle<mirror::ObjectArray<mirror::Object>> array(
-      hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), ca.Get(), 10)));
-
-  // Build a string -> should be assignable
-  Handle<mirror::String> str_obj(
-      hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!")));
-
-  // Build a generic object -> should fail assigning
-  Handle<mirror::Object> obj_obj(hs.NewHandle(c->AllocObject(soa.Self())));
-
-  // Play with it...
-
-  // 1) Success cases
-  // 1.1) Assign str_obj to array[0..3]
-
-  EXPECT_FALSE(self->IsExceptionPending());
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(str_obj.Get()),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(str_obj.Get(), array->Get(0));
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 1U, reinterpret_cast<size_t>(str_obj.Get()),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(str_obj.Get(), array->Get(1));
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 2U, reinterpret_cast<size_t>(str_obj.Get()),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(str_obj.Get(), array->Get(2));
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 3U, reinterpret_cast<size_t>(str_obj.Get()),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(str_obj.Get(), array->Get(3));
-
-  // 1.2) Assign null to array[0..3]
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(nullptr),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(nullptr, array->Get(0));
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 1U, reinterpret_cast<size_t>(nullptr),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(nullptr, array->Get(1));
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 2U, reinterpret_cast<size_t>(nullptr),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(nullptr, array->Get(2));
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 3U, reinterpret_cast<size_t>(nullptr),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(nullptr, array->Get(3));
-
-  // TODO: Check _which_ exception is thrown. Then make 3) check that it's the right check order.
-
-  // 2) Failure cases (str into str[])
-  // 2.1) Array = null
-  // TODO: Throwing NPE needs actual DEX code
-
-//  Invoke3(reinterpret_cast<size_t>(nullptr), 0U, reinterpret_cast<size_t>(str_obj.Get()),
-//          reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
-//
-//  EXPECT_TRUE(self->IsExceptionPending());
-//  self->ClearException();
-
-  // 2.2) Index < 0
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), static_cast<size_t>(-1),
-          reinterpret_cast<size_t>(str_obj.Get()),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_TRUE(self->IsExceptionPending());
-  self->ClearException();
-
-  // 2.3) Index > 0
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 10U, reinterpret_cast<size_t>(str_obj.Get()),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_TRUE(self->IsExceptionPending());
-  self->ClearException();
-
-  // 3) Failure cases (obj into str[])
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(obj_obj.Get()),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_TRUE(self->IsExceptionPending());
-  self->ClearException();
-
-  // Tests done.
-#else
-  LOG(INFO) << "Skipping aput_obj as I don't know how to do that on " << kRuntimeISA;
-  // Force-print to std::cout so it's also outside the logcat.
-  std::cout << "Skipping aput_obj as I don't know how to do that on " << kRuntimeISA << std::endl;
-#endif
-}
-
 TEST_F(StubTest, AllocObject) {
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
     (defined(__x86_64__) && !defined(__APPLE__))
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 1d979d8..c420259 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1352,26 +1352,6 @@
 #endif  // USE_READ_BARRIER
 END_MACRO
 
-    /*
-     * Entry from managed code for array put operations of objects where the value being stored
-     * needs to be checked for compatibility.
-     * eax = array, ecx = index, edx = value
-     */
-DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check
-    testl %eax, %eax
-    jnz SYMBOL(art_quick_aput_obj_with_bound_check)
-    jmp SYMBOL(art_quick_throw_null_pointer_exception)
-END_FUNCTION art_quick_aput_obj_with_null_and_bound_check
-
-DEFINE_FUNCTION art_quick_aput_obj_with_bound_check
-    movl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ebx
-    cmpl %ebx, %ecx
-    jb SYMBOL(art_quick_aput_obj)
-    mov %ecx, %eax
-    mov %ebx, %ecx
-    jmp SYMBOL(art_quick_throw_array_bounds)
-END_FUNCTION art_quick_aput_obj_with_bound_check
-
 DEFINE_FUNCTION art_quick_aput_obj
     test %edx, %edx              // store of null
     jz .Ldo_aput_null
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 28034c9..f5509bd 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -986,11 +986,9 @@
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
-// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
@@ -999,9 +997,8 @@
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB)
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB)
@@ -1119,12 +1116,11 @@
 END_MACRO
 
 // The fast path code for art_quick_alloc_array_region_tlab.
-// Inputs: RDI: uint32_t type_idx, RSI: int32_t component_count, RDX: ArtMethod* method
-// Temps: RCX: the class, r8, r9
+// Inputs: RDI: the class, RSI: int32_t component_count
+// Free temps: RCX, RDX, R8, R9
 // Output: RAX: return value.
 MACRO1(ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED, slowPathLabel)
-    movq %rcx, %r8                                             // Save class for later
-    movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rcx), %ecx        // Load component type.
+    movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rdi), %ecx        // Load component type.
     UNPOISON_HEAP_REF ecx
     movl MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET(%rcx), %ecx // Load primitive type.
     shrq LITERAL(PRIMITIVE_TYPE_SIZE_SHIFT_SHIFT), %rcx        // Get component size shift.
@@ -1151,8 +1147,8 @@
                                                                // Store the class pointer in the
                                                                // header.
                                                                // No fence needed for x86.
-    POISON_HEAP_REF r8d
-    movl %r8d, MIRROR_OBJECT_CLASS_OFFSET(%rax)
+    POISON_HEAP_REF edi
+    movl %edi, MIRROR_OBJECT_CLASS_OFFSET(%rax)
     movl %esi, MIRROR_ARRAY_LENGTH_OFFSET(%rax)
     ret                                                        // Fast path succeeded.
 END_MACRO
@@ -1173,8 +1169,8 @@
 MACRO1(ALLOC_ARRAY_TLAB_SLOW_PATH, cxx_name)
     SETUP_SAVE_REFS_ONLY_FRAME                                 // save ref containing registers for GC
     // Outgoing argument set up
-    movq %gs:THREAD_SELF_OFFSET, %rcx                          // pass Thread::Current()
-    call CALLVAR(cxx_name)                                     // cxx_name(arg0, arg1, arg2, Thread*)
+    movq %gs:THREAD_SELF_OFFSET, %rdx                          // pass Thread::Current()
+    call CALLVAR(cxx_name)                                     // cxx_name(arg0, arg1, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME                               // restore frame up to return address
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER                    // return or deliver exception
 END_MACRO
@@ -1199,73 +1195,21 @@
     ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeInitializedTLAB
 END_FUNCTION art_quick_alloc_object_initialized_tlab
 
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB).
-DEFINE_FUNCTION art_quick_alloc_array_tlab
-    // RDI: uint32_t type_idx, RSI: int32_t component_count, RDX: ArtMethod*
-    // RCX: klass, R8, R9: free. RAX: return val.
-    movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rdx), %rcx      // Load dex cache resolved types array
-    movl 0(%rcx, %rdi, COMPRESSED_REFERENCE_SIZE), %ecx        // Load the class
-    testl %ecx, %ecx
-    jz .Lart_quick_alloc_array_tlab_slow_path
-    ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_tlab_slow_path
-.Lart_quick_alloc_array_tlab_slow_path:
-    ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeTLAB
-END_FUNCTION art_quick_alloc_array_tlab
-
 // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB).
 DEFINE_FUNCTION art_quick_alloc_array_resolved_tlab
-    // RDI: mirror::Class* klass, RSI: int32_t component_count, RDX: ArtMethod*
-    // RCX: mirror::Class* klass, R8, R9: free. RAX: return val.
-    movq %rdi, %rcx
-    // Already resolved, no null check.
+    // RDI: mirror::Class* klass, RSI: int32_t component_count
+    // RDX, RCX, R8, R9: free. RAX: return val.
     ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_resolved_tlab_slow_path
 .Lart_quick_alloc_array_resolved_tlab_slow_path:
     ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeResolvedTLAB
 END_FUNCTION art_quick_alloc_array_resolved_tlab
 
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB).
-DEFINE_FUNCTION art_quick_alloc_array_region_tlab
-    // Fast path region tlab allocation.
-    // RDI: uint32_t type_idx, RSI: int32_t component_count, RDX: ArtMethod*
-    // RCX: klass, R8, R9: free. RAX: return val.
-    ASSERT_USE_READ_BARRIER
-    movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rdx), %rcx      // Load dex cache resolved types array
-    movl 0(%rcx, %rdi, COMPRESSED_REFERENCE_SIZE), %ecx        // Load the class
-    // Null check so that we can load the lock word.
-    testl %ecx, %ecx
-    jz .Lart_quick_alloc_array_region_tlab_slow_path
-    // Since we have allocation entrypoint switching, we know the GC is marking.
-    // Check the mark bit, if it is 0, do the read barrier mark.
-    testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%ecx)
-    jz .Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path
-.Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path_exit:
-    ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_region_tlab_slow_path
-.Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path:
-    // The read barrier slow path. Mark the class.
-    PUSH rdi
-    PUSH rsi
-    PUSH rdx
-    // Outgoing argument set up
-    movq %rcx, %rdi                                            // Pass the class as the first param.
-    call SYMBOL(artReadBarrierMark)                            // cxx_name(mirror::Object* obj)
-    movq %rax, %rcx
-    POP rdx
-    POP rsi
-    POP rdi
-    jmp .Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path_exit
-.Lart_quick_alloc_array_region_tlab_slow_path:
-    ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeRegionTLAB
-END_FUNCTION art_quick_alloc_array_region_tlab
-
 // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB).
 DEFINE_FUNCTION art_quick_alloc_array_resolved_region_tlab
     // Fast path region tlab allocation.
-    // RDI: mirror::Class* klass, RSI: int32_t component_count, RDX: ArtMethod*
-    // RCX: mirror::Class* klass, R8, R9: free. RAX: return val.
+    // RDI: mirror::Class* klass, RSI: int32_t component_count
+    // RCX, RDX, R8, R9: free. RAX: return val.
     ASSERT_USE_READ_BARRIER
-    movq %rdi, %rcx
-    // Caller is responsible for read barrier.
-    // Already resolved, no null check.
     ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_resolved_region_tlab_slow_path
 .Lart_quick_alloc_array_resolved_region_tlab_slow_path:
     ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeResolvedRegionTLAB
@@ -1466,7 +1410,7 @@
      * 64b PUSH/POP and 32b argument.
      * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
      *
-     * As with art_quick_aput_obj* functions, the 64b versions are in comments.
+     * As with art_quick_aput_obj function, the 64b versions are in comments.
      */
 MACRO4(READ_BARRIER, obj_reg, offset, dest_reg32, dest_reg64)
 #ifdef USE_READ_BARRIER
@@ -1503,46 +1447,6 @@
 #endif  // USE_READ_BARRIER
 END_MACRO
 
-    /*
-     * Entry from managed code for array put operations of objects where the value being stored
-     * needs to be checked for compatibility.
-     *
-     * Currently all the parameters should fit into the 32b portions of the registers. Index always
-     * will. So we optimize for a tighter encoding. The 64b versions are in comments.
-     *
-     * rdi(edi) = array, rsi(esi) = index, rdx(edx) = value
-     */
-DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check
-#if defined(__APPLE__)
-    int3
-    int3
-#else
-    testl %edi, %edi
-//  testq %rdi, %rdi
-    jnz art_quick_aput_obj_with_bound_check
-    jmp art_quick_throw_null_pointer_exception
-#endif  // __APPLE__
-END_FUNCTION art_quick_aput_obj_with_null_and_bound_check
-
-
-DEFINE_FUNCTION art_quick_aput_obj_with_bound_check
-#if defined(__APPLE__)
-    int3
-    int3
-#else
-    movl MIRROR_ARRAY_LENGTH_OFFSET(%edi), %ecx
-//  movl MIRROR_ARRAY_LENGTH_OFFSET(%rdi), %ecx  // This zero-extends, so value(%rcx)=value(%ecx)
-    cmpl %ecx, %esi
-    jb art_quick_aput_obj
-    mov %esi, %edi
-//  mov %rsi, %rdi
-    mov %ecx, %esi
-//  mov %rcx, %rsi
-    jmp art_quick_throw_array_bounds
-#endif  // __APPLE__
-END_FUNCTION art_quick_aput_obj_with_bound_check
-
-
 DEFINE_FUNCTION art_quick_aput_obj
     testl %edx, %edx                // store of null
 //  test %rdx, %rdx
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 9c20740..0fd891c 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -180,20 +180,6 @@
                                                   pointer_size);
 }
 
-template <bool kWithCheck>
-inline mirror::Class* ArtMethod::GetDexCacheResolvedType(dex::TypeIndex type_index,
-                                                         PointerSize pointer_size) {
-  if (kWithCheck) {
-    mirror::DexCache* dex_cache = GetInterfaceMethodIfProxy(pointer_size)->GetDexCache();
-    if (UNLIKELY(type_index.index_ >= dex_cache->NumResolvedTypes())) {
-      ThrowArrayIndexOutOfBoundsException(type_index.index_, dex_cache->NumResolvedTypes());
-      return nullptr;
-    }
-  }
-  mirror::Class* klass = GetDexCacheResolvedTypes(pointer_size)[type_index.index_].Read();
-  return (klass != nullptr && !klass->IsErroneous()) ? klass : nullptr;
-}
-
 inline bool ArtMethod::HasDexCacheResolvedTypes(PointerSize pointer_size) {
   return GetDexCacheResolvedTypes(pointer_size) != nullptr;
 }
@@ -207,15 +193,15 @@
   return GetDexCacheResolvedTypes(pointer_size) == other->GetDexCacheResolvedTypes(pointer_size);
 }
 
-inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx,
-                                                       bool resolve,
-                                                       PointerSize pointer_size) {
-  mirror::Class* type = GetDexCacheResolvedType(type_idx, pointer_size);
-  if (type == nullptr && resolve) {
-    type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this);
+inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) {
+  ObjPtr<mirror::DexCache> dex_cache = GetDexCache();
+  ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
+  if (UNLIKELY(type == nullptr) && resolve) {
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    type = class_linker->ResolveType(type_idx, this);
     CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
   }
-  return type;
+  return type.Ptr();
 }
 
 inline bool ArtMethod::CheckIncompatibleClassChange(InvokeType type) {
@@ -333,9 +319,9 @@
   return GetDexFile()->GetCodeItem(GetCodeItemOffset());
 }
 
-inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size) {
+inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx) {
   DCHECK(!IsProxyMethod());
-  return GetDexCacheResolvedType(type_idx, pointer_size) != nullptr;
+  return GetClassFromTypeIndex(type_idx, /* resolve */ false) != nullptr;
 }
 
 inline int32_t ArtMethod::GetLineNumFromDexPC(uint32_t dex_pc) {
@@ -435,18 +421,13 @@
   SetNativePointer(DexCacheResolvedTypesOffset(pointer_size), new_dex_cache_types, pointer_size);
 }
 
-inline mirror::Class* ArtMethod::GetReturnType(bool resolve, PointerSize pointer_size) {
+inline mirror::Class* ArtMethod::GetReturnType(bool resolve) {
   DCHECK(!IsProxyMethod());
   const DexFile* dex_file = GetDexFile();
   const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex());
   const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id);
   dex::TypeIndex return_type_idx = proto_id.return_type_idx_;
-  mirror::Class* type = GetDexCacheResolvedType(return_type_idx, pointer_size);
-  if (type == nullptr && resolve) {
-    type = Runtime::Current()->GetClassLinker()->ResolveType(return_type_idx, this);
-    CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
-  }
-  return type;
+  return GetClassFromTypeIndex(return_type_idx, resolve);
 }
 
 inline bool ArtMethod::HasSingleImplementation() {
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index dfc7837..d7d39af 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -236,7 +236,6 @@
   // Default to handler not found.
   uint32_t found_dex_pc = DexFile::kDexNoIndex;
   // Iterate over the catch handlers associated with dex_pc.
-  PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
   for (CatchHandlerIterator it(*code_item, dex_pc); it.HasNext(); it.Next()) {
     dex::TypeIndex iter_type_idx = it.GetHandlerTypeIndex();
     // Catch all case
@@ -245,9 +244,7 @@
       break;
     }
     // Does this catch exception type apply?
-    mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx,
-                                                               true /* resolve */,
-                                                               pointer_size);
+    mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx, true /* resolve */);
     if (UNLIKELY(iter_exception_type == nullptr)) {
       // Now have a NoClassDefFoundError as exception. Ignore in case the exception class was
       // removed by a pro-guard like tool.
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 2c31f6c..912df85 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -351,9 +351,6 @@
   bool HasSameDexCacheResolvedMethods(ArtMethod** other_cache, PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  template <bool kWithCheck = true>
-  mirror::Class* GetDexCacheResolvedType(dex::TypeIndex type_idx, PointerSize pointer_size)
-      REQUIRES_SHARED(Locks::mutator_lock_);
   void SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_cache_types,
                                 PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -364,9 +361,7 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Get the Class* from the type index into this method's dex cache.
-  mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx,
-                                       bool resolve,
-                                       PointerSize pointer_size)
+  mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Returns true if this method has the same name and signature of the other method.
@@ -558,8 +553,7 @@
 
   const DexFile::CodeItem* GetCodeItem() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  bool IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size)
-      REQUIRES_SHARED(Locks::mutator_lock_);
+  bool IsResolvedTypeIdx(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
 
   int32_t GetLineNumFromDexPC(uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -580,8 +574,7 @@
 
   // May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large
   // number of bugs at call sites.
-  mirror::Class* GetReturnType(bool resolve, PointerSize pointer_size)
-      REQUIRES_SHARED(Locks::mutator_lock_);
+  mirror::Class* GetReturnType(bool resolve) REQUIRES_SHARED(Locks::mutator_lock_);
 
   mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 4b15a22..18a53c9 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -104,7 +104,7 @@
 
 // Offset of field Thread::tlsPtr_.mterp_current_ibase.
 #define THREAD_CURRENT_IBASE_OFFSET \
-    (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 164) * __SIZEOF_POINTER__)
+    (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 159) * __SIZEOF_POINTER__)
 ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET,
             art::Thread::MterpCurrentIBaseOffset<POINTER_SIZE>().Int32Value())
 // Offset of field Thread::tlsPtr_.mterp_default_ibase.
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index edb58c4..fcc92dd 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -46,6 +46,7 @@
 ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr;
 Mutex* Locks::instrument_entrypoints_lock_ = nullptr;
 Mutex* Locks::intern_table_lock_ = nullptr;
+Mutex* Locks::jni_function_table_lock_ = nullptr;
 Mutex* Locks::jni_libraries_lock_ = nullptr;
 Mutex* Locks::logging_lock_ = nullptr;
 Mutex* Locks::mem_maps_lock_ = nullptr;
@@ -958,6 +959,7 @@
     DCHECK(verifier_deps_lock_ != nullptr);
     DCHECK(host_dlopen_handles_lock_ != nullptr);
     DCHECK(intern_table_lock_ != nullptr);
+    DCHECK(jni_function_table_lock_ != nullptr);
     DCHECK(jni_libraries_lock_ != nullptr);
     DCHECK(logging_lock_ != nullptr);
     DCHECK(mutator_lock_ != nullptr);
@@ -1104,6 +1106,10 @@
     DCHECK(jni_weak_globals_lock_ == nullptr);
     jni_weak_globals_lock_ = new Mutex("JNI weak global reference table lock", current_lock_level);
 
+    UPDATE_CURRENT_LOCK_LEVEL(kJniFunctionTableLock);
+    DCHECK(jni_function_table_lock_ == nullptr);
+    jni_function_table_lock_ = new Mutex("JNI function table lock", current_lock_level);
+
     UPDATE_CURRENT_LOCK_LEVEL(kAbortLock);
     DCHECK(abort_lock_ == nullptr);
     abort_lock_ = new Mutex("abort lock", current_lock_level, true);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index e44bdb8..3867b1b 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -68,6 +68,7 @@
   kRosAllocBulkFreeLock,
   kMarkSweepMarkStackLock,
   kTransactionLogLock,
+  kJniFunctionTableLock,
   kJniWeakGlobalsLock,
   kJniGlobalsLock,
   kReferenceQueueSoftReferencesLock,
@@ -702,8 +703,11 @@
   // Guard accesses to the JNI Weak Global Reference table.
   static Mutex* jni_weak_globals_lock_ ACQUIRED_AFTER(jni_globals_lock_);
 
+  // Guard accesses to the JNI function table override.
+  static Mutex* jni_function_table_lock_ ACQUIRED_AFTER(jni_weak_globals_lock_);
+
   // Have an exclusive aborting thread.
-  static Mutex* abort_lock_ ACQUIRED_AFTER(jni_weak_globals_lock_);
+  static Mutex* abort_lock_ ACQUIRED_AFTER(jni_function_table_lock_);
 
   // Allow mutual exclusion when manipulating Thread::suspend_count_.
   // TODO: Does the trade-off of a per-thread lock make sense?
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 5fc5f1a..2e17dd8 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -25,7 +25,6 @@
 #include "mirror/class_loader.h"
 #include "mirror/dex_cache-inl.h"
 #include "mirror/iftable.h"
-#include "mirror/throwable.h"
 #include "mirror/object_array.h"
 #include "handle_scope-inl.h"
 #include "scoped_thread_state_change-inl.h"
@@ -90,25 +89,16 @@
   if (kIsDebugBuild) {
     Thread::Current()->AssertNoPendingException();
   }
-  ObjPtr<mirror::Class> resolved_type =
-      referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_);
+  ObjPtr<mirror::Class> resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx);
   if (UNLIKELY(resolved_type == nullptr)) {
     StackHandleScope<2> hs(Thread::Current());
-    // There could be an out of bounds exception from GetDexCacheResolvedType, don't call
-    // ResolveType for this case.
-    if (LIKELY(!hs.Self()->IsExceptionPending())) {
-      ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
-      Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
-      Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
-      const DexFile& dex_file = *dex_cache->GetDexFile();
-      resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader);
-      // Note: We cannot check here to see whether we added the type to the cache. The type
-      //       might be an erroneous class, which results in it being hidden from us.
-    } else {
-      // Make sure its an array out of bounds exception.
-      DCHECK(hs.Self()->GetException()->GetClass()->DescriptorEquals(
-          "Ljava/lang/ArrayIndexOutOfBoundsException;"));
-    }
+    ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
+    Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
+    Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
+    const DexFile& dex_file = *dex_cache->GetDexFile();
+    resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader);
+    // Note: We cannot check here to see whether we added the type to the cache. The type
+    //       might be an erroneous class, which results in it being hidden from us.
   }
   return resolved_type.Ptr();
 }
@@ -256,8 +246,8 @@
     // Locate the dex cache of the original interface/Object
     for (const DexCacheData& data : dex_caches_) {
       if (!self->IsJWeakCleared(data.weak_root) &&
-          proxy_method->HasSameDexCacheResolvedTypes(data.resolved_types,
-                                                     image_pointer_size_)) {
+          proxy_method->HasSameDexCacheResolvedMethods(data.resolved_methods,
+                                                       image_pointer_size_)) {
         ObjPtr<mirror::DexCache> dex_cache =
             ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root));
         if (dex_cache != nullptr) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 448b460..07c6eda 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3303,7 +3303,7 @@
   DexCacheData data;
   data.weak_root = dex_cache_jweak;
   data.dex_file = dex_cache->GetDexFile();
-  data.resolved_types = dex_cache->GetResolvedTypes();
+  data.resolved_methods = dex_cache->GetResolvedMethods();
   dex_caches_.push_back(data);
 }
 
@@ -4373,8 +4373,7 @@
   CHECK_STREQ(np->GetName(), prototype->GetName());
   CHECK_STREQ(np->GetShorty(), prototype->GetShorty());
   // More complex sanity - via dex cache
-  CHECK_EQ(np->GetReturnType(true /* resolve */, image_pointer_size_),
-           prototype->GetReturnType(true /* resolve */, image_pointer_size_));
+  CHECK_EQ(np->GetReturnType(true /* resolve */), prototype->GetReturnType(true /* resolve */));
 }
 
 bool ClassLinker::CanWeInitializeClass(ObjPtr<mirror::Class> klass, bool can_init_statics,
@@ -4836,7 +4835,6 @@
 }
 
 static bool HasSameSignatureWithDifferentClassLoaders(Thread* self,
-                                                      PointerSize pointer_size,
                                                       Handle<mirror::Class> klass,
                                                       Handle<mirror::Class> super_klass,
                                                       ArtMethod* method1,
@@ -4844,14 +4842,12 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   {
     StackHandleScope<1> hs(self);
-    Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */,
-                                                                          pointer_size)));
+    Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */)));
     if (UNLIKELY(return_type.Get() == nullptr)) {
       ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method1);
       return false;
     }
-    ObjPtr<mirror::Class> other_return_type = method2->GetReturnType(true /* resolve */,
-                                                              pointer_size);
+    ObjPtr<mirror::Class> other_return_type = method2->GetReturnType(true /* resolve */);
     if (UNLIKELY(other_return_type == nullptr)) {
       ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method2);
       return false;
@@ -4896,7 +4892,7 @@
     StackHandleScope<1> hs(self);
     dex::TypeIndex param_type_idx = types1->GetTypeItem(i).type_idx_;
     Handle<mirror::Class> param_type(hs.NewHandle(
-        method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */, pointer_size)));
+        method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */)));
     if (UNLIKELY(param_type.Get() == nullptr)) {
       ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
                                              method1, i, param_type_idx);
@@ -4904,7 +4900,7 @@
     }
     dex::TypeIndex other_param_type_idx = types2->GetTypeItem(i).type_idx_;
     ObjPtr<mirror::Class> other_param_type =
-        method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */, pointer_size);
+        method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */);
     if (UNLIKELY(other_param_type == nullptr)) {
       ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
                                              method2, i, other_param_type_idx);
@@ -4940,9 +4936,11 @@
       auto* m = klass->GetVTableEntry(i, image_pointer_size_);
       auto* super_m = klass->GetSuperClass()->GetVTableEntry(i, image_pointer_size_);
       if (m != super_m) {
-        if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_,
-                                                                klass, super_klass,
-                                                                m, super_m))) {
+        if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self,
+                                                                klass,
+                                                                super_klass,
+                                                                m,
+                                                                super_m))) {
           self->AssertPendingException();
           return false;
         }
@@ -4958,9 +4956,11 @@
             j, image_pointer_size_);
         auto* super_m = super_klass->GetVirtualMethod(j, image_pointer_size_);
         if (m != super_m) {
-          if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_,
-                                                                  klass, super_klass,
-                                                                  m, super_m))) {
+          if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self,
+                                                                  klass,
+                                                                  super_klass,
+                                                                  m,
+                                                                  super_m))) {
             self->AssertPendingException();
             return false;
           }
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index cad674e..9b98671 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -649,6 +649,10 @@
   ClassTable* ClassTableForClassLoader(ObjPtr<mirror::ClassLoader> class_loader)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  void AppendToBootClassPath(Thread* self, const DexFile& dex_file)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!Locks::dex_lock_);
+
   struct DexCacheData {
     // Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may
     // not work properly.
@@ -657,7 +661,7 @@
     // jweak decode that triggers read barriers (and mark them alive unnecessarily and mess with
     // class unloading.)
     const DexFile* dex_file;
-    GcRoot<mirror::Class>* resolved_types;
+    ArtMethod** resolved_methods;
   };
 
  private:
@@ -744,9 +748,6 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
 
-  void AppendToBootClassPath(Thread* self, const DexFile& dex_file)
-      REQUIRES_SHARED(Locks::mutator_lock_)
-      REQUIRES(!Locks::dex_lock_);
   void AppendToBootClassPath(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index c97c4e4..6da7e3a 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -3987,9 +3987,7 @@
         if (shorty[i + 1] == 'L') {
           // Did we really get an argument of an appropriate reference type?
           mirror::Class* parameter_type =
-              m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_,
-                                       true /* resolve */,
-                                       kRuntimePointerSize);
+              m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_, true /* resolve */);
           mirror::Object* argument = gRegistry->Get<mirror::Object*>(arg_values[i], &error);
           if (error != JDWP::ERR_NONE) {
             return JDWP::ERR_INVALID_OBJECT;
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index 9504e8b..16a447b 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -608,7 +608,7 @@
     return nullptr;
   }
   Handle<mirror::Class> method_return(hs.NewHandle(
-      annotation_method->GetReturnType(true /* resolve */, pointer_size)));
+      annotation_method->GetReturnType(true /* resolve */)));
 
   DexFile::AnnotationValue annotation_value;
   if (!ProcessAnnotationValue(klass, annotation, &annotation_value, method_return,
@@ -948,9 +948,7 @@
   DexFile::AnnotationValue annotation_value;
   StackHandleScope<2> hs(Thread::Current());
   Handle<mirror::Class> h_klass(hs.NewHandle(klass));
-  PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-  Handle<mirror::Class> return_type(hs.NewHandle(
-      method->GetReturnType(true /* resolve */, pointer_size)));
+  Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType(true /* resolve */)));
   if (!ProcessAnnotationValue(h_klass, &annotation, &annotation_value, return_type,
                               DexFile::kAllObjects)) {
     return nullptr;
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 7d6f866..ac0ce36 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -241,10 +241,9 @@
     *slow_path = true;
     return nullptr;  // Failure
   }
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  PointerSize pointer_size = class_linker->GetImagePointerSize();
-  mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, pointer_size);
+  mirror::Class* klass = method->GetDexCache()->GetResolvedType(type_idx);
   if (UNLIKELY(klass == nullptr)) {  // Not in dex cache so try to resolve
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     klass = class_linker->ResolveType(type_idx, method);
     *slow_path = true;
     if (klass == nullptr) {  // Error
@@ -294,11 +293,10 @@
                                              klass->GetComponentSizeShift(), allocator_type);
 }
 
-template <bool kAccessCheck, bool kInstrumented>
+template <bool kInstrumented>
 ALWAYS_INLINE
 inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass,
                                                  int32_t component_count,
-                                                 ArtMethod* method,
                                                  Thread* self,
                                                  gc::AllocatorType allocator_type) {
   DCHECK(klass != nullptr);
@@ -306,13 +304,6 @@
     ThrowNegativeArraySizeException(component_count);
     return nullptr;  // Failure
   }
-  if (kAccessCheck) {
-    mirror::Class* referrer = method->GetDeclaringClass();
-    if (UNLIKELY(!referrer->CanAccess(klass))) {
-      ThrowIllegalAccessErrorClass(referrer, klass);
-      return nullptr;  // Failure
-    }
-  }
   // No need to retry a slow-path allocation as the above code won't cause a GC or thread
   // suspension.
   return mirror::Array::Alloc<kInstrumented>(self, klass, component_count,
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index b17e1a8..25fd727 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -38,98 +38,13 @@
 
 namespace art {
 
-static inline mirror::Class* CheckFilledNewArrayAlloc(dex::TypeIndex type_idx,
-                                                      int32_t component_count,
-                                                      ArtMethod* referrer,
-                                                      Thread* self,
-                                                      bool access_check)
-    REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
-  if (UNLIKELY(component_count < 0)) {
-    ThrowNegativeArraySizeException(component_count);
-    return nullptr;  // Failure
-  }
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  PointerSize pointer_size = class_linker->GetImagePointerSize();
-  mirror::Class* klass = referrer->GetDexCacheResolvedType<false>(type_idx, pointer_size);
-  if (UNLIKELY(klass == nullptr)) {  // Not in dex cache so try to resolve
-    klass = class_linker->ResolveType(type_idx, referrer);
-    if (klass == nullptr) {  // Error
-      DCHECK(self->IsExceptionPending());
-      return nullptr;  // Failure
-    }
-  }
-  if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) {
-    if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) {
-      ThrowRuntimeException("Bad filled array request for type %s",
-                            klass->PrettyDescriptor().c_str());
-    } else {
-      self->ThrowNewExceptionF(
-          "Ljava/lang/InternalError;",
-          "Found type %s; filled-new-array not implemented for anything but 'int'",
-          klass->PrettyDescriptor().c_str());
-    }
-    return nullptr;  // Failure
-  }
-  if (access_check) {
-    mirror::Class* referrer_klass = referrer->GetDeclaringClass();
-    if (UNLIKELY(!referrer_klass->CanAccess(klass))) {
-      ThrowIllegalAccessErrorClass(referrer_klass, klass);
-      return nullptr;  // Failure
-    }
-  }
-  DCHECK(klass->IsArrayClass()) << klass->PrettyClass();
-  return klass;
-}
-
-// Helper function to allocate array for FILLED_NEW_ARRAY.
-mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx,
-                                          int32_t component_count,
-                                          ArtMethod* referrer,
-                                          Thread* self,
-                                          bool access_check,
-                                          gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) {
-  mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self,
-                                                  access_check);
-  if (UNLIKELY(klass == nullptr)) {
-    return nullptr;
-  }
-  // Always go slow path for now, filled new array is not common.
-  gc::Heap* heap = Runtime::Current()->GetHeap();
-  // Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then
-  // the heap switched the allocator type while we were suspended.
-  return mirror::Array::Alloc<false>(self, klass, component_count,
-                                     klass->GetComponentSizeShift(),
-                                     heap->GetCurrentAllocator());
-}
-
-// Helper function to allocate array for FILLED_NEW_ARRAY.
-mirror::Array* CheckAndAllocArrayFromCodeInstrumented(
-    dex::TypeIndex type_idx,
-    int32_t component_count,
-    ArtMethod* referrer,
-    Thread* self,
-    bool access_check,
-    gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) {
-  mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self,
-                                                  access_check);
-  if (UNLIKELY(klass == nullptr)) {
-    return nullptr;
-  }
-  gc::Heap* heap = Runtime::Current()->GetHeap();
-  // Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then
-  // the heap switched the allocator type while we were suspended.
-  return mirror::Array::Alloc<true>(self, klass, component_count,
-                                    klass->GetComponentSizeShift(),
-                                    heap->GetCurrentAllocator());
-}
-
 void CheckReferenceResult(Handle<mirror::Object> o, Thread* self) {
   if (o.Get() == nullptr) {
     return;
   }
   // Make sure that the result is an instance of the type this method was expected to return.
   ArtMethod* method = self->GetCurrentMethod(nullptr);
-  mirror::Class* return_type = method->GetReturnType(true /* resolve */, kRuntimePointerSize);
+  mirror::Class* return_type = method->GetReturnType(true /* resolve */);
 
   if (!o->InstanceOf(return_type)) {
     Runtime::Current()->GetJavaVM()->JniAbortF(nullptr,
@@ -192,8 +107,7 @@
       ArtMethod* interface_method =
           soa.Decode<mirror::Method>(interface_method_jobj)->GetArtMethod();
       // This can cause thread suspension.
-      PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-      mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */, pointer_size);
+      mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */);
       ObjPtr<mirror::Object> result_ref = soa.Decode<mirror::Object>(result);
       JValue result_unboxed;
       if (!UnboxPrimitiveForResult(result_ref.Ptr(), result_type, &result_unboxed)) {
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index d4cf83c..6a04f20 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -93,33 +93,14 @@
     REQUIRES_SHARED(Locks::mutator_lock_)
     REQUIRES(!Roles::uninterruptible_);
 
-template <bool kAccessCheck, bool kInstrumented>
+template <bool kInstrumented>
 ALWAYS_INLINE inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass,
                                                                int32_t component_count,
-                                                               ArtMethod* method,
                                                                Thread* self,
                                                                gc::AllocatorType allocator_type)
     REQUIRES_SHARED(Locks::mutator_lock_)
     REQUIRES(!Roles::uninterruptible_);
 
-mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx,
-                                          int32_t component_count,
-                                          ArtMethod* method,
-                                          Thread* self,
-                                          bool access_check,
-                                          gc::AllocatorType allocator_type)
-    REQUIRES_SHARED(Locks::mutator_lock_)
-    REQUIRES(!Roles::uninterruptible_);
-
-mirror::Array* CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex type_idx,
-                                                      int32_t component_count,
-                                                      ArtMethod* method,
-                                                      Thread* self,
-                                                      bool access_check,
-                                                      gc::AllocatorType allocator_type)
-    REQUIRES_SHARED(Locks::mutator_lock_)
-    REQUIRES(!Roles::uninterruptible_);
-
 // Type of find field operation for fast and slow case.
 enum FindFieldType {
   InstanceObjectRead,
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 2d06508..3fa5fbf 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -93,11 +93,11 @@
                                                       allocator_type); \
 } \
 extern "C" mirror::Array* artAllocArrayFromCodeResolved##suffix##suffix2( \
-    mirror::Class* klass, int32_t component_count, ArtMethod* method, Thread* self) \
+    mirror::Class* klass, int32_t component_count, Thread* self) \
     REQUIRES_SHARED(Locks::mutator_lock_) { \
   ScopedQuickEntrypointChecks sqec(self); \
-  return AllocArrayFromCodeResolved<false, instrumented_bool>(klass, component_count, method, self, \
-                                                              allocator_type); \
+  return AllocArrayFromCodeResolved<instrumented_bool>(klass, component_count, self, \
+                                                       allocator_type); \
 } \
 extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \
     uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \
@@ -109,46 +109,6 @@
                                                      self, \
                                                      allocator_type); \
 } \
-extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \
-    uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \
-    REQUIRES_SHARED(Locks::mutator_lock_) { \
-  ScopedQuickEntrypointChecks sqec(self); \
-  if (!(instrumented_bool)) { \
-    return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \
-                                      component_count, \
-                                      method, \
-                                      self, \
-                                      false, \
-                                      allocator_type); \
-  } else { \
-    return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \
-                                                  component_count, \
-                                                  method, \
-                                                  self, \
-                                                  false, \
-                                                  allocator_type); \
-  } \
-} \
-extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \
-    uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \
-    REQUIRES_SHARED(Locks::mutator_lock_) { \
-  ScopedQuickEntrypointChecks sqec(self); \
-  if (!(instrumented_bool)) { \
-    return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \
-                                      component_count, \
-                                      method, \
-                                      self, \
-                                      true, \
-                                      allocator_type); \
-  } else { \
-    return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \
-                                                  component_count, \
-                                                  method, \
-                                                  self, \
-                                                  true, \
-                                                  allocator_type); \
-  } \
-} \
 extern "C" mirror::String* artAllocStringFromBytesFromCode##suffix##suffix2( \
     mirror::ByteArray* byte_array, int32_t high, int32_t offset, int32_t byte_count, \
     Thread* self) \
@@ -189,7 +149,7 @@
 
 #define GENERATE_ENTRYPOINTS(suffix) \
 extern "C" void* art_quick_alloc_array##suffix(uint32_t, int32_t, ArtMethod* ref); \
-extern "C" void* art_quick_alloc_array_resolved##suffix(mirror::Class* klass, int32_t, ArtMethod* ref); \
+extern "C" void* art_quick_alloc_array_resolved##suffix(mirror::Class* klass, int32_t); \
 extern "C" void* art_quick_alloc_array_with_access_check##suffix(uint32_t, int32_t, ArtMethod* ref); \
 extern "C" void* art_quick_alloc_object_resolved##suffix(mirror::Class* klass); \
 extern "C" void* art_quick_alloc_object_initialized##suffix(mirror::Class* klass); \
@@ -200,7 +160,7 @@
 extern "C" void* art_quick_alloc_string_from_chars##suffix(int32_t, int32_t, void*); \
 extern "C" void* art_quick_alloc_string_from_string##suffix(void*); \
 extern "C" void* art_quick_alloc_array##suffix##_instrumented(uint32_t, int32_t, ArtMethod* ref); \
-extern "C" void* art_quick_alloc_array_resolved##suffix##_instrumented(mirror::Class* klass, int32_t, ArtMethod* ref); \
+extern "C" void* art_quick_alloc_array_resolved##suffix##_instrumented(mirror::Class* klass, int32_t); \
 extern "C" void* art_quick_alloc_array_with_access_check##suffix##_instrumented(uint32_t, int32_t, ArtMethod* ref); \
 extern "C" void* art_quick_alloc_object##suffix##_instrumented(uint32_t type_idx, ArtMethod* ref); \
 extern "C" void* art_quick_alloc_object_resolved##suffix##_instrumented(mirror::Class* klass); \
@@ -219,8 +179,6 @@
     qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix##_instrumented; \
     qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix##_instrumented; \
     qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix##_instrumented; \
-    qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix##_instrumented; \
-    qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented; \
     qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix##_instrumented; \
     qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix##_instrumented; \
     qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix##_instrumented; \
@@ -231,8 +189,6 @@
     qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix; \
     qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix; \
     qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix; \
-    qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix; \
-    qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix; \
     qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix; \
     qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix; \
     qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix; \
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
index 8ce61c1..6481b97 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -66,10 +66,7 @@
   qpoints->pGetObjStatic = art_quick_get_obj_static;
 
   // Array
-  qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
-  qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
   qpoints->pAputObject = art_quick_aput_obj;
-  qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
 
   // JNI
   qpoints->pJniMethodStart = JniMethodStart;
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index 4d5d6de..b44f29b 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -21,13 +21,11 @@
 
 #define QUICK_ENTRYPOINT_LIST(V) \
   V(AllocArray, void*, uint32_t, int32_t, ArtMethod*) \
-  V(AllocArrayResolved, void*, mirror::Class*, int32_t, ArtMethod*) \
+  V(AllocArrayResolved, void*, mirror::Class*, int32_t) \
   V(AllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*) \
   V(AllocObjectResolved, void*, mirror::Class*) \
   V(AllocObjectInitialized, void*, mirror::Class*) \
   V(AllocObjectWithChecks, void*, mirror::Class*) \
-  V(CheckAndAllocArray, void*, uint32_t, int32_t, ArtMethod*) \
-  V(CheckAndAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*) \
   V(AllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t) \
   V(AllocStringFromChars, void*, int32_t, int32_t, void*) \
   V(AllocStringFromString, void*, void*) \
@@ -65,10 +63,7 @@
   V(GetObjInstance, void*, uint32_t, void*) \
   V(GetObjStatic, void*, uint32_t) \
 \
-  V(AputObjectWithNullAndBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \
-  V(AputObjectWithBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \
   V(AputObject, void, mirror::Array*, int32_t, mirror::Object*) \
-  V(HandleFillArrayData, void, void*, void*) \
 \
   V(JniMethodStart, uint32_t, Thread*) \
   V(JniMethodFastStart, uint32_t, Thread*) \
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 6301f93..b9caf0f 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -161,12 +161,8 @@
                          sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectInitialized, pAllocObjectWithChecks,
                          sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pCheckAndAllocArray,
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pAllocStringFromBytes,
                          sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArray, pCheckAndAllocArrayWithAccessCheck,
-                         sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArrayWithAccessCheck,
-                         pAllocStringFromBytes, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromBytes, pAllocStringFromChars,
                          sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromChars, pAllocStringFromString,
@@ -205,13 +201,8 @@
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Instance, pGet64Static, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Static, pGetObjInstance, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjInstance, pGetObjStatic, sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjStatic, pAputObjectWithNullAndBoundCheck,
-                         sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithNullAndBoundCheck,
-                         pAputObjectWithBoundCheck, sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithBoundCheck, pAputObject, sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pHandleFillArrayData, sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pHandleFillArrayData, pJniMethodStart, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjStatic, pAputObject, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pJniMethodStart, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodStart, pJniMethodFastStart,
                          sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodFastStart, pJniMethodStartSynchronized,
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 7b86339..6044053 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -846,7 +846,7 @@
         // Some threads in 'runnable_thread_ids' are probably stuck. Try to dump their stacks.
         // Avoid using ThreadList::Dump() initially because it is likely to get stuck as well.
         {
-          ReaderMutexLock mu0(self, *Locks::mutator_lock_);
+          ScopedObjectAccess soa(self);
           MutexLock mu1(self, *Locks::thread_list_lock_);
           for (Thread* thread : thread_list->GetList()) {
             uint32_t tid = thread->GetThreadId();
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index bebcd71..9e7f52b 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -60,10 +60,6 @@
 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DEX_CACHE_METHODS_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: DexCacheResolvedMethodsOffset(art::PointerSize::k32).Int32Value())))
 #define ART_METHOD_DEX_CACHE_METHODS_OFFSET_64 24
 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DEX_CACHE_METHODS_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: DexCacheResolvedMethodsOffset(art::PointerSize::k64).Int32Value())))
-#define ART_METHOD_DEX_CACHE_TYPES_OFFSET_32 24
-DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DEX_CACHE_TYPES_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: DexCacheResolvedTypesOffset(art::PointerSize::k32).Int32Value())))
-#define ART_METHOD_DEX_CACHE_TYPES_OFFSET_64 32
-DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DEX_CACHE_TYPES_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: DexCacheResolvedTypesOffset(art::PointerSize::k64).Int32Value())))
 #define ART_METHOD_JNI_OFFSET_32 28
 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_JNI_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromJniOffset(art::PointerSize::k32).Int32Value())))
 #define ART_METHOD_JNI_OFFSET_64 40
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index ca26207..76777d9 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -751,16 +751,14 @@
         case 'L': {
           ObjPtr<mirror::Object> o = shadow_frame.GetVRegReference(src_reg);
           if (do_assignability_check && o != nullptr) {
-            PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
             const dex::TypeIndex type_idx = params->GetTypeItem(shorty_pos).type_idx_;
-            ObjPtr<mirror::Class> arg_type = method->GetDexCacheResolvedType(type_idx,
-                                                                             pointer_size);
+            ObjPtr<mirror::Class> arg_type = method->GetDexCache()->GetResolvedType(type_idx);
             if (arg_type == nullptr) {
               StackHandleScope<1> hs(self);
               // Preserve o since it is used below and GetClassFromTypeIndex may cause thread
               // suspension.
               HandleWrapperObjPtr<mirror::Object> h = hs.NewHandleWrapper(&o);
-              arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */, pointer_size);
+              arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */);
               if (arg_type == nullptr) {
                 CHECK(self->IsExceptionPending());
                 return false;
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index d7dfcd4..a77a3fc 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -285,9 +285,7 @@
         const size_t ref_idx = inst->VRegA_11x(inst_data);
         ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx);
         if (do_assignability_check && obj_result != nullptr) {
-          PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-          ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */,
-                                                                    pointer_size);
+          ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */);
           // Re-load since it might have moved.
           obj_result = shadow_frame.GetVRegReference(ref_idx);
           if (return_type == nullptr) {
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index 5a3fafa..0148a1c 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -29,6 +29,7 @@
 #include "mirror/object-inl.h"
 #include "nth_caller_visitor.h"
 #include "thread-inl.h"
+#include "thread_list.h"
 
 namespace art {
 
@@ -37,6 +38,8 @@
 static constexpr size_t kMonitorsInitial = 32;  // Arbitrary.
 static constexpr size_t kMonitorsMax = 4096;  // Arbitrary sanity check.
 
+const JNINativeInterface* JNIEnvExt::table_override_ = nullptr;
+
 // Checking "locals" requires the mutator lock, but at creation time we're really only interested
 // in validity, which isn't changing. To avoid grabbing the mutator lock, factored out and tagged
 // with NO_THREAD_SAFETY_ANALYSIS.
@@ -78,10 +81,10 @@
       runtime_deleted(false),
       critical(0),
       monitors("monitors", kMonitorsInitial, kMonitorsMax) {
-  functions = unchecked_functions = GetJniNativeInterface();
-  if (vm->IsCheckJniEnabled()) {
-    SetCheckJniEnabled(true);
-  }
+  MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_);
+  check_jni = vm->IsCheckJniEnabled();
+  functions = GetFunctionTable(check_jni);
+  unchecked_functions = GetJniNativeInterface();
 }
 
 void JNIEnvExt::SetFunctionsToRuntimeShutdownFunctions() {
@@ -107,7 +110,12 @@
 
 void JNIEnvExt::SetCheckJniEnabled(bool enabled) {
   check_jni = enabled;
-  functions = enabled ? GetCheckJniNativeInterface() : GetJniNativeInterface();
+  MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_);
+  functions = GetFunctionTable(enabled);
+  // Check whether this is a no-op because of override.
+  if (enabled && JNIEnvExt::table_override_ != nullptr) {
+    LOG(WARNING) << "Enabling CheckJNI after a JNIEnv function table override is not functional.";
+  }
 }
 
 void JNIEnvExt::DumpReferenceTables(std::ostream& os) {
@@ -269,4 +277,33 @@
   }
 }
 
+static void ThreadResetFunctionTable(Thread* thread, void* arg ATTRIBUTE_UNUSED)
+    REQUIRES(Locks::jni_function_table_lock_) {
+  JNIEnvExt* env = thread->GetJniEnv();
+  bool check_jni = env->check_jni;
+  env->functions = JNIEnvExt::GetFunctionTable(check_jni);
+}
+
+void JNIEnvExt::SetTableOverride(const JNINativeInterface* table_override) {
+  MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+  MutexLock mu2(Thread::Current(), *Locks::jni_function_table_lock_);
+
+  JNIEnvExt::table_override_ = table_override;
+
+  // See if we have a runtime. Note: we cannot run other code (like JavaVMExt's CheckJNI install
+  // code), as we'd have to recursively lock the mutex.
+  Runtime* runtime = Runtime::Current();
+  if (runtime != nullptr) {
+    runtime->GetThreadList()->ForEach(ThreadResetFunctionTable, nullptr);
+  }
+}
+
+const JNINativeInterface* JNIEnvExt::GetFunctionTable(bool check_jni) {
+  const JNINativeInterface* override = JNIEnvExt::table_override_;
+  if (override != nullptr) {
+    return override;
+  }
+  return check_jni ? GetCheckJniNativeInterface() : GetJniNativeInterface();
+}
+
 }  // namespace art
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
index 5cca0ae..4004c45 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -43,7 +43,7 @@
   void DumpReferenceTables(std::ostream& os)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  void SetCheckJniEnabled(bool enabled);
+  void SetCheckJniEnabled(bool enabled) REQUIRES(!Locks::jni_function_table_lock_);
 
   void PushFrame(int capacity) REQUIRES_SHARED(Locks::mutator_lock_);
   void PopFrame() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -104,10 +104,27 @@
   // Set the functions to the runtime shutdown functions.
   void SetFunctionsToRuntimeShutdownFunctions();
 
+  // Set the function table override. This will install the override (or original table, if null)
+  // to all threads.
+  // Note: JNI function table overrides are sensitive to the order of operations wrt/ CheckJNI.
+  //       After overriding the JNI function table, CheckJNI toggling is ignored.
+  static void SetTableOverride(const JNINativeInterface* table_override)
+      REQUIRES(!Locks::thread_list_lock_, !Locks::jni_function_table_lock_);
+
+  // Return either the regular, or the CheckJNI function table. Will return table_override_ instead
+  // if it is not null.
+  static const JNINativeInterface* GetFunctionTable(bool check_jni)
+      REQUIRES(Locks::jni_function_table_lock_);
+
  private:
+  // Override of function tables. This applies to both default as well as instrumented (CheckJNI)
+  // function tables.
+  static const JNINativeInterface* table_override_ GUARDED_BY(Locks::jni_function_table_lock_);
+
   // The constructor should not be called directly. It may leave the object in an erroneous state,
   // and the result needs to be checked.
-  JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg);
+  JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg)
+      REQUIRES(!Locks::jni_function_table_lock_);
 
   // All locked objects, with the (Java caller) stack frame that locked them. Used in CheckJNI
   // to ensure that only monitors locked in this native frame are being unlocked, and that at
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 4da5e23..08d1eeb 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -2346,4 +2346,39 @@
   EXPECT_EQ(segment_state_now, segment_state_computed);
 }
 
+static size_t gGlobalRefCount = 0;
+static const JNINativeInterface* gOriginalEnv = nullptr;
+
+static jobject CountNewGlobalRef(JNIEnv* env, jobject o) {
+  ++gGlobalRefCount;
+  return gOriginalEnv->NewGlobalRef(env, o);
+}
+
+// Test the table override.
+TEST_F(JniInternalTest, JNIEnvExtTableOverride) {
+  JNINativeInterface env_override;
+  memcpy(&env_override, env_->functions, sizeof(JNINativeInterface));
+
+  gOriginalEnv = env_->functions;
+  env_override.NewGlobalRef = CountNewGlobalRef;
+  gGlobalRefCount = 0;
+
+  jclass local = env_->FindClass("java/lang/Object");
+  ASSERT_TRUE(local != nullptr);
+
+  // Set the table, add a global ref, see whether the counter increases.
+  JNIEnvExt::SetTableOverride(&env_override);
+
+  jobject global = env_->NewGlobalRef(local);
+  EXPECT_EQ(1u, gGlobalRefCount);
+  env_->DeleteGlobalRef(global);
+
+  // Reset
+  JNIEnvExt::SetTableOverride(nullptr);
+
+  jobject global2 = env_->NewGlobalRef(local);
+  EXPECT_EQ(1u, gGlobalRefCount);
+  env_->DeleteGlobalRef(global2);
+}
+
 }  // namespace art
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index a6f56ae..6a4ec9d 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -306,23 +306,6 @@
 }
 
 
-TEST_F(ObjectTest, CheckAndAllocArrayFromCode) {
-  // pretend we are trying to call 'new char[3]' from String.toCharArray
-  ScopedObjectAccess soa(Thread::Current());
-  Class* java_util_Arrays = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/Arrays;");
-  ArtMethod* sort = java_util_Arrays->FindDirectMethod("sort", "([I)V", kRuntimePointerSize);
-  const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId("[I");
-  ASSERT_TRUE(type_id != nullptr);
-  dex::TypeIndex type_idx = java_lang_dex_file_->GetIndexForTypeId(*type_id);
-  Object* array = CheckAndAllocArrayFromCodeInstrumented(
-      type_idx, 3, sort, Thread::Current(), false,
-      Runtime::Current()->GetHeap()->GetCurrentAllocator());
-  EXPECT_TRUE(array->IsArrayInstance());
-  EXPECT_EQ(3, array->AsArray()->GetLength());
-  EXPECT_TRUE(array->GetClass()->IsArrayClass());
-  EXPECT_TRUE(array->GetClass()->GetComponentType()->IsPrimitive());
-}
-
 TEST_F(ObjectTest, CreateMultiArray) {
   ScopedObjectAccess soa(Thread::Current());
 
diff --git a/runtime/oat.h b/runtime/oat.h
index ab03252..5a4bc1c 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  static constexpr uint8_t kOatVersion[] = { '0', '9', '8', '\0' };  // art::Thread fields reorder
+  static constexpr uint8_t kOatVersion[] = { '1', '0', '0', '\0' };  // AllocArrayResolved change
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index 42fed50..d5c6520 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -23,13 +23,16 @@
            "ti_class.cc",
            "ti_field.cc",
            "ti_heap.cc",
+           "ti_jni.cc",
            "ti_method.cc",
            "ti_monitor.cc",
            "ti_object.cc",
            "ti_properties.cc",
+           "ti_search.cc",
            "ti_stack.cc",
            "ti_redefine.cc",
            "ti_thread.cc",
+           "ti_threadgroup.cc",
            "ti_timers.cc",
            "transform.cc"],
     include_dirs: ["art/runtime"],
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 4aedec9..195f179 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -51,13 +51,16 @@
 #include "ti_class.h"
 #include "ti_field.h"
 #include "ti_heap.h"
+#include "ti_jni.h"
 #include "ti_method.h"
 #include "ti_monitor.h"
 #include "ti_object.h"
 #include "ti_properties.h"
 #include "ti_redefine.h"
+#include "ti_search.h"
 #include "ti_stack.h"
 #include "ti_thread.h"
+#include "ti_threadgroup.h"
 #include "ti_timers.h"
 #include "transform.h"
 
@@ -205,13 +208,13 @@
   static jvmtiError GetTopThreadGroups(jvmtiEnv* env,
                                        jint* group_count_ptr,
                                        jthreadGroup** groups_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ThreadGroupUtil::GetTopThreadGroups(env, group_count_ptr, groups_ptr);
   }
 
   static jvmtiError GetThreadGroupInfo(jvmtiEnv* env,
                                        jthreadGroup group,
                                        jvmtiThreadGroupInfo* info_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ThreadGroupUtil::GetThreadGroupInfo(env, group, info_ptr);
   }
 
   static jvmtiError GetThreadGroupChildren(jvmtiEnv* env,
@@ -220,7 +223,12 @@
                                            jthread** threads_ptr,
                                            jint* group_count_ptr,
                                            jthreadGroup** groups_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ThreadGroupUtil::GetThreadGroupChildren(env,
+                                                   group,
+                                                   thread_count_ptr,
+                                                   threads_ptr,
+                                                   group_count_ptr,
+                                                   groups_ptr);
   }
 
   static jvmtiError GetStackTrace(jvmtiEnv* env,
@@ -795,11 +803,11 @@
   }
 
   static jvmtiError SetJNIFunctionTable(jvmtiEnv* env, const jniNativeInterface* function_table) {
-    return ERR(NOT_IMPLEMENTED);
+    return JNIUtil::SetJNIFunctionTable(env, function_table);
   }
 
   static jvmtiError GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table) {
-    return ERR(NOT_IMPLEMENTED);
+    return JNIUtil::GetJNIFunctionTable(env, function_table);
   }
 
   // TODO: This will require locking, so that an agent can't remove callbacks when we're dispatching
@@ -850,7 +858,8 @@
       }
     }
 
-    return gEventHandler.SetEvent(ArtJvmTiEnv::AsArtJvmTiEnv(env), art_thread, event_type, mode);
+    ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env);
+    return gEventHandler.SetEvent(art_env, art_thread, GetArtJvmtiEvent(art_env, event_type), mode);
   }
 
   static jvmtiError GenerateEvents(jvmtiEnv* env, jvmtiEvent event_type) {
@@ -1046,11 +1055,11 @@
   }
 
   static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment) {
-    return ERR(NOT_IMPLEMENTED);
+    return SearchUtil::AddToBootstrapClassLoaderSearch(env, segment);
   }
 
   static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment) {
-    return ERR(NOT_IMPLEMENTED);
+    return SearchUtil::AddToSystemClassLoaderSearch(env, segment);
   }
 
   static jvmtiError GetSystemProperties(jvmtiEnv* env, jint* count_ptr, char*** property_ptr) {
@@ -1225,7 +1234,12 @@
   }
 
   static jvmtiError GetJLocationFormat(jvmtiEnv* env, jvmtiJlocationFormat* format_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    // Report BCI as jlocation format. We report dex bytecode indices.
+    if (format_ptr == nullptr) {
+      return ERR(NULL_POINTER);
+    }
+    *format_ptr = jvmtiJlocationFormat::JVMTI_JLOCATION_JVMBCI;
+    return ERR(NONE);
   }
 
   // TODO Remove this once events are working.
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index d027201..fb39db5 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -23,8 +23,13 @@
 
 namespace openjdkjvmti {
 
+static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env ATTRIBUTE_UNUSED,
+                                                           jvmtiEvent e) {
+  return static_cast<ArtJvmtiEvent>(e);
+}
+
 template <typename FnType>
-ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, jvmtiEvent event) {
+ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, ArtJvmtiEvent event) {
   if (env->event_callbacks == nullptr) {
     return nullptr;
   }
@@ -33,84 +38,79 @@
   //       function.
 
   switch (event) {
-    case JVMTI_EVENT_VM_INIT:
+    case ArtJvmtiEvent::kVmInit:
       return reinterpret_cast<FnType*>(env->event_callbacks->VMInit);
-    case JVMTI_EVENT_VM_DEATH:
+    case ArtJvmtiEvent::kVmDeath:
       return reinterpret_cast<FnType*>(env->event_callbacks->VMDeath);
-    case JVMTI_EVENT_THREAD_START:
+    case ArtJvmtiEvent::kThreadStart:
       return reinterpret_cast<FnType*>(env->event_callbacks->ThreadStart);
-    case JVMTI_EVENT_THREAD_END:
+    case ArtJvmtiEvent::kThreadEnd:
       return reinterpret_cast<FnType*>(env->event_callbacks->ThreadEnd);
-    case JVMTI_EVENT_CLASS_FILE_LOAD_HOOK:
+    case ArtJvmtiEvent::kClassFileLoadHook:
       return reinterpret_cast<FnType*>(env->event_callbacks->ClassFileLoadHook);
-    case JVMTI_EVENT_CLASS_LOAD:
+    case ArtJvmtiEvent::kClassLoad:
       return reinterpret_cast<FnType*>(env->event_callbacks->ClassLoad);
-    case JVMTI_EVENT_CLASS_PREPARE:
+    case ArtJvmtiEvent::kClassPrepare:
       return reinterpret_cast<FnType*>(env->event_callbacks->ClassPrepare);
-    case JVMTI_EVENT_VM_START:
+    case ArtJvmtiEvent::kVmStart:
       return reinterpret_cast<FnType*>(env->event_callbacks->VMStart);
-    case JVMTI_EVENT_EXCEPTION:
+    case ArtJvmtiEvent::kException:
       return reinterpret_cast<FnType*>(env->event_callbacks->Exception);
-    case JVMTI_EVENT_EXCEPTION_CATCH:
+    case ArtJvmtiEvent::kExceptionCatch:
       return reinterpret_cast<FnType*>(env->event_callbacks->ExceptionCatch);
-    case JVMTI_EVENT_SINGLE_STEP:
+    case ArtJvmtiEvent::kSingleStep:
       return reinterpret_cast<FnType*>(env->event_callbacks->SingleStep);
-    case JVMTI_EVENT_FRAME_POP:
+    case ArtJvmtiEvent::kFramePop:
       return reinterpret_cast<FnType*>(env->event_callbacks->FramePop);
-    case JVMTI_EVENT_BREAKPOINT:
+    case ArtJvmtiEvent::kBreakpoint:
       return reinterpret_cast<FnType*>(env->event_callbacks->Breakpoint);
-    case JVMTI_EVENT_FIELD_ACCESS:
+    case ArtJvmtiEvent::kFieldAccess:
       return reinterpret_cast<FnType*>(env->event_callbacks->FieldAccess);
-    case JVMTI_EVENT_FIELD_MODIFICATION:
+    case ArtJvmtiEvent::kFieldModification:
       return reinterpret_cast<FnType*>(env->event_callbacks->FieldModification);
-    case JVMTI_EVENT_METHOD_ENTRY:
+    case ArtJvmtiEvent::kMethodEntry:
       return reinterpret_cast<FnType*>(env->event_callbacks->MethodEntry);
-    case JVMTI_EVENT_METHOD_EXIT:
+    case ArtJvmtiEvent::kMethodExit:
       return reinterpret_cast<FnType*>(env->event_callbacks->MethodExit);
-    case JVMTI_EVENT_NATIVE_METHOD_BIND:
+    case ArtJvmtiEvent::kNativeMethodBind:
       return reinterpret_cast<FnType*>(env->event_callbacks->NativeMethodBind);
-    case JVMTI_EVENT_COMPILED_METHOD_LOAD:
+    case ArtJvmtiEvent::kCompiledMethodLoad:
       return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodLoad);
-    case JVMTI_EVENT_COMPILED_METHOD_UNLOAD:
+    case ArtJvmtiEvent::kCompiledMethodUnload:
       return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodUnload);
-    case JVMTI_EVENT_DYNAMIC_CODE_GENERATED:
+    case ArtJvmtiEvent::kDynamicCodeGenerated:
       return reinterpret_cast<FnType*>(env->event_callbacks->DynamicCodeGenerated);
-    case JVMTI_EVENT_DATA_DUMP_REQUEST:
+    case ArtJvmtiEvent::kDataDumpRequest:
       return reinterpret_cast<FnType*>(env->event_callbacks->DataDumpRequest);
-    case JVMTI_EVENT_MONITOR_WAIT:
+    case ArtJvmtiEvent::kMonitorWait:
       return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWait);
-    case JVMTI_EVENT_MONITOR_WAITED:
+    case ArtJvmtiEvent::kMonitorWaited:
       return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWaited);
-    case JVMTI_EVENT_MONITOR_CONTENDED_ENTER:
+    case ArtJvmtiEvent::kMonitorContendedEnter:
       return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEnter);
-    case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED:
+    case ArtJvmtiEvent::kMonitorContendedEntered:
       return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEntered);
-    case JVMTI_EVENT_RESOURCE_EXHAUSTED:
+    case ArtJvmtiEvent::kResourceExhausted:
       return reinterpret_cast<FnType*>(env->event_callbacks->ResourceExhausted);
-    case JVMTI_EVENT_GARBAGE_COLLECTION_START:
+    case ArtJvmtiEvent::kGarbageCollectionStart:
       return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionStart);
-    case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
+    case ArtJvmtiEvent::kGarbageCollectionFinish:
       return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionFinish);
-    case JVMTI_EVENT_OBJECT_FREE:
+    case ArtJvmtiEvent::kObjectFree:
       return reinterpret_cast<FnType*>(env->event_callbacks->ObjectFree);
-    case JVMTI_EVENT_VM_OBJECT_ALLOC:
+    case ArtJvmtiEvent::kVmObjectAlloc:
       return reinterpret_cast<FnType*>(env->event_callbacks->VMObjectAlloc);
   }
   return nullptr;
 }
 
 template <typename ...Args>
-inline void EventHandler::DispatchEvent(art::Thread* thread, jvmtiEvent event, Args... args) {
+inline void EventHandler::DispatchEvent(art::Thread* thread,
+                                        ArtJvmtiEvent event,
+                                        Args... args) const {
   using FnType = void(jvmtiEnv*, Args...);
   for (ArtJvmTiEnv* env : envs) {
-    bool dispatch = env->event_masks.global_event_mask.Test(event);
-
-    if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) {
-      EventMask* mask = env->event_masks.GetEventMaskOrNull(thread);
-      dispatch = mask != nullptr && mask->Test(event);
-    }
-
-    if (dispatch) {
+    if (ShouldDispatch(event, env, thread)) {
       FnType* callback = GetCallback<FnType>(env, event);
       if (callback != nullptr) {
         (*callback)(env, args...);
@@ -119,6 +119,18 @@
   }
 }
 
+inline bool EventHandler::ShouldDispatch(ArtJvmtiEvent event,
+                                         ArtJvmTiEnv* env,
+                                         art::Thread* thread) {
+  bool dispatch = env->event_masks.global_event_mask.Test(event);
+
+  if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) {
+    EventMask* mask = env->event_masks.GetEventMaskOrNull(thread);
+    dispatch = mask != nullptr && mask->Test(event);
+  }
+  return dispatch;
+}
+
 }  // namespace openjdkjvmti
 
 #endif  // ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 12692a1..66929cf 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -83,7 +83,7 @@
 }
 
 
-void EventMasks::EnableEvent(art::Thread* thread, jvmtiEvent event) {
+void EventMasks::EnableEvent(art::Thread* thread, ArtJvmtiEvent event) {
   DCHECK(EventMask::EventIsInRange(event));
   GetEventMask(thread).Set(event);
   if (thread != nullptr) {
@@ -91,7 +91,7 @@
   }
 }
 
-void EventMasks::DisableEvent(art::Thread* thread, jvmtiEvent event) {
+void EventMasks::DisableEvent(art::Thread* thread, ArtJvmtiEvent event) {
   DCHECK(EventMask::EventIsInRange(event));
   GetEventMask(thread).Set(event, false);
   if (thread != nullptr) {
@@ -111,16 +111,16 @@
   envs.push_back(env);
 }
 
-static bool IsThreadControllable(jvmtiEvent event) {
+static bool IsThreadControllable(ArtJvmtiEvent event) {
   switch (event) {
-    case JVMTI_EVENT_VM_INIT:
-    case JVMTI_EVENT_VM_START:
-    case JVMTI_EVENT_VM_DEATH:
-    case JVMTI_EVENT_THREAD_START:
-    case JVMTI_EVENT_COMPILED_METHOD_LOAD:
-    case JVMTI_EVENT_COMPILED_METHOD_UNLOAD:
-    case JVMTI_EVENT_DYNAMIC_CODE_GENERATED:
-    case JVMTI_EVENT_DATA_DUMP_REQUEST:
+    case ArtJvmtiEvent::kVmInit:
+    case ArtJvmtiEvent::kVmStart:
+    case ArtJvmtiEvent::kVmDeath:
+    case ArtJvmtiEvent::kThreadStart:
+    case ArtJvmtiEvent::kCompiledMethodLoad:
+    case ArtJvmtiEvent::kCompiledMethodUnload:
+    case ArtJvmtiEvent::kDynamicCodeGenerated:
+    case ArtJvmtiEvent::kDataDumpRequest:
       return false;
 
     default:
@@ -136,7 +136,7 @@
       OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
     DCHECK_EQ(self, art::Thread::Current());
 
-    if (handler_->IsEventEnabledAnywhere(JVMTI_EVENT_VM_OBJECT_ALLOC)) {
+    if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kVmObjectAlloc)) {
       art::StackHandleScope<1> hs(self);
       auto h = hs.NewHandleWrapper(obj);
       // jvmtiEventVMObjectAlloc parameters:
@@ -162,7 +162,7 @@
           jni_env, jni_env->AddLocalReference<jclass>(obj->Ptr()->GetClass()));
 
       handler_->DispatchEvent(self,
-                              JVMTI_EVENT_VM_OBJECT_ALLOC,
+                              ArtJvmtiEvent::kVmObjectAlloc,
                               jni_env,
                               thread.get(),
                               object.get(),
@@ -196,11 +196,11 @@
         finish_enabled_(false) {}
 
   void StartPause() OVERRIDE {
-    handler_->DispatchEvent(nullptr, JVMTI_EVENT_GARBAGE_COLLECTION_START);
+    handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionStart);
   }
 
   void EndPause() OVERRIDE {
-    handler_->DispatchEvent(nullptr, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH);
+    handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionFinish);
   }
 
   bool IsEnabled() {
@@ -221,10 +221,10 @@
   bool finish_enabled_;
 };
 
-static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, jvmtiEvent event, bool enable) {
+static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, ArtJvmtiEvent event, bool enable) {
   bool old_state = listener->IsEnabled();
 
-  if (event == JVMTI_EVENT_GARBAGE_COLLECTION_START) {
+  if (event == ArtJvmtiEvent::kGarbageCollectionStart) {
     listener->SetStartEnabled(enable);
   } else {
     listener->SetFinishEnabled(enable);
@@ -242,14 +242,14 @@
 }
 
 // Handle special work for the given event type, if necessary.
-void EventHandler::HandleEventType(jvmtiEvent event, bool enable) {
+void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) {
   switch (event) {
-    case JVMTI_EVENT_VM_OBJECT_ALLOC:
+    case ArtJvmtiEvent::kVmObjectAlloc:
       SetupObjectAllocationTracking(alloc_listener_.get(), enable);
       return;
 
-    case JVMTI_EVENT_GARBAGE_COLLECTION_START:
-    case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
+    case ArtJvmtiEvent::kGarbageCollectionStart:
+    case ArtJvmtiEvent::kGarbageCollectionFinish:
       SetupGcPauseTracking(gc_pause_listener_.get(), event, enable);
       return;
 
@@ -260,7 +260,7 @@
 
 jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env,
                                   art::Thread* thread,
-                                  jvmtiEvent event,
+                                  ArtJvmtiEvent event,
                                   jvmtiEventMode mode) {
   if (thread != nullptr) {
     art::ThreadState state = thread->GetState();
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 07d6bfd..8f56145 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -30,22 +30,70 @@
 class JvmtiAllocationListener;
 class JvmtiGcPauseListener;
 
+// an enum for ArtEvents.
+enum class ArtJvmtiEvent {
+    kMinEventTypeVal = JVMTI_MIN_EVENT_TYPE_VAL,
+    kVmInit = JVMTI_EVENT_VM_INIT,
+    kVmDeath = JVMTI_EVENT_VM_DEATH,
+    kThreadStart = JVMTI_EVENT_THREAD_START,
+    kThreadEnd = JVMTI_EVENT_THREAD_END,
+    kClassFileLoadHook = JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
+    kClassLoad = JVMTI_EVENT_CLASS_LOAD,
+    kClassPrepare = JVMTI_EVENT_CLASS_PREPARE,
+    kVmStart = JVMTI_EVENT_VM_START,
+    kException = JVMTI_EVENT_EXCEPTION,
+    kExceptionCatch = JVMTI_EVENT_EXCEPTION_CATCH,
+    kSingleStep = JVMTI_EVENT_SINGLE_STEP,
+    kFramePop = JVMTI_EVENT_FRAME_POP,
+    kBreakpoint = JVMTI_EVENT_BREAKPOINT,
+    kFieldAccess = JVMTI_EVENT_FIELD_ACCESS,
+    kFieldModification = JVMTI_EVENT_FIELD_MODIFICATION,
+    kMethodEntry = JVMTI_EVENT_METHOD_ENTRY,
+    kMethodExit = JVMTI_EVENT_METHOD_EXIT,
+    kNativeMethodBind = JVMTI_EVENT_NATIVE_METHOD_BIND,
+    kCompiledMethodLoad = JVMTI_EVENT_COMPILED_METHOD_LOAD,
+    kCompiledMethodUnload = JVMTI_EVENT_COMPILED_METHOD_UNLOAD,
+    kDynamicCodeGenerated = JVMTI_EVENT_DYNAMIC_CODE_GENERATED,
+    kDataDumpRequest = JVMTI_EVENT_DATA_DUMP_REQUEST,
+    kMonitorWait = JVMTI_EVENT_MONITOR_WAIT,
+    kMonitorWaited = JVMTI_EVENT_MONITOR_WAITED,
+    kMonitorContendedEnter = JVMTI_EVENT_MONITOR_CONTENDED_ENTER,
+    kMonitorContendedEntered = JVMTI_EVENT_MONITOR_CONTENDED_ENTERED,
+    kResourceExhausted = JVMTI_EVENT_RESOURCE_EXHAUSTED,
+    kGarbageCollectionStart = JVMTI_EVENT_GARBAGE_COLLECTION_START,
+    kGarbageCollectionFinish = JVMTI_EVENT_GARBAGE_COLLECTION_FINISH,
+    kObjectFree = JVMTI_EVENT_OBJECT_FREE,
+    kVmObjectAlloc = JVMTI_EVENT_VM_OBJECT_ALLOC,
+    kMaxEventTypeVal = JVMTI_MAX_EVENT_TYPE_VAL,
+};
+
+// Convert a jvmtiEvent into a ArtJvmtiEvent
+ALWAYS_INLINE static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e);
+
+ALWAYS_INLINE static inline jvmtiEvent GetJvmtiEvent(ArtJvmtiEvent e) {
+  return static_cast<jvmtiEvent>(e);
+}
+
 struct EventMask {
-  static constexpr size_t kEventsSize = JVMTI_MAX_EVENT_TYPE_VAL - JVMTI_MIN_EVENT_TYPE_VAL + 1;
+  static constexpr size_t kEventsSize =
+      static_cast<size_t>(ArtJvmtiEvent::kMaxEventTypeVal) -
+      static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal) + 1;
   std::bitset<kEventsSize> bit_set;
 
-  static bool EventIsInRange(jvmtiEvent event) {
-    return event >= JVMTI_MIN_EVENT_TYPE_VAL && event <= JVMTI_MAX_EVENT_TYPE_VAL;
+  static bool EventIsInRange(ArtJvmtiEvent event) {
+    return event >= ArtJvmtiEvent::kMinEventTypeVal && event <= ArtJvmtiEvent::kMaxEventTypeVal;
   }
 
-  void Set(jvmtiEvent event, bool value = true) {
+  void Set(ArtJvmtiEvent event, bool value = true) {
     DCHECK(EventIsInRange(event));
-    bit_set.set(event - JVMTI_MIN_EVENT_TYPE_VAL, value);
+    bit_set.set(static_cast<size_t>(event) - static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal),
+                value);
   }
 
-  bool Test(jvmtiEvent event) const {
+  bool Test(ArtJvmtiEvent event) const {
     DCHECK(EventIsInRange(event));
-    return bit_set.test(event - JVMTI_MIN_EVENT_TYPE_VAL);
+    return bit_set.test(
+        static_cast<size_t>(event) - static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal));
   }
 };
 
@@ -68,8 +116,8 @@
 
   EventMask& GetEventMask(art::Thread* thread);
   EventMask* GetEventMaskOrNull(art::Thread* thread);
-  void EnableEvent(art::Thread* thread, jvmtiEvent event);
-  void DisableEvent(art::Thread* thread, jvmtiEvent event);
+  void EnableEvent(art::Thread* thread, ArtJvmtiEvent event);
+  void DisableEvent(art::Thread* thread, ArtJvmtiEvent event);
 };
 
 // Helper class for event handling.
@@ -82,20 +130,27 @@
   // enabled, yet.
   void RegisterArtJvmTiEnv(ArtJvmTiEnv* env);
 
-  bool IsEventEnabledAnywhere(jvmtiEvent event) {
+  bool IsEventEnabledAnywhere(ArtJvmtiEvent event) const {
     if (!EventMask::EventIsInRange(event)) {
       return false;
     }
     return global_mask.Test(event);
   }
 
-  jvmtiError SetEvent(ArtJvmTiEnv* env, art::Thread* thread, jvmtiEvent event, jvmtiEventMode mode);
+  jvmtiError SetEvent(ArtJvmTiEnv* env,
+                      art::Thread* thread,
+                      ArtJvmtiEvent event,
+                      jvmtiEventMode mode);
 
   template <typename ...Args>
-  ALWAYS_INLINE inline void DispatchEvent(art::Thread* thread, jvmtiEvent event, Args... args);
+  ALWAYS_INLINE
+  inline void DispatchEvent(art::Thread* thread, ArtJvmtiEvent event, Args... args) const;
 
  private:
-  void HandleEventType(jvmtiEvent event, bool enable);
+  ALWAYS_INLINE
+  static inline bool ShouldDispatch(ArtJvmtiEvent event, ArtJvmTiEnv* env, art::Thread* thread);
+
+  void HandleEventType(ArtJvmtiEvent event, bool enable);
 
   // List of all JvmTiEnv objects that have been created, in their creation order.
   std::vector<ArtJvmTiEnv*> envs;
diff --git a/runtime/openjdkjvmti/jvmti.h b/runtime/openjdkjvmti/jvmti.h
index ee708cb..de07c16 100644
--- a/runtime/openjdkjvmti/jvmti.h
+++ b/runtime/openjdkjvmti/jvmti.h
@@ -74,7 +74,7 @@
 typedef jlong jlocation;
 struct _jrawMonitorID;
 typedef struct _jrawMonitorID *jrawMonitorID;
-typedef struct JNINativeInterface_ jniNativeInterface;
+typedef struct JNINativeInterface jniNativeInterface;
 
     /* Constants */
 
diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc
index b983e79..94cb46a 100644
--- a/runtime/openjdkjvmti/object_tagging.cc
+++ b/runtime/openjdkjvmti/object_tagging.cc
@@ -177,7 +177,7 @@
 }
 
 void ObjectTagTable::Sweep(art::IsMarkedVisitor* visitor) {
-  if (event_handler_->IsEventEnabledAnywhere(JVMTI_EVENT_OBJECT_FREE)) {
+  if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kObjectFree)) {
     SweepImpl<true>(visitor);
   } else {
     SweepImpl<false>(visitor);
@@ -207,7 +207,7 @@
 }
 
 void ObjectTagTable::HandleNullSweep(jlong tag) {
-  event_handler_->DispatchEvent(nullptr, JVMTI_EVENT_OBJECT_FREE, tag);
+  event_handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kObjectFree, tag);
 }
 
 template <typename T, ObjectTagTable::TableUpdateNullTarget kTargetNull>
diff --git a/runtime/openjdkjvmti/ti_jni.cc b/runtime/openjdkjvmti/ti_jni.cc
new file mode 100644
index 0000000..88f0395
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_jni.cc
@@ -0,0 +1,91 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_jni.h"
+
+#include "jni.h"
+
+#include "art_jvmti.h"
+#include "base/mutex.h"
+#include "java_vm_ext.h"
+#include "jni_env_ext.h"
+#include "runtime.h"
+#include "thread-inl.h"
+
+namespace openjdkjvmti {
+
+jvmtiError JNIUtil::SetJNIFunctionTable(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                        const jniNativeInterface* function_table) {
+  // While we supporting setting null (which will reset the table), the spec says no.
+  if (function_table == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::JNIEnvExt::SetTableOverride(function_table);
+  return ERR(NONE);
+}
+
+jvmtiError JNIUtil::GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table) {
+  if (function_table == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  // We use the generic JNIEnvExt::GetFunctionTable instead of querying a specific JNIEnv, as
+  // this has to work in the start phase.
+
+  // Figure out which table is current. Conservatively assume check-jni is off.
+  bool check_jni = false;
+  art::Runtime* runtime = art::Runtime::Current();
+  if (runtime != nullptr && runtime->GetJavaVM() != nullptr) {
+    check_jni = runtime->GetJavaVM()->IsCheckJniEnabled();
+  }
+
+  // Get that table.
+  const JNINativeInterface* current_table;
+  {
+    art::MutexLock mu(art::Thread::Current(), *art::Locks::jni_function_table_lock_);
+    current_table = art::JNIEnvExt::GetFunctionTable(check_jni);
+  }
+
+  // Allocate memory and copy the table.
+  unsigned char* data;
+  jvmtiError data_result = env->Allocate(sizeof(JNINativeInterface), &data);
+  if (data_result != ERR(NONE)) {
+    return data_result;
+  }
+  memcpy(data, current_table, sizeof(JNINativeInterface));
+
+  *function_table = reinterpret_cast<JNINativeInterface*>(data);
+
+  return ERR(NONE);
+}
+
+}  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_jni.h b/runtime/openjdkjvmti/ti_jni.h
new file mode 100644
index 0000000..906aab0
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_jni.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+// Note: Currently, JNI function table changes are sensitive to the order of operations wrt/
+//       CheckJNI. If an agent sets the function table, and a program than late-enables CheckJNI,
+//       CheckJNI will not be working (as the agent will forward to the non-CheckJNI table).
+//
+//       This behavior results from our usage of the function table to avoid a check of the
+//       CheckJNI flag. A future implementation may install on loading of this plugin an
+//       intermediate function table that explicitly checks the flag, so that switching CheckJNI
+//       is transparently handled.
+
+class JNIUtil {
+ public:
+  static jvmtiError SetJNIFunctionTable(jvmtiEnv* env, const jniNativeInterface* function_table);
+
+  static jvmtiError GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table);
+};
+
+}  // namespace openjdkjvmti
+
+#endif  // ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_
diff --git a/runtime/openjdkjvmti/ti_search.cc b/runtime/openjdkjvmti/ti_search.cc
new file mode 100644
index 0000000..913d2b6
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_search.cc
@@ -0,0 +1,122 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_search.h"
+
+#include "jni.h"
+
+#include "art_jvmti.h"
+#include "base/macros.h"
+#include "class_linker.h"
+#include "dex_file.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "ScopedLocalRef.h"
+
+namespace openjdkjvmti {
+
+jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                                       const char* segment) {
+  art::Runtime* current = art::Runtime::Current();
+  if (current == nullptr) {
+    return ERR(WRONG_PHASE);
+  }
+  if (current->GetClassLinker() == nullptr) {
+    // TODO: Support boot classpath change in OnLoad.
+    return ERR(WRONG_PHASE);
+  }
+  if (segment == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  std::string error_msg;
+  std::vector<std::unique_ptr<const art::DexFile>> dex_files;
+  if (!art::DexFile::Open(segment, segment, true, &error_msg, &dex_files)) {
+    LOG(WARNING) << "Could not open " << segment << " for boot classpath extension: " << error_msg;
+    return ERR(ILLEGAL_ARGUMENT);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  for (std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
+    current->GetClassLinker()->AppendToBootClassPath(art::Thread::Current(), *dex_file.release());
+  }
+
+  return ERR(NONE);
+}
+
+jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED,
+                                                    const char* segment) {
+  if (segment == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::Runtime* current = art::Runtime::Current();
+  if (current == nullptr) {
+    return ERR(WRONG_PHASE);
+  }
+  jobject sys_class_loader = current->GetSystemClassLoader();
+  if (sys_class_loader == nullptr) {
+    // TODO: Support classpath change in OnLoad.
+    return ERR(WRONG_PHASE);
+  }
+
+  // We'll use BaseDexClassLoader.addDexPath, as it takes care of array resizing etc. As a downside,
+  // exceptions are swallowed.
+
+  art::Thread* self = art::Thread::Current();
+  JNIEnv* env = self->GetJniEnv();
+  if (!env->IsInstanceOf(sys_class_loader,
+                         art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
+    return ERR(INTERNAL);
+  }
+
+  jmethodID add_dex_path_id = env->GetMethodID(
+      art::WellKnownClasses::dalvik_system_BaseDexClassLoader,
+      "addDexPath",
+      "(Ljava/lang/String;)V");
+  if (add_dex_path_id == nullptr) {
+    return ERR(INTERNAL);
+  }
+
+  ScopedLocalRef<jstring> dex_path(env, env->NewStringUTF(segment));
+  if (dex_path.get() == nullptr) {
+    return ERR(INTERNAL);
+  }
+  env->CallVoidMethod(sys_class_loader, add_dex_path_id, dex_path.get());
+
+  if (env->ExceptionCheck()) {
+    env->ExceptionClear();
+    return ERR(ILLEGAL_ARGUMENT);
+  }
+  return ERR(NONE);
+}
+
+}  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_search.h b/runtime/openjdkjvmti/ti_search.h
new file mode 100644
index 0000000..6a52e80
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_search.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
+
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class SearchUtil {
+ public:
+  static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment);
+
+  static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment);
+};
+
+}  // namespace openjdkjvmti
+
+#endif  // ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
diff --git a/runtime/openjdkjvmti/ti_threadgroup.cc b/runtime/openjdkjvmti/ti_threadgroup.cc
new file mode 100644
index 0000000..35b1bfd
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_threadgroup.cc
@@ -0,0 +1,285 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_threadgroup.h"
+
+#include "art_field.h"
+#include "art_jvmti.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "handle_scope-inl.h"
+#include "jni_internal.h"
+#include "mirror/class.h"
+#include "mirror/object-inl.h"
+#include "mirror/string.h"
+#include "obj_ptr.h"
+#include "object_lock.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+#include "thread_list.h"
+#include "well_known_classes.h"
+
+namespace openjdkjvmti {
+
+
+jvmtiError ThreadGroupUtil::GetTopThreadGroups(jvmtiEnv* env,
+                                               jint* group_count_ptr,
+                                               jthreadGroup** groups_ptr) {
+  // We only have a single top group. So we can take the current thread and move upwards.
+  if (group_count_ptr == nullptr || groups_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::Runtime* runtime = art::Runtime::Current();
+  if (runtime == nullptr) {
+    // Must be starting the runtime, or dying.
+    return ERR(WRONG_PHASE);
+  }
+
+  jobject sys_thread_group = runtime->GetSystemThreadGroup();
+  if (sys_thread_group == nullptr) {
+    // Seems we're still starting up.
+    return ERR(WRONG_PHASE);
+  }
+
+  unsigned char* data;
+  jvmtiError result = env->Allocate(sizeof(jthreadGroup), &data);
+  if (result != ERR(NONE)) {
+    return result;
+  }
+
+  jthreadGroup* groups = reinterpret_cast<jthreadGroup*>(data);
+  *groups =
+      reinterpret_cast<JNIEnv*>(art::Thread::Current()->GetJniEnv())->NewLocalRef(sys_thread_group);
+  *groups_ptr = groups;
+  *group_count_ptr = 1;
+
+  return ERR(NONE);
+}
+
+jvmtiError ThreadGroupUtil::GetThreadGroupInfo(jvmtiEnv* env,
+                                               jthreadGroup group,
+                                               jvmtiThreadGroupInfo* info_ptr) {
+  if (group == nullptr) {
+    return ERR(INVALID_THREAD_GROUP);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  if (soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup) == JNI_FALSE) {
+    return ERR(INVALID_THREAD_GROUP);
+  }
+
+  art::ObjPtr<art::mirror::Object> obj = soa.Decode<art::mirror::Object>(group);
+
+  // Do the name first. It's the only thing that can fail.
+  {
+    art::ArtField* name_field =
+        art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_name);
+    CHECK(name_field != nullptr);
+    art::ObjPtr<art::mirror::String> name_obj =
+        art::ObjPtr<art::mirror::String>::DownCast(name_field->GetObject(obj));
+    std::string tmp_str;
+    const char* tmp_cstr;
+    if (name_obj == nullptr) {
+      tmp_cstr = "";
+    } else {
+      tmp_str = name_obj->ToModifiedUtf8();
+      tmp_cstr = tmp_str.c_str();
+    }
+    jvmtiError result =
+        CopyString(env, tmp_cstr, reinterpret_cast<unsigned char**>(&info_ptr->name));
+    if (result != ERR(NONE)) {
+      return result;
+    }
+  }
+
+  // Parent.
+  {
+    art::ArtField* parent_field =
+        art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_parent);
+    CHECK(parent_field != nullptr);
+    art::ObjPtr<art::mirror::Object> parent_group = parent_field->GetObject(obj);
+    info_ptr->parent = parent_group == nullptr
+                           ? nullptr
+                           : soa.AddLocalReference<jthreadGroup>(parent_group);
+  }
+
+  // Max priority.
+  {
+    art::ArtField* prio_field = obj->GetClass()->FindDeclaredInstanceField("maxPriority", "I");
+    CHECK(prio_field != nullptr);
+    info_ptr->max_priority = static_cast<jint>(prio_field->GetInt(obj));
+  }
+
+  // Daemon.
+  {
+    art::ArtField* daemon_field = obj->GetClass()->FindDeclaredInstanceField("daemon", "Z");
+    CHECK(daemon_field != nullptr);
+    info_ptr->is_daemon = daemon_field->GetBoolean(obj) == 0 ? JNI_FALSE : JNI_TRUE;
+  }
+
+  return ERR(NONE);
+}
+
+
+static bool IsInDesiredThreadGroup(art::Handle<art::mirror::Object> desired_thread_group,
+                                   art::ObjPtr<art::mirror::Object> peer)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  CHECK(desired_thread_group.Get() != nullptr);
+
+  art::ArtField* thread_group_field =
+      art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group);
+  DCHECK(thread_group_field != nullptr);
+  art::ObjPtr<art::mirror::Object> group = thread_group_field->GetObject(peer);
+  return (group == desired_thread_group.Get());
+}
+
+static void GetThreads(art::Handle<art::mirror::Object> thread_group,
+                       std::vector<art::ObjPtr<art::mirror::Object>>* thread_peers)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!art::Locks::thread_list_lock_) {
+  CHECK(thread_group.Get() != nullptr);
+
+  art::MutexLock mu(art::Thread::Current(), *art::Locks::thread_list_lock_);
+  for (art::Thread* t : art::Runtime::Current()->GetThreadList()->GetList()) {
+    if (t->IsStillStarting()) {
+      continue;
+    }
+    art::ObjPtr<art::mirror::Object> peer = t->GetPeer();
+    if (peer == nullptr) {
+      continue;
+    }
+    if (IsInDesiredThreadGroup(thread_group, peer)) {
+      thread_peers->push_back(peer);
+    }
+  }
+}
+
+static void GetChildThreadGroups(art::Handle<art::mirror::Object> thread_group,
+                                 std::vector<art::ObjPtr<art::mirror::Object>>* thread_groups)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  CHECK(thread_group.Get() != nullptr);
+
+  // Get the ThreadGroup[] "groups" out of this thread group...
+  art::ArtField* groups_field =
+      art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_groups);
+  art::ObjPtr<art::mirror::Object> groups_array = groups_field->GetObject(thread_group.Get());
+
+  if (groups_array == nullptr) {
+    return;
+  }
+  CHECK(groups_array->IsObjectArray());
+
+  art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> groups_array_as_array =
+      groups_array->AsObjectArray<art::mirror::Object>();
+
+  // Copy all non-null elements.
+  for (int32_t i = 0; i < groups_array_as_array->GetLength(); ++i) {
+    art::ObjPtr<art::mirror::Object> entry = groups_array_as_array->Get(i);
+    if (entry != nullptr) {
+      thread_groups->push_back(entry);
+    }
+  }
+}
+
+jvmtiError ThreadGroupUtil::GetThreadGroupChildren(jvmtiEnv* env,
+                                                   jthreadGroup group,
+                                                   jint* thread_count_ptr,
+                                                   jthread** threads_ptr,
+                                                   jint* group_count_ptr,
+                                                   jthreadGroup** groups_ptr) {
+  if (group == nullptr) {
+    return ERR(INVALID_THREAD_GROUP);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+
+  if (!soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup)) {
+    return ERR(INVALID_THREAD_GROUP);
+  }
+
+  art::StackHandleScope<1> hs(soa.Self());
+  art::Handle<art::mirror::Object> thread_group = hs.NewHandle(
+      soa.Decode<art::mirror::Object>(group));
+
+  art::ObjectLock<art::mirror::Object> thread_group_lock(soa.Self(), thread_group);
+
+  std::vector<art::ObjPtr<art::mirror::Object>> thread_peers;
+  GetThreads(thread_group, &thread_peers);
+
+  std::vector<art::ObjPtr<art::mirror::Object>> thread_groups;
+  GetChildThreadGroups(thread_group, &thread_groups);
+
+  jthread* thread_data = nullptr;
+  JvmtiUniquePtr peers_uptr;
+  if (!thread_peers.empty()) {
+    unsigned char* data;
+    jvmtiError res = env->Allocate(sizeof(jthread) * thread_peers.size(), &data);
+    if (res != ERR(NONE)) {
+      return res;
+    }
+    thread_data = reinterpret_cast<jthread*>(data);
+    peers_uptr = MakeJvmtiUniquePtr(env, data);
+  }
+
+  jthreadGroup* group_data = nullptr;
+  if (!thread_groups.empty()) {
+    unsigned char* data;
+    jvmtiError res = env->Allocate(sizeof(jthreadGroup) * thread_groups.size(), &data);
+    if (res != ERR(NONE)) {
+      return res;
+    }
+    group_data = reinterpret_cast<jthreadGroup*>(data);
+  }
+
+  // Can't fail anymore from here on.
+
+  // Copy data into out buffers.
+  for (size_t i = 0; i != thread_peers.size(); ++i) {
+    thread_data[i] = soa.AddLocalReference<jthread>(thread_peers[i]);
+  }
+  for (size_t i = 0; i != thread_groups.size(); ++i) {
+    group_data[i] = soa.AddLocalReference<jthreadGroup>(thread_groups[i]);
+  }
+
+  *thread_count_ptr = static_cast<jint>(thread_peers.size());
+  *threads_ptr = thread_data;
+  *group_count_ptr = static_cast<jint>(thread_groups.size());
+  *groups_ptr = group_data;
+
+  // Everything's fine.
+  peers_uptr.release();
+
+  return ERR(NONE);
+}
+
+}  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_threadgroup.h b/runtime/openjdkjvmti/ti_threadgroup.h
new file mode 100644
index 0000000..c3a0ff5
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_threadgroup.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class ThreadGroupUtil {
+ public:
+  static jvmtiError GetTopThreadGroups(jvmtiEnv* env,
+                                       jint* group_count_ptr,
+                                       jthreadGroup** groups_ptr);
+
+  static jvmtiError GetThreadGroupInfo(jvmtiEnv* env,
+                                       jthreadGroup group,
+                                       jvmtiThreadGroupInfo* info_ptr);
+
+  static jvmtiError GetThreadGroupChildren(jvmtiEnv* env,
+                                           jthreadGroup group,
+                                           jint* thread_count_ptr,
+                                           jthread** threads_ptr,
+                                           jint* group_count_ptr,
+                                           jthreadGroup** groups_ptr);
+};
+
+}  // namespace openjdkjvmti
+
+#endif  // ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 4d24501..75176f9 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -227,11 +227,11 @@
     for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {
       ObjPtr<mirror::Object> arg(args->Get(args_offset));
       if (((shorty_[i] == 'L') && (arg != nullptr)) || ((arg == nullptr && shorty_[i] != 'L'))) {
-        PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+        // Note: The method's parameter's type must have been previously resolved.
         ObjPtr<mirror::Class> dst_class(
             m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_,
-                                     true /* resolve */,
-                                     pointer_size));
+                                     false /* resolve */));
+        DCHECK(dst_class != nullptr) << m->PrettyMethod() << " arg #" << i;
         if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) {
           ThrowIllegalArgumentException(
               StringPrintf("method %s argument %zd has type %s, got %s",
@@ -363,12 +363,9 @@
   }
   // TODO: If args contain object references, it may cause problems.
   Thread* const self = Thread::Current();
-  PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
   for (uint32_t i = 0; i < num_params; i++) {
     dex::TypeIndex type_idx = params->GetTypeItem(i).type_idx_;
-    ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx,
-                                                              true /* resolve*/,
-                                                              pointer_size));
+    ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx, true /* resolve */));
     if (param_type == nullptr) {
       CHECK(self->IsExceptionPending());
       LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: "
diff --git a/runtime/thread.cc b/runtime/thread.cc
index bf69e10..8c8afdc 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2641,8 +2641,6 @@
   QUICK_ENTRY_POINT_INFO(pAllocObjectResolved)
   QUICK_ENTRY_POINT_INFO(pAllocObjectInitialized)
   QUICK_ENTRY_POINT_INFO(pAllocObjectWithChecks)
-  QUICK_ENTRY_POINT_INFO(pCheckAndAllocArray)
-  QUICK_ENTRY_POINT_INFO(pCheckAndAllocArrayWithAccessCheck)
   QUICK_ENTRY_POINT_INFO(pAllocStringFromBytes)
   QUICK_ENTRY_POINT_INFO(pAllocStringFromChars)
   QUICK_ENTRY_POINT_INFO(pAllocStringFromString)
@@ -2676,10 +2674,7 @@
   QUICK_ENTRY_POINT_INFO(pGet64Static)
   QUICK_ENTRY_POINT_INFO(pGetObjInstance)
   QUICK_ENTRY_POINT_INFO(pGetObjStatic)
-  QUICK_ENTRY_POINT_INFO(pAputObjectWithNullAndBoundCheck)
-  QUICK_ENTRY_POINT_INFO(pAputObjectWithBoundCheck)
   QUICK_ENTRY_POINT_INFO(pAputObject)
-  QUICK_ENTRY_POINT_INFO(pHandleFillArrayData)
   QUICK_ENTRY_POINT_INFO(pJniMethodStart)
   QUICK_ENTRY_POINT_INFO(pJniMethodStartSynchronized)
   QUICK_ENTRY_POINT_INFO(pJniMethodEnd)
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 25a179b..b915457 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2901,9 +2901,7 @@
       ArtMethod* called_method = VerifyInvocationArgs(inst, type, is_range);
       const RegType* return_type = nullptr;
       if (called_method != nullptr) {
-        PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-        mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_,
-                                                                        pointer_size);
+        mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
         if (return_type_class != nullptr) {
           return_type = &FromClass(called_method->GetReturnTypeDescriptor(),
                                    return_type_class,
@@ -2946,9 +2944,7 @@
       } else {
         is_constructor = called_method->IsConstructor();
         return_type_descriptor = called_method->GetReturnTypeDescriptor();
-        PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-        mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_,
-                                                                        pointer_size);
+        mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
         if (return_type_class != nullptr) {
           return_type = &FromClass(return_type_descriptor,
                                    return_type_class,
@@ -5133,9 +5129,7 @@
 const RegType& MethodVerifier::GetMethodReturnType() {
   if (return_type_ == nullptr) {
     if (mirror_method_ != nullptr) {
-      PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-      mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_,
-                                                                       pointer_size);
+      mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_);
       if (return_type_class != nullptr) {
         return_type_ = &FromClass(mirror_method_->GetReturnTypeDescriptor(),
                                   return_type_class,
diff --git a/test/129-ThreadGetId/src/Main.java b/test/129-ThreadGetId/src/Main.java
index 6ba01ff..4e48e0e 100644
--- a/test/129-ThreadGetId/src/Main.java
+++ b/test/129-ThreadGetId/src/Main.java
@@ -23,7 +23,6 @@
 
     public static void main(String[] args) throws Exception {
         final Thread[] threads = new Thread[numberOfThreads];
-        test_getStackTraces();
         for (int t = 0; t < threads.length; t++) {
             threads[t] = new Thread(new Main());
             threads[t].start();
@@ -31,6 +30,9 @@
         for (Thread t : threads) {
             t.join();
         }
+        // Do this test after the other part to leave some time for the heap task daemon to start
+        // up.
+        test_getStackTraces();
         System.out.println("Finishing");
     }
 
diff --git a/test/559-checker-rtp-ifnotnull/src/Main.java b/test/559-checker-rtp-ifnotnull/src/Main.java
index 2dc5666..1e15654 100644
--- a/test/559-checker-rtp-ifnotnull/src/Main.java
+++ b/test/559-checker-rtp-ifnotnull/src/Main.java
@@ -18,7 +18,6 @@
 public class Main {
 
   /// CHECK-START: void Main.boundTypeForIfNotNull() builder (after)
-  /// CHECK-DAG:     <<Method:(i|j)\d+>>  CurrentMethod
   /// CHECK-DAG:     <<Null:l\d+>>        NullConstant
   /// CHECK-DAG:     <<Cst5:i\d+>>        IntConstant 5
   /// CHECK-DAG:     <<Cst10:i\d+>>       IntConstant 10
@@ -28,10 +27,12 @@
   /// CHECK-DAG:     <<LoopPhi>>          Phi [<<Null>>,<<MergePhi:l\d+>>] klass:int[]
 
   /// CHECK-DAG:     <<BoundType:l\d+>>   BoundType [<<LoopPhi>>] klass:int[] can_be_null:false
-  /// CHECK-DAG:     <<NewArray10:l\d+>>  NewArray [<<Cst10>>,<<Method>>] klass:int[]
+  /// CHECK-DAG:     <<LoadClass1:l\d+>>  LoadClass
+  /// CHECK-DAG:     <<LoadClass2:l\d+>>  LoadClass
+  /// CHECK-DAG:     <<NewArray10:l\d+>>  NewArray [<<LoadClass2>>,<<Cst10>>] klass:int[]
   /// CHECK-DAG:     <<NotNullPhi:l\d+>>  Phi [<<BoundType>>,<<NewArray10>>] klass:int[]
 
-  /// CHECK-DAG:     <<NewArray5:l\d+>>   NewArray [<<Cst5>>,<<Method>>] klass:int[]
+  /// CHECK-DAG:     <<NewArray5:l\d+>>   NewArray [<<LoadClass1>>,<<Cst5>>] klass:int[]
   /// CHECK-DAG:     <<MergePhi>>         Phi [<<NewArray5>>,<<NotNullPhi>>] klass:int[]
 
   public static void boundTypeForIfNotNull() {
diff --git a/test/572-checker-array-get-regression/src/Main.java b/test/572-checker-array-get-regression/src/Main.java
index 89b97ed..03a8448 100644
--- a/test/572-checker-array-get-regression/src/Main.java
+++ b/test/572-checker-array-get-regression/src/Main.java
@@ -21,10 +21,10 @@
   }
 
   /// CHECK-START: java.lang.Integer Main.test() builder (after)
-  /// CHECK-DAG:     <<Method:[ij]\d+>>    CurrentMethod
   /// CHECK-DAG:     <<Const2P19:i\d+>>    IntConstant 524288
   /// CHECK-DAG:     <<ConstM1:i\d+>>      IntConstant -1
-  /// CHECK-DAG:     <<Array:l\d+>>        NewArray [<<Const2P19>>,<<Method>>]
+  /// CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass
+  /// CHECK-DAG:     <<Array:l\d+>>        NewArray [<<LoadClass>>,<<Const2P19>>]
   /// CHECK-DAG:     <<Length1:i\d+>>      ArrayLength [<<Array>>]
   /// CHECK-DAG:     <<Index:i\d+>>        Add [<<Length1>>,<<ConstM1>>]
   /// CHECK-DAG:     <<Length2:i\d+>>      ArrayLength [<<Array>>]
@@ -34,10 +34,10 @@
 
 
   /// CHECK-START: java.lang.Integer Main.test() register (before)
-  /// CHECK-DAG:     <<Method:[ij]\d+>>    CurrentMethod
   /// CHECK-DAG:     <<Const2P19:i\d+>>    IntConstant 524288
   /// CHECK-DAG:     <<Const2P19M1:i\d+>>  IntConstant 524287
-  /// CHECK-DAG:     <<Array:l\d+>>        NewArray [<<Const2P19>>,<<Method>>]
+  /// CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass
+  /// CHECK-DAG:     <<Array:l\d+>>        NewArray [<<LoadClass>>,<<Const2P19>>]
   /// CHECK-DAG:     <<LastElement:l\d+>>  ArrayGet [<<Array>>,<<Const2P19M1>>]
   /// CHECK-DAG:                           Return [<<LastElement>>]
 
diff --git a/test/925-threadgroups/build b/test/925-threadgroups/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/925-threadgroups/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-build "$@" --experimental agents
diff --git a/test/925-threadgroups/expected.txt b/test/925-threadgroups/expected.txt
new file mode 100644
index 0000000..7d1a259
--- /dev/null
+++ b/test/925-threadgroups/expected.txt
@@ -0,0 +1,16 @@
+java.lang.ThreadGroup[name=main,maxpri=10]
+  java.lang.ThreadGroup[name=system,maxpri=10]
+  main
+  10
+  false
+java.lang.ThreadGroup[name=system,maxpri=10]
+  null
+  system
+  10
+  false
+main:
+  [Thread[main,5,main]]
+  []
+system:
+  [Thread[FinalizerDaemon,5,system], Thread[FinalizerWatchdogDaemon,5,system], Thread[HeapTaskDaemon,5,system], Thread[ReferenceQueueDaemon,5,system], Thread[Signal Catcher,5,system]]
+  [java.lang.ThreadGroup[name=main,maxpri=10]]
diff --git a/test/925-threadgroups/info.txt b/test/925-threadgroups/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/925-threadgroups/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/925-threadgroups/run b/test/925-threadgroups/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/925-threadgroups/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --jvmti
diff --git a/test/925-threadgroups/src/Main.java b/test/925-threadgroups/src/Main.java
new file mode 100644
index 0000000..c59efe2
--- /dev/null
+++ b/test/925-threadgroups/src/Main.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[1]);
+
+    doTest();
+  }
+
+  private static void doTest() throws Exception {
+    Thread t1 = Thread.currentThread();
+    ThreadGroup curGroup = t1.getThreadGroup();
+
+    ThreadGroup rootGroup = curGroup;
+    while (rootGroup.getParent() != null) {
+      rootGroup = rootGroup.getParent();
+    }
+
+    ThreadGroup topGroups[] = getTopThreadGroups();
+    if (topGroups == null || topGroups.length != 1 || topGroups[0] != rootGroup) {
+      System.out.println(Arrays.toString(topGroups));
+      throw new RuntimeException("Unexpected topGroups");
+    }
+
+    printThreadGroupInfo(curGroup);
+    printThreadGroupInfo(rootGroup);
+
+    waitGroupChildren(rootGroup, 5 /* # daemons */, 30 /* timeout in seconds */);
+
+    checkChildren(curGroup);
+  }
+
+  private static void printThreadGroupInfo(ThreadGroup tg) {
+    Object[] threadGroupInfo = getThreadGroupInfo(tg);
+    if (threadGroupInfo == null || threadGroupInfo.length != 4) {
+      System.out.println(Arrays.toString(threadGroupInfo));
+      throw new RuntimeException("threadGroupInfo length wrong");
+    }
+
+    System.out.println(tg);
+    System.out.println("  " + threadGroupInfo[0]);  // Parent
+    System.out.println("  " + threadGroupInfo[1]);  // Name
+    System.out.println("  " + threadGroupInfo[2]);  // Priority
+    System.out.println("  " + threadGroupInfo[3]);  // Daemon
+  }
+
+  private static void checkChildren(ThreadGroup tg) {
+    Object[] data = getThreadGroupChildren(tg);
+    Thread[] threads = (Thread[])data[0];
+    ThreadGroup[] groups = (ThreadGroup[])data[1];
+
+    Arrays.sort(threads, THREAD_COMP);
+    Arrays.sort(groups, THREADGROUP_COMP);
+    System.out.println(tg.getName() + ":");
+    System.out.println("  " + Arrays.toString(threads));
+    System.out.println("  " + Arrays.toString(groups));
+
+    if (tg.getParent() != null) {
+      checkChildren(tg.getParent());
+    }
+  }
+
+  private static void waitGroupChildren(ThreadGroup tg, int expectedChildCount, int timeoutS)
+      throws Exception {
+    for (int i = 0; i <  timeoutS; i++) {
+      Object[] data = getThreadGroupChildren(tg);
+      Thread[] threads = (Thread[])data[0];
+      if (threads.length == expectedChildCount) {
+        return;
+      }
+      Thread.sleep(1000);
+    }
+
+    Object[] data = getThreadGroupChildren(tg);
+    Thread[] threads = (Thread[])data[0];
+    System.out.println(Arrays.toString(threads));
+    throw new RuntimeException("Waited unsuccessfully for " + expectedChildCount + " children.");
+  }
+
+  private final static Comparator<Thread> THREAD_COMP = new Comparator<Thread>() {
+    public int compare(Thread o1, Thread o2) {
+      return o1.getName().compareTo(o2.getName());
+    }
+  };
+
+  private final static Comparator<ThreadGroup> THREADGROUP_COMP = new Comparator<ThreadGroup>() {
+    public int compare(ThreadGroup o1, ThreadGroup o2) {
+      return o1.getName().compareTo(o2.getName());
+    }
+  };
+
+  private static native ThreadGroup[] getTopThreadGroups();
+  private static native Object[] getThreadGroupInfo(ThreadGroup tg);
+  // Returns an array where element 0 is an array of threads and element 1 is an array of groups.
+  private static native Object[] getThreadGroupChildren(ThreadGroup tg);
+}
diff --git a/test/925-threadgroups/threadgroups.cc b/test/925-threadgroups/threadgroups.cc
new file mode 100644
index 0000000..6c6e835
--- /dev/null
+++ b/test/925-threadgroups/threadgroups.cc
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <stdio.h>
+
+#include "android-base/stringprintf.h"
+#include "base/macros.h"
+#include "base/logging.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedLocalRef.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test925ThreadGroups {
+
+//   private static native Object[] getThreadGroupInfo();
+//   // Returns an array where element 0 is an array of threads and element 1 is an array of groups.
+//   private static native Object[] getThreadGroupChildren();
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getTopThreadGroups(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
+  jthreadGroup* groups;
+  jint group_count;
+  jvmtiError result = jvmti_env->GetTopThreadGroups(&group_count, &groups);
+  if (JvmtiErrorToException(env, result)) {
+    return nullptr;
+  }
+
+  auto callback = [&](jint index) -> jobject {
+    return groups[index];
+  };
+  jobjectArray ret = CreateObjectArray(env, group_count, "java/lang/ThreadGroup", callback);
+
+  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(groups));
+
+  return ret;
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getThreadGroupInfo(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthreadGroup group) {
+  jvmtiThreadGroupInfo info;
+  jvmtiError result = jvmti_env->GetThreadGroupInfo(group, &info);
+  if (JvmtiErrorToException(env, result)) {
+    return nullptr;
+  }
+
+  auto callback = [&](jint index) -> jobject {
+    switch (index) {
+      // The parent.
+      case 0:
+        return info.parent;
+
+      // The name.
+      case 1:
+        return (info.name == nullptr) ? nullptr : env->NewStringUTF(info.name);
+
+      // The priority. Use a string for simplicity of construction.
+      case 2:
+        return env->NewStringUTF(android::base::StringPrintf("%d", info.max_priority).c_str());
+
+      // Whether it's a daemon. Use a string for simplicity of construction.
+      case 3:
+        return env->NewStringUTF(info.is_daemon == JNI_TRUE ? "true" : "false");
+    }
+    LOG(FATAL) << "Should not reach here";
+    UNREACHABLE();
+  };
+  return CreateObjectArray(env, 4, "java/lang/Object", callback);
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getThreadGroupChildren(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthreadGroup group) {
+  jint thread_count;
+  jthread* threads;
+  jint threadgroup_count;
+  jthreadGroup* groups;
+
+  jvmtiError result = jvmti_env->GetThreadGroupChildren(group,
+                                                        &thread_count,
+                                                        &threads,
+                                                        &threadgroup_count,
+                                                        &groups);
+  if (JvmtiErrorToException(env, result)) {
+    return nullptr;
+  }
+
+  auto callback = [&](jint component_index) -> jobject {
+    if (component_index == 0) {
+      // Threads.
+      auto inner_callback = [&](jint index) {
+        return threads[index];
+      };
+      return CreateObjectArray(env, thread_count, "java/lang/Thread", inner_callback);
+    } else {
+      // Groups.
+      auto inner_callback = [&](jint index) {
+        return groups[index];
+      };
+      return CreateObjectArray(env, threadgroup_count, "java/lang/ThreadGroup", inner_callback);
+    }
+  };
+  jobjectArray ret = CreateObjectArray(env, 2, "java/lang/Object", callback);
+
+  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(threads));
+  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(groups));
+
+  return ret;
+}
+
+}  // namespace Test925ThreadGroups
+}  // namespace art
diff --git a/test/928-jni-table/build b/test/928-jni-table/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/928-jni-table/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-build "$@" --experimental agents
diff --git a/test/928-jni-table/expected.txt b/test/928-jni-table/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/test/928-jni-table/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/928-jni-table/info.txt b/test/928-jni-table/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/928-jni-table/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/928-jni-table/jni_table.cc b/test/928-jni-table/jni_table.cc
new file mode 100644
index 0000000..5123d3a
--- /dev/null
+++ b/test/928-jni-table/jni_table.cc
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include <stdio.h>
+
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test927JNITable {
+
+// This test is equivalent to the jni_internal_test JNIEnvExtTableOverride.
+
+static size_t gGlobalRefCount = 0;
+static JNINativeInterface* gOriginalEnv = nullptr;
+
+static jobject CountNewGlobalRef(JNIEnv* env, jobject o) {
+  ++gGlobalRefCount;
+  return gOriginalEnv->NewGlobalRef(env, o);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_doJNITableTest(
+    JNIEnv* env, jclass klass) {
+  // Get the current table, as the delegate.
+  jvmtiError getorig_result = jvmti_env->GetJNIFunctionTable(&gOriginalEnv);
+  if (JvmtiErrorToException(env, getorig_result)) {
+    return;
+  }
+
+  // Get the current table, as the override we'll install.
+  JNINativeInterface* env_override;
+  jvmtiError getoverride_result = jvmti_env->GetJNIFunctionTable(&env_override);
+  if (JvmtiErrorToException(env, getoverride_result)) {
+    return;
+  }
+
+  env_override->NewGlobalRef = CountNewGlobalRef;
+  gGlobalRefCount = 0;
+
+  // Install the override.
+  jvmtiError setoverride_result = jvmti_env->SetJNIFunctionTable(env_override);
+  if (JvmtiErrorToException(env, setoverride_result)) {
+    return;
+  }
+
+  jobject global = env->NewGlobalRef(klass);
+  CHECK_EQ(1u, gGlobalRefCount);
+  env->DeleteGlobalRef(global);
+
+  // Install the "original." There is no real reset.
+  jvmtiError setoverride2_result = jvmti_env->SetJNIFunctionTable(gOriginalEnv);
+  if (JvmtiErrorToException(env, setoverride2_result)) {
+    return;
+  }
+
+  jobject global2 = env->NewGlobalRef(klass);
+  CHECK_EQ(1u, gGlobalRefCount);
+  env->DeleteGlobalRef(global2);
+
+  // Try to install null. Should return NULL_POINTER error.
+  jvmtiError setoverride3_result = jvmti_env->SetJNIFunctionTable(nullptr);
+  if (setoverride3_result != JVMTI_ERROR_NULL_POINTER) {
+    LOG(FATAL) << "Didn't receive NULL_POINTER";
+  }
+
+  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(env_override));
+}
+
+}  // namespace Test927JNITable
+}  // namespace art
diff --git a/test/928-jni-table/run b/test/928-jni-table/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/928-jni-table/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --jvmti
diff --git a/test/928-jni-table/src/Main.java b/test/928-jni-table/src/Main.java
new file mode 100644
index 0000000..b0baea1
--- /dev/null
+++ b/test/928-jni-table/src/Main.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 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) throws Exception {
+    System.loadLibrary(args[1]);
+
+    doJNITableTest();
+
+    System.out.println("Done");
+  }
+
+  public static native void doJNITableTest();
+}
diff --git a/test/929-search/build b/test/929-search/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/929-search/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-build "$@" --experimental agents
diff --git a/test/929-search/expected.txt b/test/929-search/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/test/929-search/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/929-search/info.txt b/test/929-search/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/929-search/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/929-search/run b/test/929-search/run
new file mode 100755
index 0000000..0a8d067
--- /dev/null
+++ b/test/929-search/run
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+# This test checks whether dex files can be injected into parent classloaders. App images preload
+# classes, which will make the injection moot. Turn off app images to avoid the issue.
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --jvmti \
+                   --no-app-image
diff --git a/test/929-search/search.cc b/test/929-search/search.cc
new file mode 100644
index 0000000..d1c6984
--- /dev/null
+++ b/test/929-search/search.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <inttypes.h>
+
+#include "android-base/stringprintf.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedUtfChars.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test929Search {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_addToBootClassLoader(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring segment) {
+  ScopedUtfChars utf(env, segment);
+  if (utf.c_str() == nullptr) {
+    return;
+  }
+  jvmtiError result = jvmti_env->AddToBootstrapClassLoaderSearch(utf.c_str());
+  JvmtiErrorToException(env, result);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_addToSystemClassLoader(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring segment) {
+  ScopedUtfChars utf(env, segment);
+  if (utf.c_str() == nullptr) {
+    return;
+  }
+  jvmtiError result = jvmti_env->AddToSystemClassLoaderSearch(utf.c_str());
+  JvmtiErrorToException(env, result);
+}
+
+}  // namespace Test929Search
+}  // namespace art
diff --git a/test/929-search/src-ex/A.java b/test/929-search/src-ex/A.java
new file mode 100644
index 0000000..64acb2f
--- /dev/null
+++ b/test/929-search/src-ex/A.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2017 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 A {
+}
\ No newline at end of file
diff --git a/test/929-search/src/B.java b/test/929-search/src/B.java
new file mode 100644
index 0000000..f1458c3
--- /dev/null
+++ b/test/929-search/src/B.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2017 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 B {
+}
\ No newline at end of file
diff --git a/test/929-search/src/Main.java b/test/929-search/src/Main.java
new file mode 100644
index 0000000..d253e6f
--- /dev/null
+++ b/test/929-search/src/Main.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+import java.util.Arrays;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[1]);
+
+    doTest();
+  }
+
+  private static void doTest() throws Exception {
+    doTest(true, DEX1, "B");
+    doTest(false, DEX2, "A");
+    System.out.println("Done");
+  }
+
+  private static void doTest(boolean boot, String segment, String className) throws Exception {
+    ClassLoader expectedClassLoader;
+    if (boot) {
+      expectedClassLoader = Object.class.getClassLoader();
+      addToBootClassLoader(segment);
+    } else {
+      expectedClassLoader = ClassLoader.getSystemClassLoader();
+      addToSystemClassLoader(segment);
+    }
+
+    Class<?> c = Class.forName(className);
+    if (c.getClassLoader() != expectedClassLoader) {
+      throw new RuntimeException(className + "(" + boot + "/" + segment + "): " +
+          c.getClassLoader() + " vs " + expectedClassLoader);
+    }
+  }
+
+  private static native void addToBootClassLoader(String s);
+  private static native void addToSystemClassLoader(String s);
+
+  private static final String DEX1 = System.getenv("DEX_LOCATION") + "/929-search.jar";
+  private static final String DEX2 = System.getenv("DEX_LOCATION") + "/929-search-ex.jar";
+}
diff --git a/test/Android.bp b/test/Android.bp
index 1ea1252..965d07a 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -265,7 +265,10 @@
         "922-properties/properties.cc",
         "923-monitors/monitors.cc",
         "924-threads/threads.cc",
+        "925-threadgroups/threadgroups.cc",
         "927-timers/timers.cc",
+        "928-jni-table/jni_table.cc",
+        "929-search/search.cc",
     ],
     shared_libs: [
         "libbase",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 55cef97..e604c93 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -280,6 +280,7 @@
 # These 9** tests are not supported in current form due to linker
 # restrictions. See b/31681198
 TEST_ART_BROKEN_TARGET_TESTS += \
+  901-hello-ti-agent \
   902-hello-transformation \
   903-hello-tagging \
   904-object-allocation \
@@ -303,8 +304,11 @@
   922-properties \
   923-monitors \
   924-threads \
+  925-threadgroups \
   926-multi-obsolescence \
   927-timers \
+  928-jni-table \
+  929-search \
 
 ifneq (,$(filter target,$(TARGET_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -465,10 +469,12 @@
   629-vdex-speed
 
 # This test fails without an image.
-# 964 often times out due to the large number of classes it tries to compile.
+# 018, 961, 964 often time out. b/34369284
 TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \
   137-cfi \
   138-duplicate-classes-check \
+  018-stack-overflow \
+  961-default-iface-resolution-gen \
   964-default-iface-init
 
 ifneq (,$(filter no-dex2oat,$(PREBUILD_TYPES)))
diff --git a/tools/cpp-define-generator/offset_dexcache.def b/tools/cpp-define-generator/offset_dexcache.def
index abb5e1e..43f9434 100644
--- a/tools/cpp-define-generator/offset_dexcache.def
+++ b/tools/cpp-define-generator/offset_dexcache.def
@@ -34,7 +34,6 @@
 
 //                         New macro suffix          Method Name (of the Offset method)
 DEFINE_ART_METHOD_OFFSET_SIZED(DEX_CACHE_METHODS,    DexCacheResolvedMethods)
-DEFINE_ART_METHOD_OFFSET_SIZED(DEX_CACHE_TYPES,      DexCacheResolvedTypes)
 DEFINE_ART_METHOD_OFFSET_SIZED(JNI,                  EntryPointFromJni)
 DEFINE_ART_METHOD_OFFSET_SIZED(QUICK_CODE,           EntryPointFromQuickCompiledCode)
 DEFINE_ART_METHOD_OFFSET(DECLARING_CLASS,            DeclaringClass)