Merge "ART: Add GetClassLoaderClasses"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 5bdfbc7..392f044 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -120,14 +120,14 @@
 ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \
   $(HOST_CORE_IMAGE_optimizing_pic_64) \
   $(HOST_CORE_IMAGE_optimizing_pic_32) \
-  $(HOST_CORE_IMAGE_optimizing_no-pic_64) \
-  $(HOST_CORE_IMAGE_optimizing_no-pic_32) \
+  $(HOST_CORE_IMAGE_optimizing_pic_multi_64) \
+  $(HOST_CORE_IMAGE_optimizing_pic_multi_32) \
   $(HOST_OUT_EXECUTABLES)/patchoatd
 ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \
   $(TARGET_CORE_IMAGE_optimizing_pic_64) \
   $(TARGET_CORE_IMAGE_optimizing_pic_32) \
-  $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \
-  $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \
+  $(TARGET_CORE_IMAGE_optimizing_pic_multi_64) \
+  $(TARGET_CORE_IMAGE_optimizing_pic_multi_32) \
   $(TARGET_OUT_EXECUTABLES)/patchoatd
 
 ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index e41d9bd..28c009e 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -770,8 +770,6 @@
       existing = existing | ExperimentalFlags::kAgents;
     } else if (option == "runtime-plugins") {
       existing = existing | ExperimentalFlags::kRuntimePlugins;
-    } else if (option == "method-handles") {
-      existing = existing | ExperimentalFlags::kMethodHandles;
     } else {
       return Result::Failure(std::string("Unknown option '") + option + "'");
     }
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 2950266..5da59f3 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1950,66 +1950,82 @@
   DCHECK(!it.HasNext());
 }
 
-void CompilerDriver::Verify(jobject jclass_loader,
-                            const std::vector<const DexFile*>& dex_files,
-                            TimingLogger* timings) {
+bool CompilerDriver::FastVerify(jobject jclass_loader,
+                                const std::vector<const DexFile*>& dex_files,
+                                TimingLogger* timings) {
   verifier::VerifierDeps* verifier_deps =
       Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
   // If there is an existing `VerifierDeps`, try to use it for fast verification.
-  if (verifier_deps != nullptr) {
-    TimingLogger::ScopedTiming t("Fast Verify", timings);
-    ScopedObjectAccess soa(Thread::Current());
-    StackHandleScope<2> hs(soa.Self());
-    Handle<mirror::ClassLoader> class_loader(
-        hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
-    MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr));
-    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    if (verifier_deps->ValidateDependencies(class_loader, soa.Self())) {
-      // We successfully validated the dependencies, now update class status
-      // of verified classes. Note that the dependencies also record which classes
-      // could not be fully verified; we could try again, but that would hurt verification
-      // time. So instead we assume these classes still need to be verified at
-      // runtime.
-      for (const DexFile* dex_file : dex_files) {
-        // Fetch the list of unverified classes and turn it into a set for faster
-        // lookups.
-        const std::vector<dex::TypeIndex>& unverified_classes =
-            verifier_deps->GetUnverifiedClasses(*dex_file);
-        std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end());
-        for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
-          const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
-          if (set.find(class_def.class_idx_) == set.end()) {
-            if (!GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
-              // Just update the compiled_classes_ map. The compiler doesn't need to resolve
-              // the type.
-              compiled_classes_.Overwrite(
-                  ClassReference(dex_file, i), new CompiledClass(mirror::Class::kStatusVerified));
-            } else {
-              // Resolve the type, so later compilation stages know they don't need to verify
-              // the class.
-              const char* descriptor = dex_file->GetClassDescriptor(class_def);
-              cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader));
-              if (cls.Get() != nullptr) {
-                ObjectLock<mirror::Class> lock(soa.Self(), cls);
-                mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self());
-              } else {
-                DCHECK(soa.Self()->IsExceptionPending());
-                soa.Self()->ClearException();
-              }
-              // Create `VerifiedMethod`s for each methods, the compiler expects one for
-              // quickening or compiling.
-              // Note that this means:
-              // - We're only going to compile methods that did verify.
-              // - Quickening will not do checkcast ellision.
-              // TODO(ngeoffray): Reconsider this once we refactor compiler filters.
-              PopulateVerifiedMethods(*dex_file, i, verification_results_);
+  if (verifier_deps == nullptr) {
+    return false;
+  }
+  TimingLogger::ScopedTiming t("Fast Verify", timings);
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
+  MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr));
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  if (!verifier_deps->ValidateDependencies(class_loader, soa.Self())) {
+    return false;
+  }
+
+  // We successfully validated the dependencies, now update class status
+  // of verified classes. Note that the dependencies also record which classes
+  // could not be fully verified; we could try again, but that would hurt verification
+  // time. So instead we assume these classes still need to be verified at
+  // runtime.
+  for (const DexFile* dex_file : dex_files) {
+    // Fetch the list of unverified classes and turn it into a set for faster
+    // lookups.
+    const std::vector<dex::TypeIndex>& unverified_classes =
+        verifier_deps->GetUnverifiedClasses(*dex_file);
+    std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end());
+    for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
+      const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
+      if (set.find(class_def.class_idx_) == set.end()) {
+        if (!GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
+          // Just update the compiled_classes_ map. The compiler doesn't need to resolve
+          // the type.
+          compiled_classes_.Overwrite(
+              ClassReference(dex_file, i), new CompiledClass(mirror::Class::kStatusVerified));
+        } else {
+          // Resolve the type, so later compilation stages know they don't need to verify
+          // the class.
+          const char* descriptor = dex_file->GetClassDescriptor(class_def);
+          cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader));
+          if (cls.Get() != nullptr) {
+            // Check that the class is resolved with the current dex file. We might get
+            // a boot image class, or a class in a different dex file for multidex, and
+            // we should not update the status in that case.
+            if (&cls->GetDexFile() == dex_file) {
+              ObjectLock<mirror::Class> lock(soa.Self(), cls);
+              mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self());
             }
+          } else {
+            DCHECK(soa.Self()->IsExceptionPending());
+            soa.Self()->ClearException();
           }
+          // Create `VerifiedMethod`s for each methods, the compiler expects one for
+          // quickening or compiling.
+          // Note that this means:
+          // - We're only going to compile methods that did verify.
+          // - Quickening will not do checkcast ellision.
+          // TODO(ngeoffray): Reconsider this once we refactor compiler filters.
+          PopulateVerifiedMethods(*dex_file, i, verification_results_);
         }
       }
-      return;
     }
   }
+  return true;
+}
+
+void CompilerDriver::Verify(jobject jclass_loader,
+                            const std::vector<const DexFile*>& dex_files,
+                            TimingLogger* timings) {
+  if (FastVerify(jclass_loader, dex_files, timings)) {
+    return;
+  }
 
   // If there is no existing `verifier_deps` (because of non-existing vdex), or
   // the existing `verifier_deps` is not valid anymore, create a new one for
@@ -2017,7 +2033,7 @@
   // Then dex2oat can update the vdex file with these new dependencies.
   if (!GetCompilerOptions().IsBootImage()) {
     // Create the main VerifierDeps, and set it to this thread.
-    verifier_deps = new verifier::VerifierDeps(dex_files);
+    verifier::VerifierDeps* verifier_deps = new verifier::VerifierDeps(dex_files);
     Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(verifier_deps);
     Thread::Current()->SetVerifierDeps(verifier_deps);
     // Create per-thread VerifierDeps to avoid contention on the main one.
@@ -2026,6 +2042,7 @@
       worker->GetThread()->SetVerifierDeps(new verifier::VerifierDeps(dex_files));
     }
   }
+
   // Note: verification should not be pulling in classes anymore when compiling the boot image,
   //       as all should have been resolved before. As such, doing this in parallel should still
   //       be deterministic.
@@ -2041,6 +2058,7 @@
 
   if (!GetCompilerOptions().IsBootImage()) {
     // Merge all VerifierDeps into the main one.
+    verifier::VerifierDeps* verifier_deps = Thread::Current()->GetVerifierDeps();
     for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
       verifier::VerifierDeps* thread_deps = worker->GetThread()->GetVerifierDeps();
       worker->GetThread()->SetVerifierDeps(nullptr);
@@ -2061,7 +2079,10 @@
     ScopedObjectAccess soa(Thread::Current());
     const DexFile& dex_file = *manager_->GetDexFile();
     if (!manager_->GetCompiler()->ShouldVerifyClassBasedOnProfile(dex_file, class_def_index)) {
-      // Skip verification since the class is not in the profile.
+      // Skip verification since the class is not in the profile, and let the VerifierDeps know
+      // that the class will need to be verified at runtime.
+      verifier::VerifierDeps::MaybeRecordVerificationStatus(
+          dex_file, dex::TypeIndex(class_def_index), verifier::MethodVerifier::kSoftFailure);
       return;
     }
     const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 2e3b7c8..6bfdd4d 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -433,12 +433,18 @@
                       TimingLogger* timings)
       REQUIRES(!Locks::mutator_lock_);
 
+  // Do fast verification through VerifierDeps if possible. Return whether
+  // verification was successful.
   // NO_THREAD_SAFETY_ANALYSIS as the method accesses a guarded value in a
   // single-threaded way.
+  bool FastVerify(jobject class_loader,
+                  const std::vector<const DexFile*>& dex_files,
+                  TimingLogger* timings)
+      NO_THREAD_SAFETY_ANALYSIS;
+
   void Verify(jobject class_loader,
               const std::vector<const DexFile*>& dex_files,
-              TimingLogger* timings)
-    NO_THREAD_SAFETY_ANALYSIS;
+              TimingLogger* timings);
 
   void VerifyDexFile(jobject class_loader,
                      const DexFile& dex_file,
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index f896f11..8cf4089 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -63,7 +63,8 @@
                              driver,
                              interpreter_metadata,
                              compiler_stats,
-                             dex_cache) {}
+                             dex_cache,
+                             handles) {}
 
   // Only for unit testing.
   HGraphBuilder(HGraph* graph,
@@ -90,7 +91,8 @@
                              /* compiler_driver */ nullptr,
                              /* interpreter_metadata */ nullptr,
                              /* compiler_stats */ nullptr,
-                             null_dex_cache_) {}
+                             null_dex_cache_,
+                             handles) {}
 
   GraphAnalysisResult BuildGraph();
 
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 6c680c8..70c2738 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -936,10 +936,10 @@
   if (environment->GetParent() != nullptr) {
     // We emit the parent environment first.
     EmitEnvironment(environment->GetParent(), slow_path);
-    stack_map_stream_.BeginInlineInfoEntry(environment->GetMethodIdx(),
+    stack_map_stream_.BeginInlineInfoEntry(environment->GetMethod(),
                                            environment->GetDexPc(),
-                                           environment->GetInvokeType(),
-                                           environment->Size());
+                                           environment->Size(),
+                                           &graph_->GetDexFile());
   }
 
   // Walk over the environment, and record the location of dex registers.
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index ef4bd1e..07b1746 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -5770,7 +5770,9 @@
   locations->SetOut(Location::RequiresRegister());
 }
 
-void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) {
+// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
+// move.
+void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
   if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
     codegen_->GenerateLoadClassRuntimeCall(cls);
@@ -5821,8 +5823,9 @@
     }
     case HLoadClass::LoadKind::kBootImageAddress: {
       DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
-      DCHECK_NE(cls->GetAddress(), 0u);
-      uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+      uint32_t address = dchecked_integral_cast<uint32_t>(
+          reinterpret_cast<uintptr_t>(cls->GetClass().Get()));
+      DCHECK_NE(address, 0u);
       __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
       break;
     }
@@ -5842,7 +5845,7 @@
     case HLoadClass::LoadKind::kJitTableAddress: {
       __ LoadLiteral(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
                                                                cls->GetTypeIndex(),
-                                                               cls->GetAddress()));
+                                                               cls->GetClass()));
       // /* GcRoot<mirror::Class> */ out = *out
       GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
       break;
@@ -7348,8 +7351,9 @@
 
 Literal* CodeGeneratorARM::DeduplicateJitClassLiteral(const DexFile& dex_file,
                                                       dex::TypeIndex type_index,
-                                                      uint64_t address) {
-  jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), address);
+                                                      Handle<mirror::Class> handle) {
+  jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index),
+                             reinterpret_cast64<uint64_t>(handle.GetReference()));
   return jit_class_patches_.GetOrCreate(
       TypeReference(&dex_file, type_index),
       [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index bd237e9..52d1857 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -496,7 +496,7 @@
                                        Handle<mirror::String> handle);
   Literal* DeduplicateJitClassLiteral(const DexFile& dex_file,
                                       dex::TypeIndex type_index,
-                                      uint64_t address);
+                                      Handle<mirror::Class> handle);
 
   void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
 
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index a9617e1..b094e54 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -4013,7 +4013,7 @@
       break;
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
       // Add ADRP with its PC-relative DexCache access patch.
-      const DexFile& dex_file = invoke->GetDexFile();
+      const DexFile& dex_file = invoke->GetDexFileForPcRelativeDexCache();
       uint32_t element_offset = invoke->GetDexCacheArrayOffset();
       vixl::aarch64::Label* adrp_label = NewPcRelativeDexCacheArrayPatch(dex_file, element_offset);
       EmitAdrpPlaceholder(adrp_label, XRegisterFrom(temp));
@@ -4181,8 +4181,9 @@
 }
 
 vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateJitClassLiteral(
-    const DexFile& dex_file, dex::TypeIndex type_index, uint64_t address) {
-  jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), address);
+    const DexFile& dex_file, dex::TypeIndex type_index, Handle<mirror::Class> handle) {
+  jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index),
+                             reinterpret_cast64<uint64_t>(handle.GetReference()));
   return jit_class_patches_.GetOrCreate(
       TypeReference(&dex_file, type_index),
       [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); });
@@ -4377,7 +4378,9 @@
   locations->SetOut(Location::RequiresRegister());
 }
 
-void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) {
+// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
+// move.
+void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
   if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
     codegen_->GenerateLoadClassRuntimeCall(cls);
@@ -4426,8 +4429,10 @@
     }
     case HLoadClass::LoadKind::kBootImageAddress: {
       DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
-      DCHECK(cls->GetAddress() != 0u && IsUint<32>(cls->GetAddress()));
-      __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(cls->GetAddress()));
+      uint32_t address = dchecked_integral_cast<uint32_t>(
+          reinterpret_cast<uintptr_t>(cls->GetClass().Get()));
+      DCHECK_NE(address, 0u);
+      __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
       break;
     }
     case HLoadClass::LoadKind::kBssEntry: {
@@ -4452,7 +4457,7 @@
     case HLoadClass::LoadKind::kJitTableAddress: {
       __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
                                                        cls->GetTypeIndex(),
-                                                       cls->GetAddress()));
+                                                       cls->GetClass()));
       GenerateGcRootFieldLoad(cls,
                               out_loc,
                               out.X(),
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index c7a0614..a9dca92 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -579,7 +579,7 @@
                                                                 Handle<mirror::String> handle);
   vixl::aarch64::Literal<uint32_t>* DeduplicateJitClassLiteral(const DexFile& dex_file,
                                                                dex::TypeIndex string_index,
-                                                               uint64_t address);
+                                                               Handle<mirror::Class> handle);
 
   void EmitAdrpPlaceholder(vixl::aarch64::Label* fixup_label, vixl::aarch64::Register reg);
   void EmitAddPlaceholder(vixl::aarch64::Label* fixup_label,
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 83c289e..05a76e1 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -5846,7 +5846,9 @@
   locations->SetOut(Location::RequiresRegister());
 }
 
-void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) {
+// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
+// move.
+void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
   if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
     codegen_->GenerateLoadClassRuntimeCall(cls);
@@ -5892,8 +5894,9 @@
     }
     case HLoadClass::LoadKind::kBootImageAddress: {
       DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
-      DCHECK_NE(cls->GetAddress(), 0u);
-      uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+      uint32_t address = dchecked_integral_cast<uint32_t>(
+          reinterpret_cast<uintptr_t>(cls->GetClass().Get()));
+      DCHECK_NE(address, 0u);
       __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
       break;
     }
@@ -5908,7 +5911,7 @@
     case HLoadClass::LoadKind::kJitTableAddress: {
       __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
                                                        cls->GetTypeIndex(),
-                                                       cls->GetAddress()));
+                                                       cls->GetClass()));
       // /* GcRoot<mirror::Class> */ out = *out
       GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
       break;
@@ -7466,8 +7469,9 @@
 
 VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFile& dex_file,
                                                       dex::TypeIndex type_index,
-                                                      uint64_t address) {
-  jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), address);
+                                                      Handle<mirror::Class> handle) {
+  jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index),
+                             reinterpret_cast64<uint64_t>(handle.GetReference()));
   return jit_class_patches_.GetOrCreate(
       TypeReference(&dex_file, type_index),
       [this]() {
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 0f0a954..be65353 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -579,7 +579,7 @@
                                                  Handle<mirror::String> handle);
   VIXLUInt32Literal* DeduplicateJitClassLiteral(const DexFile& dex_file,
                                                 dex::TypeIndex type_index,
-                                                uint64_t address);
+                                                Handle<mirror::Class> handle);
 
   void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
 
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 1f4ff27..24234e1 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -5490,7 +5490,9 @@
   locations->SetOut(Location::RequiresRegister());
 }
 
-void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) {
+// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
+// move.
+void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
   if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
     codegen_->GenerateLoadClassRuntimeCall(cls);
@@ -5548,8 +5550,9 @@
     }
     case HLoadClass::LoadKind::kBootImageAddress: {
       DCHECK(!kEmitCompilerReadBarrier);
-      DCHECK_NE(cls->GetAddress(), 0u);
-      uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+      uint32_t address = dchecked_integral_cast<uint32_t>(
+          reinterpret_cast<uintptr_t>(cls->GetClass().Get()));
+      DCHECK_NE(address, 0u);
       __ LoadLiteral(out,
                      base_or_current_method_reg,
                      codegen_->DeduplicateBootImageAddressLiteral(address));
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index a350de7..fc8fb7a 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -3395,7 +3395,7 @@
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
       uint32_t offset = invoke->GetDexCacheArrayOffset();
       CodeGeneratorMIPS64::PcRelativePatchInfo* info =
-          NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset);
+          NewPcRelativeDexCacheArrayPatch(invoke->GetDexFileForPcRelativeDexCache(), offset);
       EmitPcRelativeAddressPlaceholderHigh(info, AT);
       __ Ld(temp.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678);
       break;
@@ -3524,7 +3524,9 @@
   locations->SetOut(Location::RequiresRegister());
 }
 
-void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) {
+// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
+// move.
+void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
   if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
     codegen_->GenerateLoadClassRuntimeCall(cls);
@@ -3569,8 +3571,9 @@
     }
     case HLoadClass::LoadKind::kBootImageAddress: {
       DCHECK(!kEmitCompilerReadBarrier);
-      DCHECK_NE(cls->GetAddress(), 0u);
-      uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+      uint32_t address = dchecked_integral_cast<uint32_t>(
+          reinterpret_cast<uintptr_t>(cls->GetClass().Get()));
+      DCHECK_NE(address, 0u);
       __ LoadLiteral(out,
                      kLoadUnsignedWord,
                      codegen_->DeduplicateBootImageAddressLiteral(address));
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index a850d38..cc727d2 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -4521,7 +4521,7 @@
       __ movl(temp.AsRegister<Register>(), Address(base_reg, kDummy32BitOffset));
       // Bind a new fixup label at the end of the "movl" insn.
       uint32_t offset = invoke->GetDexCacheArrayOffset();
-      __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset));
+      __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFileForPcRelativeDexCache(), offset));
       break;
     }
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
@@ -6055,15 +6055,18 @@
 
 Label* CodeGeneratorX86::NewJitRootClassPatch(const DexFile& dex_file,
                                               dex::TypeIndex dex_index,
-                                              uint64_t address) {
-  jit_class_roots_.Overwrite(TypeReference(&dex_file, dex_index), address);
+                                              Handle<mirror::Class> handle) {
+  jit_class_roots_.Overwrite(TypeReference(&dex_file, dex_index),
+                             reinterpret_cast64<uint64_t>(handle.GetReference()));
   // Add a patch entry and return the label.
   jit_class_patches_.emplace_back(dex_file, dex_index.index_);
   PatchInfo<Label>* info = &jit_class_patches_.back();
   return &info->label;
 }
 
