Merge "Make --debuggable rely on JIT code."
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index 291db8b..b7a2379 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -87,8 +87,8 @@
 # Do you want tests with the JNI forcecopy mode enabled run?
 ART_TEST_JNI_FORCECOPY ?= $(ART_TEST_FULL)
 
-# Do you want run-tests with relocation disabled run?
-ART_TEST_RUN_TEST_NO_RELOCATE ?= $(ART_TEST_FULL)
+# Do you want run-tests with relocation enabled run?
+ART_TEST_RUN_TEST_RELOCATE ?= $(ART_TEST_FULL)
 
 # Do you want run-tests with prebuilding?
 ART_TEST_RUN_TEST_PREBUILD ?= true
@@ -96,6 +96,9 @@
 # Do you want run-tests with no prebuilding enabled run?
 ART_TEST_RUN_TEST_NO_PREBUILD ?= $(ART_TEST_FULL)
 
+# Do you want run-tests with a pregenerated core.art?
+ART_TEST_RUN_TEST_IMAGE ?= true
+
 # Do you want run-tests without a pregenerated core.art?
 ART_TEST_RUN_TEST_NO_IMAGE ?= $(ART_TEST_FULL)
 
diff --git a/compiler/compiler.h b/compiler/compiler.h
index 2ca0b77..908d366 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -27,6 +27,7 @@
   class JitCodeCache;
 }
 namespace mirror {
+  class ClassLoader;
   class DexCache;
 }
 
@@ -63,7 +64,7 @@
                                   InvokeType invoke_type,
                                   uint16_t class_def_idx,
                                   uint32_t method_idx,
-                                  jobject class_loader,
+                                  Handle<mirror::ClassLoader> class_loader,
                                   const DexFile& dex_file,
                                   Handle<mirror::DexCache> dex_cache) const = 0;
 
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index d4f6545..76aeaa5 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -284,16 +284,13 @@
   }
   uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
   ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
-      soa.Decode<mirror::ClassLoader>(unit_.GetClassLoader())));
 
   ClassLinker* class_linker = unit_.GetClassLinker();
   ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::kForceICCECheck>(
       GetDexFile(),
       method_idx,
       unit_.GetDexCache(),
-      class_loader,
+      unit_.GetClassLoader(),
       /* referrer */ nullptr,
       kVirtual);
 
@@ -330,7 +327,7 @@
     InvokeType invoke_type ATTRIBUTE_UNUSED,
     uint16_t class_def_idx,
     uint32_t method_idx,
-    jobject class_loader,
+    Handle<mirror::ClassLoader> class_loader,
     const DexFile& dex_file,
     DexToDexCompilationLevel dex_to_dex_compilation_level) {
   DCHECK(driver != nullptr);
diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h
index 0a00d45..00c596d 100644
--- a/compiler/dex/dex_to_dex_compiler.h
+++ b/compiler/dex/dex_to_dex_compiler.h
@@ -18,6 +18,7 @@
 #define ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_
 
 #include "dex_file.h"
+#include "handle.h"
 #include "invoke_type.h"
 
 namespace art {
@@ -25,6 +26,10 @@
 class CompiledMethod;
 class CompilerDriver;
 
+namespace mirror {
+class ClassLoader;
+}  // namespace mirror
+
 namespace optimizer {
 
 enum class DexToDexCompilationLevel {
@@ -40,7 +45,7 @@
                               InvokeType invoke_type,
                               uint16_t class_def_idx,
                               uint32_t method_idx,
-                              jobject class_loader,
+                              Handle<mirror::ClassLoader> class_loader,
                               const DexFile& dex_file,
                               DexToDexCompilationLevel dex_to_dex_compilation_level);
 
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index f056dd3..81d80f4 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -31,17 +31,12 @@
 
 namespace art {
 
-inline mirror::ClassLoader* CompilerDriver::GetClassLoader(const ScopedObjectAccess& soa,
-                                                           const DexCompilationUnit* mUnit) {
-  return soa.Decode<mirror::ClassLoader>(mUnit->GetClassLoader()).Ptr();
-}
-
 inline mirror::Class* CompilerDriver::ResolveClass(
     const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader, dex::TypeIndex cls_index,
     const DexCompilationUnit* mUnit) {
   DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile());
-  DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
+  DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
   mirror::Class* cls = mUnit->GetClassLinker()->ResolveType(
       *mUnit->GetDexFile(), cls_index, dex_cache, class_loader);
   DCHECK_EQ(cls == nullptr, soa.Self()->IsExceptionPending());
@@ -56,7 +51,7 @@
     const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) {
   DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile());
-  DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
+  DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
   const DexFile::MethodId& referrer_method_id =
       mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex());
   return ResolveClass(soa, dex_cache, class_loader, referrer_method_id.class_idx_, mUnit);
@@ -87,7 +82,7 @@
     const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
     uint32_t field_idx, bool is_static) {
-  DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
+  DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
   return ResolveFieldWithDexFile(soa, dex_cache, class_loader, mUnit->GetDexFile(), field_idx,
                                  is_static);
 }
@@ -198,7 +193,7 @@
     ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
     uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change) {
-  DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
+  DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
   ArtMethod* resolved_method =
       check_incompatible_class_change
           ? mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kForceICCECheck>(
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 1d4eaf8..4e19dbe 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -583,7 +583,7 @@
                           InvokeType invoke_type,
                           uint16_t class_def_idx,
                           uint32_t method_idx,
-                          jobject class_loader,
+                          Handle<mirror::ClassLoader> class_loader,
                           const DexFile& dex_file,
                           optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level,
                           bool compilation_enabled,
@@ -624,9 +624,6 @@
       // Look-up the ArtMethod associated with this code_item (if any)
       // -- It is later used to lookup any [optimization] annotations for this method.
       ScopedObjectAccess soa(self);
-      StackHandleScope<1> hs(soa.Self());
-      Handle<mirror::ClassLoader> class_loader_handle(hs.NewHandle(
-          soa.Decode<mirror::ClassLoader>(class_loader)));
 
       // TODO: Lookup annotation from DexFile directly without resolving method.
       ArtMethod* method =
@@ -634,7 +631,7 @@
               dex_file,
               method_idx,
               dex_cache,
-              class_loader_handle,
+              class_loader,
               /* referrer */ nullptr,
               invoke_type);
 
@@ -681,9 +678,14 @@
 
     if (compile) {
       // NOTE: if compiler declines to compile this method, it will return null.
-      compiled_method = driver->GetCompiler()->Compile(code_item, access_flags, invoke_type,
-                                                       class_def_idx, method_idx, class_loader,
-                                                       dex_file, dex_cache);
+      compiled_method = driver->GetCompiler()->Compile(code_item,
+                                                       access_flags,
+                                                       invoke_type,
+                                                       class_def_idx,
+                                                       method_idx,
+                                                       class_loader,
+                                                       dex_file,
+                                                       dex_cache);
     }
     if (compiled_method == nullptr &&
         dex_to_dex_compilation_level != optimizer::DexToDexCompilationLevel::kDontDexToDexCompile) {
@@ -730,12 +732,14 @@
   uint32_t method_idx = method->GetDexMethodIndex();
   uint32_t access_flags = method->GetAccessFlags();
   InvokeType invoke_type = method->GetInvokeType();
-  StackHandleScope<1> hs(self);
+  StackHandleScope<2> hs(self);
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(method->GetDeclaringClass()->GetClassLoader()));
   {
     ScopedObjectAccessUnchecked soa(self);
     ScopedLocalRef<jobject> local_class_loader(
-        soa.Env(), soa.AddLocalReference<jobject>(method->GetDeclaringClass()->GetClassLoader()));
+        soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get()));
     jclass_loader = soa.Env()->NewGlobalRef(local_class_loader.get());
     // Find the dex_file
     dex_file = method->GetDexFile();
@@ -769,7 +773,7 @@
                 invoke_type,
                 class_def_idx,
                 method_idx,
-                jclass_loader,
+                class_loader,
                 *dex_file,
                 dex_to_dex_compilation_level,
                 true,
@@ -795,7 +799,7 @@
                   invoke_type,
                   class_def_idx,
                   method_idx,
-                  jclass_loader,
+                  class_loader,
                   *dex_file,
                   dex_to_dex_compilation_level,
                   true,
@@ -1070,22 +1074,30 @@
 
 class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor {
  public:
-  explicit ResolveCatchBlockExceptionsClassVisitor(
-      std::set<std::pair<dex::TypeIndex, const DexFile*>>& exceptions_to_resolve)
-     : exceptions_to_resolve_(exceptions_to_resolve) {}
+  ResolveCatchBlockExceptionsClassVisitor() : classes_() {}
 
   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);
-    }
+    classes_.push_back(c);
     return true;
   }
 
- private:
-  void ResolveExceptionsForMethod(ArtMethod* method_handle)
+  void FindExceptionTypesToResolve(
+      std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    const DexFile::CodeItem* code_item = method_handle->GetCodeItem();
+    const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+    for (ObjPtr<mirror::Class> klass : classes_) {
+      for (ArtMethod& method : klass->GetMethods(pointer_size)) {
+        FindExceptionTypesToResolveForMethod(&method, exceptions_to_resolve);
+      }
+    }
+  }
+
+ private:
+  void FindExceptionTypesToResolveForMethod(
+      ArtMethod* method,
+      std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    const DexFile::CodeItem* code_item = method->GetCodeItem();
     if (code_item == nullptr) {
       return;  // native or abstract method
     }
@@ -1105,9 +1117,9 @@
         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)) {
-          exceptions_to_resolve_.emplace(encoded_catch_handler_handlers_type_idx,
-                                         method_handle->GetDexFile());
+        if (!method->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
+          exceptions_to_resolve->emplace(encoded_catch_handler_handlers_type_idx,
+                                         method->GetDexFile());
         }
         // ignore address associated with catch handler
         DecodeUnsignedLeb128(&encoded_catch_handler_list);
@@ -1119,7 +1131,7 @@
     }
   }
 
-  std::set<std::pair<dex::TypeIndex, const DexFile*>>& exceptions_to_resolve_;
+  std::vector<ObjPtr<mirror::Class>> classes_;
 };
 
 class RecordImageClassesVisitor : public ClassVisitor {
@@ -1173,8 +1185,14 @@
       hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/lang/Throwable;")));
   do {
     unresolved_exception_types.clear();
-    ResolveCatchBlockExceptionsClassVisitor visitor(unresolved_exception_types);
-    class_linker->VisitClasses(&visitor);
+    {
+      // Thread suspension is not allowed while ResolveCatchBlockExceptionsClassVisitor
+      // is using a std::vector<ObjPtr<mirror::Class>>.
+      ScopedAssertNoThreadSuspension ants(__FUNCTION__);
+      ResolveCatchBlockExceptionsClassVisitor visitor;
+      class_linker->VisitClasses(&visitor);
+      visitor.FindExceptionTypesToResolve(&unresolved_exception_types);
+    }
     for (const auto& exception_type : unresolved_exception_types) {
       dex::TypeIndex exception_type_idx = exception_type.first;
       const DexFile* dex_file = exception_type.second;
@@ -1423,19 +1441,14 @@
   dex_to_dex_references_.back().GetMethodIndexes().SetBit(method_ref.dex_method_index);
 }
 
-bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx,
-                                                Handle<mirror::DexCache> dex_cache,
-                                                dex::TypeIndex type_idx) {
-  // Get type from dex cache assuming it was populated by the verifier
-  mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
+bool CompilerDriver::CanAccessTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class,
+                                                ObjPtr<mirror::Class> resolved_class) {
   if (resolved_class == nullptr) {
     stats_->TypeNeedsAccessCheck();
     return false;  // Unknown class needs access checks.
   }
-  const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(referrer_idx);
   bool is_accessible = resolved_class->IsPublic();  // Public classes are always accessible.
   if (!is_accessible) {
-    mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
     if (referrer_class == nullptr) {
       stats_->TypeNeedsAccessCheck();
       return false;  // Incomplete referrer knowledge needs access check.
@@ -1452,12 +1465,9 @@
   return is_accessible;
 }
 
-bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx,
-                                                            Handle<mirror::DexCache> dex_cache,
-                                                            dex::TypeIndex type_idx,
+bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class,
+                                                            ObjPtr<mirror::Class> resolved_class,
                                                             bool* finalizable) {
-  // Get type from dex cache assuming it was populated by the verifier.
-  mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
   if (resolved_class == nullptr) {
     stats_->TypeNeedsAccessCheck();
     // Be conservative.
@@ -1465,10 +1475,8 @@
     return false;  // Unknown class needs access checks.
   }
   *finalizable = resolved_class->IsFinalizable();
-  const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(referrer_idx);
   bool is_accessible = resolved_class->IsPublic();  // Public classes are always accessible.
   if (!is_accessible) {
-    mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
     if (referrer_class == nullptr) {
       stats_->TypeNeedsAccessCheck();
       return false;  // Incomplete referrer knowledge needs access check.
@@ -1512,9 +1520,7 @@
   mirror::Class* referrer_class;
   Handle<mirror::DexCache> dex_cache(mUnit->GetDexCache());
   {
-    StackHandleScope<1> hs(soa.Self());
-    Handle<mirror::ClassLoader> class_loader_handle(
-        hs.NewHandle(soa.Decode<mirror::ClassLoader>(mUnit->GetClassLoader())));
+    Handle<mirror::ClassLoader> class_loader_handle = mUnit->GetClassLoader();
     resolved_field = ResolveField(soa, dex_cache, class_loader_handle, mUnit, field_idx, false);
     referrer_class = resolved_field != nullptr
         ? ResolveCompilingMethodsClass(soa, dex_cache, class_loader_handle, mUnit) : nullptr;
@@ -2587,10 +2593,18 @@
         continue;
       }
       previous_direct_method_idx = method_idx;
-      CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
-                    it.GetMethodInvokeType(class_def), class_def_index,
-                    method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
-                    compilation_enabled, dex_cache);
+      CompileMethod(soa.Self(),
+                    driver,
+                    it.GetMethodCodeItem(),
+                    it.GetMethodAccessFlags(),
+                    it.GetMethodInvokeType(class_def),
+                    class_def_index,
+                    method_idx,
+                    class_loader,
+                    dex_file,
+                    dex_to_dex_compilation_level,
+                    compilation_enabled,
+                    dex_cache);
       it.Next();
     }
     // Compile virtual methods
@@ -2604,10 +2618,17 @@
         continue;
       }
       previous_virtual_method_idx = method_idx;
-      CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
-                    it.GetMethodInvokeType(class_def), class_def_index,
-                    method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
-                    compilation_enabled, dex_cache);
+      CompileMethod(soa.Self(),
+                    driver, it.GetMethodCodeItem(),
+                    it.GetMethodAccessFlags(),
+                    it.GetMethodInvokeType(class_def),
+                    class_def_index,
+                    method_idx,
+                    class_loader,
+                    dex_file,
+                    dex_to_dex_compilation_level,
+                    compilation_enabled,
+                    dex_cache);
       it.Next();
     }
     DCHECK(!it.HasNext());
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 503fe3a..d032a26 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -187,16 +187,14 @@
       REQUIRES(!requires_constructor_barrier_lock_);
 
   // Are runtime access checks necessary in the compiled code?
-  bool CanAccessTypeWithoutChecks(uint32_t referrer_idx,
-                                  Handle<mirror::DexCache> dex_cache,
-                                  dex::TypeIndex type_idx)
+  bool CanAccessTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class,
+                                  ObjPtr<mirror::Class> resolved_class)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Are runtime access and instantiable checks necessary in the code?
   // out_is_finalizable is set to whether the type is finalizable.
-  bool CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx,
-                                              Handle<mirror::DexCache> dex_cache,
-                                              dex::TypeIndex type_idx,
+  bool CanAccessInstantiableTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class,
+                                              ObjPtr<mirror::Class> resolved_class,
                                               bool* out_is_finalizable)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -406,10 +404,6 @@
                                       uint32_t field_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  mirror::ClassLoader* GetClassLoader(const ScopedObjectAccess& soa,
-                                      const DexCompilationUnit* mUnit)
-    REQUIRES_SHARED(Locks::mutator_lock_);
-
  private:
   void PreCompile(jobject class_loader,
                   const std::vector<const DexFile*>& dex_files,
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 1e4ca16..e4b66eb 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -101,6 +101,7 @@
 };
 
 // Disabled due to 10 second runtime on host
+// TODO: Update the test for hash-based dex cache arrays. Bug: 30627598
 TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) {
   CompileAll(nullptr);
 
diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc
index 47b1929..7e8e812 100644
--- a/compiler/driver/dex_compilation_unit.cc
+++ b/compiler/driver/dex_compilation_unit.cc
@@ -21,7 +21,7 @@
 
 namespace art {
 
-DexCompilationUnit::DexCompilationUnit(jobject class_loader,
+DexCompilationUnit::DexCompilationUnit(Handle<mirror::ClassLoader> class_loader,
                                        ClassLinker* class_linker,
                                        const DexFile& dex_file,
                                        const DexFile::CodeItem* code_item,
diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h
index 854927d..24a9a5b 100644
--- a/compiler/driver/dex_compilation_unit.h
+++ b/compiler/driver/dex_compilation_unit.h
@@ -34,7 +34,7 @@
 
 class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> {
  public:
-  DexCompilationUnit(jobject class_loader,
+  DexCompilationUnit(Handle<mirror::ClassLoader> class_loader,
                      ClassLinker* class_linker,
                      const DexFile& dex_file,
                      const DexFile::CodeItem* code_item,
@@ -44,7 +44,7 @@
                      const VerifiedMethod* verified_method,
                      Handle<mirror::DexCache> dex_cache);
 
-  jobject GetClassLoader() const {
+  Handle<mirror::ClassLoader> GetClassLoader() const {
     return class_loader_;
   }
 
@@ -113,7 +113,7 @@
   }
 
  private:
-  const jobject class_loader_;
+  const Handle<mirror::ClassLoader> class_loader_;
 
   ClassLinker* const class_linker_;
 
@@ -125,7 +125,7 @@
   const uint32_t access_flags_;
   const VerifiedMethod* verified_method_;
 
-  Handle<mirror::DexCache> dex_cache_;
+  const Handle<mirror::DexCache> dex_cache_;
 
   std::string symbol_;
 };
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index c72edb1..3e9ae08 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -940,9 +940,11 @@
     }
     ObjPtr<mirror::DexCache> dex_cache = self->DecodeJObject(data.weak_root)->AsDexCache();
     for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
-      Class* klass = dex_cache->GetResolvedType(dex::TypeIndex(i));
+      mirror::TypeDexCachePair pair =
+          dex_cache->GetResolvedTypes()[i].load(std::memory_order_relaxed);
+      mirror::Class* klass = pair.object.Read();
       if (klass != nullptr && !KeepClass(klass)) {
-        dex_cache->SetResolvedType(dex::TypeIndex(i), nullptr);
+        dex_cache->ClearResolvedType(dex::TypeIndex(pair.index));
       }
     }
     ArtMethod** resolved_methods = dex_cache->GetResolvedMethods();
@@ -1922,8 +1924,7 @@
     // above comment for intern tables.
     ClassTable temp_class_table;
     temp_class_table.ReadFromMemory(class_table_memory_ptr);
-    CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u);
-    mirror::ClassLoader* class_loader = compile_app_image_ ? *class_loaders_.begin() : nullptr;
+    ObjPtr<mirror::ClassLoader> class_loader = GetClassLoader();
     CHECK_EQ(temp_class_table.NumZygoteClasses(class_loader),
              table->NumNonZygoteClasses(class_loader) + table->NumZygoteClasses(class_loader));
     UnbufferedRootVisitor visitor(&root_visitor, RootInfo(kRootUnknown));
@@ -2213,7 +2214,7 @@
     orig_dex_cache->FixupStrings(NativeCopyLocation(orig_strings, orig_dex_cache),
                                  ImageAddressVisitor(this));
   }
-  GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes();
+  mirror::TypeDexCacheType* orig_types = orig_dex_cache->GetResolvedTypes();
   if (orig_types != nullptr) {
     copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedTypesOffset(),
                                                NativeLocationInImage(orig_types),
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index cc7df1c..bdc7146 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -51,8 +51,13 @@
 }  // namespace space
 }  // namespace gc
 
+namespace mirror {
+class ClassLoader;
+}  // namespace mirror
+
 class ClassLoaderVisitor;
 class ClassTable;
+class ImtConflictTable;
 
 static constexpr int kInvalidFd = -1;
 
@@ -79,6 +84,11 @@
     return true;
   }
 
+  ObjPtr<mirror::ClassLoader> GetClassLoader() {
+    CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u);
+    return compile_app_image_ ? *class_loaders_.begin() : nullptr;
+  }
+
   template <typename T>
   T* GetImageAddress(T* object) const REQUIRES_SHARED(Locks::mutator_lock_) {
     if (object == nullptr || IsInBootImage(object)) {
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index bd2c5e3..227fdc4 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1060,6 +1060,7 @@
   WriteCodeMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset,
                          size_t relative_offset) SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
     : OatDexMethodVisitor(writer, relative_offset),
+      class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr),
       out_(out),
       file_offset_(file_offset),
       soa_(Thread::Current()),
@@ -1245,12 +1246,13 @@
   }
 
  private:
+  ObjPtr<mirror::ClassLoader> class_loader_;
   OutputStream* const out_;
   const size_t file_offset_;
   const ScopedObjectAccess soa_;
   const ScopedAssertNoThreadSuspension no_thread_suspension_;
   ClassLinker* const class_linker_;
-  mirror::DexCache* dex_cache_;
+  ObjPtr<mirror::DexCache> dex_cache_;
   std::vector<uint8_t> patched_code_;
 
   void ReportWriteFailure(const char* what, const ClassDataItemIterator& it) {
@@ -1261,7 +1263,7 @@
   ArtMethod* GetTargetMethod(const LinkerPatch& patch)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     MethodReference ref = patch.TargetMethod();
-    mirror::DexCache* dex_cache =
+    ObjPtr<mirror::DexCache> dex_cache =
         (dex_file_ == ref.dex_file) ? dex_cache_ : class_linker_->FindDexCache(
             Thread::Current(), *ref.dex_file);
     ArtMethod* method = dex_cache->GetResolvedMethod(
@@ -1295,7 +1297,7 @@
     return target_offset;
   }
 
-  mirror::DexCache* GetDexCache(const DexFile* target_dex_file)
+  ObjPtr<mirror::DexCache> GetDexCache(const DexFile* target_dex_file)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     return (target_dex_file == dex_file_)
         ? dex_cache_
@@ -1303,10 +1305,12 @@
   }
 
   mirror::Class* GetTargetType(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) {
-    mirror::DexCache* dex_cache = GetDexCache(patch.TargetTypeDexFile());
-    mirror::Class* type = dex_cache->GetResolvedType(patch.TargetTypeIndex());
+    DCHECK(writer_->HasImage());
+    ObjPtr<mirror::DexCache> dex_cache = GetDexCache(patch.TargetTypeDexFile());
+    ObjPtr<mirror::Class> type =
+        ClassLinker::LookupResolvedType(patch.TargetTypeIndex(), dex_cache, class_loader_);
     CHECK(type != nullptr);
-    return type;
+    return type.Ptr();
   }
 
   mirror::String* GetTargetString(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 8cf4089..223439b 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -51,7 +51,10 @@
         compiler_driver_(driver),
         compilation_stats_(compiler_stats),
         block_builder_(graph, dex_file, code_item),
-        ssa_builder_(graph, dex_compilation_unit->GetDexCache(), handles),
+        ssa_builder_(graph,
+                     dex_compilation_unit->GetClassLoader(),
+                     dex_compilation_unit->GetDexCache(),
+                     handles),
         instruction_builder_(graph,
                              &block_builder_,
                              &ssa_builder_,
@@ -76,10 +79,12 @@
         code_item_(code_item),
         dex_compilation_unit_(nullptr),
         compiler_driver_(nullptr),
-        null_dex_cache_(),
         compilation_stats_(nullptr),
         block_builder_(graph, nullptr, code_item),
-        ssa_builder_(graph, null_dex_cache_, handles),
+        ssa_builder_(graph,
+                     handles->NewHandle<mirror::ClassLoader>(nullptr),
+                     handles->NewHandle<mirror::DexCache>(nullptr),
+                     handles),
         instruction_builder_(graph,
                              &block_builder_,
                              &ssa_builder_,
@@ -91,7 +96,7 @@
                              /* compiler_driver */ nullptr,
                              /* interpreter_metadata */ nullptr,
                              /* compiler_stats */ nullptr,
-                             null_dex_cache_,
+                             handles->NewHandle<mirror::DexCache>(nullptr),
                              handles) {}
 
   GraphAnalysisResult BuildGraph();
@@ -112,8 +117,6 @@
 
   CompilerDriver* const compiler_driver_;
 
-  ScopedNullHandle<mirror::DexCache> null_dex_cache_;
-
   OptimizingCompilerStats* compilation_stats_;
 
   HBasicBlockBuilder block_builder_;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 1e89ba5..cf824a1 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1533,7 +1533,9 @@
       HConstant* src_cst = source.GetConstant();
       CPURegister temp;
       if (src_cst->IsZeroBitPattern()) {
-        temp = (src_cst->IsLongConstant() || src_cst->IsDoubleConstant()) ? xzr : wzr;
+        temp = (src_cst->IsLongConstant() || src_cst->IsDoubleConstant())
+            ? Register(xzr)
+            : Register(wzr);
       } else {
         if (src_cst->IsIntConstant()) {
           temp = temps.AcquireW();
@@ -1903,6 +1905,9 @@
                                                        LocationSummary::kNoCall);
   if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
+    // We need a temporary register for the read barrier marking slow
+    // path in CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier.
+    locations->AddTemp(Location::RequiresRegister());
   }
   locations->SetInAt(0, Location::RequiresRegister());
   if (Primitive::IsFloatingPointType(instruction->GetType())) {
@@ -1930,11 +1935,9 @@
 
   if (field_type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
     // Object FieldGet with Baker's read barrier case.
-    MacroAssembler* masm = GetVIXLAssembler();
-    UseScratchRegisterScope temps(masm);
     // /* HeapReference<Object> */ out = *(base + offset)
     Register base = RegisterFrom(base_loc, Primitive::kPrimNot);
-    Register temp = temps.AcquireW();
+    Register temp = WRegisterFrom(locations->GetTemp(0));
     // Note that potential implicit null checks are handled in this
     // CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier call.
     codegen_->GenerateFieldLoadWithBakerReadBarrier(
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 893e465..f4d3ec5 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -7679,15 +7679,21 @@
     vixl32::Register jump_offset = temps.Acquire();
 
     // Load jump offset from the table.
-    __ Adr(table_base, jump_table->GetTableStartLabel());
-    __ Ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2));
+    {
+      const size_t jump_size = switch_instr->GetNumEntries() * sizeof(int32_t);
+      ExactAssemblyScope aas(GetVIXLAssembler(),
+                             (vixl32::kMaxInstructionSizeInBytes * 4) + jump_size,
+                             CodeBufferCheckScope::kMaximumSize);
+      __ adr(table_base, jump_table->GetTableStartLabel());
+      __ ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2));
 
-    // Jump to target block by branching to table_base(pc related) + offset.
-    vixl32::Register target_address = table_base;
-    __ Add(target_address, table_base, jump_offset);
-    __ Bx(target_address);
+      // Jump to target block by branching to table_base(pc related) + offset.
+      vixl32::Register target_address = table_base;
+      __ add(target_address, table_base, jump_offset);
+      __ bx(target_address);
 
-    jump_table->EmitTable(codegen_);
+      jump_table->EmitTable(codegen_);
+    }
   }
 }
 void LocationsBuilderARMVIXL::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) {
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 52b780c..5ba8912 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -115,12 +115,11 @@
   Location GetReturnLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
     return Location::RegisterLocation(V0);
   }
