Merge "ARM: VIXL32: Implement HClassTableGet Visitor."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 4fce235..9902628 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -116,10 +116,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_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_OUT_EXECUTABLES)/patchoatd
 
 ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 1737376..b883e08 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -53,6 +53,7 @@
         "optimizing/code_generator_utils.cc",
         "optimizing/constant_folding.cc",
         "optimizing/dead_code_elimination.cc",
+        "optimizing/escape.cc",
         "optimizing/graph_checker.cc",
         "optimizing/graph_visualizer.cc",
         "optimizing/gvn.cc",
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index e155e10..ad75ec4 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -45,6 +45,7 @@
 #include "dex_file-inl.h"
 #include "dex_instruction-inl.h"
 #include "dex/dex_to_dex_compiler.h"
+#include "dex/dex_to_dex_decompiler.h"
 #include "dex/verification_results.h"
 #include "dex/verified_method.h"
 #include "driver/compiler_options.h"
@@ -72,6 +73,7 @@
 #include "transaction.h"
 #include "utils/dex_cache_arrays_layout-inl.h"
 #include "utils/swap_space.h"
+#include "vdex_file.h"
 #include "verifier/method_verifier.h"
 #include "verifier/method_verifier-inl.h"
 #include "verifier/verifier_log_mode.h"
@@ -394,7 +396,6 @@
 
 void CompilerDriver::CompileAll(jobject class_loader,
                                 const std::vector<const DexFile*>& dex_files,
-                                verifier::VerifierDeps* verifier_deps,
                                 TimingLogger* timings) {
   DCHECK(!Runtime::Current()->IsStarted());
 
@@ -406,7 +407,7 @@
   // 2) Resolve all classes
   // 3) Attempt to verify all classes
   // 4) Attempt to initialize image classes, and trivially initialized classes
-  PreCompile(class_loader, dex_files, verifier_deps, timings);
+  PreCompile(class_loader, dex_files, timings);
   if (GetCompilerOptions().IsBootImage()) {
     // We don't need to setup the intrinsics for non boot image compilation, as
     // those compilations will pick up a boot image that have the ArtMethod already
@@ -433,6 +434,72 @@
   FreeThreadPools();
 }
 
+// In-place unquicken the given `dex_files` based on `quickening_info`.
+static void Unquicken(const std::vector<const DexFile*>& dex_files,
+                      const ArrayRef<const uint8_t>& quickening_info) {
+  const uint8_t* quickening_info_ptr = quickening_info.data();
+  const uint8_t* const quickening_info_end = quickening_info.data() + quickening_info.size();
+  for (const DexFile* dex_file : dex_files) {
+    for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
+      const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
+      const uint8_t* class_data = dex_file->GetClassData(class_def);
+      if (class_data == nullptr) {
+        continue;
+      }
+      ClassDataItemIterator it(*dex_file, class_data);
+      // Skip fields
+      while (it.HasNextStaticField()) {
+        it.Next();
+      }
+      while (it.HasNextInstanceField()) {
+        it.Next();
+      }
+
+      // Unquicken each method.
+      while (it.HasNextDirectMethod()) {
+        const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+        if (code_item != nullptr) {
+          uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
+          quickening_info_ptr += sizeof(uint32_t);
+          optimizer::ArtDecompileDEX(
+              *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size));
+          quickening_info_ptr += quickening_size;
+        }
+        it.Next();
+      }
+
+      while (it.HasNextVirtualMethod()) {
+        const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+        if (code_item != nullptr) {
+          uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
+          quickening_info_ptr += sizeof(uint32_t);
+          optimizer::ArtDecompileDEX(
+              *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size));
+          quickening_info_ptr += quickening_size;
+        }
+        it.Next();
+      }
+      DCHECK(!it.HasNext());
+    }
+  }
+  DCHECK_EQ(quickening_info_ptr, quickening_info_end) << "Failed to use all quickening info";
+}
+
+void CompilerDriver::CompileAll(jobject class_loader,
+                                const std::vector<const DexFile*>& dex_files,
+                                VdexFile* vdex_file,
+                                TimingLogger* timings) {
+  if (vdex_file != nullptr) {
+    // TODO: we unquicken unconditionnally, as we don't know
+    // if the boot image has changed. How exactly we'll know is under
+    // experimentation.
+    Unquicken(dex_files, vdex_file->GetQuickeningInfo());
+    Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
+        new verifier::VerifierDeps(dex_files, vdex_file->GetVerifierDepsData()));
+  }
+  CompileAll(class_loader, dex_files, timings);
+}
+
 static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel(
     Thread* self, const CompilerDriver& driver, Handle<mirror::ClassLoader> class_loader,
     const DexFile& dex_file, const DexFile::ClassDef& class_def)
@@ -673,7 +740,7 @@
 
   InitializeThreadPools();
 
-  PreCompile(jclass_loader, dex_files, /* verifier_deps */ nullptr, timings);
+  PreCompile(jclass_loader, dex_files, timings);
 
   // Can we run DEX-to-DEX compiler on this class ?
   optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level =
@@ -870,7 +937,6 @@
 
 void CompilerDriver::PreCompile(jobject class_loader,
                                 const std::vector<const DexFile*>& dex_files,
-                                verifier::VerifierDeps* verifier_deps,
                                 TimingLogger* timings) {
   CheckThreadPools();
 
@@ -904,7 +970,7 @@
     VLOG(compiler) << "Resolve const-strings: " << GetMemoryUsageString(false);
   }
 
-  Verify(class_loader, dex_files, verifier_deps, timings);
+  Verify(class_loader, dex_files, timings);
   VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
 
   if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) {
@@ -1936,8 +2002,10 @@
 
 void CompilerDriver::Verify(jobject jclass_loader,
                             const std::vector<const DexFile*>& dex_files,
-                            verifier::VerifierDeps* verifier_deps,
                             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());
@@ -1975,16 +2043,15 @@
     }
   }
 
-  // If there is no passed `verifier_deps` (because of non-existing vdex), or
-  // the passed `verifier_deps` is not valid anymore, create a new one for
+  // 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
   // non boot image compilation. The verifier will need it to record the new dependencies.
   // Then dex2oat can update the vdex file with these new dependencies.
   if (!GetCompilerOptions().IsBootImage()) {
     // Create the main VerifierDeps, and set it to this thread.
-    Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
-        new verifier::VerifierDeps(dex_files));
-    Thread::Current()->SetVerifierDeps(
-        Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps());
+    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.
     // We will merge them after verification.
     for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
@@ -2005,13 +2072,11 @@
   }
 
   if (!GetCompilerOptions().IsBootImage()) {
-    verifier::VerifierDeps* main_deps =
-        Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
     // Merge all VerifierDeps into the main one.
     for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
       verifier::VerifierDeps* thread_deps = worker->GetThread()->GetVerifierDeps();
       worker->GetThread()->SetVerifierDeps(nullptr);
-      main_deps->MergeWith(*thread_deps, dex_files);;
+      verifier_deps->MergeWith(*thread_deps, dex_files);;
       delete thread_deps;
     }
     Thread::Current()->SetVerifierDeps(nullptr);
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index c7719fb..7418b00 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -51,7 +51,6 @@
 
 namespace verifier {
 class MethodVerifier;
-class VerifierDeps;
 class VerifierDepsTest;
 }  // namespace verifier
 
@@ -69,6 +68,7 @@
 using SwapSrcMap = SrcMap<SwapAllocator<SrcMapElem>>;
 template<class T> class Handle;
 class TimingLogger;
+class VdexFile;
 class VerificationResults;
 class VerifiedMethod;
 
@@ -119,7 +119,12 @@
 
   void CompileAll(jobject class_loader,
                   const std::vector<const DexFile*>& dex_files,
-                  verifier::VerifierDeps* verifier_deps,
+                  TimingLogger* timings)
+      REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_);
+
+  void CompileAll(jobject class_loader,
+                  const std::vector<const DexFile*>& dex_files,
+                  VdexFile* vdex_file,
                   TimingLogger* timings)
       REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_);
 
@@ -420,7 +425,6 @@
  private:
   void PreCompile(jobject class_loader,
                   const std::vector<const DexFile*>& dex_files,
-                  verifier::VerifierDeps* verifier_deps,
                   TimingLogger* timings)
       REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
 
@@ -443,7 +447,6 @@
 
   void Verify(jobject class_loader,
               const std::vector<const DexFile*>& dex_files,
-              verifier::VerifierDeps* verifier_deps,
               TimingLogger* timings);
 
   void VerifyDexFile(jobject class_loader,
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 0f41cbf..a706697 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -838,64 +838,19 @@
   return true;
 }
 
-class ImageWriter::PruneClassesVisitor : public ClassVisitor {
+class ImageWriter::NonImageClassesVisitor : public ClassVisitor {
  public:
-  PruneClassesVisitor(ImageWriter* image_writer, ObjPtr<mirror::ClassLoader> class_loader)
-      : image_writer_(image_writer),
-        class_loader_(class_loader),
-        classes_to_prune_(),
-        defined_class_count_(0u) { }
+  explicit NonImageClassesVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {}
 
-  bool operator()(ObjPtr<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+  bool operator()(ObjPtr<Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
     if (!image_writer_->KeepClass(klass.Ptr())) {
       classes_to_prune_.insert(klass.Ptr());
-      if (klass->GetClassLoader() == class_loader_) {
-        ++defined_class_count_;
-      }
     }
     return true;
   }
 
-  size_t Prune() REQUIRES_SHARED(Locks::mutator_lock_) {
-    ClassTable* class_table =
-        Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader_);
-    for (mirror::Class* klass : classes_to_prune_) {
-      std::string storage;
-      const char* descriptor = klass->GetDescriptor(&storage);
-      bool result = class_table->Remove(descriptor);
-      DCHECK(result);
-    }
-    return defined_class_count_;
-  }
-
- private:
-  ImageWriter* const image_writer_;
-  const ObjPtr<mirror::ClassLoader> class_loader_;
   std::unordered_set<mirror::Class*> classes_to_prune_;
-  size_t defined_class_count_;
-};
-
-class ImageWriter::PruneClassLoaderClassesVisitor : public ClassLoaderVisitor {
- public:
-  explicit PruneClassLoaderClassesVisitor(ImageWriter* image_writer)
-      : image_writer_(image_writer), removed_class_count_(0) {}
-
-  virtual void Visit(ObjPtr<mirror::ClassLoader> class_loader) OVERRIDE
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    PruneClassesVisitor classes_visitor(image_writer_, class_loader);
-    ClassTable* class_table =
-        Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader);
-    class_table->Visit(classes_visitor);
-    removed_class_count_ += classes_visitor.Prune();
-  }
-
-  size_t GetRemovedClassCount() const {
-    return removed_class_count_;
-  }
-
- private:
   ImageWriter* const image_writer_;
-  size_t removed_class_count_;
 };
 
 void ImageWriter::PruneNonImageClasses() {
@@ -907,13 +862,21 @@
   // path dex caches.
   class_linker->ClearClassTableStrongRoots();
 
+  // Make a list of classes we would like to prune.
+  NonImageClassesVisitor visitor(this);
+  class_linker->VisitClasses(&visitor);
+
   // Remove the undesired classes from the class roots.
-  {
-    ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
-    PruneClassLoaderClassesVisitor class_loader_visitor(this);
-    class_loader_visitor.Visit(nullptr);  // Visit boot class loader.
-    class_linker->VisitClassLoaders(&class_loader_visitor);
-    VLOG(compiler) << "Pruned " << class_loader_visitor.GetRemovedClassCount() << " classes";
+  VLOG(compiler) << "Pruning " << visitor.classes_to_prune_.size() << " classes";
+  for (mirror::Class* klass : visitor.classes_to_prune_) {
+    std::string temp;
+    const char* name = klass->GetDescriptor(&temp);
+    VLOG(compiler) << "Pruning class " << name;
+    if (!compile_app_image_) {
+      DCHECK(IsBootClassLoaderClass(klass));
+    }
+    bool result = class_linker->RemoveClass(name, klass->GetClassLoader());
+    DCHECK(result);
   }
 
   // Clear references to removed classes from the DexCaches.
@@ -1544,10 +1507,8 @@
     }
     // Calculate the size of the class table.
     ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
-    CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u);
-    mirror::ClassLoader* class_loader = compile_app_image_ ? *class_loaders_.begin() : nullptr;
-    DCHECK_EQ(image_info.class_table_->NumZygoteClasses(class_loader), 0u);
-    if (image_info.class_table_->NumNonZygoteClasses(class_loader) != 0u) {
+    DCHECK_EQ(image_info.class_table_->NumZygoteClasses(), 0u);
+    if (image_info.class_table_->NumNonZygoteClasses() != 0u) {
       image_info.class_table_bytes_ += image_info.class_table_->WriteToMemory(nullptr);
     }
   }
@@ -1892,10 +1853,8 @@
     // above comment for intern tables.
     ClassTable temp_class_table;
     temp_class_table.ReadFromMemory(class_table_memory_ptr);
-    CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u);
-    mirror::ClassLoader* class_loader = compile_app_image_ ? *class_loaders_.begin() : nullptr;
-    CHECK_EQ(temp_class_table.NumZygoteClasses(class_loader),
-             table->NumNonZygoteClasses(class_loader) + table->NumZygoteClasses(class_loader));
+    CHECK_EQ(temp_class_table.NumZygoteClasses(), table->NumNonZygoteClasses() +
+             table->NumZygoteClasses());
     BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(&root_visitor,
                                                                     RootInfo(kRootUnknown));
     temp_class_table.VisitRoots(buffered_visitor);
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index ad6ffd8..24fad46 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -588,8 +588,7 @@
   class FixupVisitor;
   class GetRootsVisitor;
   class NativeLocationVisitor;
-  class PruneClassesVisitor;
-  class PruneClassLoaderClassesVisitor;
+  class NonImageClassesVisitor;
   class VisitReferencesVisitor;
 
   DISALLOW_COPY_AND_ASSIGN(ImageWriter);
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index eed9d11..153aff4 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -403,6 +403,35 @@
   return true;
 }
 
+// Add dex file source(s) from a vdex file specified by a file handle.
+bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file,
+                                      const char* location,
+                                      CreateTypeLookupTable create_type_lookup_table) {
+  DCHECK(write_state_ == WriteState::kAddingDexFileSources);
+  const uint8_t* current_dex_data = nullptr;
+  for (size_t i = 0; ; ++i) {
+    current_dex_data = vdex_file.GetNextDexFileData(current_dex_data);
+    if (current_dex_data == nullptr) {
+      break;
+    }
+    if (!DexFile::IsMagicValid(current_dex_data)) {
+      LOG(ERROR) << "Invalid magic in vdex file created from " << location;
+      return false;
+    }
+    // We used `zipped_dex_file_locations_` to keep the strings in memory.
+    zipped_dex_file_locations_.push_back(DexFile::GetMultiDexLocation(i, location));
+    const char* full_location = zipped_dex_file_locations_.back().c_str();
+    oat_dex_files_.emplace_back(full_location,
+                                DexFileSource(current_dex_data),
+                                create_type_lookup_table);
+  }
+  if (oat_dex_files_.empty()) {
+    LOG(ERROR) << "No dex files in vdex file created from " << location;
+    return false;
+  }
+  return true;
+}
+
 // Add dex file source from raw memory.
 bool OatWriter::AddRawDexFileSource(const ArrayRef<const uint8_t>& data,
                                     const char* location,
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index f9671d7..0dcf79e 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -42,6 +42,7 @@
 class OutputStream;
 class TimingLogger;
 class TypeLookupTable;
+class VdexFile;
 class ZipEntry;
 
 namespace debug {
@@ -116,7 +117,8 @@
   // To produce a valid oat file, the user must first add sources with any combination of
   //   - AddDexFileSource(),
   //   - AddZippedDexFilesSource(),
-  //   - AddRawDexFileSource().
+  //   - AddRawDexFileSource(),
+  //   - AddVdexDexFilesSource().
   // Then the user must call in order
   //   - WriteAndOpenDexFiles()
   //   - Initialize()
@@ -145,6 +147,11 @@
       const char* location,
       uint32_t location_checksum,
       CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
+  // Add dex file source(s) from a vdex file.
+  bool AddVdexDexFilesSource(
+      const VdexFile& vdex_file,
+      const char* location,
+      CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
   dchecked_vector<const char*> GetSourceLocations() const;
 
   // Write raw dex files to the vdex file, mmap the file and open the dex files from it.
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 59e1784..a78b3da 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -572,8 +572,10 @@
 
   // We are about to use the assembler to place literals directly. Make sure we have enough
   // underlying code buffer and we have generated the jump table with right size.
-  CodeBufferCheckScope scope(codegen->GetVIXLAssembler(), num_entries * sizeof(int32_t),
-                             CodeBufferCheckScope::kCheck, CodeBufferCheckScope::kExactSize);
+  vixl::CodeBufferCheckScope scope(codegen->GetVIXLAssembler(),
+                                   num_entries * sizeof(int32_t),
+                                   vixl::CodeBufferCheckScope::kReserveBufferSpace,
+                                   vixl::CodeBufferCheckScope::kExactSize);
 
   __ Bind(&table_start_);
   const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
@@ -2260,10 +2262,10 @@
         masm->GetCursorAddress<vixl::aarch64::Instruction*>() - kInstructionSize;
     if (prev->IsLoadOrStore()) {
       // Make sure we emit only exactly one nop.
-      vixl::aarch64::CodeBufferCheckScope scope(masm,
-                                                kInstructionSize,
-                                                vixl::aarch64::CodeBufferCheckScope::kCheck,
-                                                vixl::aarch64::CodeBufferCheckScope::kExactSize);
+      vixl::CodeBufferCheckScope scope(masm,
+                                       kInstructionSize,
+                                       vixl::CodeBufferCheckScope::kReserveBufferSpace,
+                                       vixl::CodeBufferCheckScope::kExactSize);
       __ nop();
     }
   }
@@ -4036,7 +4038,8 @@
       vixl::aarch64::Label* label = &relative_call_patches_.back().label;
       SingleEmissionCheckScope guard(GetVIXLAssembler());
       __ Bind(label);
-      __ bl(0);  // Branch and link to itself. This will be overriden at link time.
+      // Branch and link to itself. This will be overriden at link time.
+      __ bl(static_cast<int64_t>(0));
       break;
     }
     case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
@@ -4167,7 +4170,7 @@
   DCHECK(reg.IsX());
   SingleEmissionCheckScope guard(GetVIXLAssembler());
   __ Bind(fixup_label);
-  __ adrp(reg, /* offset placeholder */ 0);
+  __ adrp(reg, /* offset placeholder */ static_cast<int64_t>(0));
 }
 
 void CodeGeneratorARM64::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 d962b44..1b5138f 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -47,6 +47,7 @@
 using helpers::InputRegisterAt;
 using helpers::InputSRegisterAt;
 using helpers::InputVRegisterAt;
+using helpers::Int32ConstantFrom;
 using helpers::LocationFrom;
 using helpers::LowRegisterFrom;
 using helpers::LowSRegisterFrom;
@@ -132,7 +133,7 @@
       vixl32::Register base = sp;
       if (stack_offset != 0) {
         base = temps.Acquire();
-        __ Add(base, sp, stack_offset);
+        __ Add(base, sp, Operand::From(stack_offset));
       }
       __ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
     }
@@ -180,7 +181,7 @@
       vixl32::Register base = sp;
       if (stack_offset != 0) {
         base = temps.Acquire();
-        __ Add(base, sp, stack_offset);
+        __ Add(base, sp, Operand::From(stack_offset));
       }
       __ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
     }
@@ -673,8 +674,8 @@
   DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
 
   // We are about to use the assembler to place literals directly. Make sure we have enough
-  // underlying code buffer and we have generated the jump table with right size.
-  codegen->GetVIXLAssembler()->GetBuffer().Align();
+  // underlying code buffer and we have generated a jump table of the right size, using
+  // codegen->GetVIXLAssembler()->GetBuffer().Align();
   AssemblerAccurateScope aas(codegen->GetVIXLAssembler(),
                              num_entries * sizeof(int32_t),
                              CodeBufferCheckScope::kMaximumSize);
@@ -701,7 +702,7 @@
     DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
     DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
 
-    bb_addresses_[i].get()->UpdateValue(jump_offset, &codegen->GetVIXLAssembler()->GetBuffer());
+    bb_addresses_[i].get()->UpdateValue(jump_offset, codegen->GetVIXLAssembler()->GetBuffer());
   }
 }
 
@@ -1152,7 +1153,8 @@
 void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
                                                             size_t condition_input_index,
                                                             vixl32::Label* true_target,
-                                                            vixl32::Label* false_target) {
+                                                            vixl32::Label* false_target,
+                                                            bool far_target) {
   HInstruction* cond = instruction->InputAt(condition_input_index);
 
   if (true_target == nullptr && false_target == nullptr) {
@@ -1188,9 +1190,13 @@
       DCHECK(cond_val.IsRegister());
     }
     if (true_target == nullptr) {
-      __ Cbz(InputRegisterAt(instruction, condition_input_index), false_target);
+      __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index),
+                                false_target,
+                                far_target);
     } else {
-      __ Cbnz(InputRegisterAt(instruction, condition_input_index), true_target);
+      __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index),
+                                   true_target,
+                                   far_target);
     }
   } else {
     // Condition has not been materialized. Use its inputs as the comparison and
@@ -1285,7 +1291,8 @@
   GenerateTestAndBranch(select,
                         /* condition_input_index */ 2,
                         /* true_target */ nullptr,
-                        &false_target);
+                        &false_target,
+                        /* far_target */ false);
   codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
   __ Bind(&false_target);
 }
@@ -1661,7 +1668,20 @@
   // Set the hidden (in r12) argument. It is done here, right before a BLX to prevent other
   // instruction from clobbering it as they might use r12 as a scratch register.
   DCHECK(hidden_reg.Is(r12));
-  __ Mov(hidden_reg, invoke->GetDexMethodIndex());
+
+  {
+    // The VIXL macro assembler may clobber any of the scratch registers that are available to it,
+    // so it checks if the application is using them (by passing them to the macro assembler
+    // methods). The following application of UseScratchRegisterScope corrects VIXL's notion of
+    // what is available, and is the opposite of the standard usage: Instead of requesting a
+    // temporary location, it imposes an external constraint (i.e. a specific register is reserved
+    // for the hidden argument). Note that this works even if VIXL needs a scratch register itself
+    // (to materialize the constant), since the destination register becomes available for such use
+    // internally for the duration of the macro instruction.
+    UseScratchRegisterScope temps(GetVIXLAssembler());
+    temps.Exclude(hidden_reg);
+    __ Mov(hidden_reg, invoke->GetDexMethodIndex());
+  }
 
   {
     AssemblerAccurateScope aas(GetVIXLAssembler(),
@@ -2195,10 +2215,9 @@
       break;
     }
 
-    // TODO(VIXL): https://android-review.googlesource.com/#/c/254144/
     case Primitive::kPrimLong: {
       locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetInAt(1, ArmEncodableConstantOrRegister(add->InputAt(1), ADD));
       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
       break;
     }
@@ -2228,11 +2247,15 @@
       }
       break;
 
-    // TODO(VIXL): https://android-review.googlesource.com/#/c/254144/
     case Primitive::kPrimLong: {
-      DCHECK(second.IsRegisterPair());
-      __ Adds(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
-      __ Adc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
+      if (second.IsConstant()) {
+        uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
+        GenerateAddLongConst(out, first, value);
+      } else {
+        DCHECK(second.IsRegisterPair());
+        __ Adds(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
+        __ Adc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
+      }
       break;
     }
 
@@ -2257,10 +2280,9 @@
       break;
     }
 
-    // TODO(VIXL): https://android-review.googlesource.com/#/c/254144/
     case Primitive::kPrimLong: {
       locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetInAt(1, ArmEncodableConstantOrRegister(sub->InputAt(1), SUB));
       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
       break;
     }
@@ -2287,11 +2309,15 @@
       break;
     }
 
-    // TODO(VIXL): https://android-review.googlesource.com/#/c/254144/
     case Primitive::kPrimLong: {
-      DCHECK(second.IsRegisterPair());
-      __ Subs(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
-      __ Sbc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
+      if (second.IsConstant()) {
+        uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
+        GenerateAddLongConst(out, first, -value);
+      } else {
+        DCHECK(second.IsRegisterPair());
+        __ Subs(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
+        __ Sbc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
+      }
       break;
     }
 
@@ -2452,13 +2478,14 @@
   vixl32::Register dividend = InputRegisterAt(instruction, 0);
   vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0));
   vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1));
-  int64_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+  int32_t imm = Int32ConstantFrom(second);
 
   int64_t magic;
   int shift;
   CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
 
-  __ Mov(temp1, magic);
+  // TODO(VIXL): Change the static cast to Operand::From() after VIXL is fixed.
+  __ Mov(temp1, static_cast<int32_t>(magic));
   __ Smull(temp2, temp1, dividend, temp1);
 
   if (imm > 0 && magic < 0) {
@@ -2775,7 +2802,7 @@
     case Primitive::kPrimShort:
     case Primitive::kPrimInt: {
       if (value.IsRegister()) {
-        __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
+        __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
       } else {
         DCHECK(value.IsConstant()) << value;
         if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
@@ -2851,9 +2878,9 @@
     }
     // Rotate, or mov to out for zero or word size rotations.
     if (rot != 0u) {
-      __ Lsr(out_reg_hi, in_reg_hi, rot);
+      __ Lsr(out_reg_hi, in_reg_hi, Operand::From(rot));
       __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot));
-      __ Lsr(out_reg_lo, in_reg_lo, rot);
+      __ Lsr(out_reg_lo, in_reg_lo, Operand::From(rot));
       __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot));
     } else {
       __ Mov(out_reg_lo, in_reg_lo);
@@ -2868,7 +2895,7 @@
     __ And(shift_right, RegisterFrom(rhs), 0x1F);
     __ Lsrs(shift_left, RegisterFrom(rhs), 6);
     // TODO(VIXL): Check that flags are kept after "vixl32::LeaveFlags" enabled.
-    __ Rsb(shift_left, shift_right, kArmBitsPerWord);
+    __ Rsb(shift_left, shift_right, Operand::From(kArmBitsPerWord));
     __ B(cc, &shift_by_32_plus_shift_right);
 
     // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
@@ -3034,11 +3061,11 @@
           // Shift the high part
           __ Lsl(o_h, high, o_l);
           // Shift the low part and `or` what overflew on the high part
-          __ Rsb(temp, o_l, kArmBitsPerWord);
+          __ Rsb(temp, o_l, Operand::From(kArmBitsPerWord));
           __ Lsr(temp, low, temp);
           __ Orr(o_h, o_h, temp);
           // If the shift is > 32 bits, override the high part
-          __ Subs(temp, o_l, kArmBitsPerWord);
+          __ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
           {
             AssemblerAccurateScope guard(GetVIXLAssembler(),
                                          3 * kArmInstrMaxSizeInBytes,
@@ -3053,11 +3080,11 @@
           // Shift the low part
           __ Lsr(o_l, low, o_h);
           // Shift the high part and `or` what underflew on the low part
-          __ Rsb(temp, o_h, kArmBitsPerWord);
+          __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
           __ Lsl(temp, high, temp);
           __ Orr(o_l, o_l, temp);
           // If the shift is > 32 bits, override the low part
-          __ Subs(temp, o_h, kArmBitsPerWord);
+          __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
           {
             AssemblerAccurateScope guard(GetVIXLAssembler(),
                                          3 * kArmInstrMaxSizeInBytes,
@@ -3071,10 +3098,10 @@
           __ And(o_h, second_reg, kMaxLongShiftDistance);
           // same as Shr except we use `Lsr`s and not `Asr`s
           __ Lsr(o_l, low, o_h);
-          __ Rsb(temp, o_h, kArmBitsPerWord);
+          __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
           __ Lsl(temp, high, temp);
           __ Orr(o_l, o_l, temp);
-          __ Subs(temp, o_h, kArmBitsPerWord);
+          __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
           {
             AssemblerAccurateScope guard(GetVIXLAssembler(),
                                          3 * kArmInstrMaxSizeInBytes,
@@ -3418,7 +3445,7 @@
     __ Add(temp, addr, offset);
     addr = temp;
   }
-  __ Ldrexd(out_lo, out_hi, addr);
+  __ Ldrexd(out_lo, out_hi, MemOperand(addr));
 }
 
 void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
@@ -3438,10 +3465,10 @@
   __ Bind(&fail);
   // We need a load followed by store. (The address used in a STREX instruction must
   // be the same as the address in the most recently executed LDREX instruction.)
-  __ Ldrexd(temp1, temp2, addr);
+  __ Ldrexd(temp1, temp2, MemOperand(addr));
   codegen_->MaybeRecordImplicitNullCheck(instruction);
-  __ Strexd(temp1, value_lo, value_hi, addr);
-  __ Cbnz(temp1, &fail);
+  __ Strexd(temp1, value_lo, value_hi, MemOperand(addr));
+  __ CompareAndBranchIfNonZero(temp1, &fail);
 }
 
 void LocationsBuilderARMVIXL::HandleFieldSet(
@@ -3957,7 +3984,7 @@
   NullCheckSlowPathARMVIXL* slow_path =
       new (GetGraph()->GetArena()) NullCheckSlowPathARMVIXL(instruction);
   AddSlowPath(slow_path);
-  __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
+  __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
 }
 
 void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) {
@@ -4195,6 +4222,7 @@
         } else {
           codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
         }
+        temps.Release(temp);
       }
       break;
     }
@@ -4234,6 +4262,7 @@
             __ Add(temp, obj, data_offset);
           }
           codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
+          temps.Release(temp);
 
           codegen_->MaybeRecordImplicitNullCheck(instruction);
           // If read barriers are enabled, emit read barriers other than
@@ -4255,6 +4284,7 @@
         vixl32::Register temp = temps.Acquire();
         __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
         GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset);
+        temps.Release(temp);
       }
       break;
     }
@@ -4268,6 +4298,7 @@
         vixl32::Register temp = temps.Acquire();
         __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
         GetAssembler()->LoadSFromOffset(out, temp, data_offset);
+        temps.Release(temp);
       }
       break;
     }
@@ -4280,6 +4311,7 @@
         vixl32::Register temp = temps.Acquire();
         __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
         GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset);
+        temps.Release(temp);
       }
       break;
     }
@@ -4369,6 +4401,7 @@
           __ Add(temp, array, data_offset);
         }
         codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
+        temps.Release(temp);
       }
       break;
     }
@@ -4390,6 +4423,7 @@
           vixl32::Register temp = temps.Acquire();
           __ Add(temp, array, data_offset);
           codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
+          temps.Release(temp);
         }
         codegen_->MaybeRecordImplicitNullCheck(instruction);
         DCHECK(!needs_write_barrier);
@@ -4413,7 +4447,7 @@
         codegen_->AddSlowPath(slow_path);
         if (instruction->GetValueCanBeNull()) {
           vixl32::Label non_zero;
-          __ Cbnz(value, &non_zero);
+          __ CompareAndBranchIfNonZero(value, &non_zero);
           if (index.IsConstant()) {
             size_t offset =
                (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
@@ -4423,6 +4457,7 @@
             vixl32::Register temp = temps.Acquire();
             __ Add(temp, array, data_offset);
             codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
+            temps.Release(temp);
           }
           codegen_->MaybeRecordImplicitNullCheck(instruction);
           __ B(&done);
@@ -4461,7 +4496,7 @@
           GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
           // If heap poisoning is enabled, no need to unpoison
           // `temp1`, as we are comparing against null below.
-          __ Cbnz(temp1, slow_path->GetEntryLabel());
+          __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
           __ Bind(&do_put);
         } else {
           __ B(ne, slow_path->GetEntryLabel());
@@ -4492,6 +4527,7 @@
                                           LocationFrom(source),
                                           temp,
                                           RegisterFrom(index));
+        temps.Release(temp);
       }
 
       if (!may_need_runtime_call_for_type_check) {
@@ -4521,6 +4557,7 @@
         vixl32::Register temp = temps.Acquire();
         __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
         GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset);
+        temps.Release(temp);
       }
       break;
     }
@@ -4535,6 +4572,7 @@
         vixl32::Register temp = temps.Acquire();
         __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
         GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset);
+        temps.Release(temp);
       }
       break;
     }
@@ -4549,6 +4587,7 @@
         vixl32::Register temp = temps.Acquire();
         __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
         GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset);
+        temps.Release(temp);
       }
       break;
     }
@@ -4638,11 +4677,11 @@
                                       bool can_be_null) {
   vixl32::Label is_null;
   if (can_be_null) {
-    __ Cbz(value, &is_null);
+    __ CompareAndBranchIfZero(value, &is_null);
   }
   GetAssembler()->LoadFromOffset(
       kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
-  __ Lsr(temp, object, gc::accounting::CardTable::kCardShift);
+  __ Lsr(temp, object, Operand::From(gc::accounting::CardTable::kCardShift));
   __ Strb(card, MemOperand(card, temp));
   if (can_be_null) {
     __ Bind(&is_null);
@@ -4697,10 +4736,10 @@
   GetAssembler()->LoadFromOffset(
       kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
   if (successor == nullptr) {
-    __ Cbnz(temp, slow_path->GetEntryLabel());
+    __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
     __ Bind(slow_path->GetReturnLabel());
   } else {
-    __ Cbz(temp, codegen_->GetLabelOf(successor));
+    __ CompareAndBranchIfZero(temp, codegen_->GetLabelOf(successor));
     __ B(slow_path->GetEntryLabel());
   }
 }
@@ -4884,9 +4923,12 @@
   } else if (source.IsStackSlot() && destination.IsRegister()) {
     Exchange(RegisterFrom(destination), source.GetStackIndex());
   } else if (source.IsStackSlot() && destination.IsStackSlot()) {
-    TODO_VIXL32(FATAL);
+    Exchange(source.GetStackIndex(), destination.GetStackIndex());
   } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
-    TODO_VIXL32(FATAL);
+    vixl32::SRegister temp = temps.AcquireS();
+    __ Vmov(temp, SRegisterFrom(source));
+    __ Vmov(SRegisterFrom(source), SRegisterFrom(destination));
+    __ Vmov(SRegisterFrom(destination), temp);
   } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
     vixl32::DRegister temp = temps.AcquireD();
     __ Vmov(temp, LowRegisterFrom(source), HighRegisterFrom(source));
@@ -4909,9 +4951,27 @@
     __ Vmov(first, second);
     __ Vmov(second, temp);
   } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