-void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) {
+// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
+// move.
+void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
   if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
     codegen_->GenerateLoadClassRuntimeCall(cls);
@@ -6110,8 +6113,9 @@
     }
     case HLoadClass::LoadKind::kBootImageAddress: {
       DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
-      DCHECK_NE(cls->GetAddress(), 0u);
-      uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+      uint32_t address = dchecked_integral_cast<uint32_t>(
+          reinterpret_cast<uintptr_t>(cls->GetClass().Get()));
+      DCHECK_NE(address, 0u);
       __ movl(out, Immediate(address));
       codegen_->RecordSimplePatch();
       break;
@@ -6127,7 +6131,7 @@
     case HLoadClass::LoadKind::kJitTableAddress: {
       Address address = Address::Absolute(CodeGeneratorX86::kDummy32BitOffset);
       Label* fixup_label = codegen_->NewJitRootClassPatch(
-          cls->GetDexFile(), cls->GetTypeIndex(), cls->GetAddress());
+          cls->GetDexFile(), cls->GetTypeIndex(), cls->GetClass());
       // /* GcRoot<mirror::Class> */ out = *address
       GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, kCompilerReadBarrierOption);
       break;
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index b86d080..9eb9765 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -419,7 +419,9 @@
   Label* NewJitRootStringPatch(const DexFile& dex_file,
                                dex::StringIndex dex_index,
                                Handle<mirror::String> handle);
-  Label* NewJitRootClassPatch(const DexFile& dex_file, dex::TypeIndex dex_index, uint64_t address);
+  Label* NewJitRootClassPatch(const DexFile& dex_file,
+                              dex::TypeIndex dex_index,
+                              Handle<mirror::Class> handle);
 
   void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
 
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 2691af8..9adedab 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -991,7 +991,7 @@
               Address::Absolute(kDummy32BitOffset, /* no_rip */ false));
       // Bind a new fixup label at the end of the "movl" insn.
       uint32_t offset = invoke->GetDexCacheArrayOffset();
-      __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset));
+      __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFileForPcRelativeDexCache(), offset));
       break;
     }
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
@@ -5482,15 +5482,18 @@
 
 Label* CodeGeneratorX86_64::NewJitRootClassPatch(const DexFile& dex_file,
                                                  dex::TypeIndex dex_index,
-                                                 uint64_t address) {
-  jit_class_roots_.Overwrite(TypeReference(&dex_file, dex_index), address);
+                                                 Handle<mirror::Class> handle) {
+  jit_class_roots_.Overwrite(
+      TypeReference(&dex_file, dex_index), reinterpret_cast64<uint64_t>(handle.GetReference()));
   // Add a patch entry and return the label.
   jit_class_patches_.emplace_back(dex_file, dex_index.index_);
   PatchInfo<Label>* info = &jit_class_patches_.back();
   return &info->label;
 }
 
-void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) {
+// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
+// move.
+void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
   if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
     codegen_->GenerateLoadClassRuntimeCall(cls);
@@ -5528,8 +5531,9 @@
       break;
     case HLoadClass::LoadKind::kBootImageAddress: {
       DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
-      DCHECK_NE(cls->GetAddress(), 0u);
-      uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+      uint32_t address = dchecked_integral_cast<uint32_t>(
+          reinterpret_cast<uintptr_t>(cls->GetClass().Get()));
+      DCHECK_NE(address, 0u);
       __ movl(out, Immediate(address));  // Zero-extended.
       codegen_->RecordSimplePatch();
       break;
@@ -5547,7 +5551,7 @@
       Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset,
                                           /* no_rip */ true);
       Label* fixup_label =
-          codegen_->NewJitRootClassPatch(cls->GetDexFile(), cls->GetTypeIndex(), cls->GetAddress());
+          codegen_->NewJitRootClassPatch(cls->GetDexFile(), cls->GetTypeIndex(), cls->GetClass());
       // /* GcRoot<mirror::Class> */ out = *address
       GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, kCompilerReadBarrierOption);
       break;
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 8b3ab4c..3438b81 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -416,7 +416,9 @@
   Label* NewJitRootStringPatch(const DexFile& dex_file,
                                dex::StringIndex dex_index,
                                Handle<mirror::String> handle);
-  Label* NewJitRootClassPatch(const DexFile& dex_file, dex::TypeIndex dex_index, uint64_t address);
+  Label* NewJitRootClassPatch(const DexFile& dex_file,
+                              dex::TypeIndex dex_index,
+                              Handle<mirror::Class> handle);
 
   void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
 
diff --git a/compiler/optimizing/dex_cache_array_fixups_arm.cc b/compiler/optimizing/dex_cache_array_fixups_arm.cc
index a3140d0..9ddcd56 100644
--- a/compiler/optimizing/dex_cache_array_fixups_arm.cc
+++ b/compiler/optimizing/dex_cache_array_fixups_arm.cc
@@ -64,9 +64,10 @@
     // we need to add the dex cache arrays base as the special input.
     if (invoke->HasPcRelativeDexCache() &&
         !IsCallFreeIntrinsic<IntrinsicLocationsBuilderARMType>(invoke, codegen_)) {
-      HArmDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(invoke->GetDexFile());
+      HArmDexCacheArraysBase* base =
+          GetOrCreateDexCacheArrayBase(invoke->GetDexFileForPcRelativeDexCache());
       // Update the element offset in base.
-      DexCacheArraysLayout layout(kArmPointerSize, &invoke->GetDexFile());
+      DexCacheArraysLayout layout(kArmPointerSize, &invoke->GetDexFileForPcRelativeDexCache());
       base->UpdateElementOffset(layout.MethodOffset(invoke->GetDexMethodIndex()));
       // Add the special argument base to the method.
       DCHECK(!invoke->HasCurrentMethodInput());
diff --git a/compiler/optimizing/dex_cache_array_fixups_mips.cc b/compiler/optimizing/dex_cache_array_fixups_mips.cc
index 9a34f54..04a4294 100644
--- a/compiler/optimizing/dex_cache_array_fixups_mips.cc
+++ b/compiler/optimizing/dex_cache_array_fixups_mips.cc
@@ -59,9 +59,10 @@
     if (invoke->HasPcRelativeDexCache() &&
         !IsCallFreeIntrinsic<IntrinsicLocationsBuilderMIPS>(invoke, codegen_)) {
       // Initialize base for target method dex file if needed.
-      HMipsDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(invoke->GetDexFile());
+      HMipsDexCacheArraysBase* base =
+          GetOrCreateDexCacheArrayBase(invoke->GetDexFileForPcRelativeDexCache());
       // Update the element offset in base.
-      DexCacheArraysLayout layout(kMipsPointerSize, &invoke->GetDexFile());
+      DexCacheArraysLayout layout(kMipsPointerSize, &invoke->GetDexFileForPcRelativeDexCache());
       base->UpdateElementOffset(layout.MethodOffset(invoke->GetDexMethodIndex()));
       // Add the special argument base to the method.
       DCHECK(!invoke->HasCurrentMethodInput());
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index d7da46b..50aa442 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -474,10 +474,10 @@
   HInstruction* receiver = invoke_instruction->InputAt(0);
   HInstruction* cursor = invoke_instruction->GetPrevious();
   HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
-  Handle<mirror::Class> handle = handles_->NewHandle(GetMonomorphicType(classes));
+  Handle<mirror::Class> monomorphic_type = handles_->NewHandle(GetMonomorphicType(classes));
   if (!TryInlineAndReplace(invoke_instruction,
                            resolved_method,
-                           ReferenceTypeInfo::Create(handle, /* is_exact */ true),
+                           ReferenceTypeInfo::Create(monomorphic_type, /* is_exact */ true),
                            /* do_rtp */ false,
                            /* cha_devirtualize */ false)) {
     return false;
@@ -488,7 +488,7 @@
                cursor,
                bb_cursor,
                class_index,
-               GetMonomorphicType(classes),
+               monomorphic_type,
                invoke_instruction,
                /* with_deoptimization */ true);
 
@@ -533,11 +533,9 @@
                                      HInstruction* cursor,
                                      HBasicBlock* bb_cursor,
                                      dex::TypeIndex class_index,
-                                     mirror::Class* klass,
+                                     Handle<mirror::Class> klass,
                                      HInstruction* invoke_instruction,
                                      bool with_deoptimization) {
-  ScopedAssertNoThreadSuspension sants("Adding compiler type guard");
-
   ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
   HInstanceFieldGet* receiver_class = BuildGetReceiverClass(
       class_linker, receiver, invoke_instruction->GetDexPc());
@@ -548,19 +546,20 @@
   }
 
   const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
-  bool is_referrer = (klass == outermost_graph_->GetArtMethod()->GetDeclaringClass());
+  bool is_referrer = (klass.Get() == outermost_graph_->GetArtMethod()->GetDeclaringClass());
   // Note that we will just compare the classes, so we don't need Java semantics access checks.
   // Note that the type index and the dex file are relative to the method this type guard is
   // inlined into.
   HLoadClass* load_class = new (graph_->GetArena()) HLoadClass(graph_->GetCurrentMethod(),
                                                                class_index,
                                                                caller_dex_file,
+                                                               klass,
                                                                is_referrer,
                                                                invoke_instruction->GetDexPc(),
                                                                /* needs_access_check */ false);
   bb_cursor->InsertInstructionAfter(load_class, receiver_class);
   // Sharpen after adding the instruction, as the sharpening may remove inputs.
-  HSharpening::SharpenClass(load_class, klass, handles_, codegen_, compiler_driver_);
+  HSharpening::SharpenClass(load_class, codegen_, compiler_driver_);
 
   // TODO: Extend reference type propagation to understand the guard.
   HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
@@ -637,7 +636,7 @@
                                            cursor,
                                            bb_cursor,
                                            class_index,
-                                           handle.Get(),
+                                           handle,
                                            invoke_instruction,
                                            deoptimize);
       if (deoptimize) {
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 4c0b990..11aacab 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -170,7 +170,7 @@
                              HInstruction* cursor,
                              HBasicBlock* bb_cursor,
                              dex::TypeIndex class_index,
-                             mirror::Class* klass,
+                             Handle<mirror::Class> klass,
                              HInstruction* invoke_instruction,
                              bool with_deoptimization)
     REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 3cfabdd..8ed0e7f 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -208,10 +208,8 @@
     HEnvironment* environment = new (arena_) HEnvironment(
         arena_,
         current_locals_->size(),
-        graph_->GetDexFile(),
-        graph_->GetMethodIdx(),
+        graph_->GetArtMethod(),
         instruction->GetDexPc(),
-        graph_->GetInvokeType(),
         instruction);
     environment->CopyFrom(*current_locals_);
     instruction->SetRawEnvironment(environment);
@@ -936,48 +934,40 @@
 
 bool HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t dex_pc) {
   ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<1> hs(soa.Self());
   Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
-  Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
-  const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
   Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache();
 
-  bool finalizable;
-  bool needs_access_check = NeedsAccessCheck(type_index, dex_cache, &finalizable);
-
-  // Only the access check entrypoint handles the finalizable class case. If we
-  // need access checks, then we haven't resolved the method and the class may
-  // again be finalizable.
-  QuickEntrypointEnum entrypoint = (finalizable || needs_access_check)
-      ? kQuickAllocObjectWithChecks
-      : kQuickAllocObjectInitialized;
-
   if (outer_dex_cache.Get() != dex_cache.Get()) {
     // We currently do not support inlining allocations across dex files.
     return false;
   }
 
-  HLoadClass* load_class = new (arena_) HLoadClass(
-      graph_->GetCurrentMethod(),
-      type_index,
-      outer_dex_file,
-      IsOutermostCompilingClass(type_index),
-      dex_pc,
-      needs_access_check);
+  HLoadClass* load_class = BuildLoadClass(type_index, dex_pc, /* check_access */ true);
 
-  AppendInstruction(load_class);
   HInstruction* cls = load_class;
-  if (!IsInitialized(resolved_class)) {
+  Handle<mirror::Class> klass = load_class->GetClass();
+
+  if (!IsInitialized(klass)) {
     cls = new (arena_) HClinitCheck(load_class, dex_pc);
     AppendInstruction(cls);
   }
 
+  // Only the access check entrypoint handles the finalizable class case. If we
+  // need access checks, then we haven't resolved the method and the class may
+  // again be finalizable.
+  QuickEntrypointEnum entrypoint = kQuickAllocObjectInitialized;
+  if (load_class->NeedsAccessCheck() || klass->IsFinalizable() || !klass->IsInstantiable()) {
+    entrypoint = kQuickAllocObjectWithChecks;
+  }
+
+  // Consider classes we haven't resolved as potentially finalizable.
+  bool finalizable = (klass.Get() == nullptr) || klass->IsFinalizable();
+
   AppendInstruction(new (arena_) HNewInstance(
       cls,
       dex_pc,
       type_index,
       *dex_compilation_unit_->GetDexFile(),
-      needs_access_check,
       finalizable,
       entrypoint));
   return true;
@@ -1018,7 +1008,6 @@
       ArtMethod* resolved_method,
       uint32_t method_idx,
       HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement) {
-  const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
   Thread* self = Thread::Current();
   StackHandleScope<2> hs(self);
   Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
@@ -1046,15 +1035,9 @@
     *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone;
   } else if (storage_index.IsValid()) {
     *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit;
-    HLoadClass* load_class = new (arena_) HLoadClass(
-        graph_->GetCurrentMethod(),
-        storage_index,
-        outer_dex_file,
-        is_outer_class,
-        dex_pc,
-        /*needs_access_check*/ false);
-    AppendInstruction(load_class);
-    clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
+    HLoadClass* cls = BuildLoadClass(
+        storage_index, dex_pc, /* check_access */ false, /* outer */ true);
+    clinit_check = new (arena_) HClinitCheck(cls, dex_pc);
     AppendInstruction(clinit_check);
   }
   return clinit_check;
@@ -1376,7 +1359,6 @@
   }
 
   Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
-  const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
   Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache();
   Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
 
@@ -1404,16 +1386,10 @@
     }
   }
 
-  HLoadClass* constant = new (arena_) HLoadClass(graph_->GetCurrentMethod(),
-                                                 storage_index,
-                                                 outer_dex_file,
-                                                 is_outer_class,
-                                                 dex_pc,
-                                                 /*needs_access_check*/ false);
-  AppendInstruction(constant);
+  HLoadClass* constant = BuildLoadClass(
+      storage_index, dex_pc, /* check_access */ false, /* outer */ true);
 
   HInstruction* cls = constant;
-
   Handle<mirror::Class> klass(hs.NewHandle(resolved_field->GetDeclaringClass()));
   if (!IsInitialized(klass)) {
     cls = new (arena_) HClinitCheck(constant, dex_pc);
@@ -1660,33 +1636,53 @@
   }
 }
 
+HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index,
+                                                uint32_t dex_pc,
+                                                bool check_access,
+                                                bool outer) {
+  ScopedObjectAccess soa(Thread::Current());
+  const DexCompilationUnit* compilation_unit =
+      outer ? outer_compilation_unit_ : dex_compilation_unit_;
+  const DexFile& dex_file = *compilation_unit->GetDexFile();
+  Handle<mirror::DexCache> dex_cache = compilation_unit->GetDexCache();
+  bool is_accessible = false;
+  Handle<mirror::Class> klass = handles_->NewHandle(dex_cache->GetResolvedType(type_index));
+  if (!check_access) {
+    is_accessible = true;
+  } else if (klass.Get() != nullptr) {
+    if (klass->IsPublic()) {
+      is_accessible = true;
+    } else {
+      mirror::Class* compiling_class = GetCompilingClass();
+      if (compiling_class != nullptr && compiling_class->CanAccess(klass.Get())) {
+        is_accessible = true;
+      }
+    }
+  }
+
+  HLoadClass* load_class = new (arena_) HLoadClass(
+      graph_->GetCurrentMethod(),
+      type_index,
+      dex_file,
+      klass,
+      klass.Get() != nullptr && (klass.Get() == GetOutermostCompilingClass()),
+      dex_pc,
+      !is_accessible);
+
+  AppendInstruction(load_class);
+  return load_class;
+}
+
 void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction,
                                          uint8_t destination,
                                          uint8_t reference,
                                          dex::TypeIndex type_index,
                                          uint32_t dex_pc) {
-  ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<1> hs(soa.Self());
-  const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
-  Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
-  Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
-
-  bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
-      dex_compilation_unit_->GetDexMethodIndex(),
-      dex_cache,
-      type_index);
-
   HInstruction* object = LoadLocal(reference, Primitive::kPrimNot);
-  HLoadClass* cls = new (arena_) HLoadClass(
-      graph_->GetCurrentMethod(),
-      type_index,
-      dex_file,
-      IsOutermostCompilingClass(type_index),
-      dex_pc,
-      !can_access);
-  AppendInstruction(cls);
+  HLoadClass* cls = BuildLoadClass(type_index, dex_pc, /* check_access */ true);
 
-  TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class);
+  ScopedObjectAccess soa(Thread::Current());
+  TypeCheckKind check_kind = ComputeTypeCheckKind(cls->GetClass());
   if (instruction.Opcode() == Instruction::INSTANCE_OF) {
     AppendInstruction(new (arena_) HInstanceOf(object, cls, check_kind, dex_pc));
     UpdateLocal(destination, current_block_->GetLastInstruction());
@@ -2690,21 +2686,7 @@
 
     case Instruction::CONST_CLASS: {
       dex::TypeIndex type_index(instruction.VRegB_21c());
-      // `CanAccessTypeWithoutChecks` will tell whether the method being
-      // built is trying to access its own class, so that the generated
-      // code can optimize for this case. However, the optimization does not
-      // work for inlining, so we use `IsOutermostCompilingClass` instead.
-      ScopedObjectAccess soa(Thread::Current());
-      Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
-      bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
-          dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index);
-      AppendInstruction(new (arena_) HLoadClass(
-          graph_->GetCurrentMethod(),
-          type_index,
-          *dex_file_,
-          IsOutermostCompilingClass(type_index),
-          dex_pc,
-          !can_access));
+      BuildLoadClass(type_index, dex_pc, /* check_access */ true);
       UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
       break;
     }
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index aef0b94..5efe950 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -46,9 +46,11 @@
                       CompilerDriver* driver,
                       const uint8_t* interpreter_metadata,
                       OptimizingCompilerStats* compiler_stats,
-                      Handle<mirror::DexCache> dex_cache)
+                      Handle<mirror::DexCache> dex_cache,
+                      VariableSizedHandleScope* handles)
       : arena_(graph->GetArena()),
         graph_(graph),
+        handles_(handles),
         dex_file_(dex_file),
         code_item_(code_item),
         return_type_(return_type),
@@ -223,6 +225,14 @@
   // Builds an instruction sequence for a switch statement.
   void BuildSwitch(const Instruction& instruction, uint32_t dex_pc);
 
+  // Builds a `HLoadClass` loading the given `type_index`. If `outer` is true,
+  // this method will use the outer class's dex file to lookup the type at
+  // `type_index`.
+  HLoadClass* BuildLoadClass(dex::TypeIndex type_index,
+                             uint32_t dex_pc,
+                             bool check_access,
+                             bool outer = false);
+
   // Returns the outer-most compiling method's class.
   mirror::Class* GetOutermostCompilingClass() const;
 
@@ -282,6 +292,7 @@
 
   ArenaAllocator* const arena_;
   HGraph* const graph_;
+  VariableSizedHandleScope* handles_;
 
   // The dex file where the method being compiled is, and the bytecode data.
   const DexFile* const dex_file_;
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index fc6ff7b..17d683f 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -145,7 +145,7 @@
           if (!CheckInvokeType(intrinsic, invoke)) {
             LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
                 << intrinsic << " for "
-                << invoke->GetDexFile().PrettyMethod(invoke->GetDexMethodIndex())
+                << art_method->PrettyMethod()
                 << invoke->DebugName();
           } else {
             invoke->SetIntrinsic(intrinsic,
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 4f30b11..2d3c00f 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -979,7 +979,7 @@
     }
     if (ref_info->IsSingletonAndRemovable() &&
         !new_instance->IsFinalizable() &&
-        !new_instance->NeedsAccessCheck()) {
+        !new_instance->NeedsChecks()) {
       singleton_new_instances_.push_back(new_instance);
     }
     ArenaVector<HInstruction*>& heap_values =
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 0af0d19..d15145e 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -2391,6 +2391,14 @@
   return !opt.GetDoesNotNeedEnvironment();
 }
 
+const DexFile& HInvokeStaticOrDirect::GetDexFileForPcRelativeDexCache() const {
+  ArtMethod* caller = GetEnvironment()->GetMethod();
+  ScopedObjectAccess soa(Thread::Current());
+  // `caller` is null for a top-level graph representing a method whose declaring
+  // class was not resolved.
+  return caller == nullptr ? GetBlock()->GetGraph()->GetDexFile() : *caller->GetDexFile();
+}
+
 bool HInvokeStaticOrDirect::NeedsDexCacheOfDeclaringClass() const {
   if (GetMethodLoadKind() != MethodLoadKind::kDexCacheViaMethod) {
     return false;
@@ -2434,17 +2442,6 @@
   }
 }
 