-  Location GetSetValueLocation(Primitive::Type type, bool is_instance) const OVERRIDE {
-    return Primitive::Is64BitType(type)
+  Location GetSetValueLocation(Primitive::Type type ATTRIBUTE_UNUSED,
+                               bool is_instance) const OVERRIDE {
+    return is_instance
         ? Location::RegisterLocation(A2)
-        : (is_instance
-            ? Location::RegisterLocation(A2)
-            : Location::RegisterLocation(A1));
+        : Location::RegisterLocation(A1);
   }
   Location GetFpuLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
     return Location::FpuRegisterLocation(F0);
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index 776a483..93ea090 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -130,8 +130,8 @@
   Primitive::Type input_type = input->GetType();
   if (input->IsConstant() && input->AsConstant()->IsZeroBitPattern()) {
     return (Primitive::ComponentSize(input_type) >= vixl::aarch64::kXRegSizeInBytes)
-        ? vixl::aarch64::xzr
-        : vixl::aarch64::wzr;
+        ? vixl::aarch64::Register(vixl::aarch64::xzr)
+        : vixl::aarch64::Register(vixl::aarch64::wzr);
   }
   return InputCPURegisterAt(instr, index);
 }
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 7772e8f..22f0646 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -198,9 +198,9 @@
 }
 
 static dex::TypeIndex FindClassIndexIn(mirror::Class* cls,
-                                       const DexFile& dex_file,
-                                       Handle<mirror::DexCache> dex_cache)
+                                       const DexCompilationUnit& compilation_unit)
     REQUIRES_SHARED(Locks::mutator_lock_) {
+  const DexFile& dex_file = *compilation_unit.GetDexFile();
   dex::TypeIndex index;
   if (cls->GetDexCache() == nullptr) {
     DCHECK(cls->IsArrayClass()) << cls->PrettyClass();
@@ -209,22 +209,19 @@
     DCHECK(cls->IsProxyClass()) << cls->PrettyClass();
     // TODO: deal with proxy classes.
   } else if (IsSameDexFile(cls->GetDexFile(), dex_file)) {
-    DCHECK_EQ(cls->GetDexCache(), dex_cache.Get());
+    DCHECK_EQ(cls->GetDexCache(), compilation_unit.GetDexCache().Get());
     index = cls->GetDexTypeIndex();
-    // Update the dex cache to ensure the class is in. The generated code will
-    // consider it is. We make it safe by updating the dex cache, as other
-    // dex files might also load the class, and there is no guarantee the dex
-    // cache of the dex file of the class will be updated.
-    if (dex_cache->GetResolvedType(index) == nullptr) {
-      dex_cache->SetResolvedType(index, cls);
-    }
   } else {
     index = cls->FindTypeIndexInOtherDexFile(dex_file);
-    // We cannot guarantee the entry in the dex cache will resolve to the same class,
+    // We cannot guarantee the entry will resolve to the same class,
     // as there may be different class loaders. So only return the index if it's
-    // the right class in the dex cache already.
-    if (index.IsValid() && dex_cache->GetResolvedType(index) != cls) {
-      index = dex::TypeIndex::Invalid();
+    // the right class already resolved with the class loader.
+    if (index.IsValid()) {
+      ObjPtr<mirror::Class> resolved = ClassLinker::LookupResolvedType(
+          index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get());
+      if (resolved != cls) {
+        index = dex::TypeIndex::Invalid();
+      }
     }
   }
 
@@ -451,9 +448,8 @@
   DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface())
       << invoke_instruction->DebugName();
 
-  const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
   dex::TypeIndex class_index = FindClassIndexIn(
-      GetMonomorphicType(classes), caller_dex_file, caller_compilation_unit_.GetDexCache());
+      GetMonomorphicType(classes), caller_compilation_unit_);
   if (!class_index.IsValid()) {
     VLOG(compiler) << "Call to " << ArtMethod::PrettyMethod(resolved_method)
                    << " from inline cache is not inlined because its class is not"
@@ -496,6 +492,7 @@
   // Run type propagation to get the guard typed, and eventually propagate the
   // type of the receiver.
   ReferenceTypePropagation rtp_fixup(graph_,
+                                     outer_compilation_unit_.GetClassLoader(),
                                      outer_compilation_unit_.GetDexCache(),
                                      handles_,
                                      /* is_first_run */ false);
@@ -586,7 +583,6 @@
 
   ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
   PointerSize pointer_size = class_linker->GetImagePointerSize();
-  const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
 
   bool all_targets_inlined = true;
   bool one_target_inlined = false;
@@ -608,8 +604,7 @@
     HInstruction* cursor = invoke_instruction->GetPrevious();
     HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
 
-    dex::TypeIndex class_index = FindClassIndexIn(
-        handle.Get(), caller_dex_file, caller_compilation_unit_.GetDexCache());
+    dex::TypeIndex class_index = FindClassIndexIn(handle.Get(), caller_compilation_unit_);
     HInstruction* return_replacement = nullptr;
     if (!class_index.IsValid() ||
         !TryBuildAndInline(invoke_instruction,
@@ -665,6 +660,7 @@
 
   // Run type propagation to get the guards typed.
   ReferenceTypePropagation rtp_fixup(graph_,
+                                     outer_compilation_unit_.GetClassLoader(),
                                      outer_compilation_unit_.GetDexCache(),
                                      handles_,
                                      /* is_first_run */ false);
@@ -859,6 +855,7 @@
 
   // Run type propagation to get the guard typed.
   ReferenceTypePropagation rtp_fixup(graph_,
+                                     outer_compilation_unit_.GetClassLoader(),
                                      outer_compilation_unit_.GetDexCache(),
                                      handles_,
                                      /* is_first_run */ false);
@@ -927,6 +924,7 @@
     // Actual return value has a more specific type than the method's declared
     // return type. Run RTP again on the outer graph to propagate it.
     ReferenceTypePropagation(graph_,
+                             outer_compilation_unit_.GetClassLoader(),
                              outer_compilation_unit_.GetDexCache(),
                              handles_,
                              /* is_first_run */ false).Run();
@@ -1179,7 +1177,11 @@
       /* dex_pc */ 0);
   if (iget->GetType() == Primitive::kPrimNot) {
     // Use the same dex_cache that we used for field lookup as the hint_dex_cache.
-    ReferenceTypePropagation rtp(graph_, dex_cache, handles_, /* is_first_run */ false);
+    ReferenceTypePropagation rtp(graph_,
+                                 outer_compilation_unit_.GetClassLoader(),
+                                 dex_cache,
+                                 handles_,
+                                 /* is_first_run */ false);
     rtp.Visit(iget);
   }
   return iget;
@@ -1225,7 +1227,7 @@
       resolved_method->GetDeclaringClass()->GetClassLoader()));
 
   DexCompilationUnit dex_compilation_unit(
-      class_loader.ToJObject(),
+      class_loader,
       class_linker,
       callee_dex_file,
       code_item,
@@ -1341,6 +1343,7 @@
   // are more specific than the declared ones, run RTP again on the inner graph.
   if (run_rtp || ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) {
     ReferenceTypePropagation(callee_graph,
+                             outer_compilation_unit_.GetClassLoader(),
                              dex_compilation_unit.GetDexCache(),
                              handles_,
                              /* is_first_run */ false).Run();
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index cac385c..3d911d7 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -668,11 +668,10 @@
 
 ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) {
   ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<3> hs(soa.Self());
+  StackHandleScope<2> hs(soa.Self());
 
   ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker();
-  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
-      soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader())));
+  Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
   Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass()));
   // We fetch the referenced class eagerly (that is, the class pointed by in the MethodId
   // at method_idx), as `CanAccessResolvedMethod` expects it be be in the dex cache.
@@ -1284,9 +1283,7 @@
 static mirror::Class* GetClassFrom(CompilerDriver* driver,
                                    const DexCompilationUnit& compilation_unit) {
   ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
-      soa.Decode<mirror::ClassLoader>(compilation_unit.GetClassLoader())));
+  Handle<mirror::ClassLoader> class_loader = compilation_unit.GetClassLoader();
   Handle<mirror::DexCache> dex_cache = compilation_unit.GetDexCache();
 
   return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit);
@@ -1302,10 +1299,9 @@
 
 bool HInstructionBuilder::IsOutermostCompilingClass(dex::TypeIndex type_index) const {
   ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<3> hs(soa.Self());
+  StackHandleScope<2> hs(soa.Self());
   Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
-  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
-      soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader())));
+  Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
   Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass(
       soa, dex_cache, class_loader, type_index, dex_compilation_unit_)));
   Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
@@ -1343,10 +1339,8 @@
   uint16_t field_index = instruction.VRegB_21c();
 
   ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<3> hs(soa.Self());
   Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
-  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
-      soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader())));
+  Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
   ArtField* resolved_field = compiler_driver_->ResolveField(
       soa, dex_cache, class_loader, dex_compilation_unit_, field_index, true);
 
@@ -1357,6 +1351,7 @@
     return true;
   }
 
+  StackHandleScope<2> hs(soa.Self());
   Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
   Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache();
   Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
@@ -1635,9 +1630,7 @@
   const DexCompilationUnit* compilation_unit =
       outer ? outer_compilation_unit_ : dex_compilation_unit_;
   const DexFile& dex_file = *compilation_unit->GetDexFile();
-  StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
-      soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader())));
+  Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
   Handle<mirror::Class> klass = handles_->NewHandle(compiler_driver_->ResolveClass(
       soa, compilation_unit->GetDexCache(), class_loader, type_index, compilation_unit));
 
@@ -1692,17 +1685,9 @@
   }
 }
 
-bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index,
-                                           Handle<mirror::DexCache> dex_cache,
-                                           bool* finalizable) const {
-  return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks(
-      dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index, finalizable);
-}
-
 bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, bool* finalizable) const {
-  ScopedObjectAccess soa(Thread::Current());
-  Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
-  return NeedsAccessCheck(type_index, dex_cache, finalizable);
+  return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks(
+      LookupReferrerClass(), LookupResolvedType(type_index, *dex_compilation_unit_), finalizable);
 }
 
 bool HInstructionBuilder::CanDecodeQuickenedInfo() const {
@@ -2742,4 +2727,18 @@
   return true;
 }  // NOLINT(readability/fn_size)
 
+ObjPtr<mirror::Class> HInstructionBuilder::LookupResolvedType(
+    dex::TypeIndex type_index,
+    const DexCompilationUnit& compilation_unit) const {
+  return ClassLinker::LookupResolvedType(
+        type_index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get());
+}
+
+ObjPtr<mirror::Class> HInstructionBuilder::LookupReferrerClass() const {
+  // TODO: Cache the result in a Handle<mirror::Class>.
+  const DexFile::MethodId& method_id =
+      dex_compilation_unit_->GetDexFile()->GetMethodId(dex_compilation_unit_->GetDexMethodIndex());
+  return LookupResolvedType(method_id.class_idx_, *dex_compilation_unit_);
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index 5efe950..6e3b078 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -103,11 +103,8 @@
 
   // Returns whether the current method needs access check for the type.
   // Output parameter finalizable is set to whether the type is finalizable.
-  bool NeedsAccessCheck(dex::TypeIndex type_index,
-                        Handle<mirror::DexCache> dex_cache,
-                        /*out*/bool* finalizable) const
+  bool NeedsAccessCheck(dex::TypeIndex type_index, /*out*/bool* finalizable) const
       REQUIRES_SHARED(Locks::mutator_lock_);
-  bool NeedsAccessCheck(dex::TypeIndex type_index, /*out*/bool* finalizable) const;
 
   template<typename T>
   void Unop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
@@ -290,6 +287,12 @@
   // not be resolved.
   ArtMethod* ResolveMethod(uint16_t method_idx, InvokeType invoke_type);
 
+  ObjPtr<mirror::Class> LookupResolvedType(dex::TypeIndex type_index,
+                                           const DexCompilationUnit& compilation_unit) const
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  ObjPtr<mirror::Class> LookupReferrerClass() const REQUIRES_SHARED(Locks::mutator_lock_);
+
   ArenaAllocator* const arena_;
   HGraph* const graph_;
   VariableSizedHandleScope* handles_;
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 68c2d2e..91d9c56 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -2742,14 +2742,36 @@
   __ Bind(slow_path->GetExitLabel());
 }
 
+void IntrinsicLocationsBuilderARMVIXL::VisitMathCeil(HInvoke* invoke) {
+  if (features_.HasARMv8AInstructions()) {
+    CreateFPToFPLocations(arena_, invoke);
+  }
+}
+
+void IntrinsicCodeGeneratorARMVIXL::VisitMathCeil(HInvoke* invoke) {
+  ArmVIXLAssembler* assembler = GetAssembler();
+  DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
+  __ Vrintp(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
+}
+
+void IntrinsicLocationsBuilderARMVIXL::VisitMathFloor(HInvoke* invoke) {
+  if (features_.HasARMv8AInstructions()) {
+    CreateFPToFPLocations(arena_, invoke);
+  }
+}
+
+void IntrinsicCodeGeneratorARMVIXL::VisitMathFloor(HInvoke* invoke) {
+  ArmVIXLAssembler* assembler = GetAssembler();
+  DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
+  __ Vrintm(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
+}
+
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMinDoubleDouble)
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMinFloatFloat)
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMaxDoubleDouble)
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMaxFloatFloat)
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMinLongLong)
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMaxLongLong)
-UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathCeil)          // Could be done by changing rounding mode, maybe?
-UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathFloor)         // Could be done by changing rounding mode, maybe?
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRint)
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundDouble)   // Could be done by changing rounding mode, maybe?
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundFloat)    // Could be done by changing rounding mode, maybe?
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 297500b..dad87e3 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -305,7 +305,7 @@
                           InvokeType invoke_type,
                           uint16_t class_def_idx,
                           uint32_t method_idx,
-                          jobject class_loader,
+                          Handle<mirror::ClassLoader> class_loader,
                           const DexFile& dex_file,
                           Handle<mirror::DexCache> dex_cache) const OVERRIDE;
 
@@ -374,7 +374,7 @@
                             InvokeType invoke_type,
                             uint16_t class_def_idx,
                             uint32_t method_idx,
-                            jobject class_loader,
+                            Handle<mirror::ClassLoader> class_loader,
                             const DexFile& dex_file,
                             Handle<mirror::DexCache> dex_cache,
                             ArtMethod* method,
@@ -871,7 +871,7 @@
                                               InvokeType invoke_type,
                                               uint16_t class_def_idx,
                                               uint32_t method_idx,
-                                              jobject class_loader,
+                                              Handle<mirror::ClassLoader> class_loader,
                                               const DexFile& dex_file,
                                               Handle<mirror::DexCache> dex_cache,
                                               ArtMethod* method,
@@ -942,11 +942,8 @@
   const uint8_t* interpreter_metadata = nullptr;
   if (method == nullptr) {
     ScopedObjectAccess soa(Thread::Current());
-    StackHandleScope<1> hs(soa.Self());
-    Handle<mirror::ClassLoader> loader(hs.NewHandle(
-        soa.Decode<mirror::ClassLoader>(class_loader)));
     method = compiler_driver->ResolveMethod(
-        soa, dex_cache, loader, &dex_compilation_unit, method_idx, invoke_type);
+        soa, dex_cache, class_loader, &dex_compilation_unit, method_idx, invoke_type);
   }
   // For AOT compilation, we may not get a method, for example if its class is erroneous.
   // JIT should always have a method.
@@ -955,16 +952,6 @@
     graph->SetArtMethod(method);
     ScopedObjectAccess soa(Thread::Current());
     interpreter_metadata = method->GetQuickenedInfo(class_linker->GetImagePointerSize());
-    dex::TypeIndex type_index = method->GetDeclaringClass()->GetDexTypeIndex();
-
-    // Update the dex cache if the type is not in it yet. Note that under AOT,
-    // the verifier must have set it, but under JIT, there's no guarantee, as we
-    // don't necessarily run the verifier.
-    // The compiler and the compiler driver assume the compiling class is
-    // in the dex cache.
-    if (dex_cache->GetResolvedType(type_index) == nullptr) {
-      dex_cache->SetResolvedType(type_index, method->GetDeclaringClass());
-    }
   }
 
   std::unique_ptr<CodeGenerator> codegen(
@@ -1044,7 +1031,7 @@
                                             InvokeType invoke_type,
                                             uint16_t class_def_idx,
                                             uint32_t method_idx,
-                                            jobject jclass_loader,
+                                            Handle<mirror::ClassLoader> jclass_loader,
                                             const DexFile& dex_file,
                                             Handle<mirror::DexCache> dex_cache) const {
   CompilerDriver* compiler_driver = GetCompilerDriver();
@@ -1139,7 +1126,6 @@
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
   DCHECK(method->IsCompilable());
 
-  jobject jclass_loader = class_loader.ToJObject();
   const DexFile* dex_file = method->GetDexFile();
   const uint16_t class_def_idx = method->GetClassDefIndex();
   const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset());
@@ -1163,7 +1149,7 @@
                    invoke_type,
                    class_def_idx,
                    method_idx,
-                   jclass_loader,
+                   class_loader,
                    *dex_file,
                    dex_cache,
                    method,
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index b02f250..be4857a 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -66,11 +66,13 @@
 class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor {
  public:
   RTPVisitor(HGraph* graph,
+             Handle<mirror::ClassLoader> class_loader,
              Handle<mirror::DexCache> hint_dex_cache,
              HandleCache* handle_cache,
              ArenaVector<HInstruction*>* worklist,
              bool is_first_run)
     : HGraphDelegateVisitor(graph),
+      class_loader_(class_loader),
       hint_dex_cache_(hint_dex_cache),
       handle_cache_(handle_cache),
       worklist_(worklist),
@@ -102,6 +104,7 @@
                                bool is_exact);
 
  private:
+  Handle<mirror::ClassLoader> class_loader_;
   Handle<mirror::DexCache> hint_dex_cache_;
   HandleCache* handle_cache_;
   ArenaVector<HInstruction*>* worklist_;
@@ -109,11 +112,13 @@
 };
 
 ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph,
+                                                   Handle<mirror::ClassLoader> class_loader,
                                                    Handle<mirror::DexCache> hint_dex_cache,
                                                    VariableSizedHandleScope* handles,
                                                    bool is_first_run,
                                                    const char* name)
     : HOptimization(graph, name),
+      class_loader_(class_loader),
       hint_dex_cache_(hint_dex_cache),
       handle_cache_(handles),
       worklist_(graph->GetArena()->Adapter(kArenaAllocReferenceTypePropagation)),
@@ -148,7 +153,12 @@
 }
 
 void ReferenceTypePropagation::Visit(HInstruction* instruction) {
-  RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_);
+  RTPVisitor visitor(graph_,
+                     class_loader_,
+                     hint_dex_cache_,
+                     &handle_cache_,
+                     &worklist_,
+                     is_first_run_);
   instruction->Accept(&visitor);
 }
 
@@ -322,7 +332,12 @@
 }
 
 void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
-  RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_);
+  RTPVisitor visitor(graph_,
+                     class_loader_,
+                     hint_dex_cache_,
+                     &handle_cache_,
+                     &worklist_,
+                     is_first_run_);
   // Handle Phis first as there might be instructions in the same block who depend on them.
   for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
     VisitPhi(it.Current()->AsPhi());
@@ -542,9 +557,10 @@
   DCHECK_EQ(instr->GetType(), Primitive::kPrimNot);
 
   ScopedObjectAccess soa(Thread::Current());
-  mirror::DexCache* dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_);
-  // Get type from dex cache assuming it was populated by the verifier.
-  SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact);
+  ObjPtr<mirror::DexCache> dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_);
+  ObjPtr<mirror::Class> klass =
+      ClassLinker::LookupResolvedType(type_idx, dex_cache, class_loader_.Get());
+  SetClassAsTypeInfo(instr, klass, is_exact);
 }
 
 void ReferenceTypePropagation::RTPVisitor::VisitNewInstance(HNewInstance* instr) {
@@ -557,25 +573,13 @@
   SetClassAsTypeInfo(instr, instr->GetLoadClass()->GetClass().Get(), /* is_exact */ true);
 }
 
-static mirror::Class* GetClassFromDexCache(Thread* self,
-                                           const DexFile& dex_file,
-                                           dex::TypeIndex type_idx,
-                                           Handle<mirror::DexCache> hint_dex_cache)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  mirror::DexCache* dex_cache = FindDexCacheWithHint(self, dex_file, hint_dex_cache);
-  // Get type from dex cache assuming it was populated by the verifier.
-  return dex_cache->GetResolvedType(type_idx);
-}
-
 void ReferenceTypePropagation::RTPVisitor::VisitParameterValue(HParameterValue* instr) {
   // We check if the existing type is valid: the inliner may have set it.
   if (instr->GetType() == Primitive::kPrimNot && !instr->GetReferenceTypeInfo().IsValid()) {
-    ScopedObjectAccess soa(Thread::Current());
-    mirror::Class* resolved_class = GetClassFromDexCache(soa.Self(),
-                                                         instr->GetDexFile(),
-                                                         instr->GetTypeIndex(),
-                                                         hint_dex_cache_);
-    SetClassAsTypeInfo(instr, resolved_class, /* is_exact */ false);
+    UpdateReferenceTypeInfo(instr,
+                            instr->GetTypeIndex(),
+                            instr->GetDexFile(),
+                            /* is_exact */ false);
   }
 }
 
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 4663471..215e967 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -33,6 +33,7 @@
 class ReferenceTypePropagation : public HOptimization {
  public:
   ReferenceTypePropagation(HGraph* graph,
+                           Handle<mirror::ClassLoader> class_loader,
                            Handle<mirror::DexCache> hint_dex_cache,
                            VariableSizedHandleScope* handles,
                            bool is_first_run,
@@ -105,6 +106,8 @@
 
   void ValidateTypes();
 
+  Handle<mirror::ClassLoader> class_loader_;
+
   // Note: hint_dex_cache_ is usually, but not necessarily, the dex cache associated with
   // graph_->GetDexFile(). Since we may look up also in other dex files, it's used only
   // as a hint, to reduce the number of calls to the costly ClassLinker::FindDexCache().
diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc
index b061c87..84a4bab 100644
--- a/compiler/optimizing/reference_type_propagation_test.cc
+++ b/compiler/optimizing/reference_type_propagation_test.cc
@@ -38,6 +38,7 @@
   void SetupPropagation(VariableSizedHandleScope* handles) {
     graph_->InitializeInexactObjectRTI(handles);
     propagation_ = new (&allocator_) ReferenceTypePropagation(graph_,
+                                                              Handle<mirror::ClassLoader>(),
                                                               Handle<mirror::DexCache>(),
                                                               handles,
                                                               true,
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index ae1e369..d6edb65 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -497,7 +497,11 @@
 
   // 4) Compute type of reference type instructions. The pass assumes that
   // NullConstant has been fixed up.
-  ReferenceTypePropagation(graph_, dex_cache_, handles_, /* is_first_run */ true).Run();
+  ReferenceTypePropagation(graph_,
+                           class_loader_,
+                           dex_cache_,
+                           handles_,
+                           /* is_first_run */ true).Run();
 
   // 5) HInstructionBuilder duplicated ArrayGet instructions with ambiguous type
   // (int/float or long/double) and marked ArraySets with ambiguous input type.
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index 45dac54..978f113 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -48,9 +48,11 @@
 class SsaBuilder : public ValueObject {
  public:
   SsaBuilder(HGraph* graph,
+             Handle<mirror::ClassLoader> class_loader,
              Handle<mirror::DexCache> dex_cache,
              VariableSizedHandleScope* handles)
       : graph_(graph),
+        class_loader_(class_loader),
         dex_cache_(dex_cache),
         handles_(handles),
         agets_fixed_(false),
@@ -115,6 +117,7 @@
   void RemoveRedundantUninitializedStrings();
 
   HGraph* graph_;
+  Handle<mirror::ClassLoader> class_loader_;
   Handle<mirror::DexCache> dex_cache_;
   VariableSizedHandleScope* const handles_;
 
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index a9a1e6f..1b9bd7e 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -165,7 +165,7 @@
                                                            inline_info_size_,
                                                            register_mask_max_,
                                                            stack_mask_number_of_bits);
-  stack_maps_size_ = stack_maps_.size() * stack_map_size;
+  stack_maps_size_ = RoundUp(stack_maps_.size() * stack_map_size, kBitsPerByte) / kBitsPerByte;
   dex_register_location_catalog_size_ = ComputeDexRegisterLocationCatalogSize();
 
   size_t non_header_size =
@@ -178,7 +178,7 @@
   CodeInfoEncoding code_info_encoding;
   code_info_encoding.non_header_size = non_header_size;
   code_info_encoding.number_of_stack_maps = stack_maps_.size();
-  code_info_encoding.stack_map_size_in_bytes = stack_map_size;
+  code_info_encoding.stack_map_size_in_bits = stack_map_size;
   code_info_encoding.stack_map_encoding = stack_map_encoding_;
   code_info_encoding.inline_info_encoding = inline_info_encoding_;
   code_info_encoding.number_of_location_catalog_entries = location_catalog_entries_.size();
@@ -322,7 +322,7 @@
     stack_map.SetDexPc(stack_map_encoding_, entry.dex_pc);
     stack_map.SetNativePcCodeOffset(stack_map_encoding_, entry.native_pc_code_offset);
     stack_map.SetRegisterMask(stack_map_encoding_, entry.register_mask);
-    size_t number_of_stack_mask_bits = stack_map.GetNumberOfStackMaskBits(stack_map_encoding_);
+    size_t number_of_stack_mask_bits = code_info.GetNumberOfStackMaskBits(encoding);
     if (entry.sp_mask != nullptr) {
       for (size_t bit = 0; bit < number_of_stack_mask_bits; bit++) {
         stack_map.SetStackMaskBit(stack_map_encoding_, bit, entry.sp_mask->IsBitSet(bit));
@@ -551,7 +551,7 @@
               entry.native_pc_code_offset.Uint32Value(instruction_set_));
     DCHECK_EQ(stack_map.GetDexPc(stack_map_encoding), entry.dex_pc);
     DCHECK_EQ(stack_map.GetRegisterMask(stack_map_encoding), entry.register_mask);
-    size_t num_stack_mask_bits = stack_map.GetNumberOfStackMaskBits(stack_map_encoding);
+    size_t num_stack_mask_bits = code_info.GetNumberOfStackMaskBits(encoding);
     if (entry.sp_mask != nullptr) {
       DCHECK_GE(num_stack_mask_bits, entry.sp_mask->GetNumberOfBits());
       for (size_t b = 0; b < num_stack_mask_bits; b++) {
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index f68695b..da4597e 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -27,10 +27,10 @@
 // Check that the stack mask of given stack map is identical
 // to the given bit vector. Returns true if they are same.
 static bool CheckStackMask(
+    int number_of_bits,
     const StackMap& stack_map,
     StackMapEncoding& encoding,
     const BitVector& bit_vector) {
-  int number_of_bits = stack_map.GetNumberOfStackMaskBits(encoding);
   if (bit_vector.GetHighestBitSet() >= number_of_bits) {
     return false;
   }
@@ -81,7 +81,10 @@
   ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA));
   ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding.stack_map_encoding));
 
-  ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask));
+  ASSERT_TRUE(CheckStackMask(code_info.GetNumberOfStackMaskBits(encoding),
+                             stack_map,
+                             encoding.stack_map_encoding,
+                             sp_mask));
 
   ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map_encoding));
   DexRegisterMap dex_register_map =
@@ -196,7 +199,10 @@
     ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA));
     ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding.stack_map_encoding));
 
-    ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask1));
+    ASSERT_TRUE(CheckStackMask(code_info.GetNumberOfStackMaskBits(encoding),
+                               stack_map,
+                               encoding.stack_map_encoding,
+                               sp_mask1));
 
     ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map_encoding));
     DexRegisterMap dex_register_map =
@@ -255,7 +261,10 @@
     ASSERT_EQ(128u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA));
     ASSERT_EQ(0xFFu, stack_map.GetRegisterMask(encoding.stack_map_encoding));
 
-    ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask2));
+    ASSERT_TRUE(CheckStackMask(code_info.GetNumberOfStackMaskBits(encoding),
+                               stack_map,
+                               encoding.stack_map_encoding,
+                               sp_mask2));
 
     ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map_encoding));
     DexRegisterMap dex_register_map =
@@ -309,7 +318,10 @@
     ASSERT_EQ(192u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA));
     ASSERT_EQ(0xABu, stack_map.GetRegisterMask(encoding.stack_map_encoding));
 
-    ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask3));
+    ASSERT_TRUE(CheckStackMask(code_info.GetNumberOfStackMaskBits(encoding),
+                               stack_map,
+                               encoding.stack_map_encoding,
+                               sp_mask3));
 
     ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map_encoding));
     DexRegisterMap dex_register_map =
@@ -363,7 +375,10 @@
     ASSERT_EQ(256u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA));
     ASSERT_EQ(0xCDu, stack_map.GetRegisterMask(encoding.stack_map_encoding));
 
-    ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask4));
+    ASSERT_TRUE(CheckStackMask(code_info.GetNumberOfStackMaskBits(encoding),
+                               stack_map,
+                               encoding.stack_map_encoding,
+                               sp_mask4));
 
     ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map_encoding));
     DexRegisterMap dex_register_map =
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 69901c1..9b4d3e1 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1575,7 +1575,7 @@
           stats_.AddBits(
               Stats::kByteKindStackMapRegisterMask,
               stack_map_encoding.GetRegisterMaskEncoding().BitSize() * num_stack_maps);
-          const size_t stack_mask_bits = encoding.stack_map_size_in_bytes * kBitsPerByte -
+          const size_t stack_mask_bits = encoding.stack_map_size_in_bits -
               stack_map_encoding.GetStackMaskBitOffset();
           stats_.AddBits(
               Stats::kByteKindStackMapMask,
@@ -1584,7 +1584,7 @@
               stack_map_encoding.GetStackMaskBitOffset() + stack_mask_bits;
           stats_.AddBits(
               Stats::kByteKindStackMapOther,
-              (encoding.stack_map_size_in_bytes * kBitsPerByte - stack_map_bits) * num_stack_maps);
+              (encoding.stack_map_size_in_bits - stack_map_bits) * num_stack_maps);
           const size_t stack_map_bytes = helper.GetCodeInfo().GetStackMapsSize(encoding);
           const size_t location_catalog_bytes =
               helper.GetCodeInfo().GetDexRegisterLocationCatalogSize(encoding);
@@ -2180,9 +2180,14 @@
           ScopedIndentation indent2(&state->vios_);
           auto* resolved_types = dex_cache->GetResolvedTypes();
           for (size_t i = 0; i < num_types; ++i) {
-            auto* elem = resolved_types[i].Read();
+            auto pair = resolved_types[i].load(std::memory_order_relaxed);
             size_t run = 0;
-            for (size_t j = i + 1; j != num_types && elem == resolved_types[j].Read(); ++j) {
+            for (size_t j = i + 1; j != num_types; ++j) {
+              auto other_pair = resolved_types[j].load(std::memory_order_relaxed);
+              if (pair.index != other_pair.index ||
+                  pair.object.Read() != other_pair.object.Read()) {
+                break;
+              }
               ++run;
             }
             if (run == 0) {
@@ -2192,12 +2197,13 @@
               i = i + run;
             }
             std::string msg;
+            auto* elem = pair.object.Read();
             if (elem == nullptr) {
               msg = "null";
             } else {
               msg = elem->PrettyClass();
             }
-            os << StringPrintf("%p   %s\n", elem, msg.c_str());
+            os << StringPrintf("%p   %u %s\n", elem, pair.index, msg.c_str());
           }
         }
       }
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 9a73830..2546822 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -643,8 +643,8 @@
     if (orig_strings != nullptr) {
       orig_dex_cache->FixupStrings(RelocatedCopyOf(orig_strings), RelocatedPointerVisitor(this));
     }