-    TODO_VIXL32(FATAL);
+    vixl32::DRegister reg = source.IsFpuRegisterPair()
+        ? DRegisterFrom(source)
+        : DRegisterFrom(destination);
+    int mem = source.IsFpuRegisterPair()
+        ? destination.GetStackIndex()
+        : source.GetStackIndex();
+    vixl32::DRegister temp = temps.AcquireD();
+    __ Vmov(temp, reg);
+    GetAssembler()->LoadDFromOffset(reg, sp, mem);
+    GetAssembler()->StoreDToOffset(temp, sp, mem);
   } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
-    TODO_VIXL32(FATAL);
+    vixl32::SRegister reg = source.IsFpuRegister()
+        ? SRegisterFrom(source)
+        : SRegisterFrom(destination);
+    int mem = source.IsFpuRegister()
+        ? destination.GetStackIndex()
+        : source.GetStackIndex();
+    vixl32::Register temp = temps.Acquire();
+    __ Vmov(temp, reg);
+    GetAssembler()->LoadSFromOffset(reg, sp, mem);
+    GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
   } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
     vixl32::DRegister temp1 = temps.AcquireD();
     vixl32::DRegister temp2 = temps.AcquireD();
@@ -5015,7 +5075,7 @@
         cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
     codegen_->AddSlowPath(slow_path);
     if (generate_null_check) {
-      __ Cbz(out, slow_path->GetEntryLabel());
+      __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
     }
     if (cls->MustGenerateClinitCheck()) {
       GenerateClassInitializationCheck(slow_path, out);
@@ -5206,7 +5266,7 @@
   // Return 0 if `obj` is null.
   // avoid null check if we know obj is not null.
   if (instruction->MustDoNullCheck()) {
-    __ Cbz(obj, &zero);
+    __ CompareAndBranchIfZero(obj, &zero, /* far_target */ false);
   }
 
   switch (type_check_kind) {
@@ -5239,7 +5299,7 @@
       // /* HeapReference<Class> */ out = out->super_class_
       GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc);
       // If `out` is null, we use it for the result, and jump to `done`.
-      __ Cbz(out, &done);
+      __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
       __ Cmp(out, cls);
       __ B(ne, &loop);
       __ Mov(out, 1);
@@ -5263,7 +5323,7 @@
       __ B(eq, &success);
       // /* HeapReference<Class> */ out = out->super_class_
       GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc);
-      __ Cbnz(out, &loop);
+      __ CompareAndBranchIfNonZero(out, &loop);
       // If `out` is null, we use it for the result, and jump to `done`.
       __ B(&done);
       __ Bind(&success);
@@ -5289,10 +5349,10 @@
       // /* HeapReference<Class> */ out = out->component_type_
       GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, maybe_temp_loc);
       // If `out` is null, we use it for the result, and jump to `done`.
-      __ Cbz(out, &done);
+      __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
       GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
       static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-      __ Cbnz(out, &zero);
+      __ CompareAndBranchIfNonZero(out, &zero, /* far_target */ false);
       __ Bind(&exact_check);
       __ Mov(out, 1);
       __ B(&done);
@@ -5428,7 +5488,7 @@
   vixl32::Label done;
   // Avoid null check if we know obj is not null.
   if (instruction->MustDoNullCheck()) {
-    __ Cbz(obj, &done);
+    __ CompareAndBranchIfZero(obj, &done, /* far_target */ false);
   }
 
   // /* HeapReference<Class> */ temp = obj->klass_
@@ -5454,7 +5514,7 @@
 
       // If the class reference currently in `temp` is null, jump to the slow path to throw the
       // exception.
-      __ Cbz(temp, type_check_slow_path->GetEntryLabel());
+      __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
 
       // Otherwise, compare the classes.
       __ Cmp(temp, cls);
@@ -5474,7 +5534,7 @@
 
       // If the class reference currently in `temp` is null, jump to the slow path to throw the
       // exception.
-      __ Cbz(temp, type_check_slow_path->GetEntryLabel());
+      __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
       // Otherwise, jump to the beginning of the loop.
       __ B(&loop);
       break;
@@ -5489,12 +5549,12 @@
       // /* HeapReference<Class> */ temp = temp->component_type_
       GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, maybe_temp2_loc);
       // If the component type is null, jump to the slow path to throw the exception.
-      __ Cbz(temp, type_check_slow_path->GetEntryLabel());
+      __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
       // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
       // to further check that this component type is not a primitive type.
       GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
       static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot");
-      __ Cbnz(temp, type_check_slow_path->GetEntryLabel());
+      __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
       break;
     }
 
@@ -5695,6 +5755,33 @@
   __ Eor(out, first, value);
 }
 
+void InstructionCodeGeneratorARMVIXL::GenerateAddLongConst(Location out,
+                                                           Location first,
+                                                           uint64_t value) {
+  vixl32::Register out_low = LowRegisterFrom(out);
+  vixl32::Register out_high = HighRegisterFrom(out);
+  vixl32::Register first_low = LowRegisterFrom(first);
+  vixl32::Register first_high = HighRegisterFrom(first);
+  uint32_t value_low = Low32Bits(value);
+  uint32_t value_high = High32Bits(value);
+  if (value_low == 0u) {
+    if (!out_low.Is(first_low)) {
+      __ Mov(out_low, first_low);
+    }
+    __ Add(out_high, first_high, value_high);
+    return;
+  }
+  __ Adds(out_low, first_low, value_low);
+  if (GetAssembler()->ShifterOperandCanHold(ADC, value_high, kCcKeep)) {
+    __ Adc(out_high, first_high, value_high);
+  } else if (GetAssembler()->ShifterOperandCanHold(SBC, ~value_high, kCcKeep)) {
+    __ Sbc(out_high, first_high, ~value_high);
+  } else {
+    LOG(FATAL) << "Unexpected constant " << value_high;
+    UNREACHABLE();
+  }
+}
+
 void InstructionCodeGeneratorARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   Location first = locations->InAt(0);
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 302ee38..89fef43 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -369,7 +369,7 @@
   FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR)
 
   ArmVIXLAssembler* GetAssembler() const { return assembler_; }
-  vixl::aarch32::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
+  ArmVIXLMacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
 
  private:
   void VisitUnimplemementedInstruction(HInstruction* instruction) {
@@ -386,6 +386,7 @@
   void GenerateAndConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
   void GenerateOrrConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
   void GenerateEorConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
+  void GenerateAddLongConst(Location out, Location first, uint64_t value);
   void HandleBitwiseOperation(HBinaryOperation* operation);
   void HandleCondition(HCondition* condition);
   void HandleIntegerRotate(HRor* ror);
@@ -451,7 +452,8 @@
   void GenerateTestAndBranch(HInstruction* instruction,
                              size_t condition_input_index,
                              vixl::aarch32::Label* true_target,
-                             vixl::aarch32::Label* false_target);
+                             vixl::aarch32::Label* false_target,
+                             bool far_target = true);
   void GenerateCompareTestAndBranch(HCondition* condition,
                                     vixl::aarch32::Label* true_target,
                                     vixl::aarch32::Label* false_target);
@@ -504,7 +506,7 @@
 
   const ArmVIXLAssembler& GetAssembler() const OVERRIDE { return assembler_; }
 
-  vixl::aarch32::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
+  ArmVIXLMacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
 
   size_t GetWordSize() const OVERRIDE { return kArmWordSize; }
 
diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h
index 5129daf..d3623f1 100644
--- a/compiler/optimizing/common_arm.h
+++ b/compiler/optimizing/common_arm.h
@@ -139,9 +139,14 @@
   HConstant* instr = location.GetConstant();
   if (instr->IsIntConstant()) {
     return instr->AsIntConstant()->GetValue();
-  } else {
-    DCHECK(instr->IsNullConstant()) << instr->DebugName();
+  } else if (instr->IsNullConstant()) {
     return 0;
+  } else {
+    DCHECK(instr->IsLongConstant()) << instr->DebugName();
+    const int64_t ret = instr->AsLongConstant()->GetValue();
+    DCHECK_GE(ret, std::numeric_limits<int32_t>::min());
+    DCHECK_LE(ret, std::numeric_limits<int32_t>::max());
+    return ret;
   }
 }
 
@@ -161,7 +166,7 @@
   if (location.IsRegister()) {
     return vixl::aarch32::Operand(RegisterFrom(location, type));
   } else {
-    return vixl::aarch32::Operand(Int64ConstantFrom(location));
+    return vixl::aarch32::Operand(Int32ConstantFrom(location));
   }
 }
 
diff --git a/compiler/optimizing/escape.cc b/compiler/optimizing/escape.cc
new file mode 100644
index 0000000..c80e19e
--- /dev/null
+++ b/compiler/optimizing/escape.cc
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+#include "escape.h"
+
+#include "nodes.h"
+
+namespace art {
+
+void CalculateEscape(HInstruction* reference,
+                     bool (*no_escape)(HInstruction*, HInstruction*),
+                     /*out*/ bool* is_singleton,
+                     /*out*/ bool* is_singleton_and_non_escaping) {
+  // For references not allocated in the method, don't assume anything.
+  if (!reference->IsNewInstance() && !reference->IsNewArray()) {
+    *is_singleton = false;
+    *is_singleton_and_non_escaping = false;
+    return;
+  }
+  // Assume the best until proven otherwise.
+  *is_singleton = true;
+  *is_singleton_and_non_escaping = true;
+  // Visit all uses to determine if this reference can escape into the heap,
+  // a method call, an alias, etc.
+  for (const HUseListNode<HInstruction*>& use : reference->GetUses()) {
+    HInstruction* user = use.GetUser();
+    if (no_escape != nullptr && (*no_escape)(reference, user)) {
+      // Client supplied analysis says there is no escape.
+      continue;
+    } else if (user->IsBoundType() || user->IsNullCheck()) {
+      // BoundType shouldn't normally be necessary for an allocation. Just be conservative
+      // for the uncommon cases. Similarly, null checks are eventually eliminated for explicit
+      // allocations, but if we see one before it is simplified, assume an alias.
+      *is_singleton = false;
+      *is_singleton_and_non_escaping = false;
+      return;
+    } else if (user->IsPhi() || user->IsSelect() || user->IsInvoke() ||
+               (user->IsInstanceFieldSet() && (reference == user->InputAt(1))) ||
+               (user->IsUnresolvedInstanceFieldSet() && (reference == user->InputAt(1))) ||
+               (user->IsStaticFieldSet() && (reference == user->InputAt(1))) ||
+               (user->IsUnresolvedStaticFieldSet() && (reference == user->InputAt(0))) ||
+               (user->IsArraySet() && (reference == user->InputAt(2)))) {
+      // The reference is merged to HPhi/HSelect, passed to a callee, or stored to heap.
+      // Hence, the reference is no longer the only name that can refer to its value.
+      *is_singleton = false;
+      *is_singleton_and_non_escaping = false;
+      return;
+    } else if ((user->IsUnresolvedInstanceFieldGet() && (reference == user->InputAt(0))) ||
+               (user->IsUnresolvedInstanceFieldSet() && (reference == user->InputAt(0)))) {
+      // The field is accessed in an unresolved way. We mark the object as a non-singleton.
+      // Note that we could optimize this case and still perform some optimizations until
+      // we hit the unresolved access, but the conservative assumption is the simplest.
+      *is_singleton = false;
+      *is_singleton_and_non_escaping = false;
+      return;
+    } else if (user->IsReturn()) {
+      *is_singleton_and_non_escaping = false;
+    }
+  }
+
+  // Need for further analysis?
+  if (!*is_singleton_and_non_escaping) {
+    return;
+  }
+
+  // Look at the environment uses and if it's for HDeoptimize, it's treated the
+  // same as a return which escapes at the end of executing the compiled code.
+  // Other environment uses are fine, as long as all client optimizations that
+  // rely on this informations are disabled for debuggable.
+  for (const HUseListNode<HEnvironment*>& use : reference->GetEnvUses()) {
+    HEnvironment* user = use.GetUser();
+    if (user->GetHolder()->IsDeoptimize()) {
+      *is_singleton_and_non_escaping = false;
+      break;
+    }
+  }
+}
+
+bool IsNonEscapingSingleton(HInstruction* reference,
+                            bool (*no_escape)(HInstruction*, HInstruction*)) {
+  bool is_singleton = true;
+  bool is_singleton_and_non_escaping = true;
+  CalculateEscape(reference, no_escape, &is_singleton, &is_singleton_and_non_escaping);
+  return is_singleton_and_non_escaping;
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/escape.h b/compiler/optimizing/escape.h
new file mode 100644
index 0000000..6514843
--- /dev/null
+++ b/compiler/optimizing/escape.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_ESCAPE_H_
+#define ART_COMPILER_OPTIMIZING_ESCAPE_H_
+
+namespace art {
+
+class HInstruction;
+
+/*
+ * Methods related to escape analysis, i.e. determining whether an object
+ * allocation is visible outside ('escapes') its immediate method context.
+ */
+
+/*
+ * Performs escape analysis on the given instruction, typically a reference to an
+ * allocation. The method assigns true to parameter 'is_singleton' if the reference
+ * is the only name that can refer to its value during the lifetime of the method,
+ * meaning that the reference is not aliased with something else, is not stored to
+ * heap memory, and not passed to another method. The method assigns true to parameter
+ * 'is_singleton_and_non_escaping' if the reference is a singleton and is not returned
+ * to the caller or used as an environment local of an HDeoptimize instruction.
+ *
+ * When set, the no_escape function is applied to any use of the allocation instruction
+ * prior to any built-in escape analysis. This allows clients to define better escape
+ * analysis in certain case-specific circumstances. If 'no_escape(reference, user)'
+ * returns true, the user is assumed *not* to cause any escape right away. The return
+ * value false means the client cannot provide a definite answer and built-in escape
+ * analysis is applied to the user instead.
+ */
+void CalculateEscape(HInstruction* reference,
+                     bool (*no_escape)(HInstruction*, HInstruction*),
+                     /*out*/ bool* is_singleton,
+                     /*out*/ bool* is_singleton_and_non_escaping);
+
+/*
+ * Convenience method for testing singleton and non-escaping property at once.
+ * Callers should be aware that this method invokes the full analysis at each call.
+ */
+bool IsNonEscapingSingleton(HInstruction* reference,
+                            bool (*no_escape)(HInstruction*, HInstruction*));
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_ESCAPE_H_
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 16a465a..01e89bb 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -258,6 +258,40 @@
   ProfilingInfo* const profiling_info_;
 };
 
+static bool IsMonomorphic(Handle<mirror::ObjectArray<mirror::Class>> classes)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK_GE(InlineCache::kIndividualCacheSize, 2);
+  return classes->Get(0) != nullptr && classes->Get(1) == nullptr;
+}
+
+static bool IsMegamorphic(Handle<mirror::ObjectArray<mirror::Class>> classes)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) {
+    if (classes->Get(i) == nullptr) {
+      return false;
+    }
+  }
+  return true;
+}
+
+static mirror::Class* GetMonomorphicType(Handle<mirror::ObjectArray<mirror::Class>> classes)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK(classes->Get(0) != nullptr);
+  return classes->Get(0);
+}
+
+static bool IsUninitialized(Handle<mirror::ObjectArray<mirror::Class>> classes)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return classes->Get(0) == nullptr;
+}
+
+static bool IsPolymorphic(Handle<mirror::ObjectArray<mirror::Class>> classes)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK_GE(InlineCache::kIndividualCacheSize, 3);
+  return classes->Get(1) != nullptr &&
+      classes->Get(InlineCache::kIndividualCacheSize - 1) == nullptr;
+}
+
 bool HInliner::TryInline(HInvoke* invoke_instruction) {
   if (invoke_instruction->IsInvokeUnresolved()) {
     return false;  // Don't bother to move further if we know the method is unresolved.
@@ -301,31 +335,48 @@
     ScopedProfilingInfoInlineUse spiis(caller, soa.Self());
     ProfilingInfo* profiling_info = spiis.GetProfilingInfo();
     if (profiling_info != nullptr) {
-      const InlineCache& ic = *profiling_info->GetInlineCache(invoke_instruction->GetDexPc());
-      if (ic.IsUninitialized()) {
-        VLOG(compiler) << "Interface or virtual call to "
-                       << caller_dex_file.PrettyMethod(method_index)
-                       << " is not hit and not inlined";
+      StackHandleScope<1> hs(soa.Self());
+      ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
+      Handle<mirror::ObjectArray<mirror::Class>> inline_cache = hs.NewHandle(
+          mirror::ObjectArray<mirror::Class>::Alloc(
+              soa.Self(),
+              class_linker->GetClassRoot(ClassLinker::kClassArrayClass),
+              InlineCache::kIndividualCacheSize));
+      if (inline_cache.Get() == nullptr) {
+        // We got an OOME. Just clear the exception, and don't inline.
+        DCHECK(soa.Self()->IsExceptionPending());
+        soa.Self()->ClearException();
+        VLOG(compiler) << "Out of memory in the compiler when trying to inline";
         return false;
-      } else if (ic.IsMonomorphic()) {
-        MaybeRecordStat(kMonomorphicCall);
-        if (outermost_graph_->IsCompilingOsr()) {
-          // If we are compiling OSR, we pretend this call is polymorphic, as we may come from the
-          // interpreter and it may have seen different receiver types.
-          return TryInlinePolymorphicCall(invoke_instruction, resolved_method, ic);
-        } else {
-          return TryInlineMonomorphicCall(invoke_instruction, resolved_method, ic);
-        }
-      } else if (ic.IsPolymorphic()) {
-        MaybeRecordStat(kPolymorphicCall);
-        return TryInlinePolymorphicCall(invoke_instruction, resolved_method, ic);
       } else {
-        DCHECK(ic.IsMegamorphic());
-        VLOG(compiler) << "Interface or virtual call to "
-                       << caller_dex_file.PrettyMethod(method_index)
-                       << " is megamorphic and not inlined";
-        MaybeRecordStat(kMegamorphicCall);
-        return false;
+        Runtime::Current()->GetJit()->GetCodeCache()->CopyInlineCacheInto(
+            *profiling_info->GetInlineCache(invoke_instruction->GetDexPc()),
+            inline_cache);
+        if (IsUninitialized(inline_cache)) {
+          VLOG(compiler) << "Interface or virtual call to "
+                         << caller_dex_file.PrettyMethod(method_index)
+                         << " is not hit and not inlined";
+          return false;
+        } else if (IsMonomorphic(inline_cache)) {
+          MaybeRecordStat(kMonomorphicCall);
+          if (outermost_graph_->IsCompilingOsr()) {
+            // If we are compiling OSR, we pretend this call is polymorphic, as we may come from the
+            // interpreter and it may have seen different receiver types.
+            return TryInlinePolymorphicCall(invoke_instruction, resolved_method, inline_cache);
+          } else {
+            return TryInlineMonomorphicCall(invoke_instruction, resolved_method, inline_cache);
+          }
+        } else if (IsPolymorphic(inline_cache)) {
+          MaybeRecordStat(kPolymorphicCall);
+          return TryInlinePolymorphicCall(invoke_instruction, resolved_method, inline_cache);
+        } else {
+          DCHECK(IsMegamorphic(inline_cache));
+          VLOG(compiler) << "Interface or virtual call to "
+                         << caller_dex_file.PrettyMethod(method_index)
+                         << " is megamorphic and not inlined";
+          MaybeRecordStat(kMegamorphicCall);
+          return false;
+        }
       }
     }
   }
@@ -358,13 +409,13 @@
 
 bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction,
                                         ArtMethod* resolved_method,
-                                        const InlineCache& ic) {
+                                        Handle<mirror::ObjectArray<mirror::Class>> classes) {
   DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface())
       << invoke_instruction->DebugName();
 
   const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
   dex::TypeIndex class_index = FindClassIndexIn(
-      ic.GetMonomorphicType(), caller_dex_file, caller_compilation_unit_.GetDexCache());
+      GetMonomorphicType(classes), caller_dex_file, caller_compilation_unit_.GetDexCache());
   if (!class_index.IsValid()) {
     VLOG(compiler) << "Call to " << ArtMethod::PrettyMethod(resolved_method)
                    << " from inline cache is not inlined because its class is not"
@@ -375,11 +426,11 @@
   ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
   PointerSize pointer_size = class_linker->GetImagePointerSize();
   if (invoke_instruction->IsInvokeInterface()) {
-    resolved_method = ic.GetMonomorphicType()->FindVirtualMethodForInterface(
+    resolved_method = GetMonomorphicType(classes)->FindVirtualMethodForInterface(
         resolved_method, pointer_size);
   } else {
     DCHECK(invoke_instruction->IsInvokeVirtual());
-    resolved_method = ic.GetMonomorphicType()->FindVirtualMethodForVirtual(
+    resolved_method = GetMonomorphicType(classes)->FindVirtualMethodForVirtual(
         resolved_method, pointer_size);
   }
   DCHECK(resolved_method != nullptr);
@@ -393,7 +444,7 @@
 
   // We successfully inlined, now add a guard.
   bool is_referrer =
-      (ic.GetMonomorphicType() == outermost_graph_->GetArtMethod()->GetDeclaringClass());
+      (GetMonomorphicType(classes) == outermost_graph_->GetArtMethod()->GetDeclaringClass());
   AddTypeGuard(receiver,
                cursor,
                bb_cursor,
@@ -457,11 +508,11 @@
 
 bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction,
                                         ArtMethod* resolved_method,
-                                        const InlineCache& ic) {
+                                        Handle<mirror::ObjectArray<mirror::Class>> classes) {
   DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface())
       << invoke_instruction->DebugName();
 
-  if (TryInlinePolymorphicCallToSameTarget(invoke_instruction, resolved_method, ic)) {
+  if (TryInlinePolymorphicCallToSameTarget(invoke_instruction, resolved_method, classes)) {
     return true;
   }
 
@@ -472,16 +523,16 @@
   bool all_targets_inlined = true;
   bool one_target_inlined = false;
   for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) {
-    if (ic.GetTypeAt(i) == nullptr) {
+    if (classes->Get(i) == nullptr) {
       break;
     }
     ArtMethod* method = nullptr;
     if (invoke_instruction->IsInvokeInterface()) {
-      method = ic.GetTypeAt(i)->FindVirtualMethodForInterface(
+      method = classes->Get(i)->FindVirtualMethodForInterface(
           resolved_method, pointer_size);
     } else {
       DCHECK(invoke_instruction->IsInvokeVirtual());
-      method = ic.GetTypeAt(i)->FindVirtualMethodForVirtual(
+      method = classes->Get(i)->FindVirtualMethodForVirtual(
           resolved_method, pointer_size);
     }
 
@@ -490,20 +541,20 @@
     HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
 
     dex::TypeIndex class_index = FindClassIndexIn(
-        ic.GetTypeAt(i), caller_dex_file, caller_compilation_unit_.GetDexCache());
+        classes->Get(i), caller_dex_file, caller_compilation_unit_.GetDexCache());
     HInstruction* return_replacement = nullptr;
     if (!class_index.IsValid() ||
         !TryBuildAndInline(invoke_instruction, method, &return_replacement)) {
       all_targets_inlined = false;
     } else {
       one_target_inlined = true;
-      bool is_referrer = (ic.GetTypeAt(i) == outermost_graph_->GetArtMethod()->GetDeclaringClass());
+      bool is_referrer = (classes->Get(i) == outermost_graph_->GetArtMethod()->GetDeclaringClass());
 
       // If we have inlined all targets before, and this receiver is the last seen,
       // we deoptimize instead of keeping the original invoke instruction.
       bool deoptimize = all_targets_inlined &&
           (i != InlineCache::kIndividualCacheSize - 1) &&
-          (ic.GetTypeAt(i + 1) == nullptr);
+          (classes->Get(i + 1) == nullptr);
 
       if (outermost_graph_->IsCompilingOsr()) {
         // We do not support HDeoptimize in OSR methods.
@@ -618,9 +669,10 @@
       merge, original_invoke_block, /* replace_if_back_edge */ true);
 }
 
-bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction,
-                                                    ArtMethod* resolved_method,
-                                                    const InlineCache& ic) {
+bool HInliner::TryInlinePolymorphicCallToSameTarget(
+    HInvoke* invoke_instruction,
+    ArtMethod* resolved_method,
+    Handle<mirror::ObjectArray<mirror::Class>> classes) {
   // This optimization only works under JIT for now.
   DCHECK(Runtime::Current()->UseJitCompilation());
   if (graph_->GetInstructionSet() == kMips64) {
@@ -639,12 +691,12 @@
   // Check whether we are actually calling the same method among
   // the different types seen.
   for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) {
-    if (ic.GetTypeAt(i) == nullptr) {
+    if (classes->Get(i) == nullptr) {
       break;
     }
     ArtMethod* new_method = nullptr;
     if (invoke_instruction->IsInvokeInterface()) {
-      new_method = ic.GetTypeAt(i)->GetImt(pointer_size)->Get(
+      new_method = classes->Get(i)->GetImt(pointer_size)->Get(
           method_index, pointer_size);
       if (new_method->IsRuntimeMethod()) {
         // Bail out as soon as we see a conflict trampoline in one of the target's
@@ -653,7 +705,7 @@
       }
     } else {
       DCHECK(invoke_instruction->IsInvokeVirtual());
-      new_method = ic.GetTypeAt(i)->GetEmbeddedVTableEntry(method_index, pointer_size);
+      new_method = classes->Get(i)->GetEmbeddedVTableEntry(method_index, pointer_size);
     }
     DCHECK(new_method != nullptr);
     if (actual_method == nullptr) {
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 682393e..a2b4fc9 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -28,7 +28,6 @@
 class DexCompilationUnit;
 class HGraph;
 class HInvoke;
-class InlineCache;
 class OptimizingCompilerStats;
 
 class HInliner : public HOptimization {
@@ -105,18 +104,18 @@
   // ... // inlined code
   bool TryInlineMonomorphicCall(HInvoke* invoke_instruction,
                                 ArtMethod* resolved_method,
-                                const InlineCache& ic)
+                                Handle<mirror::ObjectArray<mirror::Class>> classes)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Try to inline targets of a polymorphic call.
   bool TryInlinePolymorphicCall(HInvoke* invoke_instruction,
                                 ArtMethod* resolved_method,
-                                const InlineCache& ic)
+                                Handle<mirror::ObjectArray<mirror::Class>> classes)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction,
                                             ArtMethod* resolved_method,
-                                            const InlineCache& ic)
+                                            Handle<mirror::ObjectArray<mirror::Class>> classes)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 451abc5..17a97da 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -2165,11 +2165,11 @@
   __ Cbz(dst, slow_path->GetEntryLabel());
 
   if (!length.IsConstant()) {
-    // If the length is negative, bail out.
-    __ Tbnz(WRegisterFrom(length), kWRegSize - 1, slow_path->GetEntryLabel());
-    // If the length > 32 then (currently) prefer libcore's native implementation.
+    // Merge the following two comparisons into one:
+    //   If the length is negative, bail out (delegate to libcore's native implementation).
+    //   If the length > 32 then (currently) prefer libcore's native implementation.
     __ Cmp(WRegisterFrom(length), kSystemArrayCopyCharThreshold);
-    __ B(slow_path->GetEntryLabel(), gt);
+    __ B(slow_path->GetEntryLabel(), hi);
   } else {
     // We have already checked in the LocationsBuilder for the constant case.
     DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0);
@@ -2379,11 +2379,11 @@
   if (!length.IsConstant() &&
       !optimizations.GetCountIsSourceLength() &&
       !optimizations.GetCountIsDestinationLength()) {
-    // If the length is negative, bail out.
-    __ Tbnz(WRegisterFrom(length), kWRegSize - 1, intrinsic_slow_path->GetEntryLabel());
-    // If the length >= 128 then (currently) prefer native implementation.
+    // Merge the following two comparisons into one:
+    //   If the length is negative, bail out (delegate to libcore's native implementation).
+    //   If the length >= 128 then (currently) prefer native implementation.
     __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold);
-    __ B(intrinsic_slow_path->GetEntryLabel(), ge);
+    __ B(intrinsic_slow_path->GetEntryLabel(), hs);
   }
   // Validity checks: source.
   CheckSystemArrayCopyPosition(masm,
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 110430f..c8e3534 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -303,7 +303,7 @@
     vixl32::Register in_reg_hi = HighRegisterFrom(in);
     vixl32::Label end;
     __ Clz(out, in_reg_hi);
-    __ Cbnz(in_reg_hi, &end);
+    __ CompareAndBranchIfNonZero(in_reg_hi, &end, /* far_target */ false);
     __ Clz(out, in_reg_lo);
     __ Add(out, out, 32);
     __ Bind(&end);
@@ -345,7 +345,7 @@
     vixl32::Label end;
     __ Rbit(out, in_reg_lo);
     __ Clz(out, out);
-    __ Cbnz(in_reg_lo, &end);
+    __ CompareAndBranchIfNonZero(in_reg_lo, &end, /* far_target */ false);
     __ Rbit(out, in_reg_hi);
     __ Clz(out, out);
     __ Add(out, out, 32);
@@ -518,7 +518,7 @@
 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) {
   ArmVIXLAssembler* assembler = GetAssembler();
   // Ignore upper 4B of long address.
-  __ Ldrsb(OutputRegister(invoke), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+  __ Ldrsb(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) {
@@ -528,7 +528,7 @@
 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) {
   ArmVIXLAssembler* assembler = GetAssembler();
   // Ignore upper 4B of long address.
-  __ Ldr(OutputRegister(invoke), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+  __ Ldr(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekLongNative(HInvoke* invoke) {
@@ -545,9 +545,9 @@
   vixl32::Register hi = HighRegisterFrom(invoke->GetLocations()->Out());
   if (addr.Is(lo)) {
     __ Ldr(hi, MemOperand(addr, 4));
-    __ Ldr(lo, addr);
+    __ Ldr(lo, MemOperand(addr));
   } else {
-    __ Ldr(lo, addr);
+    __ Ldr(lo, MemOperand(addr));
     __ Ldr(hi, MemOperand(addr, 4));
   }
 }
@@ -559,7 +559,7 @@
 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekShortNative(HInvoke* invoke) {
   ArmVIXLAssembler* assembler = GetAssembler();
   // Ignore upper 4B of long address.
-  __ Ldrsh(OutputRegister(invoke), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+  __ Ldrsh(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
 }
 
 static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
@@ -576,7 +576,7 @@
 
 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeByte(HInvoke* invoke) {
   ArmVIXLAssembler* assembler = GetAssembler();
-  __ Strb(InputRegisterAt(invoke, 1), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+  __ Strb(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) {
@@ -585,7 +585,7 @@
 
 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) {
   ArmVIXLAssembler* assembler = GetAssembler();
-  __ Str(InputRegisterAt(invoke, 1), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+  __ Str(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeLongNative(HInvoke* invoke) {
@@ -598,7 +598,7 @@
   vixl32::Register addr = LowRegisterFrom(invoke->GetLocations()->InAt(0));
   // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
   // exception. So we can't use ldrd as addr may be unaligned.
-  __ Str(LowRegisterFrom(invoke->GetLocations()->InAt(1)), addr);
+  __ Str(LowRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr));
   __ Str(HighRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr, 4));
 }
 
@@ -608,7 +608,7 @@
 
 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeShortNative(HInvoke* invoke) {
   ArmVIXLAssembler* assembler = GetAssembler();
-  __ Strh(InputRegisterAt(invoke, 1), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+  __ Strh(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitThreadCurrentThread(HInvoke* invoke) {
@@ -842,8 +842,8 @@
       __ Add(temp_reg, base, offset);
       vixl32::Label loop_head;
       __ Bind(&loop_head);
-      __ Ldrexd(temp_lo, temp_hi, temp_reg);
-      __ Strexd(temp_lo, value_lo, value_hi, temp_reg);
+      __ Ldrexd(temp_lo, temp_hi, MemOperand(temp_reg));
+      __ Strexd(temp_lo, value_lo, value_hi, MemOperand(temp_reg));
       __ Cmp(temp_lo, 0);
       __ B(ne, &loop_head);
     } else {
@@ -1042,7 +1042,7 @@
   vixl32::Label loop_head;
   __ Bind(&loop_head);
 
-  __ Ldrex(tmp, tmp_ptr);
+  __ Ldrex(tmp, MemOperand(tmp_ptr));
 
   __ Subs(tmp, tmp, expected);
 
@@ -1052,7 +1052,7 @@
                                CodeBufferCheckScope::kMaximumSize);
 
     __ itt(eq);
-    __ strex(eq, tmp, value, tmp_ptr);
+    __ strex(eq, tmp, value, MemOperand(tmp_ptr));
     __ cmp(eq, tmp, 1);
   }
 
@@ -1158,7 +1158,7 @@
   if (can_slow_path) {
     slow_path = new (GetAllocator()) IntrinsicSlowPathARMVIXL(invoke);
     codegen_->AddSlowPath(slow_path);
-    __ Cbz(arg, slow_path->GetEntryLabel());
+    __ CompareAndBranchIfZero(arg, slow_path->GetEntryLabel());
   }
 
   // Reference equality check, return 0 if same reference.
@@ -1191,7 +1191,9 @@
   }
 
   // Shorter string is empty?
-  __ Cbz(temp0, &end);
+  // Note that mirror::kUseStringCompression==true introduces lots of instructions,
+  // which makes &end label far away from this branch and makes it not 'CBZ-encodable'.
+  __ CompareAndBranchIfZero(temp0, &end, mirror::kUseStringCompression);
 
   if (mirror::kUseStringCompression) {
     // Check if both strings using same compression style to use this comparison loop.
@@ -1218,7 +1220,7 @@
   static_assert(IsAligned<8>(kObjectAlignment),
                 "String data must be 8-byte aligned for unrolled CompareTo loop.");
 
-  const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
+  const unsigned char_size = Primitive::ComponentSize(Primitive::kPrimChar);
   DCHECK_EQ(char_size, 2u);
 
   UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
@@ -1414,7 +1416,7 @@
   StringEqualsOptimizations optimizations(invoke);
   if (!optimizations.GetArgumentNotNull()) {
     // Check if input is null, return false if it is.
-    __ Cbz(arg, &return_false);
+    __ CompareAndBranchIfZero(arg, &return_false, /* far_target */ false);
   }
 
   // Reference equality check, return true if same reference.
@@ -1442,7 +1444,7 @@
   // Return true if both strings are empty. Even with string compression `count == 0` means empty.
   static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
                 "Expecting 0=compressed, 1=uncompressed");
-  __ Cbz(temp, &return_true);
+  __ CompareAndBranchIfZero(temp, &return_true, /* far_target */ false);
 
   // Assertions that must hold in order to compare strings 4 bytes at a time.
   DCHECK_ALIGNED(value_offset, 4);
@@ -1467,7 +1469,7 @@
   __ Bind(&loop);
   __ Ldr(out, MemOperand(str, temp1));
   __ Ldr(temp2, MemOperand(arg, temp1));
-  __ Add(temp1, temp1, sizeof(uint32_t));
+  __ Add(temp1, temp1, Operand::From(sizeof(uint32_t)));
   __ Cmp(out, temp2);
   __ B(ne, &return_false);
   // With string compression, we have compared 4 bytes, otherwise 2 chars.
@@ -1718,7 +1720,7 @@
   } else if (length_is_input_length) {
     // The only way the copy can succeed is if pos is zero.
     vixl32::Register pos_reg = RegisterFrom(pos);
-    __ Cbnz(pos_reg, slow_path->GetEntryLabel());
+    __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel());
   } else {
     // Check that pos >= 0.
     vixl32::Register pos_reg = RegisterFrom(pos);
@@ -1815,12 +1817,12 @@
 
   if (!optimizations.GetSourceIsNotNull()) {
     // Bail out if the source is null.
-    __ Cbz(src, intrinsic_slow_path->GetEntryLabel());
+    __ CompareAndBranchIfZero(src, intrinsic_slow_path->GetEntryLabel());
   }
 
   if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
     // Bail out if the destination is null.
-    __ Cbz(dest, intrinsic_slow_path->GetEntryLabel());
+    __ CompareAndBranchIfZero(dest, intrinsic_slow_path->GetEntryLabel());
   }
 
   // If the length is negative, bail out.
@@ -1865,13 +1867,13 @@
         // /* HeapReference<Class> */ temp1 = temp1->component_type_
         codegen_->GenerateFieldLoadWithBakerReadBarrier(
             invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
-        __ Cbz(temp1, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfZero(temp1, intrinsic_slow_path->GetEntryLabel());
         // If heap poisoning is enabled, `temp1` has been unpoisoned
         // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
         // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
         __ Ldrh(temp1, MemOperand(temp1, primitive_offset));
         static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-        __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
       }
 
       // /* HeapReference<Class> */ temp1 = dest->klass_
@@ -1889,13 +1891,13 @@
         // /* HeapReference<Class> */ temp2 = temp1->component_type_
         codegen_->GenerateFieldLoadWithBakerReadBarrier(
             invoke, temp2_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false);
-        __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfZero(temp2, intrinsic_slow_path->GetEntryLabel());
         // If heap poisoning is enabled, `temp2` has been unpoisoned
         // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
         // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
         __ Ldrh(temp2, MemOperand(temp2, primitive_offset));
         static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-        __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfNonZero(temp2, intrinsic_slow_path->GetEntryLabel());
       }
 
       // For the same reason given earlier, `temp1` is not trashed by the
@@ -1918,7 +1920,7 @@
         // comparison with null below, and this reference is not
         // kept afterwards.
         __ Ldr(temp1, MemOperand(temp1, super_offset));
-        __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
         __ Bind(&do_copy);
       } else {
         __ B(ne, intrinsic_slow_path->GetEntryLabel());
@@ -1944,24 +1946,24 @@
         // Bail out if the destination is not a non primitive array.
         // /* HeapReference<Class> */ temp3 = temp1->component_type_
         __ Ldr(temp3, MemOperand(temp1, component_offset));
-        __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
         assembler->MaybeUnpoisonHeapReference(temp3);
         // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
         __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
         static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-        __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
       }
 
       if (!optimizations.GetSourceIsNonPrimitiveArray()) {
         // Bail out if the source is not a non primitive array.
         // /* HeapReference<Class> */ temp3 = temp2->component_type_
         __ Ldr(temp3, MemOperand(temp2, component_offset));
-        __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
         assembler->MaybeUnpoisonHeapReference(temp3);
         // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
         __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
         static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-        __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
       }
 
       __ Cmp(temp1, temp2);
@@ -1978,7 +1980,7 @@
         // /* HeapReference<Class> */ temp1 = temp1->super_class_
         __ Ldr(temp1, MemOperand(temp1, super_offset));
         // No need to unpoison the result, we're comparing against null.
-        __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
         __ Bind(&do_copy);
       } else {
         __ B(ne, intrinsic_slow_path->GetEntryLabel());
@@ -1994,7 +1996,7 @@
       // /* HeapReference<Class> */ temp3 = temp1->component_type_
       codegen_->GenerateFieldLoadWithBakerReadBarrier(
           invoke, temp3_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
-      __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
+      __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
       // If heap poisoning is enabled, `temp3` has been unpoisoned
       // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
     } else {
@@ -2003,13 +2005,13 @@
       assembler->MaybeUnpoisonHeapReference(temp1);
       // /* HeapReference<Class> */ temp3 = temp1->component_type_
       __ Ldr(temp3, MemOperand(temp1, component_offset));
-      __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
+      __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
       assembler->MaybeUnpoisonHeapReference(temp3);
     }
     // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
     __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
     static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-    __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
+    __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
   }
 
   int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 15e6059..edecf17 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -15,6 +15,8 @@
  */
 
 #include "load_store_elimination.h"
+
+#include "escape.h"
 #include "side_effects_analysis.h"
 
 #include <iostream>
@@ -31,70 +33,12 @@
 // whether it's a singleton, returned, etc.
 class ReferenceInfo : public ArenaObject<kArenaAllocMisc> {
  public:
-  ReferenceInfo(HInstruction* reference, size_t pos) : reference_(reference), position_(pos) {
-    is_singleton_ = true;
-    is_singleton_and_non_escaping_ = true;
-    if (!reference_->IsNewInstance() && !reference_->IsNewArray()) {
-      // For references not allocated in the method, don't assume anything.
-      is_singleton_ = false;
-      is_singleton_and_non_escaping_ = false;
-      return;
-    }
-
-    // Visit all uses to determine if this reference can spread into the heap,
-    // a method call, etc.
-    for (const HUseListNode<HInstruction*>& use : reference_->GetUses()) {
-      HInstruction* user = use.GetUser();
-      DCHECK(!user->IsNullCheck()) << "NullCheck should have been eliminated";
-      if (user->IsBoundType()) {
-        // BoundType shouldn't normally be necessary for a NewInstance.
-        // Just be conservative for the uncommon cases.
-        is_singleton_ = false;
-        is_singleton_and_non_escaping_ = false;
-        return;
-      }
-      if (user->IsPhi() || user->IsSelect() || user->IsInvoke() ||
-          (user->IsInstanceFieldSet() && (reference_ == user->InputAt(1))) ||
-          (user->IsUnresolvedInstanceFieldSet() && (reference_ == user->InputAt(1))) ||
-          (user->IsStaticFieldSet() && (reference_ == user->InputAt(1))) ||
-          (user->IsUnresolvedStaticFieldSet() && (reference_ == user->InputAt(0))) ||
-          (user->IsArraySet() && (reference_ == user->InputAt(2)))) {
-        // reference_ is merged to HPhi/HSelect, passed to a callee, or stored to heap.
-        // reference_ isn't the only name that can refer to its value anymore.
-        is_singleton_ = false;
-        is_singleton_and_non_escaping_ = false;
-        return;
-      }
-      if ((user->IsUnresolvedInstanceFieldGet() && (reference_ == user->InputAt(0))) ||
-          (user->IsUnresolvedInstanceFieldSet() && (reference_ == user->InputAt(0)))) {
-        // The field is accessed in an unresolved way. We mark the object as a non-singleton
-        // to disable load/store optimizations on it.
-        // Note that we could optimize this case and still perform some optimizations until
-        // we hit the unresolved access, but disabling is the simplest.
-        is_singleton_ = false;
-        is_singleton_and_non_escaping_ = false;
-        return;
-      }
-      if (user->IsReturn()) {
-        is_singleton_and_non_escaping_ = false;
-      }
-    }
-
-    if (!is_singleton_ || !is_singleton_and_non_escaping_) {
-      return;
-    }
-
-    // Look at Environment uses and if it's for HDeoptimize, it's treated the same
-    // as a return which escapes at the end of executing the compiled code. We don't
-    // do store elimination for singletons that escape through HDeoptimize.
-    // Other Environment uses are fine since LSE is disabled for debuggable.
-    for (const HUseListNode<HEnvironment*>& use : reference_->GetEnvUses()) {
-      HEnvironment* user = use.GetUser();
-      if (user->GetHolder()->IsDeoptimize()) {
-        is_singleton_and_non_escaping_ = false;
-        break;
-      }
-    }
+  ReferenceInfo(HInstruction* reference, size_t pos)
+      : reference_(reference),
+        position_(pos),
+        is_singleton_(true),
+        is_singleton_and_non_escaping_(true) {
+    CalculateEscape(reference_, nullptr, &is_singleton_, &is_singleton_and_non_escaping_);
   }
 
   HInstruction* GetReference() const {
diff --git a/compiler/utils/arm/assembler_arm_vixl.cc b/compiler/utils/arm/assembler_arm_vixl.cc
index e3b9fb6..c35c393 100644
--- a/compiler/utils/arm/assembler_arm_vixl.cc
+++ b/compiler/utils/arm/assembler_arm_vixl.cc
@@ -43,12 +43,12 @@
 }
 
 const uint8_t* ArmVIXLAssembler::CodeBufferBaseAddress() const {
-  return vixl_masm_.GetStartAddress<uint8_t*>();
+  return vixl_masm_.GetBuffer().GetStartAddress<const uint8_t*>();
 }
 
 void ArmVIXLAssembler::FinalizeInstructions(const MemoryRegion& region) {
   // Copy the instructions from the buffer.
-  MemoryRegion from(vixl_masm_.GetStartAddress<void*>(), CodeSize());
+  MemoryRegion from(vixl_masm_.GetBuffer()->GetStartAddress<void*>(), CodeSize());
   region.CopyFrom(0, from);
 }
 
@@ -365,7 +365,7 @@
       if (stack_offset != 0) {
         base = temps.Acquire();
         DCHECK_EQ(regs & (1u << base.GetCode()), 0u);
-        ___ Add(base, sp, stack_offset);
+        ___ Add(base, sp, Operand::From(stack_offset));
       }
       ___ Stm(base, NO_WRITE_BACK, RegisterList(regs));
     } else {
@@ -385,7 +385,7 @@
       vixl32::Register base = sp;
       if (stack_offset != 0) {
         base = temps.Acquire();
-        ___ Add(base, sp, stack_offset);
+        ___ Add(base, sp, Operand::From(stack_offset));
       }
       ___ Ldm(base, NO_WRITE_BACK, RegisterList(regs));
     } else {
@@ -429,5 +429,31 @@
   }
 }
 
+void ArmVIXLMacroAssembler::CompareAndBranchIfZero(vixl32::Register rn,
+                                                   vixl32::Label* label,
+                                                   bool is_far_target) {
+  if (!is_far_target && rn.IsLow() && !label->IsBound()) {
+    // In T32, Cbz/Cbnz instructions have following limitations:
+    // - There are only 7 bits (i:imm5:0) to encode branch target address (cannot be far target).
+    // - Only low registers (i.e R0 .. R7) can be encoded.
+    // - Only forward branches (unbound labels) are supported.
+    Cbz(rn, label);
+    return;
+  }
+  Cmp(rn, 0);
+  B(eq, label);
+}
+
+void ArmVIXLMacroAssembler::CompareAndBranchIfNonZero(vixl32::Register rn,
+                                                      vixl32::Label* label,
+                                                      bool is_far_target) {
+  if (!is_far_target && rn.IsLow() && !label->IsBound()) {
+    Cbnz(rn, label);
+    return;
+  }
+  Cmp(rn, 0);
+  B(ne, label);
+}
+
 }  // namespace arm
 }  // namespace art
diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h
index e020628..b4a4abc 100644
--- a/compiler/utils/arm/assembler_arm_vixl.h
+++ b/compiler/utils/arm/assembler_arm_vixl.h
@@ -37,6 +37,25 @@
 namespace art {
 namespace arm {
 
+class ArmVIXLMacroAssembler FINAL : public vixl32::MacroAssembler {
+ public:
+  // The following interfaces can generate CMP+Bcc or Cbz/Cbnz.
+  // CMP+Bcc are generated by default.
+  // If a hint is given (is_far_target = false) and rn and label can all fit into Cbz/Cbnz,
+  // then Cbz/Cbnz is generated.
+  // Prefer following interfaces to using vixl32::MacroAssembler::Cbz/Cbnz.
+  // In T32, Cbz/Cbnz instructions have following limitations:
+  // - Far targets, which are over 126 bytes away, are not supported.
+  // - Only low registers can be encoded.
+  // - Backward branches are not supported.
+  void CompareAndBranchIfZero(vixl32::Register rn,
+                              vixl32::Label* label,
+                              bool is_far_target = true);
+  void CompareAndBranchIfNonZero(vixl32::Register rn,
+                                 vixl32::Label* label,
+                                 bool is_far_target = true);
+};
+
 class ArmVIXLAssembler FINAL : public Assembler {
  private:
   class ArmException;
@@ -48,7 +67,7 @@
   }
 
   virtual ~ArmVIXLAssembler() {}
-  vixl32::MacroAssembler* GetVIXLAssembler() { return &vixl_masm_; }
+  ArmVIXLMacroAssembler* GetVIXLAssembler() { return &vixl_masm_; }
   void FinalizeCode() OVERRIDE;
 
   // Size of generated code.
@@ -117,7 +136,7 @@
 
  private:
   // VIXL assembler.
-  vixl32::MacroAssembler vixl_masm_;
+  ArmVIXLMacroAssembler vixl_masm_;
 };
 
 // Thread register declaration.
diff --git a/compiler/utils/arm/jni_macro_assembler_arm.cc b/compiler/utils/arm/jni_macro_assembler_arm.cc
index cf7a4d1..3f425df 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm.cc
@@ -594,6 +594,41 @@
   __ b(slow->Entry(), NE);
 }
 
+std::unique_ptr<JNIMacroLabel> ArmJNIMacroAssembler::CreateLabel() {
+  return std::unique_ptr<JNIMacroLabel>(new ArmJNIMacroLabel());
+}
+
+void ArmJNIMacroAssembler::Jump(JNIMacroLabel* label) {
+  CHECK(label != nullptr);
+  __ b(ArmJNIMacroLabel::Cast(label)->AsArm());
+}
+
+void ArmJNIMacroAssembler::Jump(JNIMacroLabel* label,
+                                JNIMacroUnaryCondition condition,
+                                ManagedRegister test) {
+  CHECK(label != nullptr);
+
+  arm::Condition arm_cond;
+  switch (condition) {
+    case JNIMacroUnaryCondition::kZero:
+      arm_cond = EQ;
+      break;
+    case JNIMacroUnaryCondition::kNotZero:
+      arm_cond = NE;
+      break;
+    default:
+      LOG(FATAL) << "Not implemented condition: " << static_cast<int>(condition);
+      UNREACHABLE();
+  }
+  __ cmp(test.AsArm().AsCoreRegister(), ShifterOperand(0));
+  __ b(ArmJNIMacroLabel::Cast(label)->AsArm(), arm_cond);
+}
+
+void ArmJNIMacroAssembler::Bind(JNIMacroLabel* label) {
+  CHECK(label != nullptr);
+  __ Bind(ArmJNIMacroLabel::Cast(label)->AsArm());
+}
+
 #undef __
 
 void ArmExceptionSlowPath::Emit(Assembler* sasm) {
diff --git a/compiler/utils/arm/jni_macro_assembler_arm.h b/compiler/utils/arm/jni_macro_assembler_arm.h
index 4471906..809ac8b 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm.h
+++ b/compiler/utils/arm/jni_macro_assembler_arm.h
@@ -25,6 +25,7 @@
 #include "base/enums.h"
 #include "base/macros.h"
 #include "utils/jni_macro_assembler.h"
+#include "utils/label.h"
 #include "offsets.h"
 
 namespace art {
@@ -159,10 +160,26 @@
 
   void MemoryBarrier(ManagedRegister scratch) OVERRIDE;
 
+  // Create a new label that can be used with Jump/Bind calls.
+  std::unique_ptr<JNIMacroLabel> CreateLabel() OVERRIDE;
+  // Emit an unconditional jump to the label.
+  void Jump(JNIMacroLabel* label) OVERRIDE;
+  // Emit a conditional jump to the label by applying a unary condition test to the register.
+  void Jump(JNIMacroLabel* label, JNIMacroUnaryCondition cond, ManagedRegister test) OVERRIDE;
+  // Code at this offset will serve as the target for the Jump call.
+  void Bind(JNIMacroLabel* label) OVERRIDE;
+
  private:
   std::unique_ptr<ArmAssembler> asm_;
 };
 
+class ArmJNIMacroLabel FINAL : public JNIMacroLabelCommon<ArmJNIMacroLabel, art::Label, kArm> {
+ public:
+  art::Label* AsArm() {
+    return AsPlatformLabel();
+  }
+};
+
 }  // namespace arm
 }  // namespace art
 
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index 23b2774..fb6f172 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -168,6 +168,8 @@
     CHECK_EQ(0u, size);
   } else if (src.IsCoreRegister()) {
     CHECK_EQ(4u, size);
+    UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+    temps.Exclude(src.AsVIXLRegister());
     asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value());
   } else if (src.IsRegisterPair()) {
     CHECK_EQ(8u, size);
@@ -186,12 +188,16 @@
 void ArmVIXLJNIMacroAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
   ArmManagedRegister src = msrc.AsArm();
   CHECK(src.IsCoreRegister()) << src;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(src.AsVIXLRegister());
   asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value());
 }
 
 void ArmVIXLJNIMacroAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
   ArmManagedRegister src = msrc.AsArm();
   CHECK(src.IsCoreRegister()) << src;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(src.AsVIXLRegister());
   asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value());
 }
 
@@ -202,6 +208,8 @@
   ArmManagedRegister src = msrc.AsArm();
   ArmManagedRegister scratch = mscratch.AsArm();
   asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value());
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(scratch.AsVIXLRegister());
   asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, in_off.Int32Value());
   asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, dest.Int32Value() + 4);
 }
@@ -210,6 +218,8 @@
                                        FrameOffset src,
                                        ManagedRegister mscratch) {
   ArmManagedRegister scratch = mscratch.AsArm();
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(scratch.AsVIXLRegister());
   asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, src.Int32Value());
   asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, dest.Int32Value());
 }