-// Helper for InstructionDataEquals to fetch the mirror Class out
-// from a kJitTableAddress LoadClass kind.
-// NO_THREAD_SAFETY_ANALYSIS because even though we're accessing
-// mirrors, they are stored in a variable size handle scope which is always
-// visited during a pause. Also, the only caller of this helper
-// only uses the mirror for pointer comparison.
-static inline mirror::Class* AsMirrorInternal(uint64_t address)
-    NO_THREAD_SAFETY_ANALYSIS {
-  return reinterpret_cast<StackReference<mirror::Class>*>(address)->AsMirrorPtr();
-}
-
 bool HLoadClass::InstructionDataEquals(const HInstruction* other) const {
   const HLoadClass* other_load_class = other->AsLoadClass();
   // TODO: To allow GVN for HLoadClass from different dex files, we should compare the type
@@ -2455,9 +2452,10 @@
   }
   switch (GetLoadKind()) {
     case LoadKind::kBootImageAddress:
-      return GetAddress() == other_load_class->GetAddress();
-    case LoadKind::kJitTableAddress:
-      return AsMirrorInternal(GetAddress()) == AsMirrorInternal(other_load_class->GetAddress());
+    case LoadKind::kJitTableAddress: {
+      ScopedObjectAccess soa(Thread::Current());
+      return GetClass().Get() == other_load_class->GetClass().Get();
+    }
     default:
       DCHECK(HasTypeReference(GetLoadKind()));
       return IsSameDexFile(GetDexFile(), other_load_class->GetDexFile());
@@ -2502,17 +2500,6 @@
   }
 }
 
-// Helper for InstructionDataEquals to fetch the mirror String out
-// from a kJitTableAddress LoadString kind.
-// NO_THREAD_SAFETY_ANALYSIS because even though we're accessing
-// mirrors, they are stored in a variable size handle scope which is always
-// visited during a pause. Also, the only caller of this helper
-// only uses the mirror for pointer comparison.
-static inline mirror::String* AsMirrorInternal(Handle<mirror::String> handle)
-    NO_THREAD_SAFETY_ANALYSIS {
-  return handle.Get();
-}
-
 bool HLoadString::InstructionDataEquals(const HInstruction* other) const {
   const HLoadString* other_load_string = other->AsLoadString();
   // TODO: To allow GVN for HLoadString from different dex files, we should compare the strings
@@ -2523,8 +2510,10 @@
   }
   switch (GetLoadKind()) {
     case LoadKind::kBootImageAddress:
-    case LoadKind::kJitTableAddress:
-      return AsMirrorInternal(GetString()) == AsMirrorInternal(other_load_string->GetString());
+    case LoadKind::kJitTableAddress: {
+      ScopedObjectAccess soa(Thread::Current());
+      return GetString().Get() == other_load_string->GetString().Get();
+    }
     default:
       return IsSameDexFile(GetDexFile(), other_load_string->GetDexFile());
   }
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 3e7914c..53b0fdd 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1724,28 +1724,22 @@
  public:
   HEnvironment(ArenaAllocator* arena,
                size_t number_of_vregs,
-               const DexFile& dex_file,
-               uint32_t method_idx,
+               ArtMethod* method,
                uint32_t dex_pc,
-               InvokeType invoke_type,
                HInstruction* holder)
      : vregs_(number_of_vregs, arena->Adapter(kArenaAllocEnvironmentVRegs)),
        locations_(number_of_vregs, arena->Adapter(kArenaAllocEnvironmentLocations)),
        parent_(nullptr),
-       dex_file_(dex_file),
-       method_idx_(method_idx),
+       method_(method),
        dex_pc_(dex_pc),
-       invoke_type_(invoke_type),
        holder_(holder) {
   }
 
   HEnvironment(ArenaAllocator* arena, const HEnvironment& to_copy, HInstruction* holder)
       : HEnvironment(arena,
                      to_copy.Size(),
-                     to_copy.GetDexFile(),
-                     to_copy.GetMethodIdx(),
+                     to_copy.GetMethod(),
                      to_copy.GetDexPc(),
-                     to_copy.GetInvokeType(),
                      holder) {}
 
   void SetAndCopyParentChain(ArenaAllocator* allocator, HEnvironment* parent) {
@@ -1794,16 +1788,8 @@
     return dex_pc_;
   }
 
-  uint32_t GetMethodIdx() const {
-    return method_idx_;
-  }
-
-  InvokeType GetInvokeType() const {
-    return invoke_type_;
-  }
-
-  const DexFile& GetDexFile() const {
-    return dex_file_;
+  ArtMethod* GetMethod() const {
+    return method_;
   }
 
   HInstruction* GetHolder() const {
@@ -1819,10 +1805,8 @@
   ArenaVector<HUserRecord<HEnvironment*>> vregs_;
   ArenaVector<Location> locations_;
   HEnvironment* parent_;
-  const DexFile& dex_file_;
-  const uint32_t method_idx_;
+  ArtMethod* method_;
   const uint32_t dex_pc_;
-  const InvokeType invoke_type_;
 
   // The instruction that holds this environment.
   HInstruction* const holder_;
@@ -3784,14 +3768,12 @@
                uint32_t dex_pc,
                dex::TypeIndex type_index,
                const DexFile& dex_file,
-               bool needs_access_check,
                bool finalizable,
                QuickEntrypointEnum entrypoint)
       : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc),
         type_index_(type_index),
         dex_file_(dex_file),
         entrypoint_(entrypoint) {
-    SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);
     SetPackedFlag<kFlagFinalizable>(finalizable);
     SetRawInputAt(0, cls);
   }
@@ -3805,8 +3787,9 @@
   // Can throw errors when out-of-memory or if it's not instantiable/accessible.
   bool CanThrow() const OVERRIDE { return true; }
 
-  // Needs to call into runtime to make sure it's instantiable/accessible.
-  bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); }
+  bool NeedsChecks() const {
+    return entrypoint_ == kQuickAllocObjectWithChecks;
+  }
 
   bool IsFinalizable() const { return GetPackedFlag<kFlagFinalizable>(); }
 
@@ -3823,8 +3806,7 @@
   DECLARE_INSTRUCTION(NewInstance);
 
  private:
-  static constexpr size_t kFlagNeedsAccessCheck = kNumberOfExpressionPackedBits;
-  static constexpr size_t kFlagFinalizable = kFlagNeedsAccessCheck + 1;
+  static constexpr size_t kFlagFinalizable = kNumberOfExpressionPackedBits;
   static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1;
   static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits,
                 "Too many packed fields.");
@@ -3870,7 +3852,6 @@
   Primitive::Type GetType() const OVERRIDE { return GetPackedField<ReturnTypeField>(); }
 
   uint32_t GetDexMethodIndex() const { return dex_method_index_; }
-  const DexFile& GetDexFile() const { return GetEnvironment()->GetDexFile(); }
 
   InvokeType GetInvokeType() const {
     return GetPackedField<InvokeTypeField>();
@@ -4190,6 +4171,8 @@
     return dispatch_info_.method_load_data;
   }
 
+  const DexFile& GetDexFileForPcRelativeDexCache() const;
+
   ClinitCheckRequirement GetClinitCheckRequirement() const {
     return GetPackedField<ClinitCheckRequirementField>();
   }
@@ -5449,10 +5432,10 @@
   HBoundsCheck(HInstruction* index,
                HInstruction* length,
                uint32_t dex_pc,
-               uint32_t string_char_at_method_index = DexFile::kDexNoIndex)
-      : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc),
-        string_char_at_method_index_(string_char_at_method_index) {
+               bool string_char_at = false)
+      : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
     DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(index->GetType()));
+    SetPackedFlag<kFlagIsStringCharAt>(string_char_at);
     SetRawInputAt(0, index);
     SetRawInputAt(1, length);
   }
@@ -5466,22 +5449,14 @@
 
   bool CanThrow() const OVERRIDE { return true; }
 
-  bool IsStringCharAt() const { return GetStringCharAtMethodIndex() != DexFile::kDexNoIndex; }
-  uint32_t GetStringCharAtMethodIndex() const { return string_char_at_method_index_; }
+  bool IsStringCharAt() const { return GetPackedFlag<kFlagIsStringCharAt>(); }
 
   HInstruction* GetIndex() const { return InputAt(0); }
 
   DECLARE_INSTRUCTION(BoundsCheck);
 
  private:
-  // We treat a String as an array, creating the HBoundsCheck from String.charAt()
-  // intrinsic in the instruction simplifier. We want to include the String.charAt()
-  // in the stack trace if we actually throw the StringIndexOutOfBoundsException,
-  // so we need to create an HEnvironment which will be translated to an InlineInfo
-  // indicating the extra stack frame. Since we add this HEnvironment quite late,
-  // in the PrepareForRegisterAllocation pass, we need to remember the method index
-  // from the invoke as we don't want to look again at the dex bytecode.
-  uint32_t string_char_at_method_index_;  // DexFile::kDexNoIndex if regular array.
+  static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits;
 
   DISALLOW_COPY_AND_ASSIGN(HBoundsCheck);
 };
@@ -5567,6 +5542,7 @@
   HLoadClass(HCurrentMethod* current_method,
              dex::TypeIndex type_index,
              const DexFile& dex_file,
+             Handle<mirror::Class> klass,
              bool is_referrers_class,
              uint32_t dex_pc,
              bool needs_access_check)
@@ -5574,7 +5550,7 @@
         special_input_(HUserRecord<HInstruction*>(current_method)),
         type_index_(type_index),
         dex_file_(dex_file),
-        address_(0u),
+        klass_(klass),
         loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) {
     // Referrers class should not need access check. We never inline unverified
     // methods so we can't possibly end up in this situation.
@@ -5587,10 +5563,7 @@
     SetPackedFlag<kFlagGenerateClInitCheck>(false);
   }
 
-  void SetLoadKindWithAddress(LoadKind load_kind, uint64_t address) {
-    DCHECK(HasAddress(load_kind));
-    DCHECK_NE(address, 0u);
-    address_ = address;
+  void SetLoadKind(LoadKind load_kind) {
     SetLoadKindInternal(load_kind);
   }
 
@@ -5657,11 +5630,6 @@
   dex::TypeIndex GetTypeIndex() const { return type_index_; }
   const DexFile& GetDexFile() const { return dex_file_; }
 
-  uint64_t GetAddress() const {
-    DCHECK(HasAddress(GetLoadKind()));
-    return address_;
-  }
-
   bool NeedsDexCacheOfDeclaringClass() const OVERRIDE {
     return GetLoadKind() == LoadKind::kDexCacheViaMethod;
   }
@@ -5691,6 +5659,10 @@
     return Primitive::kPrimNot;
   }
 
+  Handle<mirror::Class> GetClass() const {
+    return klass_;
+  }
+
   DECLARE_INSTRUCTION(LoadClass);
 
  private:
@@ -5714,11 +5686,6 @@
         load_kind == LoadKind::kDexCacheViaMethod;
   }
 
-  static bool HasAddress(LoadKind load_kind) {
-    return load_kind == LoadKind::kBootImageAddress ||
-        load_kind == LoadKind::kJitTableAddress;
-  }
-
   void SetLoadKindInternal(LoadKind load_kind);
 
   // The special input is the HCurrentMethod for kDexCacheViaMethod or kReferrersClass.
@@ -5729,7 +5696,7 @@
   const dex::TypeIndex type_index_;
   const DexFile& dex_file_;
 
-  uint64_t address_;  // Up to 64-bit, needed for kJitTableAddress on 64-bit targets.
+  Handle<mirror::Class> klass_;
 
   ReferenceTypeInfo loaded_class_rti_;
 
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
index 5d9a652..7686ba8 100644
--- a/compiler/optimizing/nodes_test.cc
+++ b/compiler/optimizing/nodes_test.cc
@@ -52,7 +52,7 @@
   exit_block->AddInstruction(new (&allocator) HExit());
 
   HEnvironment* environment = new (&allocator) HEnvironment(
-      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, null_check);
+      &allocator, 1, graph->GetArtMethod(), 0, null_check);
   null_check->SetRawEnvironment(environment);
   environment->SetRawEnvAt(0, parameter);
   parameter->AddEnvUseAt(null_check->GetEnvironment(), 0);
@@ -137,7 +137,7 @@
   ASSERT_TRUE(parameter1->GetUses().HasExactlyOneElement());
 
   HEnvironment* environment = new (&allocator) HEnvironment(
-      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, with_environment);
+      &allocator, 1, graph->GetArtMethod(), 0, with_environment);
   ArenaVector<HInstruction*> array(allocator.Adapter());
   array.push_back(parameter1);
 
@@ -148,13 +148,13 @@
   ASSERT_TRUE(parameter1->GetEnvUses().HasExactlyOneElement());
 
   HEnvironment* parent1 = new (&allocator) HEnvironment(
-      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, nullptr);
+      &allocator, 1, graph->GetArtMethod(), 0, nullptr);
   parent1->CopyFrom(array);
 
   ASSERT_EQ(parameter1->GetEnvUses().SizeSlow(), 2u);
 
   HEnvironment* parent2 = new (&allocator) HEnvironment(
-      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, nullptr);
+      &allocator, 1, graph->GetArtMethod(), 0, nullptr);
   parent2->CopyFrom(array);
   parent1->SetAndCopyParentChain(&allocator, parent2);
 
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index db7c1fb..efbaf6c 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -16,6 +16,9 @@
 
 #include "prepare_for_register_allocation.h"
 
+#include "jni_internal.h"
+#include "well_known_classes.h"
+
 namespace art {
 
 void PrepareForRegisterAllocation::Run() {
@@ -42,16 +45,12 @@
   if (check->IsStringCharAt()) {
     // Add a fake environment for String.charAt() inline info as we want
     // the exception to appear as being thrown from there.
-    const DexFile& dex_file = check->GetEnvironment()->GetDexFile();
-    DCHECK_STREQ(dex_file.PrettyMethod(check->GetStringCharAtMethodIndex()).c_str(),
-                 "char java.lang.String.charAt(int)");
+    ArtMethod* char_at_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt);
     ArenaAllocator* arena = GetGraph()->GetArena();
     HEnvironment* environment = new (arena) HEnvironment(arena,
                                                          /* number_of_vregs */ 0u,
-                                                         dex_file,
-                                                         check->GetStringCharAtMethodIndex(),
+                                                         char_at_method,
                                                          /* dex_pc */ DexFile::kDexNoIndex,
-                                                         kVirtual,
                                                          check);
     check->InsertRawEnvironment(environment);
   }
@@ -199,8 +198,7 @@
       return false;
     }
     if (user_environment->GetDexPc() != input_environment->GetDexPc() ||
-        user_environment->GetMethodIdx() != input_environment->GetMethodIdx() ||
-        !IsSameDexFile(user_environment->GetDexFile(), input_environment->GetDexFile())) {
+        user_environment->GetMethod() != input_environment->GetMethod()) {
       return false;
     }
     user_environment = user_environment->GetParent();
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index f8a4469..a4d59ab 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -295,13 +295,13 @@
   }
 
   if (check->IsIf()) {
-    HBasicBlock* trueBlock = check->IsEqual()
+    HBasicBlock* trueBlock = compare->IsEqual()
         ? check->AsIf()->IfTrueSuccessor()
         : check->AsIf()->IfFalseSuccessor();
     BoundTypeIn(receiver, trueBlock, /* start_instruction */ nullptr, class_rti);
   } else {
     DCHECK(check->IsDeoptimize());
-    if (check->IsEqual()) {
+    if (compare->IsEqual()) {
       BoundTypeIn(receiver, check->GetBlock(), check, class_rti);
     }
   }
@@ -499,18 +499,19 @@
   if (instr->IsInvokeStaticOrDirect() && instr->AsInvokeStaticOrDirect()->IsStringInit()) {
     // Calls to String.<init> are replaced with a StringFactory.
     if (kIsDebugBuild) {
-      HInvoke* invoke = instr->AsInvoke();
+      HInvokeStaticOrDirect* invoke = instr->AsInvokeStaticOrDirect();
       ClassLinker* cl = Runtime::Current()->GetClassLinker();
       Thread* self = Thread::Current();
       StackHandleScope<2> hs(self);
+      const DexFile& dex_file = *invoke->GetTargetMethod().dex_file;
       Handle<mirror::DexCache> dex_cache(
-          hs.NewHandle(FindDexCacheWithHint(self, invoke->GetDexFile(), hint_dex_cache_)));
+          hs.NewHandle(FindDexCacheWithHint(self, dex_file, hint_dex_cache_)));
       // Use a null loader. We should probably use the compiling method's class loader,
       // but then we would need to pass it to RTPVisitor just for this debug check. Since
       // the method is from the String class, the null loader is good enough.
       Handle<mirror::ClassLoader> loader;
       ArtMethod* method = cl->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
-          invoke->GetDexFile(), invoke->GetDexMethodIndex(), dex_cache, loader, nullptr, kDirect);
+          dex_file, invoke->GetDexMethodIndex(), dex_cache, loader, nullptr, kDirect);
       DCHECK(method != nullptr);
       mirror::Class* declaring_class = method->GetDeclaringClass();
       DCHECK(declaring_class != nullptr);
@@ -619,14 +620,10 @@
 
 void ReferenceTypePropagation::RTPVisitor::VisitLoadClass(HLoadClass* instr) {
   ScopedObjectAccess soa(Thread::Current());
-  // Get type from dex cache assuming it was populated by the verifier.
-  mirror::Class* resolved_class = GetClassFromDexCache(soa.Self(),
-                                                       instr->GetDexFile(),
-                                                       instr->GetTypeIndex(),
-                                                       hint_dex_cache_);
-  if (IsAdmissible(resolved_class)) {
+  Handle<mirror::Class> resolved_class = instr->GetClass();
+  if (IsAdmissible(resolved_class.Get())) {
     instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(
-        handle_cache_->NewHandle(resolved_class), /* is_exact */ true));
+        resolved_class, /* is_exact */ true));
   }
   instr->SetReferenceTypeInfo(
       ReferenceTypeInfo::Create(handle_cache_->GetClassClassHandle(), /* is_exact */ true));
@@ -844,10 +841,8 @@
 
   ScopedObjectAccess soa(Thread::Current());
   ClassLinker* cl = Runtime::Current()->GetClassLinker();
-  mirror::DexCache* dex_cache =
-      FindDexCacheWithHint(soa.Self(), instr->GetDexFile(), hint_dex_cache_);
   PointerSize pointer_size = cl->GetImagePointerSize();