-    GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes();
-    GcRoot<mirror::Class>* relocated_types = RelocatedAddressOfPointer(orig_types);
+    mirror::TypeDexCacheType* orig_types = orig_dex_cache->GetResolvedTypes();
+    mirror::TypeDexCacheType* relocated_types = RelocatedAddressOfPointer(orig_types);
     copy_dex_cache->SetField64<false>(
         mirror::DexCache::ResolvedTypesOffset(),
         static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_types)));
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 7f98513..196c65e 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -154,6 +154,7 @@
         "native/java_lang_Thread.cc",
         "native/java_lang_Throwable.cc",
         "native/java_lang_VMClassLoader.cc",
+        "native/java_lang_invoke_MethodHandleImpl.cc",
         "native/java_lang_ref_FinalizerReference.cc",
         "native/java_lang_ref_Reference.cc",
         "native/java_lang_reflect_Array.cc",
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 76218fb..2d5eca0 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1450,316 +1450,83 @@
     move $a2, rSELF                 # pass Thread::Current
 END art_quick_aput_obj
 
-    /*
-     * Called by managed code to resolve a static field and load a boolean primitive value.
-     */
-    .extern artGetBooleanStaticFromCode
-ENTRY art_quick_get_boolean_static
-    lw     $a1, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artGetBooleanStaticFromCode
-    jalr   $t9                           # (uint32_t field_idx, const Method* referrer, Thread*)
-    move   $a2, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_boolean_static
-    /*
-     * Called by managed code to resolve a static field and load a byte primitive value.
-     */
-    .extern artGetByteStaticFromCode
-ENTRY art_quick_get_byte_static
-    lw     $a1, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artGetByteStaticFromCode
-    jalr   $t9                           # (uint32_t field_idx, const Method* referrer, Thread*)
-    move   $a2, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_byte_static
+// Macros taking opportunity of code similarities for downcalls.
+.macro ONE_ARG_REF_DOWNCALL name, entrypoint, return
+    .extern \entrypoint
+ENTRY \name
+    SETUP_SAVE_REFS_ONLY_FRAME        # save callee saves in case of GC
+    la      $t9, \entrypoint
+    jalr    $t9                       # (field_idx, Thread*)
+    move    $a1, rSELF                # pass Thread::Current
+    \return                           # RETURN_IF_NO_EXCEPTION or RETURN_IF_ZERO
+END \name
+.endm
+
+.macro TWO_ARG_REF_DOWNCALL name, entrypoint, return
+    .extern \entrypoint
+ENTRY \name
+    SETUP_SAVE_REFS_ONLY_FRAME        # save callee saves in case of GC
+    la      $t9, \entrypoint
+    jalr    $t9                       # (field_idx, Object*, Thread*) or
+                                      # (field_idx, new_val, Thread*)
+    move    $a2, rSELF                # pass Thread::Current
+    \return                           # RETURN_IF_NO_EXCEPTION or RETURN_IF_ZERO
+END \name
+.endm
+
+.macro THREE_ARG_REF_DOWNCALL name, entrypoint, return
+    .extern \entrypoint
+ENTRY \name
+    SETUP_SAVE_REFS_ONLY_FRAME        # save callee saves in case of GC
+    la      $t9, \entrypoint
+    jalr    $t9                       # (field_idx, Object*, new_val, Thread*)
+    move    $a3, rSELF                # pass Thread::Current
+    \return                           # RETURN_IF_NO_EXCEPTION or RETURN_IF_ZERO
+END \name
+.endm
+
+.macro FOUR_ARG_REF_DOWNCALL name, entrypoint, return
+    .extern \entrypoint
+ENTRY \name
+    SETUP_SAVE_REFS_ONLY_FRAME        # save callee saves in case of GC
+    la      $t9, \entrypoint
+    jalr    $t9                       # (field_idx, Object*, 64-bit new_val, Thread*) or
+                                      # (field_idx, 64-bit new_val, Thread*)
+                                      # Note that a 64-bit new_val needs to be aligned with
+                                      # an even-numbered register, hence A1 may be skipped
+                                      # for new_val to reside in A2-A3.
+    sw      rSELF, 16($sp)            # pass Thread::Current
+    \return                           # RETURN_IF_NO_EXCEPTION or RETURN_IF_ZERO
+END \name
+.endm
 
     /*
-     * Called by managed code to resolve a static field and load a char primitive value.
+     * Called by managed code to resolve a static/instance field and load/store a value.
      */
-    .extern artGetCharStaticFromCode
-ENTRY art_quick_get_char_static
-    lw     $a1, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artGetCharStaticFromCode
-    jalr   $t9                           # (uint32_t field_idx, const Method* referrer, Thread*)
-    move   $a2, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_char_static
-    /*
-     * Called by managed code to resolve a static field and load a short primitive value.
-     */
-    .extern artGetShortStaticFromCode
-ENTRY art_quick_get_short_static
-    lw     $a1, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artGetShortStaticFromCode
-    jalr   $t9                           # (uint32_t field_idx, const Method* referrer, Thread*)
-    move   $a2, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_short_static
-
-    /*
-     * Called by managed code to resolve a static field and load a 32-bit primitive value.
-     */
-    .extern artGet32StaticFromCode
-ENTRY art_quick_get32_static
-    lw     $a1, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artGet32StaticFromCode
-    jalr   $t9                           # (uint32_t field_idx, const Method* referrer, Thread*)
-    move   $a2, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get32_static
-
-    /*
-     * Called by managed code to resolve a static field and load a 64-bit primitive value.
-     */
-    .extern artGet64StaticFromCode
-ENTRY art_quick_get64_static
-    lw     $a1, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artGet64StaticFromCode
-    jalr   $t9                           # (uint32_t field_idx, const Method* referrer, Thread*)
-    move   $a2, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get64_static
-
-    /*
-     * Called by managed code to resolve a static field and load an object reference.
-     */
-    .extern artGetObjStaticFromCode
-ENTRY art_quick_get_obj_static
-    lw     $a1, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artGetObjStaticFromCode
-    jalr   $t9                           # (uint32_t field_idx, const Method* referrer, Thread*)
-    move   $a2, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_obj_static
-
-    /*
-     * Called by managed code to resolve an instance field and load a boolean primitive value.
-     */
-    .extern artGetBooleanInstanceFromCode
-ENTRY art_quick_get_boolean_instance
-    lw     $a2, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artGetBooleanInstanceFromCode
-    jalr   $t9                           # (field_idx, Object*, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_boolean_instance
-    /*
-     * Called by managed code to resolve an instance field and load a byte primitive value.
-     */
-    .extern artGetByteInstanceFromCode
-ENTRY art_quick_get_byte_instance
-    lw     $a2, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artGetByteInstanceFromCode
-    jalr   $t9                           # (field_idx, Object*, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_byte_instance
-
-    /*
-     * Called by managed code to resolve an instance field and load a char primitive value.
-     */
-    .extern artGetCharInstanceFromCode
-ENTRY art_quick_get_char_instance
-    lw     $a2, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artGetCharInstanceFromCode
-    jalr   $t9                           # (field_idx, Object*, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_char_instance
-    /*
-     * Called by managed code to resolve an instance field and load a short primitive value.
-     */
-    .extern artGetShortInstanceFromCode
-ENTRY art_quick_get_short_instance
-    lw     $a2, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artGetShortInstanceFromCode
-    jalr   $t9                           # (field_idx, Object*, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_short_instance
-
-    /*
-     * Called by managed code to resolve an instance field and load a 32-bit primitive value.
-     */
-    .extern artGet32InstanceFromCode
-ENTRY art_quick_get32_instance
-    lw     $a2, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artGet32InstanceFromCode
-    jalr   $t9                           # (field_idx, Object*, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get32_instance
-
-    /*
-     * Called by managed code to resolve an instance field and load a 64-bit primitive value.
-     */
-    .extern artGet64InstanceFromCode
-ENTRY art_quick_get64_instance
-    lw     $a2, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artGet64InstanceFromCode
-    jalr   $t9                           # (field_idx, Object*, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get64_instance
-
-    /*
-     * Called by managed code to resolve an instance field and load an object reference.
-     */
-    .extern artGetObjInstanceFromCode
-ENTRY art_quick_get_obj_instance
-    lw     $a2, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artGetObjInstanceFromCode
-    jalr   $t9                           # (field_idx, Object*, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_obj_instance
-
-    /*
-     * Called by managed code to resolve a static field and store a 8-bit primitive value.
-     */
-    .extern artSet8StaticFromCode
-ENTRY art_quick_set8_static
-    lw     $a2, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artSet8StaticFromCode
-    jalr   $t9                           # (field_idx, new_val, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set8_static
-
-    /*
-     * Called by managed code to resolve a static field and store a 16-bit primitive value.
-     */
-    .extern artSet16StaticFromCode
-ENTRY art_quick_set16_static
-    lw     $a2, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artSet16StaticFromCode
-    jalr   $t9                           # (field_idx, new_val, referrer, Thread*, $sp)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set16_static
-
-    /*
-     * Called by managed code to resolve a static field and store a 32-bit primitive value.
-     */
-    .extern artSet32StaticFromCode
-ENTRY art_quick_set32_static
-    lw     $a2, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artSet32StaticFromCode
-    jalr   $t9                           # (field_idx, new_val, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set32_static
-
-    /*
-     * Called by managed code to resolve a static field and store a 64-bit primitive value.
-     */
-    .extern artSet64StaticFromCode
-ENTRY art_quick_set64_static
-    lw     $a1, 0($sp)                   # pass referrer's Method*
-                                         # 64 bit new_val is in a2:a3 pair
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artSet64StaticFromCode
-    jalr   $t9                           # (field_idx, referrer, new_val, Thread*)
-    sw     rSELF, 16($sp)                # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set64_static
-
-    /*
-     * Called by managed code to resolve a static field and store an object reference.
-     */
-    .extern artSetObjStaticFromCode
-ENTRY art_quick_set_obj_static
-    lw     $a2, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artSetObjStaticFromCode
-    jalr   $t9                           # (field_idx, new_val, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set_obj_static
-
-    /*
-     * Called by managed code to resolve an instance field and store a 8-bit primitive value.
-     */
-    .extern artSet8InstanceFromCode
-ENTRY art_quick_set8_instance
-    lw     $a3, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artSet8InstanceFromCode
-    jalr   $t9                           # (field_idx, Object*, new_val, referrer, Thread*)
-    sw     rSELF, 16($sp)                # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set8_instance
-
-    /*
-     * Called by managed code to resolve an instance field and store a 16-bit primitive value.
-     */
-    .extern artSet16InstanceFromCode
-ENTRY art_quick_set16_instance
-    lw     $a3, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artSet16InstanceFromCode
-    jalr   $t9                           # (field_idx, Object*, new_val, referrer, Thread*)
-    sw     rSELF, 16($sp)                # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set16_instance
-
-    /*
-     * Called by managed code to resolve an instance field and store a 32-bit primitive value.
-     */
-    .extern artSet32InstanceFromCode
-ENTRY art_quick_set32_instance
-    lw     $a3, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artSet32InstanceFromCode
-    jalr   $t9                           # (field_idx, Object*, new_val, referrer, Thread*)
-    sw     rSELF, 16($sp)                # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set32_instance
-
-    /*
-     * Called by managed code to resolve an instance field and store a 64-bit primitive value.
-     */
-    .extern artSet64InstanceFromCode
-ENTRY art_quick_set64_instance
-    lw     $t1, 0($sp)                   # load referrer's Method*
-                                         # 64 bit new_val is in a2:a3 pair
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    sw     rSELF, 20($sp)                # pass Thread::Current
-    la     $t9, artSet64InstanceFromCode
-    jalr   $t9                           # (field_idx, Object*, new_val, referrer, Thread*)
-    sw     $t1, 16($sp)                  # pass referrer's Method*
-    RETURN_IF_ZERO
-END art_quick_set64_instance
-
-    /*
-     * Called by managed code to resolve an instance field and store an object reference.
-     */
-    .extern artSetObjInstanceFromCode
-ENTRY art_quick_set_obj_instance
-    lw     $a3, 0($sp)                   # pass referrer's Method*
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    la     $t9, artSetObjInstanceFromCode
-    jalr   $t9                           # (field_idx, Object*, new_val, referrer, Thread*)
-    sw     rSELF, 16($sp)                # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set_obj_instance
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_IF_NO_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_IF_NO_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_IF_NO_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_IF_NO_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_IF_NO_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_IF_NO_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCompiledCode, RETURN_IF_NO_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_IF_NO_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_IF_NO_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_IF_NO_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_IF_NO_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_IF_NO_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_IF_NO_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCompiledCode, RETURN_IF_NO_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_set8_static, artSet8StaticFromCompiledCode, RETURN_IF_ZERO
+TWO_ARG_REF_DOWNCALL art_quick_set16_static, artSet16StaticFromCompiledCode, RETURN_IF_ZERO
+TWO_ARG_REF_DOWNCALL art_quick_set32_static, artSet32StaticFromCompiledCode, RETURN_IF_ZERO
+TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCompiledCode, RETURN_IF_ZERO
+FOUR_ARG_REF_DOWNCALL art_quick_set64_static, artSet64StaticFromCompiledCode, RETURN_IF_ZERO
+THREE_ARG_REF_DOWNCALL art_quick_set8_instance, artSet8InstanceFromCompiledCode, RETURN_IF_ZERO
+THREE_ARG_REF_DOWNCALL art_quick_set16_instance, artSet16InstanceFromCompiledCode, RETURN_IF_ZERO
+THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCompiledCode, RETURN_IF_ZERO
+THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCompiledCode, RETURN_IF_ZERO
+FOUR_ARG_REF_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCompiledCode, RETURN_IF_ZERO
 
 // Macro to facilitate adding new allocation entrypoints.
 .macro ONE_ARG_DOWNCALL name, entrypoint, return
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index b53fd10..f3629d9 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1416,296 +1416,77 @@
     move   $a2, rSELF               # pass Thread::Current
 END art_quick_aput_obj
 
-    /*
-     * Called by managed code to resolve a static field and load a boolean primitive value.
-     */
-    .extern artGetBooleanStaticFromCode
-ENTRY art_quick_get_boolean_static
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a1, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artGetBooleanStaticFromCode   # (uint32_t field_idx, const Method* referrer, Thread*)
-    move   $a2, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_boolean_static
+// Macros taking opportunity of code similarities for downcalls.
+.macro ONE_ARG_REF_DOWNCALL name, entrypoint, return, extend=0
+    .extern \entrypoint
+ENTRY \name
+    SETUP_SAVE_REFS_ONLY_FRAME        # save callee saves in case of GC
+    dla     $t9, \entrypoint
+    jalr    $t9                       # (field_idx, Thread*)
+    move    $a1, rSELF                # pass Thread::Current
+    .if     \extend
+    sll     $v0, $v0, 0               # sign-extend 32-bit result
+    .endif
+    \return                           # RETURN_IF_NO_EXCEPTION or RETURN_IF_ZERO
+END \name
+.endm
+
+.macro TWO_ARG_REF_DOWNCALL name, entrypoint, return, extend=0
+    .extern \entrypoint
+ENTRY \name
+    SETUP_SAVE_REFS_ONLY_FRAME        # save callee saves in case of GC
+    dla     $t9, \entrypoint
+    jalr    $t9                       # (field_idx, Object*, Thread*) or
+                                      # (field_idx, new_val, Thread*)
+    move    $a2, rSELF                # pass Thread::Current
+    .if     \extend
+    sll     $v0, $v0, 0               # sign-extend 32-bit result
+    .endif
+    \return                           # RETURN_IF_NO_EXCEPTION or RETURN_IF_ZERO
+END \name
+.endm
+
+.macro THREE_ARG_REF_DOWNCALL name, entrypoint, return, extend=0
+    .extern \entrypoint
+ENTRY \name
+    SETUP_SAVE_REFS_ONLY_FRAME        # save callee saves in case of GC
+    dla     $t9, \entrypoint
+    jalr    $t9                       # (field_idx, Object*, new_val, Thread*)
+    move    $a3, rSELF                # pass Thread::Current
+    .if     \extend
+    sll     $v0, $v0, 0               # sign-extend 32-bit result
+    .endif
+    \return                           # RETURN_IF_NO_EXCEPTION or RETURN_IF_ZERO
+END \name
+.endm
 
     /*
-     * Called by managed code to resolve a static field and load a byte primitive value.
+     * Called by managed code to resolve a static/instance field and load/store a value.
      */
-    .extern artGetByteStaticFromCode
-ENTRY art_quick_get_byte_static
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a1, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artGetByteStaticFromCode      # (uint32_t field_idx, const Method* referrer, Thread*)
-    move   $a2, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_byte_static
-
-    /*
-     * Called by managed code to resolve a static field and load a char primitive value.
-     */
-    .extern artGetCharStaticFromCode
-ENTRY art_quick_get_char_static
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a1, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artGetCharStaticFromCode      # (uint32_t field_idx, const Method* referrer, Thread*)
-    move   $a2, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_char_static
-
-    /*
-     * Called by managed code to resolve a static field and load a short primitive value.
-     */
-    .extern artGetShortStaticFromCode
-ENTRY art_quick_get_short_static
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a1, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artGetShortStaticFromCode     # (uint32_t field_idx, const Method* referrer, Thread*)
-    move   $a2, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_short_static
-
-    /*
-     * Called by managed code to resolve a static field and load a 32-bit primitive value.
-     */
-    .extern artGet32StaticFromCode
-ENTRY art_quick_get32_static
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a1, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artGet32StaticFromCode        # (uint32_t field_idx, const Method* referrer, Thread*)
-    move   $a2, rSELF                    # pass Thread::Current
-    sll    $v0, $v0, 0                   # sign-extend result
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get32_static
-
-    /*
-     * Called by managed code to resolve a static field and load a 64-bit primitive value.
-     */
-    .extern artGet64StaticFromCode
-ENTRY art_quick_get64_static
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a1, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artGet64StaticFromCode        # (uint32_t field_idx, const Method* referrer, Thread*)
-    move   $a2, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get64_static
-
-    /*
-     * Called by managed code to resolve a static field and load an object reference.
-     */
-    .extern artGetObjStaticFromCode
-ENTRY art_quick_get_obj_static
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a1, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artGetObjStaticFromCode       # (uint32_t field_idx, const Method* referrer, Thread*)
-    move   $a2, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_obj_static
-
-    /*
-     * Called by managed code to resolve an instance field and load a boolean primitive value.
-     */
-    .extern artGetBooleanInstanceFromCode
-ENTRY art_quick_get_boolean_instance
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a2, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artGetBooleanInstanceFromCode # (field_idx, Object*, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_boolean_instance
-
-    /*
-     * Called by managed code to resolve an instance field and load a byte primitive value.
-     */
-    .extern artGetByteInstanceFromCode
-ENTRY art_quick_get_byte_instance
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a2, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artGetByteInstanceFromCode    # (field_idx, Object*, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_byte_instance
-
-    /*
-     * Called by managed code to resolve an instance field and load a char primitive value.
-     */
-    .extern artGetCharInstanceFromCode
-ENTRY art_quick_get_char_instance
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a2, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artGetCharInstanceFromCode    # (field_idx, Object*, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_char_instance
-
-    /*
-     * Called by managed code to resolve an instance field and load a short primitive value.
-     */
-    .extern artGetShortInstanceFromCode
-ENTRY art_quick_get_short_instance
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a2, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artGetShortInstanceFromCode   # (field_idx, Object*, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_short_instance
-
-    /*
-     * Called by managed code to resolve an instance field and load a 32-bit primitive value.
-     */
-    .extern artGet32InstanceFromCode
-ENTRY art_quick_get32_instance
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a2, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artGet32InstanceFromCode      # (field_idx, Object*, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    sll    $v0, $v0, 0                   # sign-extend result
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get32_instance
-
-    /*
-     * Called by managed code to resolve an instance field and load a 64-bit primitive value.
-     */
-    .extern artGet64InstanceFromCode
-ENTRY art_quick_get64_instance
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a2, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artGet64InstanceFromCode      # (field_idx, Object*, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get64_instance
-
-    /*
-     * Called by managed code to resolve an instance field and load an object reference.
-     */
-    .extern artGetObjInstanceFromCode
-ENTRY art_quick_get_obj_instance
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a2, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artGetObjInstanceFromCode     # (field_idx, Object*, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_NO_EXCEPTION
-END art_quick_get_obj_instance
-
-    /*
-     * Called by managed code to resolve a static field and store a 8-bit primitive value.
-     */
-    .extern artSet8StaticFromCode
-ENTRY art_quick_set8_static
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a2, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artSet8StaticFromCode         # (field_idx, new_val, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set8_static
-
-    /*
-     * Called by managed code to resolve a static field and store a 16-bit primitive value.
-     */
-    .extern artSet16StaticFromCode
-ENTRY art_quick_set16_static
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a2, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artSet16StaticFromCode        # (field_idx, new_val, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set16_static
-
-    /*
-     * Called by managed code to resolve a static field and store a 32-bit primitive value.
-     */
-    .extern artSet32StaticFromCode
-ENTRY art_quick_set32_static
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a2, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artSet32StaticFromCode        # (field_idx, new_val, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set32_static
-
-    /*
-     * Called by managed code to resolve a static field and store a 64-bit primitive value.
-     */
-    .extern artSet64StaticFromCode
-ENTRY art_quick_set64_static
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-                                         # a2 contains the new val
-    ld     $a1, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artSet64StaticFromCode        # (field_idx, referrer, new_val, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set64_static
-
-    /*
-     * Called by managed code to resolve a static field and store an object reference.
-     */
-    .extern artSetObjStaticFromCode
-ENTRY art_quick_set_obj_static
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a2, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artSetObjStaticFromCode       # (field_idx, new_val, referrer, Thread*)
-    move   $a3, rSELF                    # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set_obj_static
-
-    /*
-     * Called by managed code to resolve an instance field and store a 8-bit primitive value.
-     */
-    .extern artSet8InstanceFromCode
-ENTRY art_quick_set8_instance
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a3, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artSet8InstanceFromCode       # (field_idx, Object*, new_val, referrer, Thread*)
-    move   $a4, rSELF                    # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set8_instance
-
-    /*
-     * Called by managed code to resolve an instance field and store a 16-bit primitive value.
-     */
-    .extern artSet16InstanceFromCode
-ENTRY art_quick_set16_instance
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a3, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artSet16InstanceFromCode      # (field_idx, Object*, new_val, referrer, Thread*)
-    move   $a4, rSELF                    # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set16_instance
-
-    /*
-     * Called by managed code to resolve an instance field and store a 32-bit primitive value.
-     */
-    .extern artSet32InstanceFromCode
-ENTRY art_quick_set32_instance
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a3, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artSet32InstanceFromCode      # (field_idx, Object*, new_val, referrer, Thread*)
-    move   $a4, rSELF                    # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set32_instance
-
-    /*
-     * Called by managed code to resolve an instance field and store a 64-bit primitive value.
-     */
-    .extern artSet64InstanceFromCode
-ENTRY art_quick_set64_instance
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a3, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artSet64InstanceFromCode      # (field_idx, Object*, new_val, referrer, Thread*)
-    move   $a4, rSELF                    # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set64_instance
-
-    /*
-     * Called by managed code to resolve an instance field and store an object reference.
-     */
-    .extern artSetObjInstanceFromCode
-ENTRY art_quick_set_obj_instance
-    SETUP_SAVE_REFS_ONLY_FRAME           # save callee saves in case of GC
-    ld     $a3, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
-    jal    artSetObjInstanceFromCode     # (field_idx, Object*, new_val, referrer, Thread*)
-    move   $a4, rSELF                    # pass Thread::Current
-    RETURN_IF_ZERO
-END art_quick_set_obj_instance
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_IF_NO_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_IF_NO_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_IF_NO_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_IF_NO_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_IF_NO_EXCEPTION, 1
+ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_IF_NO_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCompiledCode, RETURN_IF_NO_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_IF_NO_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_IF_NO_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_IF_NO_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_IF_NO_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_IF_NO_EXCEPTION, 1
+TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_IF_NO_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCompiledCode, RETURN_IF_NO_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_set8_static, artSet8StaticFromCompiledCode, RETURN_IF_ZERO
+TWO_ARG_REF_DOWNCALL art_quick_set16_static, artSet16StaticFromCompiledCode, RETURN_IF_ZERO
+TWO_ARG_REF_DOWNCALL art_quick_set32_static, artSet32StaticFromCompiledCode, RETURN_IF_ZERO
+TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCompiledCode, RETURN_IF_ZERO
+TWO_ARG_REF_DOWNCALL art_quick_set64_static, artSet64StaticFromCompiledCode, RETURN_IF_ZERO
+THREE_ARG_REF_DOWNCALL art_quick_set8_instance, artSet8InstanceFromCompiledCode, RETURN_IF_ZERO
+THREE_ARG_REF_DOWNCALL art_quick_set16_instance, artSet16InstanceFromCompiledCode, RETURN_IF_ZERO
+THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCompiledCode, RETURN_IF_ZERO
+THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCompiledCode, RETURN_IF_ZERO
+THREE_ARG_REF_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCompiledCode, RETURN_IF_ZERO
 
 // Macro to facilitate adding new allocation entrypoints.
 .macro ONE_ARG_DOWNCALL name, entrypoint, return
diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h
index 80af8e7..16b73c6 100644
--- a/runtime/art_field-inl.h
+++ b/runtime/art_field-inl.h
@@ -311,6 +311,8 @@
 
 template <bool kResolve>
 inline ObjPtr<mirror::Class> ArtField::GetType() {
+  // TODO: Refactor this function into two functions, ResolveType() and LookupType()
+  // so that we can properly annotate it with no-suspension possible / suspension possible.
   const uint32_t field_index = GetDexFieldIndex();
   ObjPtr<mirror::Class> declaring_class = GetDeclaringClass();
   if (UNLIKELY(declaring_class->IsProxyClass())) {
@@ -320,9 +322,16 @@
   const DexFile* const dex_file = dex_cache->GetDexFile();
   const DexFile::FieldId& field_id = dex_file->GetFieldId(field_index);
   ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(field_id.type_idx_);
-  if (kResolve && UNLIKELY(type == nullptr)) {
-    type = ResolveGetType(field_id.type_idx_);
-    CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
+  if (UNLIKELY(type == nullptr)) {
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    if (kResolve) {
+      type = class_linker->ResolveType(*dex_file, field_id.type_idx_, declaring_class);
+      CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
+    } else {
+      type = class_linker->LookupResolvedType(
+          *dex_file, field_id.type_idx_, dex_cache, declaring_class->GetClassLoader());
+      DCHECK(!Thread::Current()->IsExceptionPending());
+    }
   }
   return type;
 }
diff --git a/runtime/art_field.cc b/runtime/art_field.cc
index a4a6e5a..7e13104 100644
--- a/runtime/art_field.cc
+++ b/runtime/art_field.cc
@@ -48,10 +48,6 @@
   return Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), descriptor);
 }
 
-ObjPtr<mirror::Class> ArtField::ResolveGetType(dex::TypeIndex type_idx) {
-  return Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this);
-}
-
 ObjPtr<mirror::String> ArtField::ResolveGetStringName(Thread* self,
                                                       const DexFile& dex_file,
                                                       dex::StringIndex string_idx,
diff --git a/runtime/art_field.h b/runtime/art_field.h
index 427e103..75dd981 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -217,8 +217,6 @@
  private:
   ObjPtr<mirror::Class> ProxyFindSystemClass(const char* descriptor)
       REQUIRES_SHARED(Locks::mutator_lock_);
-  ObjPtr<mirror::Class> ResolveGetType(dex::TypeIndex type_idx)
-      REQUIRES_SHARED(Locks::mutator_lock_);
   ObjPtr<mirror::String> ResolveGetStringName(Thread* self,
                                               const DexFile& dex_file,
                                               dex::StringIndex string_idx,
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index a35c7ab..efcdbbf 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -175,12 +175,19 @@
 }
 
 inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) {
+  // TODO: Refactor this function into two functions, Resolve...() and Lookup...()
+  // so that we can properly annotate it with no-suspension possible / suspension possible.
   ObjPtr<mirror::DexCache> dex_cache = GetDexCache();
   ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
-  if (UNLIKELY(type == nullptr) && resolve) {
+  if (UNLIKELY(type == nullptr)) {
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    type = class_linker->ResolveType(type_idx, this);
-    CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
+    if (resolve) {
+      type = class_linker->ResolveType(type_idx, this);
+      CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
+    } else {
+      type = class_linker->LookupResolvedType(
+          *dex_cache->GetDexFile(), type_idx, dex_cache, GetClassLoader());
+    }
   }
   return type.Ptr();
 }
@@ -244,7 +251,9 @@
 }
 
 inline const DexFile* ArtMethod::GetDexFile() {
-  return GetDexCache()->GetDexFile();
+  // It is safe to avoid the read barrier here since the dex file is constant, so if we read the
+  // from-space dex file pointer it will be equal to the to-space copy.
+  return GetDexCache<kWithoutReadBarrier>()->GetDexFile();
 }
 
 inline const char* ArtMethod::GetDeclaringClassDescriptor() {
@@ -361,9 +370,11 @@
   return GetDeclaringClass()->GetClassLoader();
 }
 