@@ -220,6 +230,8 @@
                                        bool unpoison_reference) {
   ArmManagedRegister dst = dest.AsArm();
   CHECK(dst.IsCoreRegister() && dst.IsCoreRegister()) << dst;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(dst.AsVIXLRegister(), base.AsArm().AsVIXLRegister());
   asm_.LoadFromOffset(kLoadWord,
                       dst.AsVIXLRegister(),
                       base.AsArm().AsVIXLRegister(),
@@ -246,6 +258,8 @@
                                                      ManagedRegister scratch) {
   ArmManagedRegister mscratch = scratch.AsArm();
   CHECK(mscratch.IsCoreRegister()) << mscratch;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(mscratch.AsVIXLRegister());
   asm_.LoadImmediate(mscratch.AsVIXLRegister(), imm);
   asm_.StoreToOffset(kStoreWord, mscratch.AsVIXLRegister(), sp, dest.Int32Value());
 }
@@ -263,6 +277,8 @@
 void ArmVIXLJNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister m_dst, ThreadOffset32 offs) {
   ArmManagedRegister dst = m_dst.AsArm();
   CHECK(dst.IsCoreRegister()) << dst;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(dst.AsVIXLRegister());
   asm_.LoadFromOffset(kLoadWord, dst.AsVIXLRegister(), tr, offs.Int32Value());
 }
 
@@ -271,6 +287,8 @@
                                                     ManagedRegister mscratch) {
   ArmManagedRegister scratch = mscratch.AsArm();
   CHECK(scratch.IsCoreRegister()) << scratch;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(scratch.AsVIXLRegister());
   asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), tr, thr_offs.Int32Value());
   asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, fr_offs.Int32Value());
 }
@@ -286,6 +304,8 @@
                                                         ManagedRegister mscratch) {
   ArmManagedRegister scratch = mscratch.AsArm();
   CHECK(scratch.IsCoreRegister()) << scratch;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(scratch.AsVIXLRegister());
   asm_.AddConstant(scratch.AsVIXLRegister(), sp, fr_offs.Int32Value());
   asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), tr, thr_offs.Int32Value());
 }