-  ArtMethod* method = dex_cache->GetResolvedMethod(instr->GetDexMethodIndex(), pointer_size);
+  ArtMethod* method = instr->GetResolvedMethod();
   mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false, pointer_size);
   SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
 }
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index eabda26..c529410 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -133,24 +133,13 @@
 
 void HSharpening::ProcessLoadClass(HLoadClass* load_class) {
   ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<1> hs(soa.Self());
-  Runtime* runtime = Runtime::Current();
-  ClassLinker* class_linker = runtime->GetClassLinker();
-  const DexFile& dex_file = load_class->GetDexFile();
-  dex::TypeIndex type_index = load_class->GetTypeIndex();
-  Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile())
-      ? compilation_unit_.GetDexCache()
-      : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file));
-  mirror::Class* cls = dex_cache->GetResolvedType(type_index);
-  SharpenClass(load_class, cls, handles_, codegen_, compiler_driver_);
+  SharpenClass(load_class, codegen_, compiler_driver_);
 }
 
 void HSharpening::SharpenClass(HLoadClass* load_class,
-                               mirror::Class* klass,
-                               VariableSizedHandleScope* handles,
                                CodeGenerator* codegen,
                                CompilerDriver* compiler_driver) {
-  ScopedAssertNoThreadSuspension sants("Sharpening class in compiler");
+  Handle<mirror::Class> klass = load_class->GetClass();
   DCHECK(load_class->GetLoadKind() == HLoadClass::LoadKind::kDexCacheViaMethod ||
          load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass)
       << load_class->GetLoadKind();
@@ -174,7 +163,6 @@
 
   bool is_in_boot_image = false;
   HLoadClass::LoadKind desired_load_kind = static_cast<HLoadClass::LoadKind>(-1);
-  uint64_t address = 0u;  // Class or dex cache element address.
   Runtime* runtime = Runtime::Current();
   if (codegen->GetCompilerOptions().IsBootImage()) {
     // Compiling boot image. Check if the class is a boot image class.
@@ -182,7 +170,7 @@
     if (!compiler_driver->GetSupportBootImageFixup()) {
       // compiler_driver_test. Do not sharpen.
       desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
-    } else if ((klass != nullptr) && compiler_driver->IsImageClass(
+    } else if ((klass.Get() != nullptr) && compiler_driver->IsImageClass(
         dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) {
       is_in_boot_image = true;
       desired_load_kind = codegen->GetCompilerOptions().GetCompilePic()
@@ -194,20 +182,16 @@
       desired_load_kind = HLoadClass::LoadKind::kBssEntry;
     }
   } else {
-    is_in_boot_image = (klass != nullptr) && runtime->GetHeap()->ObjectIsInBootImageSpace(klass);
+    is_in_boot_image = (klass.Get() != nullptr) &&
+        runtime->GetHeap()->ObjectIsInBootImageSpace(klass.Get());
     if (runtime->UseJitCompilation()) {
       // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
       // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
       if (is_in_boot_image) {
         // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787
         desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
-        address = reinterpret_cast64<uint64_t>(klass);
-      } else if (klass != nullptr) {
+      } else if (klass.Get() != nullptr) {
         desired_load_kind = HLoadClass::LoadKind::kJitTableAddress;
-        // We store in the address field the location of the stack reference maintained
-        // by the handle. We do this now so that the code generation does not need to figure
-        // out which class loader to use.
-        address = reinterpret_cast<uint64_t>(handles->NewHandle(klass).GetReference());
       } else {
         // Class not loaded yet. This happens when the dex code requesting
         // this `HLoadClass` hasn't been executed in the interpreter.
@@ -218,7 +202,6 @@
     } else if (is_in_boot_image && !codegen->GetCompilerOptions().GetCompilePic()) {
       // AOT app compilation. Check if the class is in the boot image.
       desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
-      address = reinterpret_cast64<uint64_t>(klass);
     } else {
       // Not JIT and either the klass is not in boot image or we are compiling in PIC mode.
       desired_load_kind = HLoadClass::LoadKind::kBssEntry;
@@ -240,8 +223,7 @@
       break;
     case HLoadClass::LoadKind::kBootImageAddress:
     case HLoadClass::LoadKind::kJitTableAddress:
-      DCHECK_NE(address, 0u);
-      load_class->SetLoadKindWithAddress(load_kind, address);
+      load_class->SetLoadKind(load_kind);
       break;
     default:
       LOG(FATAL) << "Unexpected load kind: " << load_kind;
diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h
index ae5ccb3..ae3d83e 100644
--- a/compiler/optimizing/sharpening.h
+++ b/compiler/optimizing/sharpening.h
@@ -49,8 +49,6 @@
 
   // Used internally but also by the inliner.
   static void SharpenClass(HLoadClass* load_class,
-                           mirror::Class* klass,
-                           VariableSizedHandleScope* handles,
                            CodeGenerator* codegen,
                            CompilerDriver* compiler_driver)
     REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index fc8af64..6087e36 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -13,8 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include "stack_map_stream.h"
 
+#include "art_method.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+
 namespace art {
 
 void StackMapStream::BeginStackMapEntry(uint32_t dex_pc,
@@ -98,15 +103,27 @@
   current_dex_register_++;
 }
 
-void StackMapStream::BeginInlineInfoEntry(uint32_t method_index,
+static bool EncodeArtMethodInInlineInfo(ArtMethod* method ATTRIBUTE_UNUSED) {
+  // Note: the runtime is null only for unit testing.
+  return Runtime::Current() == nullptr || !Runtime::Current()->IsAotCompiler();
+}
+
+void StackMapStream::BeginInlineInfoEntry(ArtMethod* method,
                                           uint32_t dex_pc,
-                                          InvokeType invoke_type,
-                                          uint32_t num_dex_registers) {
+                                          uint32_t num_dex_registers,
+                                          const DexFile* outer_dex_file) {
   DCHECK(!in_inline_frame_);
   in_inline_frame_ = true;
-  current_inline_info_.method_index = method_index;
+  if (EncodeArtMethodInInlineInfo(method)) {
+    current_inline_info_.method = method;
+  } else {
+    if (dex_pc != static_cast<uint32_t>(-1) && kIsDebugBuild) {
+      ScopedObjectAccess soa(Thread::Current());
+      DCHECK(IsSameDexFile(*outer_dex_file, *method->GetDexFile()));
+    }
+    current_inline_info_.method_index = method->GetDexMethodIndexUnchecked();
+  }
   current_inline_info_.dex_pc = dex_pc;
-  current_inline_info_.invoke_type = invoke_type;
   current_inline_info_.num_dex_registers = num_dex_registers;
   current_inline_info_.dex_register_locations_start_index = dex_register_locations_.size();
   if (num_dex_registers != 0) {
@@ -229,25 +246,32 @@
 void StackMapStream::ComputeInlineInfoEncoding() {
   uint32_t method_index_max = 0;
   uint32_t dex_pc_max = DexFile::kDexNoIndex;
-  uint32_t invoke_type_max = 0;
+  uint32_t extra_data_max = 0;
 
   uint32_t inline_info_index = 0;
   for (const StackMapEntry& entry : stack_maps_) {
     for (size_t j = 0; j < entry.inlining_depth; ++j) {
       InlineInfoEntry inline_entry = inline_infos_[inline_info_index++];
-      method_index_max = std::max(method_index_max, inline_entry.method_index);
+      if (inline_entry.method == nullptr) {
+        method_index_max = std::max(method_index_max, inline_entry.method_index);
+        extra_data_max = std::max(extra_data_max, 1u);
+      } else {
+        method_index_max = std::max(
+            method_index_max, High32Bits(reinterpret_cast<uintptr_t>(inline_entry.method)));
+        extra_data_max = std::max(
+            extra_data_max, Low32Bits(reinterpret_cast<uintptr_t>(inline_entry.method)));
+      }
       if (inline_entry.dex_pc != DexFile::kDexNoIndex &&
           (dex_pc_max == DexFile::kDexNoIndex || dex_pc_max < inline_entry.dex_pc)) {
         dex_pc_max = inline_entry.dex_pc;
       }
-      invoke_type_max = std::max(invoke_type_max, static_cast<uint32_t>(inline_entry.invoke_type));
     }
   }
   DCHECK_EQ(inline_info_index, inline_infos_.size());
 
   inline_info_encoding_.SetFromSizes(method_index_max,
                                      dex_pc_max,
-                                     invoke_type_max,
+                                     extra_data_max,
                                      dex_register_maps_size_);
 }
 
@@ -354,9 +378,20 @@
       DCHECK_LE(entry.inline_infos_start_index + entry.inlining_depth, inline_infos_.size());
       for (size_t depth = 0; depth < entry.inlining_depth; ++depth) {
         InlineInfoEntry inline_entry = inline_infos_[depth + entry.inline_infos_start_index];
-        inline_info.SetMethodIndexAtDepth(inline_info_encoding_, depth, inline_entry.method_index);
+        if (inline_entry.method != nullptr) {
+          inline_info.SetMethodIndexAtDepth(
+              inline_info_encoding_,
+              depth,
+              High32Bits(reinterpret_cast<uintptr_t>(inline_entry.method)));
+          inline_info.SetExtraDataAtDepth(
+              inline_info_encoding_,
+              depth,
+              Low32Bits(reinterpret_cast<uintptr_t>(inline_entry.method)));
+        } else {
+          inline_info.SetMethodIndexAtDepth(inline_info_encoding_, depth, inline_entry.method_index);
+          inline_info.SetExtraDataAtDepth(inline_info_encoding_, depth, 1);
+        }
         inline_info.SetDexPcAtDepth(inline_info_encoding_, depth, inline_entry.dex_pc);
-        inline_info.SetInvokeTypeAtDepth(inline_info_encoding_, depth, inline_entry.invoke_type);
         if (inline_entry.num_dex_registers == 0) {
           // No dex map available.
           inline_info.SetDexRegisterMapOffsetAtDepth(inline_info_encoding_,
@@ -544,10 +579,13 @@
         InlineInfoEntry inline_entry = inline_infos_[inline_info_index];
         DCHECK_EQ(inline_info.GetDexPcAtDepth(encoding.inline_info_encoding, d),
                   inline_entry.dex_pc);
-        DCHECK_EQ(inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, d),
-                  inline_entry.method_index);
-        DCHECK_EQ(inline_info.GetInvokeTypeAtDepth(encoding.inline_info_encoding, d),
-                  inline_entry.invoke_type);
+        if (inline_info.EncodesArtMethodAtDepth(encoding.inline_info_encoding, d)) {
+          DCHECK_EQ(inline_info.GetArtMethodAtDepth(encoding.inline_info_encoding, d),
+                    inline_entry.method);
+        } else {
+          DCHECK_EQ(inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, d),
+                    inline_entry.method_index);
+        }
 
         CheckDexRegisterMap(code_info,
                             code_info.GetDexRegisterMapAtDepth(
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 53a9795..d6f42b3 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -109,8 +109,8 @@
 
   struct InlineInfoEntry {
     uint32_t dex_pc;  // DexFile::kDexNoIndex for intrinsified native methods.
+    ArtMethod* method;
     uint32_t method_index;
-    InvokeType invoke_type;
     uint32_t num_dex_registers;
     BitVector* live_dex_registers_mask;
     size_t dex_register_locations_start_index;
@@ -126,10 +126,10 @@
 
   void AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value);
 
-  void BeginInlineInfoEntry(uint32_t method_index,
+  void BeginInlineInfoEntry(ArtMethod* method,
                             uint32_t dex_pc,
-                            InvokeType invoke_type,
-                            uint32_t num_dex_registers);
+                            uint32_t num_dex_registers,
+                            const DexFile* outer_dex_file = nullptr);
   void EndInlineInfoEntry();
 
   size_t GetNumberOfStackMaps() const {
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index 967fd96..22810ea 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -16,6 +16,7 @@
 
 #include "stack_map.h"
 
+#include "art_method.h"
 #include "base/arena_bit_vector.h"
 #include "stack_map_stream.h"
 
@@ -128,6 +129,7 @@
   ArenaPool pool;
   ArenaAllocator arena(&pool);
   StackMapStream stream(&arena);
+  ArtMethod art_method;
 
   ArenaBitVector sp_mask1(&arena, 0, true);
   sp_mask1.SetBit(2);
@@ -137,9 +139,9 @@
   stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 2);
   stream.AddDexRegisterEntry(Kind::kInStack, 0);         // Short location.
   stream.AddDexRegisterEntry(Kind::kConstant, -2);       // Large location.
-  stream.BeginInlineInfoEntry(82, 3, kDirect, number_of_dex_registers_in_inline_info);
+  stream.BeginInlineInfoEntry(&art_method, 3, number_of_dex_registers_in_inline_info);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(42, 2, kStatic, number_of_dex_registers_in_inline_info);
+  stream.BeginInlineInfoEntry(&art_method, 2, number_of_dex_registers_in_inline_info);
   stream.EndInlineInfoEntry();
   stream.EndStackMapEntry();
 
@@ -238,12 +240,10 @@
     ASSERT_TRUE(stack_map.HasInlineInfo(encoding.stack_map_encoding));
     InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
     ASSERT_EQ(2u, inline_info.GetDepth(encoding.inline_info_encoding));
-    ASSERT_EQ(82u, inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0));
-    ASSERT_EQ(42u, inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1));
     ASSERT_EQ(3u, inline_info.GetDexPcAtDepth(encoding.inline_info_encoding, 0));
     ASSERT_EQ(2u, inline_info.GetDexPcAtDepth(encoding.inline_info_encoding, 1));
-    ASSERT_EQ(kDirect, inline_info.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0));
-    ASSERT_EQ(kStatic, inline_info.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1));
+    ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 0));
+    ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 1));
   }
 
   // Second stack map.
@@ -662,6 +662,7 @@
   ArenaPool pool;
   ArenaAllocator arena(&pool);
   StackMapStream stream(&arena);
+  ArtMethod art_method;
 
   ArenaBitVector sp_mask1(&arena, 0, true);
   sp_mask1.SetBit(2);
@@ -672,10 +673,10 @@
   stream.AddDexRegisterEntry(Kind::kInStack, 0);
   stream.AddDexRegisterEntry(Kind::kConstant, 4);
 
-  stream.BeginInlineInfoEntry(42, 2, kStatic, 1);
+  stream.BeginInlineInfoEntry(&art_method, 2, 1);
   stream.AddDexRegisterEntry(Kind::kInStack, 8);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(82, 3, kStatic, 3);
+  stream.BeginInlineInfoEntry(&art_method, 3, 3);
   stream.AddDexRegisterEntry(Kind::kInStack, 16);
   stream.AddDexRegisterEntry(Kind::kConstant, 20);
   stream.AddDexRegisterEntry(Kind::kInRegister, 15);
@@ -688,15 +689,15 @@
   stream.AddDexRegisterEntry(Kind::kInStack, 56);
   stream.AddDexRegisterEntry(Kind::kConstant, 0);
 
-  stream.BeginInlineInfoEntry(42, 2, kDirect, 1);
+  stream.BeginInlineInfoEntry(&art_method, 2, 1);
   stream.AddDexRegisterEntry(Kind::kInStack, 12);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(82, 3, kStatic, 3);
+  stream.BeginInlineInfoEntry(&art_method, 3, 3);
   stream.AddDexRegisterEntry(Kind::kInStack, 80);
   stream.AddDexRegisterEntry(Kind::kConstant, 10);
   stream.AddDexRegisterEntry(Kind::kInRegister, 5);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(52, 5, kVirtual, 0);
+  stream.BeginInlineInfoEntry(&art_method, 5, 0);
   stream.EndInlineInfoEntry();
 
   stream.EndStackMapEntry();
@@ -712,12 +713,12 @@
   stream.AddDexRegisterEntry(Kind::kInStack, 56);
   stream.AddDexRegisterEntry(Kind::kConstant, 0);
 
-  stream.BeginInlineInfoEntry(42, 2, kVirtual, 0);
+  stream.BeginInlineInfoEntry(&art_method, 2, 0);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(52, 5, kInterface, 1);
+  stream.BeginInlineInfoEntry(&art_method, 5, 1);
   stream.AddDexRegisterEntry(Kind::kInRegister, 2);
   stream.EndInlineInfoEntry();
-  stream.BeginInlineInfoEntry(52, 10, kStatic, 2);
+  stream.BeginInlineInfoEntry(&art_method, 10, 2);
   stream.AddDexRegisterEntry(Kind::kNone, 0);
   stream.AddDexRegisterEntry(Kind::kInRegister, 3);
   stream.EndInlineInfoEntry();
@@ -743,11 +744,9 @@
     InlineInfo if0 = ci.GetInlineInfoOf(sm0, encoding);
     ASSERT_EQ(2u, if0.GetDepth(encoding.inline_info_encoding));
     ASSERT_EQ(2u, if0.GetDexPcAtDepth(encoding.inline_info_encoding, 0));
-    ASSERT_EQ(42u, if0.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0));
-    ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0));
+    ASSERT_TRUE(if0.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 0));
     ASSERT_EQ(3u, if0.GetDexPcAtDepth(encoding.inline_info_encoding, 1));
-    ASSERT_EQ(82u, if0.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1));
-    ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1));
+    ASSERT_TRUE(if0.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 1));
 
     DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if0, encoding, 1);
     ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding));
@@ -769,14 +768,11 @@
     InlineInfo if1 = ci.GetInlineInfoOf(sm1, encoding);
     ASSERT_EQ(3u, if1.GetDepth(encoding.inline_info_encoding));
     ASSERT_EQ(2u, if1.GetDexPcAtDepth(encoding.inline_info_encoding, 0));
-    ASSERT_EQ(42u, if1.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0));
-    ASSERT_EQ(kDirect, if1.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0));
+    ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 0));
     ASSERT_EQ(3u, if1.GetDexPcAtDepth(encoding.inline_info_encoding, 1));
-    ASSERT_EQ(82u, if1.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1));
-    ASSERT_EQ(kStatic, if1.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1));
+    ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 1));
     ASSERT_EQ(5u, if1.GetDexPcAtDepth(encoding.inline_info_encoding, 2));
-    ASSERT_EQ(52u, if1.GetMethodIndexAtDepth(encoding.inline_info_encoding, 2));
-    ASSERT_EQ(kVirtual, if1.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 2));
+    ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 2));
 
     DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if1, encoding, 1);
     ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding));
@@ -810,14 +806,11 @@
     InlineInfo if2 = ci.GetInlineInfoOf(sm3, encoding);
     ASSERT_EQ(3u, if2.GetDepth(encoding.inline_info_encoding));
     ASSERT_EQ(2u, if2.GetDexPcAtDepth(encoding.inline_info_encoding, 0));
-    ASSERT_EQ(42u, if2.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0));
-    ASSERT_EQ(kVirtual, if2.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0));
+    ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 0));
     ASSERT_EQ(5u, if2.GetDexPcAtDepth(encoding.inline_info_encoding, 1));
-    ASSERT_EQ(52u, if2.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1));
-    ASSERT_EQ(kInterface, if2.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1));
+    ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 1));
     ASSERT_EQ(10u, if2.GetDexPcAtDepth(encoding.inline_info_encoding, 2));
-    ASSERT_EQ(52u, if2.GetMethodIndexAtDepth(encoding.inline_info_encoding, 2));
-    ASSERT_EQ(kStatic, if2.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 2));
+    ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 2));
 
     ASSERT_FALSE(if2.HasDexRegisterMapAtDepth(encoding.inline_info_encoding, 0));
 
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index ab4f9e9..5e55210 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -5610,7 +5610,7 @@
   " 214:	ecbd 8a10 	vpop	{s16-s31}\n",
   " 218:	e8bd 8de0 	ldmia.w	sp!, {r5, r6, r7, r8, sl, fp, pc}\n",
   " 21c:	4660      	mov	r0, ip\n",