+template <ReadBarrierOption kReadBarrierOption>
 inline mirror::DexCache* ArtMethod::GetDexCache() {
   if (LIKELY(!IsObsolete())) {
-    return GetDeclaringClass()->GetDexCache();
+    mirror::Class* klass = GetDeclaringClass<kReadBarrierOption>();
+    return klass->GetDexCache<kDefaultVerifyFlags, kReadBarrierOption>();
   } else {
     DCHECK(!IsProxyMethod());
     return GetObsoleteDexCache();
@@ -379,14 +390,13 @@
   if (LIKELY(!IsProxyMethod())) {
     return this;
   }
-  mirror::Class* klass = GetDeclaringClass();
   ArtMethod* interface_method = mirror::DexCache::GetElementPtrSize(
       GetDexCacheResolvedMethods(pointer_size),
       GetDexMethodIndex(),
       pointer_size);
   DCHECK(interface_method != nullptr);
   DCHECK_EQ(interface_method,
-            Runtime::Current()->GetClassLinker()->FindMethodForProxy(klass, this));
+            Runtime::Current()->GetClassLinker()->FindMethodForProxy(GetDeclaringClass(), this));
   return interface_method;
 }
 
diff --git a/runtime/art_method.h b/runtime/art_method.h
index a33111a..f145d7c 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -563,6 +563,7 @@
 
   mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_);
 
+  template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   mirror::DexCache* GetDexCache() REQUIRES_SHARED(Locks::mutator_lock_);
   mirror::DexCache* GetObsoleteDexCache() REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/bit_memory_region.h b/runtime/bit_memory_region.h
new file mode 100644
index 0000000..90a1981
--- /dev/null
+++ b/runtime/bit_memory_region.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_BIT_MEMORY_REGION_H_
+#define ART_RUNTIME_BIT_MEMORY_REGION_H_
+
+#include "memory_region.h"
+
+namespace art {
+
+// Bit memory region is a bit offset subregion of a normal memoryregion. This is useful for
+// abstracting away the bit start offset to avoid needing passing as an argument everywhere.
+class BitMemoryRegion FINAL : public ValueObject {
+ public:
+  BitMemoryRegion() = default;
+  BitMemoryRegion(MemoryRegion region, size_t bit_offset, size_t bit_size) {
+    bit_start_ = bit_offset % kBitsPerByte;
+    const size_t start = bit_offset / kBitsPerByte;
+    const size_t end = (bit_offset + bit_size + kBitsPerByte - 1) / kBitsPerByte;
+    region_ = region.Subregion(start, end - start);
+  }
+
+  void* pointer() const { return region_.pointer(); }
+  size_t size() const { return region_.size(); }
+  size_t BitOffset() const { return bit_start_; }
+  size_t size_in_bits() const {
+    return region_.size_in_bits();
+  }
+
+  // Load a single bit in the region. The bit at offset 0 is the least
+  // significant bit in the first byte.
+  ALWAYS_INLINE bool LoadBit(uintptr_t bit_offset) const {
+    return region_.LoadBit(bit_offset + bit_start_);
+  }
+
+  ALWAYS_INLINE void StoreBit(uintptr_t bit_offset, bool value) const {
+    region_.StoreBit(bit_offset + bit_start_, value);
+  }
+
+  ALWAYS_INLINE uint32_t LoadBits(uintptr_t bit_offset, size_t length) const {
+    return region_.LoadBits(bit_offset + bit_start_, length);
+  }
+
+  // Store at a bit offset from inside the bit memory region.
+  ALWAYS_INLINE void StoreBits(uintptr_t bit_offset, uint32_t value, size_t length) {
+    region_.StoreBits(bit_offset + bit_start_, value, length);
+  }
+
+ private:
+  MemoryRegion region_;
+  size_t bit_start_ = 0;
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_BIT_MEMORY_REGION_H_
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 2e17dd8..65178ed 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -68,22 +68,28 @@
 inline mirror::String* ClassLinker::ResolveString(dex::StringIndex string_idx,
                                                   ArtMethod* referrer) {
   Thread::PoisonObjectPointersIfDebug();
-  ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
-  // MethodVerifier refuses methods with string_idx out of bounds.
-  DCHECK_LT(string_idx.index_, declaring_class->GetDexFile().NumStringIds());
-  ObjPtr<mirror::String> string =
-        mirror::StringDexCachePair::Lookup(declaring_class->GetDexCache()->GetStrings(),
-                                           string_idx.index_,
-                                           mirror::DexCache::kDexCacheStringCacheSize).Read();
+  ObjPtr<mirror::String> string = referrer->GetDexCache()->GetResolvedString(string_idx);
   if (UNLIKELY(string == nullptr)) {
     StackHandleScope<1> hs(Thread::Current());
-    Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
+    Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
     const DexFile& dex_file = *dex_cache->GetDexFile();
     string = ResolveString(dex_file, string_idx, dex_cache);
   }
   return string.Ptr();
 }
 
+inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(
+    dex::TypeIndex type_idx,
+    ObjPtr<mirror::DexCache> dex_cache,
+    ObjPtr<mirror::ClassLoader> class_loader) {
+  ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
+  if (type == nullptr) {
+    type = Runtime::Current()->GetClassLinker()->LookupResolvedType(
+        *dex_cache->GetDexFile(), type_idx, dex_cache, class_loader);
+  }
+  return type;
+}
+
 inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) {
   Thread::PoisonObjectPointersIfDebug();
   if (kIsDebugBuild) {
@@ -97,25 +103,6 @@
     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();
-}
-
-inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtField* referrer) {
-  Thread::PoisonObjectPointersIfDebug();
-  ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
-  ObjPtr<mirror::DexCache> dex_cache_ptr = declaring_class->GetDexCache();
-  ObjPtr<mirror::Class> resolved_type = dex_cache_ptr->GetResolvedType(type_idx);
-  if (UNLIKELY(resolved_type == nullptr)) {
-    StackHandleScope<2> hs(Thread::Current());
-    Handle<mirror::DexCache> dex_cache(hs.NewHandle(dex_cache_ptr));
-    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();
 }
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index edd6e3b..8669367 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1171,6 +1171,23 @@
   }
 }
 
+template <typename T>
+static void CopyDexCachePairs(const std::atomic<mirror::DexCachePair<T>>* src,
+                              size_t count,
+                              std::atomic<mirror::DexCachePair<T>>* dst) {
+  DCHECK_NE(count, 0u);
+  DCHECK(!src[0].load(std::memory_order_relaxed).object.IsNull() ||
+         src[0].load(std::memory_order_relaxed).index != 0u);
+  for (size_t i = 0; i < count; ++i) {
+    DCHECK_EQ(dst[i].load(std::memory_order_relaxed).index, 0u);
+    DCHECK(dst[i].load(std::memory_order_relaxed).object.IsNull());
+    mirror::DexCachePair<T> source = src[i].load(std::memory_order_relaxed);
+    if (source.index != 0u || !source.object.IsNull()) {
+      dst[i].store(source, std::memory_order_relaxed);
+    }
+  }
+}
+
 bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
     gc::space::ImageSpace* space,
     Handle<mirror::ClassLoader> class_loader,
@@ -1224,7 +1241,10 @@
         if (dex_file->NumStringIds() < num_strings) {
           num_strings = dex_file->NumStringIds();
         }
-        const size_t num_types = dex_file->NumTypeIds();
+        size_t num_types = mirror::DexCache::kDexCacheTypeCacheSize;
+        if (dex_file->NumTypeIds() < num_types) {
+          num_types = dex_file->NumTypeIds();
+        }
         const size_t num_methods = dex_file->NumMethodIds();
         const size_t num_fields = dex_file->NumFieldIds();
         size_t num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize;
@@ -1243,28 +1263,14 @@
           mirror::StringDexCacheType* const image_resolved_strings = dex_cache->GetStrings();
           mirror::StringDexCacheType* const strings =
               reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset());
-          for (size_t j = 0; j < num_strings; ++j) {
-            DCHECK_EQ(strings[j].load(std::memory_order_relaxed).index, 0u);
-            DCHECK(strings[j].load(std::memory_order_relaxed).object.IsNull());
-            strings[j].store(image_resolved_strings[j].load(std::memory_order_relaxed),
-                             std::memory_order_relaxed);
-          }
-          mirror::StringDexCachePair::Initialize(strings);
+          CopyDexCachePairs(image_resolved_strings, num_strings, strings);
           dex_cache->SetStrings(strings);
         }
         if (num_types != 0u) {
-          GcRoot<mirror::Class>* const image_resolved_types = dex_cache->GetResolvedTypes();
-          GcRoot<mirror::Class>* const types =
-              reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset());
-          for (size_t j = 0; kIsDebugBuild && j < num_types; ++j) {
-            DCHECK(types[j].IsNull());
-          }
-          CopyNonNull(image_resolved_types,
-                      num_types,
-                      types,
-                      [](const GcRoot<mirror::Class>& elem) {
-                          return elem.IsNull();
-                      });
+          mirror::TypeDexCacheType* const image_resolved_types = dex_cache->GetResolvedTypes();
+          mirror::TypeDexCacheType* const types =
+              reinterpret_cast<mirror::TypeDexCacheType*>(raw_arrays + layout.TypesOffset());
+          CopyDexCachePairs(image_resolved_types, num_types, types);
           dex_cache->SetResolvedTypes(types);
         }
         if (num_methods != 0u) {
@@ -1305,15 +1311,7 @@
           mirror::MethodTypeDexCacheType* const method_types =
               reinterpret_cast<mirror::MethodTypeDexCacheType*>(
                   raw_arrays + layout.MethodTypesOffset());
-          for (size_t j = 0; j < num_method_types; ++j) {
-            DCHECK_EQ(method_types[j].load(std::memory_order_relaxed).index, 0u);
-            DCHECK(method_types[j].load(std::memory_order_relaxed).object.IsNull());
-            method_types[j].store(
-                image_resolved_method_types[j].load(std::memory_order_relaxed),
-                std::memory_order_relaxed);
-          }
-
-          mirror::MethodTypeDexCachePair::Initialize(method_types);
+          CopyDexCachePairs(image_resolved_method_types, num_method_types, method_types);
           dex_cache->SetResolvedMethodTypes(method_types);
         }
       }
@@ -1335,11 +1333,11 @@
       }
       if (kIsDebugBuild) {
         CHECK(new_class_set != nullptr);
-        GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes();
+        mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
         const size_t num_types = dex_cache->NumResolvedTypes();
-        for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) {
+        for (size_t j = 0; j != num_types; ++j) {
           // The image space is not yet added to the heap, avoid read barriers.
-          ObjPtr<mirror::Class> klass = types[j].Read();
+          ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read();
           if (space->HasAddress(klass.Ptr())) {
             DCHECK(!klass->IsErroneous()) << klass->GetStatus();
             auto it = new_class_set->Find(ClassTable::TableSlot(klass));
@@ -1700,9 +1698,9 @@
       // The current dex file field is bogus, overwrite it so that we can get the dex file in the
       // loop below.
       h_dex_cache->SetDexFile(dex_file.get());
-      GcRoot<mirror::Class>* const types = h_dex_cache->GetResolvedTypes();
+      mirror::TypeDexCacheType* const types = h_dex_cache->GetResolvedTypes();
       for (int32_t j = 0, num_types = h_dex_cache->NumResolvedTypes(); j < num_types; j++) {
-        ObjPtr<mirror::Class> klass = types[j].Read();
+        ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read();
         if (klass != nullptr) {
           DCHECK(!klass->IsErroneous()) << klass->GetStatus();
         }
@@ -7698,7 +7696,9 @@
   uint32_t utf16_length;
   const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length);
   ObjPtr<mirror::String> string = intern_table_->InternStrong(utf16_length, utf8_data);
-  dex_cache->SetResolvedString(string_idx, string);
+  if (string != nullptr) {
+    dex_cache->SetResolvedString(string_idx, string);
+  }
   return string.Ptr();
 }
 
@@ -7741,6 +7741,7 @@
     }
   }
   if (type != nullptr && type->IsResolved()) {
+    dex_cache->SetResolvedType(type_idx, type);
     return type.Ptr();
   }
   return nullptr;
@@ -7763,6 +7764,12 @@
   Thread::PoisonObjectPointersIfDebug();
   ObjPtr<mirror::Class> resolved = dex_cache->GetResolvedType(type_idx);
   if (resolved == nullptr) {
+    // TODO: Avoid this lookup as it duplicates work done in FindClass(). It is here
+    // as a workaround for FastNative JNI to avoid AssertNoPendingException() when
+    // trying to resolve annotations while an exception may be pending. Bug: 34659969
+    resolved = LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get());
+  }
+  if (resolved == nullptr) {
     Thread* self = Thread::Current();
     const char* descriptor = dex_file.StringByTypeIdx(type_idx);
     resolved = FindClass(self, descriptor, class_loader);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 5042fb7..21edd51 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -262,10 +262,6 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
 
-  mirror::Class* ResolveType(dex::TypeIndex type_idx, ArtField* referrer)
-      REQUIRES_SHARED(Locks::mutator_lock_)
-      REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
-
   // Look up a resolved type with the given ID from the DexFile. The ClassLoader is used to search
   // for the type, since it may be referenced from but not contained within the given DexFile.
   ObjPtr<mirror::Class> LookupResolvedType(const DexFile& dex_file,
@@ -273,6 +269,10 @@
                                            ObjPtr<mirror::DexCache> dex_cache,
                                            ObjPtr<mirror::ClassLoader> class_loader)
       REQUIRES_SHARED(Locks::mutator_lock_);
+  static ObjPtr<mirror::Class> LookupResolvedType(dex::TypeIndex type_idx,
+                                                  ObjPtr<mirror::DexCache> dex_cache,
+                                                  ObjPtr<mirror::ClassLoader> class_loader)
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Resolve a type with the given ID from the DexFile, storing the
   // result in DexCache. The ClassLoader is used to search for the
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index e806e7d..6eee0bd 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -743,15 +743,22 @@
   }
 };
 
+struct MethodHandleOffsets : public CheckOffsets<mirror::MethodHandle> {
+  MethodHandleOffsets() : CheckOffsets<mirror::MethodHandle>(
+      false, "Ljava/lang/invoke/MethodHandle;") {
+    addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, art_field_or_method_), "artFieldOrMethod");
+    addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, cached_spread_invoker_),
+              "cachedSpreadInvoker");
+    addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, handle_kind_), "handleKind");
+    addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, nominal_type_), "nominalType");
+    addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, method_type_), "type");
+  }
+};
+
 struct MethodHandleImplOffsets : public CheckOffsets<mirror::MethodHandleImpl> {
   MethodHandleImplOffsets() : CheckOffsets<mirror::MethodHandleImpl>(
-      false, "Ljava/lang/invoke/MethodHandle;") {
-    addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, art_field_or_method_), "artFieldOrMethod");
-    addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, cached_spread_invoker_),
-              "cachedSpreadInvoker");
-    addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, handle_kind_), "handleKind");
-    addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, nominal_type_), "nominalType");
-    addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, method_type_), "type");
+      false, "Ljava/lang/invoke/MethodHandleImpl;") {
+    addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, info_), "info");
   }
 };
 
@@ -785,6 +792,7 @@
   EXPECT_TRUE(FieldOffsets().Check());
   EXPECT_TRUE(ExecutableOffsets().Check());
   EXPECT_TRUE(MethodTypeOffsets().Check());
+  EXPECT_TRUE(MethodHandleOffsets().Check());
   EXPECT_TRUE(MethodHandleImplOffsets().Check());
   EXPECT_TRUE(EmulatedStackFrameOffsets().Check());
 }
@@ -906,7 +914,7 @@
       class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()),
       klass);
   // Zero out the resolved type and make sure LookupResolvedType still finds it.
-  dex_cache->SetResolvedType(type_idx, nullptr);
+  dex_cache->ClearResolvedType(type_idx);
   EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr);
   EXPECT_OBJ_PTR_EQ(
       class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()),
@@ -941,7 +949,7 @@
       class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()),
       array_klass);
   // Zero out the resolved type and make sure LookupResolvedType() still finds it.
-  dex_cache->SetResolvedType(array_idx, nullptr);
+  dex_cache->ClearResolvedType(array_idx);
   EXPECT_TRUE(dex_cache->GetResolvedType(array_idx) == nullptr);
   EXPECT_OBJ_PTR_EQ(
       class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()),
@@ -964,7 +972,7 @@
       class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
       klass.Get());
   // Zero out the resolved type and make sure LookupResolvedType still finds it.
-  dex_cache->SetResolvedType(type_idx, nullptr);
+  dex_cache->ClearResolvedType(type_idx);
   EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr);
   EXPECT_OBJ_PTR_EQ(
       class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
@@ -982,7 +990,7 @@
       class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
       klass.Get());
   // Zero out the resolved type and make sure LookupResolvedType() still finds it.
-  dex_cache->SetResolvedType(type_idx, nullptr);
+  dex_cache->ClearResolvedType(type_idx);
   EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr);
   EXPECT_OBJ_PTR_EQ(
       class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index ac0ce36..1b267eb 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -705,10 +705,10 @@
     return resolved_method;
   } else if (type == kSuper) {
     // TODO This lookup is rather slow.
-    dex::TypeIndex method_type_idx =
-        referrer->GetDexFile()->GetMethodId(method_idx).class_idx_;
-    mirror::Class* method_reference_class =
-        referrer->GetDexCache()->GetResolvedType(method_type_idx);
+    ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache();
+    dex::TypeIndex method_type_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
+    ObjPtr<mirror::Class> method_reference_class = ClassLinker::LookupResolvedType(
+        method_type_idx, dex_cache, referrer->GetClassLoader());
     if (method_reference_class == nullptr) {
       // Need to do full type resolution...
       return nullptr;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index e03958d..e56f0dc 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1225,9 +1225,9 @@
           }
           dex_cache->FixupStrings<kWithoutReadBarrier>(new_strings, fixup_adapter);
         }
-        GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes();
+        mirror::TypeDexCacheType* types = dex_cache->GetResolvedTypes();
         if (types != nullptr) {
-          GcRoot<mirror::Class>* new_types = fixup_adapter.ForwardObject(types);
+          mirror::TypeDexCacheType* new_types = fixup_adapter.ForwardObject(types);
           if (types != new_types) {
             dex_cache->SetResolvedTypes(new_types);
           }
diff --git a/runtime/image.cc b/runtime/image.cc
index 54b099e..87f4295 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -25,7 +25,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '6', '\0' };  // Erroneous resolved class.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '7', '\0' };  // hash-based DexCache types
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 28bcb97..c235317 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -438,14 +438,22 @@
 // about ALWAYS_INLINE (-Werror, -Wgcc-compat) in definitions.
 //
 
+// b/30419309
+#if defined(__i386__)
+#define IF_X86_OPTNONE_ELSE_ALWAYS_INLINE __attribute__((optnone))
+#else
+#define IF_X86_OPTNONE_ELSE_ALWAYS_INLINE ALWAYS_INLINE
+#endif
+
 template <bool is_range, bool do_assignability_check>
-static ALWAYS_INLINE bool DoCallCommon(ArtMethod* called_method,
-                                       Thread* self,
-                                       ShadowFrame& shadow_frame,
-                                       JValue* result,
-                                       uint16_t number_of_inputs,
-                                       uint32_t (&arg)[Instruction::kMaxVarArgRegs],
-                                       uint32_t vregC) REQUIRES_SHARED(Locks::mutator_lock_);
+IF_X86_OPTNONE_ELSE_ALWAYS_INLINE
+static bool DoCallCommon(ArtMethod* called_method,
+                         Thread* self,
+                         ShadowFrame& shadow_frame,
+                         JValue* result,
+                         uint16_t number_of_inputs,
+                         uint32_t (&arg)[Instruction::kMaxVarArgRegs],
+                         uint32_t vregC) REQUIRES_SHARED(Locks::mutator_lock_);
 
 template <bool is_range>
 ALWAYS_INLINE void CopyRegisters(ShadowFrame& caller_frame,
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index aeb438f..7ef3508 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -255,17 +255,11 @@
     }
   }
   ArtMethod* method = shadow_frame.GetMethod();
-  // MethodVerifier refuses methods with string_idx out of bounds.
-  DCHECK_LT(string_idx.index_ % mirror::DexCache::kDexCacheStringCacheSize,
-            method->GetDexFile()->NumStringIds());
-  ObjPtr<mirror::String> string_ptr =
-      mirror::StringDexCachePair::Lookup(method->GetDexCache()->GetStrings(),
-                                         string_idx.index_,
-                                         mirror::DexCache::kDexCacheStringCacheSize).Read();
+  ObjPtr<mirror::String> string_ptr = method->GetDexCache()->GetResolvedString(string_idx);
   if (UNLIKELY(string_ptr == nullptr)) {
     StackHandleScope<1> hs(self);
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
-    string_ptr = Runtime::Current()->GetClassLinker()->ResolveString(*method->GetDexFile(),
+    string_ptr = Runtime::Current()->GetClassLinker()->ResolveString(*dex_cache->GetDexFile(),
                                                                      string_idx,
                                                                      dex_cache);
   }
diff --git a/runtime/memory_region.cc b/runtime/memory_region.cc
index 5bf0f40..b0ecab4 100644
--- a/runtime/memory_region.cc
+++ b/runtime/memory_region.cc
@@ -43,8 +43,8 @@
   // Bits are stored in this order {7 6 5 4 3 2 1 0}.
   // How many remaining bits in current byte is (bit_offset % kBitsPerByte) + 1.
   uint8_t* out = ComputeInternalPointer<uint8_t>(bit_offset >> kBitsPerByteLog2);
-  auto orig_len = length;
-  auto orig_value = value;
+  size_t orig_len = length;
+  uint32_t orig_value = value;
   uintptr_t bit_remainder = bit_offset % kBitsPerByte;
   while (true) {
     const uintptr_t remaining_bits = kBitsPerByte - bit_remainder;
diff --git a/runtime/memory_region_test.cc b/runtime/memory_region_test.cc
index 72e03a4..6634c60 100644
--- a/runtime/memory_region_test.cc
+++ b/runtime/memory_region_test.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "bit_memory_region.h"
 #include "memory_region.h"
 
 #include "gtest/gtest.h"
@@ -55,4 +56,35 @@
   }
 }
 
+TEST(MemoryRegion, TestBits) {
+  const size_t n = 8;
+  uint8_t data[n] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+  MemoryRegion region(&data, n);
+  uint32_t value = 0xDEADBEEF;
+  // Try various offsets and lengths.
+  for (size_t bit_offset = 0; bit_offset < 2 * kBitsPerByte; ++bit_offset) {
+    for (size_t length = 0; length < 2 * kBitsPerByte; ++length) {
+      const uint32_t length_mask = (1 << length) - 1;
+      uint32_t masked_value = value & length_mask;
+      BitMemoryRegion bmr(region, bit_offset, length);
+      region.StoreBits(bit_offset, masked_value, length);
+      EXPECT_EQ(region.LoadBits(bit_offset, length), masked_value);
+      EXPECT_EQ(bmr.LoadBits(0, length), masked_value);
+      // Check adjacent bits to make sure they were not incorrectly cleared.
+      EXPECT_EQ(region.LoadBits(0, bit_offset), (1u << bit_offset) - 1);
+      EXPECT_EQ(region.LoadBits(bit_offset + length, length), length_mask);
+      region.StoreBits(bit_offset, length_mask, length);
+      // Store with bit memory region.
+      bmr.StoreBits(0, masked_value, length);
+      EXPECT_EQ(bmr.LoadBits(0, length), masked_value);
+      // Check adjacent bits to make sure they were not incorrectly cleared.
+      EXPECT_EQ(region.LoadBits(0, bit_offset), (1u << bit_offset) - 1);
+      EXPECT_EQ(region.LoadBits(bit_offset + length, length), length_mask);
+      region.StoreBits(bit_offset, length_mask, length);
+      // Flip the value to try different edge bit combinations.
+      value = ~value;
+    }
+  }
+}
+
 }  // namespace art
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 2fb8d28..6a65e12 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -71,9 +71,10 @@
       OFFSET_OF_OBJECT_MEMBER(Class, class_loader_));
 }
 
-template<VerifyObjectFlags kVerifyFlags>
+template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
 inline DexCache* Class::GetDexCache() {
-  return GetFieldObject<DexCache, kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_));
+  return GetFieldObject<DexCache, kVerifyFlags, kReadBarrierOption>(
+      OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_));
 }
 
 inline uint32_t Class::GetCopiedMethodsStartOffset() {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index f08d4da..85636fb 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -951,7 +951,8 @@
     return interfaces->Get(idx);
   } else {
     dex::TypeIndex type_idx = klass->GetDirectInterfaceTypeIdx(idx);
-    ObjPtr<Class> interface = klass->GetDexCache()->GetResolvedType(type_idx);
+    ObjPtr<Class> interface = ClassLinker::LookupResolvedType(
+        type_idx, klass->GetDexCache(), klass->GetClassLoader());
     return interface;
   }
 }
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 7f6aa12..c9f27ad 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -722,7 +722,8 @@
 
   void DumpClass(std::ostream& os, int flags) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
+           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   DexCache* GetDexCache() REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Also updates the dex_cache_strings_ variable from new_dex_cache.
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index a59bb7b..bef3ad2 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -40,14 +40,22 @@
   return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0, pointer_size);
 }
 
-inline mirror::String* DexCache::GetResolvedString(dex::StringIndex string_idx) {
+inline uint32_t DexCache::StringSlotIndex(dex::StringIndex string_idx) {
   DCHECK_LT(string_idx.index_, GetDexFile()->NumStringIds());
-  return StringDexCachePair::Lookup(GetStrings(), string_idx.index_, NumStrings()).Read();
+  const uint32_t slot_idx = string_idx.index_ % kDexCacheStringCacheSize;
+  DCHECK_LT(slot_idx, NumStrings());
+  return slot_idx;
 }
 
-inline void DexCache::SetResolvedString(dex::StringIndex string_idx,
-                                        ObjPtr<mirror::String> resolved) {
-  StringDexCachePair::Assign(GetStrings(), string_idx.index_, resolved.Ptr(), NumStrings());
+inline String* DexCache::GetResolvedString(dex::StringIndex string_idx) {
+  return GetStrings()[StringSlotIndex(string_idx)].load(
+      std::memory_order_relaxed).GetObjectForIndex(string_idx.index_);
+}
+
+inline void DexCache::SetResolvedString(dex::StringIndex string_idx, ObjPtr<String> resolved) {
+  DCHECK(resolved != nullptr);
+  GetStrings()[StringSlotIndex(string_idx)].store(
+      StringDexCachePair(resolved, string_idx.index_), std::memory_order_relaxed);
   Runtime* const runtime = Runtime::Current();
   if (UNLIKELY(runtime->IsActiveTransaction())) {
     DCHECK(runtime->IsAotCompiler());
@@ -58,50 +66,70 @@
 }
 
 inline void DexCache::ClearString(dex::StringIndex string_idx) {
-  const uint32_t slot_idx = string_idx.index_ % NumStrings();
   DCHECK(Runtime::Current()->IsAotCompiler());
+  uint32_t slot_idx = StringSlotIndex(string_idx);
   StringDexCacheType* slot = &GetStrings()[slot_idx];
   // This is racy but should only be called from the transactional interpreter.
   if (slot->load(std::memory_order_relaxed).index == string_idx.index_) {
-    StringDexCachePair cleared(
-        nullptr,
-        StringDexCachePair::InvalidIndexForSlot(slot_idx));
+    StringDexCachePair cleared(nullptr, StringDexCachePair::InvalidIndexForSlot(slot_idx));
     slot->store(cleared, std::memory_order_relaxed);
   }
 }
 
+inline uint32_t DexCache::TypeSlotIndex(dex::TypeIndex type_idx) {
+  DCHECK_LT(type_idx.index_, GetDexFile()->NumTypeIds());
+  const uint32_t slot_idx = type_idx.index_ % kDexCacheTypeCacheSize;
+  DCHECK_LT(slot_idx, NumResolvedTypes());
+  return slot_idx;
+}
+
 inline Class* DexCache::GetResolvedType(dex::TypeIndex type_idx) {
   // It is theorized that a load acquire is not required since obtaining the resolved class will
   // always have an address dependency or a lock.
-  DCHECK_LT(type_idx.index_, NumResolvedTypes());
-  return GetResolvedTypes()[type_idx.index_].Read();
+  return GetResolvedTypes()[TypeSlotIndex(type_idx)].load(
+      std::memory_order_relaxed).GetObjectForIndex(type_idx.index_);
 }
 
 inline void DexCache::SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved) {
-  DCHECK_LT(type_idx.index_, NumResolvedTypes());  // NOTE: Unchecked, i.e. not throwing AIOOB.
+  DCHECK(resolved != nullptr);
   // TODO default transaction support.
   // Use a release store for SetResolvedType. This is done to prevent other threads from seeing a
   // class but not necessarily seeing the loaded members like the static fields array.
   // See b/32075261.
-  reinterpret_cast<Atomic<GcRoot<mirror::Class>>&>(GetResolvedTypes()[type_idx.index_]).
-      StoreRelease(GcRoot<Class>(resolved));
+  GetResolvedTypes()[TypeSlotIndex(type_idx)].store(
+      TypeDexCachePair(resolved, type_idx.index_), std::memory_order_release);
   // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
   Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
 }
 
-inline MethodType* DexCache::GetResolvedMethodType(uint32_t proto_idx) {
+inline void DexCache::ClearResolvedType(dex::TypeIndex type_idx) {
+  DCHECK(Runtime::Current()->IsAotCompiler());
+  uint32_t slot_idx = TypeSlotIndex(type_idx);
+  TypeDexCacheType* slot = &GetResolvedTypes()[slot_idx];
+  // This is racy but should only be called from the single-threaded ImageWriter and tests.
+  if (slot->load(std::memory_order_relaxed).index == type_idx.index_) {
+    TypeDexCachePair cleared(nullptr, TypeDexCachePair::InvalidIndexForSlot(slot_idx));
+    slot->store(cleared, std::memory_order_relaxed);
+  }
+}
+
+inline uint32_t DexCache::MethodTypeSlotIndex(uint32_t proto_idx) {
   DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
   DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds());
-  return MethodTypeDexCachePair::Lookup(
-      GetResolvedMethodTypes(), proto_idx, NumResolvedMethodTypes()).Read();
+  const uint32_t slot_idx = proto_idx % kDexCacheMethodTypeCacheSize;
+  DCHECK_LT(slot_idx, NumResolvedMethodTypes());
+  return slot_idx;
+}
+
+inline MethodType* DexCache::GetResolvedMethodType(uint32_t proto_idx) {
+  return GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].load(
+      std::memory_order_relaxed).GetObjectForIndex(proto_idx);
 }
 
 inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) {
-  DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
-  DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds());
-
-  MethodTypeDexCachePair::Assign(GetResolvedMethodTypes(), proto_idx, resolved,
-                                 NumResolvedMethodTypes());
+  DCHECK(resolved != nullptr);
+  GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].store(
+      MethodTypeDexCachePair(resolved, proto_idx), std::memory_order_relaxed);
   // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
   Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
 }