@@ -312,6 +332,8 @@
   if (!dst.Equals(src)) {
     if (dst.IsCoreRegister()) {
       CHECK(src.IsCoreRegister()) << src;
+      UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+      temps.Exclude(dst.AsVIXLRegister());
       ___ Mov(dst.AsVIXLRegister(), src.AsVIXLRegister());
     } else if (dst.IsDRegister()) {
       if (src.IsDRegister()) {
@@ -351,6 +373,8 @@
   ArmManagedRegister temp = scratch.AsArm();
   CHECK(temp.IsCoreRegister()) << temp;
   CHECK(size == 4 || size == 8) << size;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(temp.AsVIXLRegister());
   if (size == 4) {
     asm_.LoadFromOffset(kLoadWord, temp.AsVIXLRegister(), sp, src.Int32Value());
     asm_.StoreToOffset(kStoreWord, temp.AsVIXLRegister(), sp, dest.Int32Value());
@@ -414,6 +438,8 @@
   ArmManagedRegister in_reg = min_reg.AsArm();
   CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
   CHECK(out_reg.IsCoreRegister()) << out_reg;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(out_reg.AsVIXLRegister());
   if (null_allowed) {
     // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
     // the address in the handle scope holding the reference.
@@ -425,6 +451,8 @@
                           handle_scope_offset.Int32Value());
       in_reg = out_reg;
     }
+
+    temps.Exclude(in_reg.AsVIXLRegister());
     ___ Cmp(in_reg.AsVIXLRegister(), 0);
 
     if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value(), kCcDontCare)) {
@@ -457,6 +485,8 @@
                                                       bool null_allowed) {
   ArmManagedRegister scratch = mscratch.AsArm();
   CHECK(scratch.IsCoreRegister()) << scratch;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(scratch.AsVIXLRegister());
   if (null_allowed) {
     asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value());
     // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
@@ -503,6 +533,8 @@
   ArmManagedRegister scratch = mscratch.AsArm();
   CHECK(base.IsCoreRegister()) << base;
   CHECK(scratch.IsCoreRegister()) << scratch;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(scratch.AsVIXLRegister());
   asm_.LoadFromOffset(kLoadWord,
                       scratch.AsVIXLRegister(),
                       base.AsVIXLRegister(),
@@ -514,6 +546,8 @@
 void ArmVIXLJNIMacroAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
   ArmManagedRegister scratch = mscratch.AsArm();
   CHECK(scratch.IsCoreRegister()) << scratch;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(scratch.AsVIXLRegister());
   // Call *(*(SP + base) + offset)
   asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, base.Int32Value());
   asm_.LoadFromOffset(kLoadWord,
@@ -530,6 +564,8 @@
 }
 
 void ArmVIXLJNIMacroAssembler::GetCurrentThread(ManagedRegister mtr) {
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(mtr.AsArm().AsVIXLRegister());
   ___ Mov(mtr.AsArm().AsVIXLRegister(), tr);
 }
 
@@ -541,6 +577,8 @@
 void ArmVIXLJNIMacroAssembler::ExceptionPoll(ManagedRegister m_scratch, size_t stack_adjust) {
   CHECK_ALIGNED(stack_adjust, kStackAlignment);
   ArmManagedRegister scratch = m_scratch.AsArm();
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(scratch.AsVIXLRegister());
   exception_blocks_.emplace_back(
       new ArmVIXLJNIMacroAssembler::ArmException(scratch, stack_adjust));
   asm_.LoadFromOffset(kLoadWord,
@@ -572,12 +610,16 @@
                                     ManagedRegister test) {
   CHECK(label != nullptr);
 
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(test.AsArm().AsVIXLRegister());
   switch (condition) {
     case JNIMacroUnaryCondition::kZero:
-      ___ Cbz(test.AsArm().AsVIXLRegister(), ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
+      ___ CompareAndBranchIfZero(test.AsArm().AsVIXLRegister(),
+                                 ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
       break;
     case JNIMacroUnaryCondition::kNotZero:
-      ___ Cbnz(test.AsArm().AsVIXLRegister(), ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
+      ___ CompareAndBranchIfNonZero(test.AsArm().AsVIXLRegister(),
+                                    ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
       break;
     default:
       LOG(FATAL) << "Not implemented unary condition: " << static_cast<int>(condition);
@@ -596,11 +638,14 @@
   if (exception->stack_adjust_ != 0) {  // Fix up the frame.
     DecreaseFrameSize(exception->stack_adjust_);
   }
+
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(exception->scratch_.AsVIXLRegister());
   // Pass exception object as argument.
   // Don't care about preserving r0 as this won't return.
   ___ Mov(r0, exception->scratch_.AsVIXLRegister());
+  temps.Include(exception->scratch_.AsVIXLRegister());
   // TODO: check that exception->scratch_ is dead by this point.
-  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
   vixl32::Register temp = temps.Acquire();
   ___ Ldr(temp,
           MemOperand(tr,
@@ -622,6 +667,9 @@
   } else if (dest.IsCoreRegister()) {
     CHECK(!dest.AsVIXLRegister().Is(sp)) << dest;
 
+    UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+    temps.Exclude(dest.AsVIXLRegister());
+
     if (size == 1u) {
       ___ Ldrb(dest.AsVIXLRegister(), MemOperand(base, offset));
     } else {
diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc
index f91bcfa..6ed0e9b 100644
--- a/compiler/utils/arm64/assembler_arm64.cc
+++ b/compiler/utils/arm64/assembler_arm64.cc
@@ -40,12 +40,12 @@
 }
 
 const uint8_t* Arm64Assembler::CodeBufferBaseAddress() const {
-  return vixl_masm_.GetStartAddress<uint8_t*>();
+  return vixl_masm_.GetBuffer().GetStartAddress<const uint8_t*>();
 }
 
 void Arm64Assembler::FinalizeInstructions(const MemoryRegion& region) {
   // Copy the instructions from the buffer.
-  MemoryRegion from(vixl_masm_.GetStartAddress<void*>(), CodeSize());
+  MemoryRegion from(vixl_masm_.GetBuffer()->GetStartAddress<void*>(), CodeSize());
   region.CopyFrom(0, from);
 }
 
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 10bed13..50a1d9f 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -1753,7 +1753,10 @@
   __ LoadFromOffset(kLoadWordPair, R2, R4, 0x40400);
   __ LoadFromOffset(kLoadWordPair, R4, R4, 0x40400);
 
+  vixl::aarch32::UseScratchRegisterScope temps(assembler.asm_.GetVIXLAssembler());
+  temps.Exclude(R12);
   __ LoadFromOffset(kLoadWord, R0, R12, 12);  // 32-bit because of R12.
+  temps.Include(R12);
   __ LoadFromOffset(kLoadWord, R2, R4, 0xa4 - 0x100000);
 
   __ LoadFromOffset(kLoadSignedByte, R2, R4, 12);
@@ -1783,7 +1786,10 @@
   __ StoreToOffset(kStoreWordPair, R2, R4, 0x40400);
   __ StoreToOffset(kStoreWordPair, R4, R4, 0x40400);
 
+  vixl::aarch32::UseScratchRegisterScope temps(assembler.asm_.GetVIXLAssembler());
+  temps.Exclude(R12);
   __ StoreToOffset(kStoreWord, R0, R12, 12);  // 32-bit because of R12.
+  temps.Include(R12);
   __ StoreToOffset(kStoreWord, R2, R4, 0xa4 - 0x100000);
 
   __ StoreToOffset(kStoreByte, R2, R4, 12);
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index dcf3619..525a2ee 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -83,10 +83,13 @@
     // The compiler driver handles the verifier deps in the callbacks, so
     // remove what this class did for unit testing.
     verifier_deps_.reset(nullptr);
-    callbacks_->SetVerifierDeps(nullptr);
-    compiler_driver_->Verify(class_loader_, dex_files_, deps, &timings);
+    callbacks_->SetVerifierDeps(deps);
+    compiler_driver_->Verify(class_loader_, dex_files_, &timings);
     // The compiler driver may have updated the VerifierDeps in the callback object.
-    verifier_deps_.reset(callbacks_->GetVerifierDeps());
+    if (callbacks_->GetVerifierDeps() != deps) {
+      verifier_deps_.reset(callbacks_->GetVerifierDeps());
+    }
+    callbacks_->SetVerifierDeps(nullptr);
   }
 
   void SetVerifierDeps(const std::vector<const DexFile*>& dex_files) {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 81baa80..9e6032f 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -78,6 +78,7 @@
 #include "ScopedLocalRef.h"
 #include "scoped_thread_state_change-inl.h"
 #include "utils.h"
+#include "vdex_file.h"
 #include "verifier/verifier_deps.h"
 #include "well_known_classes.h"
 #include "zip_archive.h"
@@ -520,6 +521,7 @@
       oat_fd_(-1),
       input_vdex_fd_(-1),
       output_vdex_fd_(-1),
+      input_vdex_file_(nullptr),
       zip_fd_(-1),
       image_base_(0U),
       image_classes_zip_filename_(nullptr),
@@ -710,6 +712,10 @@
       Usage("Output must be supplied with either --oat-file or --oat-fd");
     }
 
+    if (input_vdex_fd_ != -1 && !input_vdex_.empty()) {
+      Usage("Can't have both --input-vdex-fd and --input-vdex");
+    }
+
     if (!oat_filenames_.empty() && oat_fd_ != -1) {
       Usage("--oat-file should not be used with --oat-fd");
     }
@@ -1123,6 +1129,8 @@
         zip_location_ = option.substr(strlen("--zip-location=")).data();
       } else if (option.starts_with("--input-vdex-fd=")) {
         ParseInputVdexFd(option);
+      } else if (option.starts_with("--input-vdex=")) {
+        input_vdex_ = option.substr(strlen("--input-vdex=")).data();
       } else if (option.starts_with("--output-vdex-fd=")) {
         ParseOutputVdexFd(option);
       } else if (option.starts_with("--oat-file=")) {
@@ -1266,6 +1274,17 @@
           return false;
         }
         oat_files_.push_back(std::move(oat_file));
+        DCHECK_EQ(input_vdex_fd_, -1);
+        if (!input_vdex_.empty()) {
+          std::string error_msg;
+          input_vdex_file_.reset(VdexFile::Open(input_vdex_,
+                                                /* writable */ false,
+                                                /* low_4gb */ false,
+                                                &error_msg));
+          if (input_vdex_file_ != nullptr && !input_vdex_file_->IsValid()) {
+            input_vdex_file_.reset(nullptr);
+          }
+        }
 
         DCHECK_EQ(output_vdex_fd_, -1);
         std::string vdex_filename = ReplaceFileExtension(oat_filename, "vdex");
@@ -1293,6 +1312,31 @@
       }
       oat_files_.push_back(std::move(oat_file));
 
+      DCHECK_NE(input_vdex_fd_, output_vdex_fd_);
+      if (input_vdex_fd_ != -1) {
+        struct stat s;
+        int rc = TEMP_FAILURE_RETRY(fstat(input_vdex_fd_, &s));
+        if (rc == -1) {
+          PLOG(WARNING) << "Failed getting length of vdex file";
+        } else {
+          std::string error_msg;
+          input_vdex_file_.reset(VdexFile::Open(input_vdex_fd_,
+                                                s.st_size,
+                                                "vdex",
+                                                /* writable */ false,
+                                                /* low_4gb */ false,
+                                                &error_msg));
+          // If there's any problem with the passed vdex, just warn and proceed
+          // without it.
+          if (input_vdex_file_ == nullptr) {
+            PLOG(WARNING) << "Failed opening vdex file " << error_msg;
+          } else if (!input_vdex_file_->IsValid()) {
+            PLOG(WARNING) << "Existing vdex file is invalid";
+            input_vdex_file_.reset(nullptr);
+          }
+        }
+      }
+
       DCHECK_NE(output_vdex_fd_, -1);
       std::string vdex_location = ReplaceFileExtension(oat_location_, "vdex");
       std::unique_ptr<File> vdex_file(new File(output_vdex_fd_, vdex_location, /* check_usage */ true));
@@ -1388,7 +1432,6 @@
   // boot class path.
   bool Setup() {
     TimingLogger::ScopedTiming t("dex2oat Setup", timings_);
-    art::MemMap::Init();  // For ZipEntry::ExtractToMemMap.
 
     if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods()) {
       return false;
@@ -1480,8 +1523,11 @@
         // Unzip or copy dex files straight to the oat file.
         std::unique_ptr<MemMap> opened_dex_files_map;
         std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
-        // Dexlayout verifies the dex file, so disable dex file verification in that case.
-        bool verify = compiler_options_->GetCompilerFilter() != CompilerFilter::kLayoutProfile;
+        // No need to verify the dex file for:
+        // 1) dexlayout, which already verified it
+        // 2) when we have a vdex file, which means it was already verified.
+        bool verify = compiler_options_->GetCompilerFilter() != CompilerFilter::kLayoutProfile &&
+            (input_vdex_file_ == nullptr);
         if (!oat_writers_[i]->WriteAndOpenDexFiles(
             kIsVdexEnabled ? vdex_files_[i].get() : oat_files_[i].get(),
             rodata_.back(),
@@ -1668,7 +1714,7 @@
                                      swap_fd_,
                                      profile_compilation_info_.get()));
     driver_->SetDexFilesForOatFile(dex_files_);
-    driver_->CompileAll(class_loader_, dex_files_, /* verifier_deps */ nullptr, timings_);
+    driver_->CompileAll(class_loader_, dex_files_, input_vdex_file_.get(), timings_);
   }
 
   // Notes on the interleaving of creating the images and oat files to
@@ -2241,7 +2287,14 @@
 
   bool AddDexFileSources() {
     TimingLogger::ScopedTiming t2("AddDexFileSources", timings_);
-    if (zip_fd_ != -1) {
+    if (input_vdex_file_ != nullptr) {
+      DCHECK_EQ(oat_writers_.size(), 1u);
+      const std::string& name = zip_location_.empty() ? dex_locations_[0] : zip_location_;
+      DCHECK(!name.empty());
+      if (!oat_writers_[0]->AddVdexDexFilesSource(*input_vdex_file_.get(), name.c_str())) {
+        return false;
+      }
+    } else if (zip_fd_ != -1) {
       DCHECK_EQ(oat_writers_.size(), 1u);
       if (!oat_writers_[0]->AddZippedDexFilesSource(File(zip_fd_, /* check_usage */ false),
                                                     zip_location_.c_str())) {
@@ -2599,6 +2652,8 @@
   int oat_fd_;
   int input_vdex_fd_;
   int output_vdex_fd_;
+  std::string input_vdex_;
+  std::unique_ptr<VdexFile> input_vdex_file_;
   std::vector<const char*> dex_filenames_;
   std::vector<const char*> dex_locations_;
   int zip_fd_;
@@ -2795,6 +2850,8 @@
     }
   }
 
+  art::MemMap::Init();  // For ZipEntry::ExtractToMemMap, and vdex.
+
   // Check early that the result of compilation can be written
   if (!dex2oat->OpenFile()) {
     return EXIT_FAILURE;
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index 30b708c..3347dac 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -63,9 +63,7 @@
         case kVld2Location:
         case kVld3Location:
         case kVld4Location: {
-          const uintptr_t pc_delta = disasm_->IsT32()
-              ? vixl::aarch32::kT32PcDelta
-              : vixl::aarch32::kA32PcDelta;
+          const uintptr_t pc_delta = label.GetLabel()->GetPcOffset();
           const int32_t offset = label.GetLabel()->GetLocation();
 
           os() << "[pc, #" << offset - pc_delta << "]";
@@ -77,7 +75,7 @@
       }
     }
 
-    DisassemblerStream& operator<<(const vixl::aarch32::Register reg) OVERRIDE {
+    DisassemblerStream& operator<<(vixl::aarch32::Register reg) OVERRIDE {
       if (reg.Is(tr)) {
         os() << "tr";
         return *this;
@@ -118,20 +116,11 @@
   CustomDisassembler(std::ostream& os, const DisassemblerOptions* options)
       : PrintDisassembler(&disassembler_stream_), disassembler_stream_(os, this, options) {}
 
-  void PrintPc(uint32_t prog_ctr) OVERRIDE {
+  void PrintCodeAddress(uint32_t prog_ctr) OVERRIDE {
     os() << "0x" << std::hex << std::setw(8) << std::setfill('0') << prog_ctr << ": ";
   }
 
-  bool IsT32() const {
-    return is_t32_;
-  }
-
-  void SetT32(bool is_t32) {
-    is_t32_ = is_t32;
-  }
-
  private:
-  bool is_t32_;
   CustomDisassemblerStream disassembler_stream_;
 };
 
@@ -152,7 +141,7 @@
       sizeof(unaligned_float), sizeof(unaligned_double)};
   const uintptr_t begin = reinterpret_cast<uintptr_t>(options_->base_address_);
   const uintptr_t end = reinterpret_cast<uintptr_t>(options_->end_address_);
-  uintptr_t literal_addr = RoundDown(disasm_->GetPc(), vixl::aarch32::kRegSizeInBytes) + offset;
+  uintptr_t literal_addr = RoundDown(disasm_->GetCodeAddress(), vixl::aarch32::kRegSizeInBytes) + offset;
 
   if (!options_->absolute_addresses_) {
     literal_addr += begin;
@@ -208,12 +197,14 @@
   // Remove the Thumb specifier bit; no effect if begin does not point to T32 code.
   const uintptr_t instr_ptr = reinterpret_cast<uintptr_t>(begin) & ~1;
 
-  disasm_->SetT32((reinterpret_cast<uintptr_t>(begin) & 1) != 0);
-  disasm_->JumpToPc(GetPc(instr_ptr));
+  const bool is_t32 = (reinterpret_cast<uintptr_t>(begin) & 1) != 0;
+  disasm_->SetCodeAddress(GetPc(instr_ptr));
 
-  if (disasm_->IsT32()) {
+  if (is_t32) {
     const uint16_t* const ip = reinterpret_cast<const uint16_t*>(instr_ptr);
-    next = reinterpret_cast<uintptr_t>(disasm_->DecodeT32At(ip));
+    const uint16_t* const end_address = reinterpret_cast<const uint16_t*>(
+        GetDisassemblerOptions()->end_address_);
+    next = reinterpret_cast<uintptr_t>(disasm_->DecodeT32At(ip, end_address));
   } else {
     const uint32_t* const ip = reinterpret_cast<const uint32_t*>(instr_ptr);
     next = reinterpret_cast<uintptr_t>(disasm_->DecodeA32At(ip));
@@ -230,10 +221,10 @@
   // Remove the Thumb specifier bit; no effect if begin does not point to T32 code.
   const uintptr_t base = reinterpret_cast<uintptr_t>(begin) & ~1;
 
-  disasm_->SetT32((reinterpret_cast<uintptr_t>(begin) & 1) != 0);
-  disasm_->JumpToPc(GetPc(base));
+  const bool is_t32 = (reinterpret_cast<uintptr_t>(begin) & 1) != 0;
+  disasm_->SetCodeAddress(GetPc(base));
 
-  if (disasm_->IsT32()) {
+  if (is_t32) {
     // The Thumb specifier bits cancel each other.
     disasm_->DisassembleT32Buffer(reinterpret_cast<const uint16_t*>(base), end - begin);
   } else {
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 2dfdc16..1e809d5 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -466,20 +466,6 @@
                     klass, this));
       interface_method->VisitRoots(visitor, pointer_size);
     }
-    // We know we don't have profiling information if the class hasn't been verified. Note
-    // that this check also ensures the IsNative call can be made, as IsNative expects a fully
-    // created class (and not a retired one).
-    if (klass->IsVerified()) {
-      // Runtime methods and native methods use the same field as the profiling info for
-      // storing their own data (jni entrypoint for native methods, and ImtConflictTable for
-      // some runtime methods).
-      if (!IsNative<kReadBarrierOption>() && !IsRuntimeMethod()) {
-        ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size);
-        if (profiling_info != nullptr) {
-          profiling_info->VisitRoots(visitor);
-        }
-      }
-    }
   }
 }
 
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index 6b21a56..1dca428 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -80,7 +80,7 @@
     gCmdLine.reset(new std::string("<unset>"));
   }
 
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
 #define INIT_LOGGING_DEFAULT_LOGGER android::base::LogdLogger()
 #else
 #define INIT_LOGGING_DEFAULT_LOGGER android::base::StderrLogger
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 216ec9e..f3aba97 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1949,36 +1949,13 @@
   void Visit(ObjPtr<mirror::ClassLoader> class_loader)
       REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
     ClassTable* const class_table = class_loader->GetClassTable();
-    if (!done_ && class_table != nullptr) {
-      DefiningClassLoaderFilterVisitor visitor(class_loader, visitor_);
-      if (!class_table->Visit(visitor)) {
-        // If the visitor ClassTable returns false it means that we don't need to continue.
-        done_ = true;
-      }
+    if (!done_ && class_table != nullptr && !class_table->Visit(*visitor_)) {
+      // If the visitor ClassTable returns false it means that we don't need to continue.
+      done_ = true;
     }
   }
 
  private:
-  // Class visitor that limits the class visits from a ClassTable to the classes with
-  // the provided defining class loader. This filter is used to avoid multiple visits
-  // of the same class which can be recorded for multiple initiating class loaders.
-  class DefiningClassLoaderFilterVisitor : public ClassVisitor {
-   public:
-    DefiningClassLoaderFilterVisitor(ObjPtr<mirror::ClassLoader> defining_class_loader,
-                                     ClassVisitor* visitor)
-        : defining_class_loader_(defining_class_loader), visitor_(visitor) { }
-
-    bool operator()(ObjPtr<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
-      if (klass->GetClassLoader() != defining_class_loader_) {
-        return true;
-      }
-      return (*visitor_)(klass);
-    }
-
-    ObjPtr<mirror::ClassLoader> const defining_class_loader_;
-    ClassVisitor* const visitor_;
-  };
-
   ClassVisitor* const visitor_;
   // If done is true then we don't need to do any more visiting.
   bool done_;
@@ -2563,109 +2540,56 @@
     }
   } else {
     ScopedObjectAccessUnchecked soa(self);
-    ObjPtr<mirror::Class> result_ptr;
-    bool descriptor_equals;
-    bool known_hierarchy =
-        FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &result_ptr);
-    if (result_ptr != nullptr) {
-      // The chain was understood and we found the class. We still need to add the class to
-      // the class table to protect from racy programs that can try and redefine the path list
-      // which would change the Class<?> returned for subsequent evaluation of const-class.
-      DCHECK(known_hierarchy);
-      DCHECK(result_ptr->DescriptorEquals(descriptor));
-      descriptor_equals = true;
-    } else {
-      // Either the chain wasn't understood or the class wasn't found.
-      //
-      // If the chain was understood but we did not find the class, let the Java-side
-      // rediscover all this and throw the exception with the right stack trace. Note that
-      // the Java-side could still succeed for racy programs if another thread is actively
-      // modifying the class loader's path list.
+    ObjPtr<mirror::Class> cp_klass;
+    if (FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &cp_klass)) {
+      // The chain was understood. So the value in cp_klass is either the class we were looking
+      // for, or not found.
+      if (cp_klass != nullptr) {
+        return cp_klass.Ptr();
+      }
+      // TODO: We handle the boot classpath loader in FindClassInBaseDexClassLoader. Try to unify
+      //       this and the branch above. TODO: throw the right exception here.
 
-      if (Runtime::Current()->IsAotCompiler()) {
-        // Oops, compile-time, can't run actual class-loader code.
-        ObjPtr<mirror::Throwable> pre_allocated =
-            Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
-        self->SetException(pre_allocated);
-        return nullptr;
-      }
-
-      ScopedLocalRef<jobject> class_loader_object(
-          soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get()));
-      std::string class_name_string(DescriptorToDot(descriptor));
-      ScopedLocalRef<jobject> result(soa.Env(), nullptr);
-      {
-        ScopedThreadStateChange tsc(self, kNative);
-        ScopedLocalRef<jobject> class_name_object(
-            soa.Env(), soa.Env()->NewStringUTF(class_name_string.c_str()));
-        if (class_name_object.get() == nullptr) {
-          DCHECK(self->IsExceptionPending());  // OOME.
-          return nullptr;
-        }
-        CHECK(class_loader_object.get() != nullptr);
-        result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
-                                                 WellKnownClasses::java_lang_ClassLoader_loadClass,
-                                                 class_name_object.get()));
-      }
-      if (self->IsExceptionPending()) {
-        // If the ClassLoader threw, pass that exception up.
-        // However, to comply with the RI behavior, first check if another thread succeeded.
-        result_ptr = LookupClass(self, descriptor, hash, class_loader.Get());
-        if (result_ptr != nullptr && !result_ptr->IsErroneous()) {
-          self->ClearException();
-          return EnsureResolved(self, descriptor, result_ptr);
-        }
-        return nullptr;
-      } else if (result.get() == nullptr) {
-        // broken loader - throw NPE to be compatible with Dalvik
-        ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s",
-                                               class_name_string.c_str()).c_str());
-        return nullptr;
-      }
-      result_ptr = soa.Decode<mirror::Class>(result.get());
-      // Check the name of the returned class.
-      descriptor_equals = result_ptr->DescriptorEquals(descriptor);
+      // We'll let the Java-side rediscover all this and throw the exception with the right stack
+      // trace.
     }
 
-    // Try to insert the class to the class table, checking for mismatch.
-    ObjPtr<mirror::Class> old;
-    {
-      ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
-      ClassTable* const class_table = InsertClassTableForClassLoader(class_loader.Get());
-      old = class_table->Lookup(descriptor, hash);
-      if (old == nullptr) {
-        old = result_ptr;  // For the comparison below, after releasing the lock.
-        if (descriptor_equals) {
-          class_table->InsertWithHash(result_ptr.Ptr(), hash);
-          Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
-        }  // else throw below, after releasing the lock.
-      }
-    }
-    if (UNLIKELY(old != result_ptr)) {
-      // Return `old` (even if `!descriptor_equals`) to mimic the RI behavior for parallel
-      // capable class loaders.  (All class loaders are considered parallel capable on Android.)
-      mirror::Class* loader_class = class_loader->GetClass();
-      const char* loader_class_name =
-          loader_class->GetDexFile().StringByTypeIdx(loader_class->GetDexTypeIndex());
-      LOG(WARNING) << "Initiating class loader of type " << DescriptorToDot(loader_class_name)
-          << " is not well-behaved; it returned a different Class for racing loadClass(\""
-          << DescriptorToDot(descriptor) << "\").";
-      return EnsureResolved(self, descriptor, old);
-    }
-    if (UNLIKELY(!descriptor_equals)) {
-      std::string result_storage;
-      const char* result_name = result_ptr->GetDescriptor(&result_storage);
-      std::string loader_storage;
-      const char* loader_class_name = class_loader->GetClass()->GetDescriptor(&loader_storage);
-      ThrowNoClassDefFoundError(
-          "Initiating class loader of type %s returned class %s instead of %s.",
-          DescriptorToDot(loader_class_name).c_str(),
-          DescriptorToDot(result_name).c_str(),
-          DescriptorToDot(descriptor).c_str());
+    if (Runtime::Current()->IsAotCompiler()) {
+      // Oops, compile-time, can't run actual class-loader code.
+      ObjPtr<mirror::Throwable> pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
+      self->SetException(pre_allocated);
       return nullptr;
     }
-    // success, return mirror::Class*
-    return result_ptr.Ptr();
+
+    ScopedLocalRef<jobject> class_loader_object(soa.Env(),
+                                                soa.AddLocalReference<jobject>(class_loader.Get()));
+    std::string class_name_string(DescriptorToDot(descriptor));
+    ScopedLocalRef<jobject> result(soa.Env(), nullptr);
+    {
+      ScopedThreadStateChange tsc(self, kNative);
+      ScopedLocalRef<jobject> class_name_object(soa.Env(),
+                                                soa.Env()->NewStringUTF(class_name_string.c_str()));
+      if (class_name_object.get() == nullptr) {
+        DCHECK(self->IsExceptionPending());  // OOME.
+        return nullptr;
+      }
+      CHECK(class_loader_object.get() != nullptr);
+      result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
+                                               WellKnownClasses::java_lang_ClassLoader_loadClass,
+                                               class_name_object.get()));
+    }
+    if (self->IsExceptionPending()) {
+      // If the ClassLoader threw, pass that exception up.
+      return nullptr;
+    } else if (result.get() == nullptr) {
+      // broken loader - throw NPE to be compatible with Dalvik
+      ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s",
+                                             class_name_string.c_str()).c_str());
+      return nullptr;
+    } else {
+      // success, return mirror::Class*
+      return soa.Decode<mirror::Class>(result.get()).Ptr();
+    }
   }
   UNREACHABLE();
 }
@@ -3746,6 +3670,12 @@
   Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass);
 }
 
+bool ClassLinker::RemoveClass(const char* descriptor, ObjPtr<mirror::ClassLoader> class_loader) {
+  WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+  ClassTable* const class_table = ClassTableForClassLoader(class_loader);
+  return class_table != nullptr && class_table->Remove(descriptor);
+}
+
 mirror::Class* ClassLinker::LookupClass(Thread* self,
                                         const char* descriptor,
                                         size_t hash,
@@ -3796,8 +3726,7 @@
       REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
     ClassTable* const class_table = class_loader->GetClassTable();
     ObjPtr<mirror::Class> klass = class_table->Lookup(descriptor_, hash_);
-    // Add `klass` only if `class_loader` is its defining (not just initiating) class loader.
-    if (klass != nullptr && klass->GetClassLoader() == class_loader) {
+    if (klass != nullptr) {
       result_->push_back(klass);
     }
   }
@@ -3816,7 +3745,6 @@
   const size_t hash = ComputeModifiedUtf8Hash(descriptor);
   ObjPtr<mirror::Class> klass = boot_class_table_.Lookup(descriptor, hash);
   if (klass != nullptr) {
-    DCHECK(klass->GetClassLoader() == nullptr);
     result.push_back(klass);
   }
   LookupClassesVisitor visitor(descriptor, hash, &result);
@@ -8124,8 +8052,8 @@
       REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
     ClassTable* const class_table = class_loader->GetClassTable();
     if (class_table != nullptr) {
-      num_zygote_classes += class_table->NumZygoteClasses(class_loader);
-      num_non_zygote_classes += class_table->NumNonZygoteClasses(class_loader);
+      num_zygote_classes += class_table->NumZygoteClasses();
+      num_non_zygote_classes += class_table->NumNonZygoteClasses();
     }
   }
 
@@ -8136,13 +8064,13 @@
 size_t ClassLinker::NumZygoteClasses() const {
   CountClassesVisitor visitor;
   VisitClassLoaders(&visitor);
-  return visitor.num_zygote_classes + boot_class_table_.NumZygoteClasses(nullptr);
+  return visitor.num_zygote_classes + boot_class_table_.NumZygoteClasses();
 }
 
 size_t ClassLinker::NumNonZygoteClasses() const {
   CountClassesVisitor visitor;
   VisitClassLoaders(&visitor);
-  return visitor.num_non_zygote_classes + boot_class_table_.NumNonZygoteClasses(nullptr);
+  return visitor.num_non_zygote_classes + boot_class_table_.NumNonZygoteClasses();
 }
 
 size_t ClassLinker::NumLoadedClasses() {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 88028ea..9563448 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -218,6 +218,12 @@
 
   mirror::Class* FindPrimitiveClass(char type) REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // General class unloading is not supported, this is used to prune
+  // unwanted classes during image writing.
+  bool RemoveClass(const char* descriptor, ObjPtr<mirror::ClassLoader> class_loader)
+      REQUIRES(!Locks::classlinker_classes_lock_)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   void DumpAllClasses(int flags)
       REQUIRES(!Locks::classlinker_classes_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/class_table.cc b/runtime/class_table.cc
index 81cdce5..0fcce6b 100644
--- a/runtime/class_table.cc
+++ b/runtime/class_table.cc
@@ -83,29 +83,18 @@
 
 #pragma clang diagnostic pop  // http://b/31104323
 
-size_t ClassTable::CountDefiningLoaderClasses(ObjPtr<mirror::ClassLoader> defining_loader,
-                                              const ClassSet& set) const {
-  size_t count = 0;
-  for (const GcRoot<mirror::Class>& klass : set) {
-    if (klass.Read()->GetClassLoader() == defining_loader) {
-      ++count;
-    }
-  }
-  return count;
-}
-
-size_t ClassTable::NumZygoteClasses(ObjPtr<mirror::ClassLoader> defining_loader) const {
+size_t ClassTable::NumZygoteClasses() const {
   ReaderMutexLock mu(Thread::Current(), lock_);
   size_t sum = 0;
   for (size_t i = 0; i < classes_.size() - 1; ++i) {
-    sum += CountDefiningLoaderClasses(defining_loader, classes_[i]);
+    sum += classes_[i].Size();
   }
   return sum;
 }
 
-size_t ClassTable::NumNonZygoteClasses(ObjPtr<mirror::ClassLoader> defining_loader) const {
+size_t ClassTable::NumNonZygoteClasses() const {
   ReaderMutexLock mu(Thread::Current(), lock_);
-  return CountDefiningLoaderClasses(defining_loader, classes_.back());
+  return classes_.back().Size();
 }
 
 mirror::Class* ClassTable::Lookup(const char* descriptor, size_t hash) {
@@ -113,7 +102,7 @@
   for (ClassSet& class_set : classes_) {
     auto it = class_set.FindWithHash(descriptor, hash);
     if (it != class_set.end()) {
-      return it->Read();
+     return it->Read();
     }
   }
   return nullptr;
@@ -153,6 +142,7 @@
 
 bool ClassTable::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a,
                                                        const GcRoot<mirror::Class>& b) const {
+  DCHECK_EQ(a.Read()->GetClassLoader(), b.Read()->GetClassLoader());
   std::string temp;
   return a.Read()->DescriptorEquals(b.Read()->GetDescriptor(&temp));
 }
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 92634a4..558c144 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -84,14 +84,10 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Returns the number of classes in previous snapshots.
-  size_t NumZygoteClasses(ObjPtr<mirror::ClassLoader> defining_loader) const
-      REQUIRES(!lock_)
-      REQUIRES_SHARED(Locks::mutator_lock_);
+  size_t NumZygoteClasses() const REQUIRES(!lock_);
 
   // Returns all off the classes in the lastest snapshot.
-  size_t NumNonZygoteClasses(ObjPtr<mirror::ClassLoader> defining_loader) const
-      REQUIRES(!lock_)
-      REQUIRES_SHARED(Locks::mutator_lock_);
+  size_t NumNonZygoteClasses() const REQUIRES(!lock_);
 
   // Update a class in the table with the new class. Returns the existing class which was replaced.
   mirror::Class* UpdateClass(const char* descriptor, mirror::Class* new_klass, size_t hash)
@@ -177,11 +173,6 @@
  private:
   void InsertWithoutLocks(ObjPtr<mirror::Class> klass) NO_THREAD_SAFETY_ANALYSIS;
 
-  size_t CountDefiningLoaderClasses(ObjPtr<mirror::ClassLoader> defining_loader,
-                                    const ClassSet& set) const
-      REQUIRES(lock_)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Return true if we inserted the oat file, false if it already exists.
   bool InsertOatFileLocked(const OatFile* oat_file)
       REQUIRES(lock_)
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index d717ec0..b0c4597 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -136,7 +136,9 @@
       + "/core.art";
   }
 
-  bool GetCachedImageFile(/*out*/std::string* image, std::string* error_msg) const {
+  bool GetCachedImageFile(const std::string& image_location,
+                          /*out*/std::string* image,
+                          /*out*/std::string* error_msg) const {
     std::string cache;
     bool have_android_data;
     bool dalvik_cache_exists;
@@ -151,7 +153,14 @@
       *error_msg = "Failed to create dalvik cache";
       return false;
     }
-    return GetDalvikCacheFilename(GetImageLocation().c_str(), cache.c_str(), image, error_msg);
+    return GetDalvikCacheFilename(image_location.c_str(), cache.c_str(), image, error_msg);
+  }
+
+  // Returns the path to an image location whose contents differ from the
+  // 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";
   }
 
   std::string GetDexSrc1() const {
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 673a97e..06ed029 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -413,42 +413,48 @@
     if (UNLIKELY(obj == nullptr || !IsAligned<kPageSize>(obj) ||
                  (kIsDebugBuild && large_object_space != nullptr &&
                      !large_object_space->Contains(obj)))) {
-      LOG(FATAL_WITHOUT_ABORT) << "Tried to mark " << obj << " not contained by any spaces";
+      // Lowest priority logging first:
+      PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT);
+      MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true);
+      // Buffer the output in the string stream since it is more important than the stack traces
+      // and we want it to have log priority. The stack traces are printed from Runtime::Abort
+      // which is called from LOG(FATAL) but before the abort message.
+      std::ostringstream oss;
+      oss << "Tried to mark " << obj << " not contained by any spaces" << std::endl;
       if (holder_ != nullptr) {
         size_t holder_size = holder_->SizeOf();
         ArtField* field = holder_->FindFieldByOffset(offset_);
-        LOG(FATAL_WITHOUT_ABORT) << "Field info: "
-                            << " holder=" << holder_
-                            << " holder is "
-                            << (mark_sweep_->GetHeap()->IsLiveObjectLocked(holder_)
-                                ? "alive" : "dead")
-                            << " holder_size=" << holder_size
-                            << " holder_type=" << holder_->PrettyTypeOf()
-                            << " offset=" << offset_.Uint32Value()
-                            << " field=" << (field != nullptr ? field->GetName() : "nullptr")
-                            << " field_type="
-                            << (field != nullptr ? field->GetTypeDescriptor() : "")
-                            << " first_ref_field_offset="
-                            << (holder_->IsClass()
-                                ? holder_->AsClass()->GetFirstReferenceStaticFieldOffset(
-                                    kRuntimePointerSize)
-                                : holder_->GetClass()->GetFirstReferenceInstanceFieldOffset())
-                            << " num_of_ref_fields="
-                            << (holder_->IsClass()
-                                ? holder_->AsClass()->NumReferenceStaticFields()
-                                : holder_->GetClass()->NumReferenceInstanceFields());
+        oss << "Field info: "
+            << " holder=" << holder_
+            << " holder is "
+            << (mark_sweep_->GetHeap()->IsLiveObjectLocked(holder_)
+                ? "alive" : "dead")
+            << " holder_size=" << holder_size
+            << " holder_type=" << holder_->PrettyTypeOf()
+            << " offset=" << offset_.Uint32Value()
+            << " field=" << (field != nullptr ? field->GetName() : "nullptr")
+            << " field_type="
+            << (field != nullptr ? field->GetTypeDescriptor() : "")
+            << " first_ref_field_offset="
+            << (holder_->IsClass()
+                ? holder_->AsClass()->GetFirstReferenceStaticFieldOffset(
+                    kRuntimePointerSize)
+                : holder_->GetClass()->GetFirstReferenceInstanceFieldOffset())
+            << " num_of_ref_fields="
+            << (holder_->IsClass()
+                ? holder_->AsClass()->NumReferenceStaticFields()
+                : holder_->GetClass()->NumReferenceInstanceFields())
+            << std::endl;
         // Print the memory content of the holder.
         for (size_t i = 0; i < holder_size / sizeof(uint32_t); ++i) {
           uint32_t* p = reinterpret_cast<uint32_t*>(holder_);
-          LOG(FATAL_WITHOUT_ABORT) << &p[i] << ": " << "holder+" << (i * sizeof(uint32_t)) << " = "
-                              << std::hex << p[i];
+          oss << &p[i] << ": " << "holder+" << (i * sizeof(uint32_t)) << " = " << std::hex << p[i]
+              << std::endl;
         }
       }
-      PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT);
-      MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true);
-      LOG(FATAL_WITHOUT_ABORT) << "Attempting see if it's a bad thread root";
-      mark_sweep_->VerifySuspendedThreadRoots();
-      LOG(FATAL) << "Can't mark invalid object";
+      oss << "Attempting see if it's a bad thread root" << std::endl;
+      mark_sweep_->VerifySuspendedThreadRoots(oss);
+      LOG(FATAL) << oss.str();
     }
   }
 
@@ -567,6 +573,8 @@
 
 class MarkSweep::VerifyRootVisitor : public SingleRootVisitor {
  public:
+  explicit VerifyRootVisitor(std::ostream& os) : os_(os) {}
+
   void VisitRoot(mirror::Object* root, const RootInfo& info) OVERRIDE
       REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
     // See if the root is on any space bitmap.
@@ -574,14 +582,17 @@
     if (heap->GetLiveBitmap()->GetContinuousSpaceBitmap(root) == nullptr) {
       space::LargeObjectSpace* large_object_space = heap->GetLargeObjectsSpace();
       if (large_object_space != nullptr && !large_object_space->Contains(root)) {
-        LOG(FATAL_WITHOUT_ABORT) << "Found invalid root: " << root << " " << info;
+        os_ << "Found invalid root: " << root << " " << info << std::endl;
       }
     }
   }
+
+ private:
+  std::ostream& os_;
 };
 
-void MarkSweep::VerifySuspendedThreadRoots() {
-  VerifyRootVisitor visitor;
+void MarkSweep::VerifySuspendedThreadRoots(std::ostream& os) {
+  VerifyRootVisitor visitor(os);
   Runtime::Current()->GetThreadList()->VisitRootsForSuspendedThreads(&visitor);
 }
 
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index a94cb27..02cf462 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -250,7 +250,7 @@
 
   // Verify the roots of the heap and print out information related to any invalid roots.
   // Called in MarkObject, so may we may not hold the mutator lock.
-  void VerifySuspendedThreadRoots()
+  void VerifySuspendedThreadRoots(std::ostream& os)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Expand mark stack to 2x its current size.
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index f0e619d..6a97edd 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2742,12 +2742,6 @@
     concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
   }
 
-  // It's time to clear all inline caches, in case some classes can be unloaded.
-  if (((gc_type == collector::kGcTypeFull) || (gc_type == collector::kGcTypePartial)) &&
-      (runtime->GetJit() != nullptr)) {
-    runtime->GetJit()->GetCodeCache()->ClearGcRootsInInlineCaches(self);
-  }
-
   CHECK(collector != nullptr)
       << "Could not find garbage collector with collector_type="
       << static_cast<size_t>(collector_type_) << " and gc_type=" << gc_type;
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index d098ee2..72dbe6a 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -427,7 +427,7 @@
         if (!reg->VerifierInstanceOf(field_class.Ptr())) {
           // This should never happen.
           std::string temp1, temp2, temp3;
-          self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
+          self->ThrowNewExceptionF("Ljava/lang/InternalError;",
                                    "Put '%s' that is not instance of field '%s' in '%s'",
                                    reg->GetClass()->GetDescriptor(&temp1),
                                    field_class->GetDescriptor(&temp2),
@@ -1493,7 +1493,7 @@
             if (!o->VerifierInstanceOf(arg_type)) {
               // This should never happen.
               std::string temp1, temp2;
-              self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
+              self->ThrowNewExceptionF("Ljava/lang/InternalError;",
                                        "Invoking %s with bad arg %d, type '%s' not instance of '%s'",
                                        new_shadow_frame->GetMethod()->GetName(), shorty_pos,
                                        o->GetClass()->GetDescriptor(&temp1),
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 989b7da..22c0fe0 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -287,7 +287,7 @@
           if (!obj_result->VerifierInstanceOf(return_type)) {
             // This should never happen.
             std::string temp1, temp2;
-            self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
+            self->ThrowNewExceptionF("Ljava/lang/InternalError;",
                                      "Returning '%s' that is not instance of return type '%s'",
                                      obj_result->GetClass()->GetDescriptor(&temp1),
                                      return_type->GetDescriptor(&temp2));
@@ -577,7 +577,7 @@
         } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
           // This should never happen.
           std::string temp;
-          self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
+          self->ThrowNewExceptionF("Ljava/lang/InternalError;",
                                    "Throwing '%s' that is not instance of Throwable",
                                    exception->GetClass()->GetDescriptor(&temp));
         } else {
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index fad7d90..5574a11 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -621,8 +621,8 @@
   Thread* const self = Thread::Current();
   self->AssertThreadSuspensionIsAllowable();
   CHECK(pReq != nullptr);
+  CHECK_EQ(threadId, Dbg::GetThreadSelfId()) << "Only the current thread can suspend itself";
   /* send request and possibly suspend ourselves */
-  JDWP::ObjectId thread_self_id = Dbg::GetThreadSelfId();
   ScopedThreadSuspension sts(self, kWaitingForDebuggerSend);
   if (suspend_policy != SP_NONE) {
     AcquireJdwpTokenForEvent(threadId);
@@ -631,7 +631,7 @@
   {
     // Before suspending, we change our state to kSuspended so the debugger sees us as RUNNING.
     ScopedThreadStateChange stsc(self, kSuspended);
-    SuspendByPolicy(suspend_policy, thread_self_id);
+    SuspendByPolicy(suspend_policy, threadId);
   }
 }
 
@@ -658,13 +658,10 @@
 }
 
 void JdwpState::AcquireJdwpTokenForEvent(ObjectId threadId) {
-  CHECK_NE(Thread::Current(), GetDebugThread()) << "Expected event thread";
-  CHECK_NE(debug_thread_id_, threadId) << "Not expected debug thread";
   SetWaitForJdwpToken(threadId);
 }
 
 void JdwpState::ReleaseJdwpTokenForEvent() {
-  CHECK_NE(Thread::Current(), GetDebugThread()) << "Expected event thread";
   ClearWaitForJdwpToken();
 }
 
@@ -685,23 +682,28 @@
   /* this is held for very brief periods; contention is unlikely */
   MutexLock mu(self, jdwp_token_lock_);
 
-  CHECK_NE(jdwp_token_owner_thread_id_, threadId) << "Thread is already holding event thread lock";
+  if (jdwp_token_owner_thread_id_ == threadId) {
+    // Only the debugger thread may already hold the event token. For instance, it may trigger
+    // a CLASS_PREPARE event while processing a command that initializes a class.
+    CHECK_EQ(threadId, debug_thread_id_) << "Non-debugger thread is already holding event token";
+  } else {
+    /*
+     * If another thread is already doing stuff, wait for it.  This can
+     * go to sleep indefinitely.
+     */
 
-  /*
-   * If another thread is already doing stuff, wait for it.  This can
-   * go to sleep indefinitely.
-   */
-  while (jdwp_token_owner_thread_id_ != 0) {
-    VLOG(jdwp) << StringPrintf("event in progress (%#" PRIx64 "), %#" PRIx64 " sleeping",
-                               jdwp_token_owner_thread_id_, threadId);
-    waited = true;
-    jdwp_token_cond_.Wait(self);
-  }
+    while (jdwp_token_owner_thread_id_ != 0) {
+      VLOG(jdwp) << StringPrintf("event in progress (%#" PRIx64 "), %#" PRIx64 " sleeping",
+                                 jdwp_token_owner_thread_id_, threadId);
+      waited = true;
+      jdwp_token_cond_.Wait(self);
+    }
 
-  if (waited || threadId != debug_thread_id_) {
-    VLOG(jdwp) << StringPrintf("event token grabbed (%#" PRIx64 ")", threadId);
+    if (waited || threadId != debug_thread_id_) {
+      VLOG(jdwp) << StringPrintf("event token grabbed (%#" PRIx64 ")", threadId);
+    }
+    jdwp_token_owner_thread_id_ = threadId;
   }
-  jdwp_token_owner_thread_id_ = threadId;
 }
 
 /*
@@ -1224,14 +1226,15 @@
     VLOG(jdwp) << "  suspend_policy=" << suspend_policy;
   }
 
-  if (thread_id == debug_thread_id_) {
+  ObjectId reported_thread_id = thread_id;
+  if (reported_thread_id == debug_thread_id_) {
     /*
      * JDWP says that, for a class prep in the debugger thread, we
      * should set thread to null and if any threads were supposed
      * to be suspended then we suspend all other threads.
      */
     VLOG(jdwp) << "  NOTE: class prepare in debugger thread!";
-    thread_id = 0;
+    reported_thread_id = 0;
     if (suspend_policy == SP_EVENT_THREAD) {
       suspend_policy = SP_ALL;
     }
@@ -1244,7 +1247,7 @@
   for (const JdwpEvent* pEvent : match_list) {
     expandBufAdd1(pReq, pEvent->eventKind);
     expandBufAdd4BE(pReq, pEvent->requestId);
-    expandBufAddObjectId(pReq, thread_id);
+    expandBufAddObjectId(pReq, reported_thread_id);
     expandBufAdd1(pReq, tag);
     expandBufAddRefTypeId(pReq, class_id);
     expandBufAddUtf8String(pReq, signature);
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index f0ed237..1c47e7e 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -133,7 +133,7 @@
                            size_t max_capacity,
                            bool garbage_collect_code)
     : lock_("Jit code cache", kJitCodeCacheLock),
-      lock_cond_("Jit code cache variable", lock_),
+      lock_cond_("Jit code cache condition variable", lock_),
       collection_in_progress_(false),
       code_map_(code_map),
       data_map_(data_map),
@@ -152,7 +152,9 @@
       number_of_collections_(0),
       histogram_stack_map_memory_use_("Memory used for stack maps", 16),
       histogram_code_memory_use_("Memory used for compiled code", 16),
-      histogram_profiling_info_memory_use_("Memory used for profiling info", 16) {
+      histogram_profiling_info_memory_use_("Memory used for profiling info", 16),
+      is_weak_access_enabled_(true),
+      inline_cache_cond_("Jit inline cache condition variable", lock_) {
 
   DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity);
   code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_end_, false /*locked*/);
@@ -327,6 +329,34 @@
       }
     }
   }