-  " 21e:	f8d9 c2b0 	ldr.w	ip, [r9, #688]	; 0x2b0\n",
+  " 21e:	f8d9 c2c0 	ldr.w	ip, [r9, #704]	; 0x2c0\n",
   " 222:	47e0      	blx	ip\n",
   nullptr
 };
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 7ae13a5..2bf7495 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -54,48 +54,6 @@
 
 namespace art {
 
-static bool LocationToFilename(const std::string& location, InstructionSet isa,
-                               std::string* filename) {
-  bool has_system = false;
-  bool has_cache = false;
-  // image_location = /system/framework/boot.art
-  // system_image_filename = /system/framework/<image_isa>/boot.art
-  std::string system_filename(GetSystemImageFilename(location.c_str(), isa));
-  if (OS::FileExists(system_filename.c_str())) {
-    has_system = true;
-  }
-
-  bool have_android_data = false;
-  bool dalvik_cache_exists = false;
-  bool is_global_cache = false;
-  std::string dalvik_cache;
-  GetDalvikCache(GetInstructionSetString(isa), false, &dalvik_cache,
-                 &have_android_data, &dalvik_cache_exists, &is_global_cache);
-
-  std::string cache_filename;
-  if (have_android_data && dalvik_cache_exists) {
-    // Always set output location even if it does not exist,
-    // so that the caller knows where to create the image.
-    //
-    // image_location = /system/framework/boot.art
-    // *image_filename = /data/dalvik-cache/<image_isa>/boot.art
-    std::string error_msg;
-    if (GetDalvikCacheFilename(location.c_str(), dalvik_cache.c_str(),
-                               &cache_filename, &error_msg)) {
-      has_cache = true;
-    }
-  }
-  if (has_system) {
-    *filename = system_filename;
-    return true;
-  } else if (has_cache) {
-    *filename = cache_filename;
-    return true;
-  } else {
-    return false;
-  }
-}
-
 static const OatHeader* GetOatHeader(const ElfFile* elf_file) {
   uint64_t off = 0;
   if (!elf_file->GetSectionOffsetAndSize(".rodata", &off, nullptr)) {
@@ -106,28 +64,10 @@
   return oat_header;
 }
 
-// This function takes an elf file and reads the current patch delta value
-// encoded in its oat header value
-static bool ReadOatPatchDelta(const ElfFile* elf_file, off_t* delta, std::string* error_msg) {
-  const OatHeader* oat_header = GetOatHeader(elf_file);
-  if (oat_header == nullptr) {
-    *error_msg = "Unable to get oat header from elf file.";
-    return false;
-  }
-  if (!oat_header->IsValid()) {
-    *error_msg = "Elf file has an invalid oat header";
-    return false;
-  }
-  *delta = oat_header->GetImagePatchDelta();
-  return true;
-}
-
-static File* CreateOrOpen(const char* name, bool* created) {
+static File* CreateOrOpen(const char* name) {
   if (OS::FileExists(name)) {
-    *created = false;
     return OS::OpenFileReadWrite(name);
   } else {
-    *created = true;
     std::unique_ptr<File> f(OS::CreateEmptyFile(name));
     if (f.get() != nullptr) {
       if (fchmod(f->Fd(), 0644) != 0) {
@@ -206,12 +146,11 @@
   Thread::Current()->TransitionFromRunnableToSuspended(kNative);
   ScopedObjectAccess soa(Thread::Current());
 
-  t.NewTiming("Image and oat Patching setup");
+  t.NewTiming("Image Patching setup");
   std::vector<gc::space::ImageSpace*> spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces();
   std::map<gc::space::ImageSpace*, std::unique_ptr<File>> space_to_file_map;
   std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>> space_to_memmap_map;
   std::map<gc::space::ImageSpace*, PatchOat> space_to_patchoat_map;
-  std::map<gc::space::ImageSpace*, bool> space_to_skip_patching_map;
 
   for (size_t i = 0; i < spaces.size(); ++i) {
     gc::space::ImageSpace* space = spaces[i];
@@ -255,8 +194,7 @@
     space_to_memmap_map.emplace(space, std::move(image));
   }
 
-  // Do a first pass over the image spaces. Symlink PIC oat and vdex files, and
-  // prepare PatchOat instances for the rest.
+  // Symlink PIC oat and vdex files and patch the image spaces in memory.
   for (size_t i = 0; i < spaces.size(); ++i) {
     gc::space::ImageSpace* space = spaces[i];
     std::string input_image_filename = space->GetImageFilename();
@@ -277,14 +215,17 @@
       return false;
     }
 
-    bool skip_patching_oat = false;
     MaybePic is_oat_pic = IsOatPic(elf.get());
     if (is_oat_pic >= ERROR_FIRST) {
       // Error logged by IsOatPic
       return false;
-    } else if (is_oat_pic == PIC) {
-      // Do not need to do ELF-file patching. Create a symlink and skip the ELF patching.
+    } else if (is_oat_pic == NOT_PIC) {
+      LOG(ERROR) << input_oat_file->GetPath() << " is not PIC";
+      return false;
+    } else {
+      CHECK(is_oat_pic == PIC);
 
+      // Create a symlink.
       std::string converted_image_filename = space->GetImageLocation();
       std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@');
       std::string output_image_filename = output_directory +
@@ -296,23 +237,16 @@
           ImageHeader::GetOatLocationFromImageLocation(output_image_filename);
 
       if (!ReplaceOatFileWithSymlink(input_oat_file->GetPath(),
-                                     output_oat_filename,
-                                     false,
-                                     true) ||
+                                     output_oat_filename) ||
           !SymlinkFile(input_vdex_filename, output_vdex_filename)) {
         // Errors already logged by above call.
         return false;
       }
-      // Don't patch the OAT, since we just symlinked it. Image still needs patching.
-      skip_patching_oat = true;
-    } else {
-      CHECK(is_oat_pic == NOT_PIC);
     }
 
     PatchOat& p = space_to_patchoat_map.emplace(space,
                                                 PatchOat(
                                                     isa,
-                                                    elf.release(),
                                                     space_to_memmap_map.find(space)->second.get(),
                                                     space->GetLiveBitmap(),
                                                     space->GetMemMap(),
@@ -320,36 +254,24 @@
                                                     &space_to_memmap_map,
                                                     timings)).first->second;
 
-    t.NewTiming("Patching files");
-    if (!skip_patching_oat && !p.PatchElf()) {
-      LOG(ERROR) << "Failed to patch oat file " << input_oat_file->GetPath();
-      return false;
-    }
+    t.NewTiming("Patching image");
     if (!p.PatchImage(i == 0)) {
       LOG(ERROR) << "Failed to patch image file " << input_image_filename;
       return false;
     }
-
-    space_to_skip_patching_map.emplace(space, skip_patching_oat);
   }
 
-  // Do a second pass over the image spaces. Patch image files, non-PIC oat files
-  // and symlink their corresponding vdex files.
+  // Write the patched image spaces.
   for (size_t i = 0; i < spaces.size(); ++i) {
     gc::space::ImageSpace* space = spaces[i];
-    std::string input_image_filename = space->GetImageFilename();
-    std::string input_vdex_filename =
-        ImageHeader::GetVdexLocationFromImageLocation(input_image_filename);
 
-    t.NewTiming("Writing files");
+    t.NewTiming("Writing image");
     std::string converted_image_filename = space->GetImageLocation();
     std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@');
     std::string output_image_filename = output_directory +
         (android::base::StartsWith(converted_image_filename, "/") ? "" : "/") +
         converted_image_filename;
-    bool new_oat_out;
-    std::unique_ptr<File>
-        output_image_file(CreateOrOpen(output_image_filename.c_str(), &new_oat_out));
+    std::unique_ptr<File> output_image_file(CreateOrOpen(output_image_filename.c_str()));
     if (output_image_file.get() == nullptr) {
       LOG(ERROR) << "Failed to open output image file at " << output_image_filename;
       return false;
@@ -362,48 +284,10 @@
     if (!success) {
       return false;
     }
-
-    bool skip_patching_oat = space_to_skip_patching_map.find(space)->second;
-    if (!skip_patching_oat) {
-      std::string output_vdex_filename =
-          ImageHeader::GetVdexLocationFromImageLocation(output_image_filename);
-      std::string output_oat_filename =
-          ImageHeader::GetOatLocationFromImageLocation(output_image_filename);
-
-      std::unique_ptr<File>
-          output_oat_file(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
-      if (output_oat_file.get() == nullptr) {
-        LOG(ERROR) << "Failed to open output oat file at " << output_oat_filename;
-        return false;
-      }
-      success = p.WriteElf(output_oat_file.get());
-      success = FinishFile(output_oat_file.get(), success);
-      if (success) {
-        success = SymlinkFile(input_vdex_filename, output_vdex_filename);
-      }
-      if (!success) {
-        return false;
-      }
-    }
   }
   return true;
 }
 
-bool PatchOat::WriteElf(File* out) {
-  TimingLogger::ScopedTiming t("Writing Elf File", timings_);
-
-  CHECK(oat_file_.get() != nullptr);
-  CHECK(out != nullptr);
-  size_t expect = oat_file_->Size();
-  if (out->WriteFully(reinterpret_cast<char*>(oat_file_->Begin()), expect) &&
-      out->SetLength(expect) == 0) {
-    return true;
-  } else {
-    LOG(ERROR) << "Writing to oat file " << out->GetPath() << " failed.";
-    return false;
-  }
-}
-
 bool PatchOat::WriteImage(File* out) {
   TimingLogger::ScopedTiming t("Writing image File", timings_);
   std::string error_msg;
@@ -466,22 +350,7 @@
 }
 
 bool PatchOat::ReplaceOatFileWithSymlink(const std::string& input_oat_filename,
-                                         const std::string& output_oat_filename,
-                                         bool output_oat_opened_from_fd,
-                                         bool new_oat_out) {
-  // Need a file when we are PIC, since we symlink over it. Refusing to symlink into FD.
-  if (output_oat_opened_from_fd) {
-    // TODO: installd uses --output-oat-fd. Should we change class linking logic for PIC?
-    LOG(ERROR) << "No output oat filename specified, needs filename for when we are PIC";
-    return false;
-  }
-
-  // Image was PIC. Create symlink where the oat is supposed to go.
-  if (!new_oat_out) {
-    LOG(ERROR) << "Oat file " << output_oat_filename << " already exists, refusing to overwrite";
-    return false;
-  }
-
+                                         const std::string& output_oat_filename) {
   // Delete the original file, since we won't need it.
   unlink(output_oat_filename.c_str());
 
@@ -799,133 +668,6 @@
       object->GetDataPtrSize(pointer_size)), pointer_size);
 }
 
-bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogger* timings,
-                     bool output_oat_opened_from_fd, bool new_oat_out) {
-  CHECK(input_oat != nullptr);
-  CHECK(output_oat != nullptr);
-  CHECK_GE(input_oat->Fd(), 0);
-  CHECK_GE(output_oat->Fd(), 0);
-  TimingLogger::ScopedTiming t("Setup Oat File Patching", timings);
-
-  std::string error_msg;
-  std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat,
-                                             PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg));
-  if (elf.get() == nullptr) {
-    LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
-    return false;
-  }
-
-  MaybePic is_oat_pic = IsOatPic(elf.get());
-  if (is_oat_pic >= ERROR_FIRST) {
-    // Error logged by IsOatPic
-    return false;
-  } else if (is_oat_pic == PIC) {
-    // Do not need to do ELF-file patching. Create a symlink and skip the rest.
-    // Any errors will be logged by the function call.
-    return ReplaceOatFileWithSymlink(input_oat->GetPath(),
-                                     output_oat->GetPath(),
-                                     output_oat_opened_from_fd,
-                                     new_oat_out);
-  } else {
-    CHECK(is_oat_pic == NOT_PIC);
-  }
-
-  PatchOat p(elf.release(), delta, timings);
-  t.NewTiming("Patch Oat file");
-  if (!p.PatchElf()) {
-    return false;
-  }
-
-  t.NewTiming("Writing oat file");
-  if (!p.WriteElf(output_oat)) {
-    return false;
-  }
-  return true;
-}
-
-template <typename ElfFileImpl>
-bool PatchOat::PatchOatHeader(ElfFileImpl* oat_file) {
-  auto rodata_sec = oat_file->FindSectionByName(".rodata");
-  if (rodata_sec == nullptr) {
-    return false;
-  }
-  OatHeader* oat_header = reinterpret_cast<OatHeader*>(oat_file->Begin() + rodata_sec->sh_offset);
-  if (!oat_header->IsValid()) {
-    LOG(ERROR) << "Elf file " << oat_file->GetFilePath() << " has an invalid oat header";
-    return false;
-  }
-  oat_header->RelocateOat(delta_);
-  return true;
-}
-
-bool PatchOat::PatchElf() {
-  if (oat_file_->Is64Bit()) {
-    return PatchElf<ElfFileImpl64>(oat_file_->GetImpl64());
-  } else {
-    return PatchElf<ElfFileImpl32>(oat_file_->GetImpl32());
-  }
-}
-
-template <typename ElfFileImpl>
-bool PatchOat::PatchElf(ElfFileImpl* oat_file) {
-  TimingLogger::ScopedTiming t("Fixup Elf Text Section", timings_);
-
-  // Fix up absolute references to locations within the boot image.
-  if (!oat_file->ApplyOatPatchesTo(".text", delta_)) {
-    return false;
-  }
-
-  // Update the OatHeader fields referencing the boot image.
-  if (!PatchOatHeader<ElfFileImpl>(oat_file)) {
-    return false;
-  }
-
-  bool need_boot_oat_fixup = true;
-  for (unsigned int i = 0; i < oat_file->GetProgramHeaderNum(); ++i) {
-    auto hdr = oat_file->GetProgramHeader(i);
-    if (hdr->p_type == PT_LOAD && hdr->p_vaddr == 0u) {
-      need_boot_oat_fixup = false;
-      break;
-    }
-  }
-  if (!need_boot_oat_fixup) {
-    // This is an app oat file that can be loaded at an arbitrary address in memory.
-    // Boot image references were patched above and there's nothing else to do.
-    return true;
-  }
-
-  // This is a boot oat file that's loaded at a particular address and we need
-  // to patch all absolute addresses, starting with ELF program headers.
-
-  t.NewTiming("Fixup Elf Headers");
-  // Fixup Phdr's
-  oat_file->FixupProgramHeaders(delta_);
-
-  t.NewTiming("Fixup Section Headers");
-  // Fixup Shdr's
-  oat_file->FixupSectionHeaders(delta_);
-
-  t.NewTiming("Fixup Dynamics");
-  oat_file->FixupDynamic(delta_);
-
-  t.NewTiming("Fixup Elf Symbols");
-  // Fixup dynsym
-  if (!oat_file->FixupSymbols(delta_, true)) {
-    return false;
-  }
-  // Fixup symtab
-  if (!oat_file->FixupSymbols(delta_, false)) {
-    return false;
-  }
-
-  t.NewTiming("Fixup Debug Sections");
-  if (!oat_file->FixupDebugSections(delta_)) {
-    return false;
-  }
-
-  return true;
-}
-
 static int orig_argc;
 static char** orig_argv;
 
@@ -960,32 +702,10 @@
   UsageError("Usage: patchoat [options]...");
   UsageError("");
   UsageError("  --instruction-set=<isa>: Specifies the instruction set the patched code is");
-  UsageError("      compiled for. Required if you use --input-oat-location");
-  UsageError("");
-  UsageError("  --input-oat-file=<file.oat>: Specifies the exact filename of the oat file to be");
-  UsageError("      patched.");
-  UsageError("");
-  UsageError("  --input-oat-fd=<file-descriptor>: Specifies the file-descriptor of the oat file");
-  UsageError("      to be patched.");
-  UsageError("");
-  UsageError("  --input-vdex-fd=<file-descriptor>: Specifies the file-descriptor of the vdex file");
-  UsageError("      associated with the oat file.");
-  UsageError("");
-  UsageError("  --input-oat-location=<file.oat>: Specifies the 'location' to read the patched");
-  UsageError("      oat file from. If used one must also supply the --instruction-set");
+  UsageError("      compiled for (required).");
   UsageError("");
   UsageError("  --input-image-location=<file.art>: Specifies the 'location' of the image file to");
-  UsageError("      be patched. If --instruction-set is not given it will use the instruction set");
-  UsageError("      extracted from the --input-oat-file.");
-  UsageError("");
-  UsageError("  --output-oat-file=<file.oat>: Specifies the exact file to write the patched oat");
-  UsageError("      file to.");
-  UsageError("");
-  UsageError("  --output-oat-fd=<file-descriptor>: Specifies the file-descriptor to write the");
-  UsageError("      patched oat file to.");
-  UsageError("");
-  UsageError("  --output-vdex-fd=<file-descriptor>: Specifies the file-descriptor to copy the");
-  UsageError("      the vdex file associated with the patch oat file to.");
+  UsageError("      be patched.");
   UsageError("");
   UsageError("  --output-image-file=<file.art>: Specifies the exact file to write the patched");
   UsageError("      image file to.");
@@ -993,15 +713,6 @@
   UsageError("  --base-offset-delta=<delta>: Specify the amount to change the old base-offset by.");
   UsageError("      This value may be negative.");
   UsageError("");
-  UsageError("  --patched-image-location=<file.art>: Relocate the oat file to be the same as the");
-  UsageError("      image at the given location. If used one must also specify the");
-  UsageError("      --instruction-set flag. It will search for this image in the same way that");
-  UsageError("      is done when loading one.");
-  UsageError("");
-  UsageError("  --lock-output: Obtain a flock on output oat file before starting.");
-  UsageError("");
-  UsageError("  --no-lock-output: Do not attempt to obtain a flock on output oat file.");
-  UsageError("");
   UsageError("  --dump-timings: dump out patch timing information");
   UsageError("");
   UsageError("  --no-dump-timings: do not dump out patch timing information");
@@ -1010,34 +721,6 @@
   exit(EXIT_FAILURE);
 }
 