@@ -198,49 +226,49 @@
   VisitInstanceFieldsReferences<kVerifyFlags, kReadBarrierOption>(klass, visitor);
   // Visit arrays after.
   if (kVisitNativeRoots) {
-    VisitDexCachePairs<mirror::String, kReadBarrierOption, Visitor>(
+    VisitDexCachePairs<String, kReadBarrierOption, Visitor>(
         GetStrings(), NumStrings(), visitor);
 
-    GcRoot<mirror::Class>* resolved_types = GetResolvedTypes();
-    for (size_t i = 0, num_types = NumResolvedTypes(); i != num_types; ++i) {
-      visitor.VisitRootIfNonNull(resolved_types[i].AddressWithoutBarrier());
-    }
+    VisitDexCachePairs<Class, kReadBarrierOption, Visitor>(
+        GetResolvedTypes(), NumResolvedTypes(), visitor);
 
-    VisitDexCachePairs<mirror::MethodType, kReadBarrierOption, Visitor>(
+    VisitDexCachePairs<MethodType, kReadBarrierOption, Visitor>(
         GetResolvedMethodTypes(), NumResolvedMethodTypes(), visitor);
   }
 }
 
 template <ReadBarrierOption kReadBarrierOption, typename Visitor>
-inline void DexCache::FixupStrings(mirror::StringDexCacheType* dest, const Visitor& visitor) {
-  mirror::StringDexCacheType* src = GetStrings();
+inline void DexCache::FixupStrings(StringDexCacheType* dest, const Visitor& visitor) {
+  StringDexCacheType* src = GetStrings();
   for (size_t i = 0, count = NumStrings(); i < count; ++i) {
     StringDexCachePair source = src[i].load(std::memory_order_relaxed);
-    mirror::String* ptr = source.object.Read<kReadBarrierOption>();
-    mirror::String* new_source = visitor(ptr);
+    String* ptr = source.object.Read<kReadBarrierOption>();
+    String* new_source = visitor(ptr);
     source.object = GcRoot<String>(new_source);
     dest[i].store(source, std::memory_order_relaxed);
   }
 }
 
 template <ReadBarrierOption kReadBarrierOption, typename Visitor>
-inline void DexCache::FixupResolvedTypes(GcRoot<mirror::Class>* dest, const Visitor& visitor) {
-  GcRoot<mirror::Class>* src = GetResolvedTypes();
+inline void DexCache::FixupResolvedTypes(TypeDexCacheType* dest, const Visitor& visitor) {
+  TypeDexCacheType* src = GetResolvedTypes();
   for (size_t i = 0, count = NumResolvedTypes(); i < count; ++i) {
-    mirror::Class* source = src[i].Read<kReadBarrierOption>();
-    mirror::Class* new_source = visitor(source);
-    dest[i] = GcRoot<mirror::Class>(new_source);
+    TypeDexCachePair source = src[i].load(std::memory_order_relaxed);
+    Class* ptr = source.object.Read<kReadBarrierOption>();
+    Class* new_source = visitor(ptr);
+    source.object = GcRoot<Class>(new_source);
+    dest[i].store(source, std::memory_order_relaxed);
   }
 }
 
 template <ReadBarrierOption kReadBarrierOption, typename Visitor>
-inline void DexCache::FixupResolvedMethodTypes(mirror::MethodTypeDexCacheType* dest,
+inline void DexCache::FixupResolvedMethodTypes(MethodTypeDexCacheType* dest,
                                                const Visitor& visitor) {
-  mirror::MethodTypeDexCacheType* src = GetResolvedMethodTypes();
+  MethodTypeDexCacheType* src = GetResolvedMethodTypes();
   for (size_t i = 0, count = NumResolvedMethodTypes(); i < count; ++i) {
     MethodTypeDexCachePair source = src[i].load(std::memory_order_relaxed);
-    mirror::MethodType* ptr = source.object.Read<kReadBarrierOption>();
-    mirror::MethodType* new_source = visitor(ptr);
+    MethodType* ptr = source.object.Read<kReadBarrierOption>();
+    MethodType* new_source = visitor(ptr);
     source.object = GcRoot<MethodType>(new_source);
     dest[i].store(source, std::memory_order_relaxed);
   }
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index 741cf3b..3103a92 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -58,8 +58,8 @@
 
   mirror::StringDexCacheType* strings = (dex_file->NumStringIds() == 0u) ? nullptr :
       reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset());
-  GcRoot<mirror::Class>* types = (dex_file->NumTypeIds() == 0u) ? nullptr :
-      reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset());
+  mirror::TypeDexCacheType* types = (dex_file->NumTypeIds() == 0u) ? nullptr :
+      reinterpret_cast<mirror::TypeDexCacheType*>(raw_arrays + layout.TypesOffset());
   ArtMethod** methods = (dex_file->NumMethodIds() == 0u) ? nullptr :
       reinterpret_cast<ArtMethod**>(raw_arrays + layout.MethodsOffset());
   ArtField** fields = (dex_file->NumFieldIds() == 0u) ? nullptr :
@@ -69,6 +69,10 @@
   if (dex_file->NumStringIds() < num_strings) {
     num_strings = dex_file->NumStringIds();
   }
+  size_t num_types = mirror::DexCache::kDexCacheTypeCacheSize;
+  if (dex_file->NumTypeIds() < num_types) {
+    num_types = dex_file->NumTypeIds();
+  }
 
   // Note that we allocate the method type dex caches regardless of this flag,
   // and we make sure here that they're not used by the runtime. This is in the
@@ -104,8 +108,9 @@
       CHECK_EQ(strings[i].load(std::memory_order_relaxed).index, 0u);
       CHECK(strings[i].load(std::memory_order_relaxed).object.IsNull());
     }
-    for (size_t i = 0; i < dex_file->NumTypeIds(); ++i) {
-      CHECK(types[i].IsNull());
+    for (size_t i = 0; i < num_types; ++i) {
+      CHECK_EQ(types[i].load(std::memory_order_relaxed).index, 0u);
+      CHECK(types[i].load(std::memory_order_relaxed).object.IsNull());
     }
     for (size_t i = 0; i < dex_file->NumMethodIds(); ++i) {
       CHECK(mirror::DexCache::GetElementPtrSize(methods, i, image_pointer_size) == nullptr);
@@ -121,6 +126,9 @@
   if (strings != nullptr) {
     mirror::StringDexCachePair::Initialize(strings);
   }
+  if (types != nullptr) {
+    mirror::TypeDexCachePair::Initialize(types);
+  }
   if (method_types != nullptr) {
     mirror::MethodTypeDexCachePair::Initialize(method_types);
   }
@@ -129,7 +137,7 @@
                   strings,
                   num_strings,
                   types,
-                  dex_file->NumTypeIds(),
+                  num_types,
                   methods,
                   dex_file->NumMethodIds(),
                   fields,
@@ -143,7 +151,7 @@
                     ObjPtr<String> location,
                     StringDexCacheType* strings,
                     uint32_t num_strings,
-                    GcRoot<Class>* resolved_types,
+                    TypeDexCacheType* resolved_types,
                     uint32_t num_resolved_types,
                     ArtMethod** resolved_methods,
                     uint32_t num_resolved_methods,
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 6f88cc5..e68b0c7 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -18,14 +18,14 @@
 #define ART_RUNTIME_MIRROR_DEX_CACHE_H_
 
 #include "array.h"
-#include "art_field.h"
-#include "class.h"
+#include "base/bit_utils.h"
 #include "dex_file_types.h"
 #include "object.h"
 #include "object_array.h"
 
 namespace art {
 
+class ArtField;
 class ArtMethod;
 struct DexCacheOffsets;
 class DexFile;
@@ -36,6 +36,7 @@
 
 namespace mirror {
 
+class Class;
 class MethodType;
 class String;
 
@@ -60,7 +61,7 @@
   // it's always non-null if the id branch succeeds (except for the 0th id).
   // Set the initial state for the 0th entry to be {0,1} which is guaranteed to fail
   // the lookup id == stored id branch.
-  DexCachePair(T* object, uint32_t index)
+  DexCachePair(ObjPtr<T> object, uint32_t index)
       : object(object),
         index(index) {}
   DexCachePair() = default;
@@ -74,39 +75,28 @@
     dex_cache[0].store(first_elem, std::memory_order_relaxed);
   }
 
-  static GcRoot<T> Lookup(std::atomic<DexCachePair<T>>* dex_cache,
-                          uint32_t idx,
-                          uint32_t cache_size) {
-    DCHECK_NE(cache_size, 0u);
-    DexCachePair<T> element = dex_cache[idx % cache_size].load(std::memory_order_relaxed);
-    if (idx != element.index) {
-      return GcRoot<T>(nullptr);
-    }
-
-    DCHECK(!element.object.IsNull());
-    return element.object;
-  }
-
-  static void Assign(std::atomic<DexCachePair<T>>* dex_cache,
-                     uint32_t idx,
-                     T* object,
-                     uint32_t cache_size) {
-    DCHECK_LT(idx % cache_size, cache_size);
-    dex_cache[idx % cache_size].store(
-        DexCachePair<T>(object, idx), std::memory_order_relaxed);
-  }
-
   static uint32_t InvalidIndexForSlot(uint32_t slot) {
     // Since the cache size is a power of two, 0 will always map to slot 0.
     // Use 1 for slot 0 and 0 for all other slots.
     return (slot == 0) ? 1u : 0u;
   }
+
+  T* GetObjectForIndex(uint32_t idx) REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (idx != index) {
+      return nullptr;
+    }
+    DCHECK(!object.IsNull());
+    return object.Read();
+  }
 };
 
-using StringDexCachePair = DexCachePair<mirror::String>;
+using TypeDexCachePair = DexCachePair<Class>;
+using TypeDexCacheType = std::atomic<TypeDexCachePair>;
+
+using StringDexCachePair = DexCachePair<String>;
 using StringDexCacheType = std::atomic<StringDexCachePair>;
 
-using MethodTypeDexCachePair = DexCachePair<mirror::MethodType>;
+using MethodTypeDexCachePair = DexCachePair<MethodType>;
 using MethodTypeDexCacheType = std::atomic<MethodTypeDexCachePair>;
 
 // C++ mirror of java.lang.DexCache.
@@ -115,6 +105,11 @@
   // Size of java.lang.DexCache.class.
   static uint32_t ClassSize(PointerSize pointer_size);
 
+  // Size of type dex cache. Needs to be a power of 2 for entrypoint assumptions to hold.
+  static constexpr size_t kDexCacheTypeCacheSize = 1024;
+  static_assert(IsPowerOfTwo(kDexCacheTypeCacheSize),
+                "Type dex cache size is not a power of 2.");
+
   // Size of string dex cache. Needs to be a power of 2 for entrypoint assumptions to hold.
   static constexpr size_t kDexCacheStringCacheSize = 1024;
   static_assert(IsPowerOfTwo(kDexCacheStringCacheSize),
@@ -126,6 +121,10 @@
   static_assert(IsPowerOfTwo(kDexCacheMethodTypeCacheSize),
                 "MethodType dex cache size is not a power of 2.");
 
+  static constexpr size_t StaticTypeSize() {
+    return kDexCacheTypeCacheSize;
+  }
+
   static constexpr size_t StaticStringSize() {
     return kDexCacheStringCacheSize;
   }
@@ -156,7 +155,7 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
-  void FixupResolvedTypes(GcRoot<mirror::Class>* dest, const Visitor& visitor)
+  void FixupResolvedTypes(TypeDexCacheType* dest, const Visitor& visitor)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
@@ -211,7 +210,7 @@
     return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_method_types_);
   }
 
-  mirror::String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE
+  String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   void SetResolvedString(dex::StringIndex string_idx, ObjPtr<mirror::String> resolved) ALWAYS_INLINE
@@ -226,6 +225,8 @@
   void SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  void ClearResolvedType(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+
   ALWAYS_INLINE ArtMethod* GetResolvedMethod(uint32_t method_idx, PointerSize ptr_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -254,11 +255,11 @@
     SetFieldPtr<false>(StringsOffset(), strings);
   }
 
-  GcRoot<Class>* GetResolvedTypes() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetFieldPtr<GcRoot<Class>*>(ResolvedTypesOffset());
+  TypeDexCacheType* GetResolvedTypes() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetFieldPtr<TypeDexCacheType*>(ResolvedTypesOffset());
   }
 
-  void SetResolvedTypes(GcRoot<Class>* resolved_types)
+  void SetResolvedTypes(TypeDexCacheType* resolved_types)
       ALWAYS_INLINE
       REQUIRES_SHARED(Locks::mutator_lock_) {
     SetFieldPtr<false>(ResolvedTypesOffset(), resolved_types);
@@ -323,7 +324,7 @@
     SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file);
   }
 
-  void SetLocation(ObjPtr<mirror::String> location) REQUIRES_SHARED(Locks::mutator_lock_);
+  void SetLocation(ObjPtr<String> location) REQUIRES_SHARED(Locks::mutator_lock_);
 
   // NOTE: Get/SetElementPtrSize() are intended for working with ArtMethod** and ArtField**
   // provided by GetResolvedMethods/Fields() and ArtMethod::GetDexCacheResolvedMethods(),
@@ -340,7 +341,7 @@
             ObjPtr<String> location,
             StringDexCacheType* strings,
             uint32_t num_strings,
-            GcRoot<Class>* resolved_types,
+            TypeDexCacheType* resolved_types,
             uint32_t num_resolved_types,
             ArtMethod** resolved_methods,
             uint32_t num_resolved_methods,
@@ -351,12 +352,16 @@
             PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  uint32_t StringSlotIndex(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+  uint32_t TypeSlotIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+  uint32_t MethodTypeSlotIndex(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Visit instance fields of the dex cache as well as its associated arrays.
   template <bool kVisitNativeRoots,
             VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
             ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
             typename Visitor>
-  void VisitReferences(ObjPtr<mirror::Class> klass, const Visitor& visitor)
+  void VisitReferences(ObjPtr<Class> klass, const Visitor& visitor)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
 
   HeapReference<Object> dex_;
@@ -366,7 +371,7 @@
   uint64_t resolved_method_types_;  // std::atomic<MethodTypeDexCachePair>* array with
                                     // num_resolved_method_types_ elements.
   uint64_t resolved_methods_;       // ArtMethod*, array with num_resolved_methods_ elements.
-  uint64_t resolved_types_;         // GcRoot<Class>*, array with num_resolved_types_ elements.
+  uint64_t resolved_types_;         // TypeDexCacheType*, array with num_resolved_types_ elements.
   uint64_t strings_;                // std::atomic<StringDexCachePair>*, array with num_strings_
                                     // elements.
 
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index 8f978e1..5693f67 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -51,7 +51,8 @@
 
   EXPECT_TRUE(dex_cache->StaticStringSize() == dex_cache->NumStrings()
       || java_lang_dex_file_->NumStringIds() == dex_cache->NumStrings());
-  EXPECT_EQ(java_lang_dex_file_->NumTypeIds(),   dex_cache->NumResolvedTypes());
+  EXPECT_TRUE(dex_cache->StaticTypeSize() == dex_cache->NumResolvedTypes()
+      || java_lang_dex_file_->NumTypeIds() == dex_cache->NumResolvedTypes());
   EXPECT_EQ(java_lang_dex_file_->NumMethodIds(), dex_cache->NumResolvedMethods());
   EXPECT_EQ(java_lang_dex_file_->NumFieldIds(),  dex_cache->NumResolvedFields());
   EXPECT_TRUE(dex_cache->StaticMethodTypeSize() == dex_cache->NumResolvedMethodTypes()
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
index 2f26a22..dca3062 100644
--- a/runtime/mirror/method_handle_impl.h
+++ b/runtime/mirror/method_handle_impl.h
@@ -25,6 +25,7 @@
 
 namespace art {
 
+struct MethodHandleOffsets;
 struct MethodHandleImplOffsets;
 
 namespace mirror {
@@ -105,7 +106,7 @@
     return MemberOffset(OFFSETOF_MEMBER(MethodHandle, handle_kind_));
   }
 
-  friend struct art::MethodHandleImplOffsets;  // for verifying offset information
+  friend struct art::MethodHandleOffsets;  // for verifying offset information
   DISALLOW_IMPLICIT_CONSTRUCTORS(MethodHandle);
 };
 
@@ -121,6 +122,11 @@
   static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
 
  private:
+  static MemberOffset InfoOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodHandleImpl, info_));
+  }
+
+  HeapReference<mirror::Object> info_;  // Unused by the runtime.
   static GcRoot<mirror::Class> static_class_;  // java.lang.invoke.MethodHandleImpl.class
 
   friend struct art::MethodHandleImplOffsets;  // for verifying offset information
diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc
index f1c350f..0b667fe 100644
--- a/runtime/native/java_lang_DexCache.cc
+++ b/runtime/native/java_lang_DexCache.cc
@@ -53,7 +53,7 @@
 static jobject DexCache_getResolvedType(JNIEnv* env, jobject javaDexCache, jint type_index) {
   ScopedFastNativeObjectAccess soa(env);
   ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache);
-  CHECK_LT(static_cast<size_t>(type_index), dex_cache->NumResolvedTypes());
+  CHECK_LT(static_cast<size_t>(type_index), dex_cache->GetDexFile()->NumTypeIds());
   return soa.AddLocalReference<jobject>(dex_cache->GetResolvedType(dex::TypeIndex(type_index)));
 }
 
@@ -69,8 +69,11 @@
                                      jobject type) {
   ScopedFastNativeObjectAccess soa(env);
   ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache);
-  CHECK_LT(static_cast<size_t>(type_index), dex_cache->NumResolvedTypes());
-  dex_cache->SetResolvedType(dex::TypeIndex(type_index), soa.Decode<mirror::Class>(type));
+  CHECK_LT(static_cast<size_t>(type_index), dex_cache->GetDexFile()->NumTypeIds());
+  ObjPtr<mirror::Class> t = soa.Decode<mirror::Class>(type);
+  if (t != nullptr) {
+    dex_cache->SetResolvedType(dex::TypeIndex(type_index), t);
+  }
 }
 
 static void DexCache_setResolvedString(JNIEnv* env, jobject javaDexCache, jint string_index,
@@ -78,7 +81,10 @@
   ScopedFastNativeObjectAccess soa(env);
   ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache);
   CHECK_LT(static_cast<size_t>(string_index), dex_cache->GetDexFile()->NumStringIds());
-  dex_cache->SetResolvedString(dex::StringIndex(string_index), soa.Decode<mirror::String>(string));
+  ObjPtr<mirror::String> s = soa.Decode<mirror::String>(string);
+  if (s != nullptr) {
+    dex_cache->SetResolvedString(dex::StringIndex(string_index), s);
+  }
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/runtime/native/java_lang_invoke_MethodHandleImpl.cc b/runtime/native/java_lang_invoke_MethodHandleImpl.cc
new file mode 100644
index 0000000..72a37f8
--- /dev/null
+++ b/runtime/native/java_lang_invoke_MethodHandleImpl.cc
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008 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 "java_lang_invoke_MethodHandleImpl.h"
+
+#include "art_method.h"
+#include "handle_scope-inl.h"
+#include "jni_internal.h"
+#include "mirror/field.h"
+#include "mirror/method.h"
+#include "mirror/method_handle_impl.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+
+namespace art {
+
+static jobject MethodHandleImpl_getMemberInternal(JNIEnv* env, jobject thiz) {
+  ScopedObjectAccess soa(env);
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::MethodHandleImpl> handle = hs.NewHandle(
+      soa.Decode<mirror::MethodHandleImpl>(thiz));
+
+  // Check the handle kind, we need to materialize a Field for field accessors,
+  // a Method for method invokers and a Constructor for constructors.
+  const mirror::MethodHandle::Kind handle_kind = handle->GetHandleKind();
+
+  // We check this here because we pass false to CreateFromArtField and
+  // CreateFromArtMethod.
+  DCHECK(!Runtime::Current()->IsActiveTransaction());
+
+  MutableHandle<mirror::Object> h_object(hs.NewHandle<mirror::Object>(nullptr));
+  if (handle_kind >= mirror::MethodHandle::kFirstAccessorKind) {
+    ArtField* const field = handle->GetTargetField();
+    h_object.Assign(mirror::Field::CreateFromArtField<kRuntimePointerSize, false>(
+        soa.Self(), field, false /* force_resolve */));
+  } else {
+    ArtMethod* const method = handle->GetTargetMethod();
+    if (method->IsConstructor()) {
+      h_object.Assign(mirror::Constructor::CreateFromArtMethod<kRuntimePointerSize, false>(
+          soa.Self(), method));
+    } else {
+      h_object.Assign(mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(
+          soa.Self(), method));
+    }
+  }
+
+  if (UNLIKELY(h_object.Get() == nullptr)) {
+    soa.Self()->AssertPendingOOMException();
+    return nullptr;
+  }
+
+  return soa.AddLocalReference<jobject>(h_object.Get());
+}
+
+static JNINativeMethod gMethods[] = {
+  NATIVE_METHOD(MethodHandleImpl, getMemberInternal, "()Ljava/lang/reflect/Member;"),
+};
+
+void register_java_lang_invoke_MethodHandleImpl(JNIEnv* env) {
+  REGISTER_NATIVE_METHODS("java/lang/invoke/MethodHandleImpl");
+}
+
+}  // namespace art
diff --git a/runtime/native/java_lang_invoke_MethodHandleImpl.h b/runtime/native/java_lang_invoke_MethodHandleImpl.h
new file mode 100644
index 0000000..0e50371
--- /dev/null
+++ b/runtime/native/java_lang_invoke_MethodHandleImpl.h
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_INVOKE_METHODHANDLEIMPL_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_INVOKE_METHODHANDLEIMPL_H_
+
+#include <jni.h>
+
+namespace art {
+
+void register_java_lang_invoke_MethodHandleImpl(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_INVOKE_METHODHANDLEIMPL_H_
diff --git a/runtime/oat.h b/runtime/oat.h
index 4a68036..62f010b 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[] = { '1', '0', '4', '\0' };  // Array allocation entrypoints
+  static constexpr uint8_t kOatVersion[] = { '1', '0', '6', '\0' };  // hash-based DexCache types
 
   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 976a1e7..c01e3f4 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -22,6 +22,7 @@
            "OpenjdkJvmTi.cc",
            "ti_class.cc",
            "ti_class_definition.cc",
+           "ti_class_loader.cc",
            "ti_dump.cc",
            "ti_field.cc",
            "ti_heap.cc",
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index c0c301f..a815a60 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -1368,6 +1368,7 @@
   ThreadUtil::Register(&gEventHandler);
   ClassUtil::Register(&gEventHandler);
   DumpUtil::Register(&gEventHandler);
+  SearchUtil::Register();
 
   runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler);
   runtime->AddSystemWeakHolder(&gObjectTagTable);
@@ -1380,6 +1381,7 @@
   ThreadUtil::Unregister();
   ClassUtil::Unregister();
   DumpUtil::Unregister();
+  SearchUtil::Unregister();
 
   return true;
 }
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index d484c98..c14fd84 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -54,6 +54,7 @@
 #include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 #include "thread_list.h"
+#include "ti_class_loader.h"
 #include "ti_redefine.h"
 #include "utils.h"
 
@@ -228,12 +229,15 @@
         return;
       }
 
-      // TODO Check Redefined dex file for invariants.
+      // TODO Check Redefined dex file for all invariants.
       LOG(WARNING) << "Dex file created by class-definition time transformation of "
                    << descriptor << " is not checked for all retransformation invariants.";
-      // TODO Put it in classpath
-      LOG(WARNING) << "Dex file created for class-definition time transformation of "
-                   << descriptor << " was not added to any classpaths!";
+
+      if (!ClassLoaderHelper::AddToClassLoader(self, class_loader, dex_file.get())) {
+        LOG(ERROR) << "Unable to add " << descriptor << " to class loader!";
+        return;
+      }
+
       // Actually set the ClassExt's original bytes once we have actually succeeded.
       ext->SetOriginalDexFileBytes(arr.Get());
       // Set the return values