+  // Walk over inline caches to clear entries containing unloaded classes.
+  for (ProfilingInfo* info : profiling_infos_) {
+    for (size_t i = 0; i < info->number_of_inline_caches_; ++i) {
+      InlineCache* cache = &info->cache_[i];
+      for (size_t j = 0; j < InlineCache::kIndividualCacheSize; ++j) {
+        // This does not need a read barrier because this is called by GC.
+        mirror::Class* cls = cache->classes_[j].Read<kWithoutReadBarrier>();
+        if (cls != nullptr) {
+          // Look at the classloader of the class to know if it has been
+          // unloaded.
+          // This does not need a read barrier because this is called by GC.
+          mirror::Object* class_loader =
+              cls->GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>();
+          if (visitor->IsMarked(class_loader) != nullptr) {
+            // The class loader is live, update the entry if the class has moved.
+            mirror::Class* new_cls = down_cast<mirror::Class*>(visitor->IsMarked(cls));
+            // Note that new_object can be null for CMS and newly allocated objects.
+            if (new_cls != nullptr && new_cls != cls) {
+              cache->classes_[j] = GcRoot<mirror::Class>(new_cls);
+            }
+          } else {
+            // The class loader is not live, clear the entry.
+            cache->classes_[j] = GcRoot<mirror::Class>(nullptr);
+          }
+        }
+      }
+    }
+  }
 }
 
 void JitCodeCache::FreeCode(const void* code_ptr, ArtMethod* method ATTRIBUTE_UNUSED) {
@@ -375,11 +405,51 @@
   }
 }
 
-void JitCodeCache::ClearGcRootsInInlineCaches(Thread* self) {
+bool JitCodeCache::IsWeakAccessEnabled(Thread* self) const {
+  return kUseReadBarrier
+      ? self->GetWeakRefAccessEnabled()
+      : is_weak_access_enabled_.LoadSequentiallyConsistent();
+}
+
+void JitCodeCache::WaitUntilInlineCacheAccessible(Thread* self) {
+  if (IsWeakAccessEnabled(self)) {
+    return;
+  }
+  ScopedThreadSuspension sts(self, kWaitingWeakGcRootRead);
   MutexLock mu(self, lock_);
-  for (ProfilingInfo* info : profiling_infos_) {
-    if (!info->IsInUseByCompiler()) {
-      info->ClearGcRootsInInlineCaches();
+  while (!IsWeakAccessEnabled(self)) {
+    inline_cache_cond_.Wait(self);
+  }
+}
+
+void JitCodeCache::BroadcastForInlineCacheAccess() {
+  Thread* self = Thread::Current();
+  MutexLock mu(self, lock_);
+  inline_cache_cond_.Broadcast(self);
+}
+
+void JitCodeCache::AllowInlineCacheAccess() {
+  DCHECK(!kUseReadBarrier);
+  is_weak_access_enabled_.StoreSequentiallyConsistent(true);
+  BroadcastForInlineCacheAccess();
+}
+
+void JitCodeCache::DisallowInlineCacheAccess() {
+  DCHECK(!kUseReadBarrier);
+  is_weak_access_enabled_.StoreSequentiallyConsistent(false);
+}
+
+void JitCodeCache::CopyInlineCacheInto(const InlineCache& ic,
+                                       Handle<mirror::ObjectArray<mirror::Class>> array) {
+  WaitUntilInlineCacheAccessible(Thread::Current());
+  // Note that we don't need to lock `lock_` here, the compiler calling
+  // this method has already ensured the inline cache will not be deleted.
+  for (size_t in_cache = 0, in_array = 0;
+       in_cache < InlineCache::kIndividualCacheSize;
+       ++in_cache) {
+    mirror::Class* object = ic.classes_[in_cache].Read();
+    if (object != nullptr) {
+      array->Set(in_array++, object);
     }
   }
 }
@@ -428,10 +498,16 @@
           core_spill_mask,
           fp_spill_mask,
           code_size);
+      // Flush caches before we remove write permission because on some ARMv8 hardware,
+      // flushing caches require write permissions.
+      //
+      // For reference, here are kernel patches discussing about this issue:
+      // https://android.googlesource.com/kernel/msm/%2B/0e7f7bcc3fc87489cda5aa6aff8ce40eed912279
+      // https://patchwork.kernel.org/patch/9047921/
+      FlushInstructionCache(reinterpret_cast<char*>(code_ptr),
+                            reinterpret_cast<char*>(code_ptr + code_size));
     }
 
-    FlushInstructionCache(reinterpret_cast<char*>(code_ptr),
-                          reinterpret_cast<char*>(code_ptr + code_size));
     number_of_compilations_++;
   }
   // We need to update the entry point in the runnable state for the instrumentation.
@@ -831,8 +907,6 @@
 
   if (collect_profiling_info) {
     ScopedThreadSuspension sts(self, kSuspended);
-    gc::ScopedGCCriticalSection gcs(
-        self, gc::kGcCauseJitCodeCache, gc::kCollectorTypeJitCodeCache);
     MutexLock mu(self, lock_);
     // Free all profiling infos of methods not compiled nor being compiled.
     auto profiling_kept_end = std::remove_if(profiling_infos_.begin(), profiling_infos_.end(),
@@ -846,10 +920,6 @@
         // code cache collection.
         if (ContainsPc(ptr) &&
             info->GetMethod()->GetProfilingInfo(kRuntimePointerSize) == nullptr) {
-          // We clear the inline caches as classes in it might be stalled.
-          info->ClearGcRootsInInlineCaches();
-          // Do a fence to make sure the clearing is seen before attaching to the method.
-          QuasiAtomic::ThreadFenceRelease();
           info->GetMethod()->SetProfilingInfo(info);
         } else if (info->GetMethod()->GetProfilingInfo(kRuntimePointerSize) != info) {
           // No need for this ProfilingInfo object anymore.
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 40112fe..be2cec5 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -36,6 +36,7 @@
 
 class ArtMethod;
 class LinearAlloc;
+class InlineCache;
 class ProfilingInfo;
 
 namespace jit {
@@ -156,7 +157,9 @@
       REQUIRES(!lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  void ClearGcRootsInInlineCaches(Thread* self) REQUIRES(!lock_);
+  void CopyInlineCacheInto(const InlineCache& ic, Handle<mirror::ObjectArray<mirror::Class>> array)
+      REQUIRES(!lock_)
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Create a 'ProfileInfo' for 'method'. If 'retry_allocation' is true,
   // will collect and retry if the first allocation is unsuccessful.
@@ -200,6 +203,12 @@
       REQUIRES(!lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // The GC needs to disallow the reading of inline caches when it processes them,
+  // to avoid having a class being used while it is being deleted.
+  void AllowInlineCacheAccess() REQUIRES(!lock_);
+  void DisallowInlineCacheAccess() REQUIRES(!lock_);
+  void BroadcastForInlineCacheAccess() REQUIRES(!lock_);
+
  private:
   // Take ownership of maps.
   JitCodeCache(MemMap* code_map,
@@ -275,6 +284,11 @@
   void FreeData(uint8_t* data) REQUIRES(lock_);
   uint8_t* AllocateData(size_t data_size) REQUIRES(lock_);
 
+  bool IsWeakAccessEnabled(Thread* self) const;
+  void WaitUntilInlineCacheAccessible(Thread* self)
+      REQUIRES(!lock_)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Lock for guarding allocations, collections, and the method_code_map_.
   Mutex lock_;
   // Condition to wait on during collection.
@@ -347,6 +361,14 @@
   // Histograms for keeping track of profiling info statistics.
   Histogram<uint64_t> histogram_profiling_info_memory_use_ GUARDED_BY(lock_);
 
+  // Whether the GC allows accessing weaks in inline caches. Note that this
+  // is not used by the concurrent collector, which uses
+  // Thread::SetWeakRefAccessEnabled instead.
+  Atomic<bool> is_weak_access_enabled_;
+
+  // Condition to wait on for accessing inline caches.
+  ConditionVariable inline_cache_cond_ GUARDED_BY(lock_);
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache);
 };
 
diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc
index 9ec46f0..405280d 100644
--- a/runtime/jit/profiling_info.cc
+++ b/runtime/jit/profiling_info.cc
@@ -36,15 +36,6 @@
   for (size_t i = 0; i < number_of_inline_caches_; ++i) {
     cache_[i].dex_pc_ = entries[i];
   }
-  if (method->IsCopied()) {
-    // GetHoldingClassOfCopiedMethod is expensive, but creating a profiling info for a copied method
-    // appears to happen very rarely in practice.
-    holding_class_ = GcRoot<mirror::Class>(
-        Runtime::Current()->GetClassLinker()->GetHoldingClassOfCopiedMethod(method));
-  } else {
-    holding_class_ = GcRoot<mirror::Class>(method->GetDeclaringClass());
-  }
-  DCHECK(!holding_class_.IsNull());
 }
 
 bool ProfilingInfo::Create(Thread* self, ArtMethod* method, bool retry_allocation) {
@@ -116,14 +107,6 @@
         --i;
       } else {
         // We successfully set `cls`, just return.
-        // Since the instrumentation is marked from the declaring class we need to mark the card so
-        // that mod-union tables and card rescanning know about the update.
-        // Note that the declaring class is not necessarily the holding class if the method is
-        // copied. We need the card mark to be in the holding class since that is from where we
-        // will visit the profiling info.
-        if (!holding_class_.IsNull()) {
-          Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(holding_class_.Read());
-        }
         return;
       }
     }
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
index 1056fac..9902bb5 100644
--- a/runtime/jit/profiling_info.h
+++ b/runtime/jit/profiling_info.h
@@ -39,46 +39,13 @@
 // Once the classes_ array is full, we consider the INVOKE to be megamorphic.
 class InlineCache {
  public:
-  bool IsMonomorphic() const {
-    DCHECK_GE(kIndividualCacheSize, 2);
-    return !classes_[0].IsNull() && classes_[1].IsNull();
-  }
-
-  bool IsMegamorphic() const {
-    for (size_t i = 0; i < kIndividualCacheSize; ++i) {
-      if (classes_[i].IsNull()) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  mirror::Class* GetMonomorphicType() const REQUIRES_SHARED(Locks::mutator_lock_) {
-    // Note that we cannot ensure the inline cache is actually monomorphic
-    // at this point, as other threads may have updated it.
-    DCHECK(!classes_[0].IsNull());
-    return classes_[0].Read();
-  }
-
-  bool IsUninitialized() const {
-    return classes_[0].IsNull();
-  }
-
-  bool IsPolymorphic() const {
-    DCHECK_GE(kIndividualCacheSize, 3);
-    return !classes_[1].IsNull() && classes_[kIndividualCacheSize - 1].IsNull();
-  }
-
-  mirror::Class* GetTypeAt(size_t i) const REQUIRES_SHARED(Locks::mutator_lock_) {
-    return classes_[i].Read();
-  }
-
   static constexpr uint16_t kIndividualCacheSize = 5;
 
  private:
   uint32_t dex_pc_;
   GcRoot<mirror::Class> classes_[kIndividualCacheSize];
 
+  friend class jit::JitCodeCache;
   friend class ProfilingInfo;
 
   DISALLOW_COPY_AND_ASSIGN(InlineCache);
@@ -102,18 +69,6 @@
       REQUIRES(Roles::uninterruptible_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // NO_THREAD_SAFETY_ANALYSIS since we don't know what the callback requires.
-  template<typename RootVisitorType>
-  void VisitRoots(RootVisitorType& visitor) NO_THREAD_SAFETY_ANALYSIS {
-    visitor.VisitRootIfNonNull(holding_class_.AddressWithoutBarrier());
-    for (size_t i = 0; i < number_of_inline_caches_; ++i) {
-      InlineCache* cache = &cache_[i];
-      for (size_t j = 0; j < InlineCache::kIndividualCacheSize; ++j) {
-        visitor.VisitRootIfNonNull(cache->classes_[j].AddressWithoutBarrier());
-      }
-    }
-  }
-
   ArtMethod* GetMethod() const {
     return method_;
   }
@@ -175,9 +130,6 @@
   // Method this profiling info is for.
   ArtMethod* const method_;
 
-  // Holding class for the method in case method is a copied method.
-  GcRoot<mirror::Class> holding_class_;
-
   // Whether the ArtMethod is currently being compiled. This flag
   // is implicitly guarded by the JIT code cache lock.
   // TODO: Make the JIT code cache lock global.
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 1ec59b3..6da72e4 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -282,6 +282,7 @@
 #ifndef __LP64__
   UNUSED(low_4gb);
 #endif
+  use_ashmem = use_ashmem && !kIsTargetLinux;
   if (byte_count == 0) {
     return new MemMap(name, nullptr, 0, nullptr, 0, prot, false);
   }
@@ -522,6 +523,7 @@
 
 MemMap* MemMap::RemapAtEnd(uint8_t* new_end, const char* tail_name, int tail_prot,
                            std::string* error_msg, bool use_ashmem) {
+  use_ashmem = use_ashmem && !kIsTargetLinux;
   DCHECK_GE(new_end, Begin());
   DCHECK_LE(new_end, End());
   DCHECK_LE(begin_ + size_, reinterpret_cast<uint8_t*>(base_begin_) + base_size_);
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index 049ae12..0fea1a5 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -68,7 +68,7 @@
                               bool low_4gb,
                               bool reuse,
                               std::string* error_msg,
-                              bool use_ashmem = !kIsTargetLinux);
+                              bool use_ashmem = true);
 
   // Create placeholder for a region allocated by direct call to mmap.
   // This is useful when we do not have control over the code calling mmap,
@@ -172,7 +172,7 @@
                      const char* tail_name,
                      int tail_prot,
                      std::string* error_msg,
-                     bool use_ashmem = !kIsTargetLinux);
+                     bool use_ashmem = true);
 
   static bool CheckNoGaps(MemMap* begin_map, MemMap* end_map)
       REQUIRES(!Locks::mem_maps_lock_);
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index aa5da2e..5def65e 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -65,8 +65,10 @@
       OFFSET_OF_OBJECT_MEMBER(Class, super_class_));
 }
 
+template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
 inline ClassLoader* Class::GetClassLoader() {
-  return GetFieldObject<ClassLoader>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_));
+  return GetFieldObject<ClassLoader, kVerifyFlags, kReadBarrierOption>(
+      OFFSET_OF_OBJECT_MEMBER(Class, class_loader_));
 }
 
 template<VerifyObjectFlags kVerifyFlags>
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 792f626..248c941 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -674,6 +674,8 @@
     return MemberOffset(OFFSETOF_MEMBER(Class, super_class_));
   }
 
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
+           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   ClassLoader* GetClassLoader() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
 
   void SetClassLoader(ObjPtr<ClassLoader> new_cl) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index df0849a..1a77072 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -413,33 +413,7 @@
 
   OatFileAssistant oat_file_assistant(filename.c_str(), target_instruction_set,
                                       false /* load_executable */);
-
-  std::ostringstream status;
-  bool oat_file_exists = false;
-  bool odex_file_exists = false;
-  if (oat_file_assistant.OatFileExists()) {
-    oat_file_exists = true;
-    status << *oat_file_assistant.OatFileName() << " [compilation_filter=";
-    status << CompilerFilter::NameOfFilter(oat_file_assistant.OatFileCompilerFilter());
-    status << ", status=" << oat_file_assistant.OatFileStatus();
-  }
-
-  if (oat_file_assistant.OdexFileExists()) {
-    odex_file_exists = true;
-    if (oat_file_exists) {
-      status << "] ";
-    }
-    status << *oat_file_assistant.OdexFileName() << " [compilation_filter=";
-    status << CompilerFilter::NameOfFilter(oat_file_assistant.OdexFileCompilerFilter());
-    status << ", status=" << oat_file_assistant.OdexFileStatus();
-  }
-
-  if (!oat_file_exists && !odex_file_exists) {
-    status << "invalid[";
-  }
-
-  status << "]";
-  return env->NewStringUTF(status.str().c_str());
+  return env->NewStringUTF(oat_file_assistant.GetStatusDump().c_str());
 }
 
 static jint DexFile_getDexOptNeeded(JNIEnv* env,
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 404e5ce..bdf8b0e 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -719,7 +719,7 @@
     dlopen_handle_ = android_dlopen_ext(absolute_path.get(), RTLD_NOW, &extinfo);
 #else
     UNUSED(oat_file_begin);
-    static_assert(!kIsTargetBuild, "host_dlopen_handles_ will leak handles");
+    static_assert(!kIsTargetBuild || kIsTargetLinux, "host_dlopen_handles_ will leak handles");
     MutexLock mu(Thread::Current(), *Locks::host_dlopen_handles_lock_);
     dlopen_handle_ = dlopen(absolute_path.get(), RTLD_NOW);
     if (dlopen_handle_ != nullptr) {
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 0679360..4d1e1ea 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -16,6 +16,8 @@
 
 #include "oat_file_assistant.h"
 
+#include <sstream>
+
 #include <sys/stat.h>
 #include "base/logging.h"
 #include "base/stringprintf.h"
@@ -34,15 +36,21 @@
 
 std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStatus status) {
   switch (status) {
-    case OatFileAssistant::kOatOutOfDate:
-      stream << "kOatOutOfDate";
+    case OatFileAssistant::kOatCannotOpen:
+      stream << "kOatCannotOpen";
+      break;
+    case OatFileAssistant::kOatDexOutOfDate:
+      stream << "kOatDexOutOfDate";
+      break;
+    case OatFileAssistant::kOatBootImageOutOfDate:
+      stream << "kOatBootImageOutOfDate";
+      break;
+    case OatFileAssistant::kOatRelocationOutOfDate:
+      stream << "kOatRelocationOutOfDate";
       break;
     case OatFileAssistant::kOatUpToDate:
       stream << "kOatUpToDate";
       break;
-    case OatFileAssistant::kOatNeedsRelocation:
-      stream << "kOatNeedsRelocation";
-      break;
     default:
       UNREACHABLE();
   }
@@ -60,7 +68,10 @@
                                    const char* oat_location,
                                    const InstructionSet isa,
                                    bool load_executable)
-    : isa_(isa), load_executable_(load_executable), odex_(this), oat_(this) {
+    : isa_(isa),
+      load_executable_(load_executable),
+      odex_(this, /*is_oat_location*/ false),
+      oat_(this, /*is_oat_location*/ true) {
   CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
   dex_location_.assign(dex_location);
 
@@ -135,51 +146,13 @@
   return true;
 }
 
-OatFileAssistant::DexOptNeeded
-OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, bool profile_changed) {
-  bool compilation_desired = CompilerFilter::IsBytecodeCompilationEnabled(target);
-
-  // See if the oat file is in good shape as is.
-  bool oat_okay = oat_.CompilerFilterIsOkay(target, profile_changed);
-  if (oat_okay) {
-    if (compilation_desired) {
-      if (oat_.IsUpToDate()) {
-        return kNoDexOptNeeded;
-      }
-    } else {
-      if (!oat_.IsOutOfDate()) {
-        return kNoDexOptNeeded;
-      }
-    }
+int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, bool profile_changed) {
+  OatFileInfo& info = GetBestInfo();
+  DexOptNeeded dexopt_needed = info.GetDexOptNeeded(target, profile_changed);
+  if (info.IsOatLocation() || dexopt_needed == kDex2OatFromScratch) {
+    return dexopt_needed;
   }
-
-  // See if the odex file is in good shape as is.
-  bool odex_okay = odex_.CompilerFilterIsOkay(target, profile_changed);
-  if (odex_okay) {
-    if (compilation_desired) {
-      if (odex_.IsUpToDate()) {
-        return kNoDexOptNeeded;
-      }
-    } else {
-      if (!odex_.IsOutOfDate()) {
-        return kNoDexOptNeeded;
-      }
-    }
-  }
-
-  // See if we can get an up-to-date file by running patchoat.
-  if (compilation_desired) {
-    if (odex_okay && odex_.NeedsRelocation() && odex_.HasPatchInfo()) {
-      return kPatchOatNeeded;
-    }
-
-    if (oat_okay && oat_.NeedsRelocation() && oat_.HasPatchInfo()) {
-      return kSelfPatchOatNeeded;
-    }
-  }
-
-  // We can only run dex2oat if there are original dex files.
-  return HasOriginalDexFiles() ? kDex2OatNeeded : kNoDexOptNeeded;
+  return -dexopt_needed;
 }
 
 // Figure out the currently specified compile filter option in the runtime.
@@ -205,7 +178,7 @@
 }
 
 bool OatFileAssistant::IsUpToDate() {
-  return OatFileIsUpToDate() || OdexFileIsUpToDate();
+  return GetBestInfo().Status() == kOatUpToDate;
 }
 
 OatFileAssistant::ResultOfAttemptToUpdate
@@ -215,59 +188,66 @@
     return kUpdateNotAttempted;
   }
 
-  switch (GetDexOptNeeded(target, profile_changed)) {
-    case kNoDexOptNeeded: return kUpdateSucceeded;
-    case kDex2OatNeeded: return GenerateOatFile(error_msg);
-    case kPatchOatNeeded: return RelocateOatFile(odex_.Filename(), error_msg);
-    case kSelfPatchOatNeeded: return RelocateOatFile(oat_.Filename(), error_msg);
+  OatFileInfo& info = GetBestInfo();
+  switch (info.GetDexOptNeeded(target, profile_changed)) {
+    case kNoDexOptNeeded:
+      return kUpdateSucceeded;
+
+    // TODO: For now, don't bother with all the different ways we can call
+    // dex2oat to generate the oat file. Always generate the oat file as if it
+    // were kDex2OatFromScratch.
+    case kDex2OatFromScratch:
+    case kDex2OatForBootImage:
+    case kDex2OatForRelocation:
+    case kDex2OatForFilter:
+      return GenerateOatFile(error_msg);
+
+    case kPatchoatForRelocation: {
+      return RelocateOatFile(info.Filename(), error_msg);
+    }
   }
   UNREACHABLE();
 }
 
 std::unique_ptr<OatFile> OatFileAssistant::GetBestOatFile() {
-  // The best oat files are, in descending order of bestness:
-  // 1. Properly relocated files. These may be opened executable.
-  // 2. Not out-of-date files that are already opened non-executable.
-  // 3. Not out-of-date files that we must reopen non-executable.
+  return GetBestInfo().ReleaseFileForUse();
+}
 
-  if (oat_.IsUpToDate()) {
-    return oat_.ReleaseFile();
+std::string OatFileAssistant::GetStatusDump() {
+  std::ostringstream status;
+  bool oat_file_exists = false;
+  bool odex_file_exists = false;
+  if (oat_.Status() != kOatCannotOpen) {
+    // If we can open the file, neither Filename nor GetFile should return null.
+    CHECK(oat_.Filename() != nullptr);
+    CHECK(oat_.GetFile() != nullptr);
+
+    oat_file_exists = true;
+    status << *oat_.Filename() << " [compilation_filter=";
+    status << CompilerFilter::NameOfFilter(oat_.GetFile()->GetCompilerFilter());
+    status << ", status=" << oat_.Status();
   }
 
-  if (odex_.IsUpToDate()) {
-    return odex_.ReleaseFile();
-  }
+  if (odex_.Status() != kOatCannotOpen) {
+    // If we can open the file, neither Filename nor GetFile should return null.
+    CHECK(odex_.Filename() != nullptr);
+    CHECK(odex_.GetFile() != nullptr);
 
-  VLOG(oat) << "Oat File Assistant: No relocated oat file found,"
-    << " attempting to fall back to interpreting oat file instead.";
-
-  if (!oat_.IsOutOfDate() && !oat_.IsExecutable()) {
-    return oat_.ReleaseFile();
-  }
-
-  if (!odex_.IsOutOfDate() && !odex_.IsExecutable()) {
-    return odex_.ReleaseFile();
-  }
-
-  if (!oat_.IsOutOfDate()) {
-    load_executable_ = false;
-    oat_.Reset();
-    if (!oat_.IsOutOfDate()) {
-      CHECK(!oat_.IsExecutable());
-      return oat_.ReleaseFile();
+    odex_file_exists = true;
+    if (oat_file_exists) {
+      status << "] ";
     }
+    status << *odex_.Filename() << " [compilation_filter=";
+    status << CompilerFilter::NameOfFilter(odex_.GetFile()->GetCompilerFilter());
+    status << ", status=" << odex_.Status();
   }
 
-  if (!odex_.IsOutOfDate()) {
-    load_executable_ = false;
-    odex_.Reset();
-    if (!odex_.IsOutOfDate()) {
-      CHECK(!odex_.IsExecutable());
-      return odex_.ReleaseFile();
-    }
+  if (!oat_file_exists && !odex_file_exists) {
+    status << "invalid[";
   }
 
-  return std::unique_ptr<OatFile>();
+  status << "]";
+  return status.str();
 }
 
 std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
@@ -317,62 +297,14 @@
   return has_original_dex_files_;
 }
 
-const std::string* OatFileAssistant::OdexFileName() {
-  return odex_.Filename();
-}
-
-bool OatFileAssistant::OdexFileExists() {
-  return odex_.Exists();
-}
-
 OatFileAssistant::OatStatus OatFileAssistant::OdexFileStatus() {
   return odex_.Status();
 }
 
-bool OatFileAssistant::OdexFileIsOutOfDate() {
-  return odex_.IsOutOfDate();
-}
-
-bool OatFileAssistant::OdexFileNeedsRelocation() {
-  return odex_.NeedsRelocation();
-}
-
-bool OatFileAssistant::OdexFileIsUpToDate() {
-  return odex_.IsUpToDate();
-}
-
-CompilerFilter::Filter OatFileAssistant::OdexFileCompilerFilter() {
-  return odex_.CompilerFilter();
-}
-
-const std::string* OatFileAssistant::OatFileName() {
-  return oat_.Filename();
-}
-
-bool OatFileAssistant::OatFileExists() {
-  return oat_.Exists();
-}
-
 OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() {
   return oat_.Status();
 }
 
-bool OatFileAssistant::OatFileIsOutOfDate() {
-  return oat_.IsOutOfDate();
-}
-
-bool OatFileAssistant::OatFileNeedsRelocation() {
-  return oat_.NeedsRelocation();
-}
-
-bool OatFileAssistant::OatFileIsUpToDate() {
-  return oat_.IsUpToDate();
-}
-
-CompilerFilter::Filter OatFileAssistant::OatFileCompilerFilter() {
-  return oat_.CompilerFilter();
-}
-
 OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) {
   // Verify the dex checksum.
   // Note: GetOatDexFile will return null if the dex checksum doesn't match
@@ -383,7 +315,7 @@
       dex_location_.c_str(), dex_checksum_pointer, &error_msg);
   if (oat_dex_file == nullptr) {
     VLOG(oat) << error_msg;
-    return kOatOutOfDate;
+    return kOatDexOutOfDate;
   }
 
   // Verify the dex checksums for any secondary multidex files
@@ -406,7 +338,7 @@
           << secondary_dex_location
           << ". Expected: " << expected_secondary_checksum
           << ", Actual: " << actual_secondary_checksum;
-        return kOatOutOfDate;
+        return kOatDexOutOfDate;
       }
     } else {
       // If we can't get the checksum for the secondary location, we assume
@@ -425,7 +357,7 @@
       VLOG(oat) << "No image for oat image checksum to match against.";
 
       if (HasOriginalDexFiles()) {
-        return kOatOutOfDate;
+        return kOatBootImageOutOfDate;
       }
 
       // If there is no original dex file to fall back to, grudgingly accept
@@ -439,7 +371,7 @@
     } else if (file.GetOatHeader().GetImageFileLocationOatChecksum()
         != GetCombinedImageChecksum()) {
       VLOG(oat) << "Oat image checksum does not match image checksum.";
-      return kOatOutOfDate;
+      return kOatBootImageOutOfDate;
     }
   } else {
     VLOG(oat) << "Image checksum test skipped for compiler filter " << current_compiler_filter;
@@ -450,7 +382,7 @@
       const ImageInfo* image_info = GetImageInfo();
       if (image_info == nullptr) {
         VLOG(oat) << "No image to check oat relocation against.";
-        return kOatNeedsRelocation;
+        return kOatRelocationOutOfDate;
       }
 
       // Verify the oat_data_begin recorded for the image in the oat file matches
@@ -462,7 +394,7 @@
           ": Oat file image oat_data_begin (" << oat_data_begin << ")"
           << " does not match actual image oat_data_begin ("
           << image_info->oat_data_begin << ")";
-        return kOatNeedsRelocation;
+        return kOatRelocationOutOfDate;
       }
 
       // Verify the oat_patch_delta recorded for the image in the oat file matches
@@ -473,7 +405,7 @@
           ": Oat file image patch delta (" << oat_patch_delta << ")"
           << " does not match actual image patch delta ("
           << image_info->patch_delta << ")";
-        return kOatNeedsRelocation;
+        return kOatRelocationOutOfDate;
       }
     } else {
       // Oat files compiled in PIC mode do not require relocation.
@@ -841,6 +773,11 @@
   return combined_image_checksum_;
 }
 
+OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() {
+  bool use_oat = oat_.IsUseable() || odex_.Status() == kOatCannotOpen;
+  return use_oat ? oat_ : odex_;
+}
+
 std::unique_ptr<gc::space::ImageSpace> OatFileAssistant::OpenImageSpace(const OatFile* oat_file) {
   DCHECK(oat_file != nullptr);
   std::string art_file = ReplaceFileExtension(oat_file->GetLocation(), "art");
@@ -857,16 +794,29 @@
   return ret;
 }
 
-OatFileAssistant::OatFileInfo::OatFileInfo(OatFileAssistant* oat_file_assistant)
-  : oat_file_assistant_(oat_file_assistant)
+OatFileAssistant::OatFileInfo::OatFileInfo(OatFileAssistant* oat_file_assistant,
+                                           bool is_oat_location)
+  : oat_file_assistant_(oat_file_assistant), is_oat_location_(is_oat_location)
 {}
 
+bool OatFileAssistant::OatFileInfo::IsOatLocation() {
+  return is_oat_location_;
+}
+
 const std::string* OatFileAssistant::OatFileInfo::Filename() {
   return filename_provided_ ? &filename_ : nullptr;
 }
 