-static bool ReadBaseDelta(const char* name, off_t* delta, std::string* error_msg) {
-  CHECK(name != nullptr);
-  CHECK(delta != nullptr);
-  std::unique_ptr<File> file;
-  if (OS::FileExists(name)) {
-    file.reset(OS::OpenFileForReading(name));
-    if (file.get() == nullptr) {
-      *error_msg = "Failed to open file %s for reading";
-      return false;
-    }
-  } else {
-    *error_msg = "File %s does not exist";
-    return false;
-  }
-  CHECK(file.get() != nullptr);
-  ImageHeader hdr;
-  if (sizeof(hdr) != file->Read(reinterpret_cast<char*>(&hdr), sizeof(hdr), 0)) {
-    *error_msg = "Failed to read file %s";
-    return false;
-  }
-  if (!hdr.IsValid()) {
-    *error_msg = "%s does not contain a valid image header.";
-    return false;
-  }
-  *delta = hdr.GetPatchDelta();
-  return true;
-}
-
 static int patchoat_image(TimingLogger& timings,
                           InstructionSet isa,
                           const std::string& input_image_location,
@@ -1076,293 +759,6 @@
   return ret ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
-static int patchoat_oat(TimingLogger& timings,
-                        InstructionSet isa,
-                        const std::string& patched_image_location,
-                        off_t base_delta,
-                        bool base_delta_set,
-                        int input_oat_fd,
-                        int input_vdex_fd,
-                        const std::string& input_oat_location,
-                        std::string input_oat_filename,
-                        bool have_input_oat,
-                        int output_oat_fd,
-                        int output_vdex_fd,
-                        std::string output_oat_filename,
-                        bool have_output_oat,
-                        bool lock_output,
-                        bool debug) {
-  {
-    // Only 1 of these may be set.
-    uint32_t cnt = 0;
-    cnt += (base_delta_set) ? 1 : 0;
-    cnt += (!patched_image_location.empty()) ? 1 : 0;
-    if (cnt > 1) {
-      Usage("Only one of --base-offset-delta or --patched-image-location may be used.");
-    } else if (cnt == 0) {
-      Usage("Must specify --base-offset-delta or --patched-image-location.");
-    }
-  }
-
-  if (!have_input_oat || !have_output_oat) {
-    Usage("Both input and output oat must be supplied to patch an app odex.");
-  }
-
-  if (!input_oat_location.empty()) {
-    if (!LocationToFilename(input_oat_location, isa, &input_oat_filename)) {
-      Usage("Unable to find filename for input oat location %s", input_oat_location.c_str());
-    }
-    if (debug) {
-      LOG(INFO) << "Using input-oat-file " << input_oat_filename;
-    }
-  }
-
-  if ((input_oat_fd == -1) != (input_vdex_fd == -1)) {
-    Usage("Either both input oat and vdex have to be passed as file descriptors or none of them");
-  } else if ((output_oat_fd == -1) != (output_vdex_fd == -1)) {
-    Usage("Either both output oat and vdex have to be passed as file descriptors or none of them");
-  }
-
-  bool match_delta = false;
-  if (!patched_image_location.empty()) {
-    std::string system_filename;
-    bool has_system = false;
-    std::string cache_filename;
-    bool has_cache = false;
-    bool has_android_data_unused = false;
-    bool is_global_cache = false;
-    if (!gc::space::ImageSpace::FindImageFilename(patched_image_location.c_str(), isa,
-                                                  &system_filename, &has_system, &cache_filename,
-                                                  &has_android_data_unused, &has_cache,
-                                                  &is_global_cache)) {
-      Usage("Unable to determine image file for location %s", patched_image_location.c_str());
-    }
-    std::string patched_image_filename;
-    if (has_cache) {
-      patched_image_filename = cache_filename;
-    } else if (has_system) {
-      LOG(WARNING) << "Only image file found was in /system for image location "
-          << patched_image_location;
-      patched_image_filename = system_filename;
-    } else {
-      Usage("Unable to determine image file for location %s", patched_image_location.c_str());
-    }
-    if (debug) {
-      LOG(INFO) << "Using patched-image-file " << patched_image_filename;
-    }
-
-    base_delta_set = true;
-    match_delta = true;
-    std::string error_msg;
-    if (!ReadBaseDelta(patched_image_filename.c_str(), &base_delta, &error_msg)) {
-      Usage(error_msg.c_str(), patched_image_filename.c_str());
-    }
-  }
-
-  if (!IsAligned<kPageSize>(base_delta)) {
-    Usage("Base offset/delta must be alligned to a pagesize (0x%08x) boundary.", kPageSize);
-  }
-
-  // We can symlink VDEX only if we have both input and output specified as filenames.
-  // Store that piece of information before we possibly create bogus filenames for
-  // files passed as file descriptors.
-  bool symlink_vdex = !input_oat_filename.empty() && !output_oat_filename.empty();
-
-  // Infer names of VDEX files.
-  std::string input_vdex_filename;
-  std::string output_vdex_filename;
-  if (!input_oat_filename.empty()) {
-    input_vdex_filename = ReplaceFileExtension(input_oat_filename, "vdex");
-  }
-  if (!output_oat_filename.empty()) {
-    output_vdex_filename = ReplaceFileExtension(output_oat_filename, "vdex");
-  }
-
-  // Do we need to cleanup output files if we fail?
-  bool new_oat_out = false;
-  bool new_vdex_out = false;
-
-  std::unique_ptr<File> input_oat;
-  std::unique_ptr<File> output_oat;
-
-  if (input_oat_fd != -1) {
-    if (input_oat_filename.empty()) {
-      input_oat_filename = "input-oat-file";
-    }
-    input_oat.reset(new File(input_oat_fd, input_oat_filename, false));
-    if (input_oat_fd == output_oat_fd) {
-      input_oat.get()->DisableAutoClose();
-    }
-    if (input_oat == nullptr) {
-      // Unlikely, but ensure exhaustive logging in non-0 exit code case
-      LOG(ERROR) << "Failed to open input oat file by its FD" << input_oat_fd;
-      return EXIT_FAILURE;
-    }
-  } else {
-    CHECK(!input_oat_filename.empty());
-    input_oat.reset(OS::OpenFileForReading(input_oat_filename.c_str()));
-    if (input_oat == nullptr) {
-      int err = errno;
-      LOG(ERROR) << "Failed to open input oat file " << input_oat_filename
-          << ": " << strerror(err) << "(" << err << ")";
-      return EXIT_FAILURE;
-    }
-  }
-
-  std::string error_msg;
-  std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat.get(), PROT_READ, MAP_PRIVATE, &error_msg));
-  if (elf.get() == nullptr) {
-    LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
-    return EXIT_FAILURE;
-  }
-  if (!elf->HasSection(".text.oat_patches")) {
-    LOG(ERROR) << "missing oat patch section in input oat file " << input_oat->GetPath();
-    return EXIT_FAILURE;
-  }
-
-  if (output_oat_fd != -1) {
-    if (output_oat_filename.empty()) {
-      output_oat_filename = "output-oat-file";
-    }
-    output_oat.reset(new File(output_oat_fd, output_oat_filename, true));
-    if (output_oat == nullptr) {
-      // Unlikely, but ensure exhaustive logging in non-0 exit code case
-      LOG(ERROR) << "Failed to open output oat file by its FD" << output_oat_fd;
-    }
-  } else {
-    CHECK(!output_oat_filename.empty());
-    output_oat.reset(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
-    if (output_oat == nullptr) {
-      int err = errno;
-      LOG(ERROR) << "Failed to open output oat file " << output_oat_filename
-          << ": " << strerror(err) << "(" << err << ")";
-    }
-  }
-
-  // Open VDEX files if we are not symlinking them.
-  std::unique_ptr<File> input_vdex;
-  std::unique_ptr<File> output_vdex;
-  if (symlink_vdex) {
-    new_vdex_out = !OS::FileExists(output_vdex_filename.c_str());
-  } else {
-    if (input_vdex_fd != -1) {
-      input_vdex.reset(new File(input_vdex_fd, input_vdex_filename, true));
-      if (input_vdex == nullptr) {
-        // Unlikely, but ensure exhaustive logging in non-0 exit code case
-        LOG(ERROR) << "Failed to open input vdex file by its FD" << input_vdex_fd;
-      }
-    } else {
-      input_vdex.reset(OS::OpenFileForReading(input_vdex_filename.c_str()));
-      if (input_vdex == nullptr) {
-        PLOG(ERROR) << "Failed to open input vdex file " << input_vdex_filename;
-        return EXIT_FAILURE;
-      }
-    }
-    if (output_vdex_fd != -1) {
-      output_vdex.reset(new File(output_vdex_fd, output_vdex_filename, true));
-      if (output_vdex == nullptr) {
-        // Unlikely, but ensure exhaustive logging in non-0 exit code case
-        LOG(ERROR) << "Failed to open output vdex file by its FD" << output_vdex_fd;
-      }
-    } else {
-      output_vdex.reset(CreateOrOpen(output_vdex_filename.c_str(), &new_vdex_out));
-      if (output_vdex == nullptr) {
-        PLOG(ERROR) << "Failed to open output vdex file " << output_vdex_filename;
-        return EXIT_FAILURE;
-      }
-    }
-  }
-
-  // TODO: get rid of this.
-  auto cleanup = [&output_oat_filename, &output_vdex_filename, &new_oat_out, &new_vdex_out]
-                 (bool success) {
-    if (!success) {
-      if (new_oat_out) {
-        CHECK(!output_oat_filename.empty());
-        unlink(output_oat_filename.c_str());
-      }
-      if (new_vdex_out) {
-        CHECK(!output_vdex_filename.empty());
-        unlink(output_vdex_filename.c_str());
-      }
-    }
-
-    if (kIsDebugBuild) {
-      LOG(INFO) << "Cleaning up.. success? " << success;
-    }
-  };
-
-  if (output_oat.get() == nullptr) {
-    cleanup(false);
-    return EXIT_FAILURE;
-  }
-
-  if (match_delta) {
-    // Figure out what the current delta is so we can match it to the desired delta.
-    off_t current_delta = 0;
-    if (!ReadOatPatchDelta(elf.get(), &current_delta, &error_msg)) {
-      LOG(ERROR) << "Unable to get current delta: " << error_msg;
-      cleanup(false);
-      return EXIT_FAILURE;
-    }
-    // Before this line base_delta is the desired final delta. We need it to be the actual amount to
-    // change everything by. We subtract the current delta from it to make it this.
-    base_delta -= current_delta;
-    if (!IsAligned<kPageSize>(base_delta)) {
-      LOG(ERROR) << "Given image file was relocated by an illegal delta";
-      cleanup(false);
-      return false;
-    }
-  }
-
-  if (debug) {
-    LOG(INFO) << "moving offset by " << base_delta
-        << " (0x" << std::hex << base_delta << ") bytes or "
-        << std::dec << (base_delta/kPageSize) << " pages.";
-  }
-
-  ScopedFlock output_oat_lock;
-  if (lock_output) {
-    if (!output_oat_lock.Init(output_oat.get(), &error_msg)) {
-      LOG(ERROR) << "Unable to lock output oat " << output_oat->GetPath() << ": " << error_msg;
-      cleanup(false);
-      return EXIT_FAILURE;
-    }
-  }
-
-  TimingLogger::ScopedTiming pt("patch oat", &timings);
-  bool ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings,
-                             output_oat_fd >= 0,  // was it opened from FD?
-                             new_oat_out);
-  ret = FinishFile(output_oat.get(), ret);
-
-  if (ret) {
-    if (symlink_vdex) {
-      ret = SymlinkFile(input_vdex_filename, output_vdex_filename);
-    } else {
-      ret = unix_file::CopyFile(*input_vdex.get(), output_vdex.get());
-    }
-  }
-
-  if (kIsDebugBuild) {
-    LOG(INFO) << "Exiting with return ... " << ret;
-  }
-  cleanup(ret);
-  return ret ? EXIT_SUCCESS : EXIT_FAILURE;
-}
-
-static int ParseFd(const StringPiece& option, const char* cmdline_arg) {
-  int fd;
-  const char* fd_str = option.substr(strlen(cmdline_arg)).data();
-  if (!ParseInt(fd_str, &fd)) {
-    Usage("Failed to parse %d argument '%s' as an integer", cmdline_arg, fd_str);
-  }
-  if (fd < 0) {
-    Usage("%s pass a negative value %d", cmdline_arg, fd);
-  }
-  return fd;
-}
-
 static int patchoat(int argc, char **argv) {
   InitLogging(argv, Runtime::Aborter);
   MemMap::Init();
@@ -1384,23 +780,11 @@
   // cmd line args
   bool isa_set = false;
   InstructionSet isa = kNone;
-  std::string input_oat_filename;
-  std::string input_oat_location;
-  int input_oat_fd = -1;
-  int input_vdex_fd = -1;
-  bool have_input_oat = false;
   std::string input_image_location;
-  std::string output_oat_filename;
-  int output_oat_fd = -1;
-  int output_vdex_fd = -1;
-  bool have_output_oat = false;
   std::string output_image_filename;
   off_t base_delta = 0;
   bool base_delta_set = false;
-  std::string patched_image_filename;
-  std::string patched_image_location;
   bool dump_timings = kIsDebugBuild;
-  bool lock_output = true;
 
   for (int i = 0; i < argc; ++i) {
     const StringPiece option(argv[i]);
@@ -1415,42 +799,8 @@
       if (isa == kNone) {
         Usage("Unknown or invalid instruction set %s", isa_str);
       }
-    } else if (option.starts_with("--input-oat-location=")) {
-      if (have_input_oat) {
-        Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used.");
-      }
-      have_input_oat = true;
-      input_oat_location = option.substr(strlen("--input-oat-location=")).data();
-    } else if (option.starts_with("--input-oat-file=")) {
-      if (have_input_oat) {
-        Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used.");
-      }
-      have_input_oat = true;
-      input_oat_filename = option.substr(strlen("--input-oat-file=")).data();
-    } else if (option.starts_with("--input-oat-fd=")) {
-      if (have_input_oat) {
-        Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used.");
-      }
-      have_input_oat = true;
-      input_oat_fd = ParseFd(option, "--input-oat-fd=");
-    } else if (option.starts_with("--input-vdex-fd=")) {
-      input_vdex_fd = ParseFd(option, "--input-vdex-fd=");
     } else if (option.starts_with("--input-image-location=")) {
       input_image_location = option.substr(strlen("--input-image-location=")).data();
-    } else if (option.starts_with("--output-oat-file=")) {
-      if (have_output_oat) {
-        Usage("Only one of --output-oat-file, and --output-oat-fd may be used.");
-      }
-      have_output_oat = true;
-      output_oat_filename = option.substr(strlen("--output-oat-file=")).data();
-    } else if (option.starts_with("--output-oat-fd=")) {
-      if (have_output_oat) {
-        Usage("Only one of --output-oat-file, --output-oat-fd may be used.");
-      }
-      have_output_oat = true;
-      output_oat_fd = ParseFd(option, "--output-oat-fd=");
-    } else if (option.starts_with("--output-vdex-fd=")) {
-      output_vdex_fd = ParseFd(option, "--output-vdex-fd=");
     } else if (option.starts_with("--output-image-file=")) {
       output_image_filename = option.substr(strlen("--output-image-file=")).data();
     } else if (option.starts_with("--base-offset-delta=")) {
@@ -1459,12 +809,6 @@
       if (!ParseInt(base_delta_str, &base_delta)) {
         Usage("Failed to parse --base-offset-delta argument '%s' as an off_t", base_delta_str);
       }
-    } else if (option.starts_with("--patched-image-location=")) {
-      patched_image_location = option.substr(strlen("--patched-image-location=")).data();
-    } else if (option == "--lock-output") {
-      lock_output = true;
-    } else if (option == "--no-lock-output") {
-      lock_output = false;
     } else if (option == "--dump-timings") {
       dump_timings = true;
     } else if (option == "--no-dump-timings") {
@@ -1479,33 +823,13 @@
     Usage("Instruction set must be set.");
   }
 
-  int ret;
-  if (!input_image_location.empty()) {
-    ret = patchoat_image(timings,
-                         isa,
-                         input_image_location,
-                         output_image_filename,
-                         base_delta,
-                         base_delta_set,
-                         debug);
-  } else {
-    ret = patchoat_oat(timings,
-                       isa,
-                       patched_image_location,
-                       base_delta,
-                       base_delta_set,
-                       input_oat_fd,
-                       input_vdex_fd,
-                       input_oat_location,
-                       input_oat_filename,
-                       have_input_oat,
-                       output_oat_fd,
-                       output_vdex_fd,
-                       output_oat_filename,
-                       have_output_oat,
-                       lock_output,
-                       debug);
-  }
+  int ret = patchoat_image(timings,
+                           isa,
+                           input_image_location,
+                           output_image_filename,
+                           base_delta,
+                           base_delta_set,
+                           debug);
 
   timings.EndTiming();
   if (dump_timings) {
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index a519631..e15a6bc 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -44,17 +44,7 @@
 
 class PatchOat {
  public:
-  // Patch only the oat file
-  static bool Patch(File* oat_in, off_t delta, File* oat_out, TimingLogger* timings,
-                    bool output_oat_opened_from_fd,  // Was this using --oatput-oat-fd ?
-                    bool new_oat_out);               // Output oat was a new file created by us?
-
-  // Patch only the image (art file)
-  static bool Patch(const std::string& art_location, off_t delta, File* art_out, InstructionSet isa,
-                    TimingLogger* timings);
-
-  // Patch both the image and the oat file
-  static bool Patch(const std::string& art_location,
+  static bool Patch(const std::string& image_location,
                     off_t delta,
                     const std::string& output_directory,
                     InstructionSet isa,
@@ -64,18 +54,11 @@
   PatchOat(PatchOat&&) = default;
 
  private:
-  // Takes ownership only of the ElfFile. All other pointers are only borrowed.
-  PatchOat(ElfFile* oat_file, off_t delta, TimingLogger* timings)
-      : oat_file_(oat_file), image_(nullptr), bitmap_(nullptr), heap_(nullptr), delta_(delta),
-        isa_(kNone), space_map_(nullptr), timings_(timings) {}
-  PatchOat(InstructionSet isa, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap,
-           MemMap* heap, off_t delta, TimingLogger* timings)
-      : image_(image), bitmap_(bitmap), heap_(heap),
-        delta_(delta), isa_(isa), space_map_(nullptr), timings_(timings) {}
-  PatchOat(InstructionSet isa, ElfFile* oat_file, MemMap* image,
+  // All pointers are only borrowed.
+  PatchOat(InstructionSet isa, MemMap* image,
            gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta,
            std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>>* map, TimingLogger* timings)
-      : oat_file_(oat_file), image_(image), bitmap_(bitmap), heap_(heap),
+      : image_(image), bitmap_(bitmap), heap_(heap),
         delta_(delta), isa_(isa), space_map_(map), timings_(timings) {}
 
   // Was the .art image at image_path made with --compile-pic ?
@@ -94,9 +77,7 @@
   // Attempt to replace the file with a symlink
   // Returns false if it fails
   static bool ReplaceOatFileWithSymlink(const std::string& input_oat_filename,
-                                        const std::string& output_oat_filename,
-                                        bool output_oat_opened_from_fd,
-                                        bool new_oat_out);  // Output oat was newly created?
+                                        const std::string& output_oat_filename);
 
   static void BitmapCallback(mirror::Object* obj, void* arg)
       REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -108,13 +89,6 @@
   void FixupMethod(ArtMethod* object, ArtMethod* copy)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Patches oat in place, modifying the oat_file given to the constructor.
-  bool PatchElf();
-  template <typename ElfFileImpl>
-  bool PatchElf(ElfFileImpl* oat_file);
-  template <typename ElfFileImpl>
-  bool PatchOatHeader(ElfFileImpl* oat_file);
-
   bool PatchImage(bool primary_image) REQUIRES_SHARED(Locks::mutator_lock_);
   void PatchArtFields(const ImageHeader* image_header) REQUIRES_SHARED(Locks::mutator_lock_);
   void PatchArtMethods(const ImageHeader* image_header) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -128,7 +102,6 @@
   void PatchDexFileArrays(mirror::ObjectArray<mirror::Object>* img_roots)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  bool WriteElf(File* out);
   bool WriteImage(File* out);
 
   template <typename T>
@@ -175,19 +148,6 @@
     return reinterpret_cast<T*>(ret);
   }
 
-  template <typename T>
-  T RelocatedAddressOfIntPointer(T obj) const {
-    if (obj == 0) {
-      return obj;
-    }
-    T ret = obj + delta_;
-    // Trim off high bits in case negative relocation with 64 bit patchoat.
-    if (Is32BitISA()) {
-      ret = static_cast<T>(static_cast<uint32_t>(ret));
-    }
-    return ret;
-  }
-
   bool Is32BitISA() const {
     return InstructionSetPointerSize(isa_) == PointerSize::k32;
   }
@@ -213,8 +173,6 @@
     mirror::Object* const copy_;
   };
 
-  // The elf file we are patching.
-  std::unique_ptr<ElfFile> oat_file_;
   // A mmap of the image we are patching. This is modified.
   const MemMap* const image_;
   // The bitmap over the image within the heap we are patching. This is not modified.
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 96976d9..9c20740 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -121,7 +121,7 @@
 inline uint32_t ArtMethod::GetDexMethodIndex() {
   DCHECK(IsRuntimeMethod() || GetDeclaringClass()->IsIdxLoaded() ||
          GetDeclaringClass()->IsErroneous());
-  return dex_method_index_;
+  return GetDexMethodIndexUnchecked();
 }
 
 inline ArtMethod** ArtMethod::GetDexCacheResolvedMethods(PointerSize pointer_size) {
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 11dcc35..2c31f6c 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -322,6 +322,9 @@
   // Number of 32bit registers that would be required to hold all the arguments
   static size_t NumArgRegisters(const StringPiece& shorty);
 
+  ALWAYS_INLINE uint32_t GetDexMethodIndexUnchecked() {
+    return dex_method_index_;
+  }
   ALWAYS_INLINE uint32_t GetDexMethodIndex() REQUIRES_SHARED(Locks::mutator_lock_);
 
   void SetDexMethodIndex(uint32_t new_idx) {
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index e4972da..4b15a22 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -90,7 +90,7 @@
             art::Thread::SelfOffset<POINTER_SIZE>().Int32Value())
 
 // Offset of field Thread::tlsPtr_.thread_local_pos.
-#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 198 * __SIZEOF_POINTER__)
+#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 34 * __SIZEOF_POINTER__)
 ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET,
             art::Thread::ThreadLocalPosOffset<POINTER_SIZE>().Int32Value())
 // Offset of field Thread::tlsPtr_.thread_local_end.
@@ -98,11 +98,13 @@
 ADD_TEST_EQ(THREAD_LOCAL_END_OFFSET,
             art::Thread::ThreadLocalEndOffset<POINTER_SIZE>().Int32Value())
 // Offset of field Thread::tlsPtr_.thread_local_objects.
-#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_END_OFFSET + 2 * __SIZEOF_POINTER__)
+#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_END_OFFSET + __SIZEOF_POINTER__)
 ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET,
             art::Thread::ThreadLocalObjectsOffset<POINTER_SIZE>().Int32Value())
+
 // Offset of field Thread::tlsPtr_.mterp_current_ibase.
-#define THREAD_CURRENT_IBASE_OFFSET (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__)
+#define THREAD_CURRENT_IBASE_OFFSET \
+    (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 164) * __SIZEOF_POINTER__)
 ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET,
             art::Thread::MterpCurrentIBaseOffset<POINTER_SIZE>().Int32Value())
 // Offset of field Thread::tlsPtr_.mterp_default_ibase.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 129c93f..448b460 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2711,10 +2711,6 @@
   CHECK(h_new_class.Get() != nullptr) << descriptor;
   CHECK(h_new_class->IsResolved()) << descriptor;
 
-  // Update the dex cache of where the class is defined. Inlining depends on having
-  // this filled.
-  h_new_class->GetDexCache()->SetResolvedType(h_new_class->GetDexTypeIndex(), h_new_class.Get());
-
   // Instrumentation may have updated entrypoints for all methods of all
   // classes. However it could not update methods of this class while we
   // were loading it. Now the class is resolved, we can update entrypoints
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 42108d8..bbe7280 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -460,7 +460,6 @@
  protected:
   virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
     CommonRuntimeTest::SetUpRuntimeOptions(options);
-    options->push_back(std::make_pair("-Xexperimental:method-handles", nullptr));
   }
 };
 
@@ -757,6 +756,7 @@
 struct EmulatedStackFrameOffsets : public CheckOffsets<mirror::EmulatedStackFrame> {
   EmulatedStackFrameOffsets() : CheckOffsets<mirror::EmulatedStackFrame>(
       false, "Ldalvik/system/EmulatedStackFrame;") {
+    addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, callsite_type_), "callsiteType");
     addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, references_), "references");
     addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, stack_frame_), "stackFrame");
     addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, type_), "type");
@@ -899,7 +899,6 @@
   dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_;
   ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache();
   const DexFile& dex_file = klass->GetDexFile();