diff --git a/runtime/openjdkjvmti/ti_class_loader.cc b/runtime/openjdkjvmti/ti_class_loader.cc
new file mode 100644
index 0000000..b68fc60
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_class_loader.cc
@@ -0,0 +1,202 @@
+/* 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_class_loader.h"
+
+#include <limits>
+
+#include "android-base/stringprintf.h"
+
+#include "art_jvmti.h"
+#include "base/array_slice.h"
+#include "base/logging.h"
+#include "dex_file.h"
+#include "dex_file_types.h"
+#include "events-inl.h"
+#include "gc/allocation_listener.h"
+#include "gc/heap.h"
+#include "instrumentation.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
+#include "jni_env_ext-inl.h"
+#include "jvmti_allocator.h"
+#include "mirror/class.h"
+#include "mirror/class_ext.h"
+#include "mirror/object.h"
+#include "object_lock.h"
+#include "runtime.h"
+#include "ScopedLocalRef.h"
+#include "transform.h"
+
+namespace openjdkjvmti {
+
+bool ClassLoaderHelper::AddToClassLoader(art::Thread* self,
+                                         art::Handle<art::mirror::ClassLoader> loader,
+                                         const art::DexFile* dex_file) {
+  art::StackHandleScope<2> hs(self);
+  art::Handle<art::mirror::Object> java_dex_file_obj(hs.NewHandle(FindSourceDexFileObject(self,
+                                                                                          loader)));
+  if (java_dex_file_obj.IsNull()) {
+    return false;
+  }
+  art::Handle<art::mirror::LongArray> cookie(hs.NewHandle(
+      AllocateNewDexFileCookie(self, java_dex_file_obj, dex_file)));
+  if (cookie.IsNull()) {
+    return false;
+  }
+  art::ScopedAssertNoThreadSuspension nts("Replacing cookie fields in j.l.DexFile object");
+  UpdateJavaDexFile(java_dex_file_obj.Get(), cookie.Get());
+  return true;
+}
+
+void ClassLoaderHelper::UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
+                                          art::ObjPtr<art::mirror::LongArray> new_cookie) {
+  art::ArtField* internal_cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField(
+      "mInternalCookie", "Ljava/lang/Object;");
+  art::ArtField* cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField(
+      "mCookie", "Ljava/lang/Object;");
+  CHECK(internal_cookie_field != nullptr);
+  art::ObjPtr<art::mirror::LongArray> orig_internal_cookie(
+      internal_cookie_field->GetObject(java_dex_file)->AsLongArray());
+  art::ObjPtr<art::mirror::LongArray> orig_cookie(
+      cookie_field->GetObject(java_dex_file)->AsLongArray());
+  internal_cookie_field->SetObject<false>(java_dex_file, new_cookie);
+  if (!orig_cookie.IsNull()) {
+    cookie_field->SetObject<false>(java_dex_file, new_cookie);
+  }
+}
+
+// TODO Really wishing I had that mirror of java.lang.DexFile now.
+art::ObjPtr<art::mirror::LongArray> ClassLoaderHelper::AllocateNewDexFileCookie(
+    art::Thread* self,
+    art::Handle<art::mirror::Object> java_dex_file_obj,
+    const art::DexFile* dex_file) {
+  art::StackHandleScope<2> hs(self);
+  // mCookie is nulled out if the DexFile has been closed but mInternalCookie sticks around until
+  // the object is finalized. Since they always point to the same array if mCookie is not null we
+  // just use the mInternalCookie field. We will update one or both of these fields later.
+  // TODO Should I get the class from the classloader or directly?
+  art::ArtField* internal_cookie_field = java_dex_file_obj->GetClass()->FindDeclaredInstanceField(
+      "mInternalCookie", "Ljava/lang/Object;");
+  // TODO Add check that mCookie is either null or same as mInternalCookie
+  CHECK(internal_cookie_field != nullptr);
+  art::Handle<art::mirror::LongArray> cookie(
+      hs.NewHandle(internal_cookie_field->GetObject(java_dex_file_obj.Get())->AsLongArray()));
+  // TODO Maybe make these non-fatal.
+  CHECK(cookie.Get() != nullptr);
+  CHECK_GE(cookie->GetLength(), 1);
+  art::Handle<art::mirror::LongArray> new_cookie(
+      hs.NewHandle(art::mirror::LongArray::Alloc(self, cookie->GetLength() + 1)));
+  if (new_cookie.Get() == nullptr) {
+    self->AssertPendingOOMException();
+    return nullptr;
+  }
+  // Copy the oat-dex field at the start.
+  // TODO Should I clear this field?
+  // TODO This is a really crappy thing here with the first element being different.
+  new_cookie->SetWithoutChecks<false>(0, cookie->GetWithoutChecks(0));
+  new_cookie->SetWithoutChecks<false>(
+      1, static_cast<int64_t>(reinterpret_cast<intptr_t>(dex_file)));
+  new_cookie->Memcpy(2, cookie.Get(), 1, cookie->GetLength() - 1);
+  return new_cookie.Get();
+}
+
+// TODO This should return the actual source java.lang.DexFile object for the klass being loaded.
+art::ObjPtr<art::mirror::Object> ClassLoaderHelper::FindSourceDexFileObject(
+    art::Thread* self, art::Handle<art::mirror::ClassLoader> loader) {
+  const char* dex_path_list_element_array_name = "[Ldalvik/system/DexPathList$Element;";
+  const char* dex_path_list_element_name = "Ldalvik/system/DexPathList$Element;";
+  const char* dex_file_name = "Ldalvik/system/DexFile;";
+  const char* dex_path_list_name = "Ldalvik/system/DexPathList;";
+  const char* dex_class_loader_name = "Ldalvik/system/BaseDexClassLoader;";
+
+  CHECK(!self->IsExceptionPending());
+  art::StackHandleScope<5> hs(self);
+  art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
+
+  art::Handle<art::mirror::ClassLoader> null_loader(hs.NewHandle<art::mirror::ClassLoader>(
+      nullptr));
+  art::Handle<art::mirror::Class> base_dex_loader_class(hs.NewHandle(class_linker->FindClass(
+      self, dex_class_loader_name, null_loader)));
+
+  // Get all the ArtFields so we can look in the BaseDexClassLoader
+  art::ArtField* path_list_field = base_dex_loader_class->FindDeclaredInstanceField(
+      "pathList", dex_path_list_name);
+  CHECK(path_list_field != nullptr);
+
+  art::ArtField* dex_path_list_element_field =
+      class_linker->FindClass(self, dex_path_list_name, null_loader)
+        ->FindDeclaredInstanceField("dexElements", dex_path_list_element_array_name);
+  CHECK(dex_path_list_element_field != nullptr);
+
+  art::ArtField* element_dex_file_field =
+      class_linker->FindClass(self, dex_path_list_element_name, null_loader)
+        ->FindDeclaredInstanceField("dexFile", dex_file_name);
+  CHECK(element_dex_file_field != nullptr);
+
+  // Check if loader is a BaseDexClassLoader
+  art::Handle<art::mirror::Class> loader_class(hs.NewHandle(loader->GetClass()));
+  // Currently only base_dex_loader is allowed to actually define classes but if this changes in the
+  // future we should make sure to support all class loader types.
+  if (!loader_class->IsSubClass(base_dex_loader_class.Get())) {
+    LOG(ERROR) << "The classloader is not a BaseDexClassLoader which is currently the only "
+               << "supported class loader type!";
+    return nullptr;
+  }
+  // Start navigating the fields of the loader (now known to be a BaseDexClassLoader derivative)
+  art::Handle<art::mirror::Object> path_list(
+      hs.NewHandle(path_list_field->GetObject(loader.Get())));
+  CHECK(path_list.Get() != nullptr);
+  CHECK(!self->IsExceptionPending());
+  art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(hs.NewHandle(
+      dex_path_list_element_field->GetObject(path_list.Get())->
+      AsObjectArray<art::mirror::Object>()));
+  CHECK(!self->IsExceptionPending());
+  CHECK(dex_elements_list.Get() != nullptr);
+  size_t num_elements = dex_elements_list->GetLength();
+  // Iterate over the DexPathList$Element to find the right one
+  for (size_t i = 0; i < num_elements; i++) {
+    art::ObjPtr<art::mirror::Object> current_element = dex_elements_list->Get(i);
+    CHECK(!current_element.IsNull());
+    // TODO It would be cleaner to put the art::DexFile into the dalvik.system.DexFile the class
+    // comes from but it is more annoying because we would need to find this class. It is not
+    // necessary for proper function since we just need to be in front of the classes old dex file
+    // in the path.
+    art::ObjPtr<art::mirror::Object> first_dex_file(
+        element_dex_file_field->GetObject(current_element));
+    if (!first_dex_file.IsNull()) {
+      return first_dex_file;
+    }
+  }
+  return nullptr;
+}
+
+}  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_class_loader.h b/runtime/openjdkjvmti/ti_class_loader.h
new file mode 100644
index 0000000..17ed0eb
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_class_loader.h
@@ -0,0 +1,96 @@
+/* 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_CLASS_LOADER_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_LOADER_H_
+
+#include <string>
+
+#include <jni.h>
+
+#include "art_jvmti.h"
+#include "art_method.h"
+#include "base/array_slice.h"
+#include "class_linker.h"
+#include "dex_file.h"
+#include "gc_root-inl.h"
+#include "globals.h"
+#include "jni_env_ext-inl.h"
+#include "jvmti.h"
+#include "linear_alloc.h"
+#include "mem_map.h"
+#include "mirror/array-inl.h"
+#include "mirror/array.h"
+#include "mirror/class-inl.h"
+#include "mirror/class.h"
+#include "mirror/class_loader-inl.h"
+#include "mirror/string-inl.h"
+#include "oat_file.h"
+#include "obj_ptr.h"
+#include "scoped_thread_state_change-inl.h"
+#include "stack.h"
+#include "ti_class_definition.h"
+#include "thread_list.h"
+#include "transform.h"
+#include "utf.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
+
+namespace openjdkjvmti {
+
+// Class that can redefine a single class's methods.
+// TODO We should really make this be driven by an outside class so we can do multiple classes at
+// the same time and have less required cleanup.
+class ClassLoaderHelper {
+ public:
+  static bool AddToClassLoader(art::Thread* self,
+                               art::Handle<art::mirror::ClassLoader> loader,
+                               const art::DexFile* dex_file)
+      REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+  // Finds a java.lang.DexFile object that is associated with the given ClassLoader. Each of these
+  // j.l.DexFile objects holds several art::DexFile*s in it.
+  // TODO This should return the actual source java.lang.DexFile object for the klass being loaded.
+  static art::ObjPtr<art::mirror::Object> FindSourceDexFileObject(
+      art::Thread* self, art::Handle<art::mirror::ClassLoader> loader)
+      REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+  static art::ObjPtr<art::mirror::LongArray> AllocateNewDexFileCookie(
+      art::Thread* self,
+      art::Handle<art::mirror::Object> java_dex_file,
+      const art::DexFile* new_dex_file) REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+  static void UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
+                                art::ObjPtr<art::mirror::LongArray> new_cookie)
+      REQUIRES(art::Roles::uninterruptible_) REQUIRES_SHARED(art::Locks::mutator_lock_);
+};
+
+}  // namespace openjdkjvmti
+#endif  // ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_LOADER_H_
diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc
index 4970288..60371cf 100644
--- a/runtime/openjdkjvmti/ti_phase.cc
+++ b/runtime/openjdkjvmti/ti_phase.cc
@@ -136,4 +136,8 @@
   art::Runtime::Current()->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&gPhaseCallback);
 }
 
+jvmtiPhase PhaseUtil::GetPhaseUnchecked() {
+  return PhaseUtil::current_phase_;
+}
+
 }  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_phase.h b/runtime/openjdkjvmti/ti_phase.h
index bd15fa6..851fc27 100644
--- a/runtime/openjdkjvmti/ti_phase.h
+++ b/runtime/openjdkjvmti/ti_phase.h
@@ -57,6 +57,8 @@
 
   struct PhaseCallback;
 
+  static jvmtiPhase GetPhaseUnchecked();
+
  private:
   static jvmtiPhase current_phase_;
 };
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index d2ddc21..b76d74a 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -54,6 +54,7 @@
 #include "object_lock.h"
 #include "runtime.h"
 #include "ScopedLocalRef.h"
+#include "ti_class_loader.h"
 #include "transform.h"
 
 namespace openjdkjvmti {
@@ -386,85 +387,6 @@
   return OK;
 }
 
-// TODO *MAJOR* This should return the actual source java.lang.DexFile object for the klass.
-// TODO Make mirror of DexFile and associated types to make this less hellish.
-// TODO Make mirror of BaseDexClassLoader and associated types to make this less hellish.
-art::mirror::Object* Redefiner::ClassRedefinition::FindSourceDexFileObject(
-    art::Handle<art::mirror::ClassLoader> loader) {
-  const char* dex_path_list_element_array_name = "[Ldalvik/system/DexPathList$Element;";
-  const char* dex_path_list_element_name = "Ldalvik/system/DexPathList$Element;";
-  const char* dex_file_name = "Ldalvik/system/DexFile;";
-  const char* dex_path_list_name = "Ldalvik/system/DexPathList;";
-  const char* dex_class_loader_name = "Ldalvik/system/BaseDexClassLoader;";
-
-  CHECK(!driver_->self_->IsExceptionPending());
-  art::StackHandleScope<11> hs(driver_->self_);
-  art::ClassLinker* class_linker = driver_->runtime_->GetClassLinker();
-
-  art::Handle<art::mirror::ClassLoader> null_loader(hs.NewHandle<art::mirror::ClassLoader>(
-      nullptr));
-  art::Handle<art::mirror::Class> base_dex_loader_class(hs.NewHandle(class_linker->FindClass(
-      driver_->self_, dex_class_loader_name, null_loader)));
-
-  // Get all the ArtFields so we can look in the BaseDexClassLoader
-  art::ArtField* path_list_field = base_dex_loader_class->FindDeclaredInstanceField(
-      "pathList", dex_path_list_name);
-  CHECK(path_list_field != nullptr);
-
-  art::ArtField* dex_path_list_element_field =
-      class_linker->FindClass(driver_->self_, dex_path_list_name, null_loader)
-        ->FindDeclaredInstanceField("dexElements", dex_path_list_element_array_name);
-  CHECK(dex_path_list_element_field != nullptr);
-
-  art::ArtField* element_dex_file_field =
-      class_linker->FindClass(driver_->self_, dex_path_list_element_name, null_loader)
-        ->FindDeclaredInstanceField("dexFile", dex_file_name);
-  CHECK(element_dex_file_field != nullptr);
-
-  // Check if loader is a BaseDexClassLoader
-  art::Handle<art::mirror::Class> loader_class(hs.NewHandle(loader->GetClass()));
-  if (!loader_class->IsSubClass(base_dex_loader_class.Get())) {
-    LOG(ERROR) << "The classloader is not a BaseDexClassLoader which is currently the only "
-               << "supported class loader type!";
-    return nullptr;
-  }
-  // Start navigating the fields of the loader (now known to be a BaseDexClassLoader derivative)
-  art::Handle<art::mirror::Object> path_list(
-      hs.NewHandle(path_list_field->GetObject(loader.Get())));
-  CHECK(path_list.Get() != nullptr);
-  CHECK(!driver_->self_->IsExceptionPending());
-  art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(hs.NewHandle(
-      dex_path_list_element_field->GetObject(path_list.Get())->
-      AsObjectArray<art::mirror::Object>()));
-  CHECK(!driver_->self_->IsExceptionPending());
-  CHECK(dex_elements_list.Get() != nullptr);
-  size_t num_elements = dex_elements_list->GetLength();
-  art::MutableHandle<art::mirror::Object> current_element(
-      hs.NewHandle<art::mirror::Object>(nullptr));
-  art::MutableHandle<art::mirror::Object> first_dex_file(
-      hs.NewHandle<art::mirror::Object>(nullptr));
-  // Iterate over the DexPathList$Element to find the right one
-  // TODO Or not ATM just return the first one.
-  for (size_t i = 0; i < num_elements; i++) {
-    current_element.Assign(dex_elements_list->Get(i));
-    CHECK(current_element.Get() != nullptr);
-    CHECK(!driver_->self_->IsExceptionPending());
-    CHECK(dex_elements_list.Get() != nullptr);
-    CHECK_EQ(current_element->GetClass(), class_linker->FindClass(driver_->self_,
-                                                                  dex_path_list_element_name,
-                                                                  null_loader));
-    // TODO It would be cleaner to put the art::DexFile into the dalvik.system.DexFile the class
-    // comes from but it is more annoying because we would need to find this class. It is not
-    // necessary for proper function since we just need to be in front of the classes old dex file
-    // in the path.
-    first_dex_file.Assign(element_dex_file_field->GetObject(current_element.Get()));
-    if (first_dex_file.Get() != nullptr) {
-      return first_dex_file.Get();
-    }
-  }
-  return nullptr;
-}
-
 art::mirror::Class* Redefiner::ClassRedefinition::GetMirrorClass() {
   return driver_->self_->DecodeJObject(klass_)->AsClass();
 }
@@ -478,39 +400,6 @@
   return driver_->runtime_->GetClassLinker()->RegisterDexFile(*dex_file_, loader.Get());
 }
 
-// TODO Really wishing I had that mirror of java.lang.DexFile now.
-art::mirror::LongArray* Redefiner::ClassRedefinition::AllocateDexFileCookie(
-    art::Handle<art::mirror::Object> java_dex_file_obj) {
-  art::StackHandleScope<2> hs(driver_->self_);
-  // mCookie is nulled out if the DexFile has been closed but mInternalCookie sticks around until
-  // the object is finalized. Since they always point to the same array if mCookie is not null we
-  // just use the mInternalCookie field. We will update one or both of these fields later.
-  // TODO Should I get the class from the classloader or directly?
-  art::ArtField* internal_cookie_field = java_dex_file_obj->GetClass()->FindDeclaredInstanceField(
-      "mInternalCookie", "Ljava/lang/Object;");
-  // TODO Add check that mCookie is either null or same as mInternalCookie
-  CHECK(internal_cookie_field != nullptr);
-  art::Handle<art::mirror::LongArray> cookie(
-      hs.NewHandle(internal_cookie_field->GetObject(java_dex_file_obj.Get())->AsLongArray()));
-  // TODO Maybe make these non-fatal.
-  CHECK(cookie.Get() != nullptr);
-  CHECK_GE(cookie->GetLength(), 1);
-  art::Handle<art::mirror::LongArray> new_cookie(
-      hs.NewHandle(art::mirror::LongArray::Alloc(driver_->self_, cookie->GetLength() + 1)));
-  if (new_cookie.Get() == nullptr) {
-    driver_->self_->AssertPendingOOMException();
-    return nullptr;
-  }
-  // Copy the oat-dex field at the start.
-  // TODO Should I clear this field?
-  // TODO This is a really crappy thing here with the first element being different.
-  new_cookie->SetWithoutChecks<false>(0, cookie->GetWithoutChecks(0));
-  new_cookie->SetWithoutChecks<false>(
-      1, static_cast<int64_t>(reinterpret_cast<intptr_t>(dex_file_.get())));
-  new_cookie->Memcpy(2, cookie.Get(), 1, cookie->GetLength() - 1);
-  return new_cookie.Get();
-}
-
 void Redefiner::RecordFailure(jvmtiError result,
                               const std::string& class_sig,
                               const std::string& error_msg) {
@@ -854,14 +743,18 @@
     RecordFailure(ERR(INTERNAL), "Unable to find class loader!");
     return false;
   }
-  art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(FindSourceDexFileObject(loader)));
+  art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(
+      ClassLoaderHelper::FindSourceDexFileObject(driver_->self_, loader)));
   holder->SetJavaDexFile(klass_index, dex_file_obj.Get());
   if (dex_file_obj.Get() == nullptr) {
     // TODO Better error msg.
     RecordFailure(ERR(INTERNAL), "Unable to find class loader!");
     return false;
   }
-  holder->SetNewDexFileCookie(klass_index, AllocateDexFileCookie(dex_file_obj));
+  holder->SetNewDexFileCookie(klass_index,
+                              ClassLoaderHelper::AllocateNewDexFileCookie(driver_->self_,
+                                                                          dex_file_obj,
+                                                                          dex_file_.get()).Ptr());
   if (holder->GetNewDexFileCookie(klass_index) == nullptr) {
     driver_->self_->AssertPendingOOMException();
     driver_->self_->ClearException();
@@ -973,8 +866,10 @@
   // TODO We need to decide on & implement semantics for JNI jmethodids when we redefine methods.
   int32_t cnt = 0;
   for (Redefiner::ClassRedefinition& redef : redefinitions_) {
+    art::ScopedAssertNoThreadSuspension nts("Updating runtime objects for redefinition");
     art::mirror::Class* klass = holder.GetMirrorClass(cnt);
-    redef.UpdateJavaDexFile(holder.GetJavaDexFile(cnt), holder.GetNewDexFileCookie(cnt));
+    ClassLoaderHelper::UpdateJavaDexFile(holder.GetJavaDexFile(cnt),
+                                         holder.GetNewDexFileCookie(cnt));
     // TODO Rewrite so we don't do a stack walk for each and every class.
     redef.FindAndAllocateObsoleteMethods(klass);
     redef.UpdateClass(klass, holder.GetNewDexCache(cnt), holder.GetOriginalDexFileBytes(cnt));
@@ -1090,24 +985,6 @@
   ext->SetOriginalDexFileBytes(original_dex_file);
 }
 
-void Redefiner::ClassRedefinition::UpdateJavaDexFile(
-    art::ObjPtr<art::mirror::Object> java_dex_file,
-    art::ObjPtr<art::mirror::LongArray> new_cookie) {
-  art::ArtField* internal_cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField(
-      "mInternalCookie", "Ljava/lang/Object;");
-  art::ArtField* cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField(
-      "mCookie", "Ljava/lang/Object;");
-  CHECK(internal_cookie_field != nullptr);
-  art::ObjPtr<art::mirror::LongArray> orig_internal_cookie(
-      internal_cookie_field->GetObject(java_dex_file)->AsLongArray());
-  art::ObjPtr<art::mirror::LongArray> orig_cookie(
-      cookie_field->GetObject(java_dex_file)->AsLongArray());
-  internal_cookie_field->SetObject<false>(java_dex_file, new_cookie);
-  if (!orig_cookie.IsNull()) {
-    cookie_field->SetObject<false>(java_dex_file, new_cookie);
-  }
-}
-
 // This function does all (java) allocations we need to do for the Class being redefined.
 // TODO Change this name maybe?
 bool Redefiner::ClassRedefinition::EnsureClassAllocationsFinished() {
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index fdc13ee..85df6e1 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -127,18 +127,9 @@
     art::mirror::Class* GetMirrorClass() REQUIRES_SHARED(art::Locks::mutator_lock_);
     art::mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(art::Locks::mutator_lock_);
 
-    // This finds the java.lang.DexFile we will add the native DexFile to as part of the classpath.
-    // TODO Make sure the DexFile object returned is the one that the klass_ actually comes from.
-    art::mirror::Object* FindSourceDexFileObject(art::Handle<art::mirror::ClassLoader> loader)
-        REQUIRES_SHARED(art::Locks::mutator_lock_);
-
     art::mirror::DexCache* CreateNewDexCache(art::Handle<art::mirror::ClassLoader> loader)
         REQUIRES_SHARED(art::Locks::mutator_lock_);
 
-    // Allocates and fills the new DexFileCookie
-    art::mirror::LongArray* AllocateDexFileCookie(art::Handle<art::mirror::Object> j_dex_file_obj)
-        REQUIRES_SHARED(art::Locks::mutator_lock_);
-
     // This may return nullptr with a OOME pending if allocation fails.
     art::mirror::ByteArray* AllocateOrGetOriginalDexFileBytes()
         REQUIRES_SHARED(art::Locks::mutator_lock_);
diff --git a/runtime/openjdkjvmti/ti_search.cc b/runtime/openjdkjvmti/ti_search.cc
index 913d2b6..df80f85 100644
--- a/runtime/openjdkjvmti/ti_search.cc
+++ b/runtime/openjdkjvmti/ti_search.cc
@@ -34,15 +34,177 @@
 #include "jni.h"
 
 #include "art_jvmti.h"
+#include "base/enums.h"
 #include "base/macros.h"
 #include "class_linker.h"
 #include "dex_file.h"
+#include "jni_internal.h"
+#include "mirror/class-inl.h"
+#include "mirror/object.h"
+#include "mirror/string.h"
+#include "obj_ptr-inl.h"
 #include "runtime.h"
+#include "runtime_callbacks.h"
 #include "scoped_thread_state_change-inl.h"
 #include "ScopedLocalRef.h"
+#include "ti_phase.h"
+#include "thread-inl.h"
+#include "thread_list.h"
 
 namespace openjdkjvmti {
 
+static std::vector<std::string> gSystemOnloadSegments;
+
+static art::ObjPtr<art::mirror::Object> GetSystemProperties(art::Thread* self,
+                                                            art::ClassLinker* class_linker)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  art::ObjPtr<art::mirror::Class> system_class =
+      class_linker->LookupClass(self, "Ljava/lang/System;", nullptr);
+  DCHECK(system_class != nullptr);
+  DCHECK(system_class->IsInitialized());
+
+  art::ArtField* props_field =
+      system_class->FindDeclaredStaticField("props", "Ljava/util/Properties;");
+  DCHECK(props_field != nullptr);
+
+  art::ObjPtr<art::mirror::Object> props_obj = props_field->GetObject(system_class);
+  DCHECK(props_obj != nullptr);
+
+  return props_obj;
+}
+
+static void Update() REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  if (gSystemOnloadSegments.empty()) {
+    return;
+  }
+
+  // In the on-load phase we have to modify java.class.path to influence the system classloader.
+  // As this is an unmodifiable system property, we have to access the "defaults" field.
+  art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
+  DCHECK(class_linker != nullptr);
+  art::Thread* self = art::Thread::Current();
+
+  // Prepare: collect classes, fields and methods.
+  art::ObjPtr<art::mirror::Class> properties_class =
+      class_linker->LookupClass(self, "Ljava/util/Properties;", nullptr);
+  DCHECK(properties_class != nullptr);
+
+  ScopedLocalRef<jobject> defaults_jobj(self->GetJniEnv(), nullptr);
+  {
+    art::ObjPtr<art::mirror::Object> props_obj = GetSystemProperties(self, class_linker);
+
+    art::ArtField* defaults_field =
+        properties_class->FindDeclaredInstanceField("defaults", "Ljava/util/Properties;");
+    DCHECK(defaults_field != nullptr);
+
+    art::ObjPtr<art::mirror::Object> defaults_obj = defaults_field->GetObject(props_obj);
+    DCHECK(defaults_obj != nullptr);
+    defaults_jobj.reset(self->GetJniEnv()->AddLocalReference<jobject>(defaults_obj));
+  }
+
+  art::ArtMethod* get_property =
+      properties_class->FindDeclaredVirtualMethod(
+          "getProperty",
+          "(Ljava/lang/String;)Ljava/lang/String;",
+          art::kRuntimePointerSize);
+  DCHECK(get_property != nullptr);
+  art::ArtMethod* set_property =
+      properties_class->FindDeclaredVirtualMethod(
+          "setProperty",
+          "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;",
+          art::kRuntimePointerSize);
+  DCHECK(set_property != nullptr);
+
+  // This is an allocation. Do this late to avoid the need for handles.
+  ScopedLocalRef<jobject> cp_jobj(self->GetJniEnv(), nullptr);
+  {
+    art::ObjPtr<art::mirror::Object> cp_key =
+        art::mirror::String::AllocFromModifiedUtf8(self, "java.class.path");
+    if (cp_key == nullptr) {
+      self->AssertPendingOOMException();
+      self->ClearException();
+      return;
+    }
+    cp_jobj.reset(self->GetJniEnv()->AddLocalReference<jobject>(cp_key));
+  }
+
+  // OK, now get the current value.
+  std::string str_value;
+  {
+    ScopedLocalRef<jobject> old_value(self->GetJniEnv(),
+                                      self->GetJniEnv()->CallObjectMethod(
+                                          defaults_jobj.get(),
+                                          art::jni::EncodeArtMethod(get_property),
+                                          cp_jobj.get()));
+    DCHECK(old_value.get() != nullptr);
+
+    str_value = self->DecodeJObject(old_value.get())->AsString()->ToModifiedUtf8();
+    self->GetJniEnv()->DeleteLocalRef(old_value.release());
+  }
+
+  // Update the value by appending the new segments.
+  for (const std::string& segment : gSystemOnloadSegments) {
+    if (!str_value.empty()) {
+      str_value += ":";
+    }
+    str_value += segment;
+  }
+  gSystemOnloadSegments.clear();
+
+  // Create the new value object.
+  ScopedLocalRef<jobject> new_val_jobj(self->GetJniEnv(), nullptr);
+  {
+    art::ObjPtr<art::mirror::Object> new_value =
+        art::mirror::String::AllocFromModifiedUtf8(self, str_value.c_str());
+    if (new_value == nullptr) {
+      self->AssertPendingOOMException();
+      self->ClearException();
+      return;
+    }
+
+    new_val_jobj.reset(self->GetJniEnv()->AddLocalReference<jobject>(new_value));
+  }
+
+  // Write to the defaults.
+  ScopedLocalRef<jobject> res_obj(self->GetJniEnv(),
+                                  self->GetJniEnv()->CallObjectMethod(defaults_jobj.get(),
+                                      art::jni::EncodeArtMethod(set_property),
+                                      cp_jobj.get(),
+                                      new_val_jobj.get()));
+  if (self->IsExceptionPending()) {
+    self->ClearException();
+    return;
+  }
+}
+
+struct SearchCallback : public art::RuntimePhaseCallback {
+  void NextRuntimePhase(RuntimePhase phase) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    if (phase == RuntimePhase::kStart) {
+      // It's time to update the system properties.
+      Update();
+    }
+  }
+};
+
+static SearchCallback gSearchCallback;
+
+void SearchUtil::Register() {
+  art::Runtime* runtime = art::Runtime::Current();
+
+  art::ScopedThreadStateChange stsc(art::Thread::Current(),
+                                    art::ThreadState::kWaitingForDebuggerToAttach);
+  art::ScopedSuspendAll ssa("Add search callback");
+  runtime->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gSearchCallback);
+}
+
+void SearchUtil::Unregister() {
+  art::ScopedThreadStateChange stsc(art::Thread::Current(),
+                                    art::ThreadState::kWaitingForDebuggerToAttach);
+  art::ScopedSuspendAll ssa("Remove search callback");
+  art::Runtime* runtime = art::Runtime::Current();
+  runtime->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&gSearchCallback);
+}
+
 jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env ATTRIBUTE_UNUSED,
                                                        const char* segment) {
   art::Runtime* current = art::Runtime::Current();
@@ -78,14 +240,21 @@
     return ERR(NULL_POINTER);
   }
 
-  art::Runtime* current = art::Runtime::Current();
-  if (current == nullptr) {
+  jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked();
+
+  if (phase == jvmtiPhase::JVMTI_PHASE_ONLOAD) {
+    // We could try and see whether it is a valid path. We could also try to allocate Java
+    // objects to avoid later OOME.
+    gSystemOnloadSegments.push_back(segment);
+    return ERR(NONE);
+  } else if (phase != jvmtiPhase::JVMTI_PHASE_LIVE) {
     return ERR(WRONG_PHASE);
   }
-  jobject sys_class_loader = current->GetSystemClassLoader();
+
+  jobject sys_class_loader = art::Runtime::Current()->GetSystemClassLoader();
   if (sys_class_loader == nullptr) {
-    // TODO: Support classpath change in OnLoad.
-    return ERR(WRONG_PHASE);
+    // This is unexpected.
+    return ERR(INTERNAL);
   }
 
   // We'll use BaseDexClassLoader.addDexPath, as it takes care of array resizing etc. As a downside,
diff --git a/runtime/openjdkjvmti/ti_search.h b/runtime/openjdkjvmti/ti_search.h
index 6a52e80..cd7b4be 100644
--- a/runtime/openjdkjvmti/ti_search.h
+++ b/runtime/openjdkjvmti/ti_search.h
@@ -32,12 +32,17 @@
 #ifndef ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
 #define ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
 
+#include <vector>
+
 #include "jvmti.h"
 
 namespace openjdkjvmti {
 
 class SearchUtil {
  public:
+  static void Register();
+  static void Unregister();
+
   static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment);
 
   static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment);
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 110c74b..4e76951 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -440,7 +440,7 @@
           const uint8_t* addr = reinterpret_cast<const uint8_t*>(GetCurrentQuickFrame()) + offset;
           value = *reinterpret_cast<const uint32_t*>(addr);
           uint32_t bit = (offset >> 2);
-          if (stack_map.GetNumberOfStackMaskBits(encoding.stack_map_encoding) > bit &&
+          if (code_info.GetNumberOfStackMaskBits(encoding) > bit &&
               stack_map.GetStackMaskBit(encoding.stack_map_encoding, bit)) {
             is_reference = true;
           }
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 0bf3018..693b8f4 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -114,6 +114,7 @@
 #include "native/java_lang_Thread.h"
 #include "native/java_lang_Throwable.h"
 #include "native/java_lang_VMClassLoader.h"
+#include "native/java_lang_invoke_MethodHandleImpl.h"
 #include "native/java_lang_ref_FinalizerReference.h"
 #include "native/java_lang_ref_Reference.h"
 #include "native/java_lang_reflect_Array.h"
@@ -1537,6 +1538,7 @@
   register_java_lang_Class(env);
   register_java_lang_DexCache(env);
   register_java_lang_Object(env);
+  register_java_lang_invoke_MethodHandleImpl(env);
   register_java_lang_ref_FinalizerReference(env);
   register_java_lang_reflect_Array(env);
   register_java_lang_reflect_Constructor(env);
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index 690b069..e093293 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -198,7 +198,7 @@
       << "StackMap" << header_suffix
       << std::hex
       << " [native_pc=0x" << code_offset + pc_offset << "]"
-      << " [entry_size=0x" << encoding.stack_map_size_in_bytes << "]"
+      << " [entry_size=0x" << encoding.stack_map_size_in_bits << " bits]"
       << " (dex_pc=0x" << GetDexPc(stack_map_encoding)
       << ", native_pc_offset=0x" << pc_offset
       << ", dex_register_map_offset=0x" << GetDexRegisterMapOffset(stack_map_encoding)
@@ -206,7 +206,7 @@
       << ", register_mask=0x" << GetRegisterMask(stack_map_encoding)
       << std::dec
       << ", stack_mask=0b";
-  for (size_t i = 0, e = GetNumberOfStackMaskBits(stack_map_encoding); i < e; ++i) {
+  for (size_t i = 0, e = code_info.GetNumberOfStackMaskBits(encoding); i < e; ++i) {
     vios->Stream() << GetStackMaskBit(stack_map_encoding, e - i - 1);
   }
   vios->Stream() << ")\n";
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 5782521..679218d 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -20,6 +20,7 @@
 #include "arch/code_offset.h"
 #include "base/bit_vector.h"
 #include "base/bit_utils.h"
+#include "bit_memory_region.h"
 #include "dex_file.h"
 #include "memory_region.h"
 #include "leb128.h"
@@ -665,12 +666,14 @@
 
   ALWAYS_INLINE size_t BitSize() const { return end_offset_ - start_offset_; }
 
-  ALWAYS_INLINE int32_t Load(const MemoryRegion& region) const {
+  template <typename Region>
+  ALWAYS_INLINE int32_t Load(const Region& region) const {
     DCHECK_LE(end_offset_, region.size_in_bits());
     return static_cast<int32_t>(region.LoadBits(start_offset_, BitSize())) + min_value_;
   }
 
-  ALWAYS_INLINE void Store(MemoryRegion region, int32_t value) const {
+  template <typename Region>
+  ALWAYS_INLINE void Store(Region region, int32_t value) const {
     region.StoreBits(start_offset_, value - min_value_, BitSize());
     DCHECK_EQ(Load(region), value);
   }
@@ -686,7 +689,7 @@
   StackMapEncoding() {}
 
   // Set stack map bit layout based on given sizes.
-  // Returns the size of stack map in bytes.
+  // Returns the size of stack map in bits.
   size_t SetFromSizes(size_t native_pc_max,
                       size_t dex_pc_max,
                       size_t dex_register_map_size,
@@ -719,7 +722,7 @@
     stack_mask_bit_offset_ = dchecked_integral_cast<uint8_t>(bit_offset);
     bit_offset += stack_mask_bit_size;
 
-    return RoundUp(bit_offset, kBitsPerByte) / kBitsPerByte;
+    return bit_offset;
   }
 
   ALWAYS_INLINE FieldEncoding GetNativePcEncoding() const {
@@ -741,6 +744,10 @@
     // The end offset is not encoded. It is implicitly the end of stack map entry.
     return stack_mask_bit_offset_;
   }
+  ALWAYS_INLINE size_t GetNumberOfStackMaskBits(size_t stack_map_bits) const {
+    // Note that the stack mask bits are last.
+    return stack_map_bits - GetStackMaskBitOffset();
+  }
 
   void Dump(VariableIndentationOutputStream* vios) const;
 
@@ -769,7 +776,7 @@
 class StackMap {
  public:
   StackMap() {}
-  explicit StackMap(MemoryRegion region) : region_(region) {}
+  explicit StackMap(BitMemoryRegion region) : region_(region) {}
 
   ALWAYS_INLINE bool IsValid() const { return region_.pointer() != nullptr; }
 
@@ -817,10 +824,6 @@
     encoding.GetRegisterMaskEncoding().Store(region_, mask);
   }
 
-  ALWAYS_INLINE size_t GetNumberOfStackMaskBits(const StackMapEncoding& encoding) const {
-    return region_.size_in_bits() - encoding.GetStackMaskBitOffset();
-  }
-
   ALWAYS_INLINE bool GetStackMaskBit(const StackMapEncoding& encoding, size_t index) const {
     return region_.LoadBit(encoding.GetStackMaskBitOffset() + index);
   }
@@ -838,7 +841,9 @@
   }
 
   ALWAYS_INLINE bool Equals(const StackMap& other) const {
-    return region_.pointer() == other.region_.pointer() && region_.size() == other.region_.size();
+    return region_.pointer() == other.region_.pointer() &&
+           region_.size() == other.region_.size() &&
+           region_.BitOffset() == other.region_.BitOffset();
   }
 
   void Dump(VariableIndentationOutputStream* vios,
@@ -860,7 +865,7 @@
  private:
   static constexpr int kFixedSize = 0;
 
-  MemoryRegion region_;
+  BitMemoryRegion region_;
 
   friend class StackMapStream;
 };
@@ -1026,7 +1031,7 @@
 struct CodeInfoEncoding {
   uint32_t non_header_size;
   uint32_t number_of_stack_maps;
-  uint32_t stack_map_size_in_bytes;
+  uint32_t stack_map_size_in_bits;
   uint32_t number_of_location_catalog_entries;
   StackMapEncoding stack_map_encoding;
   InlineInfoEncoding inline_info_encoding;
@@ -1038,7 +1043,7 @@
     const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);
     non_header_size = DecodeUnsignedLeb128(&ptr);
     number_of_stack_maps = DecodeUnsignedLeb128(&ptr);
-    stack_map_size_in_bytes = DecodeUnsignedLeb128(&ptr);
+    stack_map_size_in_bits = DecodeUnsignedLeb128(&ptr);
     number_of_location_catalog_entries = DecodeUnsignedLeb128(&ptr);
     static_assert(alignof(StackMapEncoding) == 1,
                   "StackMapEncoding should not require alignment");
@@ -1059,7 +1064,7 @@
   void Compress(Vector* dest) const {
     EncodeUnsignedLeb128(dest, non_header_size);
     EncodeUnsignedLeb128(dest, number_of_stack_maps);
-    EncodeUnsignedLeb128(dest, stack_map_size_in_bytes);
+    EncodeUnsignedLeb128(dest, stack_map_size_in_bits);
     EncodeUnsignedLeb128(dest, number_of_location_catalog_entries);
     const uint8_t* stack_map_ptr = reinterpret_cast<const uint8_t*>(&stack_map_encoding);
     dest->insert(dest->end(), stack_map_ptr, stack_map_ptr + sizeof(StackMapEncoding));
@@ -1078,7 +1083,7 @@
  *
  * where CodeInfoEncoding is of the form:
  *
- *   [non_header_size, number_of_stack_maps, stack_map_size_in_bytes,
+ *   [non_header_size, number_of_stack_maps, stack_map_size_in_bits,
  *    number_of_location_catalog_entries, StackMapEncoding]
  */
 class CodeInfo {
@@ -1108,9 +1113,13 @@
         GetDexRegisterLocationCatalogSize(encoding)));
   }
 