-bool OatFileAssistant::OatFileInfo::Exists() {
-  return GetFile() != nullptr;
+bool OatFileAssistant::OatFileInfo::IsUseable() {
+  switch (Status()) {
+    case kOatCannotOpen:
+    case kOatDexOutOfDate:
+    case kOatBootImageOutOfDate: return false;
+
+    case kOatRelocationOutOfDate:
+    case kOatUpToDate: return true;
+  }
+  UNREACHABLE();
 }
 
 OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status() {
@@ -874,7 +824,7 @@
     status_attempted_ = true;
     const OatFile* file = GetFile();
     if (file == nullptr) {
-      status_ = kOatOutOfDate;
+      status_ = kOatCannotOpen;
     } else {
       status_ = oat_file_assistant_->GivenOatFileStatus(*file);
       VLOG(oat) << file->GetLocation() << " is " << status_
@@ -884,22 +834,46 @@
   return status_;
 }
 
-bool OatFileAssistant::OatFileInfo::IsOutOfDate() {
-  return Status() == kOatOutOfDate;
-}
+OatFileAssistant::DexOptNeeded OatFileAssistant::OatFileInfo::GetDexOptNeeded(
+    CompilerFilter::Filter target, bool profile_changed) {
+  bool compilation_desired = CompilerFilter::IsBytecodeCompilationEnabled(target);
+  bool filter_okay = CompilerFilterIsOkay(target, profile_changed);
 
-bool OatFileAssistant::OatFileInfo::NeedsRelocation() {
-  return Status() == kOatNeedsRelocation;
-}
+  if (filter_okay && Status() == kOatUpToDate) {
+    // The oat file is in good shape as is.
+    return kNoDexOptNeeded;
+  }
 
-bool OatFileAssistant::OatFileInfo::IsUpToDate() {
-  return Status() == kOatUpToDate;
-}
+  if (filter_okay && !compilation_desired && Status() == kOatRelocationOutOfDate) {
+    // If no compilation is desired, then it doesn't matter if the oat
+    // file needs relocation. It's in good shape as is.
+    return kNoDexOptNeeded;
+  }
 
-CompilerFilter::Filter OatFileAssistant::OatFileInfo::CompilerFilter() {
-  const OatFile* file = GetFile();
-  CHECK(file != nullptr);
-  return file->GetCompilerFilter();
+  if (filter_okay && Status() == kOatRelocationOutOfDate && HasPatchInfo()) {
+    return kPatchoatForRelocation;
+  }
+
+  if (oat_file_assistant_->HasOriginalDexFiles()) {
+    // Run dex2oat for relocation if we didn't have the patch info necessary
+    // to use patchoat.
+    if (filter_okay && Status() == kOatRelocationOutOfDate) {
+      return kDex2OatForRelocation;
+    }
+
+    if (IsUseable()) {
+      return kDex2OatForFilter;
+    }
+
+    if (Status() == kOatBootImageOutOfDate) {
+      return kDex2OatForBootImage;
+    }
+
+    return kDex2OatFromScratch;
+  }
+
+  // Otherwise there is nothing we can do, even if we want to.
+  return kNoDexOptNeeded;
 }
 
 const OatFile* OatFileAssistant::OatFileInfo::GetFile() {
@@ -967,5 +941,31 @@
   return std::move(file_);
 }
 
+std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFileForUse() {
+  if (Status() == kOatUpToDate) {
+    return ReleaseFile();
+  }
+
+  VLOG(oat) << "Oat File Assistant: No relocated oat file found,"
+    << " attempting to fall back to interpreting oat file instead.";
+
+  if (Status() == kOatRelocationOutOfDate && !IsExecutable()) {
+    return ReleaseFile();
+  }
+
+  if (Status() == kOatRelocationOutOfDate) {
+    // We are loading an oat file for runtime use that needs relocation.
+    // Reload the file non-executable to ensure that we interpret out of the
+    // dex code in the oat file rather than trying to execute the unrelocated
+    // compiled code.
+    oat_file_assistant_->load_executable_ = false;
+    Reset();
+    if (IsUseable()) {
+      CHECK(!IsExecutable());
+      return ReleaseFile();
+    }
+  }
+  return std::unique_ptr<OatFile>();
+}
 }  // namespace art
 
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 3f018dc..bed1edc 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -48,41 +48,56 @@
 class OatFileAssistant {
  public:
   enum DexOptNeeded {
-    // kNoDexOptNeeded - The code for this dex location is up to date and can
-    // be used as is.
+    // No dexopt should (or can) be done to update the apk/jar.
     // Matches Java: dalvik.system.DexFile.NO_DEXOPT_NEEDED = 0
     kNoDexOptNeeded = 0,
 
-    // kDex2OatNeeded - In order to make the code for this dex location up to
-    // date, dex2oat must be run on the dex file.
-    // Matches Java: dalvik.system.DexFile.DEX2OAT_NEEDED = 1
-    kDex2OatNeeded = 1,
+    // dex2oat should be run to update the apk/jar from scratch.
+    // Matches Java: dalvik.system.DexFile.DEX2OAT_FROM_SCRATCH = 1
+    kDex2OatFromScratch = 1,
 
-    // kPatchOatNeeded - In order to make the code for this dex location up to
-    // date, patchoat must be run on the odex file.
-    // Matches Java: dalvik.system.DexFile.PATCHOAT_NEEDED = 2
-    kPatchOatNeeded = 2,
+    // dex2oat should be run to update the apk/jar because the existing code
+    // is out of date with respect to the boot image.
+    // Matches Java: dalvik.system.DexFile.DEX2OAT_FOR_BOOT_IMAGE
+    kDex2OatForBootImage = 2,
 
-    // kSelfPatchOatNeeded - In order to make the code for this dex location
-    // up to date, patchoat must be run on the oat file.
-    // Matches Java: dalvik.system.DexFile.SELF_PATCHOAT_NEEDED = 3
-    kSelfPatchOatNeeded = 3,
+    // dex2oat should be run to update the apk/jar because the existing code
+    // is out of date with respect to the target compiler filter.
+    // Matches Java: dalvik.system.DexFile.DEX2OAT_FOR_FILTER
+    kDex2OatForFilter = 3,
+
+    // dex2oat should be run to update the apk/jar because the existing code
+    // is not relocated to match the boot image and does not have the
+    // necessary patch information to use patchoat.
+    // Matches Java: dalvik.system.DexFile.DEX2OAT_FOR_RELOCATION
+    kDex2OatForRelocation = 4,
+
+    // patchoat should be run to update the apk/jar.
+    // Matches Java: dalvik.system.DexFile.PATCHOAT_FOR_RELOCATION
+    kPatchoatForRelocation = 5,
   };
 
   enum OatStatus {
-    // kOatOutOfDate - An oat file is said to be out of date if the file does
-    // not exist, is out of date with respect to the dex file or boot image,
-    // or does not meet the target compilation type.
-    kOatOutOfDate,
+    // kOatCannotOpen - The oat file cannot be opened, because it does not
+    // exist, is unreadable, or otherwise corrupted.
+    kOatCannotOpen,
 
-    // kOatNeedsRelocation - An oat file is said to need relocation if the
-    // code is up to date, but not yet properly relocated for address space
-    // layout randomization (ASLR). In this case, the oat file is neither
-    // "out of date" nor "up to date".
-    kOatNeedsRelocation,
+    // kOatDexOutOfDate - The oat file is out of date with respect to the dex file.
+    kOatDexOutOfDate,
 
-    // kOatUpToDate - An oat file is said to be up to date if it is not out of
-    // date and has been properly relocated for the purposes of ASLR.
+    // kOatBootImageOutOfDate - The oat file is up to date with respect to the
+    // dex file, but is out of date with respect to the boot image.
+    kOatBootImageOutOfDate,
+
+    // kOatRelocationOutOfDate - The oat file is up to date with respect to
+    // the dex file and boot image, but contains compiled code that has the
+    // wrong patch delta with respect to the boot image. Patchoat should be
+    // run on the oat file to update the patch delta of the compiled code to
+    // match the boot image.
+    kOatRelocationOutOfDate,
+
+    // kOatUpToDate - The oat file is completely up to date with respect to
+    // the dex file and boot image.
     kOatUpToDate,
   };
 
@@ -142,8 +157,10 @@
   // dex location that is at least as good as an oat file generated with the
   // given compiler filter. profile_changed should be true to indicate the
   // profile has recently changed for this dex location.
-  DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
-                               bool profile_changed = false);
+  // Returns a positive status code if the status refers to the oat file in
+  // the oat location. Returns a negative status code if the status refers to
+  // the oat file in the odex location.
+  int GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter, bool profile_changed = false);
 
   // Returns true if there is up-to-date code for this dex location,
   // irrespective of the compiler filter of the up-to-date code.
@@ -179,6 +196,10 @@
   // the OatFileAssistant object.
   std::unique_ptr<OatFile> GetBestOatFile();
 
+  // Returns a human readable description of the status of the code for the
+  // dex file. The returned description is for debugging purposes only.
+  std::string GetStatusDump();
+
   // Open and returns an image space associated with the oat file.
   static std::unique_ptr<gc::space::ImageSpace> OpenImageSpace(const OatFile* oat_file);
 
@@ -205,43 +226,16 @@
   // really an oat file. The odex file will often, but not always, have a
   // patch delta of 0 and need to be relocated before use for the purposes of
   // ASLR. The odex file is treated as if it were read-only.
-  // These methods return the location and status of the odex file for the dex
-  // location.
-  // Notes:
-  //  * OdexFileName may return null if the odex file name could not be
-  //    determined.
-  const std::string* OdexFileName();
-  bool OdexFileExists();
+  //
+  // Returns the status of the odex file for the dex location.
   OatStatus OdexFileStatus();
-  bool OdexFileIsOutOfDate();
-  bool OdexFileNeedsRelocation();
-  bool OdexFileIsUpToDate();
-  // Must only be called if the associated odex file exists, i.e, if
-  // |OdexFileExists() == true|.
-  CompilerFilter::Filter OdexFileCompilerFilter();
 
   // When the dex files is compiled on the target device, the oat file is the
   // result. The oat file will have been relocated to some
   // (possibly-out-of-date) offset for ASLR.
-  // These methods return the location and status of the target oat file for
-  // the dex location.
   //
-  // Notes:
-  //  * OatFileName may return null if the oat file name could not be
-  //    determined.
-  const std::string* OatFileName();
-  bool OatFileExists();
+  // Returns the status of the oat file for the dex location.
   OatStatus OatFileStatus();
-  bool OatFileIsOutOfDate();
-  bool OatFileNeedsRelocation();
-  bool OatFileIsUpToDate();
-  // Must only be called if the associated oat file exists, i.e, if
-  // |OatFileExists() == true|.
-  CompilerFilter::Filter OatFileCompilerFilter();
-
-  // Return the status for a given opened oat file with respect to the dex
-  // location.
-  OatStatus GivenOatFileStatus(const OatFile& file);
 
   // Generates the oat file by relocation from the named input file.
   // This does not check the current status before attempting to relocate the
@@ -311,29 +305,39 @@
     // Initially the info is for no file in particular. It will treat the
     // file as out of date until Reset is called with a real filename to use
     // the cache for.
-    explicit OatFileInfo(OatFileAssistant* oat_file_assistant);
+    // Pass true for is_oat_location if the information associated with this
+    // OatFileInfo is for the oat location, as opposed to the odex location.
+    OatFileInfo(OatFileAssistant* oat_file_assistant, bool is_oat_location);
+
+    bool IsOatLocation();
 
     const std::string* Filename();
-    bool Exists();
+
+    // Returns true if this oat file can be used for running code. The oat
+    // file can be used for running code as long as it is not out of date with
+    // respect to the dex code or boot image. An oat file that is out of date
+    // with respect to relocation is considered useable, because it's possible
+    // to interpret the dex code rather than run the unrelocated compiled
+    // code.
+    bool IsUseable();
+
+    // Returns the status of this oat file.
     OatStatus Status();
-    bool IsOutOfDate();
-    bool NeedsRelocation();
-    bool IsUpToDate();
-    // Must only be called if the associated file exists, i.e, if
-    // |Exists() == true|.
-    CompilerFilter::Filter CompilerFilter();
+
+    // Return the DexOptNeeded value for this oat file with respect to the
+    // given target_compilation_filter.
+    // profile_changed should be true to indicate the profile has recently
+    // changed for this dex location.
+    // If patchoat is needed, this function will return the kPatchOatNeeded
+    // status, not the kSelfPatchOatNeeded status.
+    DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
+                                 bool profile_changed);
 
     // Returns the loaded file.
     // Loads the file if needed. Returns null if the file failed to load.
     // The caller shouldn't clean up or free the returned pointer.
     const OatFile* GetFile();
 
-    // Returns true if the compiler filter used to generate the file is at
-    // least as good as the given target filter. profile_changed should be
-    // true to indicate the profile has recently changed for this dex
-    // location.
-    bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed);
-
     // Returns true if the file is opened executable.
     bool IsExecutable();
 
@@ -348,6 +352,23 @@
     // file with the given filename.
     void Reset(const std::string& filename);
 
+    // Release the loaded oat file for runtime use.
+    // Returns null if the oat file hasn't been loaded or is out of date.
+    // Ensures the returned file is not loaded executable if it has unuseable
+    // compiled code.
+    //
+    // After this call, no other methods of the OatFileInfo should be
+    // called, because access to the loaded oat file has been taken away from
+    // the OatFileInfo object.
+    std::unique_ptr<OatFile> ReleaseFileForUse();
+
+   private:
+    // Returns true if the compiler filter used to generate the file is at
+    // least as good as the given target filter. profile_changed should be
+    // true to indicate the profile has recently changed for this dex
+    // location.
+    bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed);
+
     // Release the loaded oat file.
     // Returns null if the oat file hasn't been loaded.
     //
@@ -356,8 +377,8 @@
     // the OatFileInfo object.
     std::unique_ptr<OatFile> ReleaseFile();
 
-   private:
     OatFileAssistant* oat_file_assistant_;
+    const bool is_oat_location_;
 
     bool filename_provided_ = false;
     std::string filename_;
@@ -374,6 +395,13 @@
     bool file_released_ = false;
   };
 
+  // Return info for the best oat file.
+  OatFileInfo& GetBestInfo();
+
+  // Return the status for a given opened oat file with respect to the dex
+  // location.
+  OatStatus GivenOatFileStatus(const OatFile& file);
+
   // Returns the current image location.
   // Returns an empty string if the image location could not be retrieved.
   //
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index d4337b9..5730cf2 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -49,9 +49,9 @@
   // Pre-Relocate the image to a known non-zero offset so we don't have to
   // deal with the runtime randomly relocating the image by 0 and messing up
   // the expected results of the tests.
-  bool PreRelocateImage(std::string* error_msg) {
+  bool PreRelocateImage(const std::string& image_location, std::string* error_msg) {
     std::string image;
-    if (!GetCachedImageFile(&image, error_msg)) {
+    if (!GetCachedImageFile(image_location, &image, error_msg)) {
       return false;
     }
 
@@ -60,7 +60,7 @@
 
     std::vector<std::string> argv;
     argv.push_back(patchoat);
-    argv.push_back("--input-image-location=" + GetImageLocation());
+    argv.push_back("--input-image-location=" + image_location);
     argv.push_back("--output-image-file=" + image);
     argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)));
     argv.push_back("--base-offset-delta=0x00008000");
@@ -69,8 +69,8 @@
 
   virtual void PreRuntimeCreate() {
     std::string error_msg;
-    ASSERT_TRUE(PreRelocateImage(&error_msg)) << error_msg;
-
+    ASSERT_TRUE(PreRelocateImage(GetImageLocation(), &error_msg)) << error_msg;
+    ASSERT_TRUE(PreRelocateImage(GetImageLocation2(), &error_msg)) << error_msg;
     UnreserveImageSpace();
   }
 
@@ -78,24 +78,32 @@
     ReserveImageSpace();
   }
 
-  // Generate a non-PIC odex file for the purposes of test.
-  // The generated odex file will be un-relocated.
-  void GenerateOdexForTest(const std::string& dex_location,
-                           const std::string& odex_location,
-                           CompilerFilter::Filter filter,
-                           bool pic = false,
-                           bool with_patch_info = true) {
-    // Temporarily redirect the dalvik cache so dex2oat doesn't find the
-    // relocated image file.
+  // Generate an oat file for the purposes of test.
+  void GenerateOatForTest(const std::string& dex_location,
+                          const std::string& oat_location,
+                          CompilerFilter::Filter filter,
+                          bool relocate,
+                          bool pic,
+                          bool with_patch_info,
+                          bool with_alternate_image) {
     std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA));
     std::string dalvik_cache_tmp = dalvik_cache + ".redirected";
-    ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno);
+
+    if (!relocate) {
+      // Temporarily redirect the dalvik cache so dex2oat doesn't find the
+      // relocated image file.
+      ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno);
+    }
 
     std::vector<std::string> args;
     args.push_back("--dex-file=" + dex_location);
-    args.push_back("--oat-file=" + odex_location);
+    args.push_back("--oat-file=" + oat_location);
     args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
     args.push_back("--runtime-arg");
+
+    // Use -Xnorelocate regardless of the relocate argument.
+    // We control relocation by redirecting the dalvik cache when needed
+    // rather than use this flag.
     args.push_back("-Xnorelocate");
 
     if (pic) {
@@ -106,14 +114,22 @@
       args.push_back("--include-patch-information");
     }
 
+    std::string image_location = GetImageLocation();
+    if (with_alternate_image) {
+      args.push_back("--boot-image=" + GetImageLocation2());
+    }
+
     std::string error_msg;
     ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
-    ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno);
 
-    // Verify the odex file was generated as expected and really is
-    // unrelocated.
-    std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
-                                                     odex_location.c_str(),
+    if (!relocate) {
+      // Restore the dalvik cache if needed.
+      ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno);
+    }
+
+    // Verify the odex file was generated as expected.
+    std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
+                                                     oat_location.c_str(),
                                                      nullptr,
                                                      nullptr,
                                                      false,
@@ -125,24 +141,59 @@
     EXPECT_EQ(with_patch_info, odex_file->HasPatchInfo());
     EXPECT_EQ(filter, odex_file->GetCompilerFilter());
 
-    if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
-      const std::vector<gc::space::ImageSpace*> image_spaces =
-        Runtime::Current()->GetHeap()->GetBootImageSpaces();
-      ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr);
-      const ImageHeader& image_header = image_spaces[0]->GetImageHeader();
-      const OatHeader& oat_header = odex_file->GetOatHeader();
-      uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum();
-      EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
-      EXPECT_NE(reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()),
-          oat_header.GetImageFileLocationOatDataBegin());
-      EXPECT_NE(image_header.GetPatchDelta(), oat_header.GetImagePatchDelta());
+    std::unique_ptr<ImageHeader> image_header(
+            gc::space::ImageSpace::ReadImageHeader(image_location.c_str(),
+                                                   kRuntimeISA,
+                                                   &error_msg));
+    ASSERT_TRUE(image_header != nullptr) << error_msg;
+    const OatHeader& oat_header = odex_file->GetOatHeader();
+    uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum();
+
+    if (CompilerFilter::DependsOnImageChecksum(filter)) {
+      if (with_alternate_image) {
+        EXPECT_NE(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
+      } else {
+        EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
+      }
     }
+
+    if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
+      if (relocate) {
+        EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+            oat_header.GetImageFileLocationOatDataBegin());
+        EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+      } else {
+        EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+            oat_header.GetImageFileLocationOatDataBegin());
+        EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+      }
+    }
+  }
+
+  // Generate a non-PIC odex file for the purposes of test.
+  // The generated odex file will be un-relocated.
+  void GenerateOdexForTest(const std::string& dex_location,
+                           const std::string& odex_location,
+                           CompilerFilter::Filter filter) {
+    GenerateOatForTest(dex_location,
+                       odex_location,
+                       filter,
+                       /*relocate*/false,
+                       /*pic*/false,
+                       /*with_patch_info*/true,
+                       /*with_alternate_image*/false);
   }
 
   void GeneratePicOdexForTest(const std::string& dex_location,
                               const std::string& odex_location,
                               CompilerFilter::Filter filter) {
-    GenerateOdexForTest(dex_location, odex_location, filter, true, false);
+    GenerateOatForTest(dex_location,
+                       odex_location,
+                       filter,
+                       /*relocate*/false,
+                       /*pic*/true,
+                       /*with_patch_info*/false,
+                       /*with_alternate_image*/false);
   }
 
   // Generate a non-PIC odex file without patch information for the purposes
@@ -150,7 +201,43 @@
   void GenerateNoPatchOdexForTest(const std::string& dex_location,
                                   const std::string& odex_location,
                                   CompilerFilter::Filter filter) {
-    GenerateOdexForTest(dex_location, odex_location, filter, false, false);
+    GenerateOatForTest(dex_location,
+                       odex_location,
+                       filter,
+                       /*relocate*/false,
+                       /*pic*/false,
+                       /*with_patch_info*/false,
+                       /*with_alternate_image*/false);
+  }
+
+  // Generate an oat file in the oat location.
+  void GenerateOatForTest(const char* dex_location,
+                          CompilerFilter::Filter filter,
+                          bool relocate,
+                          bool pic,
+                          bool with_patch_info,
+                          bool with_alternate_image) {
+    std::string oat_location;
+    std::string error_msg;
+    ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
+          dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
+    GenerateOatForTest(dex_location,
+                       oat_location,
+                       filter,
+                       relocate,
+                       pic,
+                       with_patch_info,
+                       with_alternate_image);
+  }
+
+  // Generate a standard oat file in the oat location.
+  void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) {
+    GenerateOatForTest(dex_location,
+                       filter,
+                       /*relocate*/true,
+                       /*pic*/false,
+                       /*with_patch_info*/false,
+                       /*with_alternate_image*/false);
   }
 
  private:
@@ -211,36 +298,6 @@
   }
 };
 
-// Generate an oat file for the purposes of test, as opposed to testing
-// generation of oat files.
-static void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) {
-  // Use an oat file assistant to find the proper oat location.
-  std::string oat_location;
-  std::string error_msg;
-  ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
-        dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
-
-  std::vector<std::string> args;
-  args.push_back("--dex-file=" + std::string(dex_location));
-  args.push_back("--oat-file=" + oat_location);
-  args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
-  args.push_back("--runtime-arg");
-  args.push_back("-Xnorelocate");
-  ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
-
-  // Verify the oat file was generated as expected.
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location.c_str(),
-                                                  oat_location.c_str(),
-                                                  nullptr,
-                                                  nullptr,
-                                                  false,
-                                                  /*low_4gb*/false,
-                                                  dex_location,
-                                                  &error_msg));
-  ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
-  EXPECT_EQ(filter, oat_file->GetCompilerFilter());
-}
-
 // Case: We have a DEX file, but no OAT file for it.
 // Expect: The status is kDex2OatNeeded.
 TEST_F(OatFileAssistantTest, DexNoOat) {
@@ -249,26 +306,18 @@
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
 
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OdexFileStatus());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
-  EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OatFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
@@ -307,17 +356,11 @@
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
   EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
@@ -337,17 +380,12 @@
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
 
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
 }
 
 // Case: We have a DEX file and speed-profile OAT file for it.
@@ -364,19 +402,13 @@
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile, false));
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly, false));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile, true));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly, true));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
   EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
@@ -416,7 +448,7 @@
   Copy(GetMultiDexSrc2(), dex_location);
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed, false));
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
@@ -453,10 +485,10 @@
   EXPECT_EQ(2u, dex_files.size());
 }
 
-// Case: We have a DEX file and out-of-date OAT file.
-// Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, OatOutOfDate) {
-  std::string dex_location = GetScratchDir() + "/OatOutOfDate.jar";
+// Case: We have a DEX file and an OAT file out of date with respect to the
+// dex checksum.
+TEST_F(OatFileAssistantTest, OatDexOutOfDate) {
+  std::string dex_location = GetScratchDir() + "/OatDexOutOfDate.jar";
 
   // We create a dex, generate an oat for it, then overwrite the dex with a
   // different dex to make the oat out of date.
@@ -465,18 +497,68 @@
   Copy(GetDexSrc2(), dex_location);
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+}
+
+// Case: We have a DEX file and an OAT file out of date with respect to the
+// boot image.
+TEST_F(OatFileAssistantTest, OatImageOutOfDate) {
+  std::string dex_location = GetScratchDir() + "/OatImageOutOfDate.jar";
+
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOatForTest(dex_location.c_str(),
+                     CompilerFilter::kSpeed,
+                     /*relocate*/true,
+                     /*pic*/false,
+                     /*with_patch_info*/false,
+                     /*with_alternate_image*/true);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
+  EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
+  EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatBootImageOutOfDate, oat_file_assistant.OatFileStatus());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+}
+
+// Case: We have a DEX file and a verify-at-runtime OAT file out of date with
+// respect to the boot image.
+// It shouldn't matter that the OAT file is out of date, because it is
+// verify-at-runtime.
+TEST_F(OatFileAssistantTest, OatVerifyAtRuntimeImageOutOfDate) {
+  std::string dex_location = GetScratchDir() + "/OatVerifyAtRuntimeImageOutOfDate.jar";
+
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOatForTest(dex_location.c_str(),
+                     CompilerFilter::kVerifyAtRuntime,
+                     /*relocate*/true,
+                     /*pic*/false,
+                     /*with_patch_info*/false,
+                     /*with_alternate_image*/true);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
+  EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
@@ -495,17 +577,12 @@
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
-  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+  EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   // We should still be able to get the non-executable odex file to run from.
@@ -529,16 +606,12 @@
   // Verify the status.
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
 
-  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+  EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date.
@@ -551,12 +624,8 @@
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Verify we can load the dex files from it.
@@ -590,19 +659,14 @@
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
-  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+  EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,  // Can't run dex2oat because dex file is stripped.
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date.
@@ -617,14 +681,8 @@
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Verify we can load the dex files from it.
@@ -654,13 +712,8 @@
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date. This should have no effect.
@@ -673,13 +726,8 @@
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 }
 
@@ -699,20 +747,14 @@
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
-  EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded,
+  EXPECT_EQ(OatFileAssistant::kPatchoatForRelocation,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date.
@@ -725,14 +767,8 @@
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -757,7 +793,7 @@
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
       oat_location.c_str(), kRuntimeISA, true);
 
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatForRelocation,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   // Make the oat file up to date.
@@ -778,7 +814,7 @@
 
 // Case: We have a DEX file, an ODEX file and an OAT file, where the ODEX and
 // OAT files both have patch delta of 0.
-// Expect: It shouldn't crash, and status is kPatchOatNeeded.
+// Expect: It shouldn't crash, and status is kSelfPatchOatNeeded.
 TEST_F(OatFileAssistantTest, OdexOatOverlap) {
   std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
   std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex";
@@ -796,16 +832,15 @@
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
       oat_location.c_str(), kRuntimeISA, true);
 
-  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+  // kPatchoatForRelocation is expected rather than -kPatchoatForRelocation
+  // based on the assumption that the oat location is more up-to-date than the odex
+  // location, even if they both need relocation.
+  EXPECT_EQ(OatFileAssistant::kPatchoatForRelocation,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   // Things aren't relocated, so it should fall back to interpreted.
@@ -833,16 +868,12 @@
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
@@ -861,16 +892,12 @@
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
@@ -958,7 +985,7 @@
 
   // Verify it didn't create an oat in the default location.
   OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false);
-  EXPECT_FALSE(ofm.OatFileExists());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, ofm.OatFileStatus());
 }
 
 // Case: We have a DEX file but can't write the oat file.
@@ -1043,14 +1070,10 @@
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
 }
 
 // Case: Very short, non-existent Dex location.
@@ -1063,12 +1086,8 @@
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Trying to make it up to date should have no effect.
@@ -1087,16 +1106,12 @@
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
 
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
 }
 
 // A task to generate a dex location. Used by the RaceToGenerate test.
@@ -1226,7 +1241,7 @@
       oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
@@ -1263,6 +1278,15 @@
 // Verify the dexopt status values from dalvik.system.DexFile
 // match the OatFileAssistant::DexOptStatus values.
 TEST_F(OatFileAssistantTest, DexOptStatusValues) {
+  std::pair<OatFileAssistant::DexOptNeeded, const char*> mapping[] = {
+    {OatFileAssistant::kNoDexOptNeeded, "NO_DEXOPT_NEEDED"},
+    {OatFileAssistant::kDex2OatFromScratch, "DEX2OAT_FROM_SCRATCH"},
+    {OatFileAssistant::kDex2OatForBootImage, "DEX2OAT_FOR_BOOT_IMAGE"},
+    {OatFileAssistant::kDex2OatForFilter, "DEX2OAT_FOR_FILTER"},
+    {OatFileAssistant::kDex2OatForRelocation, "DEX2OAT_FOR_RELOCATION"},
+    {OatFileAssistant::kPatchoatForRelocation, "PATCHOAT_FOR_RELOCATION"}
+  };
+
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<1> hs(soa.Self());
   ClassLinker* linker = Runtime::Current()->GetClassLinker();
@@ -1271,35 +1295,16 @@
   ASSERT_FALSE(dexfile.Get() == nullptr);
   linker->EnsureInitialized(soa.Self(), dexfile, true, true);
 
-  ArtField* no_dexopt_needed = mirror::Class::FindStaticField(
-      soa.Self(), dexfile, "NO_DEXOPT_NEEDED", "I");
-  ASSERT_FALSE(no_dexopt_needed == nullptr);
-  EXPECT_EQ(no_dexopt_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, no_dexopt_needed->GetInt(dexfile.Get()));
-
-  ArtField* dex2oat_needed = mirror::Class::FindStaticField(
-      soa.Self(), dexfile, "DEX2OAT_NEEDED", "I");
-  ASSERT_FALSE(dex2oat_needed == nullptr);
-  EXPECT_EQ(dex2oat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, dex2oat_needed->GetInt(dexfile.Get()));
-
-  ArtField* patchoat_needed = mirror::Class::FindStaticField(
-      soa.Self(), dexfile, "PATCHOAT_NEEDED", "I");
-  ASSERT_FALSE(patchoat_needed == nullptr);
-  EXPECT_EQ(patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
-  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, patchoat_needed->GetInt(dexfile.Get()));
-
-  ArtField* self_patchoat_needed = mirror::Class::FindStaticField(
-      soa.Self(), dexfile, "SELF_PATCHOAT_NEEDED", "I");
-  ASSERT_FALSE(self_patchoat_needed == nullptr);
-  EXPECT_EQ(self_patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
-  EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, self_patchoat_needed->GetInt(dexfile.Get()));
+  for (std::pair<OatFileAssistant::DexOptNeeded, const char*> field : mapping) {
+    ArtField* art_field = mirror::Class::FindStaticField(
+        soa.Self(), dexfile, field.second, "I");
+    ASSERT_FALSE(art_field == nullptr);
+    EXPECT_EQ(art_field->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+    EXPECT_EQ(field.first, art_field->GetInt(dexfile.Get()));
+  }
 }
 
 // TODO: More Tests:
-//  * Image checksum change is out of date for kIntepretOnly, but not
-//    kVerifyAtRuntime. But target of kVerifyAtRuntime still says current
-//    kInterpretOnly is out of date.
 //  * Test class linker falls back to unquickened dex for DexNoOat
 //  * Test class linker falls back to unquickened dex for MultiDexNoOat
 //  * Test using secondary isa
@@ -1313,5 +1318,4 @@
 //    because it's unrelocated and no dex2oat
 //  * Test unrelocated specific target compilation type can be relocated to
 //    make it up to date.
-
 }  // namespace art
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 8a3bac7..8551791 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1782,6 +1782,9 @@
   intern_table_->ChangeWeakRootState(gc::kWeakRootStateNoReadsOrWrites);
   java_vm_->DisallowNewWeakGlobals();
   heap_->DisallowNewAllocationRecords();