-  EXPECT_OBJ_PTR_EQ(dex_cache->GetResolvedType(type_idx), klass);
   EXPECT_OBJ_PTR_EQ(
       class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()),
       klass);
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index b0c4597..8b1b5a3 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -160,7 +160,7 @@
   // image at GetImageLocation(). This is used for testing mismatched
   // image checksums in the oat_file_assistant_tests.
   std::string GetImageLocation2() const {
-    return GetImageDirectory() + "/core-npic.art";
+    return GetImageDirectory() + "/core-multi.art";
   }
 
   std::string GetDexSrc1() const {
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 5ff95b4..7d6f866 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -52,21 +52,19 @@
   // suspended while executing it.
   ScopedAssertNoThreadSuspension sants(__FUNCTION__);
 
+  if (inline_info.EncodesArtMethodAtDepth(encoding, inlining_depth)) {
+    return inline_info.GetArtMethodAtDepth(encoding, inlining_depth);
+  }
+
   uint32_t method_index = inline_info.GetMethodIndexAtDepth(encoding, inlining_depth);
-  InvokeType invoke_type = static_cast<InvokeType>(
-        inline_info.GetInvokeTypeAtDepth(encoding, inlining_depth));
-  ArtMethod* inlined_method = outer_method->GetDexCacheResolvedMethod(method_index,
-                                                                      kRuntimePointerSize);
-  if (!inlined_method->IsRuntimeMethod()) {
+  if (inline_info.GetDexPcAtDepth(encoding, inlining_depth) == static_cast<uint32_t>(-1)) {
+    // "charAt" special case. It is the only non-leaf method we inline across dex files.
+    ArtMethod* inlined_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt);
+    DCHECK_EQ(inlined_method->GetDexMethodIndex(), method_index);
     return inlined_method;
   }
 
-  // The method in the dex cache is the runtime method responsible for invoking
-  // the stub that will then update the dex cache. Therefore, we need to do the
-  // resolution ourselves.
-
-  // We first find the dex cache of our caller. If it is the outer method, we can directly
-  // use its dex cache. Otherwise, we also need to resolve our caller.
+  // Find which method did the call in the inlining hierarchy.
   ArtMethod* caller = outer_method;
   if (inlining_depth != 0) {
     caller = GetResolvedMethod(outer_method,
@@ -74,51 +72,38 @@
                                encoding,
                                inlining_depth - 1);
   }
-  DCHECK_EQ(caller->GetDexCache(), outer_method->GetDexCache())
-      << "Compiler only supports inlining calls within the same dex cache";
-  const DexFile* dex_file = outer_method->GetDexFile();
-  const DexFile::MethodId& method_id = dex_file->GetMethodId(method_index);
 
-  if (inline_info.GetDexPcAtDepth(encoding, inlining_depth) == static_cast<uint32_t>(-1)) {
-    // "charAt" special case. It is the only non-leaf method we inline across dex files.
-    if (kIsDebugBuild) {
-      const char* name = dex_file->StringDataByIdx(method_id.name_idx_);
-      DCHECK_EQ(std::string(name), "charAt");
-      DCHECK_EQ(std::string(dex_file->GetMethodShorty(method_id)), "CI")
-          << std::string(dex_file->GetMethodShorty(method_id));
-      DCHECK_EQ(std::string(dex_file->StringByTypeIdx(method_id.class_idx_)), "Ljava/lang/String;")
-          << std::string(dex_file->StringByTypeIdx(method_id.class_idx_));
-    }
-    mirror::Class* cls =
-        Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kJavaLangString);
-    // Update the dex cache for future lookups.
-    caller->GetDexCache()->SetResolvedType(method_id.class_idx_, cls);
-    inlined_method = cls->FindVirtualMethod("charAt", "(I)C", kRuntimePointerSize);
-  } else {
-    mirror::Class* klass = caller->GetDexCache()->GetResolvedType(method_id.class_idx_);
-    DCHECK_EQ(klass->GetDexCache(), caller->GetDexCache())
-        << "Compiler only supports inlining calls within the same dex cache";
-    switch (invoke_type) {
-      case kDirect:
-      case kStatic:
-        inlined_method =
-            klass->FindDirectMethod(klass->GetDexCache(), method_index, kRuntimePointerSize);
-        break;
-      case kSuper:
-      case kVirtual:
-        inlined_method =
-            klass->FindVirtualMethod(klass->GetDexCache(), method_index, kRuntimePointerSize);
-        break;
-      default:
-        LOG(FATAL) << "Unimplemented inlined invocation type: " << invoke_type;
-        UNREACHABLE();
+  // Lookup the declaring class of the inlined method.
+  const DexFile* dex_file = caller->GetDexFile();
+  const DexFile::MethodId& method_id = dex_file->GetMethodId(method_index);
+  const char* descriptor = dex_file->StringByTypeIdx(method_id.class_idx_);
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Thread* self = Thread::Current();
+  mirror::ClassLoader* class_loader = caller->GetDeclaringClass()->GetClassLoader();
+  mirror::Class* klass = class_linker->LookupClass(self, descriptor, class_loader);
+  if (klass == nullptr) {
+      LOG(FATAL) << "Could not find an inlined method from an .oat file: "
+                 << "the class " << descriptor << " was not found in the class loader of "
+                 << caller->PrettyMethod() << ". "
+                 << "This must be due to playing wrongly with class loaders";
+  }
+
+  // Lookup the method.
+  const char* method_name = dex_file->GetMethodName(method_id);
+  const Signature signature = dex_file->GetMethodSignature(method_id);
+
+  ArtMethod* inlined_method =
+      klass->FindDeclaredDirectMethod(method_name, signature, kRuntimePointerSize);
+  if (inlined_method == nullptr) {
+    inlined_method = klass->FindDeclaredVirtualMethod(method_name, signature, kRuntimePointerSize);
+    if (inlined_method == nullptr) {
+      LOG(FATAL) << "Could not find an inlined method from an .oat file: "
+                 << "the class " << descriptor << " does not have "
+                 << method_name << signature << " declared. "
+                 << "This must be due to duplicate classes or playing wrongly with class loaders";
     }
   }
 
-  // Update the dex cache for future lookups. Note that for static methods, this is safe
-  // when the class is being initialized, as the entrypoint for the ArtMethod is at
-  // this point still the resolution trampoline.
-  outer_method->SetDexCacheResolvedMethod(method_index, inlined_method, kRuntimePointerSize);
   return inlined_method;
 }
 
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 96e17da..6301f93 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -117,15 +117,14 @@
                         sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, checkpoint_function, active_suspend_barriers,
                         sizeof(void*));
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, active_suspend_barriers, jni_entrypoints,
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, active_suspend_barriers, thread_local_start,
                         sizeof(Thread::tls_ptr_sized_values::active_suspend_barriers));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_pos, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_pos, thread_local_end, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_objects, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, jni_entrypoints, sizeof(size_t));
 
     // Skip across the entrypoints structures.
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_pos, thread_local_end, sizeof(void*));
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_start, sizeof(void*));
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_objects, sizeof(void*));
-
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, mterp_current_ibase, sizeof(size_t));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_current_ibase, mterp_default_ibase, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_default_ibase, mterp_alt_ibase, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_alt_ibase, rosalloc_runs, sizeof(void*));
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index 916f1cf..8f978e1 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -35,7 +35,6 @@
  protected:
   virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
     CommonRuntimeTest::SetUpRuntimeOptions(options);
-    options->push_back(std::make_pair("-Xexperimental:method-handles", nullptr));
   }
 };
 
diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc
index d607040..978cc32 100644
--- a/runtime/mirror/emulated_stack_frame.cc
+++ b/runtime/mirror/emulated_stack_frame.cc
@@ -195,6 +195,7 @@
   // Step 5: Construct the EmulatedStackFrame object.
   Handle<EmulatedStackFrame> sf(hs.NewHandle(
       ObjPtr<EmulatedStackFrame>::DownCast(StaticClass()->AllocObject(self))));
+  sf->SetFieldObject<false>(CallsiteTypeOffset(), caller_type.Get());
   sf->SetFieldObject<false>(TypeOffset(), callee_type.Get());
   sf->SetFieldObject<false>(ReferencesOffset(), references.Get());
   sf->SetFieldObject<false>(StackFrameOffset(), stack_frame.Get());
diff --git a/runtime/mirror/emulated_stack_frame.h b/runtime/mirror/emulated_stack_frame.h
index d83a536..ddd84a1 100644
--- a/runtime/mirror/emulated_stack_frame.h
+++ b/runtime/mirror/emulated_stack_frame.h
@@ -81,6 +81,10 @@
         OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, stack_frame_));
   }
 
+  static MemberOffset CallsiteTypeOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(EmulatedStackFrame, callsite_type_));
+  }
+
   static MemberOffset TypeOffset() {
     return MemberOffset(OFFSETOF_MEMBER(EmulatedStackFrame, type_));
   }
@@ -93,6 +97,7 @@
     return MemberOffset(OFFSETOF_MEMBER(EmulatedStackFrame, stack_frame_));
   }
 
+  HeapReference<mirror::MethodType> callsite_type_;
   HeapReference<mirror::ObjectArray<mirror::Object>> references_;
   HeapReference<mirror::ByteArray> stack_frame_;
   HeapReference<mirror::MethodType> type_;
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 3341f53..5438a6d 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -428,6 +428,10 @@
   }
   auto ret = hs.NewHandle(mirror::ObjectArray<mirror::Method>::Alloc(
       soa.Self(), mirror::Method::ArrayClass(), num_methods));
+  if (ret.Get() == nullptr) {
+    soa.Self()->AssertPendingOOMException();
+    return nullptr;
+  }
   num_methods = 0;
   for (auto& m : klass->GetDeclaredMethods(kRuntimePointerSize)) {
     auto modifiers = m.GetAccessFlags();
diff --git a/runtime/oat.h b/runtime/oat.h
index f2d457c..ab03252 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  static constexpr uint8_t kOatVersion[] = { '0', '9', '7', '\0' };  // HLoadClass/kBssEntry change
+  static constexpr uint8_t kOatVersion[] = { '0', '9', '8', '\0' };  // art::Thread fields reorder
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 53ff8ea..3825c41 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -30,6 +30,7 @@
  */
 
 #include <string>
+#include <type_traits>
 #include <vector>
 
 #include <jni.h>
@@ -37,6 +38,7 @@
 #include "openjdkjvmti/jvmti.h"
 
 #include "art_jvmti.h"
+#include "base/logging.h"
 #include "base/mutex.h"
 #include "events-inl.h"
 #include "jni_env_ext-inl.h"
@@ -1168,7 +1170,57 @@
   }
 
   static jvmtiError SetVerboseFlag(jvmtiEnv* env, jvmtiVerboseFlag flag, jboolean value) {
-    return ERR(NOT_IMPLEMENTED);
+    if (flag == jvmtiVerboseFlag::JVMTI_VERBOSE_OTHER) {
+      // OTHER is special, as it's 0, so can't do a bit check.
+      bool val = (value == JNI_TRUE) ? true : false;
+
+      art::gLogVerbosity.collector = val;
+      art::gLogVerbosity.compiler = val;
+      art::gLogVerbosity.deopt = val;
+      art::gLogVerbosity.heap = val;
+      art::gLogVerbosity.jdwp = val;
+      art::gLogVerbosity.jit = val;
+      art::gLogVerbosity.monitor = val;
+      art::gLogVerbosity.oat = val;
+      art::gLogVerbosity.profiler = val;
+      art::gLogVerbosity.signals = val;
+      art::gLogVerbosity.simulator = val;
+      art::gLogVerbosity.startup = val;
+      art::gLogVerbosity.third_party_jni = val;
+      art::gLogVerbosity.threads = val;
+      art::gLogVerbosity.verifier = val;
+      art::gLogVerbosity.image = val;
+
+      // Note: can't switch systrace_lock_logging. That requires changing entrypoints.
+
+      art::gLogVerbosity.agents = val;
+    } else {
+      // Spec isn't clear whether "flag" is a mask or supposed to be single. We implement the mask
+      // semantics.
+      constexpr std::underlying_type<jvmtiVerboseFlag>::type kMask =
+          jvmtiVerboseFlag::JVMTI_VERBOSE_GC |
+          jvmtiVerboseFlag::JVMTI_VERBOSE_CLASS |
+          jvmtiVerboseFlag::JVMTI_VERBOSE_JNI;
+      if ((flag & ~kMask) != 0) {
+        return ERR(ILLEGAL_ARGUMENT);
+      }
+
+      bool val = (value == JNI_TRUE) ? true : false;
+
+      if ((flag & jvmtiVerboseFlag::JVMTI_VERBOSE_GC) != 0) {
+        art::gLogVerbosity.gc = val;
+      }
+
+      if ((flag & jvmtiVerboseFlag::JVMTI_VERBOSE_CLASS) != 0) {
+        art::gLogVerbosity.class_linker = val;
+      }
+
+      if ((flag & jvmtiVerboseFlag::JVMTI_VERBOSE_JNI) != 0) {
+        art::gLogVerbosity.jni = val;
+      }
+    }
+
+    return ERR(NONE);
   }
 
   static jvmtiError GetJLocationFormat(jvmtiEnv* env, jvmtiJlocationFormat* format_ptr) {
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index e1022b0..a72159b 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -763,8 +763,6 @@
                        "(Enable new and experimental agent support)\n");
   UsageMessage(stream, "  -Xexperimental:agents"
                        "(Enable new and experimental agent support)\n");
-  UsageMessage(stream, "  -Xexperimental:method-handles"
-                       "(Enable new and experimental method handles support)\n");
   UsageMessage(stream, "\n");
 
   UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n");
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 8fc211c..a87e1c1 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -304,7 +304,7 @@
   }
 
   bool IsMethodHandlesEnabled() const {
-    return experimental_flags_ & ExperimentalFlags::kMethodHandles;
+    return true;
   }
 
   void DisallowNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index d1970fe..ecabf9a 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -117,7 +117,7 @@
 RUNTIME_OPTIONS_KEY (Unit,                NoDexFileFallback)
 RUNTIME_OPTIONS_KEY (std::string,         CpuAbiList)
 RUNTIME_OPTIONS_KEY (std::string,         Fingerprint)
-RUNTIME_OPTIONS_KEY (ExperimentalFlags,   Experimental,     ExperimentalFlags::kNone) // -Xexperimental:{none, agents, method-handles}
+RUNTIME_OPTIONS_KEY (ExperimentalFlags,   Experimental,     ExperimentalFlags::kNone) // -Xexperimental:{none, agents}
 RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>,         AgentLib)  // -agentlib:<libname>=<options>, Requires -Xexperimental:agents
 RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>,         AgentPath)  // -agentpath:<libname>=<options>, Requires -Xexperimental:agents
 RUNTIME_OPTIONS_KEY (std::vector<Plugin>,            Plugins)  // -Xplugin:<library> Requires -Xexperimental:runtime-plugins
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index a7e7c21..9ebf9a7 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -18,8 +18,9 @@
 
 #include <stdint.h>
 
+#include "art_method.h"
 #include "indenter.h"
-#include "invoke_type.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
@@ -106,7 +107,7 @@
       << "InlineInfoEncoding"
       << " (method_index_bit_offset=" << static_cast<uint32_t>(kMethodIndexBitOffset)
       << ", dex_pc_bit_offset=" << static_cast<uint32_t>(dex_pc_bit_offset_)
-      << ", invoke_type_bit_offset=" << static_cast<uint32_t>(invoke_type_bit_offset_)
+      << ", extra_data_bit_offset=" << static_cast<uint32_t>(extra_data_bit_offset_)
       << ", dex_register_map_bit_offset=" << static_cast<uint32_t>(dex_register_map_bit_offset_)
       << ", total_bit_size=" << static_cast<uint32_t>(total_bit_size_)
       << ")\n";
@@ -230,12 +231,16 @@
     vios->Stream()
         << " At depth " << i
         << std::hex
-        << " (dex_pc=0x" << GetDexPcAtDepth(inline_info_encoding, i)
-        << std::dec
-        << ", method_index=" << GetMethodIndexAtDepth(inline_info_encoding, i)
-        << ", invoke_type=" << static_cast<InvokeType>(GetInvokeTypeAtDepth(inline_info_encoding,
-                                                                            i))
-        << ")\n";
+        << " (dex_pc=0x" << GetDexPcAtDepth(inline_info_encoding, i);
+    if (EncodesArtMethodAtDepth(inline_info_encoding, i)) {
+      ScopedObjectAccess soa(Thread::Current());
+      vios->Stream() << ", method=" << GetArtMethodAtDepth(inline_info_encoding, i)->PrettyMethod();
+    } else {
+      vios->Stream()
+          << std::dec
+          << ", method_index=" << GetMethodIndexAtDepth(inline_info_encoding, i);
+    }
+    vios->Stream() << ")\n";
     if (HasDexRegisterMapAtDepth(inline_info_encoding, i) && (number_of_dex_registers != nullptr)) {
       CodeInfoEncoding encoding = code_info.ExtractEncoding();
       DexRegisterMap dex_register_map =
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 5e556be..15d7816 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -35,6 +35,7 @@
 // Size of Dex virtual registers.
 static constexpr size_t kVRegSize = 4;
 
+class ArtMethod;
 class CodeInfo;
 class StackMapEncoding;
 struct CodeInfoEncoding;
@@ -887,7 +888,7 @@
  public:
   void SetFromSizes(size_t method_index_max,
                     size_t dex_pc_max,
-                    size_t invoke_type_max,
+                    size_t extra_data_max,
                     size_t dex_register_map_size) {
     total_bit_size_ = kMethodIndexBitOffset;
     total_bit_size_ += MinimumBitsToStore(method_index_max);
@@ -899,8 +900,8 @@
       total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max);
     }
 
-    invoke_type_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_);
-    total_bit_size_ += MinimumBitsToStore(invoke_type_max);
+    extra_data_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_);
+    total_bit_size_ += MinimumBitsToStore(extra_data_max);
 
     // We also need +1 for kNoDexRegisterMap, but since the size is strictly
     // greater than any offset we might try to encode, we already implicitly have it.
@@ -912,10 +913,10 @@
     return FieldEncoding(kMethodIndexBitOffset, dex_pc_bit_offset_);
   }
   ALWAYS_INLINE FieldEncoding GetDexPcEncoding() const {
-    return FieldEncoding(dex_pc_bit_offset_, invoke_type_bit_offset_, -1 /* min_value */);
+    return FieldEncoding(dex_pc_bit_offset_, extra_data_bit_offset_, -1 /* min_value */);
   }