+  ALWAYS_INLINE size_t GetNumberOfStackMaskBits(const CodeInfoEncoding& encoding) const {
+    return encoding.stack_map_encoding.GetNumberOfStackMaskBits(encoding.stack_map_size_in_bits);
+  }
+
   ALWAYS_INLINE StackMap GetStackMapAt(size_t i, const CodeInfoEncoding& encoding) const {
-    size_t stack_map_size = encoding.stack_map_size_in_bytes;
-    return StackMap(GetStackMaps(encoding).Subregion(i * stack_map_size, stack_map_size));
+    const size_t map_size = encoding.stack_map_size_in_bits;
+    return StackMap(BitMemoryRegion(GetStackMaps(encoding), i * map_size, map_size));
   }
 
   uint32_t GetNumberOfLocationCatalogEntries(const CodeInfoEncoding& encoding) const {
@@ -1128,7 +1137,8 @@
 
   // Get the size of all the stack maps of this CodeInfo object, in bytes.
   size_t GetStackMapsSize(const CodeInfoEncoding& encoding) const {
-    return encoding.stack_map_size_in_bytes * GetNumberOfStackMaps(encoding);
+    return RoundUp(encoding.stack_map_size_in_bits * GetNumberOfStackMaps(encoding), kBitsPerByte) /
+        kBitsPerByte;
   }
 
   uint32_t GetDexRegisterLocationCatalogOffset(const CodeInfoEncoding& encoding) const {
@@ -1278,7 +1288,7 @@
                  << encoding.non_header_size << "\n"
                  << encoding.number_of_location_catalog_entries << "\n"
                  << encoding.number_of_stack_maps << "\n"
-                 << encoding.stack_map_size_in_bytes;
+                 << encoding.stack_map_size_in_bits;
     }
   }
 
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 151e91c..3c7a71a 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3038,7 +3038,7 @@
       T vreg_info(m, code_info, encoding, map, visitor_);
 
       // Visit stack entries that hold pointers.