+  if (GetJit() != nullptr) {
+    GetJit()->GetCodeCache()->DisallowInlineCacheAccess();
+  }
 
   // All other generic system-weak holders.
   for (gc::AbstractSystemWeakHolder* holder : system_weak_holders_) {
@@ -1795,6 +1798,9 @@
   intern_table_->ChangeWeakRootState(gc::kWeakRootStateNormal);  // TODO: Do this in the sweeping.
   java_vm_->AllowNewWeakGlobals();
   heap_->AllowNewAllocationRecords();
+  if (GetJit() != nullptr) {
+    GetJit()->GetCodeCache()->AllowInlineCacheAccess();
+  }
 
   // All other generic system-weak holders.
   for (gc::AbstractSystemWeakHolder* holder : system_weak_holders_) {
@@ -1810,6 +1816,9 @@
   intern_table_->BroadcastForNewInterns();
   java_vm_->BroadcastForNewWeakGlobals();
   heap_->BroadcastForNewAllocationRecords();
+  if (GetJit() != nullptr) {
+    GetJit()->GetCodeCache()->BroadcastForInlineCacheAccess();
+  }
 
   // All other generic system-weak holders.
   for (gc::AbstractSystemWeakHolder* holder : system_weak_holders_) {
@@ -2183,7 +2192,7 @@
 
 NO_RETURN
 void Runtime::Aborter(const char* abort_message) {
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
   android_set_abort_message(abort_message);
 #endif
   Runtime::Abort(abort_message);
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index b3dab58..843be92 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -20,6 +20,7 @@
 
 #include "base/logging.h"
 #include "base/unix_file/fd_file.h"
+#include "dex_file.h"
 
 namespace art {
 
@@ -73,10 +74,19 @@
     return nullptr;
   }
 
+  return Open(vdex_file->Fd(), vdex_length, vdex_filename, writable, low_4gb, error_msg);
+}
+
+VdexFile* VdexFile::Open(int file_fd,
+                         size_t vdex_length,
+                         const std::string& vdex_filename,
+                         bool writable,
+                         bool low_4gb,
+                         std::string* error_msg) {
   std::unique_ptr<MemMap> mmap(MemMap::MapFile(vdex_length,
                                                writable ? PROT_READ | PROT_WRITE : PROT_READ,
                                                MAP_SHARED,
-                                               vdex_file->Fd(),
+                                               file_fd,
                                                0 /* start offset */,
                                                low_4gb,
                                                vdex_filename.c_str(),
@@ -90,4 +100,16 @@
   return new VdexFile(mmap.release());
 }
 
+const uint8_t* VdexFile::GetNextDexFileData(const uint8_t* cursor) const {
+  DCHECK(cursor == nullptr || (cursor > Begin() && cursor <= End()));
+  if (cursor == nullptr) {
+    // Beginning of the iteration, return the first dex file if there is one.
+    return HasDexSection() ? DexBegin() : nullptr;
+  } else {
+    // Fetch the next dex file. Return null if there is none.
+    const uint8_t* data = cursor + reinterpret_cast<const DexFile::Header*>(cursor)->file_size_;
+    return (data == DexEnd()) ? nullptr : data;
+  }
+}
+
 }  // namespace art
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index edd6ffe..75a0d5e 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -71,6 +71,13 @@
                         bool low_4gb,
                         std::string* error_msg);
 
+  static VdexFile* Open(int file_fd,
+                        size_t vdex_length,
+                        const std::string& vdex_filename,
+                        bool writable,
+                        bool low_4gb,
+                        std::string* error_msg);
+
   const uint8_t* Begin() const { return mmap_->Begin(); }
   const uint8_t* End() const { return mmap_->End(); }
   size_t Size() const { return mmap_->Size(); }
@@ -84,9 +91,37 @@
         Begin() + sizeof(Header) + GetHeader().GetDexSize(), GetHeader().GetVerifierDepsSize());
   }
 
+  ArrayRef<const uint8_t> GetQuickeningInfo() const {
+    return ArrayRef<const uint8_t>(
+        GetVerifierDepsData().data() + GetHeader().GetVerifierDepsSize(),
+        GetHeader().GetQuickeningInfoSize());
+  }
+
+  bool IsValid() const {
+    return mmap_->Size() >= sizeof(Header) && GetHeader().IsValid();
+  }
+
+  // This method is for iterating over the dex files in the vdex. If `cursor` is null,
+  // the first dex file is returned. If `cursor` is not null, it must point to a dex
+  // file and this method returns the next dex file if there is one, or null if there
+  // is none.
+  const uint8_t* GetNextDexFileData(const uint8_t* cursor) const;
+
  private:
   explicit VdexFile(MemMap* mmap) : mmap_(mmap) {}
 
+  bool HasDexSection() const {
+    return GetHeader().GetDexSize() != 0;
+  }
+
+  const uint8_t* DexBegin() const {
+    return Begin() + sizeof(Header);
+  }
+
+  const uint8_t* DexEnd() const {
+    return Begin() + sizeof(Header) + GetHeader().GetDexSize();
+  }
+
   std::unique_ptr<MemMap> mmap_;
 
   DISALLOW_COPY_AND_ASSIGN(VdexFile);
diff --git a/test/538-checker-embed-constants/src/Main.java b/test/538-checker-embed-constants/src/Main.java
index 02c609e..0329e63 100644
--- a/test/538-checker-embed-constants/src/Main.java
+++ b/test/538-checker-embed-constants/src/Main.java
@@ -30,7 +30,7 @@
 
   /// CHECK-START-ARM: int Main.and255(int) disassembly (after)
   /// CHECK-NOT:            movs {{r\d+}}, #255
-  /// CHECK:                and {{r\d+}}, {{r\d+}}, #255
+  /// CHECK:                and {{r\d+}}, {{r\d+}}, #0xff
 
   public static int and255(int arg) {
     return arg & 255;
@@ -46,7 +46,7 @@
 
   /// CHECK-START-ARM: int Main.andNot15(int) disassembly (after)
   /// CHECK-NOT:            mvn {{r\d+}}, #15
-  /// CHECK:                bic {{r\d+}}, {{r\d+}}, #15
+  /// CHECK:                bic {{r\d+}}, {{r\d+}}, #0xf
 
   public static int andNot15(int arg) {
     return arg & ~15;
@@ -54,7 +54,7 @@
 
   /// CHECK-START-ARM: int Main.or255(int) disassembly (after)
   /// CHECK-NOT:            movs {{r\d+}}, #255
-  /// CHECK:                orr {{r\d+}}, {{r\d+}}, #255
+  /// CHECK:                orr {{r\d+}}, {{r\d+}}, #0xff
 
   public static int or255(int arg) {
     return arg | 255;
@@ -70,7 +70,7 @@
 
   /// CHECK-START-ARM: int Main.orNot15(int) disassembly (after)
   /// CHECK-NOT:            mvn {{r\d+}}, #15
-  /// CHECK:                orn {{r\d+}}, {{r\d+}}, #15
+  /// CHECK:                orn {{r\d+}}, {{r\d+}}, #0xf
 
   public static int orNot15(int arg) {
     return arg | ~15;
@@ -78,7 +78,7 @@
 
   /// CHECK-START-ARM: int Main.xor255(int) disassembly (after)
   /// CHECK-NOT:            movs {{r\d+}}, #255
-  /// CHECK:                eor {{r\d+}}, {{r\d+}}, #255
+  /// CHECK:                eor {{r\d+}}, {{r\d+}}, #0xff
 
   public static int xor255(int arg) {
     return arg ^ 255;
@@ -104,8 +104,8 @@
   /// CHECK-NOT:            movs {{r\d+}}, #255
   /// CHECK-NOT:            and{{(\.w)?}}
   /// CHECK-NOT:            bic{{(\.w)?}}
-  /// CHECK-DAG:            and {{r\d+}}, {{r\d+}}, #255
-  /// CHECK-DAG:            movs {{r\d+}}, #0
+  /// CHECK-DAG:            and {{r\d+}}, {{r\d+}}, #0xff
+  /// CHECK-DAG:            mov{{s?}} {{r\d+}}, #0
   /// CHECK-NOT:            and{{(\.w)?}}
   /// CHECK-NOT:            bic{{(\.w)?}}
 
@@ -115,7 +115,7 @@
 
   /// CHECK-START-ARM: long Main.and511(long) disassembly (after)
   /// CHECK:                mov {{r\d+}}, #511
-  /// CHECK-NEXT:           movs {{r\d+}}, #0
+  /// CHECK-NEXT:           mov{{s?}} {{r\d+}}, #0
   /// CHECK-NOT:            and{{(\.w)?}}
   /// CHECK-NOT:            bic{{(\.w)?}}
   /// CHECK:                and{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
@@ -131,7 +131,7 @@
   /// CHECK-NOT:            mvn {{r\d+}}, #15
   /// CHECK-NOT:            and{{(\.w)?}}
   /// CHECK-NOT:            bic{{(\.w)?}}
-  /// CHECK:                bic {{r\d+}}, {{r\d+}}, #15
+  /// CHECK:                bic {{r\d+}}, {{r\d+}}, #0xf
   /// CHECK-NOT:            and{{(\.w)?}}
   /// CHECK-NOT:            bic{{(\.w)?}}
 
@@ -144,8 +144,8 @@
   /// CHECK-NOT:            mvn {{r\d+}}, #15
   /// CHECK-NOT:            and{{(\.w)?}}
   /// CHECK-NOT:            bic{{(\.w)?}}
-  /// CHECK-DAG:            and {{r\d+}}, {{r\d+}}, #15
-  /// CHECK-DAG:            bic {{r\d+}}, {{r\d+}}, #15
+  /// CHECK-DAG:            and {{r\d+}}, {{r\d+}}, #0xf
+  /// CHECK-DAG:            bic {{r\d+}}, {{r\d+}}, #0xf
   /// CHECK-NOT:            and{{(\.w)?}}
   /// CHECK-NOT:            bic{{(\.w)?}}
 
@@ -157,7 +157,7 @@
   /// CHECK-NOT:            movs {{r\d+}}, #255
   /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
-  /// CHECK:                orr {{r\d+}}, {{r\d+}}, #255
+  /// CHECK:                orr {{r\d+}}, {{r\d+}}, #0xff
   /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
 
@@ -167,7 +167,7 @@
 
   /// CHECK-START-ARM: long Main.or511(long) disassembly (after)
   /// CHECK:                mov {{r\d+}}, #511
-  /// CHECK-NEXT:           movs {{r\d+}}, #0
+  /// CHECK-NEXT:           mov{{s?}} {{r\d+}}, #0
   /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
   /// CHECK:                orr{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
@@ -183,7 +183,7 @@
   /// CHECK-NOT:            mvn {{r\d+}}, #15
   /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
-  /// CHECK-DAG:            orn {{r\d+}}, {{r\d+}}, #15
+  /// CHECK-DAG:            orn {{r\d+}}, {{r\d+}}, #0xf
   /// CHECK-DAG:            mvn {{r\d+}}, #0
   /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
@@ -197,8 +197,8 @@
   /// CHECK-NOT:            mvn {{r\d+}}, #15
   /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
-  /// CHECK-DAG:            orr {{r\d+}}, {{r\d+}}, #15
-  /// CHECK-DAG:            orn {{r\d+}}, {{r\d+}}, #15
+  /// CHECK-DAG:            orr {{r\d+}}, {{r\d+}}, #0xf
+  /// CHECK-DAG:            orn {{r\d+}}, {{r\d+}}, #0xf
   /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
 
@@ -209,7 +209,7 @@
   /// CHECK-START-ARM: long Main.xor255(long) disassembly (after)
   /// CHECK-NOT:            movs {{r\d+}}, #255
   /// CHECK-NOT:            eor{{(\.w)?}}
-  /// CHECK:                eor {{r\d+}}, {{r\d+}}, #255
+  /// CHECK:                eor {{r\d+}}, {{r\d+}}, #0xff
   /// CHECK-NOT:            eor{{(\.w)?}}
 
   public static long xor255(long arg) {
@@ -218,7 +218,7 @@
 
   /// CHECK-START-ARM: long Main.xor511(long) disassembly (after)
   /// CHECK:                mov {{r\d+}}, #511
-  /// CHECK-NEXT:           movs {{r\d+}}, #0
+  /// CHECK-NEXT:           mov{{s?}} {{r\d+}}, #0
   /// CHECK-NOT:            eor{{(\.w)?}}
   /// CHECK:                eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
   /// CHECK-NEXT:           eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
@@ -242,7 +242,7 @@
 
   // Note: No support for partial long constant embedding.
   /// CHECK-START-ARM: long Main.xor0xfffffff00000000f(long) disassembly (after)
-  /// CHECK-DAG:            movs {{r\d+}}, #15
+  /// CHECK-DAG:            mov{{s?}} {{r\d+}}, #15
   /// CHECK-DAG:            mvn {{r\d+}}, #15
   /// CHECK-NOT:            eor{{(\.w)?}}
   /// CHECK-DAG:            eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
@@ -257,8 +257,8 @@
   /// CHECK-NOT:            movs {{r\d+}}, #15
   /// CHECK-NOT:            mov.w {{r\d+}}, #-268435456
   /// CHECK-NOT:            eor{{(\.w)?}}
-  /// CHECK-DAG:            eor {{r\d+}}, {{r\d+}}, #15
-  /// CHECK-DAG:            eor {{r\d+}}, {{r\d+}}, #4026531840
+  /// CHECK-DAG:            eor {{r\d+}}, {{r\d+}}, #0xf
+  /// CHECK-DAG:            eor {{r\d+}}, {{r\d+}}, #0xf0000000
   /// CHECK-NOT:            eor{{(\.w)?}}
 
   public static long xor0xf00000000000000f(long arg) {
@@ -507,7 +507,7 @@
   /// CHECK:     <<Arg:j\d+>>       ParameterValue
   /// CHECK:     <<ConstM1:j\d+>>   LongConstant -1
   /// CHECK:                        Add [<<Arg>>,<<ConstM1>>]
-  /// CHECK-NEXT:                   subs r{{\d+}}, #1
+  /// CHECK-NEXT:                   {{adds|subs}} r{{\d+}}, #{{4294967295|1}}
   /// CHECK-NEXT:                   adc r{{\d+}}, r{{\d+}}, #4294967295
   /// CHECK:                        Sub [<<Arg>>,<<ConstM1>>]
   /// CHECK-NEXT:                   adds r{{\d+}}, #1
diff --git a/test/626-const-class-linking/clear_dex_cache_types.cc b/test/626-const-class-linking/clear_dex_cache_types.cc
deleted file mode 100644
index ff33709..0000000
--- a/test/626-const-class-linking/clear_dex_cache_types.cc
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.
- */
-
-#include "jni.h"
-#include "scoped_thread_state_change-inl.h"
-
-namespace art {
-
-namespace {
-
-extern "C" JNIEXPORT void JNICALL Java_Main_clearResolvedTypes(JNIEnv*, jclass, jclass cls) {
-  ScopedObjectAccess soa(Thread::Current());
-  mirror::DexCache* dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache();
-  for (size_t i = 0, num_types = dex_cache->NumResolvedTypes(); i != num_types; ++i) {
-    dex_cache->SetResolvedType(dex::TypeIndex(i), ObjPtr<mirror::Class>(nullptr));
-  }
-}
-
-}  // namespace
-
-}  // namespace art
diff --git a/test/626-const-class-linking/expected.txt b/test/626-const-class-linking/expected.txt
deleted file mode 100644
index 1243226..0000000
--- a/test/626-const-class-linking/expected.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-JNI_OnLoad called
-first: Helper1 class loader: DelegatingLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: DelegatingLoader
-second: Test class loader: DefiningLoader
-testClearDexCache done
-first: Helper1 class loader: DelegatingLoader
-second: Test class loader: DefiningLoader
-first: Helper2 class loader: DelegatingLoader
-second: Test class loader: DefiningLoader
-testMultiDex done
-first: Helper1 class loader: RacyLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyLoader
-second: Test class loader: DefiningLoader
-total: 4
-  throwables: 0
-  class_weaks: 4 (1 unique)
-testRacyLoader done
-first: Helper1 class loader: RacyLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyLoader
-second: Test class loader: DefiningLoader
-first: Helper3 class loader: RacyLoader
-second: Test3 class loader: DefiningLoader
-first: Helper3 class loader: RacyLoader
-second: Test3 class loader: DefiningLoader
-total: 4
-  throwables: 0
-  class_weaks: 4 (2 unique)
-testRacyLoader2 done
-java.lang.NoClassDefFoundError: Initiating class loader of type MisbehavingLoader returned class Helper2 instead of Test.
-testMisbehavingLoader done
-first: Helper1 class loader: RacyMisbehavingLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyMisbehavingLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyMisbehavingLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyMisbehavingLoader
-second: Test class loader: DefiningLoader
-total: 4
-  throwables: 0
-  class_weaks: 4 (1 unique)
-testRacyMisbehavingLoader done
-first: Helper1 class loader: RacyMisbehavingLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyMisbehavingLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyMisbehavingLoader
-second: Test class loader: DefiningLoader
-first: Helper1 class loader: RacyMisbehavingLoader
-second: Test class loader: DefiningLoader
-total: 4
-  throwables: 0
-  class_weaks: 4 (1 unique)
-testRacyMisbehavingLoader2 done
diff --git a/test/626-const-class-linking/info.txt b/test/626-const-class-linking/info.txt
deleted file mode 100644
index 9c19a46..0000000
--- a/test/626-const-class-linking/info.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Test that once a const-class instruction is linked, it will keep referring
-to the same class even in the presence of custom class loaders even after
-clearing the dex cache type array.
diff --git a/test/626-const-class-linking/multidex.jpp b/test/626-const-class-linking/multidex.jpp
deleted file mode 100644
index c7a6648..0000000
--- a/test/626-const-class-linking/multidex.jpp
+++ /dev/null
@@ -1,27 +0,0 @@
-ClassPair:
-  @@com.android.jack.annotations.ForceInMainDex
-  class ClassPair
-DefiningLoader:
-  @@com.android.jack.annotations.ForceInMainDex
-  class DefiningLoader
-DelegatingLoader:
-  @@com.android.jack.annotations.ForceInMainDex
-  class DelegatingLoader
-Helper1:
-  @@com.android.jack.annotations.ForceInMainDex
-  class Helper1
-Main:
-  @@com.android.jack.annotations.ForceInMainDex
-  class Main
-MisbehavingLoader:
-  @@com.android.jack.annotations.ForceInMainDex
-  class MisbehavingLoader
-RacyLoader:
-  @@com.android.jack.annotations.ForceInMainDex
-  class RacyLoader
-RacyMisbehavingHelper:
-  @@com.android.jack.annotations.ForceInMainDex
-  class RacyMisbehavingHelper
-RacyMisbehavingLoader:
-  @@com.android.jack.annotations.ForceInMainDex
-  class RacyMisbehavingLoader
diff --git a/test/626-const-class-linking/src-multidex/Helper2.java b/test/626-const-class-linking/src-multidex/Helper2.java
deleted file mode 100644
index 5bb31ee..0000000
--- a/test/626-const-class-linking/src-multidex/Helper2.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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 Helper2 {
-    public static ClassPair get() {
-        Class<?> helper2_class = Helper2.class;
-        Class<?> test_class = Test.class;
-        return new ClassPair(helper2_class, test_class);
-    }
-}
diff --git a/test/626-const-class-linking/src-multidex/Helper3.java b/test/626-const-class-linking/src-multidex/Helper3.java
deleted file mode 100644
index af996de..0000000
--- a/test/626-const-class-linking/src-multidex/Helper3.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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 Helper3 {
-    public static ClassPair get() {
-        Class<?> helper3_class = Helper3.class;
-        Class<?> test3_class = Test3.class;
-        return new ClassPair(helper3_class, test3_class);
-    }
-}
diff --git a/test/626-const-class-linking/src-multidex/Test3.java b/test/626-const-class-linking/src-multidex/Test3.java
deleted file mode 100644
index c4b134d..0000000
--- a/test/626-const-class-linking/src-multidex/Test3.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * 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 Test3 {
-}
diff --git a/test/626-const-class-linking/src/ClassPair.java b/test/626-const-class-linking/src/ClassPair.java
deleted file mode 100644
index b07036c..0000000
--- a/test/626-const-class-linking/src/ClassPair.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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 ClassPair {
-    public Class<?> first;
-    public Class<?> second;
-
-    public ClassPair(Class<?> first, Class<?> second) {
-        this.first = first;
-        this.second = second;
-    }
-
-    public void print() {
-        String first_loader_name = first.getClassLoader().getClass().getName();
-        System.out.println("first: " + first.getName() + " class loader: " + first_loader_name);
-        String second_loader_name = second.getClassLoader().getClass().getName();
-        System.out.println("second: " + second.getName() + " class loader: " + second_loader_name);
-    }
-}
diff --git a/test/626-const-class-linking/src/DefiningLoader.java b/test/626-const-class-linking/src/DefiningLoader.java
deleted file mode 100644
index b17ab77..0000000
--- a/test/626-const-class-linking/src/DefiningLoader.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * 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.
- */
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.lang.reflect.InvocationTargetException;
-
-/**
- * A class loader with atypical behavior: we try to load a private
- * class implementation before asking the system or boot loader.  This
- * is used to create multiple classes with identical names in a single VM.
- *
- * If DexFile is available, we use that; if not, we assume we're not in
- * Dalvik and instantiate the class with defineClass().
- *
- * The location of the DEX files and class data is dependent upon the
- * test framework.
- */
-public class DefiningLoader extends ClassLoader {
-    static {
-        // For JVM, register as parallel capable.
-        // Android treats all class loaders as parallel capable and makes this a no-op.
-        registerAsParallelCapable();
-    }
-
-    /* this is where the .class files live */
-    static final String CLASS_PATH1 = "classes/";
-    static final String CLASS_PATH2 = "classes2/";
-
-    /* this is the DEX/Jar file */
-    static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/626-const-class-linking.jar";
-
-    /* on Dalvik, this is a DexFile; otherwise, it's null */
-    private Class<?> mDexClass;
-
-    private Object mDexFile;
-
-    /**
-     * Construct DefiningLoader, grabbing a reference to the DexFile class
-     * if we're running under Dalvik.
-     */
-    public DefiningLoader(ClassLoader parent) {
-        super(parent);
-
-        try {
-            mDexClass = parent.loadClass("dalvik.system.DexFile");
-        } catch (ClassNotFoundException cnfe) {
-            // ignore -- not running Dalvik
-        }
-    }
-
-    /**
-     * Finds the class with the specified binary name.
-     *
-     * We search for a file in CLASS_PATH or pull an entry from DEX_FILE.
-     * If we don't find a match, we throw an exception.
-     */
-    protected Class<?> findClass(String name) throws ClassNotFoundException
-    {
-        if (mDexClass != null) {
-            return findClassDalvik(name);
-        } else {
-            return findClassNonDalvik(name);
-        }
-    }
-
-    /**
-     * Finds the class with the specified binary name, from a DEX file.
-     */
-    private Class<?> findClassDalvik(String name)
-        throws ClassNotFoundException {
-
-        if (mDexFile == null) {
-            synchronized (DefiningLoader.class) {
-                Constructor<?> ctor;
-                /*
-                 * Construct a DexFile object through reflection.
-                 */
-                try {
-                    ctor = mDexClass.getConstructor(String.class);
-                } catch (NoSuchMethodException nsme) {
-                    throw new ClassNotFoundException("getConstructor failed",
-                        nsme);
-                }
-
-                try {
-                    mDexFile = ctor.newInstance(DEX_FILE);
-                } catch (InstantiationException ie) {
-                    throw new ClassNotFoundException("newInstance failed", ie);
-                } catch (IllegalAccessException iae) {
-                    throw new ClassNotFoundException("newInstance failed", iae);
-                } catch (InvocationTargetException ite) {
-                    throw new ClassNotFoundException("newInstance failed", ite);
-                }
-            }
-        }
-
-        /*
-         * Call DexFile.loadClass(String, ClassLoader).
-         */
-        Method meth;
-
-        try {
-            meth = mDexClass.getMethod("loadClass", String.class, ClassLoader.class);
-        } catch (NoSuchMethodException nsme) {
-            throw new ClassNotFoundException("getMethod failed", nsme);
-        }
-
-        try {
-            meth.invoke(mDexFile, name, this);
-        } catch (IllegalAccessException iae) {
-            throw new ClassNotFoundException("loadClass failed", iae);
-        } catch (InvocationTargetException ite) {
-            throw new ClassNotFoundException("loadClass failed",
-                ite.getCause());
-        }
-
-        return null;
-    }
-
-    /**
-     * Finds the class with the specified binary name, from .class files.
-     */
-    private Class<?> findClassNonDalvik(String name)
-        throws ClassNotFoundException {
-
-        String[] pathNames = { CLASS_PATH1 + name + ".class", CLASS_PATH2 + name + ".class" };
-
-        String pathName = null;
-        RandomAccessFile raf = null;
-
-        for (String pn : pathNames) {
-            pathName = pn;
-            try {
-                //System.out.println("--- Defining: looking for " + pathName);
-                raf = new RandomAccessFile(new File(pathName), "r");
-                break;
-            } catch (FileNotFoundException fnfe) {
-            }
-        }
-        if (raf == null) {
-            throw new ClassNotFoundException("Not found: " + pathNames[0] + ":" + pathNames[1]);
-        }
-
-        /* read the entire file in */
-        byte[] fileData;
-        try {
-            fileData = new byte[(int) raf.length()];
-            raf.readFully(fileData);
-        } catch (IOException ioe) {
-            throw new ClassNotFoundException("Read error: " + pathName);
-        } finally {
-            try {
-                raf.close();
-            } catch (IOException ioe) {
-                // drop
-            }
-        }
-
-        /* create the class */
-        //System.out.println("--- Defining: defining " + name);
-        try {
-            return defineClass(name, fileData, 0, fileData.length);
-        } catch (Throwable th) {
-            throw new ClassNotFoundException("defineClass failed", th);
-        }
-    }
-
-    /**
-     * Load a class.
-     *
-     * Normally a class loader wouldn't override this, but we want our
-     * version of the class to take precedence over an already-loaded
-     * version.
-     *
-     * We still want the system classes (e.g. java.lang.Object) from the
-     * bootstrap class loader.
-     */
-    synchronized protected Class<?> loadClass(String name, boolean resolve)
-        throws ClassNotFoundException
-    {
-        Class<?> res;
-
-        /*
-         * 1. Invoke findLoadedClass(String) to check if the class has
-         * already been loaded.
-         *
-         * This doesn't change.
-         */
-        res = findLoadedClass(name);
-        if (res != null) {
-            // System.out.println("FancyLoader.loadClass: " + name + " already loaded");
-            if (resolve)
-                resolveClass(res);
-            return res;
-        }
-
-        /*
-         * 3. Invoke the findClass(String) method to find the class.
-         */
-        try {
-            res = findClass(name);
-            if (resolve)
-                resolveClass(res);
-        }
-        catch (ClassNotFoundException e) {
-            // we couldn't find it, so eat the exception and keep going
-        }
-
-        /*
-         * 2. Invoke the loadClass method on the parent class loader.  If
-         * the parent loader is null the class loader built-in to the
-         * virtual machine is used, instead.
-         *
-         * (Since we're not in java.lang, we can't actually invoke the
-         * parent's loadClass() method, but we passed our parent to the
-         * super-class which can take care of it for us.)
-         */
-        res = super.loadClass(name, resolve);   // returns class or throws
-        return res;
-    }
-}
diff --git a/test/626-const-class-linking/src/DelegatingLoader.java b/test/626-const-class-linking/src/DelegatingLoader.java
deleted file mode 100644
index 49955d4..0000000
--- a/test/626-const-class-linking/src/DelegatingLoader.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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 DelegatingLoader extends DefiningLoader {
-    private DefiningLoader defining_loader;
-
-    public DelegatingLoader(ClassLoader parent, DefiningLoader defining_loader) {
-        super(parent);
-        this.defining_loader = defining_loader;
-    }
-
-    public void resetDefiningLoader(DefiningLoader defining_loader) {
-        this.defining_loader = defining_loader;
-    }
-
-    protected Class<?> findClass(String name) throws ClassNotFoundException
-    {
-        if (name.equals("Test")) {
-            throw new Error("Unexpected DelegatingLoader.findClass(\"Test\")");
-        }
-        return super.findClass(name);
-    }
-
-    protected Class<?> loadClass(String name, boolean resolve)
-        throws ClassNotFoundException
-    {
-        if (name.equals("Test")) {
-            return defining_loader.loadClass(name, resolve);
-        }
-        return super.loadClass(name, resolve);
-    }
-}
diff --git a/test/626-const-class-linking/src/Helper1.java b/test/626-const-class-linking/src/Helper1.java
deleted file mode 100644
index ff9cd1a..0000000
--- a/test/626-const-class-linking/src/Helper1.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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 Helper1 {
-    public static ClassPair get() {
-        Class<?> helper1_class = Helper1.class;
-        Class<?> test_class = Test.class;
-        return new ClassPair(helper1_class, test_class);
-    }
-}
diff --git a/test/626-const-class-linking/src/Main.java b/test/626-const-class-linking/src/Main.java
deleted file mode 100644
index 05a6bac..0000000
--- a/test/626-const-class-linking/src/Main.java
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * 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.
- */
-
-import java.lang.ref.WeakReference;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-public class Main {
-    public static void main(String[] args) throws Exception {
-        try {
-            System.loadLibrary(args[0]);
-        } catch (UnsatisfiedLinkError ule) {
-            usingRI = true;
-            // Add expected JNI_OnLoad log line to match expected.txt.
-            System.out.println("JNI_OnLoad called");
-        }
-
-        testClearDexCache();
-        testMultiDex();
-        testRacyLoader();
-        testRacyLoader2();
-        testMisbehavingLoader();
-        testRacyMisbehavingLoader();
-        testRacyMisbehavingLoader2();
-    }
-
-    private static void testClearDexCache() throws Exception {
-        DelegatingLoader delegating_loader = createDelegatingLoader();
-        Class<?> helper = delegating_loader.loadClass("Helper1");
-
-        WeakReference<Class<?>> weak_test1 = wrapHelperGet(helper);
-        changeInner(delegating_loader);
-        if (!usingRI) {
-            clearResolvedTypes(helper);
-        }
-        Runtime.getRuntime().gc();
-        WeakReference<Class<?>> weak_test2 = wrapHelperGet(helper);
-        Runtime.getRuntime().gc();
-
-        Class<?> test1 = weak_test1.get();
-        if (test1 == null) {
-            System.out.println("test1 disappeared");
-        }
-        Class<?> test2 = weak_test2.get();
-        if (test2 == null) {
-            System.out.println("test2 disappeared");
-        }
-        if (test1 != test2) {
-            System.out.println("test1 != test2");
-        }
-
-        System.out.println("testClearDexCache done");
-    }
-
-    private static void testMultiDex() throws Exception {
-        DelegatingLoader delegating_loader = createDelegatingLoader();
-
-        Class<?> helper1 = delegating_loader.loadClass("Helper1");
-        WeakReference<Class<?>> weak_test1 = wrapHelperGet(helper1);
-
-        changeInner(delegating_loader);
-
-        Class<?> helper2 = delegating_loader.loadClass("Helper2");
-        WeakReference<Class<?>> weak_test2 = wrapHelperGet(helper2);
-
-        Runtime.getRuntime().gc();
-
-        Class<?> test1 = weak_test1.get();
-        if (test1 == null) {
-            System.out.println("test1 disappeared");
-        }
-        Class<?> test2 = weak_test2.get();
-        if (test2 == null) {
-            System.out.println("test2 disappeared");
-        }
-        if (test1 != test2) {
-            System.out.println("test1 != test2");
-        }
-
-        System.out.println("testMultiDex done");
-    }
-
-    private static void testMisbehavingLoader() throws Exception {
-        ClassLoader system_loader = ClassLoader.getSystemClassLoader();
-        DefiningLoader defining_loader = new DefiningLoader(system_loader);
-        MisbehavingLoader misbehaving_loader =
-            new MisbehavingLoader(system_loader, defining_loader);
-        Class<?> helper = misbehaving_loader.loadClass("Helper1");
-
-        try {
-            WeakReference<Class<?>> weak_test = wrapHelperGet(helper);
-        } catch (InvocationTargetException ite) {
-            String message = ite.getCause().getMessage();
-            if (usingRI && "Test".equals(message)) {
-              // Replace RI message with dalvik message to match expected.txt.
-              message = "Initiating class loader of type " +
-                  misbehaving_loader.getClass().getName() +
-                  " returned class Helper2 instead of Test.";
-            }
-            System.out.println(ite.getCause().getClass().getName() + ": " + message);
-        }
-        System.out.println("testMisbehavingLoader done");
-    }
-
-    private static void testRacyLoader() throws Exception {
-        final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
-
-        final Thread[] threads = new Thread[4];
-        final Object[] results = new Object[threads.length];
-
-        final RacyLoader racy_loader = new RacyLoader(system_loader, threads.length);
-        final Class<?> helper1 = racy_loader.loadClass("Helper1");
-
-        for (int i = 0; i != threads.length; ++i) {
-            final int my_index = i;
-            Thread t = new Thread() {
-                public void run() {
-                    try {
-                        Method get = helper1.getDeclaredMethod("get");
-                        results[my_index] = get.invoke(null);
-                    } catch (InvocationTargetException ite) {
-                        results[my_index] = ite.getCause();
-                    } catch (Throwable t) {
-                        results[my_index] = t;
-                    }
-                }
-            };
-            t.start();
-            threads[i] = t;
-        }
-        for (Thread t : threads) {
-            t.join();
-        }
-        dumpResultStats(results);
-        System.out.println("testRacyLoader done");
-    }
-
-    private static void testRacyLoader2() throws Exception {
-        final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
-
-        final Thread[] threads = new Thread[4];
-        final Object[] results = new Object[threads.length];
-
-        final RacyLoader racy_loader = new RacyLoader(system_loader, threads.length);
-        final Class<?> helper1 = racy_loader.loadClass("Helper1");
-        final Class<?> helper3 = racy_loader.loadClass("Helper3");
-
-        for (int i = 0; i != threads.length; ++i) {
-            final int my_index = i;
-            Thread t = new Thread() {
-                public void run() {
-                    try {
-                        Class<?> helper = (my_index < threads.length / 2) ? helper1 : helper3;
-                        Method get = helper.getDeclaredMethod("get");
-                        results[my_index] = get.invoke(null);
-                    } catch (InvocationTargetException ite) {
-                        results[my_index] = ite.getCause();
-                    } catch (Throwable t) {
-                        results[my_index] = t;
-                    }
-                }
-            };
-            t.start();
-            threads[i] = t;
-        }
-        for (Thread t : threads) {
-            t.join();
-        }
-        dumpResultStats(results);
-        System.out.println("testRacyLoader2 done");
-    }
-
-    private static void dumpResultStats(Object[] results) throws Exception {
-        int throwables = 0;
-        int class_weaks = 0;
-        int unique_class_weaks = 0;
-        for (int i = 0; i != results.length; ++i) {
-            Object r = results[i];
-            if (r instanceof Throwable) {
-                ++throwables;
-                System.out.println(((Throwable) r).getMessage());
-            } else if (isClassPair(r)) {
-                printPair(r);
-                Object ref = getSecond(r);
-                ++class_weaks;
-                ++unique_class_weaks;
-                for (int j = 0; j != i; ++j) {
-                    Object rj = results[j];
-                    if (isClassPair(results[j]) && getSecond(results[j]) == ref) {
-                        --unique_class_weaks;
-                        break;
-                    }
-                }
-            }
-        }
-        System.out.println("total: " + results.length);
-        System.out.println("  throwables: " + throwables);
-        System.out.println("  class_weaks: " + class_weaks
-            + " (" + unique_class_weaks + " unique)");
-    }
-
-    private static void testRacyMisbehavingLoader() throws Exception {
-        final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
-
-        final Thread[] threads = new Thread[4];
-        final Object[] results = new Object[threads.length];
-
-        final RacyMisbehavingLoader racy_loader =
-            new RacyMisbehavingLoader(system_loader, threads.length, false);
-        final Class<?> helper1 = racy_loader.loadClass("RacyMisbehavingHelper");
-
-        for (int i = 0; i != threads.length; ++i) {
-            final int my_index = i;
-            Thread t = new Thread() {
-                public void run() {
-                    try {
-                        Method get = helper1.getDeclaredMethod("get");
-                        results[my_index] = get.invoke(null);
-                    } catch (InvocationTargetException ite) {
-                        results[my_index] = ite.getCause();
-                    } catch (Throwable t) {
-                        results[my_index] = t;
-                    }
-                }
-            };
-            t.start();
-            threads[i] = t;
-        }
-        for (Thread t : threads) {
-            t.join();
-        }
-        dumpResultStats(results);
-        System.out.println("testRacyMisbehavingLoader done");
-    }
-
-    private static void testRacyMisbehavingLoader2() throws Exception {
-        final ClassLoader system_loader = ClassLoader.getSystemClassLoader();
-
-        final Thread[] threads = new Thread[4];
-        final Object[] results = new Object[threads.length];
-
-        final RacyMisbehavingLoader racy_loader =
-            new RacyMisbehavingLoader(system_loader, threads.length, true);
-        final Class<?> helper1 = racy_loader.loadClass("RacyMisbehavingHelper");
-
-        for (int i = 0; i != threads.length; ++i) {
-            final int my_index = i;
-            Thread t = new Thread() {
-                public void run() {
-                    try {
-                        Method get = helper1.getDeclaredMethod("get");
-                        results[my_index] = get.invoke(null);
-                    } catch (InvocationTargetException ite) {
-                        results[my_index] = ite.getCause();
-                    } catch (Throwable t) {
-                        results[my_index] = t;
-                    }
-                }
-            };
-            t.start();
-            threads[i] = t;
-        }
-        for (Thread t : threads) {
-            t.join();
-        }
-        dumpResultStats(results);
-        System.out.println("testRacyMisbehavingLoader2 done");
-    }
-
-    private static DelegatingLoader createDelegatingLoader() {
-        ClassLoader system_loader = ClassLoader.getSystemClassLoader();
-        DefiningLoader defining_loader = new DefiningLoader(system_loader);
-        return new DelegatingLoader(system_loader, defining_loader);
-    }
-
-    private static void changeInner(DelegatingLoader delegating_loader) {
-        ClassLoader system_loader = ClassLoader.getSystemClassLoader();
-        DefiningLoader defining_loader = new DefiningLoader(system_loader);
-        delegating_loader.resetDefiningLoader(defining_loader);
-    }
-
-    private static WeakReference<Class<?>> wrapHelperGet(Class<?> helper) throws Exception {
-        Method get = helper.getDeclaredMethod("get");
-        Object pair = get.invoke(null);
-        printPair(pair);
-        return new WeakReference<Class<?>>(getSecond(pair));
-    }
-
-    private static void printPair(Object pair) throws Exception {
-        Method print = pair.getClass().getDeclaredMethod("print");
-        print.invoke(pair);
-    }
-
-    private static Class<?> getSecond(Object pair) throws Exception {
-        Field second = pair.getClass().getDeclaredField("second");
-        return (Class<?>) second.get(pair);
-    }
-
-    private static boolean isClassPair(Object r) {
-        return r != null && r.getClass().getName().equals("ClassPair");
-    }
-
-    public static native void clearResolvedTypes(Class<?> c);
-
-    static boolean usingRI = false;
-}
diff --git a/test/626-const-class-linking/src/MisbehavingLoader.java b/test/626-const-class-linking/src/MisbehavingLoader.java
deleted file mode 100644
index ca9783e..0000000
--- a/test/626-const-class-linking/src/MisbehavingLoader.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.
- */
-
-// Class loader that returns Helper2.class when asked to load "Test".
-public class MisbehavingLoader extends DefiningLoader {
-    private DefiningLoader defining_loader;
-
-    public MisbehavingLoader(ClassLoader parent, DefiningLoader defining_loader) {
-        super(parent);
-        this.defining_loader = defining_loader;
-    }
-
-    protected Class<?> findClass(String name) throws ClassNotFoundException
-    {
-        if (name.equals("Helper1") || name.equals("Helper2")) {
-            return super.findClass(name);
-        } else if (name.equals("Test")) {
-            throw new Error("Unexpected MisbehavingLoader.findClass(\"Test\")");
-        }
-        return super.findClass(name);
-    }
-
-    protected Class<?> loadClass(String name, boolean resolve)
-        throws ClassNotFoundException
-    {
-        if (name.equals("Helper1") || name.equals("Helper2")) {
-            return super.loadClass(name, resolve);
-        } else if (name.equals("Test")) {
-            // Ask for a different class.
-            return defining_loader.loadClass("Helper2", resolve);
-        }
-        return super.loadClass(name, resolve);
-    }
-}
diff --git a/test/626-const-class-linking/src/RacyLoader.java b/test/626-const-class-linking/src/RacyLoader.java
deleted file mode 100644
index abfc8ed..0000000
--- a/test/626-const-class-linking/src/RacyLoader.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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 RacyLoader extends DefiningLoader {
-    static {
-        // For JVM, register as parallel capable.
-        // Android treats all class loaders as parallel capable and makes this a no-op.
-        registerAsParallelCapable();
-    }
-
-    private Object lock = new Object();
-    private int index = 0;
-    private int count;
-
-    private DefiningLoader[] defining_loaders;
-
-    public RacyLoader(ClassLoader parent, int count) {
-        super(parent);
-        this.count = count;
-        defining_loaders = new DefiningLoader[2];
-        for (int i = 0; i != defining_loaders.length; ++i) {
-            defining_loaders[i] = new DefiningLoader(parent);
-        }
-    }
-
-    protected Class<?> findClass(String name) throws ClassNotFoundException
-    {
-        if (name.equals("Test") || name.equals("Test3")) {
-            throw new Error("Unexpected RacyLoader.findClass(\"" + name + "\")");
-        }
-        return super.findClass(name);
-    }
-
-    protected Class<?> loadClass(String name, boolean resolve)
-        throws ClassNotFoundException
-    {
-        if (name.equals("Test") || name.equals("Test3")) {
-            int my_index = syncWithOtherInstances(count);
-            Class<?> result = defining_loaders[my_index & 1].loadClass(name, resolve);
-            syncWithOtherInstances(2 * count);
-            return result;
-        }
-        return super.loadClass(name, resolve);
-    }
-
-    private int syncWithOtherInstances(int limit) {
-        int my_index;
-        synchronized (lock) {
-            my_index = index;
-            ++index;
-            if (index != limit) {
-                try {
-                    lock.wait(2000);
-                    if (index < limit) {
-                        throw new Error("Timed out; my_index=" + my_index + ", index=" + index);
-                    }
-                } catch (InterruptedException ie) {
-                    throw new Error(ie);
-                }
-            } else {
-                lock.notifyAll();
-            }
-        }
-        return my_index;
-    }
-}
diff --git a/test/626-const-class-linking/src/RacyMisbehavingHelper.java b/test/626-const-class-linking/src/RacyMisbehavingHelper.java
deleted file mode 100644
index 4525278..0000000
--- a/test/626-const-class-linking/src/RacyMisbehavingHelper.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-
-import java.lang.reflect.Method;
-
-public class RacyMisbehavingHelper {
-    public static ClassPair get() {
-        Class<?> helper1_class = Helper1.class;
-        Class<?> test_class = Test.class;
-        try {
-            // After loading the correct class, allow loading the incorrect class.
-            ClassLoader loader = helper1_class.getClassLoader();
-            Method reportAfterLoading = loader.getClass().getDeclaredMethod("reportAfterLoading");
-            reportAfterLoading.invoke(loader);
-        } catch (Throwable t) {
-            t.printStackTrace();
-        }
-        return new ClassPair(helper1_class, test_class);
-    }
-}
diff --git a/test/626-const-class-linking/src/RacyMisbehavingLoader.java b/test/626-const-class-linking/src/RacyMisbehavingLoader.java
deleted file mode 100644
index d09888e..0000000
--- a/test/626-const-class-linking/src/RacyMisbehavingLoader.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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 RacyMisbehavingLoader extends DefiningLoader {
-    static {
-        // For JVM, register as parallel capable.
-        // Android treats all class loaders as parallel capable and makes this a no-op.
-        registerAsParallelCapable();
-    }
-
-    private Object lock = new Object();
-    private int index = 0;
-    private int count;
-    private boolean throw_error;
-
-    private DefiningLoader[] defining_loaders;
-
-    public RacyMisbehavingLoader(ClassLoader parent, int count, boolean throw_error) {
-        super(parent);
-        this.count = count;
-        this.throw_error = throw_error;
-        defining_loaders = new DefiningLoader[2];
-        for (int i = 0; i != defining_loaders.length; ++i) {
-            defining_loaders[i] = new DefiningLoader(parent);
-        }
-    }
-
-    public void reportAfterLoading() {
-        synchronized (lock) {
-            ++index;
-            if (index == 2 * count) {
-                lock.notifyAll();
-            }
-        }
-    }
-
-    protected Class<?> findClass(String name) throws ClassNotFoundException
-    {
-        if (name.equals("Test")) {
-            throw new Error("Unexpected RacyLoader.findClass(\"" + name + "\")");
-        }
-        return super.findClass(name);
-    }
-
-    protected Class<?> loadClass(String name, boolean resolve)
-        throws ClassNotFoundException
-    {
-        if (name.equals("Test")) {
-            int my_index = syncWithOtherInstances(count);
-            Class<?> result;
-            if ((my_index & 1) == 0) {
-              // Do not delay loading the correct class.
-              result = defining_loaders[my_index & 1].loadClass(name, resolve);
-            } else {
-              // Delay loading the wrong class.
-              syncWithOtherInstances(2 * count);
-              if (throw_error) {
-                throw new Error("RacyMisbehavingLoader throw_error=true");
-              }
-              result = defining_loaders[my_index & 1].loadClass("Test3", resolve);
-            }
-            return result;
-        }
-        return super.loadClass(name, resolve);
-    }
-
-    private int syncWithOtherInstances(int limit) {
-        int my_index;
-        synchronized (lock) {
-            my_index = index;
-            ++index;
-            if (index != limit) {
-                try {
-                    lock.wait(2000);
-                    if (index < limit) {
-                        throw new Error("Timed out; my_index=" + my_index + ", index=" + index);
-                    }
-                } catch (InterruptedException ie) {
-                    throw new Error(ie);
-                }
-            } else {
-                lock.notifyAll();
-            }
-        }
-        return my_index;
-    }
-}
diff --git a/test/628-vdex/expected.txt b/test/628-vdex/expected.txt
new file mode 100644
index 0000000..d0f61f6
--- /dev/null
+++ b/test/628-vdex/expected.txt
@@ -0,0 +1,2 @@
+In foo
+In foo
diff --git a/test/628-vdex/info.txt b/test/628-vdex/info.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/628-vdex/info.txt
diff --git a/test/628-vdex/run b/test/628-vdex/run
new file mode 100644
index 0000000..f1b0a95
--- /dev/null
+++ b/test/628-vdex/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+exec ${RUN} --vdex "${@}"
diff --git a/test/626-const-class-linking/src-multidex/Test.java b/test/628-vdex/src/Main.java
similarity index 61%
rename from test/626-const-class-linking/src-multidex/Test.java
rename to test/628-vdex/src/Main.java
index 1b0cc2a..7ceab2c 100644
--- a/test/626-const-class-linking/src-multidex/Test.java
+++ b/test/628-vdex/src/Main.java
@@ -14,5 +14,24 @@
  * limitations under the License.
  */
 
-public class Test {
+public class Main {
+  Main() {
+    // Will be quickened with RETURN_VOID_NO_BARRIER.
+  }
+
+  public static void main(String[] args) {
+    Main m = new Main();
+    Object o = m;
+    // The call and field accesses will be quickened.
+    m.foo(m.a);
+
+    // The checkcast will be quickened.
+    m.foo(((Main)o).a);
+  }
+
+  int a;
+  void foo(int a) {
+    System.out.println("In foo");
+  }
 }
+
diff --git a/test/Android.arm_vixl.mk b/test/Android.arm_vixl.mk
index 21b31b4..72616a1 100644
--- a/test/Android.arm_vixl.mk
+++ b/test/Android.arm_vixl.mk
@@ -16,36 +16,9 @@
 
 # Known broken tests for the ARM VIXL backend.
 TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
-  003-omnibus-opcodes \
-  020-string \
-  021-string2 \
-  042-new-instance \
-  044-proxy \
-  080-oom-throw \
-  082-inline-execute \
-  096-array-copy-concurrent-gc \
-  099-vmdebug \
-  100-reflect2 \
   103-string-append \
-  114-ParallelGC \
-  122-npe \
-  129-ThreadGetId \
   137-cfi \
-  144-static-field-sigquit \
-  412-new-array \
-  439-npe \
-  450-checker-types \
   488-checker-inline-recursive-calls \
-  515-dce-dominator \
-  520-equivalent-phi \
-  525-checker-arrays-fields1 \
-  525-checker-arrays-fields2 \
-  527-checker-array-access-split \
-  538-checker-embed-constants \
   552-checker-sharpening \
   562-checker-no-intermediate \
-  570-checker-osr \
   602-deoptimizeable \
-  700-LoadArgRegs \
-  800-smali \
-
diff --git a/test/Android.bp b/test/Android.bp
index 39a4059..fe20f29 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -314,7 +314,6 @@
         "595-profile-saving/profile-saving.cc",
         "596-app-images/app_images.cc",
         "597-deopt-new-string/deopt.cc",
-        "626-const-class-linking/clear_dex_cache_types.cc",
     ],
     shared_libs: [
         "libbacktrace",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 554f66d..96b984d 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -608,10 +608,7 @@
 TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS :=
 
 # Tests that should fail in the read barrier configuration with the Optimizing compiler (AOT).
-# 484: Baker's fast path based read barrier compiler instrumentation generates code containing
-#      more parallel moves on x86, thus some Checker assertions may fail.
-TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := \
-  484-checker-register-hints
+TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS :=
 
 # Tests that should fail in the read barrier configuration with JIT (Optimizing compiler).
 TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS :=
diff --git a/test/etc/default-build b/test/etc/default-build
index 408dcfd..e663496 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -273,10 +273,8 @@
 fi
 
 # Create a single jar with two dex files for multidex.
-if [ ${NEED_DEX} = "true" ]; then
-  if [ ${HAS_SRC_MULTIDEX} = "true" ] || [ ${HAS_SMALI_MULTIDEX} = "true" ]; then
-    zip $TEST_NAME.jar classes.dex classes2.dex
-  else
-    zip $TEST_NAME.jar classes.dex
-  fi
+if [ ${HAS_SRC_MULTIDEX} = "true" ] || [ ${HAS_SMALI_MULTIDEX} = "true" ]; then
+  zip $TEST_NAME.jar classes.dex classes2.dex
+elif [ ${NEED_DEX} = "true" ]; then
+  zip $TEST_NAME.jar classes.dex
 fi
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index a71593c..bb3a3ad 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -58,6 +58,7 @@
 ARGS=""
 EXTERNAL_LOG_TAGS="n" # if y respect externally set ANDROID_LOG_TAGS.
 DRY_RUN="n" # if y prepare to run the test but don't run it.
+TEST_VDEX="n"
 
 while true; do
     if [ "x$1" = "x--quiet" ]; then
@@ -243,6 +244,9 @@
     elif [ "x$1" = "x--dry-run" ]; then
         DRY_RUN="y"
         shift
+    elif [ "x$1" = "x--vdex" ]; then
+        TEST_VDEX="y"
+        shift
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
         echo "unknown $0 option: $1" 1>&2
         exit 1
@@ -312,8 +316,7 @@
 if [ "$USE_JVM" = "y" ]; then
   export LD_LIBRARY_PATH=${ANDROID_HOST_OUT}/lib64
   # Xmx is necessary since we don't pass down the ART flags to JVM.
-  # We pass the classes2 path whether it's used (src-multidex) or not.
-  cmdline="${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes:classes2 ${FLAGS} $MAIN $@ ${ARGS}"
+  cmdline="${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes ${FLAGS} $MAIN $@ ${ARGS}"
   if [ "$DEV_MODE" = "y" ]; then
     echo $cmdline
   fi
@@ -445,6 +448,7 @@
 fi
 
 dex2oat_cmdline="true"
+vdex_cmdline="true"
 mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA"
 strip_cmdline="true"
 
@@ -474,6 +478,9 @@
     # Use -k 1m to SIGKILL it a minute later if it hasn't ended.
     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"
+  fi
 fi
 
 if [ "$STRIP_DEX" = "y" ]; then
@@ -514,6 +521,7 @@
 # Remove whitespace.
 dex2oat_cmdline=$(echo $dex2oat_cmdline)
 dalvikvm_cmdline=$(echo $dalvikvm_cmdline)
+vdex_cmdline=$(echo $vdex_cmdline)
 
 if [ "$HOST" = "n" ]; then
     adb root > /dev/null
@@ -554,6 +562,7 @@
              export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \
              export PATH=$ANDROID_ROOT/bin:$PATH && \
              $dex2oat_cmdline && \
+             $vdex_cmdline && \
              $strip_cmdline && \
              $dalvikvm_cmdline"
 
@@ -627,7 +636,7 @@
     fi
 
     if [ "$DEV_MODE" = "y" ]; then
-      echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $strip_cmdline && $cmdline"
+      echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $vdex_cmdline && $strip_cmdline && $cmdline"
     fi
 
     cd $ANDROID_BUILD_TOP
@@ -635,6 +644,7 @@
     rm -rf ${DEX_LOCATION}/dalvik-cache/
     mkdir -p ${mkdir_locations} || exit 1
     $dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
+    $vdex_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
     $strip_cmdline || { echo "Strip failed." >&2 ; exit 3; }
 
     # For running, we must turn off logging when dex2oat or patchoat are missing. Otherwise we use
diff --git a/test/run-test b/test/run-test
index 37eefb3..ea9622a 100755
--- a/test/run-test
+++ b/test/run-test
@@ -351,6 +351,9 @@
     elif [ "x$1" = "x--bisection-search" ]; then
         bisection_search="yes"
         shift
+    elif [ "x$1" = "x--vdex" ]; then
+        run_args="${run_args} --vdex"
+        shift
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
         echo "unknown $0 option: $1" 1>&2
         usage="yes"
@@ -640,6 +643,7 @@
         echo "    --pic-test            Compile the test code position independent."
         echo "    --quiet               Don't print anything except failure messages"
         echo "    --bisection-search    Perform bisection bug search."
+        echo "    --vdex                Test using vdex as in input to dex2oat. Only works with --prebuild."
     ) 1>&2  # Direct to stderr so usage is not printed if --quiet is set.
     exit 1
 fi
diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk
index 27c2054..493eafb 100644
--- a/tools/ahat/Android.mk
+++ b/tools/ahat/Android.mk
@@ -48,7 +48,7 @@
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(call all-java-files-under, test)
 LOCAL_JAR_MANIFEST := test/manifest.txt
-LOCAL_STATIC_JAVA_LIBRARIES := ahat junit
+LOCAL_STATIC_JAVA_LIBRARIES := ahat junit-host
 LOCAL_IS_HOST_MODULE := true
 LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE := ahat-tests
diff --git a/tools/art b/tools/art
index 1394a46..91d6e27 100644
--- a/tools/art
+++ b/tools/art
@@ -30,8 +30,9 @@
 }
 
 function find_libdir() {
+  # Get the actual file, $DALVIKVM may be a symbolic link.
   # Use realpath instead of readlink because Android does not have a readlink.
-  if [ "$(realpath "$ANDROID_ROOT/bin/$DALVIKVM")" = "$(realpath "$ANDROID_ROOT/bin/dalvikvm64")" ]; then
+  if [[ "$(realpath "$ANDROID_ROOT/bin/$DALVIKVM")" == *dalvikvm64 ]]; then
     echo "lib64"
   else
     echo "lib"
diff --git a/tools/dexfuzz/README b/tools/dexfuzz/README
index a0658ec..c1cdf1e 100644
--- a/tools/dexfuzz/README
+++ b/tools/dexfuzz/README
@@ -4,7 +4,7 @@
 DexFuzz is primarily a tool for fuzzing DEX files. Fuzzing is the introduction of
 subtle changes ("mutations") to a file to produce a new test case. These test cases
 can be used to test the various modes of execution available to ART (Interpreter,
-Quick compiler, Optimizing compiler) to check for bugs in these modes of execution.
+Optimizing compiler) to check for bugs in these modes of execution.
 This is done by differential testing - each test file is executed with each mode of
 execution, and any differences between the resulting outputs may be an indication of
 a bug in one of the modes.
@@ -53,17 +53,16 @@
 
 And also at least two of the following backends:
   --interpreter
-  --quick
   --optimizing
 
 Note that if you wanted to test both ARM and ARM64 on an ARM64 device, you can use
 --allarm. Also in this case only one backend is needed, if i.e., you wanted to test
-ARM Quick Backend vs. ARM64 Quick Backend.
+ARM Optimizing Backend vs. ARM64 Optimizing Backend.
 
 Some legal examples:
-  --arm --quick --optimizing
-  --x86 --quick --optimizing --interpreter
-  --allarm --quick
+  --arm --optimizing --interpreter
+  --x86 --optimizing --interpreter
+  --allarm --optimizing
 
 Add in --device=<device name, e.g. device:generic> if you want to specify a device.
 Add in --execute-dir=<dir on device> if you want to specify an execution directory.
@@ -98,7 +97,6 @@
              those occurrences.
 Timed Out  - mutated files that timed out for one or more backends.
              Current timeouts are:
-               Quick - 5 seconds
                Optimizing - 5 seconds
                Intepreter - 30 seconds
               (use --short-timeouts to set all backends to 2 seconds.)
diff --git a/tools/dexfuzz/src/dexfuzz/Options.java b/tools/dexfuzz/src/dexfuzz/Options.java
index b442b22..99e03e8 100644
--- a/tools/dexfuzz/src/dexfuzz/Options.java
+++ b/tools/dexfuzz/src/dexfuzz/Options.java
@@ -51,6 +51,7 @@
   public static boolean usingSpecificDevice = false;
   public static int repeat = 1;
   public static String executeDirectory = "/data/art-test";
+  public static String androidRoot = "";
   public static String dumpMutationsFile = "mutations.dump";
   public static String loadMutationsFile = "mutations.dump";
   public static String reportLogFile = "report.log";
@@ -61,7 +62,6 @@
   public static boolean executeOnHost;
   public static boolean noBootImage;
   public static boolean useInterpreter;
-  public static boolean useQuick;
   public static boolean useOptimizing;
   public static boolean useArchArm;
   public static boolean useArchArm64;
@@ -96,12 +96,13 @@
     Log.always("                           the argument given to adb -s. Default execution mode.");
     Log.always("    --execute-dir=<dir>  : Push tests to this directory to execute them.");
     Log.always("                           (Default: /data/art-test)");
+    Log.always("    --android-root=<dir> : Set path where dalvikvm should look for binaries.");
+    Log.always("                           Use this when pushing binaries to a custom location.");
     Log.always("    --no-boot-image      : Use this flag when boot.art is not available.");
     Log.always("    --skip-host-verify   : When executing, skip host-verification stage");
     Log.always("    --execute-class=<c>  : When executing, execute this class (default: Main)");
     Log.always("");
     Log.always("    --interpreter        : Include the Interpreter in comparisons");
-    Log.always("    --quick              : Include the Quick Compiler in comparisons");
     Log.always("    --optimizing         : Include the Optimizing Compiler in comparisons");
     Log.always("");
     Log.always("    --arm                : Include ARM backends in comparisons");
@@ -160,8 +161,6 @@
       skipHostVerify = true;
     } else if (flag.equals("interpreter")) {
       useInterpreter = true;
-    } else if (flag.equals("quick")) {
-      useQuick = true;
     } else if (flag.equals("optimizing")) {
       useOptimizing = true;
     } else if (flag.equals("arm")) {
@@ -261,6 +260,8 @@
       usingSpecificDevice = true;
     } else if (key.equals("execute-dir")) {
       executeDirectory = value;
+    } else if (key.equals("android-root")) {
+      androidRoot = value;
     } else {
       Log.error("Unrecognised key: --" + key);
       usage();
@@ -423,18 +424,15 @@
       if (useInterpreter) {
         backends++;
       }
-      if (useQuick) {
-        backends++;
-      }
       if (useOptimizing) {
         backends++;
       }
       if (useArchArm && useArchArm64) {
-        // Could just be comparing quick-ARM versus quick-ARM64?
+        // Could just be comparing optimizing-ARM versus optimizing-ARM64?
         backends++;
       }
       if (backends < 2) {
-        Log.error("Not enough backends specified! Try --quick --interpreter!");
+        Log.error("Not enough backends specified! Try --optimizing --interpreter!");
         return false;
       }
     }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java
index 72e36e8..84ed4c4 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java
@@ -29,6 +29,9 @@
   protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
+    // The -Xno-dex-file-fallback option ensures that the execution does not default to
+    // interpreter if compilations fails.
+    commandBuilder.append("-Xno-dex-file-fallback ");
     if (device.noBootImageAvailable()) {
       commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
     }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java
deleted file mode 100644
index d9228ed..0000000
--- a/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dexfuzz.executors;
-
-import dexfuzz.listeners.BaseListener;
-
-public class Arm64QuickBackendExecutor extends Executor {
-
-  public Arm64QuickBackendExecutor(BaseListener listener, Device device) {
-    super("ARM64 Quick Backend", 5, listener, Architecture.ARM64, device,
-        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
-  }
-
-  @Override
-  protected String constructCommand(String programName) {
-    StringBuilder commandBuilder = new StringBuilder();
-    commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Quick ");
-    if (device.noBootImageAvailable()) {
-      commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
-    }
-    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
-    commandBuilder.append(executeClass);
-    return commandBuilder.toString();
-  }
-}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java
index ded8cf9..26a5eea 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java
@@ -29,6 +29,9 @@
   protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
+    // The -Xno-dex-file-fallback option ensures that the execution does not default to
+    // interpreter if compilations fails.
+    commandBuilder.append("-Xno-dex-file-fallback ");
     if (device.noBootImageAvailable()) {
       commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
     }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java
deleted file mode 100644
index 0eb35f7..0000000
--- a/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dexfuzz.executors;
-
-import dexfuzz.listeners.BaseListener;
-
-public class ArmQuickBackendExecutor extends Executor {
-
-  public ArmQuickBackendExecutor(BaseListener listener, Device device) {
-    super("ARM Quick Backend", 5, listener, Architecture.ARM, device,
-        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
-  }
-
-  @Override
-  protected String constructCommand(String programName) {
-    StringBuilder commandBuilder = new StringBuilder();
-    commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Quick ");
-    if (device.noBootImageAvailable()) {
-      commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
-    }
-    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
-    commandBuilder.append(executeClass);
-    return commandBuilder.toString();
-  }
-}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Executor.java b/tools/dexfuzz/src/dexfuzz/executors/Executor.java
index c62a3ad..2bcf3a1 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Executor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Executor.java
@@ -177,7 +177,15 @@
    * Executes runtime.
    */
   public void execute(String programName) {
-    executionResult = executeCommandWithTimeout(constructCommand(programName), true);
+    String command = "";
+    String androidRoot = Options.androidRoot.trim();
+    if (androidRoot.length() != 0) {
+      command = "PATH=" + androidRoot + "/bin ";
+      command += "ANDROID_ROOT=" + androidRoot + " ";
+      command += "LD_LIBRARY_PATH="+ androidRoot + "/lib:" + androidRoot + "/lib64 ";
+    }
+    command += constructCommand(programName);
+    executionResult = executeCommandWithTimeout(command, true);
   }
 
   /**
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java
index 72d43e7..883ff2a 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java
@@ -29,6 +29,9 @@
   protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
+    // The -Xno-dex-file-fallback option ensures that the execution does not default to
+    // interpreter if compilations fails.
+    commandBuilder.append("-Xno-dex-file-fallback ");
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
     return commandBuilder.toString();
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java
deleted file mode 100644
index e7e5ff6..0000000
--- a/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dexfuzz.executors;
-
-import dexfuzz.listeners.BaseListener;
-
-public class Mips64QuickBackendExecutor extends Executor {
-
-  public Mips64QuickBackendExecutor(BaseListener listener, Device device) {
-    super("MIPS64 Quick Backend", 5, listener, Architecture.MIPS64, device,
-        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
-  }
-
-  @Override
-  protected String constructCommand(String programName) {
-    StringBuilder commandBuilder = new StringBuilder();
-    commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Quick ");
-    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
-    commandBuilder.append(executeClass);
-    return commandBuilder.toString();
-  }
-}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java
index 63f6858..b7babdc 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java
@@ -29,6 +29,9 @@
   protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
+    // The -Xno-dex-file-fallback option ensures that the execution does not default to
+    // interpreter if compilations fails.
+    commandBuilder.append("-Xno-dex-file-fallback ");
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
     return commandBuilder.toString();
diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java
deleted file mode 100644
index b262090..0000000
--- a/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dexfuzz.executors;
-
-import dexfuzz.listeners.BaseListener;
-
-public class MipsQuickBackendExecutor extends Executor {
-
-  public MipsQuickBackendExecutor(BaseListener listener, Device device) {
-    super("MIPS Quick Backend", 5, listener, Architecture.MIPS, device,
-        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
-  }
-
-  @Override
-  protected String constructCommand(String programName) {
-    StringBuilder commandBuilder = new StringBuilder();
-    commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Quick ");
-    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
-    commandBuilder.append(executeClass);
-    return commandBuilder.toString();
-  }
-}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java
index 5908a8b..1d62051 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java
@@ -30,6 +30,9 @@
   protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
+    // The -Xno-dex-file-fallback option ensures that the execution does not default to
+    // interpreter if compilations fails.
+    commandBuilder.append("-Xno-dex-file-fallback ");
     if (Options.executeOnHost) {
       commandBuilder.append(device.getHostExecutionFlags()).append(" ");
     }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java
deleted file mode 100644
index 9e8039d..0000000
--- a/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dexfuzz.executors;
-
-import dexfuzz.Options;
-import dexfuzz.listeners.BaseListener;
-
-public class X86QuickBackendExecutor extends Executor {
-
-  public X86QuickBackendExecutor(BaseListener listener, Device device) {
-    super("x86 Quick Backend", 5, listener, Architecture.X86, device,
-        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
-  }
-
-  @Override
-  protected String constructCommand(String programName) {
-    StringBuilder commandBuilder = new StringBuilder();
-    commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Quick ");
-    if (Options.executeOnHost) {
-      commandBuilder.append(device.getHostExecutionFlags()).append(" ");
-    }
-    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
-    commandBuilder.append(executeClass);
-    return commandBuilder.toString();
-  }
-}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java
index 28ff1a5..ad44259 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java
@@ -29,6 +29,9 @@
   protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
+    // The -Xno-dex-file-fallback option ensures that the execution does not default to
+    // interpreter if compilations fails.
+    commandBuilder.append("-Xno-dex-file-fallback ");
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
     return commandBuilder.toString();
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java
deleted file mode 100644
index 22cafe2..0000000
--- a/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dexfuzz.executors;
-
-import dexfuzz.listeners.BaseListener;
-
-public class X86_64QuickBackendExecutor extends Executor {
-
-  public X86_64QuickBackendExecutor(BaseListener listener, Device device) {
-    super("x86_64 Quick Backend", 5, listener, Architecture.X86_64, device,
-        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
-  }
-
-  @Override
-  protected String constructCommand(String programName) {
-    StringBuilder commandBuilder = new StringBuilder();
-    commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Quick ");
-    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
-    commandBuilder.append(executeClass);
-    return commandBuilder.toString();
-  }
-}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java
index bc39d79..1797d90 100644
--- a/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java
@@ -22,24 +22,18 @@
 import dexfuzz.executors.Architecture;
 import dexfuzz.executors.Arm64InterpreterExecutor;
 import dexfuzz.executors.Arm64OptimizingBackendExecutor;
-import dexfuzz.executors.Arm64QuickBackendExecutor;
 import dexfuzz.executors.ArmInterpreterExecutor;
 import dexfuzz.executors.ArmOptimizingBackendExecutor;
-import dexfuzz.executors.ArmQuickBackendExecutor;
 import dexfuzz.executors.Device;
 import dexfuzz.executors.Executor;
 import dexfuzz.executors.Mips64InterpreterExecutor;
 import dexfuzz.executors.Mips64OptimizingBackendExecutor;
-import dexfuzz.executors.Mips64QuickBackendExecutor;
 import dexfuzz.executors.MipsInterpreterExecutor;
 import dexfuzz.executors.MipsOptimizingBackendExecutor;
-import dexfuzz.executors.MipsQuickBackendExecutor;
 import dexfuzz.executors.X86InterpreterExecutor;
 import dexfuzz.executors.X86OptimizingBackendExecutor;
-import dexfuzz.executors.X86QuickBackendExecutor;
 import dexfuzz.executors.X86_64InterpreterExecutor;
 import dexfuzz.executors.X86_64OptimizingBackendExecutor;
-import dexfuzz.executors.X86_64QuickBackendExecutor;
 import dexfuzz.listeners.BaseListener;
 import dexfuzz.program.Mutation;
 import dexfuzz.program.Program;
@@ -121,18 +115,13 @@
     }
   }
 
-  private void addExecutorsForArchitecture(Device device, Class<? extends Executor> quick,
-      Class<? extends Executor> optimizing, Class<? extends Executor> interpreter) {
-    // NB: Currently QuickBackend MUST come immediately before same arch's Interpreter.
+  private void addExecutorsForArchitecture(Device device, Class<? extends Executor> optimizing,
+      Class<? extends Executor> interpreter) {
+    // NB: Currently OptimizingBackend MUST come immediately before same arch's Interpreter.
     // This is because intepreter execution relies on there being an OAT file already
     // created to produce correct debug information. Otherwise we will see
     // false-positive divergences.
     try {
-      if (Options.useQuick) {
-        Constructor<? extends Executor> constructor =
-            quick.getConstructor(BaseListener.class, Device.class);
-        executors.add(constructor.newInstance(listener, device));
-      }
       if (Options.useOptimizing) {
         Constructor<? extends Executor> constructor =
             optimizing.getConstructor(BaseListener.class, Device.class);
@@ -165,33 +154,33 @@
     }
 
     if (Options.useArchArm64) {
-      addExecutorsForArchitecture(device, Arm64QuickBackendExecutor.class,
-          Arm64OptimizingBackendExecutor.class, Arm64InterpreterExecutor.class);
+      addExecutorsForArchitecture(device, Arm64OptimizingBackendExecutor.class,
+          Arm64InterpreterExecutor.class);
     }
 
     if (Options.useArchArm) {
-      addExecutorsForArchitecture(device, ArmQuickBackendExecutor.class,
-          ArmOptimizingBackendExecutor.class, ArmInterpreterExecutor.class);
+      addExecutorsForArchitecture(device, ArmOptimizingBackendExecutor.class,
+          ArmInterpreterExecutor.class);
     }
 
     if (Options.useArchX86_64) {
-      addExecutorsForArchitecture(device, X86_64QuickBackendExecutor.class,
-          X86_64OptimizingBackendExecutor.class, X86_64InterpreterExecutor.class);
+      addExecutorsForArchitecture(device, X86_64OptimizingBackendExecutor.class,
+          X86_64InterpreterExecutor.class);
     }
 
     if (Options.useArchX86) {
-      addExecutorsForArchitecture(device, X86QuickBackendExecutor.class,
-          X86OptimizingBackendExecutor.class, X86InterpreterExecutor.class);
+      addExecutorsForArchitecture(device, X86OptimizingBackendExecutor.class,
+          X86InterpreterExecutor.class);
     }
 
     if (Options.useArchMips64) {
-      addExecutorsForArchitecture(device, Mips64QuickBackendExecutor.class,
-          Mips64OptimizingBackendExecutor.class, Mips64InterpreterExecutor.class);
+      addExecutorsForArchitecture(device, Mips64OptimizingBackendExecutor.class,
+          Mips64InterpreterExecutor.class);
     }
 
     if (Options.useArchMips) {
-      addExecutorsForArchitecture(device, MipsQuickBackendExecutor.class,
-          MipsOptimizingBackendExecutor.class, MipsInterpreterExecutor.class);
+      addExecutorsForArchitecture(device, MipsOptimizingBackendExecutor.class,
+          MipsInterpreterExecutor.class);
     }
 
     // Add the first backend as the golden executor for self-divergence tests.