-  ALWAYS_INLINE FieldEncoding GetInvokeTypeEncoding() const {
-    return FieldEncoding(invoke_type_bit_offset_, dex_register_map_bit_offset_);
+  ALWAYS_INLINE FieldEncoding GetExtraDataEncoding() const {
+    return FieldEncoding(extra_data_bit_offset_, dex_register_map_bit_offset_);
   }
   ALWAYS_INLINE FieldEncoding GetDexRegisterMapEncoding() const {
     return FieldEncoding(dex_register_map_bit_offset_, total_bit_size_, -1 /* min_value */);
@@ -930,7 +931,7 @@
   static constexpr uint8_t kIsLastBitOffset = 0;
   static constexpr uint8_t kMethodIndexBitOffset = 1;
   uint8_t dex_pc_bit_offset_;
-  uint8_t invoke_type_bit_offset_;
+  uint8_t extra_data_bit_offset_;
   uint8_t dex_register_map_bit_offset_;
   uint8_t total_bit_size_;
 };
@@ -938,7 +939,11 @@
 /**
  * Inline information for a specific PC. The information is of the form:
  *
- *   [is_last, method_index, dex_pc, invoke_type, dex_register_map_offset]+.
+ *   [is_last,
+ *    method_index (or ArtMethod high bits),
+ *    dex_pc,
+ *    extra_data (ArtMethod low bits or 1),
+ *    dex_register_map_offset]+.
  */
 class InlineInfo {
  public:
@@ -960,6 +965,7 @@
 
   ALWAYS_INLINE uint32_t GetMethodIndexAtDepth(const InlineInfoEncoding& encoding,
                                                uint32_t depth) const {
+    DCHECK(!EncodesArtMethodAtDepth(encoding, depth));
     return encoding.GetMethodIndexEncoding().Load(GetRegionAtDepth(encoding, depth));
   }
 
@@ -980,15 +986,28 @@
     encoding.GetDexPcEncoding().Store(GetRegionAtDepth(encoding, depth), dex_pc);
   }
 
-  ALWAYS_INLINE uint32_t GetInvokeTypeAtDepth(const InlineInfoEncoding& encoding,
-                                              uint32_t depth) const {
-    return encoding.GetInvokeTypeEncoding().Load(GetRegionAtDepth(encoding, depth));
+  ALWAYS_INLINE bool EncodesArtMethodAtDepth(const InlineInfoEncoding& encoding,
+                                             uint32_t depth) const {
+    return (encoding.GetExtraDataEncoding().Load(GetRegionAtDepth(encoding, depth)) & 1) == 0;
   }
 
-  ALWAYS_INLINE void SetInvokeTypeAtDepth(const InlineInfoEncoding& encoding,
-                                          uint32_t depth,
-                                          uint32_t invoke_type) {
-    encoding.GetInvokeTypeEncoding().Store(GetRegionAtDepth(encoding, depth), invoke_type);
+  ALWAYS_INLINE void SetExtraDataAtDepth(const InlineInfoEncoding& encoding,
+                                         uint32_t depth,
+                                         uint32_t extra_data) {
+    encoding.GetExtraDataEncoding().Store(GetRegionAtDepth(encoding, depth), extra_data);
+  }
+
+  ALWAYS_INLINE ArtMethod* GetArtMethodAtDepth(const InlineInfoEncoding& encoding,
+                                               uint32_t depth) const {
+    uint32_t low_bits = encoding.GetExtraDataEncoding().Load(GetRegionAtDepth(encoding, depth));
+    uint32_t high_bits = encoding.GetMethodIndexEncoding().Load(GetRegionAtDepth(encoding, depth));
+    if (high_bits == 0) {
+      return reinterpret_cast<ArtMethod*>(low_bits);
+    } else {
+      uint64_t address = high_bits;
+      address = address << 32;
+      return reinterpret_cast<ArtMethod*>(address | low_bits);
+    }
   }
 
   ALWAYS_INLINE uint32_t GetDexRegisterMapOffsetAtDepth(const InlineInfoEncoding& encoding,
diff --git a/runtime/thread.h b/runtime/thread.h
index 3958c10..2b451bc 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -1426,7 +1426,7 @@
       stacked_shadow_frame_record(nullptr), deoptimization_context_stack(nullptr),
       frame_id_to_shadow_frame(nullptr), name(nullptr), pthread_self(0),
       last_no_thread_suspension_cause(nullptr), checkpoint_function(nullptr),
-      thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_start(nullptr),
+      thread_local_start(nullptr), thread_local_pos(nullptr), thread_local_end(nullptr),
       thread_local_objects(0), mterp_current_ibase(nullptr), mterp_default_ibase(nullptr),
       mterp_alt_ibase(nullptr), thread_local_alloc_stack_top(nullptr),
       thread_local_alloc_stack_end(nullptr), nested_signal_state(nullptr),
@@ -1545,21 +1545,21 @@
     // to avoid additional cost of a mutex and a condition variable, as used in art::Barrier.
     AtomicInteger* active_suspend_barriers[kMaxSuspendBarriers];
 
-    // Entrypoint function pointers.
-    // TODO: move this to more of a global offset table model to avoid per-thread duplication.
-    JniEntryPoints jni_entrypoints;
-    QuickEntryPoints quick_entrypoints;
+    // Thread-local allocation pointer. Moved here to force alignment for thread_local_pos on ARM.
+    uint8_t* thread_local_start;
 
     // thread_local_pos and thread_local_end must be consecutive for ldrd and are 8 byte aligned for
     // potentially better performance.
     uint8_t* thread_local_pos;
     uint8_t* thread_local_end;
 
-    // Thread-local allocation pointer.
-    uint8_t* thread_local_start;
-
     size_t thread_local_objects;
 
+    // Entrypoint function pointers.
+    // TODO: move this to more of a global offset table model to avoid per-thread duplication.
+    JniEntryPoints jni_entrypoints;
+    QuickEntryPoints quick_entrypoints;
+
     // Mterp jump table bases.
     void* mterp_current_ibase;
     void* mterp_default_ibase;
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 3b114a9..bb9844a 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -61,7 +61,7 @@
 
    private:
     static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' };
-    static constexpr uint8_t kVdexVersion[] = { '0', '0', '1', '\0' };
+    static constexpr uint8_t kVdexVersion[] = { '0', '0', '2', '\0' };  // Handle verify-profile
 
     uint8_t magic_[4];
     uint8_t version_[4];
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index a5b275c..507ea16 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -103,6 +103,7 @@
 jmethodID WellKnownClasses::java_lang_reflect_Proxy_invoke;
 jmethodID WellKnownClasses::java_lang_Runtime_nativeLoad;
 jmethodID WellKnownClasses::java_lang_Short_valueOf;
+jmethodID WellKnownClasses::java_lang_String_charAt;
 jmethodID WellKnownClasses::java_lang_System_runFinalization = nullptr;
 jmethodID WellKnownClasses::java_lang_Thread_dispatchUncaughtException;
 jmethodID WellKnownClasses::java_lang_Thread_init;
@@ -337,6 +338,7 @@
   java_lang_ref_ReferenceQueue_add = CacheMethod(env, java_lang_ref_ReferenceQueue.get(), true, "add", "(Ljava/lang/ref/Reference;)V");
 
   java_lang_reflect_Parameter_init = CacheMethod(env, java_lang_reflect_Parameter, false, "<init>", "(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V");
+  java_lang_String_charAt = CacheMethod(env, java_lang_String, false, "charAt", "(I)C");
   java_lang_Thread_dispatchUncaughtException = CacheMethod(env, java_lang_Thread, false, "dispatchUncaughtException", "(Ljava/lang/Throwable;)V");
   java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
   java_lang_Thread_run = CacheMethod(env, java_lang_Thread, false, "run", "()V");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 371be61..b3ce3d1 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -113,6 +113,7 @@
   static jmethodID java_lang_reflect_Proxy_invoke;
   static jmethodID java_lang_Runtime_nativeLoad;
   static jmethodID java_lang_Short_valueOf;
+  static jmethodID java_lang_String_charAt;
   static jmethodID java_lang_System_runFinalization;
   static jmethodID java_lang_Thread_dispatchUncaughtException;
   static jmethodID java_lang_Thread_init;
diff --git a/test/080-oom-throw/expected.txt b/test/080-oom-throw/expected.txt
index 904393b..0967278 100644
--- a/test/080-oom-throw/expected.txt
+++ b/test/080-oom-throw/expected.txt
@@ -1,3 +1,4 @@
 Test reflection correctly threw
+Test reflection2 correctly threw
 NEW_ARRAY correctly threw OOME
 NEW_INSTANCE correctly threw OOME
diff --git a/test/080-oom-throw/src/Main.java b/test/080-oom-throw/src/Main.java
index 0ae92a9..a6c18b7 100644
--- a/test/080-oom-throw/src/Main.java
+++ b/test/080-oom-throw/src/Main.java
@@ -53,6 +53,30 @@
         }
     }
 
+    public static Object eatAllMemory() {
+        Object[] result = null;
+        int size = 1000000;
+        while (result == null && size != 0) {
+            try {
+                result = new Object[size];
+            } catch (OutOfMemoryError oome) {
+                size /= 2;
+            }
+        }
+        if (result != null) {
+            int index = 0;
+            while (index != result.length && size != 0) {
+                try {
+                    result[index] = new byte[size];
+                    ++index;
+                } catch (OutOfMemoryError oome) {
+                    size /= 2;
+                }
+            }
+        }
+        return result;
+    }
+
     static boolean triggerArrayOOM() {
         ArrayMemEater.blowup(new char[128 * 1024][]);
         return ArrayMemEater.sawOome;
@@ -74,6 +98,9 @@
         if (triggerReflectionOOM()) {
             System.out.println("Test reflection correctly threw");
         }
+        if (triggerReflectionOOM2()) {
+            System.out.println("Test reflection2 correctly threw");
+        }
 
         if (triggerArrayOOM()) {
             System.out.println("NEW_ARRAY correctly threw OOME");
@@ -125,4 +152,20 @@
         }
         return true;
     }
+
+    static boolean triggerReflectionOOM2() {
+        Object memory = eatAllMemory();
+        boolean result = false;
+        try {
+            Main.class.getDeclaredMethods();
+        } catch (OutOfMemoryError e) {
+            result = true;
+        }
+        if (!result) {
+            boolean memoryWasAllocated = (memory != null);
+            memory = null;
+            System.out.println("memoryWasAllocated = " + memoryWasAllocated);
+        }
+        return result;
+    }
 }
diff --git a/test/633-checker-rtp-getclass/expected.txt b/test/633-checker-rtp-getclass/expected.txt
new file mode 100644
index 0000000..a178d04
--- /dev/null
+++ b/test/633-checker-rtp-getclass/expected.txt
@@ -0,0 +1,3 @@
+2
+3
+6
diff --git a/test/633-checker-rtp-getclass/info.txt b/test/633-checker-rtp-getclass/info.txt
new file mode 100644
index 0000000..e98a0ac
--- /dev/null
+++ b/test/633-checker-rtp-getclass/info.txt
@@ -0,0 +1,3 @@
+Regression test for the RTP pass of the compiler, which
+used the wrong block when bounding a type after a obj.getClass()
+check.
diff --git a/test/633-checker-rtp-getclass/src/Main.java b/test/633-checker-rtp-getclass/src/Main.java
new file mode 100644
index 0000000..f29c139
--- /dev/null
+++ b/test/633-checker-rtp-getclass/src/Main.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    System.out.println($opt$noinline$foo(new Main()));
+    System.out.println($opt$noinline$foo(new SubMain()));
+    System.out.println($opt$noinline$foo(new SubSubMain()));
+  }
+
+
+  // Checker test to make sure the only inlined instruction is
+  // SubMain.bar.
+  /// CHECK-START: int Main.$opt$noinline$foo(Main) inliner (after)
+  /// CHECK-DAG:                InvokeVirtual method_name:Main.foo
+  /// CHECK-DAG: <<Const:i\d+>> IntConstant 3
+  /// CHECK:                    begin_block
+  /// CHECK:                    BoundType klass:SubMain
+  /// CHECK:                    Return [<<Const>>]
+  /// CHECK-NOT:                begin_block
+  /// CHECK:                    end_block
+  public static int $opt$noinline$foo(Main o) {
+    if (doThrow) { throw new Error(); }
+    // To exercise the bug on Jack, we need two getClass compares.
+    if (o.getClass() == Main.class || o.getClass() != SubMain.class) {
+      return o.foo();
+    } else {
+      // We used to wrongly bound the type of o to `Main` here and then realize that's
+      // impossible and mark this branch as dead.
+      return o.bar();
+    }
+  }
+
+  public int bar() {
+    return 1;
+  }
+
+  public int foo() {
+    return 2;
+  }
+
+  public static boolean doThrow = false;
+}
+
+class SubMain extends Main {
+  public int bar() {
+    return 3;
+  }
+
+  public int foo() {
+    return 4;
+  }
+}
+
+class SubSubMain extends SubMain {
+  public int bar() {
+    return 5;
+  }
+
+  public int foo() {
+    return 6;
+  }
+}
diff --git a/test/634-vdex-duplicate/expected.txt b/test/634-vdex-duplicate/expected.txt
new file mode 100644
index 0000000..557db03
--- /dev/null
+++ b/test/634-vdex-duplicate/expected.txt
@@ -0,0 +1 @@
+Hello World
diff --git a/test/634-vdex-duplicate/info.txt b/test/634-vdex-duplicate/info.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/634-vdex-duplicate/info.txt
diff --git a/test/953-invoke-polymorphic-compiler/run b/test/634-vdex-duplicate/run
old mode 100755
new mode 100644
similarity index 71%
rename from test/953-invoke-polymorphic-compiler/run
rename to test/634-vdex-duplicate/run
index a9f1822..1ccb841
--- a/test/953-invoke-polymorphic-compiler/run
+++ b/test/634-vdex-duplicate/run
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright 2016 The Android Open Source Project
+# 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
+#     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,
@@ -14,7 +14,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# make us exit on a failure
-set -e
-
-./default-run "$@" --experimental method-handles
+exec ${RUN} -Xcompiler-option --compiler-filter=verify-profile --vdex-filter speed --vdex "${@}"
diff --git a/test/634-vdex-duplicate/src/Main.java b/test/634-vdex-duplicate/src/Main.java
new file mode 100644
index 0000000..2283106
--- /dev/null
+++ b/test/634-vdex-duplicate/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    System.out.println("Hello World");
+  }
+}
diff --git a/test/634-vdex-duplicate/src/sun/misc/Unsafe.java b/test/634-vdex-duplicate/src/sun/misc/Unsafe.java
new file mode 100644
index 0000000..c32868c
--- /dev/null
+++ b/test/634-vdex-duplicate/src/sun/misc/Unsafe.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+package sun.misc;
+
+public class Unsafe {
+}
diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc
index 3a475c6..052fb9a 100644
--- a/test/901-hello-ti-agent/basics.cc
+++ b/test/901-hello-ti-agent/basics.cc
@@ -22,6 +22,9 @@
 #include "base/macros.h"
 #include "openjdkjvmti/jvmti.h"
 
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
 namespace art {
 namespace Test901HelloTi {
 
@@ -72,9 +75,22 @@
   CHECK_CALL_SUCCESS(env->DisposeEnvironment());
   CHECK_CALL_SUCCESS(env2->DisposeEnvironment());
 #undef CHECK_CALL_SUCCESS
+
+  if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+    printf("Unable to get jvmti env!\n");
+    return 1;
+  }
+  SetAllCapabilities(jvmti_env);
+
   return JNI_OK;
 }
 
+extern "C" JNIEXPORT void JNICALL Java_Main_setVerboseFlag(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jint iflag, jboolean val) {
+  jvmtiVerboseFlag flag = static_cast<jvmtiVerboseFlag>(iflag);
+  jvmtiError result = jvmti_env->SetVerboseFlag(flag, val);
+  JvmtiErrorToException(env, result);
+}
 
 }  // namespace Test901HelloTi
 }  // namespace art
diff --git a/test/901-hello-ti-agent/expected.txt b/test/901-hello-ti-agent/expected.txt
index 414eb3b..2aee99b 100644
--- a/test/901-hello-ti-agent/expected.txt
+++ b/test/901-hello-ti-agent/expected.txt
@@ -1,2 +1,8 @@
 Loaded Agent for test 901-hello-ti-agent
 Hello, world!
+0
+1
+2
+4
+8
+JVMTI_ERROR_ILLEGAL_ARGUMENT
diff --git a/test/901-hello-ti-agent/src/Main.java b/test/901-hello-ti-agent/src/Main.java
index 1ef6289..775e5c2 100644
--- a/test/901-hello-ti-agent/src/Main.java
+++ b/test/901-hello-ti-agent/src/Main.java
@@ -16,6 +16,26 @@
 
 public class Main {
   public static void main(String[] args) {
+    System.loadLibrary(args[1]);
+
     System.out.println("Hello, world!");
+
+    set(0);  // OTHER
+    set(1);  // GC
+    set(2);  // CLASS
+    set(4);  // JNI
+    set(8);  // Error.
   }
+
+  private static void set(int i) {
+    System.out.println(i);
+    try {
+      setVerboseFlag(i, true);
+      setVerboseFlag(i, false);
+    } catch (RuntimeException e) {
+      System.out.println(e.getMessage());
+    }
+  }
+
+  private static native void setVerboseFlag(int flag, boolean value);
 }
diff --git a/test/954-invoke-polymorphic-verifier/run b/test/954-invoke-polymorphic-verifier/run
deleted file mode 100755
index a9f1822..0000000
--- a/test/954-invoke-polymorphic-verifier/run
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# make us exit on a failure
-set -e
-
-./default-run "$@" --experimental method-handles
diff --git a/test/955-methodhandles-smali/run b/test/955-methodhandles-smali/run
deleted file mode 100755
index a9f1822..0000000
--- a/test/955-methodhandles-smali/run
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# make us exit on a failure
-set -e
-
-./default-run "$@" --experimental method-handles
diff --git a/test/956-methodhandles/run b/test/956-methodhandles/run
deleted file mode 100755
index a9f1822..0000000
--- a/test/956-methodhandles/run
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# make us exit on a failure
-set -e
-
-./default-run "$@" --experimental method-handles
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index 17b56b4..f8daba6 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -76,6 +76,7 @@
     testStringConstructors();
     testReturnValueConversions();
     testVariableArity();
+    testVariableArity_MethodHandles_bind();
   }
 
   public static void testfindSpecial_invokeSuperBehaviour() throws Throwable {
@@ -1466,4 +1467,23 @@
       fail();
     } catch (WrongMethodTypeException e) {}
   }
+
+  // The same tests as the above, except that we use use MethodHandles.bind instead of
+  // MethodHandle.bindTo.
+  public static void testVariableArity_MethodHandles_bind() throws Throwable {
+    VariableArityTester vat = new VariableArityTester();
+    MethodHandle mh = MethodHandles.lookup().bind(vat, "update",
+            MethodType.methodType(String.class, boolean[].class));
+    assertTrue(mh.isVarargsCollector());
+
+    assertEquals("[]", mh.invoke());
+    assertEquals("[true, false, true]", mh.invoke(true, false, true));
+    assertEquals("[true, false, true]", mh.invoke(new boolean[] { true, false, true}));
+    assertEquals("[false, true]", mh.invoke(Boolean.valueOf(false), Boolean.valueOf(true)));
+
+    try {
+      mh.invoke(true, true, 0);
+      fail();
+    } catch (WrongMethodTypeException e) {}
+  }
 }
diff --git a/test/957-methodhandle-transforms/run b/test/957-methodhandle-transforms/run
deleted file mode 100755
index a9f1822..0000000
--- a/test/957-methodhandle-transforms/run
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# make us exit on a failure
-set -e
-
-./default-run "$@" --experimental method-handles
diff --git a/test/957-methodhandle-transforms/src/Main.java b/test/957-methodhandle-transforms/src/Main.java
index 9e79ff4..eebf55f 100644
--- a/test/957-methodhandle-transforms/src/Main.java
+++ b/test/957-methodhandle-transforms/src/Main.java
@@ -33,6 +33,7 @@
     testBindTo();
     testFilterReturnValue();
     testPermuteArguments();
+    testInvokers();
   }
 
   public static void testThrowException() throws Throwable {
@@ -888,6 +889,38 @@
     }
   }
 
+  private static Object returnBar() {
+    return "bar";
+  }
+
+  public static void testInvokers() throws Throwable {
+    final MethodType targetType = MethodType.methodType(String.class, String.class);
+    final MethodHandle target = MethodHandles.lookup().findVirtual(
+        String.class, "concat", targetType);
+
+    MethodHandle invoker = MethodHandles.invoker(target.type());
+    assertEquals("barbar", (String) invoker.invoke(target, "bar", "bar"));
+    assertEquals("barbar", (String) invoker.invoke(target, (Object) returnBar(), "bar"));
+    try {
+      String foo = (String) invoker.invoke(target, "bar", "bar", 24);
+      fail();
+    } catch (WrongMethodTypeException expected) {
+    }
+
+    MethodHandle exactInvoker = MethodHandles.exactInvoker(target.type());
+    assertEquals("barbar", (String) exactInvoker.invoke(target, "bar", "bar"));
+    try {
+      String foo = (String) exactInvoker.invoke(target, (Object) returnBar(), "bar");
+      fail();
+    } catch (WrongMethodTypeException expected) {
+    }
+    try {
+      String foo = (String) exactInvoker.invoke(target, "bar", "bar", 24);
+      fail();
+    } catch (WrongMethodTypeException expected) {
+    }
+  }
+
   public static void fail() {
     System.out.println("FAIL");
     Thread.dumpStack();
diff --git a/test/958-methodhandle-emulated-stackframe/run b/test/958-methodhandle-emulated-stackframe/run
deleted file mode 100755
index a9f1822..0000000
--- a/test/958-methodhandle-emulated-stackframe/run
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# make us exit on a failure
-set -e
-
-./default-run "$@" --experimental method-handles
diff --git a/test/959-invoke-polymorphic-accessors/run b/test/959-invoke-polymorphic-accessors/run
deleted file mode 100644
index a9f1822..0000000
--- a/test/959-invoke-polymorphic-accessors/run
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# make us exit on a failure
-set -e
-
-./default-run "$@" --experimental method-handles
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 4794f6b..5f1071f 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -62,6 +62,7 @@
 TEST_VDEX="n"
 TEST_IS_NDEBUG="n"
 APP_IMAGE="y"
+VDEX_FILTER=""
 
 while true; do
     if [ "x$1" = "x--quiet" ]; then
@@ -256,6 +257,11 @@
     elif [ "x$1" = "x--vdex" ]; then
         TEST_VDEX="y"
         shift
+    elif [ "x$1" = "x--vdex-filter" ]; then
+        shift
+        option="$1"
+        VDEX_FILTER="--compiler-filter=$option"
+        shift
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
         echo "unknown $0 option: $1" 1>&2
         exit 1
@@ -514,7 +520,7 @@
     dex2oat_cmdline="timeout -k 1m -s SIGRTMIN+2 1m ${dex2oat_cmdline}"
   fi
   if [ "$TEST_VDEX" = "y" ]; then
-    vdex_cmdline="${dex2oat_cmdline} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
+    vdex_cmdline="${dex2oat_cmdline} ${VDEX_FILTER} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
   fi
 fi
 
diff --git a/test/run-test b/test/run-test
index abe73c3..a913e78 100755
--- a/test/run-test
+++ b/test/run-test
@@ -354,6 +354,11 @@
     elif [ "x$1" = "x--vdex" ]; then
         run_args="${run_args} --vdex"
         shift
+    elif [ "x$1" = "x--vdex-filter" ]; then
+        shift
+        filter=$1
+        run_args="${run_args} --vdex-filter $filter"
+        shift
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
         echo "unknown $0 option: $1" 1>&2
         usage="yes"