-      size_t number_of_bits = map.GetNumberOfStackMaskBits(encoding.stack_map_encoding);
+      size_t number_of_bits = code_info.GetNumberOfStackMaskBits(encoding);
       for (size_t i = 0; i < number_of_bits; ++i) {
         if (map.GetStackMaskBit(encoding.stack_map_encoding, i)) {
           auto* ref_addr = vreg_base + i;
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index d9179c3..d24a5e5 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -88,7 +88,10 @@
 void* ThreadPoolWorker::Callback(void* arg) {
   ThreadPoolWorker* worker = reinterpret_cast<ThreadPoolWorker*>(arg);
   Runtime* runtime = Runtime::Current();
-  CHECK(runtime->AttachCurrentThread(worker->name_.c_str(), true, nullptr, false));
+  CHECK(runtime->AttachCurrentThread(worker->name_.c_str(),
+                                     true,
+                                     nullptr,
+                                     worker->thread_pool_->create_peers_));
   worker->thread_ = Thread::Current();
   // Thread pool workers cannot call into java.
   worker->thread_->SetCanCallIntoJava(false);
@@ -112,7 +115,7 @@
   tasks_.clear();
 }
 
-ThreadPool::ThreadPool(const char* name, size_t num_threads)
+ThreadPool::ThreadPool(const char* name, size_t num_threads, bool create_peers)
   : name_(name),
     task_queue_lock_("task queue lock"),
     task_queue_condition_("task queue condition", task_queue_lock_),
@@ -124,7 +127,8 @@
     total_wait_time_(0),
     // Add one since the caller of constructor waits on the barrier too.
     creation_barier_(num_threads + 1),
-    max_active_workers_(num_threads) {
+    max_active_workers_(num_threads),
+    create_peers_(create_peers) {
   Thread* self = Thread::Current();
   while (GetThreadCount() < num_threads) {
     const std::string worker_name = StringPrintf("%s worker thread %zu", name_.c_str(),
@@ -217,6 +221,7 @@
 
 void ThreadPool::Wait(Thread* self, bool do_work, bool may_hold_locks) {
   if (do_work) {
+    CHECK(!create_peers_);
     Task* task = nullptr;
     while ((task = TryGetTask(self)) != nullptr) {
       task->Run(self);
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index 7ecfcd1..a465e11 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -105,11 +105,17 @@
   // Remove all tasks in the queue.
   void RemoveAllTasks(Thread* self) REQUIRES(!task_queue_lock_);
 
-  ThreadPool(const char* name, size_t num_threads);
+  // Create a named thread pool with the given number of threads.
+  //
+  // If create_peers is true, all worker threads will have a Java peer object. Note that if the
+  // pool is asked to do work on the current thread (see Wait), a peer may not be available. Wait
+  // will conservatively abort if create_peers and do_work are true.
+  ThreadPool(const char* name, size_t num_threads, bool create_peers = false);
   virtual ~ThreadPool();
 
   // Wait for all tasks currently on queue to get completed. If the pool has been stopped, only
   // wait till all already running tasks are done.
+  // When the pool was created with peers for workers, do_work must not be true (see ThreadPool()).
   void Wait(Thread* self, bool do_work, bool may_hold_locks) REQUIRES(!task_queue_lock_);
 
   size_t GetTaskCount(Thread* self) REQUIRES(!task_queue_lock_);
@@ -159,6 +165,7 @@
   uint64_t total_wait_time_;
   Barrier creation_barier_;
   size_t max_active_workers_ GUARDED_BY(task_queue_lock_);
+  const bool create_peers_;
 
  private:
   friend class ThreadPoolWorker;
diff --git a/runtime/thread_pool_test.cc b/runtime/thread_pool_test.cc
index 14c2c3b..28aa21f 100644
--- a/runtime/thread_pool_test.cc
+++ b/runtime/thread_pool_test.cc
@@ -20,6 +20,7 @@
 
 #include "atomic.h"
 #include "common_runtime_test.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 
 namespace art {
@@ -159,4 +160,55 @@
   EXPECT_EQ((1 << depth) - 1, count.LoadSequentiallyConsistent());
 }
 
+class PeerTask : public Task {
+ public:
+  PeerTask() {}
+
+  void Run(Thread* self) {
+    ScopedObjectAccess soa(self);
+    CHECK(self->GetPeer() != nullptr);
+  }
+
+  void Finalize() {
+    delete this;
+  }
+};
+
+class NoPeerTask : public Task {
+ public:
+  NoPeerTask() {}
+
+  void Run(Thread* self) {
+    ScopedObjectAccess soa(self);
+    CHECK(self->GetPeer() == nullptr);
+  }
+
+  void Finalize() {
+    delete this;
+  }
+};
+
+// Tests for create_peer functionality.
+TEST_F(ThreadPoolTest, PeerTest) {
+  Thread* self = Thread::Current();
+  {
+    ThreadPool thread_pool("Thread pool test thread pool", 1);
+    thread_pool.AddTask(self, new NoPeerTask());
+    thread_pool.StartWorkers(self);
+    thread_pool.Wait(self, false, false);
+  }
+
+  {
+    // To create peers, the runtime needs to be started.
+    self->TransitionFromSuspendedToRunnable();
+    bool started = runtime_->Start();
+    ASSERT_TRUE(started);
+
+    ThreadPool thread_pool("Thread pool test thread pool", 1, true);
+    thread_pool.AddTask(self, new PeerTask());
+    thread_pool.StartWorkers(self);
+    thread_pool.Wait(self, false, false);
+  }
+}
+
 }  // namespace art
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
index bd1b044..2812c21 100644
--- a/runtime/utils/dex_cache_arrays_layout-inl.h
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -48,9 +48,11 @@
     : DexCacheArraysLayout(pointer_size, dex_file->GetHeader()) {
 }
 
-inline constexpr size_t DexCacheArraysLayout::Alignment() {
-  // GcRoot<> alignment is 4, i.e. lower than or equal to the pointer alignment.
-  static_assert(alignof(GcRoot<mirror::Class>) == 4, "Expecting alignof(GcRoot<>) == 4");
+constexpr size_t DexCacheArraysLayout::Alignment() {
+  // mirror::Type/String/MethodTypeDexCacheType alignment is 8,
+  // i.e. higher than or equal to the pointer alignment.
+  static_assert(alignof(mirror::TypeDexCacheType) == 8,
+                "Expecting alignof(ClassDexCacheType) == 8");
   static_assert(alignof(mirror::StringDexCacheType) == 8,
                 "Expecting alignof(StringDexCacheType) == 8");
   static_assert(alignof(mirror::MethodTypeDexCacheType) == 8,
@@ -60,17 +62,22 @@
 }
 
 template <typename T>
-static constexpr PointerSize GcRootAsPointerSize() {
+constexpr PointerSize GcRootAsPointerSize() {
   static_assert(sizeof(GcRoot<T>) == 4U, "Unexpected GcRoot size");
   return PointerSize::k32;
 }
 
 inline size_t DexCacheArraysLayout::TypeOffset(dex::TypeIndex type_idx) const {
-  return types_offset_ + ElementOffset(GcRootAsPointerSize<mirror::Class>(), type_idx.index_);
+  return types_offset_ + ElementOffset(PointerSize::k64,
+                                       type_idx.index_ % mirror::DexCache::kDexCacheTypeCacheSize);
 }
 
 inline size_t DexCacheArraysLayout::TypesSize(size_t num_elements) const {
-  return ArraySize(GcRootAsPointerSize<mirror::Class>(), num_elements);
+  size_t cache_size = mirror::DexCache::kDexCacheTypeCacheSize;
+  if (num_elements < cache_size) {
+    cache_size = num_elements;
+  }
+  return ArraySize(PointerSize::k64, cache_size);
 }
 
 inline size_t DexCacheArraysLayout::TypesAlignment() const {
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index b915457..ba429d8 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2399,7 +2399,8 @@
       const RegType& res_type = ResolveClassAndCheckAccess(type_idx);
       if (res_type.IsConflict()) {
         // If this is a primitive type, fail HARD.
-        mirror::Class* klass = dex_cache_->GetResolvedType(type_idx);
+        ObjPtr<mirror::Class> klass =
+            ClassLinker::LookupResolvedType(type_idx, dex_cache_.Get(), class_loader_.Get());
         if (klass != nullptr && klass->IsPrimitive()) {
           Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "using primitive type "
               << dex_file_->StringByTypeIdx(type_idx) << " in instanceof in "
@@ -3684,9 +3685,10 @@
 }
 
 const RegType& MethodVerifier::ResolveClassAndCheckAccess(dex::TypeIndex class_idx) {
-  mirror::Class* klass = dex_cache_->GetResolvedType(class_idx);
+  mirror::Class* klass =
+      ClassLinker::LookupResolvedType(class_idx, dex_cache_.Get(), class_loader_.Get()).Ptr();
   const RegType* result = nullptr;
-  if (klass != nullptr) {
+  if (klass != nullptr && !klass->IsErroneous()) {
     bool precise = klass->CannotBeAssignedFromOtherTypes();
     if (precise && !IsInstantiableOrPrimitive(klass)) {
       const char* descriptor = dex_file_->StringByTypeIdx(class_idx);
diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java
index db43768..a46a23c 100644
--- a/test/552-checker-sharpening/src/Main.java
+++ b/test/552-checker-sharpening/src/Main.java
@@ -381,10 +381,6 @@
   /// CHECK-START-ARM: java.lang.Class Main.$noinline$getOtherClass() sharpening (after)
   /// CHECK:                LoadClass load_kind:BssEntry class_name:Other
 
-  /// CHECK-START-ARM: java.lang.Class Main.$noinline$getOtherClass() dex_cache_array_fixups_arm (after)
-  /// CHECK-DAG:            ArmDexCacheArraysBase
-  /// CHECK-DAG:            LoadClass load_kind:BssEntry class_name:Other
-
   /// CHECK-START-ARM64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after)
   /// CHECK:                LoadClass load_kind:BssEntry class_name:Other
 
diff --git a/test/626-const-class-linking/clear_dex_cache_types.cc b/test/626-const-class-linking/clear_dex_cache_types.cc
index b035896..6d4b645 100644
--- a/test/626-const-class-linking/clear_dex_cache_types.cc
+++ b/test/626-const-class-linking/clear_dex_cache_types.cc
@@ -24,7 +24,8 @@
   ScopedObjectAccess soa(Thread::Current());
   mirror::DexCache* dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache();
   for (size_t i = 0, num_types = dex_cache->NumResolvedTypes(); i != num_types; ++i) {
-    dex_cache->SetResolvedType(dex::TypeIndex(i), ObjPtr<mirror::Class>(nullptr));
+    mirror::TypeDexCachePair cleared(nullptr, mirror::TypeDexCachePair::InvalidIndexForSlot(i));
+    dex_cache->GetResolvedTypes()[i].store(cleared, std::memory_order_relaxed);
   }
 }
 
diff --git a/test/635-checker-arm64-volatile-load-cc/expected.txt b/test/635-checker-arm64-volatile-load-cc/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/635-checker-arm64-volatile-load-cc/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/635-checker-arm64-volatile-load-cc/info.txt b/test/635-checker-arm64-volatile-load-cc/info.txt
new file mode 100644
index 0000000..5d67df4
--- /dev/null
+++ b/test/635-checker-arm64-volatile-load-cc/info.txt
@@ -0,0 +1,3 @@
+Regression test checking that the VIXL ARM64 scratch register pool is
+not exhausted when generating a volatile field load with a large
+offset with (Baker) read barriers (b/34726333).
diff --git a/test/635-checker-arm64-volatile-load-cc/src/Main.java b/test/635-checker-arm64-volatile-load-cc/src/Main.java
new file mode 100644
index 0000000..6a26e94
--- /dev/null
+++ b/test/635-checker-arm64-volatile-load-cc/src/Main.java
@@ -0,0 +1,284 @@
+/*
+ * 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 {
+
+  static volatile Object s000, s001, s002, s003, s004, s005, s006, s007, s008, s009;
+  static volatile Object s010, s011, s012, s013, s014, s015, s016, s017, s018, s019;
+  static volatile Object s020, s021, s022, s023, s024, s025, s026, s027, s028, s029;
+  static volatile Object s030, s031, s032, s033, s034, s035, s036, s037, s038, s039;
+  static volatile Object s040, s041, s042, s043, s044, s045, s046, s047, s048, s049;
+  static volatile Object s050, s051, s052, s053, s054, s055, s056, s057, s058, s059;
+  static volatile Object s060, s061, s062, s063, s064, s065, s066, s067, s068, s069;
+  static volatile Object s070, s071, s072, s073, s074, s075, s076, s077, s078, s079;
+  static volatile Object s080, s081, s082, s083, s084, s085, s086, s087, s088, s089;
+  static volatile Object s090, s091, s092, s093, s094, s095, s096, s097, s098, s099;
+
+  static volatile Object s100, s101, s102, s103, s104, s105, s106, s107, s108, s109;
+  static volatile Object s110, s111, s112, s113, s114, s115, s116, s117, s118, s119;
+  static volatile Object s120, s121, s122, s123, s124, s125, s126, s127, s128, s129;
+  static volatile Object s130, s131, s132, s133, s134, s135, s136, s137, s138, s139;
+  static volatile Object s140, s141, s142, s143, s144, s145, s146, s147, s148, s149;
+  static volatile Object s150, s151, s152, s153, s154, s155, s156, s157, s158, s159;
+  static volatile Object s160, s161, s162, s163, s164, s165, s166, s167, s168, s169;
+  static volatile Object s170, s171, s172, s173, s174, s175, s176, s177, s178, s179;
+  static volatile Object s180, s181, s182, s183, s184, s185, s186, s187, s188, s189;
+  static volatile Object s190, s191, s192, s193, s194, s195, s196, s197, s198, s199;
+
+  static volatile Object s200, s201, s202, s203, s204, s205, s206, s207, s208, s209;
+  static volatile Object s210, s211, s212, s213, s214, s215, s216, s217, s218, s219;
+  static volatile Object s220, s221, s222, s223, s224, s225, s226, s227, s228, s229;
+  static volatile Object s230, s231, s232, s233, s234, s235, s236, s237, s238, s239;
+  static volatile Object s240, s241, s242, s243, s244, s245, s246, s247, s248, s249;
+  static volatile Object s250, s251, s252, s253, s254, s255, s256, s257, s258, s259;
+  static volatile Object s260, s261, s262, s263, s264, s265, s266, s267, s268, s269;
+  static volatile Object s270, s271, s272, s273, s274, s275, s276, s277, s278, s279;
+  static volatile Object s280, s281, s282, s283, s284, s285, s286, s287, s288, s289;
+  static volatile Object s290, s291, s292, s293, s294, s295, s296, s297, s298, s299;
+
+  static volatile Object s300, s301, s302, s303, s304, s305, s306, s307, s308, s309;
+  static volatile Object s310, s311, s312, s313, s314, s315, s316, s317, s318, s319;
+  static volatile Object s320, s321, s322, s323, s324, s325, s326, s327, s328, s329;
+  static volatile Object s330, s331, s332, s333, s334, s335, s336, s337, s338, s339;
+  static volatile Object s340, s341, s342, s343, s344, s345, s346, s347, s348, s349;
+  static volatile Object s350, s351, s352, s353, s354, s355, s356, s357, s358, s359;
+  static volatile Object s360, s361, s362, s363, s364, s365, s366, s367, s368, s369;
+  static volatile Object s370, s371, s372, s373, s374, s375, s376, s377, s378, s379;
+  static volatile Object s380, s381, s382, s383, s384, s385, s386, s387, s388, s389;
+  static volatile Object s390, s391, s392, s393, s394, s395, s396, s397, s398, s399;
+
+  static volatile Object s400, s401, s402, s403, s404, s405, s406, s407, s408, s409;
+  static volatile Object s410, s411, s412, s413, s414, s415, s416, s417, s418, s419;
+  static volatile Object s420, s421, s422, s423, s424, s425, s426, s427, s428, s429;
+  static volatile Object s430, s431, s432, s433, s434, s435, s436, s437, s438, s439;
+  static volatile Object s440, s441, s442, s443, s444, s445, s446, s447, s448, s449;
+  static volatile Object s450, s451, s452, s453, s454, s455, s456, s457, s458, s459;
+  static volatile Object s460, s461, s462, s463, s464, s465, s466, s467, s468, s469;
+  static volatile Object s470, s471, s472, s473, s474, s475, s476, s477, s478, s479;
+  static volatile Object s480, s481, s482, s483, s484, s485, s486, s487, s488, s489;
+  static volatile Object s490, s491, s492, s493, s494, s495, s496, s497, s498, s499;
+
+  static volatile Object s500, s501, s502, s503, s504, s505, s506, s507, s508, s509;
+  static volatile Object s510, s511, s512, s513, s514, s515, s516, s517, s518, s519;
+  static volatile Object s520, s521, s522, s523, s524, s525, s526, s527, s528, s529;
+  static volatile Object s530, s531, s532, s533, s534, s535, s536, s537, s538, s539;
+  static volatile Object s540, s541, s542, s543, s544, s545, s546, s547, s548, s549;
+  static volatile Object s550, s551, s552, s553, s554, s555, s556, s557, s558, s559;
+  static volatile Object s560, s561, s562, s563, s564, s565, s566, s567, s568, s569;
+  static volatile Object s570, s571, s572, s573, s574, s575, s576, s577, s578, s579;
+  static volatile Object s580, s581, s582, s583, s584, s585, s586, s587, s588, s589;
+  static volatile Object s590, s591, s592, s593, s594, s595, s596, s597, s598, s599;
+
+  static volatile Object s600, s601, s602, s603, s604, s605, s606, s607, s608, s609;
+  static volatile Object s610, s611, s612, s613, s614, s615, s616, s617, s618, s619;
+  static volatile Object s620, s621, s622, s623, s624, s625, s626, s627, s628, s629;
+  static volatile Object s630, s631, s632, s633, s634, s635, s636, s637, s638, s639;
+  static volatile Object s640, s641, s642, s643, s644, s645, s646, s647, s648, s649;
+  static volatile Object s650, s651, s652, s653, s654, s655, s656, s657, s658, s659;
+  static volatile Object s660, s661, s662, s663, s664, s665, s666, s667, s668, s669;
+  static volatile Object s670, s671, s672, s673, s674, s675, s676, s677, s678, s679;
+  static volatile Object s680, s681, s682, s683, s684, s685, s686, s687, s688, s689;
+  static volatile Object s690, s691, s692, s693, s694, s695, s696, s697, s698, s699;
+
+  static volatile Object s700, s701, s702, s703, s704, s705, s706, s707, s708, s709;
+  static volatile Object s710, s711, s712, s713, s714, s715, s716, s717, s718, s719;
+  static volatile Object s720, s721, s722, s723, s724, s725, s726, s727, s728, s729;
+  static volatile Object s730, s731, s732, s733, s734, s735, s736, s737, s738, s739;
+  static volatile Object s740, s741, s742, s743, s744, s745, s746, s747, s748, s749;
+  static volatile Object s750, s751, s752, s753, s754, s755, s756, s757, s758, s759;
+  static volatile Object s760, s761, s762, s763, s764, s765, s766, s767, s768, s769;
+  static volatile Object s770, s771, s772, s773, s774, s775, s776, s777, s778, s779;
+  static volatile Object s780, s781, s782, s783, s784, s785, s786, s787, s788, s789;
+  static volatile Object s790, s791, s792, s793, s794, s795, s796, s797, s798, s799;
+
+  static volatile Object s800, s801, s802, s803, s804, s805, s806, s807, s808, s809;
+  static volatile Object s810, s811, s812, s813, s814, s815, s816, s817, s818, s819;
+  static volatile Object s820, s821, s822, s823, s824, s825, s826, s827, s828, s829;
+  static volatile Object s830, s831, s832, s833, s834, s835, s836, s837, s838, s839;
+  static volatile Object s840, s841, s842, s843, s844, s845, s846, s847, s848, s849;
+  static volatile Object s850, s851, s852, s853, s854, s855, s856, s857, s858, s859;
+  static volatile Object s860, s861, s862, s863, s864, s865, s866, s867, s868, s869;
+  static volatile Object s870, s871, s872, s873, s874, s875, s876, s877, s878, s879;
+  static volatile Object s880, s881, s882, s883, s884, s885, s886, s887, s888, s889;
+  static volatile Object s890, s891, s892, s893, s894, s895, s896, s897, s898, s899;
+
+  static volatile Object s900, s901, s902, s903, s904, s905, s906, s907, s908, s909;
+  static volatile Object s910, s911, s912, s913, s914, s915, s916, s917, s918, s919;
+  static volatile Object s920, s921, s922, s923, s924, s925, s926, s927, s928, s929;
+  static volatile Object s930, s931, s932, s933, s934, s935, s936, s937, s938, s939;
+  static volatile Object s940, s941, s942, s943, s944, s945, s946, s947, s948, s949;
+  static volatile Object s950, s951, s952, s953, s954, s955, s956, s957, s958, s959;
+  static volatile Object s960, s961, s962, s963, s964, s965, s966, s967, s968, s969;
+  static volatile Object s970, s971, s972, s973, s974, s975, s976, s977, s978, s979;
+  static volatile Object s980, s981, s982, s983, s984, s985, s986, s987, s988, s989;
+  static volatile Object s990, s991, s992, s993, s994, s995, s996, s997, s998, s999;
+
+
+  volatile Object i0000, i0001, i0002, i0003, i0004, i0005, i0006, i0007, i0008, i0009;
+  volatile Object i0010, i0011, i0012, i0013, i0014, i0015, i0016, i0017, i0018, i0019;
+  volatile Object i0020, i0021, i0022, i0023, i0024, i0025, i0026, i0027, i0028, i0029;
+  volatile Object i0030, i0031, i0032, i0033, i0034, i0035, i0036, i0037, i0038, i0039;
+  volatile Object i0040, i0041, i0042, i0043, i0044, i0045, i0046, i0047, i0048, i0049;
+  volatile Object i0050, i0051, i0052, i0053, i0054, i0055, i0056, i0057, i0058, i0059;
+  volatile Object i0060, i0061, i0062, i0063, i0064, i0065, i0066, i0067, i0068, i0069;
+  volatile Object i0070, i0071, i0072, i0073, i0074, i0075, i0076, i0077, i0078, i0079;
+  volatile Object i0080, i0081, i0082, i0083, i0084, i0085, i0086, i0087, i0088, i0089;
+  volatile Object i0090, i0091, i0092, i0093, i0094, i0095, i0096, i0097, i0098, i0099;
+
+  volatile Object i0100, i0101, i0102, i0103, i0104, i0105, i0106, i0107, i0108, i0109;
+  volatile Object i0110, i0111, i0112, i0113, i0114, i0115, i0116, i0117, i0118, i0119;
+  volatile Object i0120, i0121, i0122, i0123, i0124, i0125, i0126, i0127, i0128, i0129;
+  volatile Object i0130, i0131, i0132, i0133, i0134, i0135, i0136, i0137, i0138, i0139;
+  volatile Object i0140, i0141, i0142, i0143, i0144, i0145, i0146, i0147, i0148, i0149;
+  volatile Object i0150, i0151, i0152, i0153, i0154, i0155, i0156, i0157, i0158, i0159;
+  volatile Object i0160, i0161, i0162, i0163, i0164, i0165, i0166, i0167, i0168, i0169;
+  volatile Object i0170, i0171, i0172, i0173, i0174, i0175, i0176, i0177, i0178, i0179;
+  volatile Object i0180, i0181, i0182, i0183, i0184, i0185, i0186, i0187, i0188, i0189;
+  volatile Object i0190, i0191, i0192, i0193, i0194, i0195, i0196, i0197, i0198, i0199;
+
+  volatile Object i0200, i0201, i0202, i0203, i0204, i0205, i0206, i0207, i0208, i0209;
+  volatile Object i0210, i0211, i0212, i0213, i0214, i0215, i0216, i0217, i0218, i0219;
+  volatile Object i0220, i0221, i0222, i0223, i0224, i0225, i0226, i0227, i0228, i0229;
+  volatile Object i0230, i0231, i0232, i0233, i0234, i0235, i0236, i0237, i0238, i0239;
+  volatile Object i0240, i0241, i0242, i0243, i0244, i0245, i0246, i0247, i0248, i0249;
+  volatile Object i0250, i0251, i0252, i0253, i0254, i0255, i0256, i0257, i0258, i0259;
+  volatile Object i0260, i0261, i0262, i0263, i0264, i0265, i0266, i0267, i0268, i0269;
+  volatile Object i0270, i0271, i0272, i0273, i0274, i0275, i0276, i0277, i0278, i0279;
+  volatile Object i0280, i0281, i0282, i0283, i0284, i0285, i0286, i0287, i0288, i0289;
+  volatile Object i0290, i0291, i0292, i0293, i0294, i0295, i0296, i0297, i0298, i0299;
+
+  volatile Object i0300, i0301, i0302, i0303, i0304, i0305, i0306, i0307, i0308, i0309;
+  volatile Object i0310, i0311, i0312, i0313, i0314, i0315, i0316, i0317, i0318, i0319;
+  volatile Object i0320, i0321, i0322, i0323, i0324, i0325, i0326, i0327, i0328, i0329;
+  volatile Object i0330, i0331, i0332, i0333, i0334, i0335, i0336, i0337, i0338, i0339;
+  volatile Object i0340, i0341, i0342, i0343, i0344, i0345, i0346, i0347, i0348, i0349;
+  volatile Object i0350, i0351, i0352, i0353, i0354, i0355, i0356, i0357, i0358, i0359;
+  volatile Object i0360, i0361, i0362, i0363, i0364, i0365, i0366, i0367, i0368, i0369;
+  volatile Object i0370, i0371, i0372, i0373, i0374, i0375, i0376, i0377, i0378, i0379;
+  volatile Object i0380, i0381, i0382, i0383, i0384, i0385, i0386, i0387, i0388, i0389;
+  volatile Object i0390, i0391, i0392, i0393, i0394, i0395, i0396, i0397, i0398, i0399;
+
+  volatile Object i0400, i0401, i0402, i0403, i0404, i0405, i0406, i0407, i0408, i0409;
+  volatile Object i0410, i0411, i0412, i0413, i0414, i0415, i0416, i0417, i0418, i0419;
+  volatile Object i0420, i0421, i0422, i0423, i0424, i0425, i0426, i0427, i0428, i0429;
+  volatile Object i0430, i0431, i0432, i0433, i0434, i0435, i0436, i0437, i0438, i0439;
+  volatile Object i0440, i0441, i0442, i0443, i0444, i0445, i0446, i0447, i0448, i0449;
+  volatile Object i0450, i0451, i0452, i0453, i0454, i0455, i0456, i0457, i0458, i0459;
+  volatile Object i0460, i0461, i0462, i0463, i0464, i0465, i0466, i0467, i0468, i0469;
+  volatile Object i0470, i0471, i0472, i0473, i0474, i0475, i0476, i0477, i0478, i0479;
+  volatile Object i0480, i0481, i0482, i0483, i0484, i0485, i0486, i0487, i0488, i0489;
+  volatile Object i0490, i0491, i0492, i0493, i0494, i0495, i0496, i0497, i0498, i0499;
+
+  volatile Object i0500, i0501, i0502, i0503, i0504, i0505, i0506, i0507, i0508, i0509;
+  volatile Object i0510, i0511, i0512, i0513, i0514, i0515, i0516, i0517, i0518, i0519;
+  volatile Object i0520, i0521, i0522, i0523, i0524, i0525, i0526, i0527, i0528, i0529;
+  volatile Object i0530, i0531, i0532, i0533, i0534, i0535, i0536, i0537, i0538, i0539;
+  volatile Object i0540, i0541, i0542, i0543, i0544, i0545, i0546, i0547, i0548, i0549;
+  volatile Object i0550, i0551, i0552, i0553, i0554, i0555, i0556, i0557, i0558, i0559;
+  volatile Object i0560, i0561, i0562, i0563, i0564, i0565, i0566, i0567, i0568, i0569;
+  volatile Object i0570, i0571, i0572, i0573, i0574, i0575, i0576, i0577, i0578, i0579;
+  volatile Object i0580, i0581, i0582, i0583, i0584, i0585, i0586, i0587, i0588, i0589;
+  volatile Object i0590, i0591, i0592, i0593, i0594, i0595, i0596, i0597, i0598, i0599;
+
+  volatile Object i0600, i0601, i0602, i0603, i0604, i0605, i0606, i0607, i0608, i0609;
+  volatile Object i0610, i0611, i0612, i0613, i0614, i0615, i0616, i0617, i0618, i0619;
+  volatile Object i0620, i0621, i0622, i0623, i0624, i0625, i0626, i0627, i0628, i0629;
+  volatile Object i0630, i0631, i0632, i0633, i0634, i0635, i0636, i0637, i0638, i0639;
+  volatile Object i0640, i0641, i0642, i0643, i0644, i0645, i0646, i0647, i0648, i0649;
+  volatile Object i0650, i0651, i0652, i0653, i0654, i0655, i0656, i0657, i0658, i0659;
+  volatile Object i0660, i0661, i0662, i0663, i0664, i0665, i0666, i0667, i0668, i0669;
+  volatile Object i0670, i0671, i0672, i0673, i0674, i0675, i0676, i0677, i0678, i0679;
+  volatile Object i0680, i0681, i0682, i0683, i0684, i0685, i0686, i0687, i0688, i0689;
+  volatile Object i0690, i0691, i0692, i0693, i0694, i0695, i0696, i0697, i0698, i0699;
+
+  volatile Object i0700, i0701, i0702, i0703, i0704, i0705, i0706, i0707, i0708, i0709;
+  volatile Object i0710, i0711, i0712, i0713, i0714, i0715, i0716, i0717, i0718, i0719;
+  volatile Object i0720, i0721, i0722, i0723, i0724, i0725, i0726, i0727, i0728, i0729;
+  volatile Object i0730, i0731, i0732, i0733, i0734, i0735, i0736, i0737, i0738, i0739;
+  volatile Object i0740, i0741, i0742, i0743, i0744, i0745, i0746, i0747, i0748, i0749;
+  volatile Object i0750, i0751, i0752, i0753, i0754, i0755, i0756, i0757, i0758, i0759;
+  volatile Object i0760, i0761, i0762, i0763, i0764, i0765, i0766, i0767, i0768, i0769;
+  volatile Object i0770, i0771, i0772, i0773, i0774, i0775, i0776, i0777, i0778, i0779;
+  volatile Object i0780, i0781, i0782, i0783, i0784, i0785, i0786, i0787, i0788, i0789;
+  volatile Object i0790, i0791, i0792, i0793, i0794, i0795, i0796, i0797, i0798, i0799;
+
+  volatile Object i0800, i0801, i0802, i0803, i0804, i0805, i0806, i0807, i0808, i0809;
+  volatile Object i0810, i0811, i0812, i0813, i0814, i0815, i0816, i0817, i0818, i0819;
+  volatile Object i0820, i0821, i0822, i0823, i0824, i0825, i0826, i0827, i0828, i0829;
+  volatile Object i0830, i0831, i0832, i0833, i0834, i0835, i0836, i0837, i0838, i0839;
+  volatile Object i0840, i0841, i0842, i0843, i0844, i0845, i0846, i0847, i0848, i0849;
+  volatile Object i0850, i0851, i0852, i0853, i0854, i0855, i0856, i0857, i0858, i0859;
+  volatile Object i0860, i0861, i0862, i0863, i0864, i0865, i0866, i0867, i0868, i0869;
+  volatile Object i0870, i0871, i0872, i0873, i0874, i0875, i0876, i0877, i0878, i0879;
+  volatile Object i0880, i0881, i0882, i0883, i0884, i0885, i0886, i0887, i0888, i0889;
+  volatile Object i0890, i0891, i0892, i0893, i0894, i0895, i0896, i0897, i0898, i0899;
+
+  volatile Object i0900, i0901, i0902, i0903, i0904, i0905, i0906, i0907, i0908, i0909;
+  volatile Object i0910, i0911, i0912, i0913, i0914, i0915, i0916, i0917, i0918, i0919;
+  volatile Object i0920, i0921, i0922, i0923, i0924, i0925, i0926, i0927, i0928, i0929;
+  volatile Object i0930, i0931, i0932, i0933, i0934, i0935, i0936, i0937, i0938, i0939;
+  volatile Object i0940, i0941, i0942, i0943, i0944, i0945, i0946, i0947, i0948, i0949;
+  volatile Object i0950, i0951, i0952, i0953, i0954, i0955, i0956, i0957, i0958, i0959;
+  volatile Object i0960, i0961, i0962, i0963, i0964, i0965, i0966, i0967, i0968, i0969;
+  volatile Object i0970, i0971, i0972, i0973, i0974, i0975, i0976, i0977, i0978, i0979;
+  volatile Object i0980, i0981, i0982, i0983, i0984, i0985, i0986, i0987, i0988, i0989;
+  volatile Object i0990, i0991, i0992, i0993, i0994, i0995, i0996, i0997, i0998, i0999;
+
+  volatile Object i1000, i1001, i1002, i1003, i1004, i1005, i1006, i1007, i1008, i1009;
+  volatile Object i1010, i1011, i1012, i1013, i1014, i1015, i1016, i1017, i1018, i1019;
+  volatile Object i1020, i1021, i1022, i1023, i1024, i1025, i1026, i1027, i1028, i1029;
+  volatile Object i1030, i1031, i1032, i1033, i1034, i1035, i1036, i1037, i1038, i1039;
+  volatile Object i1040, i1041, i1042, i1043, i1044, i1045, i1046, i1047, i1048, i1049;
+  volatile Object i1050, i1051, i1052, i1053, i1054, i1055, i1056, i1057, i1058, i1059;
+  volatile Object i1060, i1061, i1062, i1063, i1064, i1065, i1066, i1067, i1068, i1069;
+  volatile Object i1070, i1071, i1072, i1073, i1074, i1075, i1076, i1077, i1078, i1079;
+  volatile Object i1080, i1081, i1082, i1083, i1084, i1085, i1086, i1087, i1088, i1089;
+  volatile Object i1090, i1091, i1092, i1093, i1094, i1095, i1096, i1097, i1098, i1099;
+
+
+  // Note: ARM64, registers X16 and X17 are respectively IP0 and IP1,
+  // the scratch registers used by the VIXL AArch64 assembler (and to
+  // some extent, by ART's ARM64 code generator).
+
+  /// CHECK-START-ARM64: void Main.testStaticVolatileFieldGetWithLargeOffset() disassembly (after)
+  /// CHECK:               StaticFieldGet
+  /// CHECK:                 mov x17, #<<Offset:0x[0-9a-f]{4}>>
+  /// CHECK:                 add x16, {{x\d+}}, x17
+  /// CHECK:                 ldar {{w\d+}}, [x16]
+  static void testStaticVolatileFieldGetWithLargeOffset() {
+    // The offset of this static field cannot be encoded as an immediate on ARM64.
+    Object s = s999;
+  }
+
+  /// CHECK-START-ARM64: void Main.testInstanceVolatileFieldGetWithLargeOffset() disassembly (after)
+  /// CHECK:               InstanceFieldGet
+  /// CHECK:                 mov x17, #<<Offset:0x[0-9a-f]{4}>>
+  /// CHECK:                 add x16, {{x\d+}}, x17
+  /// CHECK:                 ldar {{w\d+}}, [x16]
+  void testInstanceVolatileFieldGetWithLargeOffset() {
+    // The offset of this instance field cannot be encoded as an immediate on ARM64.
+    Object i = i1029;
+  }
+
+
+  public static void main(String[] args) {
+    testStaticVolatileFieldGetWithLargeOffset();
+    Main m = new Main();
+    m.testInstanceVolatileFieldGetWithLargeOffset();
+    System.out.println("passed");
+  }
+
+}
diff --git a/test/936-search-onload/build b/test/936-search-onload/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/936-search-onload/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/936-search-onload/expected.txt b/test/936-search-onload/expected.txt
new file mode 100644
index 0000000..2eec8e1
--- /dev/null
+++ b/test/936-search-onload/expected.txt
@@ -0,0 +1,3 @@
+B was loaded with boot classloader
+A was loaded with system classloader
+Done
diff --git a/test/936-search-onload/info.txt b/test/936-search-onload/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/936-search-onload/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/936-search-onload/run b/test/936-search-onload/run
new file mode 100755
index 0000000..67923a7
--- /dev/null
+++ b/test/936-search-onload/run
@@ -0,0 +1,21 @@
+#!/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 "$@" --jvmti \
+                   --no-app-image
diff --git a/test/936-search-onload/search_onload.cc b/test/936-search-onload/search_onload.cc
new file mode 100644
index 0000000..2286a46
--- /dev/null
+++ b/test/936-search-onload/search_onload.cc
@@ -0,0 +1,63 @@
+/*
+ * 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 "search_onload.h"
+
+#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 Test936SearchOnload {
+
+jint OnLoad(JavaVM* vm,
+            char* options ATTRIBUTE_UNUSED,
+            void* reserved ATTRIBUTE_UNUSED) {
+  if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+    printf("Unable to get jvmti env!\n");
+    return 1;
+  }
+  SetAllCapabilities(jvmti_env);
+
+  char* dex_loc = getenv("DEX_LOCATION");
+  std::string dex1 = android::base::StringPrintf("%s/936-search-onload.jar", dex_loc);
+  std::string dex2 = android::base::StringPrintf("%s/936-search-onload-ex.jar", dex_loc);
+
+  jvmtiError result = jvmti_env->AddToBootstrapClassLoaderSearch(dex1.c_str());
+  if (result != JVMTI_ERROR_NONE) {
+    printf("Could not add to bootstrap classloader.\n");
+    return 1;
+  }
+
+  result = jvmti_env->AddToSystemClassLoaderSearch(dex2.c_str());
+  if (result != JVMTI_ERROR_NONE) {
+    printf("Could not add to system classloader.\n");
+    return 1;
+  }
+
+  return JNI_OK;
+}
+
+}  // namespace Test936SearchOnload
+}  // namespace art
diff --git a/test/936-search-onload/search_onload.h b/test/936-search-onload/search_onload.h
new file mode 100644
index 0000000..e556892
--- /dev/null
+++ b/test/936-search-onload/search_onload.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ART_TEST_936_SEARCH_ONLOAD_SEARCH_ONLOAD_H_
+#define ART_TEST_936_SEARCH_ONLOAD_SEARCH_ONLOAD_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test936SearchOnload {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+}  // namespace Test936SearchOnload
+}  // namespace art
+
+#endif  // ART_TEST_936_SEARCH_ONLOAD_SEARCH_ONLOAD_H_
diff --git a/test/936-search-onload/src-ex/A.java b/test/936-search-onload/src-ex/A.java
new file mode 100644
index 0000000..64acb2f
--- /dev/null
+++ b/test/936-search-onload/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/936-search-onload/src/B.java b/test/936-search-onload/src/B.java
new file mode 100644
index 0000000..f1458c3
--- /dev/null
+++ b/test/936-search-onload/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/936-search-onload/src/Main.java b/test/936-search-onload/src/Main.java
new file mode 100644
index 0000000..2e7a871
--- /dev/null
+++ b/test/936-search-onload/src/Main.java
@@ -0,0 +1,47 @@
+/*
+ * 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 {
+    doTest();
+  }
+
+  private static void doTest() throws Exception {
+    doTest(true, "B");
+    doTest(false, "A");
+    System.out.println("Done");
+  }
+
+  private static void doTest(boolean boot, String className) throws Exception {
+    ClassLoader expectedClassLoader;
+    if (boot) {
+      expectedClassLoader = Object.class.getClassLoader();
+    } else {
+      expectedClassLoader = ClassLoader.getSystemClassLoader();
+    }
+
+    Class<?> c = Class.forName(className, false, ClassLoader.getSystemClassLoader());
+    if (c.getClassLoader() != expectedClassLoader) {
+      throw new RuntimeException(className + "(" + boot + "): " +
+          c.getClassLoader() + " vs " + expectedClassLoader);
+    } else {
+      System.out.println(className + " was loaded with " + (boot ? "boot" : "system") +
+          " classloader");
+    }
+  }
+}
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index f8daba6..801904d 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -15,6 +15,7 @@
  */
 
 import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandleInfo;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.invoke.MethodType;
@@ -77,6 +78,7 @@
     testReturnValueConversions();
     testVariableArity();
     testVariableArity_MethodHandles_bind();
+    testRevealDirect();
   }
 
   public static void testfindSpecial_invokeSuperBehaviour() throws Throwable {
@@ -384,6 +386,10 @@
     public String publicMethod() {
       return "publicMethod";
     }
+
+    public String publicVarArgsMethod(String... args) {
+      return "publicVarArgsMethod";
+    }
   }
 
   public static void testUnreflects() throws Throwable {
@@ -1486,4 +1492,117 @@
       fail();
     } catch (WrongMethodTypeException e) {}
   }
+
+  public static void testRevealDirect() throws Throwable {
+    // Test with a virtual method :
+    MethodType type = MethodType.methodType(String.class);
+    MethodHandle handle = MethodHandles.lookup().findVirtual(
+        UnreflectTester.class, "publicMethod", type);
+
+    // Comparisons with an equivalent member obtained via reflection :
+    MethodHandleInfo info = MethodHandles.lookup().revealDirect(handle);
+    Method meth = UnreflectTester.class.getMethod("publicMethod");
+
+    assertEquals(MethodHandleInfo.REF_invokeVirtual, info.getReferenceKind());
+    assertEquals("publicMethod", info.getName());
+    assertTrue(UnreflectTester.class == info.getDeclaringClass());
+    assertFalse(info.isVarArgs());
+    assertEquals(meth, info.reflectAs(Method.class, MethodHandles.lookup()));
+    assertEquals(type, info.getMethodType());
+
+    // Resolution via a public lookup should fail because the method in question
+    // isn't public.
+    try {
+      info.reflectAs(Method.class, MethodHandles.publicLookup());
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+
+    // Test with a static method :
+    handle = MethodHandles.lookup().findStatic(UnreflectTester.class,
+        "publicStaticMethod",
+        MethodType.methodType(String.class));
+
+    info = MethodHandles.lookup().revealDirect(handle);
+    meth = UnreflectTester.class.getMethod("publicStaticMethod");
+    assertEquals(MethodHandleInfo.REF_invokeStatic, info.getReferenceKind());
+    assertEquals("publicStaticMethod", info.getName());
+    assertTrue(UnreflectTester.class == info.getDeclaringClass());
+    assertFalse(info.isVarArgs());
+    assertEquals(meth, info.reflectAs(Method.class, MethodHandles.lookup()));
+    assertEquals(type, info.getMethodType());
+
+    // Test with a var-args method :
+    type = MethodType.methodType(String.class, String[].class);
+    handle = MethodHandles.lookup().findVirtual(UnreflectTester.class,
+        "publicVarArgsMethod", type);
+
+    info = MethodHandles.lookup().revealDirect(handle);
+    meth = UnreflectTester.class.getMethod("publicVarArgsMethod", String[].class);
+    assertEquals(MethodHandleInfo.REF_invokeVirtual, info.getReferenceKind());
+    assertEquals("publicVarArgsMethod", info.getName());
+    assertTrue(UnreflectTester.class == info.getDeclaringClass());
+    assertTrue(info.isVarArgs());
+    assertEquals(meth, info.reflectAs(Method.class, MethodHandles.lookup()));
+    assertEquals(type, info.getMethodType());
+
+    // Test with a constructor :
+    Constructor cons = UnreflectTester.class.getConstructor(String.class, boolean.class);
+    type = MethodType.methodType(void.class, String.class, boolean.class);
+    handle = MethodHandles.lookup().findConstructor(UnreflectTester.class, type);
+
+    info = MethodHandles.lookup().revealDirect(handle);
+    assertEquals(MethodHandleInfo.REF_newInvokeSpecial, info.getReferenceKind());
+    assertEquals("<init>", info.getName());
+    assertTrue(UnreflectTester.class == info.getDeclaringClass());
+    assertFalse(info.isVarArgs());
+    assertEquals(cons, info.reflectAs(Constructor.class, MethodHandles.lookup()));
+    assertEquals(type, info.getMethodType());
+
+    // Test with a static field :
+    Field field = UnreflectTester.class.getField("publicStaticField");
+
+    handle = MethodHandles.lookup().findStaticSetter(
+        UnreflectTester.class, "publicStaticField", String.class);
+
+    info = MethodHandles.lookup().revealDirect(handle);
+    assertEquals(MethodHandleInfo.REF_putStatic, info.getReferenceKind());
+    assertEquals("publicStaticField", info.getName());
+    assertTrue(UnreflectTester.class == info.getDeclaringClass());
+    assertFalse(info.isVarArgs());
+    assertEquals(field, info.reflectAs(Field.class, MethodHandles.lookup()));
+    assertEquals(MethodType.methodType(void.class, String.class), info.getMethodType());
+
+    // Test with a setter on the same field, the type of the handle should change
+    // but everything else must remain the same.
+    handle = MethodHandles.lookup().findStaticGetter(
+        UnreflectTester.class, "publicStaticField", String.class);
+    info = MethodHandles.lookup().revealDirect(handle);
+    assertEquals(MethodHandleInfo.REF_getStatic, info.getReferenceKind());
+    assertEquals(field, info.reflectAs(Field.class, MethodHandles.lookup()));
+    assertEquals(MethodType.methodType(String.class), info.getMethodType());
+
+    // Test with an instance field :
+    field = UnreflectTester.class.getField("publicField");
+
+    handle = MethodHandles.lookup().findSetter(
+        UnreflectTester.class, "publicField", String.class);
+
+    info = MethodHandles.lookup().revealDirect(handle);
+    assertEquals(MethodHandleInfo.REF_putField, info.getReferenceKind());
+    assertEquals("publicField", info.getName());
+    assertTrue(UnreflectTester.class == info.getDeclaringClass());
+    assertFalse(info.isVarArgs());
+    assertEquals(field, info.reflectAs(Field.class, MethodHandles.lookup()));
+    assertEquals(MethodType.methodType(void.class, String.class), info.getMethodType());
+
+    // Test with a setter on the same field, the type of the handle should change
+    // but everything else must remain the same.
+    handle = MethodHandles.lookup().findGetter(
+        UnreflectTester.class, "publicField", String.class);
+    info = MethodHandles.lookup().revealDirect(handle);
+    assertEquals(MethodHandleInfo.REF_getField, info.getReferenceKind());
+    assertEquals(field, info.reflectAs(Field.class, MethodHandles.lookup()));
+    assertEquals(MethodType.methodType(String.class), info.getMethodType());
+  }
 }
diff --git a/test/Android.bp b/test/Android.bp
index 287df13..1070645 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -272,6 +272,7 @@
         "929-search/search.cc",
         "931-agent-thread/agent_thread.cc",
         "933-misc-events/misc_events.cc",
+        "936-search-onload/search_onload.cc",
     ],
     shared_libs: [
         "libbase",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 4c526a2..74b06b1 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -136,9 +136,9 @@
   COMPILER_TYPES += regalloc_gc
   OPTIMIZING_COMPILER_TYPES += regalloc_gc
 endif
-RELOCATE_TYPES := relocate
-ifeq ($(ART_TEST_RUN_TEST_NO_RELOCATE),true)
-  RELOCATE_TYPES += no-relocate
+RELOCATE_TYPES := no-relocate
+ifeq ($(ART_TEST_RUN_TEST_RELOCATE),true)
+  RELOCATE_TYPES += relocate
 endif
 ifeq ($(ART_TEST_RUN_TEST_RELOCATE_NO_PATCHOAT),true)
   RELOCATE_TYPES += relocate-npatchoat
@@ -161,7 +161,9 @@
 ifeq ($(ART_TEST_JNI_FORCECOPY),true)
   JNI_TYPES += forcecopy
 endif
+ifeq ($(ART_TEST_RUN_TEST_IMAGE),true)
 IMAGE_TYPES := picimage
+endif
 ifeq ($(ART_TEST_RUN_TEST_NO_IMAGE),true)
   IMAGE_TYPES += no-image
 endif
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 4214053..751aa95 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -39,7 +39,7 @@
 PATCHOAT=""
 PREBUILD="y"
 QUIET="n"
-RELOCATE="y"
+RELOCATE="n"
 STRIP_DEX="n"
 SECONDARY_DEX=""
 TIME_OUT="gdb"  # "n" (disabled), "timeout" (use timeout), "gdb" (use gdb)
diff --git a/test/run-test b/test/run-test
index a228789..27c700e 100755
--- a/test/run-test
+++ b/test/run-test
@@ -111,7 +111,7 @@
 dev_mode="no"
 update_mode="no"
 debug_mode="no"
-relocate="yes"
+relocate="no"
 runtime="art"
 usage="no"
 build_only="no"
@@ -156,6 +156,7 @@
         shift
     elif [ "x$1" = "x--jvm" ]; then
         target_mode="no"
+        DEX_LOCATION="$tmp_dir"
         runtime="jvm"
         image_args=""
         prebuild_mode="no"
@@ -626,8 +627,8 @@
         echo "    --strip-dex           Strip the dex files before starting test."
         echo "    --relocate            Force the use of relocating in the test, making"
         echo "                          the image and oat files be relocated to a random"
-        echo "                          address before running. (default)"
-        echo "    --no-relocate         Force the use of no relocating in the test"
+        echo "                          address before running."
+        echo "    --no-relocate         Force the use of no relocating in the test. (default)"
         echo "    --image               Run the test using a precompiled boot image. (default)"
         echo "    --no-image            Run the test without a precompiled boot image."
         echo "    --host                Use the host-mode virtual machine."
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index c30c2b1..621d45a 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -28,6 +28,7 @@
 
 #include "901-hello-ti-agent/basics.h"
 #include "909-attach-agent/attach.h"
+#include "936-search-onload/search_onload.h"
 
 namespace art {
 
@@ -112,6 +113,7 @@
   { "932-transform-saves", common_retransform::OnLoad, nullptr },
   { "934-load-transform", common_retransform::OnLoad, nullptr },
   { "935-non-retransformable", common_transform::OnLoad, nullptr },
+  { "936-search-onload", Test936SearchOnload::OnLoad, nullptr },
   { "937-hello-retransform-package", common_retransform::OnLoad, nullptr },
 };