Merge "Fix unquitable apps when debug enabled."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index d59d8f6..bd7f900 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -26,6 +26,8 @@
   AbstractMethod \
   AllFields \
   DexToDexDecompiler \
+  ErroneousA \
+  ErroneousB \
   ExceptionHandle \
   GetMethodSignature \
   ImageLayoutA \
@@ -85,7 +87,7 @@
 ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
 
 ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces
-ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
+ART_GTEST_class_linker_test_DEX_DEPS := ErroneousA ErroneousB Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
 ART_GTEST_class_table_test_DEX_DEPS := XandY
 ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
 ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
diff --git a/build/art.go b/build/art.go
index ccaa11d..e6e0544 100644
--- a/build/art.go
+++ b/build/art.go
@@ -58,7 +58,7 @@
 		asflags = append(asflags, "-DART_HEAP_POISONING=1")
 	}
 
-	if envTrue(ctx, "ART_USE_READ_BARRIER") {
+	if envTrue(ctx, "ART_USE_READ_BARRIER") || ctx.AConfig().ArtUseReadBarrier() {
 		// Used to change the read barrier type. Valid values are BAKER, BROOKS, TABLELOOKUP.
 		// The default is BAKER.
 		barrierType := envDefault(ctx, "ART_READ_BARRIER_TYPE", "BAKER")
@@ -259,7 +259,7 @@
 }
 
 func artLibrary() (blueprint.Module, []interface{}) {
-	library, _ := cc.NewLibrary(android.HostAndDeviceSupported, true, true)
+	library, _ := cc.NewLibrary(android.HostAndDeviceSupported)
 	module, props := library.Init()
 
 	props = installCodegenCustomizer(module, props, true)
diff --git a/cmdline/cmdline.h b/cmdline/cmdline.h
index 6e042c3..f4540ff 100644
--- a/cmdline/cmdline.h
+++ b/cmdline/cmdline.h
@@ -24,10 +24,12 @@
 #include <iostream>
 #include <string>
 
-#include "runtime.h"
+#include "android-base/stringprintf.h"
+
+#include "base/logging.h"
 #include "base/stringpiece.h"
 #include "noop_compiler_callbacks.h"
-#include "base/logging.h"
+#include "runtime.h"
 
 #if !defined(NDEBUG)
 #define DBG_LOG LOG(INFO)
@@ -197,7 +199,7 @@
         "      Example: --boot-image=/system/framework/boot.art\n"
         "               (specifies /system/framework/<arch>/boot.art as the image file)\n"
         "\n";
-    usage += StringPrintf(  // Optional.
+    usage += android::base::StringPrintf(  // Optional.
         "  --instruction-set=(arm|arm64|mips|mips64|x86|x86_64): for locating the image\n"
         "      file based on the image location set.\n"
         "      Example: --instruction-set=x86\n"
@@ -264,8 +266,8 @@
       // Check that the boot image location points to a valid file name.
       std::string file_name;
       if (!LocationToFilename(boot_image_location, instruction_set_, &file_name)) {
-        *error_msg = StringPrintf("No corresponding file for location '%s' exists",
-                                  file_name.c_str());
+        *error_msg = android::base::StringPrintf("No corresponding file for location '%s' exists",
+                                                 file_name.c_str());
         return false;
       }
 
diff --git a/compiler/Android.bp b/compiler/Android.bp
index db55ea0..46f3358 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -49,6 +49,7 @@
         "optimizing/block_builder.cc",
         "optimizing/bounds_check_elimination.cc",
         "optimizing/builder.cc",
+        "optimizing/cha_guard_optimization.cc",
         "optimizing/code_generator.cc",
         "optimizing/code_generator_utils.cc",
         "optimizing/constant_folding.cc",
@@ -145,6 +146,7 @@
         mips64: {
             srcs: [
                 "jni/quick/mips64/calling_convention_mips64.cc",
+                "linker/mips64/relative_patcher_mips64.cc",
                 "optimizing/code_generator_mips64.cc",
                 "optimizing/intrinsics_mips64.cc",
                 "utils/mips64/assembler_mips64.cc",
@@ -381,6 +383,11 @@
                 "linker/mips/relative_patcher_mips32r6_test.cc",
             ],
         },
+        mips64: {
+            srcs: [
+                "linker/mips64/relative_patcher_mips64_test.cc",
+            ],
+        },
         x86: {
             srcs: [
                 "linker/x86/relative_patcher_x86_test.cc",
diff --git a/compiler/compiled_class.h b/compiler/compiled_class.h
index b88d613..06ce946 100644
--- a/compiler/compiled_class.h
+++ b/compiler/compiled_class.h
@@ -28,8 +28,11 @@
   mirror::Class::Status GetStatus() const {
     return status_;
   }
+  void SetStatus(mirror::Class::Status status) {
+    status_ = status;
+  }
  private:
-  const mirror::Class::Status status_;
+  mirror::Class::Status status_;
 };
 
 }  // namespace art
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index cf69f46..d4f6545 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -16,6 +16,8 @@
 
 #include "dex_to_dex_compiler.h"
 
+#include "android-base/stringprintf.h"
+
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "base/logging.h"
@@ -32,6 +34,8 @@
 namespace art {
 namespace optimizer {
 
+using android::base::StringPrintf;
+
 // Controls quickening activation.
 const bool kEnableQuickening = true;
 // Control check-cast elision.
diff --git a/compiler/dex/dex_to_dex_decompiler.cc b/compiler/dex/dex_to_dex_decompiler.cc
index 051125e..bfd485d 100644
--- a/compiler/dex/dex_to_dex_decompiler.cc
+++ b/compiler/dex/dex_to_dex_decompiler.cc
@@ -27,10 +27,13 @@
 
 class DexDecompiler {
  public:
-  DexDecompiler(const DexFile::CodeItem& code_item, const ArrayRef<const uint8_t>& quickened_info)
+  DexDecompiler(const DexFile::CodeItem& code_item,
+                const ArrayRef<const uint8_t>& quickened_info,
+                bool decompile_return_instruction)
     : code_item_(code_item),
       quickened_info_ptr_(quickened_info.data()),
-      quickened_info_end_(quickened_info.data() + quickened_info.size()) {}
+      quickened_info_end_(quickened_info.data() + quickened_info.size()),
+      decompile_return_instruction_(decompile_return_instruction) {}
 
   bool Decompile();
 
@@ -87,6 +90,7 @@
   const DexFile::CodeItem& code_item_;
   const uint8_t* quickened_info_ptr_;
   const uint8_t* const quickened_info_end_;
+  const bool decompile_return_instruction_;
 
   DISALLOW_COPY_AND_ASSIGN(DexDecompiler);
 };
@@ -102,7 +106,9 @@
 
     switch (inst->Opcode()) {
       case Instruction::RETURN_VOID_NO_BARRIER:
-        inst->SetOpcode(Instruction::RETURN_VOID);
+        if (decompile_return_instruction_) {
+          inst->SetOpcode(Instruction::RETURN_VOID);
+        }
         break;
 
       case Instruction::NOP:
@@ -189,8 +195,12 @@
 }
 
 bool ArtDecompileDEX(const DexFile::CodeItem& code_item,
-                     const ArrayRef<const uint8_t>& quickened_info) {
-  DexDecompiler decompiler(code_item, quickened_info);
+                     const ArrayRef<const uint8_t>& quickened_info,
+                     bool decompile_return_instruction) {
+  if (quickened_info.size() == 0 && !decompile_return_instruction) {
+    return true;
+  }
+  DexDecompiler decompiler(code_item, quickened_info, decompile_return_instruction);
   return decompiler.Decompile();
 }
 
diff --git a/compiler/dex/dex_to_dex_decompiler.h b/compiler/dex/dex_to_dex_decompiler.h
index 5502ca2..b5d5b91 100644
--- a/compiler/dex/dex_to_dex_decompiler.h
+++ b/compiler/dex/dex_to_dex_decompiler.h
@@ -30,7 +30,8 @@
 // consistent with DexToDexCompiler, but we should really change it to
 // DexFile::CodeItem*.
 bool ArtDecompileDEX(const DexFile::CodeItem& code_item,
-                     const ArrayRef<const uint8_t>& quickened_data);
+                     const ArrayRef<const uint8_t>& quickened_data,
+                     bool decompile_return_instruction);
 
 }  // namespace optimizer
 }  // namespace art
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index ea6c7a2..9a8d27c 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -102,7 +102,8 @@
         if (compiled_method != nullptr) {
           table = compiled_method->GetVmapTable();
         }
-        optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table);
+        optimizer::ArtDecompileDEX(
+            *it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true);
         it.Next();
       }
       while (it.HasNextVirtualMethod()) {
@@ -113,7 +114,8 @@
         if (compiled_method != nullptr) {
           table = compiled_method->GetVmapTable();
         }
-        optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table);
+        optimizer::ArtDecompileDEX(
+            *it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true);
         it.Next();
       }
       DCHECK(!it.HasNext());
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 9d39bf2..00a7d44 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -46,9 +46,7 @@
 void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
   DCHECK(method_verifier != nullptr);
   MethodReference ref = method_verifier->GetMethodReference();
-  bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags());
-  std::unique_ptr<const VerifiedMethod> verified_method(
-      VerifiedMethod::Create(method_verifier, compile));
+  std::unique_ptr<const VerifiedMethod> verified_method(VerifiedMethod::Create(method_verifier));
   if (verified_method == nullptr) {
     // We'll punt this later.
     return;
@@ -84,7 +82,6 @@
     // TODO: Investigate why are we doing the work again for this method and try to avoid it.
     LOG(WARNING) << "Method processed more than once: " << ref.PrettyMethod();
     if (!Runtime::Current()->UseJitCompilation()) {
-      DCHECK_EQ(existing->GetDevirtMap().size(), verified_method->GetDevirtMap().size());
       DCHECK_EQ(existing->GetSafeCastSet().size(), verified_method->GetSafeCastSet().size());
     }
     // Let the unique_ptr delete the new verified method since there was already an existing one
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index 1bdace9..cbca333 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -42,25 +42,12 @@
       has_runtime_throw_(has_runtime_throw) {
 }
 
-const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_verifier,
-                                             bool compile) {
+const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_verifier) {
+  DCHECK(Runtime::Current()->IsAotCompiler());
   std::unique_ptr<VerifiedMethod> verified_method(
       new VerifiedMethod(method_verifier->GetEncounteredFailureTypes(),
                          method_verifier->HasInstructionThatWillThrow()));
 
-  if (compile) {
-    // TODO: move this out when DEX-to-DEX supports devirtualization.
-    if (method_verifier->HasVirtualOrInterfaceInvokes()) {
-      verified_method->GenerateDevirtMap(method_verifier);
-    }
-
-    // Only need dequicken info for JIT so far.
-    if (Runtime::Current()->UseJitCompilation() &&
-        !verified_method->GenerateDequickenMap(method_verifier)) {
-      return nullptr;
-    }
-  }
-
   if (method_verifier->HasCheckCasts()) {
     verified_method->GenerateSafeCastSet(method_verifier);
   }
@@ -68,140 +55,10 @@
   return verified_method.release();
 }
 
-const MethodReference* VerifiedMethod::GetDevirtTarget(uint32_t dex_pc) const {
-  auto it = devirt_map_.find(dex_pc);
-  return (it != devirt_map_.end()) ? &it->second : nullptr;
-}
-
-const DexFileReference* VerifiedMethod::GetDequickenIndex(uint32_t dex_pc) const {
-  DCHECK(Runtime::Current()->UseJitCompilation());
-  auto it = dequicken_map_.find(dex_pc);
-  return (it != dequicken_map_.end()) ? &it->second : nullptr;
-}
-
 bool VerifiedMethod::IsSafeCast(uint32_t pc) const {
   return std::binary_search(safe_cast_set_.begin(), safe_cast_set_.end(), pc);
 }
 
-bool VerifiedMethod::GenerateDequickenMap(verifier::MethodVerifier* method_verifier) {
-  if (method_verifier->HasFailures()) {
-    return false;
-  }
-  const DexFile::CodeItem* code_item = method_verifier->CodeItem();
-  const uint16_t* insns = code_item->insns_;
-  const Instruction* inst = Instruction::At(insns);
-  const Instruction* end = Instruction::At(insns + code_item->insns_size_in_code_units_);
-  for (; inst < end; inst = inst->Next()) {
-    const bool is_virtual_quick = inst->Opcode() == Instruction::INVOKE_VIRTUAL_QUICK;
-    const bool is_range_quick = inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK;
-    if (is_virtual_quick || is_range_quick) {
-      uint32_t dex_pc = inst->GetDexPc(insns);
-      verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
-      ArtMethod* method =
-          method_verifier->GetQuickInvokedMethod(inst, line, is_range_quick, true);
-      if (method == nullptr) {
-        // It can be null if the line wasn't verified since it was unreachable.
-        return false;
-      }
-      // The verifier must know what the type of the object was or else we would have gotten a
-      // failure. Put the dex method index in the dequicken map since we need this to get number of
-      // arguments in the compiler.
-      dequicken_map_.Put(dex_pc, DexFileReference(method->GetDexFile(),
-                                                  method->GetDexMethodIndex()));
-    } else if (IsInstructionIGetQuickOrIPutQuick(inst->Opcode())) {
-      uint32_t dex_pc = inst->GetDexPc(insns);
-      verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
-      ArtField* field = method_verifier->GetQuickFieldAccess(inst, line);
-      if (field == nullptr) {
-        // It can be null if the line wasn't verified since it was unreachable.
-        return false;
-      }
-      // The verifier must know what the type of the field was or else we would have gotten a
-      // failure. Put the dex field index in the dequicken map since we need this for lowering
-      // in the compiler.
-      // TODO: Putting a field index in a method reference is gross.
-      dequicken_map_.Put(dex_pc, DexFileReference(field->GetDexFile(), field->GetDexFieldIndex()));
-    }
-  }
-  return true;
-}
-
-void VerifiedMethod::GenerateDevirtMap(verifier::MethodVerifier* method_verifier) {
-  // It is risky to rely on reg_types for sharpening in cases of soft
-  // verification, we might end up sharpening to a wrong implementation. Just abort.
-  if (method_verifier->HasFailures()) {
-    return;
-  }
-
-  const DexFile::CodeItem* code_item = method_verifier->CodeItem();
-  const uint16_t* insns = code_item->insns_;
-  const Instruction* inst = Instruction::At(insns);
-  const Instruction* end = Instruction::At(insns + code_item->insns_size_in_code_units_);
-
-  for (; inst < end; inst = inst->Next()) {
-    const bool is_virtual = inst->Opcode() == Instruction::INVOKE_VIRTUAL ||
-        inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE;
-    const bool is_interface = inst->Opcode() == Instruction::INVOKE_INTERFACE ||
-        inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE;
-
-    if (!is_interface && !is_virtual) {
-      continue;
-    }
-    // Get reg type for register holding the reference to the object that will be dispatched upon.
-    uint32_t dex_pc = inst->GetDexPc(insns);
-    verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
-    const bool is_range = inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE ||
-        inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE;
-    const verifier::RegType&
-        reg_type(line->GetRegisterType(method_verifier,
-                                       is_range ? inst->VRegC_3rc() : inst->VRegC_35c()));
-
-    if (!reg_type.HasClass()) {
-      // We will compute devirtualization information only when we know the Class of the reg type.
-      continue;
-    }
-    mirror::Class* reg_class = reg_type.GetClass();
-    if (reg_class->IsInterface()) {
-      // We can't devirtualize when the known type of the register is an interface.
-      continue;
-    }
-    if (reg_class->IsAbstract() && !reg_class->IsArrayClass()) {
-      // We can't devirtualize abstract classes except on arrays of abstract classes.
-      continue;
-    }
-    auto* cl = Runtime::Current()->GetClassLinker();
-    PointerSize pointer_size = cl->GetImagePointerSize();
-    ArtMethod* abstract_method = method_verifier->GetDexCache()->GetResolvedMethod(
-        is_range ? inst->VRegB_3rc() : inst->VRegB_35c(), pointer_size);
-    if (abstract_method == nullptr) {
-      // If the method is not found in the cache this means that it was never found
-      // by ResolveMethodAndCheckAccess() called when verifying invoke_*.
-      continue;
-    }
-    // Find the concrete method.
-    ArtMethod* concrete_method = nullptr;
-    if (is_interface) {
-      concrete_method = reg_type.GetClass()->FindVirtualMethodForInterface(
-          abstract_method, pointer_size);
-    }
-    if (is_virtual) {
-      concrete_method = reg_type.GetClass()->FindVirtualMethodForVirtual(
-          abstract_method, pointer_size);
-    }
-    if (concrete_method == nullptr || !concrete_method->IsInvokable()) {
-      // In cases where concrete_method is not found, or is not invokable, continue to the next
-      // invoke.
-      continue;
-    }
-    if (reg_type.IsPreciseReference() || concrete_method->IsFinal() ||
-        concrete_method->GetDeclaringClass()->IsFinal()) {
-      // If we knew exactly the class being dispatched upon, or if the target method cannot be
-      // overridden record the target to be used in the compiler driver.
-      devirt_map_.Put(dex_pc, concrete_method->ToMethodReference());
-    }
-  }
-}
-
 void VerifiedMethod::GenerateSafeCastSet(verifier::MethodVerifier* method_verifier) {
   /*
    * Walks over the method code and adds any cast instructions in which
@@ -218,35 +75,32 @@
 
   for (; inst < end; inst = inst->Next()) {
     Instruction::Code code = inst->Opcode();
-    if ((code == Instruction::CHECK_CAST) || (code == Instruction::APUT_OBJECT)) {
+    if (code == Instruction::CHECK_CAST) {
       uint32_t dex_pc = inst->GetDexPc(code_item->insns_);
       if (!method_verifier->GetInstructionFlags(dex_pc).IsVisited()) {
         // Do not attempt to quicken this instruction, it's unreachable anyway.
         continue;
       }
       const verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
-      bool is_safe_cast = false;
-      if (code == Instruction::CHECK_CAST) {
-        const verifier::RegType& reg_type(line->GetRegisterType(method_verifier,
-                                                                inst->VRegA_21c()));
-        const verifier::RegType& cast_type =
-            method_verifier->ResolveCheckedClass(dex::TypeIndex(inst->VRegB_21c()));
-        is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type, method_verifier);
-      } else {
-        const verifier::RegType& array_type(line->GetRegisterType(method_verifier,
-                                                                  inst->VRegB_23x()));
-        // We only know its safe to assign to an array if the array type is precise. For example,
-        // an Object[] can have any type of object stored in it, but it may also be assigned a
-        // String[] in which case the stores need to be of Strings.
-        if (array_type.IsPreciseReference()) {
-          const verifier::RegType& value_type(line->GetRegisterType(method_verifier,
-                                                                    inst->VRegA_23x()));
-          const verifier::RegType& component_type = method_verifier->GetRegTypeCache()
-              ->GetComponentType(array_type, method_verifier->GetClassLoader());
-          is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type, method_verifier);
+      const verifier::RegType& reg_type(line->GetRegisterType(method_verifier,
+                                                              inst->VRegA_21c()));
+      const verifier::RegType& cast_type =
+          method_verifier->ResolveCheckedClass(dex::TypeIndex(inst->VRegB_21c()));
+      // Pass null for the method verifier to not record the VerifierDeps dependency
+      // if the types are not assignable.
+      if (cast_type.IsStrictlyAssignableFrom(reg_type, /* method_verifier */ nullptr)) {
+        // The types are assignable, we record that dependency in the VerifierDeps so
+        // that if this changes after OTA, we will re-verify again.
+        // We check if reg_type has a class, as the verifier may have inferred it's
+        // 'null'.
+        if (reg_type.HasClass()) {
+          DCHECK(cast_type.HasClass());
+          verifier::VerifierDeps::MaybeRecordAssignability(method_verifier->GetDexFile(),
+                                                           cast_type.GetClass(),
+                                                           reg_type.GetClass(),
+                                                           /* strict */ true,
+                                                           /* assignable */ true);
         }
-      }
-      if (is_safe_cast) {
         // Verify ordering for push_back() to the sorted vector.
         DCHECK(safe_cast_set_.empty() || safe_cast_set_.back() < dex_pc);
         safe_cast_set_.push_back(dex_pc);
diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h
index ce53417..439e69e 100644
--- a/compiler/dex/verified_method.h
+++ b/compiler/dex/verified_method.h
@@ -39,31 +39,14 @@
   // is better for performance (not just memory usage), especially for large sets.
   typedef std::vector<uint32_t> SafeCastSet;
 
-  // Devirtualization map type maps dex offset to concrete method reference.
-  typedef SafeMap<uint32_t, MethodReference> DevirtualizationMap;
-
-  // Devirtualization map type maps dex offset to field / method idx.
-  typedef SafeMap<uint32_t, DexFileReference> DequickenMap;
-
-  static const VerifiedMethod* Create(verifier::MethodVerifier* method_verifier, bool compile)
+  static const VerifiedMethod* Create(verifier::MethodVerifier* method_verifier)
       REQUIRES_SHARED(Locks::mutator_lock_);
   ~VerifiedMethod() = default;
 
-  const DevirtualizationMap& GetDevirtMap() const {
-    return devirt_map_;
-  }
-
   const SafeCastSet& GetSafeCastSet() const {
     return safe_cast_set_;
   }
 
-  // Returns the devirtualization target method, or null if none.
-  const MethodReference* GetDevirtTarget(uint32_t dex_pc) const;
-
-  // Returns the dequicken field / method for a quick invoke / field get. Returns null if there is
-  // no entry for that dex pc.
-  const DexFileReference* GetDequickenIndex(uint32_t dex_pc) const;
-
   // Returns true if the cast can statically be verified to be redundant
   // by using the check-cast elision peephole optimization in the verifier.
   bool IsSafeCast(uint32_t pc) const;
@@ -82,38 +65,10 @@
   }
 
  private:
-  /*
-   * Generate the GC map for a method that has just been verified (i.e. we're doing this as part of
-   * verification). For type-precise determination we have all the data we need, so we just need to
-   * encode it in some clever fashion.
-   * Stores the data in dex_gc_map_, returns true on success and false on failure.
-   */
-  bool GenerateGcMap(verifier::MethodVerifier* method_verifier);
-
-  // Verify that the GC map associated with method_ is well formed.
-  static void VerifyGcMap(verifier::MethodVerifier* method_verifier,
-                          const std::vector<uint8_t>& data);
-
-  // Compute sizes for GC map data.
-  static void ComputeGcMapSizes(verifier::MethodVerifier* method_verifier,
-                                size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc);
-
-  // Generate devirtualizaion map into devirt_map_.
-  void GenerateDevirtMap(verifier::MethodVerifier* method_verifier)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  // Generate dequickening map into dequicken_map_. Returns false if there is an error.
-  bool GenerateDequickenMap(verifier::MethodVerifier* method_verifier)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Generate safe case set into safe_cast_set_.
   void GenerateSafeCastSet(verifier::MethodVerifier* method_verifier)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  DevirtualizationMap devirt_map_;
-  // Dequicken map is required for compiling quickened byte codes. The quicken maps from
-  // dex PC to dex method index or dex field index based on the instruction.
-  DequickenMap dequicken_map_;
   SafeCastSet safe_cast_set_;
 
   const uint32_t encountered_error_types_;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index e62bdb5..2950266 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -299,7 +299,7 @@
       dump_passes_(dump_passes),
       timings_logger_(timer),
       compiler_context_(nullptr),
-      support_boot_image_fixup_(instruction_set != kMips64),
+      support_boot_image_fixup_(true),
       dex_files_for_oat_file_(nullptr),
       compiled_method_storage_(swap_fd),
       profile_compilation_info_(profile_compilation_info),
@@ -423,7 +423,7 @@
   // Compile:
   // 1) Compile all classes and methods enabled for compilation. May fall back to dex-to-dex
   //    compilation.
-  if (!GetCompilerOptions().VerifyAtRuntime()) {
+  if (GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
     Compile(class_loader, dex_files, timings);
   }
   if (dump_stats_) {
@@ -435,7 +435,8 @@
 
 // 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 ArrayRef<const uint8_t>& quickening_info,
+                      bool decompile_return_instruction) {
   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) {
@@ -454,14 +455,14 @@
         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));
+          optimizer::ArtDecompileDEX(*code_item,
+                                     ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
+                                     decompile_return_instruction);
           quickening_info_ptr += quickening_size;
         }
         it.Next();
@@ -472,8 +473,9 @@
         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));
+          optimizer::ArtDecompileDEX(*code_item,
+                                     ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
+                                     decompile_return_instruction);
           quickening_info_ptr += quickening_size;
         }
         it.Next();
@@ -492,7 +494,15 @@
     // 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());
+    if (vdex_file->GetQuickeningInfo().size() != 0) {
+      TimingLogger::ScopedTiming t("Unquicken", timings);
+      // We do not decompile a RETURN_VOID_NO_BARRIER into a RETURN_VOID, as the quickening
+      // optimization does not depend on the boot image (the optimization relies on not
+      // having final fields in a class, which does not change for an app).
+      Unquicken(dex_files,
+                vdex_file->GetQuickeningInfo(),
+                /* decompile_return_instruction */ false);
+    }
     Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
         new verifier::VerifierDeps(dex_files, vdex_file->GetVerifierDepsData()));
   }
@@ -504,10 +514,7 @@
     const DexFile& dex_file, const DexFile::ClassDef& class_def)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   auto* const runtime = Runtime::Current();
-  if (runtime->UseJitCompilation() || driver.GetCompilerOptions().VerifyAtRuntime()) {
-    // Verify at runtime shouldn't dex to dex since we didn't resolve of verify.
-    return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile;
-  }
+  DCHECK(driver.GetCompilerOptions().IsAnyMethodCompilationEnabled());
   const char* descriptor = dex_file.GetClassDescriptor(class_def);
   ClassLinker* class_linker = runtime->GetClassLinker();
   mirror::Class* klass = class_linker->FindClass(self, descriptor, class_loader);
@@ -948,24 +955,17 @@
   LoadImageClasses(timings);
   VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false);
 
-  const bool verification_enabled = compiler_options_->IsVerificationEnabled();
-  const bool never_verify = compiler_options_->NeverVerify();
-  const bool verify_only_profile = compiler_options_->VerifyOnlyProfile();
-
-  // We need to resolve for never_verify since it needs to run dex to dex to add the
-  // RETURN_VOID_NO_BARRIER.
-  // Let the verifier resolve as needed for the verify_only_profile case.
-  if ((never_verify || verification_enabled) && !verify_only_profile) {
+  if (compiler_options_->IsAnyMethodCompilationEnabled()) {
     Resolve(class_loader, dex_files, timings);
     VLOG(compiler) << "Resolve: " << GetMemoryUsageString(false);
   }
 
-  if (never_verify) {
+  if (compiler_options_->AssumeClassesAreVerified()) {
     VLOG(compiler) << "Verify none mode specified, skipping verification.";
     SetVerified(class_loader, dex_files, timings);
   }
 
-  if (!verification_enabled) {
+  if (!compiler_options_->IsVerificationEnabled()) {
     return;
   }
 
@@ -983,8 +983,10 @@
                << "situations. Please check the log.";
   }
 
-  InitializeClasses(class_loader, dex_files, timings);
-  VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
+  if (compiler_options_->IsAnyMethodCompilationEnabled()) {
+    InitializeClasses(class_loader, dex_files, timings);
+    VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
+  }
 
   UpdateImageClasses(timings);
   VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false);
@@ -1529,90 +1531,6 @@
   }
 }
 
-void CompilerDriver::GetCodeAndMethodForDirectCall(const mirror::Class* referrer_class,
-                                                   ArtMethod* method,
-                                                   uintptr_t* direct_code,
-                                                   uintptr_t* direct_method) {
-  // For direct and static methods compute possible direct_code and direct_method values, ie
-  // an address for the Method* being invoked and an address of the code for that Method*.
-  // For interface calls compute a value for direct_method that is the interface method being
-  // invoked, so this can be passed to the out-of-line runtime support code.
-  *direct_code = 0;
-  *direct_method = 0;
-  Runtime* const runtime = Runtime::Current();
-  gc::Heap* const heap = runtime->GetHeap();
-  auto* cl = runtime->GetClassLinker();
-  bool use_dex_cache = GetCompilerOptions().GetCompilePic();  // Off by default
-  const bool compiling_boot = heap->IsCompilingBoot();
-  // TODO This is somewhat hacky. We should refactor all of this invoke codepath.
-  const bool force_relocations = (compiling_boot ||
-                                  GetCompilerOptions().GetIncludePatchInformation());
-  // TODO: support patching on all architectures.
-  use_dex_cache = use_dex_cache || (force_relocations && !support_boot_image_fixup_);
-  mirror::Class* declaring_class = method->GetDeclaringClass();
-  bool method_code_in_boot = declaring_class->GetClassLoader() == nullptr;
-  if (!use_dex_cache) {
-    if (!method_code_in_boot) {
-      use_dex_cache = true;
-    } else if (method->IsStatic() &&
-               declaring_class != referrer_class &&
-               !declaring_class->IsInitialized()) {
-      // Ensure we run the clinit trampoline unless we are invoking a static method in the same
-      // class.
-      use_dex_cache = true;
-    }
-  }
-  if (runtime->UseJitCompilation()) {
-    // If we are the JIT, then don't allow a direct call to the interpreter bridge since this will
-    // never be updated even after we compile the method.
-    if (cl->IsQuickToInterpreterBridge(
-        reinterpret_cast<const void*>(compiler_->GetEntryPointOf(method)))) {
-      use_dex_cache = true;
-    }
-  }
-
-  if (!use_dex_cache && force_relocations) {
-    bool is_in_image;
-    if (GetCompilerOptions().IsBootImage()) {
-      is_in_image = IsImageClass(method->GetDeclaringClassDescriptor());
-    } else {
-      is_in_image = instruction_set_ != kX86 && instruction_set_ != kX86_64 &&
-                    heap->FindSpaceFromObject(method->GetDeclaringClass(), false)->IsImageSpace() &&
-                    !cl->IsQuickToInterpreterBridge(
-                        reinterpret_cast<const void*>(compiler_->GetEntryPointOf(method)));
-    }
-    if (!is_in_image) {
-      // We can only branch directly to Methods that are resolved in the DexCache.
-      // Otherwise we won't invoke the resolution trampoline.
-      use_dex_cache = true;
-    }
-  }
-
-  if (!use_dex_cache) {
-    bool method_in_image = false;
-    const std::vector<gc::space::ImageSpace*>& image_spaces = heap->GetBootImageSpaces();
-    for (gc::space::ImageSpace* image_space : image_spaces) {
-      const auto& method_section = image_space->GetImageHeader().GetMethodsSection();
-      if (method_section.Contains(reinterpret_cast<uint8_t*>(method) - image_space->Begin())) {
-        method_in_image = true;
-        break;
-      }
-    }
-    if (method_in_image || compiling_boot || runtime->UseJitCompilation()) {
-      // We know we must be able to get to the method in the image, so use that pointer.
-      // In the case where we are the JIT, we can always use direct pointers since we know where
-      // the method and its code are / will be. We don't sharpen to interpreter bridge since we
-      // check IsQuickToInterpreterBridge above.
-      CHECK(!method->IsAbstract());
-      *direct_method = force_relocations ? -1 : reinterpret_cast<uintptr_t>(method);
-      *direct_code = force_relocations ? -1 : compiler_->GetEntryPointOf(method);
-    } else {
-      // Set the code and rely on the dex cache for the method.
-      *direct_code = force_relocations ? -1 : compiler_->GetEntryPointOf(method);
-    }
-  }
-}
-
 const VerifiedMethod* CompilerDriver::GetVerifiedMethod(const DexFile* dex_file,
                                                         uint32_t method_idx) const {
   MethodReference ref(dex_file, method_idx);
@@ -2060,21 +1978,32 @@
         std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end());
         for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
           const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
-          const char* descriptor = dex_file->GetClassDescriptor(class_def);
-          cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader));
-          if (cls.Get() == nullptr) {
-            CHECK(soa.Self()->IsExceptionPending());
-            soa.Self()->ClearException();
-          } else if (set.find(class_def.class_idx_) == set.end()) {
-            ObjectLock<mirror::Class> lock(soa.Self(), cls);
-            mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self());
-            // Create `VerifiedMethod`s for each methods, the compiler expects one for
-            // quickening or compiling.
-            // Note that this means:
-            // - We're only going to compile methods that did verify.
-            // - Quickening will not do checkcast ellision.
-            // TODO(ngeoffray): Reconsider this once we refactor compiler filters.
-            PopulateVerifiedMethods(*dex_file, i, verification_results_);
+          if (set.find(class_def.class_idx_) == set.end()) {
+            if (!GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
+              // Just update the compiled_classes_ map. The compiler doesn't need to resolve
+              // the type.
+              compiled_classes_.Overwrite(
+                  ClassReference(dex_file, i), new CompiledClass(mirror::Class::kStatusVerified));
+            } else {
+              // Resolve the type, so later compilation stages know they don't need to verify
+              // the class.
+              const char* descriptor = dex_file->GetClassDescriptor(class_def);
+              cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader));
+              if (cls.Get() != nullptr) {
+                ObjectLock<mirror::Class> lock(soa.Self(), cls);
+                mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self());
+              } else {
+                DCHECK(soa.Self()->IsExceptionPending());
+                soa.Self()->ClearException();
+              }
+              // Create `VerifiedMethod`s for each methods, the compiler expects one for
+              // quickening or compiling.
+              // Note that this means:
+              // - We're only going to compile methods that did verify.
+              // - Quickening will not do checkcast ellision.
+              // TODO(ngeoffray): Reconsider this once we refactor compiler filters.
+              PopulateVerifiedMethods(*dex_file, i, verification_results_);
+            }
           }
         }
       }
@@ -2675,29 +2604,29 @@
 }
 
 void CompilerDriver::RecordClassStatus(ClassReference ref, mirror::Class::Status status) {
+  switch (status) {
+    case mirror::Class::kStatusNotReady:
+    case mirror::Class::kStatusError:
+    case mirror::Class::kStatusRetryVerificationAtRuntime:
+    case mirror::Class::kStatusVerified:
+    case mirror::Class::kStatusInitialized:
+    case mirror::Class::kStatusResolved:
+      break;  // Expected states.
+    default:
+      LOG(FATAL) << "Unexpected class status for class "
+          << PrettyDescriptor(ref.first->GetClassDescriptor(ref.first->GetClassDef(ref.second)))
+          << " of " << status;
+  }
+
   MutexLock mu(Thread::Current(), compiled_classes_lock_);
   auto it = compiled_classes_.find(ref);
-  if (it == compiled_classes_.end() || it->second->GetStatus() != status) {
-    // An entry doesn't exist or the status is lower than the new status.
-    if (it != compiled_classes_.end()) {
-      CHECK_GT(status, it->second->GetStatus());
-      delete it->second;
-    }
-    switch (status) {
-      case mirror::Class::kStatusNotReady:
-      case mirror::Class::kStatusError:
-      case mirror::Class::kStatusRetryVerificationAtRuntime:
-      case mirror::Class::kStatusVerified:
-      case mirror::Class::kStatusInitialized:
-      case mirror::Class::kStatusResolved:
-        break;  // Expected states.
-      default:
-        LOG(FATAL) << "Unexpected class status for class "
-            << PrettyDescriptor(ref.first->GetClassDescriptor(ref.first->GetClassDef(ref.second)))
-            << " of " << status;
-    }
+  if (it == compiled_classes_.end()) {
     CompiledClass* compiled_class = new CompiledClass(status);
     compiled_classes_.Overwrite(ref, compiled_class);
+  } else if (status > it->second->GetStatus()) {
+    // Update the status if we now have a greater one. This happens with vdex,
+    // which records a class is verified, but does not resolve it.
+    it->second->SetStatus(status);
   }
 }
 
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index cc50197..2e3b7c8 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -378,13 +378,6 @@
     return current_dex_to_dex_methods_;
   }
 
-  // Compute constant code and method pointers when possible.
-  void GetCodeAndMethodForDirectCall(const mirror::Class* referrer_class,
-                                     ArtMethod* method,
-                                     /* out */ uintptr_t* direct_code,
-                                     /* out */ uintptr_t* direct_method)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
  private:
   // Return whether the declaring class of `resolved_member` is
   // available to `referrer_class` for read or write access using two
@@ -440,9 +433,12 @@
                       TimingLogger* timings)
       REQUIRES(!Locks::mutator_lock_);
 
+  // NO_THREAD_SAFETY_ANALYSIS as the method accesses a guarded value in a
+  // single-threaded way.
   void Verify(jobject class_loader,
               const std::vector<const DexFile*>& dex_files,
-              TimingLogger* timings);
+              TimingLogger* timings)
+    NO_THREAD_SAFETY_ANALYSIS;
 
   void VerifyDexFile(jobject class_loader,
                      const DexFile& dex_file,
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 9c62f80..6894cd5 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -109,7 +109,7 @@
     return CompilerFilter::IsVerificationEnabled(compiler_filter_);
   }
 
-  bool NeverVerify() const {
+  bool AssumeClassesAreVerified() const {
     return compiler_filter_ == CompilerFilter::kVerifyNone;
   }
 
@@ -117,6 +117,10 @@
     return compiler_filter_ == CompilerFilter::kVerifyProfile;
   }
 
+  bool IsAnyMethodCompilationEnabled() const {
+    return CompilerFilter::IsAnyMethodCompilationEnabled(compiler_filter_);
+  }
+
   size_t GetHugeMethodThreshold() const {
     return huge_method_threshold_;
   }
diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc
index 64fd9e7..47b1929 100644
--- a/compiler/driver/dex_compilation_unit.cc
+++ b/compiler/driver/dex_compilation_unit.cc
@@ -16,7 +16,6 @@
 
 #include "dex_compilation_unit.h"
 
-#include "base/stringprintf.h"
 #include "mirror/dex_cache.h"
 #include "utils.h"
 
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
index 6f48779..9669c4a 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -16,7 +16,6 @@
 
 #include "elf_file.h"
 
-#include "base/stringprintf.h"
 #include "base/unix_file/fd_file.h"
 #include "common_compiler_test.h"
 #include "elf_file.h"
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 9bbe595..b0225a3 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -20,10 +20,14 @@
 #include <string>
 #include <vector>
 
+#include "android-base/stringprintf.h"
+
 #include "base/unix_file/fd_file.h"
 #include "class_linker-inl.h"
+#include "compiler_callbacks.h"
 #include "common_compiler_test.h"
 #include "debug/method_debug_info.h"
+#include "dex/quick_compiler_callbacks.h"
 #include "driver/compiler_options.h"
 #include "elf_writer.h"
 #include "elf_writer_quick.h"
@@ -74,6 +78,14 @@
                const std::string& extra_dex = "",
                const std::string& image_class = "");
 
+  void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
+    CommonCompilerTest::SetUpRuntimeOptions(options);
+    callbacks_.reset(new QuickCompilerCallbacks(
+        verification_results_.get(),
+        CompilerCallbacks::CallbackMode::kCompileBootImage));
+    options->push_back(std::make_pair("compilercallbacks", callbacks_.get()));
+  }
+
   std::unordered_set<std::string>* GetImageClasses() OVERRIDE {
     return new std::unordered_set<std::string>(image_classes_);
   }
@@ -134,7 +146,8 @@
     // Create a generic tmp file, to be the base of the .art and .oat temporary files.
     ScratchFile location;
     for (int i = 0; i < static_cast<int>(class_path.size()); ++i) {
-      std::string cur_location(StringPrintf("%s-%d.art", location.GetFilename().c_str(), i));
+      std::string cur_location =
+          android::base::StringPrintf("%s-%d.art", location.GetFilename().c_str(), i);
       image_locations.push_back(ScratchFile(cur_location));
     }
   }
@@ -241,6 +254,7 @@
             driver->GetInstructionSetFeatures(),
             &key_value_store,
             /* verify */ false,           // Dex files may be dex-to-dex-ed, don't verify.
+            /* update_input_vdex */ false,
             &cur_opened_dex_files_map,
             &cur_opened_dex_files);
         ASSERT_TRUE(dex_files_ok);
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index b22ca47..9c38445 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -1113,11 +1113,15 @@
   }
 
   // build an Object[] of the roots needed to restore the runtime
+  int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compile_app_image_);
   auto image_roots(hs.NewHandle(
-      ObjectArray<Object>::Alloc(self, object_array_class.Get(), ImageHeader::kImageRootsMax)));
+      ObjectArray<Object>::Alloc(self, object_array_class.Get(), image_roots_size)));
   image_roots->Set<false>(ImageHeader::kDexCaches, dex_caches.Get());
   image_roots->Set<false>(ImageHeader::kClassRoots, class_linker->GetClassRoots());
-  for (int i = 0; i < ImageHeader::kImageRootsMax; i++) {
+  // image_roots[ImageHeader::kClassLoader] will be set later for app image.
+  static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax,
+                "Class loader should be the last image root.");
+  for (int32_t i = 0; i < ImageHeader::kImageRootsMax - 1; ++i) {
     CHECK(image_roots->Get(i) != nullptr);
   }
   return image_roots.Get();
@@ -1539,6 +1543,12 @@
     }
     // Process the work stack in case anything was added by TryAssignBinSlot.
     ProcessWorkStack(&work_stack);
+
+    // Store the class loader in the class roots.
+    CHECK_EQ(class_loaders_.size(), 1u);
+    CHECK_EQ(image_roots.size(), 1u);
+    CHECK(*class_loaders_.begin() != nullptr);
+    image_roots[0]->Set<false>(ImageHeader::kClassLoader, *class_loaders_.begin());
   }
 
   // Verify that all objects have assigned image bin slots.
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index c537483..cc7df1c 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -27,6 +27,7 @@
 #include <string>
 #include <ostream>
 
+#include "art_method.h"
 #include "base/bit_utils.h"
 #include "base/dchecked_vector.h"
 #include "base/enums.h"
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 9dfb434..148ce4f 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -16,6 +16,8 @@
 
 #include "jit_compiler.h"
 
+#include "android-base/stringprintf.h"
+
 #include "arch/instruction_set.h"
 #include "arch/instruction_set_features.h"
 #include "art_method-inl.h"
@@ -81,7 +83,7 @@
   va_list ap;
   va_start(ap, fmt);
   std::string error;
-  StringAppendV(&error, fmt, ap);
+  android::base::StringAppendV(&error, fmt, ap);
   LOG(FATAL) << error;
   va_end(ap);
   exit(EXIT_FAILURE);
diff --git a/compiler/linker/mips/relative_patcher_mips32r6_test.cc b/compiler/linker/mips/relative_patcher_mips32r6_test.cc
index a16aaca..4f9a3a0 100644
--- a/compiler/linker/mips/relative_patcher_mips32r6_test.cc
+++ b/compiler/linker/mips/relative_patcher_mips32r6_test.cc
@@ -29,10 +29,10 @@
   Mips32r6RelativePatcherTest() : RelativePatcherTest(kMips, "mips32r6") {}
 
  protected:
-  static const uint8_t UnpatchedPcRelativeRawCode[];
-  static const uint32_t LiteralOffset;
-  static const uint32_t AnchorOffset;
-  static const ArrayRef<const uint8_t> UnpatchedPcRelativeCode;
+  static const uint8_t kUnpatchedPcRelativeRawCode[];
+  static const uint32_t kLiteralOffset;
+  static const uint32_t kAnchorOffset;
+  static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
 
   uint32_t GetMethodOffset(uint32_t method_idx) {
     auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
@@ -45,24 +45,25 @@
   void TestStringReference(uint32_t string_offset);
 };
 
-const uint8_t Mips32r6RelativePatcherTest::UnpatchedPcRelativeRawCode[] = {
+const uint8_t Mips32r6RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
     0x34, 0x12, 0x5E, 0xEE,  // auipc s2, high(diff); placeholder = 0x1234
     0x78, 0x56, 0x52, 0x26,  // addiu s2, s2, low(diff); placeholder = 0x5678
 };
-const uint32_t Mips32r6RelativePatcherTest::LiteralOffset = 0;  // At auipc (where patching starts).
-const uint32_t Mips32r6RelativePatcherTest::AnchorOffset = 0;  // At auipc (where PC+0 points).
-const ArrayRef<const uint8_t> Mips32r6RelativePatcherTest::UnpatchedPcRelativeCode(
-    UnpatchedPcRelativeRawCode);
+const uint32_t Mips32r6RelativePatcherTest::kLiteralOffset = 0;  // At auipc (where
+                                                                 // patching starts).
+const uint32_t Mips32r6RelativePatcherTest::kAnchorOffset = 0;  // At auipc (where PC+0 points).
+const ArrayRef<const uint8_t> Mips32r6RelativePatcherTest::kUnpatchedPcRelativeCode(
+    kUnpatchedPcRelativeRawCode);
 
 void Mips32r6RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches,
                                                        uint32_t target_offset) {
-  AddCompiledMethod(MethodRef(1u), UnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
+  AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
   Link();
 
   auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
   ASSERT_TRUE(result.first);
 
-  uint32_t diff = target_offset - (result.second + AnchorOffset);
+  uint32_t diff = target_offset - (result.second + kAnchorOffset);
   if (patches[0].GetType() == LinkerPatch::Type::kDexCacheArray) {
     diff += kDexCacheArrayLwOffset;
   }
@@ -79,7 +80,7 @@
                                                         uint32_t element_offset) {
   dex_cache_arrays_begin_ = dex_cache_arrays_begin;
   LinkerPatch patches[] = {
-      LinkerPatch::DexCacheArrayPatch(LiteralOffset, nullptr, AnchorOffset, element_offset)
+      LinkerPatch::DexCacheArrayPatch(kLiteralOffset, nullptr, kAnchorOffset, element_offset)
   };
   CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches),
                        dex_cache_arrays_begin_ + element_offset);
@@ -89,7 +90,7 @@
   constexpr uint32_t kStringIndex = 1u;
   string_index_to_offset_map_.Put(kStringIndex, string_offset);
   LinkerPatch patches[] = {
-      LinkerPatch::RelativeStringPatch(LiteralOffset, nullptr, AnchorOffset, kStringIndex)
+      LinkerPatch::RelativeStringPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex)
   };
   CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset);
 }
diff --git a/compiler/linker/mips/relative_patcher_mips_test.cc b/compiler/linker/mips/relative_patcher_mips_test.cc
index 335ce2e..faeb92a 100644
--- a/compiler/linker/mips/relative_patcher_mips_test.cc
+++ b/compiler/linker/mips/relative_patcher_mips_test.cc
@@ -29,10 +29,10 @@
   MipsRelativePatcherTest() : RelativePatcherTest(kMips, "mips32r2") {}
 
  protected:
-  static const uint8_t UnpatchedPcRelativeRawCode[];
-  static const uint32_t LiteralOffset;
-  static const uint32_t AnchorOffset;
-  static const ArrayRef<const uint8_t> UnpatchedPcRelativeCode;
+  static const uint8_t kUnpatchedPcRelativeRawCode[];
+  static const uint32_t kLiteralOffset;
+  static const uint32_t kAnchorOffset;
+  static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
 
   uint32_t GetMethodOffset(uint32_t method_idx) {
     auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
@@ -45,26 +45,26 @@
   void TestStringReference(uint32_t string_offset);
 };
 
-const uint8_t MipsRelativePatcherTest::UnpatchedPcRelativeRawCode[] = {
+const uint8_t MipsRelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
     0x00, 0x00, 0x10, 0x04,  // nal
     0x34, 0x12, 0x12, 0x3C,  // lui  s2, high(diff); placeholder = 0x1234
     0x78, 0x56, 0x52, 0x36,  // ori  s2, s2, low(diff); placeholder = 0x5678
     0x21, 0x90, 0x5F, 0x02,  // addu s2, s2, ra
 };
-const uint32_t MipsRelativePatcherTest::LiteralOffset = 4;  // At lui (where patching starts).
-const uint32_t MipsRelativePatcherTest::AnchorOffset = 8;  // At ori (where PC+0 points).
-const ArrayRef<const uint8_t> MipsRelativePatcherTest::UnpatchedPcRelativeCode(
-    UnpatchedPcRelativeRawCode);
+const uint32_t MipsRelativePatcherTest::kLiteralOffset = 4;  // At lui (where patching starts).
+const uint32_t MipsRelativePatcherTest::kAnchorOffset = 8;  // At ori (where PC+0 points).
+const ArrayRef<const uint8_t> MipsRelativePatcherTest::kUnpatchedPcRelativeCode(
+    kUnpatchedPcRelativeRawCode);
 
 void MipsRelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches,
                                                    uint32_t target_offset) {
-  AddCompiledMethod(MethodRef(1u), UnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
+  AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
   Link();
 
   auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
   ASSERT_TRUE(result.first);
 
-  uint32_t diff = target_offset - (result.second + AnchorOffset);
+  uint32_t diff = target_offset - (result.second + kAnchorOffset);
   if (patches[0].GetType() == LinkerPatch::Type::kDexCacheArray) {
     diff += kDexCacheArrayLwOffset;
   }
@@ -82,7 +82,7 @@
                                                     uint32_t element_offset) {
   dex_cache_arrays_begin_ = dex_cache_arrays_begin;
   LinkerPatch patches[] = {
-      LinkerPatch::DexCacheArrayPatch(LiteralOffset, nullptr, AnchorOffset, element_offset)
+      LinkerPatch::DexCacheArrayPatch(kLiteralOffset, nullptr, kAnchorOffset, element_offset)
   };
   CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches),
                        dex_cache_arrays_begin_ + element_offset);
@@ -92,7 +92,7 @@
   constexpr uint32_t kStringIndex = 1u;
   string_index_to_offset_map_.Put(kStringIndex, string_offset);
   LinkerPatch patches[] = {
-      LinkerPatch::RelativeStringPatch(LiteralOffset, nullptr, AnchorOffset, kStringIndex)
+      LinkerPatch::RelativeStringPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex)
   };
   CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset);
 }
diff --git a/compiler/linker/mips64/relative_patcher_mips64.cc b/compiler/linker/mips64/relative_patcher_mips64.cc
new file mode 100644
index 0000000..c479716
--- /dev/null
+++ b/compiler/linker/mips64/relative_patcher_mips64.cc
@@ -0,0 +1,111 @@
+/*
+ * 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 "linker/mips64/relative_patcher_mips64.h"
+
+#include "compiled_method.h"
+
+namespace art {
+namespace linker {
+
+uint32_t Mips64RelativePatcher::ReserveSpace(
+    uint32_t offset,
+    const CompiledMethod* compiled_method ATTRIBUTE_UNUSED,
+    MethodReference method_ref ATTRIBUTE_UNUSED) {
+  return offset;  // No space reserved; no limit on relative call distance.
+}
+
+uint32_t Mips64RelativePatcher::ReserveSpaceEnd(uint32_t offset) {
+  return offset;  // No space reserved; no limit on relative call distance.
+}
+
+uint32_t Mips64RelativePatcher::WriteThunks(OutputStream* out ATTRIBUTE_UNUSED, uint32_t offset) {
+  return offset;  // No thunks added; no limit on relative call distance.
+}
+
+void Mips64RelativePatcher::PatchCall(std::vector<uint8_t>* code,
+                                      uint32_t literal_offset,
+                                      uint32_t patch_offset,
+                                      uint32_t target_offset) {
+  // Basic sanity checks.
+  DCHECK_GE(code->size(), 8u);
+  DCHECK_LE(literal_offset, code->size() - 8u);
+  // auipc reg, offset_high
+  DCHECK_EQ((*code)[literal_offset + 0], 0x34);
+  DCHECK_EQ((*code)[literal_offset + 1], 0x12);
+  DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E);
+  DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC);
+  // jialc reg, offset_low
+  DCHECK_EQ((*code)[literal_offset + 4], 0x78);
+  DCHECK_EQ((*code)[literal_offset + 5], 0x56);
+  DCHECK_EQ(((*code)[literal_offset + 6] & 0xE0), 0x00);
+  DCHECK_EQ((*code)[literal_offset + 7], 0xF8);
+
+  // Apply patch.
+  uint32_t diff = target_offset - patch_offset;
+  // Note that a combination of auipc with an instruction that adds a sign-extended
+  // 16-bit immediate operand (e.g. jialc) provides a PC-relative range of
+  // PC-0x80000000 to PC+0x7FFF7FFF on MIPS64, that is, short of 2GB on one end
+  // by 32KB.
+  diff += (diff & 0x8000) << 1;  // Account for sign extension in jialc.
+
+  // auipc reg, offset_high
+  (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16);
+  (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24);
+  // jialc reg, offset_low
+  (*code)[literal_offset + 4] = static_cast<uint8_t>(diff >> 0);
+  (*code)[literal_offset + 5] = static_cast<uint8_t>(diff >> 8);
+}
+
+void Mips64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
+                                                     const LinkerPatch& patch,
+                                                     uint32_t patch_offset,
+                                                     uint32_t target_offset) {
+  uint32_t anchor_literal_offset = patch.PcInsnOffset();
+  uint32_t literal_offset = patch.LiteralOffset();
+
+  // Basic sanity checks.
+  DCHECK_GE(code->size(), 8u);
+  DCHECK_LE(literal_offset, code->size() - 8u);
+  DCHECK_EQ(literal_offset, anchor_literal_offset);
+  // auipc reg, offset_high
+  DCHECK_EQ((*code)[literal_offset + 0], 0x34);
+  DCHECK_EQ((*code)[literal_offset + 1], 0x12);
+  DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E);
+  DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC);
+  // instr reg(s), offset_low
+  DCHECK_EQ((*code)[literal_offset + 4], 0x78);
+  DCHECK_EQ((*code)[literal_offset + 5], 0x56);
+
+  // Apply patch.
+  uint32_t anchor_offset = patch_offset - literal_offset + anchor_literal_offset;
+  uint32_t diff = target_offset - anchor_offset;
+  // Note that a combination of auipc with an instruction that adds a sign-extended
+  // 16-bit immediate operand (e.g. ld) provides a PC-relative range of
+  // PC-0x80000000 to PC+0x7FFF7FFF on MIPS64, that is, short of 2GB on one end
+  // by 32KB.
+  diff += (diff & 0x8000) << 1;  // Account for sign extension in instruction following auipc.
+
+  // auipc reg, offset_high
+  (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16);
+  (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24);
+  // instr reg(s), offset_low
+  (*code)[literal_offset + 4] = static_cast<uint8_t>(diff >> 0);
+  (*code)[literal_offset + 5] = static_cast<uint8_t>(diff >> 8);
+}
+
+}  // namespace linker
+}  // namespace art
diff --git a/compiler/linker/mips64/relative_patcher_mips64.h b/compiler/linker/mips64/relative_patcher_mips64.h
new file mode 100644
index 0000000..8ef8ceb
--- /dev/null
+++ b/compiler/linker/mips64/relative_patcher_mips64.h
@@ -0,0 +1,50 @@
+/*
+ * 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_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_
+#define ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_
+
+#include "linker/relative_patcher.h"
+
+namespace art {
+namespace linker {
+
+class Mips64RelativePatcher FINAL : public RelativePatcher {
+ public:
+  explicit Mips64RelativePatcher() {}
+
+  uint32_t ReserveSpace(uint32_t offset,
+                        const CompiledMethod* compiled_method,
+                        MethodReference method_ref) OVERRIDE;
+  uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE;
+  uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
+  void PatchCall(std::vector<uint8_t>* code,
+                 uint32_t literal_offset,
+                 uint32_t patch_offset,
+                 uint32_t target_offset) OVERRIDE;
+  void PatchPcRelativeReference(std::vector<uint8_t>* code,
+                                const LinkerPatch& patch,
+                                uint32_t patch_offset,
+                                uint32_t target_offset) OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Mips64RelativePatcher);
+};
+
+}  // namespace linker
+}  // namespace art
+
+#endif  // ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_
diff --git a/compiler/linker/mips64/relative_patcher_mips64_test.cc b/compiler/linker/mips64/relative_patcher_mips64_test.cc
new file mode 100644
index 0000000..c317058
--- /dev/null
+++ b/compiler/linker/mips64/relative_patcher_mips64_test.cc
@@ -0,0 +1,127 @@
+/*
+ * 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 "linker/relative_patcher_test.h"
+#include "linker/mips64/relative_patcher_mips64.h"
+
+namespace art {
+namespace linker {
+
+class Mips64RelativePatcherTest : public RelativePatcherTest {
+ public:
+  Mips64RelativePatcherTest() : RelativePatcherTest(kMips64, "default") {}
+
+ protected:
+  static const uint8_t kUnpatchedPcRelativeRawCode[];
+  static const uint8_t kUnpatchedPcRelativeCallRawCode[];
+  static const uint32_t kLiteralOffset;
+  static const uint32_t kAnchorOffset;
+  static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
+  static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCallCode;
+
+  uint32_t GetMethodOffset(uint32_t method_idx) {
+    auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
+    CHECK(result.first);
+    return result.second;
+  }
+
+  void CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset);
+  void TestDexCacheReference(uint32_t dex_cache_arrays_begin, uint32_t element_offset);
+  void TestStringReference(uint32_t string_offset);
+};
+
+const uint8_t Mips64RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
+    0x34, 0x12, 0x5E, 0xEE,  // auipc s2, high(diff); placeholder = 0x1234
+    0x78, 0x56, 0x52, 0x66,  // daddiu s2, s2, low(diff); placeholder = 0x5678
+};
+const uint8_t Mips64RelativePatcherTest::kUnpatchedPcRelativeCallRawCode[] = {
+    0x34, 0x12, 0x3E, 0xEC,  // auipc at, high(diff); placeholder = 0x1234
+    0x78, 0x56, 0x01, 0xF8,  // jialc at, low(diff); placeholder = 0x5678
+};
+const uint32_t Mips64RelativePatcherTest::kLiteralOffset = 0;  // At auipc (where patching starts).
+const uint32_t Mips64RelativePatcherTest::kAnchorOffset = 0;  // At auipc (where PC+0 points).
+const ArrayRef<const uint8_t> Mips64RelativePatcherTest::kUnpatchedPcRelativeCode(
+    kUnpatchedPcRelativeRawCode);
+const ArrayRef<const uint8_t> Mips64RelativePatcherTest::kUnpatchedPcRelativeCallCode(
+    kUnpatchedPcRelativeCallRawCode);
+
+void Mips64RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches,
+                                                     uint32_t target_offset) {
+  AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
+  Link();
+
+  auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
+  ASSERT_TRUE(result.first);
+
+  uint32_t diff = target_offset - (result.second + kAnchorOffset);
+  diff += (diff & 0x8000) << 1;  // Account for sign extension in instruction following auipc.
+
+  const uint8_t expected_code[] = {
+      static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24), 0x5E, 0xEE,
+      static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x66,
+  };
+  EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
+}
+
+void Mips64RelativePatcherTest::TestDexCacheReference(uint32_t dex_cache_arrays_begin,
+                                                      uint32_t element_offset) {
+  dex_cache_arrays_begin_ = dex_cache_arrays_begin;
+  LinkerPatch patches[] = {
+      LinkerPatch::DexCacheArrayPatch(kLiteralOffset, nullptr, kAnchorOffset, element_offset)
+  };
+  CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches),
+                       dex_cache_arrays_begin_ + element_offset);
+}
+
+TEST_F(Mips64RelativePatcherTest, DexCacheReference) {
+  TestDexCacheReference(/* dex_cache_arrays_begin */ 0x12345678, /* element_offset */ 0x1234);
+}
+
+TEST_F(Mips64RelativePatcherTest, CallOther) {
+  LinkerPatch method1_patches[] = {
+      LinkerPatch::RelativeCodePatch(kLiteralOffset, nullptr, 2u),
+  };
+  AddCompiledMethod(MethodRef(1u),
+                    kUnpatchedPcRelativeCallCode,
+                    ArrayRef<const LinkerPatch>(method1_patches));
+  LinkerPatch method2_patches[] = {
+      LinkerPatch::RelativeCodePatch(kLiteralOffset, nullptr, 1u),
+  };
+  AddCompiledMethod(MethodRef(2u),
+                    kUnpatchedPcRelativeCallCode,
+                    ArrayRef<const LinkerPatch>(method2_patches));
+  Link();
+
+  uint32_t method1_offset = GetMethodOffset(1u);
+  uint32_t method2_offset = GetMethodOffset(2u);
+  uint32_t diff_after = method2_offset - (method1_offset + kAnchorOffset /* PC adjustment */);
+  diff_after += (diff_after & 0x8000) << 1;  // Account for sign extension in jialc.
+  static const uint8_t method1_expected_code[] = {
+      static_cast<uint8_t>(diff_after >> 16), static_cast<uint8_t>(diff_after >> 24), 0x3E, 0xEC,
+      static_cast<uint8_t>(diff_after), static_cast<uint8_t>(diff_after >> 8), 0x01, 0xF8,
+  };
+  EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
+  uint32_t diff_before = method1_offset - (method2_offset + kAnchorOffset /* PC adjustment */);
+  diff_before += (diff_before & 0x8000) << 1;  // Account for sign extension in jialc.
+  static const uint8_t method2_expected_code[] = {
+      static_cast<uint8_t>(diff_before >> 16), static_cast<uint8_t>(diff_before >> 24), 0x3E, 0xEC,
+      static_cast<uint8_t>(diff_before), static_cast<uint8_t>(diff_before >> 8), 0x01, 0xF8,
+  };
+  EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
+}
+
+}  // namespace linker
+}  // namespace art
diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc
index 7765594..f1538b1 100644
--- a/compiler/linker/relative_patcher.cc
+++ b/compiler/linker/relative_patcher.cc
@@ -25,6 +25,9 @@
 #ifdef ART_ENABLE_CODEGEN_mips
 #include "linker/mips/relative_patcher_mips.h"
 #endif
+#ifdef ART_ENABLE_CODEGEN_mips64
+#include "linker/mips64/relative_patcher_mips64.h"
+#endif
 #ifdef ART_ENABLE_CODEGEN_x86
 #include "linker/x86/relative_patcher_x86.h"
 #endif
@@ -103,6 +106,10 @@
       return std::unique_ptr<RelativePatcher>(
           new MipsRelativePatcher(features->AsMipsInstructionSetFeatures()));
 #endif
+#ifdef ART_ENABLE_CODEGEN_mips64
+    case kMips64:
+      return std::unique_ptr<RelativePatcher>(new Mips64RelativePatcher());
+#endif
     default:
       return std::unique_ptr<RelativePatcher>(new RelativePatcherNone);
   }
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 0a778b0..86d92ff 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "android-base/stringprintf.h"
+
 #include "arch/instruction_set_features.h"
 #include "art_method-inl.h"
 #include "base/enums.h"
@@ -48,7 +50,7 @@
   va_list ap;
   va_start(ap, fmt);
   std::string error;
-  StringAppendV(&error, fmt, ap);
+  android::base::StringAppendV(&error, fmt, ap);
   LOG(FATAL) << error;
   va_end(ap);
   UNREACHABLE();
@@ -196,6 +198,7 @@
                                          compiler_driver_->GetInstructionSetFeatures(),
                                          &key_value_store,
                                          verify,
+                                         /* update_input_vdex */ false,
                                          &opened_dex_files_map,
                                          &opened_dex_files)) {
       return false;
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index bebd5f5..a9da09c 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -483,6 +483,7 @@
     const InstructionSetFeatures* instruction_set_features,
     SafeMap<std::string, std::string>* key_value_store,
     bool verify,
+    bool update_input_vdex,
     /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
     /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
   CHECK(write_state_ == WriteState::kAddingDexFileSources);
@@ -511,15 +512,15 @@
   if (kIsVdexEnabled) {
     std::unique_ptr<BufferedOutputStream> vdex_out(
         MakeUnique<BufferedOutputStream>(MakeUnique<FileOutputStream>(vdex_file)));
-
     // Write DEX files into VDEX, mmap and open them.
-    if (!WriteDexFiles(vdex_out.get(), vdex_file) ||
+    if (!WriteDexFiles(vdex_out.get(), vdex_file, update_input_vdex) ||
         !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) {
       return false;
     }
   } else {
+    DCHECK(!update_input_vdex);
     // Write DEX files into OAT, mmap and open them.
-    if (!WriteDexFiles(oat_rodata, vdex_file) ||
+    if (!WriteDexFiles(oat_rodata, vdex_file, update_input_vdex) ||
         !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) {
       return false;
     }
@@ -1468,30 +1469,32 @@
       if (UNLIKELY(!visitor->StartClass(dex_file, class_def_index))) {
         return false;
       }
-      const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
-      const uint8_t* class_data = dex_file->GetClassData(class_def);
-      if (class_data != nullptr) {  // ie not an empty class, such as a marker interface
-        ClassDataItemIterator it(*dex_file, class_data);
-        while (it.HasNextStaticField()) {
-          it.Next();
-        }
-        while (it.HasNextInstanceField()) {
-          it.Next();
-        }
-        size_t class_def_method_index = 0u;
-        while (it.HasNextDirectMethod()) {
-          if (!visitor->VisitMethod(class_def_method_index, it)) {
-            return false;
+      if (compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
+        const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+        const uint8_t* class_data = dex_file->GetClassData(class_def);
+        if (class_data != nullptr) {  // ie not an empty class, such as a marker interface
+          ClassDataItemIterator it(*dex_file, class_data);
+          while (it.HasNextStaticField()) {
+            it.Next();
           }
-          ++class_def_method_index;
-          it.Next();
-        }
-        while (it.HasNextVirtualMethod()) {
-          if (UNLIKELY(!visitor->VisitMethod(class_def_method_index, it))) {
-            return false;
+          while (it.HasNextInstanceField()) {
+            it.Next();
           }
-          ++class_def_method_index;
-          it.Next();
+          size_t class_def_method_index = 0u;
+          while (it.HasNextDirectMethod()) {
+            if (!visitor->VisitMethod(class_def_method_index, it)) {
+              return false;
+            }
+            ++class_def_method_index;
+            it.Next();
+          }
+          while (it.HasNextVirtualMethod()) {
+            if (UNLIKELY(!visitor->VisitMethod(class_def_method_index, it))) {
+              return false;
+            }
+            ++class_def_method_index;
+            it.Next();
+          }
         }
       }
       if (UNLIKELY(!visitor->EndClass())) {
@@ -1548,6 +1551,9 @@
 }
 
 size_t OatWriter::InitOatMaps(size_t offset) {
+  if (!compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
+    return offset;
+  }
   InitMapMethodVisitor visitor(this, offset);
   bool success = VisitDexMethods(&visitor);
   DCHECK(success);
@@ -1594,6 +1600,9 @@
 }
 
 size_t OatWriter::InitOatCodeDexFiles(size_t offset) {
+  if (!compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
+    return offset;
+  }
   InitCodeMethodVisitor code_visitor(this, offset, vdex_quickening_info_offset_);
   bool success = VisitDexMethods(&code_visitor);
   DCHECK(success);
@@ -1745,19 +1754,24 @@
     return false;
   }
 
-  WriteQuickeningInfoMethodVisitor visitor(this, vdex_out, start_offset);
-  if (!VisitDexMethods(&visitor)) {
-    PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation();
-    return false;
+  if (compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
+    WriteQuickeningInfoMethodVisitor visitor(this, vdex_out, start_offset);
+    if (!VisitDexMethods(&visitor)) {
+      PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation();
+      return false;
+    }
+
+    if (!vdex_out->Flush()) {
+      PLOG(ERROR) << "Failed to flush stream after writing quickening info."
+                  << " File: " << vdex_out->GetLocation();
+      return false;
+    }
+    size_quickening_info_ = visitor.GetNumberOfWrittenBytes();
+  } else {
+    // We know we did not quicken.
+    size_quickening_info_ = 0;
   }
 
-  if (!vdex_out->Flush()) {
-    PLOG(ERROR) << "Failed to flush stream after writing quickening info."
-                << " File: " << vdex_out->GetLocation();
-    return false;
-  }
-
-  size_quickening_info_ = visitor.GetNumberOfWrittenBytes();
   vdex_size_ += size_quickening_info_;
   return true;
 }
@@ -2096,47 +2110,56 @@
   return true;
 }
 
-bool OatWriter::WriteDexFiles(OutputStream* out, File* file) {
+bool OatWriter::WriteDexFiles(OutputStream* out, File* file, bool update_input_vdex) {
   TimingLogger::ScopedTiming split("Write Dex files", timings_);
 
   vdex_dex_files_offset_ = vdex_size_;
 
   // Write dex files.
   for (OatDexFile& oat_dex_file : oat_dex_files_) {
-    if (!WriteDexFile(out, file, &oat_dex_file)) {
+    if (!WriteDexFile(out, file, &oat_dex_file, update_input_vdex)) {
       return false;
     }
   }
 
-  // Close sources.
+  CloseSources();
+  return true;
+}
+
+void OatWriter::CloseSources() {
   for (OatDexFile& oat_dex_file : oat_dex_files_) {
     oat_dex_file.source_.Clear();  // Get rid of the reference, it's about to be invalidated.
   }
   zipped_dex_files_.clear();
   zip_archives_.clear();
   raw_dex_files_.clear();
-  return true;
 }
 
-bool OatWriter::WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file) {
+bool OatWriter::WriteDexFile(OutputStream* out,
+                             File* file,
+                             OatDexFile* oat_dex_file,
+                             bool update_input_vdex) {
   if (!SeekToDexFile(out, file, oat_dex_file)) {
     return false;
   }
   if (profile_compilation_info_ != nullptr) {
+    DCHECK(!update_input_vdex);
     if (!LayoutAndWriteDexFile(out, oat_dex_file)) {
       return false;
     }
   } else if (oat_dex_file->source_.IsZipEntry()) {
+    DCHECK(!update_input_vdex);
     if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetZipEntry())) {
       return false;
     }
   } else if (oat_dex_file->source_.IsRawFile()) {
+    DCHECK(!update_input_vdex);
     if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetRawFile())) {
       return false;
     }
   } else {
     DCHECK(oat_dex_file->source_.IsRawData());
-    if (!WriteDexFile(out, oat_dex_file, oat_dex_file->source_.GetRawData())) {
+    if (!WriteDexFile(out, oat_dex_file, oat_dex_file->source_.GetRawData(), update_input_vdex)) {
       return false;
     }
   }
@@ -2146,6 +2169,7 @@
     DCHECK_EQ(vdex_size_, oat_dex_file->dex_file_offset_);
     vdex_size_ += oat_dex_file->dex_file_size_;
   } else {
+    DCHECK(!update_input_vdex);
     DCHECK_EQ(oat_size_, oat_dex_file->dex_file_offset_);
     oat_size_ += oat_dex_file->dex_file_size_;
   }
@@ -2216,7 +2240,7 @@
   DexLayout dex_layout(options, profile_compilation_info_, nullptr);
   dex_layout.ProcessDexFile(location.c_str(), dex_file.get(), 0);
   std::unique_ptr<MemMap> mem_map(dex_layout.GetAndReleaseMemMap());
-  if (!WriteDexFile(out, oat_dex_file, mem_map->Begin())) {
+  if (!WriteDexFile(out, oat_dex_file, mem_map->Begin(), /* update_input_vdex */ false)) {
     return false;
   }
   // Set the checksum of the new oat dex file to be the original file's checksum.
@@ -2373,22 +2397,27 @@
 
 bool OatWriter::WriteDexFile(OutputStream* out,
                              OatDexFile* oat_dex_file,
-                             const uint8_t* dex_file) {
+                             const uint8_t* dex_file,
+                             bool update_input_vdex) {
   // Note: The raw data has already been checked to contain the header
   // and all the data that the header specifies as the file size.
   DCHECK(dex_file != nullptr);
   DCHECK(ValidateDexFileHeader(dex_file, oat_dex_file->GetLocation()));
   const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(dex_file);
 
-  if (!out->WriteFully(dex_file, header->file_size_)) {
-    PLOG(ERROR) << "Failed to write dex file " << oat_dex_file->GetLocation()
-                << " to " << out->GetLocation();
-    return false;
-  }
-  if (!out->Flush()) {
-    PLOG(ERROR) << "Failed to flush stream after writing dex file."
-                << " File: " << oat_dex_file->GetLocation();
-    return false;
+  if (update_input_vdex) {
+    // The vdex already contains the dex code, no need to write it again.
+  } else {
+    if (!out->WriteFully(dex_file, header->file_size_)) {
+      PLOG(ERROR) << "Failed to write dex file " << oat_dex_file->GetLocation()
+                  << " to " << out->GetLocation();
+      return false;
+    }
+    if (!out->Flush()) {
+      PLOG(ERROR) << "Failed to flush stream after writing dex file."
+                  << " File: " << oat_dex_file->GetLocation();
+      return false;
+    }
   }
 
   // Update dex file size and resize class offsets in the OatDexFile.
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index da221d6..8d087f4 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -158,12 +158,15 @@
   // Supporting data structures are written into the .rodata section of the oat file.
   // The `verify` setting dictates whether the dex file verifier should check the dex files.
   // This is generally the case, and should only be false for tests.
+  // If `update_input_vdex` is true, then this method won't actually write the dex files,
+  // and the compiler will just re-use the existing vdex file.
   bool WriteAndOpenDexFiles(File* vdex_file,
                             OutputStream* oat_rodata,
                             InstructionSet instruction_set,
                             const InstructionSetFeatures* instruction_set_features,
                             SafeMap<std::string, std::string>* key_value_store,
                             bool verify,
+                            bool update_input_vdex,
                             /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
                             /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
   bool WriteQuickeningInfo(OutputStream* vdex_out);
@@ -263,8 +266,13 @@
   // with a given DexMethodVisitor.
   bool VisitDexMethods(DexMethodVisitor* visitor);
 
-  bool WriteDexFiles(OutputStream* out, File* file);
-  bool WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file);
+  // If `update_input_vdex` is true, then this method won't actually write the dex files,
+  // and the compiler will just re-use the existing vdex file.
+  bool WriteDexFiles(OutputStream* out, File* file, bool update_input_vdex);
+  bool WriteDexFile(OutputStream* out,
+                    File* file,
+                    OatDexFile* oat_dex_file,
+                    bool update_input_vdex);
   bool SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file);
   bool LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_file);
   bool WriteDexFile(OutputStream* out,
@@ -275,7 +283,10 @@
                     File* file,
                     OatDexFile* oat_dex_file,
                     File* dex_file);
-  bool WriteDexFile(OutputStream* out, OatDexFile* oat_dex_file, const uint8_t* dex_file);
+  bool WriteDexFile(OutputStream* out,
+                    OatDexFile* oat_dex_file,
+                    const uint8_t* dex_file,
+                    bool update_input_vdex);
   bool OpenDexFiles(File* file,
                     bool verify,
                     /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
@@ -306,6 +317,7 @@
                              const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files);
   bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta);
   void SetMultiOatRelativePatcherAdjustment();
+  void CloseSources();
 
   enum class WriteState {
     kAddingDexFileSources,
diff --git a/compiler/optimizing/cha_guard_optimization.cc b/compiler/optimizing/cha_guard_optimization.cc
new file mode 100644
index 0000000..fe42301
--- /dev/null
+++ b/compiler/optimizing/cha_guard_optimization.cc
@@ -0,0 +1,253 @@
+/*
+ * 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 "cha_guard_optimization.h"
+
+namespace art {
+
+// Note we can only do CHA guard elimination/motion in a single pass, since
+// if a guard is not removed, another guard might be removed due to
+// the existence of the first guard. The first guard should not be further
+// removed in another pass. For example, due to further optimizations,
+// a receiver of a guard might turn out to be a parameter value, or defined at
+// a different site, which makes the guard removable as a result. However
+// it's not safe to remove the guard in another pass since another guard might
+// have been removed due to the existence of this guard.
+//
+// As a consequence, we decided not to rely on other passes to remove them
+// (such as GVN or instruction simplifier).
+
+class CHAGuardVisitor : HGraphVisitor {
+ public:
+  explicit CHAGuardVisitor(HGraph* graph)
+      : HGraphVisitor(graph),
+        block_has_cha_guard_(GetGraph()->GetBlocks().size(),
+                             0,
+                             graph->GetArena()->Adapter(kArenaAllocCHA)) {
+    number_of_guards_to_visit_ = GetGraph()->GetNumberOfCHAGuards();
+    DCHECK_NE(number_of_guards_to_visit_, 0u);
+    // Will recount number of guards during guard optimization.
+    GetGraph()->SetNumberOfCHAGuards(0);
+  }
+
+  void VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) OVERRIDE;
+
+  void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
+
+ private:
+  void RemoveGuard(HShouldDeoptimizeFlag* flag);
+  // Return true if `flag` is removed.
+  bool OptimizeForParameter(HShouldDeoptimizeFlag* flag, HInstruction* receiver);
+  // Return true if `flag` is removed.
+  bool OptimizeWithDominatingGuard(HShouldDeoptimizeFlag* flag, HInstruction* receiver);
+  // Return true if `flag` is hoisted.
+  bool HoistGuard(HShouldDeoptimizeFlag* flag, HInstruction* receiver);
+
+  // Record if each block has any CHA guard. It's updated during the
+  // reverse post order visit. Use int instead of bool since ArenaVector
+  // does not support bool.
+  ArenaVector<int> block_has_cha_guard_;
+
+  // The iterator that's being used for this visitor. Need it to manually
+  // advance the iterator due to removing/moving more than one instruction.
+  HInstructionIterator* instruction_iterator_;
+
+  // Used to short-circuit the pass when there is no more guards left to visit.
+  uint32_t number_of_guards_to_visit_;
+
+  DISALLOW_COPY_AND_ASSIGN(CHAGuardVisitor);
+};
+
+void CHAGuardVisitor::VisitBasicBlock(HBasicBlock* block) {
+  if (number_of_guards_to_visit_ == 0) {
+    return;
+  }
+  // Skip phis, just iterate through instructions.
+  HInstructionIterator it(block->GetInstructions());
+  instruction_iterator_ = &it;
+  for (; !it.Done(); it.Advance()) {
+    DCHECK(it.Current()->IsInBlock());
+    it.Current()->Accept(this);
+  }
+}
+
+void CHAGuardVisitor::RemoveGuard(HShouldDeoptimizeFlag* flag) {
+  HBasicBlock* block = flag->GetBlock();
+  HInstruction* compare = flag->GetNext();
+  DCHECK(compare->IsNotEqual());
+  HInstruction* deopt = compare->GetNext();
+  DCHECK(deopt->IsDeoptimize());
+
+  // Advance instruction iterator first before we remove the guard.
+  // We need to do it twice since we remove three instructions and the
+  // visitor is responsible for advancing it once.
+  instruction_iterator_->Advance();
+  instruction_iterator_->Advance();
+  block->RemoveInstruction(deopt);
+  block->RemoveInstruction(compare);
+  block->RemoveInstruction(flag);
+}
+
+bool CHAGuardVisitor::OptimizeForParameter(HShouldDeoptimizeFlag* flag,
+                                           HInstruction* receiver) {
+  // If some compiled code is invalidated by CHA due to class loading, the
+  // compiled code will not be entered anymore. So the very fact that the
+  // compiled code is invoked guarantees that a parameter receiver conforms
+  // to all the CHA devirtualization assumptions made by the compiled code,
+  // since all parameter receivers pre-exist any (potential) invalidation of
+  // the compiled code.
+  //
+  // TODO: allow more cases such as a phi whose inputs are all parameters.
+  if (receiver->IsParameterValue()) {
+    RemoveGuard(flag);
+    return true;
+  }
+  return false;
+}
+
+bool CHAGuardVisitor::OptimizeWithDominatingGuard(HShouldDeoptimizeFlag* flag,
+                                                  HInstruction* receiver) {
+  // If there is another guard that dominates the current guard, and
+  // that guard is dominated by receiver's definition, then the current
+  // guard can be eliminated, since receiver must pre-exist that other
+  // guard, and passing that guard guarantees that receiver conforms to
+  // all the CHA devirtualization assumptions.
+  HBasicBlock* dominator = flag->GetBlock();
+  HBasicBlock* receiver_def_block = receiver->GetBlock();
+
+  // Complexity of the following algorithm:
+  // We potentially need to traverse the full dominator chain to receiver_def_block,
+  // plus a (partial) linear search within one block for each guard.
+  // So the worst case for each guard is bounded by the size of the
+  // biggest block plus the depth of the dominating tree.
+
+  while (dominator != receiver_def_block) {
+    if (block_has_cha_guard_[dominator->GetBlockId()] == 1) {
+      RemoveGuard(flag);
+      return true;
+    }
+    dominator = dominator->GetDominator();
+  }
+
+  // At this point dominator is the block where receiver is defined.
+  // We do a linear search within dominator to see if there is a guard after
+  // receiver's definition.
+  HInstruction* instruction;
+  if (dominator == flag->GetBlock()) {
+    // Flag and receiver are defined in the same block. Search backward from
+    // the current guard.
+    instruction = flag->GetPrevious();
+  } else {
+    // Search backward from the last instruction of that dominator.
+    instruction = dominator->GetLastInstruction();
+  }
+  while (instruction != receiver) {
+    if (instruction == nullptr) {
+      // receiver must be defined in this block, we didn't find it
+      // in the instruction list, so it must be a Phi.
+      DCHECK(receiver->IsPhi());
+      break;
+    }
+    if (instruction->IsShouldDeoptimizeFlag()) {
+      RemoveGuard(flag);
+      return true;
+    }
+    instruction = instruction->GetPrevious();
+  }
+  return false;
+}
+
+bool CHAGuardVisitor::HoistGuard(HShouldDeoptimizeFlag* flag,
+                                 HInstruction* receiver) {
+  // If receiver is loop invariant, we can hoist the guard out of the
+  // loop since passing a guard before entering the loop guarantees that
+  // receiver conforms to all the CHA devirtualization assumptions.
+  // We only hoist guards out of the inner loop since that offers most of the
+  // benefit and it might help remove other guards in the inner loop.
+  HBasicBlock* block = flag->GetBlock();
+  HLoopInformation* loop_info = block->GetLoopInformation();
+  if (loop_info != nullptr &&
+      !loop_info->IsIrreducible() &&
+      loop_info->IsDefinedOutOfTheLoop(receiver)) {
+    HInstruction* compare = flag->GetNext();
+    DCHECK(compare->IsNotEqual());
+    HInstruction* deopt = compare->GetNext();
+    DCHECK(deopt->IsDeoptimize());
+
+    // Advance instruction iterator first before we move the guard.
+    // We need to do it twice since we move three instructions and the
+    // visitor is responsible for advancing it once.
+    instruction_iterator_->Advance();
+    instruction_iterator_->Advance();
+
+    HBasicBlock* pre_header = loop_info->GetPreHeader();
+    flag->MoveBefore(pre_header->GetLastInstruction());
+    compare->MoveBefore(pre_header->GetLastInstruction());
+
+    block->RemoveInstruction(deopt);
+    HInstruction* suspend = loop_info->GetSuspendCheck();
+    // Need a new deoptimize instruction that copies the environment
+    // of the suspend instruction for the loop.
+    HDeoptimize* deoptimize =
+        new (GetGraph()->GetArena()) HDeoptimize(compare, suspend->GetDexPc());
+    pre_header->InsertInstructionBefore(deoptimize, pre_header->GetLastInstruction());
+    deoptimize->CopyEnvironmentFromWithLoopPhiAdjustment(
+        suspend->GetEnvironment(), loop_info->GetHeader());
+    block_has_cha_guard_[pre_header->GetBlockId()] = 1;
+    GetGraph()->IncrementNumberOfCHAGuards();
+    return true;
+  }
+  return false;
+}
+
+void CHAGuardVisitor::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
+  number_of_guards_to_visit_--;
+  HInstruction* receiver = flag->InputAt(0);
+  // Don't need the receiver anymore.
+  flag->RemoveInputAt(0);
+  if (receiver->IsNullCheck()) {
+    receiver = receiver->InputAt(0);
+  }
+
+  if (OptimizeForParameter(flag, receiver)) {
+    DCHECK(!flag->IsInBlock());
+    return;
+  }
+  if (OptimizeWithDominatingGuard(flag, receiver)) {
+    DCHECK(!flag->IsInBlock());
+    return;
+  }
+  if (HoistGuard(flag, receiver)) {
+    DCHECK(flag->IsInBlock());
+    return;
+  }
+
+  // Need to keep the CHA guard in place.
+  block_has_cha_guard_[flag->GetBlock()->GetBlockId()] = 1;
+  GetGraph()->IncrementNumberOfCHAGuards();
+}
+
+void CHAGuardOptimization::Run() {
+  if (graph_->GetNumberOfCHAGuards() == 0) {
+    return;
+  }
+  CHAGuardVisitor visitor(graph_);
+  for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+    visitor.VisitBasicBlock(block);
+  }
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/cha_guard_optimization.h b/compiler/optimizing/cha_guard_optimization.h
new file mode 100644
index 0000000..ba0cdb8
--- /dev/null
+++ b/compiler/optimizing/cha_guard_optimization.h
@@ -0,0 +1,42 @@
+/*
+ * 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_CHA_GUARD_OPTIMIZATION_H_
+#define ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_
+
+#include "optimization.h"
+
+namespace art {
+
+/**
+ * Optimize CHA guards by removing/moving them.
+ */
+class CHAGuardOptimization : public HOptimization {
+ public:
+  explicit CHAGuardOptimization(HGraph* graph)
+      : HOptimization(graph, kCHAGuardOptimizationPassName) {}
+
+  void Run() OVERRIDE;
+
+  static constexpr const char* kCHAGuardOptimizationPassName = "cha_guard_optimization";
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CHAGuardOptimization);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 8104613..8a7f6d3 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1201,11 +1201,6 @@
       isa_features_(isa_features),
       uint32_literals_(std::less<uint32_t>(),
                        graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      method_patches_(MethodReferenceComparator(),
-                      graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      call_patches_(MethodReferenceComparator(),
-                    graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       boot_image_string_patches_(StringReferenceValueComparator(),
                                  graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
@@ -7139,7 +7134,7 @@
 
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      HInvokeStaticOrDirect* invoke) {
+      HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
   HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
   // We disable pc-relative load when there is an irreducible loop, as the optimization
   // is incompatible with it.
@@ -7151,24 +7146,6 @@
     dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
   }
 
-  if (dispatch_info.code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) {
-    const DexFile& outer_dex_file = GetGraph()->GetDexFile();
-    if (&outer_dex_file != invoke->GetTargetMethod().dex_file) {
-      // Calls across dex files are more likely to exceed the available BL range,
-      // so use absolute patch with fixup if available and kCallArtMethod otherwise.
-      HInvokeStaticOrDirect::CodePtrLocation code_ptr_location =
-          (desired_dispatch_info.method_load_kind ==
-           HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup)
-          ? HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup
-          : HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
-      return HInvokeStaticOrDirect::DispatchInfo {
-        dispatch_info.method_load_kind,
-        code_ptr_location,
-        dispatch_info.method_load_data,
-        0u
-      };
-    }
-  }
   return dispatch_info;
 }
 
@@ -7199,20 +7176,6 @@
 }
 
 void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
-  // For better instruction scheduling we load the direct code pointer before the method pointer.
-  switch (invoke->GetCodePtrLocation()) {
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
-      // LR = code address from literal pool with link-time patch.
-      __ LoadLiteral(LR, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod()));
-      break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
-      // LR = invoke->GetDirectCodePtr();
-      __ LoadImmediate(LR, invoke->GetDirectCodePtr());
-      break;
-    default:
-      break;
-  }
-
   Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
   switch (invoke->GetMethodLoadKind()) {
     case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
@@ -7228,10 +7191,6 @@
     case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
       __ LoadImmediate(temp.AsRegister<Register>(), invoke->GetMethodAddress());
       break;
-    case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
-      __ LoadLiteral(temp.AsRegister<Register>(),
-                     DeduplicateMethodAddressLiteral(invoke->GetTargetMethod()));
-      break;
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
       HArmDexCacheArraysBase* base =
           invoke->InputAt(invoke->GetSpecialInputIndex())->AsArmDexCacheArraysBase();
@@ -7270,19 +7229,6 @@
     case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
       __ bl(GetFrameEntryLabel());
       break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
-      relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
-                                          invoke->GetTargetMethod().dex_method_index);
-      __ BindTrackedLabel(&relative_call_patches_.back().label);
-      // Arbitrarily branch to the BL itself, override at link time.
-      __ bl(&relative_call_patches_.back().label);
-      break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
-      // LR prepared above for better instruction scheduling.
-      // LR()
-      __ blx(LR);
-      break;
     case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
       // LR = callee_method->entry_point_from_quick_compiled_code_
       __ LoadFromOffset(
@@ -7410,9 +7356,6 @@
 void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   size_t size =
-      method_patches_.size() +
-      call_patches_.size() +
-      relative_call_patches_.size() +
       /* MOVW+MOVT for each entry */ 2u * pc_relative_dex_cache_patches_.size() +
       boot_image_string_patches_.size() +
       /* MOVW+MOVT for each entry */ 2u * pc_relative_string_patches_.size() +
@@ -7420,29 +7363,6 @@
       /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size() +
       boot_image_address_patches_.size();
   linker_patches->reserve(size);
-  for (const auto& entry : method_patches_) {
-    const MethodReference& target_method = entry.first;
-    Literal* literal = entry.second;
-    DCHECK(literal->GetLabel()->IsBound());
-    uint32_t literal_offset = literal->GetLabel()->Position();
-    linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset,
-                                                       target_method.dex_file,
-                                                       target_method.dex_method_index));
-  }
-  for (const auto& entry : call_patches_) {
-    const MethodReference& target_method = entry.first;
-    Literal* literal = entry.second;
-    DCHECK(literal->GetLabel()->IsBound());
-    uint32_t literal_offset = literal->GetLabel()->Position();
-    linker_patches->push_back(LinkerPatch::CodePatch(literal_offset,
-                                                     target_method.dex_file,
-                                                     target_method.dex_method_index));
-  }
-  for (const PatchInfo<Label>& info : relative_call_patches_) {
-    uint32_t literal_offset = info.label.Position();
-    linker_patches->push_back(
-        LinkerPatch::RelativeCodePatch(literal_offset, &info.dex_file, info.index));
-  }
   EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
                                                                linker_patches);
   for (const auto& entry : boot_image_string_patches_) {
@@ -7494,14 +7414,6 @@
       [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
 }
 
-Literal* CodeGeneratorARM::DeduplicateMethodAddressLiteral(MethodReference target_method) {
-  return DeduplicateMethodLiteral(target_method, &method_patches_);
-}
-
-Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_method) {
-  return DeduplicateMethodLiteral(target_method, &call_patches_);
-}
-
 void LocationsBuilderARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall);
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 605169d..6435851 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -607,8 +607,6 @@
 
   Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
   Literal* DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map);
-  Literal* DeduplicateMethodAddressLiteral(MethodReference target_method);
-  Literal* DeduplicateMethodCodeLiteral(MethodReference target_method);
   PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file,
                                           uint32_t offset_or_index,
                                           ArenaDeque<PcRelativePatchInfo>* patches);
@@ -627,12 +625,6 @@
 
   // Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
   Uint32ToLiteralMap uint32_literals_;
-  // Method patch info, map MethodReference to a literal for method address and method code.
-  MethodToLiteralMap method_patches_;
-  MethodToLiteralMap call_patches_;
-  // Relative call patch info.
-  // Using ArenaDeque<> which retains element addresses on push/emplace_back().
-  ArenaDeque<PatchInfo<Label>> relative_call_patches_;
   // PC-relative patch info for each HArmDexCacheArraysBase.
   ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
   // Deduplication map for boot string literals for kBootImageLinkTimeAddress.
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 5cff303..5c33fe1 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1147,11 +1147,6 @@
                        graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       uint64_literals_(std::less<uint64_t>(),
                        graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      method_patches_(MethodReferenceComparator(),
-                      graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      call_patches_(MethodReferenceComparator(),
-                    graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       boot_image_string_patches_(StringReferenceValueComparator(),
                                  graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
@@ -1538,8 +1533,17 @@
       DCHECK(source.IsStackSlot() || source.IsDoubleStackSlot());
       DCHECK(source.IsDoubleStackSlot() == destination.IsDoubleStackSlot());
       UseScratchRegisterScope temps(GetVIXLAssembler());
-      // There is generally less pressure on FP registers.
-      FPRegister temp = destination.IsDoubleStackSlot() ? temps.AcquireD() : temps.AcquireS();
+      // Use any scratch register (a core or a floating-point one)
+      // from VIXL scratch register pools as a temporary.
+      //
+      // We used to only use the FP scratch register pool, but in some
+      // rare cases the only register from this pool (D31) would
+      // already be used (e.g. within a ParallelMove instruction, when
+      // a move is blocked by a another move requiring a scratch FP
+      // register, which would reserve D31). To prevent this issue, we
+      // ask for a scratch register of any type (core or FP).
+      CPURegister temp =
+          temps.AcquireCPURegisterOfSize(destination.IsDoubleStackSlot() ? kXRegSize : kWRegSize);
       __ Ldr(temp, StackOperandFrom(source));
       __ Str(temp, StackOperandFrom(destination));
     }
@@ -3971,23 +3975,6 @@
 }
 
 void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
-  // For better instruction scheduling we load the direct code pointer before the method pointer.
-  bool direct_code_loaded = false;
-  switch (invoke->GetCodePtrLocation()) {
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
-      // LR = code address from literal pool with link-time patch.
-      __ Ldr(lr, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod()));
-      direct_code_loaded = true;
-      break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
-      // LR = invoke->GetDirectCodePtr();
-      __ Ldr(lr, DeduplicateUint64Literal(invoke->GetDirectCodePtr()));
-      direct_code_loaded = true;
-      break;
-    default:
-      break;
-  }
-
   // Make sure that ArtMethod* is passed in kArtMethodRegister as per the calling convention.
   Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
   switch (invoke->GetMethodLoadKind()) {
@@ -4005,11 +3992,6 @@
       // Load method address from literal pool.
       __ Ldr(XRegisterFrom(temp), DeduplicateUint64Literal(invoke->GetMethodAddress()));
       break;
-    case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
-      // Load method address from literal pool with a link-time patch.
-      __ Ldr(XRegisterFrom(temp),
-             DeduplicateMethodAddressLiteral(invoke->GetTargetMethod()));
-      break;
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
       // Add ADRP with its PC-relative DexCache access patch.
       const DexFile& dex_file = invoke->GetDexFile();
@@ -4051,23 +4033,6 @@
     case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
       __ Bl(&frame_entry_label_);
       break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: {
-      relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
-                                          invoke->GetTargetMethod().dex_method_index);
-      vixl::aarch64::Label* label = &relative_call_patches_.back().label;
-      SingleEmissionCheckScope guard(GetVIXLAssembler());
-      __ Bind(label);
-      // Branch and link to itself. This will be overriden at link time.
-      __ bl(static_cast<int64_t>(0));
-      break;
-    }
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
-      // LR prepared above for better instruction scheduling.
-      DCHECK(direct_code_loaded);
-      // lr()
-      __ Blr(lr);
-      break;
     case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
       // LR = callee_method->entry_point_from_quick_compiled_code_;
       __ Ldr(lr, MemOperand(
@@ -4229,9 +4194,6 @@
 void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   size_t size =
-      method_patches_.size() +
-      call_patches_.size() +
-      relative_call_patches_.size() +
       pc_relative_dex_cache_patches_.size() +
       boot_image_string_patches_.size() +
       pc_relative_string_patches_.size() +
@@ -4239,24 +4201,6 @@
       pc_relative_type_patches_.size() +
       boot_image_address_patches_.size();
   linker_patches->reserve(size);
-  for (const auto& entry : method_patches_) {
-    const MethodReference& target_method = entry.first;
-    vixl::aarch64::Literal<uint64_t>* literal = entry.second;
-    linker_patches->push_back(LinkerPatch::MethodPatch(literal->GetOffset(),
-                                                       target_method.dex_file,
-                                                       target_method.dex_method_index));
-  }
-  for (const auto& entry : call_patches_) {
-    const MethodReference& target_method = entry.first;
-    vixl::aarch64::Literal<uint64_t>* literal = entry.second;
-    linker_patches->push_back(LinkerPatch::CodePatch(literal->GetOffset(),
-                                                     target_method.dex_file,
-                                                     target_method.dex_method_index));
-  }
-  for (const PatchInfo<vixl::aarch64::Label>& info : relative_call_patches_) {
-    linker_patches->push_back(
-        LinkerPatch::RelativeCodePatch(info.label.GetLocation(), &info.dex_file, info.index));
-  }
   for (const PcRelativePatchInfo& info : pc_relative_dex_cache_patches_) {
     linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(info.label.GetLocation(),
                                                               &info.target_dex_file,
@@ -4314,17 +4258,6 @@
       [this]() { return __ CreateLiteralDestroyedWithPool<uint64_t>(/* placeholder */ 0u); });
 }
 
-vixl::aarch64::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodAddressLiteral(
-    MethodReference target_method) {
-  return DeduplicateMethodLiteral(target_method, &method_patches_);
-}
-
-vixl::aarch64::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodCodeLiteral(
-    MethodReference target_method) {
-  return DeduplicateMethodLiteral(target_method, &call_patches_);
-}
-
-
 void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
   // Explicit clinit checks triggered by static invokes must have been pruned by
   // art::PrepareForRegisterAllocation.
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 85b6f9f..8f33b6b 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -693,8 +693,6 @@
   vixl::aarch64::Literal<uint64_t>* DeduplicateUint64Literal(uint64_t value);
   vixl::aarch64::Literal<uint64_t>* DeduplicateMethodLiteral(MethodReference target_method,
                                                              MethodToLiteralMap* map);
-  vixl::aarch64::Literal<uint64_t>* DeduplicateMethodAddressLiteral(MethodReference target_method);
-  vixl::aarch64::Literal<uint64_t>* DeduplicateMethodCodeLiteral(MethodReference target_method);
 
   // The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays
   // and boot image strings/types. The only difference is the interpretation of the
@@ -737,12 +735,6 @@
   Uint32ToLiteralMap uint32_literals_;
   // Deduplication map for 64-bit literals, used for non-patchable method address or method code.
   Uint64ToLiteralMap uint64_literals_;
-  // Method patch info, map MethodReference to a literal for method address and method code.
-  MethodToLiteralMap method_patches_;
-  MethodToLiteralMap call_patches_;
-  // Relative call patch info.
-  // Using ArenaDeque<> which retains element addresses on push/emplace_back().
-  ArenaDeque<PatchInfo<vixl::aarch64::Label>> relative_call_patches_;
   // PC-relative DexCache access info.
   ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
   // Deduplication map for boot string literals for kBootImageLinkTimeAddress.
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 2c6df38..f108595 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -46,8 +46,10 @@
 using helpers::InputRegister;
 using helpers::InputRegisterAt;
 using helpers::InputSRegisterAt;
+using helpers::InputVRegister;
 using helpers::InputVRegisterAt;
 using helpers::Int32ConstantFrom;
+using helpers::Int64ConstantFrom;
 using helpers::LocationFrom;
 using helpers::LowRegisterFrom;
 using helpers::LowSRegisterFrom;
@@ -56,6 +58,10 @@
 using helpers::OutputVRegister;
 using helpers::RegisterFrom;
 using helpers::SRegisterFrom;
+using helpers::Uint64ConstantFrom;
+
+using vixl::ExactAssemblyScope;
+using vixl::CodeBufferCheckScope;
 
 using RegisterList = vixl32::RegisterList;
 
@@ -799,7 +805,7 @@
     // as-is.
     vixl32::Label done;
     __ Cmp(temp1_, ref_reg);
-    __ B(eq, &done);
+    __ B(eq, &done, /* far_target */ false);
 
     // Update the the holder's field atomically.  This may fail if
     // mutator updates before us, but it's OK.  This is achieved
@@ -843,19 +849,19 @@
     __ Subs(tmp, tmp, expected);
 
     {
-      AssemblerAccurateScope aas(arm_codegen->GetVIXLAssembler(),
-                                 2 * kMaxInstructionSizeInBytes,
-                                 CodeBufferCheckScope::kMaximumSize);
+      ExactAssemblyScope aas(arm_codegen->GetVIXLAssembler(),
+                             2 * kMaxInstructionSizeInBytes,
+                             CodeBufferCheckScope::kMaximumSize);
 
       __ it(ne);
       __ clrex(ne);
     }
 
-    __ B(ne, &exit_loop);
+    __ B(ne, &exit_loop, /* far_target */ false);
 
     __ Strex(tmp, value, MemOperand(tmp_ptr));
     __ Cmp(tmp, 1);
-    __ B(eq, &loop_head);
+    __ B(eq, &loop_head, /* far_target */ false);
 
     __ Bind(&exit_loop);
 
@@ -1237,10 +1243,21 @@
       move_resolver_(graph->GetArena(), this),
       assembler_(graph->GetArena()),
       isa_features_(isa_features),
-      relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      uint32_literals_(std::less<uint32_t>(),
+                       graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      boot_image_string_patches_(StringReferenceValueComparator(),
+                                 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
+      boot_image_type_patches_(TypeReferenceValueComparator(),
+                               graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      boot_image_address_patches_(std::less<uint32_t>(),
+                                  graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      jit_string_patches_(StringReferenceValueComparator(),
+                          graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      jit_class_patches_(TypeReferenceValueComparator(),
+                         graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
   // Always save the LR register to mimic Quick.
   AddAllocatedRegister(Location::RegisterLocation(LR));
   // Give d14 and d15 as scratch registers to VIXL.
@@ -1261,9 +1278,9 @@
   // We are about to use the assembler to place literals directly. Make sure we have enough
   // 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);
+  ExactAssemblyScope aas(codegen->GetVIXLAssembler(),
+                         num_entries * sizeof(int32_t),
+                         CodeBufferCheckScope::kMaximumSize);
   // TODO(VIXL): Check that using lower case bind is fine here.
   codegen->GetVIXLAssembler()->bind(&table_start_);
   for (uint32_t i = 0; i < num_entries; i++) {
@@ -1375,11 +1392,11 @@
   if (!skip_overflow_check) {
     UseScratchRegisterScope temps(GetVIXLAssembler());
     vixl32::Register temp = temps.Acquire();
-    __ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
+    __ Sub(temp, sp, Operand::From(GetStackOverflowReservedBytes(kArm)));
     // The load must immediately precede RecordPcInfo.
-    AssemblerAccurateScope aas(GetVIXLAssembler(),
-                               vixl32::kMaxInstructionSizeInBytes,
-                               CodeBufferCheckScope::kMaximumSize);
+    ExactAssemblyScope aas(GetVIXLAssembler(),
+                           vixl32::kMaxInstructionSizeInBytes,
+                           CodeBufferCheckScope::kMaximumSize);
     __ ldr(temp, MemOperand(temp));
     RecordPcInfo(nullptr, 0);
   }
@@ -1637,9 +1654,9 @@
   __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value()));
   // Ensure the pc position is recorded immediately after the `blx` instruction.
   // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
-  AssemblerAccurateScope aas(GetVIXLAssembler(),
-                             vixl32::k16BitT32InstructionSizeInBytes,
-                             CodeBufferCheckScope::kExactSize);
+  ExactAssemblyScope aas(GetVIXLAssembler(),
+                         vixl32::k16BitT32InstructionSizeInBytes,
+                         CodeBufferCheckScope::kExactSize);
   __ blx(lr);
   if (EntrypointRequiresStackMap(entrypoint)) {
     RecordPcInfo(instruction, dex_pc, slow_path);
@@ -1792,7 +1809,7 @@
       break;
   }
   if (right.IsConstant()) {
-    int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
+    int64_t value = Int64ConstantFrom(right);
     int32_t val_low = Low32Bits(value);
     int32_t val_high = High32Bits(value);
 
@@ -1877,7 +1894,7 @@
         __ B(true_target);
       }
     } else {
-      DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
+      DCHECK(cond->AsIntConstant()->IsFalse()) << Int32ConstantFrom(cond);
       if (false_target != nullptr) {
         __ B(false_target);
       }
@@ -2082,9 +2099,9 @@
         __ Cmp(InputRegisterAt(cond, 0),
                CodeGenerator::GetInt32ValueOf(right.GetConstant()));
       }
-      AssemblerAccurateScope aas(GetVIXLAssembler(),
-                                 3 * vixl32::kMaxInstructionSizeInBytes,
-                                 CodeBufferCheckScope::kMaximumSize);
+      ExactAssemblyScope aas(GetVIXLAssembler(),
+                             3 * vixl32::kMaxInstructionSizeInBytes,
+                             CodeBufferCheckScope::kMaximumSize);
       __ ite(ARMCondition(cond->GetCondition()));
       __ mov(ARMCondition(cond->GetCondition()), OutputRegister(cond), 1);
       __ mov(ARMCondition(cond->GetOppositeCondition()), OutputRegister(cond), 0);
@@ -2370,9 +2387,9 @@
 
   // Ensure the pc position is recorded immediately after the `ldr` instruction.
   {
-    AssemblerAccurateScope aas(GetVIXLAssembler(),
-                               vixl32::kMaxInstructionSizeInBytes,
-                               CodeBufferCheckScope::kMaximumSize);
+    ExactAssemblyScope aas(GetVIXLAssembler(),
+                           vixl32::kMaxInstructionSizeInBytes,
+                           CodeBufferCheckScope::kMaximumSize);
     // /* HeapReference<Class> */ temp = receiver->klass_
     __ ldr(temp, MemOperand(RegisterFrom(receiver), class_offset));
     codegen_->MaybeRecordImplicitNullCheck(invoke);
@@ -2418,7 +2435,7 @@
   {
     // Ensure the pc position is recorded immediately after the `blx` instruction.
     // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
-    AssemblerAccurateScope aas(GetVIXLAssembler(),
+    ExactAssemblyScope aas(GetVIXLAssembler(),
                            vixl32::k16BitT32InstructionSizeInBytes,
                            CodeBufferCheckScope::kExactSize);
     // LR();
@@ -2479,9 +2496,7 @@
 
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble:
-      // TODO(VIXL): Consider introducing an InputVRegister()
-      // helper function (equivalent to InputRegister()).
-      __ Vneg(OutputVRegister(neg), InputVRegisterAt(neg, 0));
+      __ Vneg(OutputVRegister(neg), InputVRegister(neg));
       break;
 
     default:
@@ -2771,8 +2786,8 @@
           } else {
             DCHECK(in.IsConstant());
             DCHECK(in.GetConstant()->IsLongConstant());
-            int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
-            __ Mov(OutputRegister(conversion), static_cast<int32_t>(value));
+            int32_t value = Int32ConstantFrom(in);
+            __ Mov(OutputRegister(conversion), value);
           }
           break;
 
@@ -3111,8 +3126,8 @@
       // Extra checks to protect caused by the existence of R1_R2.
       // The algorithm is wrong if out.hi is either in1.lo or in2.lo:
       // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
-      DCHECK_NE(out_hi.GetCode(), in1_lo.GetCode());
-      DCHECK_NE(out_hi.GetCode(), in2_lo.GetCode());
+      DCHECK(!out_hi.Is(in1_lo));
+      DCHECK(!out_hi.Is(in2_lo));
 
       // input: in1 - 64 bits, in2 - 64 bits
       // output: out
@@ -3152,7 +3167,7 @@
 
   vixl32::Register out = OutputRegister(instruction);
   vixl32::Register dividend = InputRegisterAt(instruction, 0);
-  int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+  int32_t imm = Int32ConstantFrom(second);
   DCHECK(imm == 1 || imm == -1);
 
   if (instruction->IsRem()) {
@@ -3177,7 +3192,7 @@
   vixl32::Register out = OutputRegister(instruction);
   vixl32::Register dividend = InputRegisterAt(instruction, 0);
   vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
-  int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+  int32_t imm = Int32ConstantFrom(second);
   uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
   int ctz_imm = CTZ(abs_imm);
 
@@ -3250,7 +3265,7 @@
   Location second = instruction->GetLocations()->InAt(1);
   DCHECK(second.IsConstant());
 
-  int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+  int32_t imm = Int32ConstantFrom(second);
   if (imm == 0) {
     // Do not generate anything. DivZeroCheck would prevent any code to be executed.
   } else if (imm == 1 || imm == -1) {
@@ -3284,7 +3299,7 @@
         locations->SetInAt(0, Location::RequiresRegister());
         locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
         locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-        int32_t value = div->InputAt(1)->AsIntConstant()->GetValue();
+        int32_t value = Int32ConstantFrom(div->InputAt(1));
         if (value == 1 || value == 0 || value == -1) {
           // No temp register required.
         } else {
@@ -3397,7 +3412,7 @@
         locations->SetInAt(0, Location::RequiresRegister());
         locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
         locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-        int32_t value = rem->InputAt(1)->AsIntConstant()->GetValue();
+        int32_t value = Int32ConstantFrom(rem->InputAt(1));
         if (value == 1 || value == 0 || value == -1) {
           // No temp register required.
         } else {
@@ -3532,7 +3547,7 @@
         __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
       } else {
         DCHECK(value.IsConstant()) << value;
-        if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
+        if (Int32ConstantFrom(value) == 0) {
           __ B(slow_path->GetEntryLabel());
         }
       }
@@ -3546,7 +3561,7 @@
         __ B(eq, slow_path->GetEntryLabel());
       } else {
         DCHECK(value.IsConstant()) << value;
-        if (value.GetConstant()->AsLongConstant()->GetValue() == 0) {
+        if (Int64ConstantFrom(value) == 0) {
           __ B(slow_path->GetEntryLabel());
         }
       }
@@ -3622,7 +3637,7 @@
     __ And(shift_right, RegisterFrom(rhs), 0x1F);
     __ Lsrs(shift_left, RegisterFrom(rhs), 6);
     __ Rsb(LeaveFlags, shift_left, shift_right, Operand::From(kArmBitsPerWord));
-    __ B(cc, &shift_by_32_plus_shift_right);
+    __ B(cc, &shift_by_32_plus_shift_right, /* far_target */ false);
 
     // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
     // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
@@ -3756,7 +3771,7 @@
           __ Lsr(out_reg, first_reg, out_reg);
         }
       } else {
-        int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
+        int32_t cst = Int32ConstantFrom(second);
         uint32_t shift_value = cst & kMaxIntShiftDistance;
         if (shift_value == 0) {  // ARM does not support shifting with 0 immediate.
           __ Mov(out_reg, first_reg);
@@ -3793,9 +3808,9 @@
           // If the shift is > 32 bits, override the high part
           __ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
           {
-            AssemblerAccurateScope guard(GetVIXLAssembler(),
-                                         2 * vixl32::kMaxInstructionSizeInBytes,
-                                         CodeBufferCheckScope::kMaximumSize);
+            ExactAssemblyScope guard(GetVIXLAssembler(),
+                                     2 * vixl32::kMaxInstructionSizeInBytes,
+                                     CodeBufferCheckScope::kMaximumSize);
             __ it(pl);
             __ lsl(pl, o_h, low, temp);
           }
@@ -3812,9 +3827,9 @@
           // If the shift is > 32 bits, override the low part
           __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
           {
-            AssemblerAccurateScope guard(GetVIXLAssembler(),
-                                         2 * vixl32::kMaxInstructionSizeInBytes,
-                                         CodeBufferCheckScope::kMaximumSize);
+            ExactAssemblyScope guard(GetVIXLAssembler(),
+                                     2 * vixl32::kMaxInstructionSizeInBytes,
+                                     CodeBufferCheckScope::kMaximumSize);
             __ it(pl);
             __ asr(pl, o_l, high, temp);
           }
@@ -3829,9 +3844,9 @@
           __ Orr(o_l, o_l, temp);
           __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
           {
-            AssemblerAccurateScope guard(GetVIXLAssembler(),
-                                         2 * vixl32::kMaxInstructionSizeInBytes,
-                                         CodeBufferCheckScope::kMaximumSize);
+            ExactAssemblyScope guard(GetVIXLAssembler(),
+                                     2 * vixl32::kMaxInstructionSizeInBytes,
+                                     CodeBufferCheckScope::kMaximumSize);
           __ it(pl);
           __ lsr(pl, o_l, high, temp);
           }
@@ -3841,7 +3856,7 @@
         // Register allocator doesn't create partial overlap.
         DCHECK(!o_l.Is(high));
         DCHECK(!o_h.Is(low));
-        int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
+        int32_t cst = Int32ConstantFrom(second);
         uint32_t shift_value = cst & kMaxLongShiftDistance;
         if (shift_value > 32) {
           if (op->IsShl()) {
@@ -3948,9 +3963,9 @@
     GetAssembler()->LoadFromOffset(kLoadWord, temp, tr, QUICK_ENTRY_POINT(pNewEmptyString));
     GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, code_offset.Int32Value());
     // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
-    AssemblerAccurateScope aas(GetVIXLAssembler(),
-                               vixl32::k16BitT32InstructionSizeInBytes,
-                               CodeBufferCheckScope::kExactSize);
+    ExactAssemblyScope aas(GetVIXLAssembler(),
+                           vixl32::k16BitT32InstructionSizeInBytes,
+                           CodeBufferCheckScope::kExactSize);
     __ blx(lr);
     codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   } else {
@@ -4094,8 +4109,8 @@
     }
     case Primitive::kPrimLong: {
       __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right));  // Signed compare.
-      __ B(lt, &less);
-      __ B(gt, &greater);
+      __ B(lt, &less, /* far_target */ false);
+      __ B(gt, &greater, /* far_target */ false);
       // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
       __ Mov(out, 0);
       __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right));  // Unsigned compare.
@@ -4116,8 +4131,8 @@
       UNREACHABLE();
   }
 
-  __ B(eq, &done);
-  __ B(less_cond, &less);
+  __ B(eq, &done, /* far_target */ false);
+  __ B(less_cond, &less, /* far_target */ false);
 
   __ Bind(&greater);
   __ Mov(out, 1);
@@ -4192,9 +4207,9 @@
   __ Bind(&fail);
   {
     // Ensure the pc position is recorded immediately after the `ldrexd` instruction.
-    AssemblerAccurateScope aas(GetVIXLAssembler(),
-                               vixl32::kMaxInstructionSizeInBytes,
-                               CodeBufferCheckScope::kMaximumSize);
+    ExactAssemblyScope aas(GetVIXLAssembler(),
+                           vixl32::kMaxInstructionSizeInBytes,
+                           CodeBufferCheckScope::kMaximumSize);
     // 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, MemOperand(addr));
@@ -4411,7 +4426,7 @@
     locations->AddTemp(Location::RequiresRegister());
   } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
     // We need a temporary register for the read barrier marking slow
-    // path in CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier.
+    // path in CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier.
     locations->AddTemp(Location::RequiresRegister());
   }
 }
@@ -4715,9 +4730,9 @@
 
   UseScratchRegisterScope temps(GetVIXLAssembler());
   // Ensure the pc position is recorded immediately after the `ldr` instruction.
-  AssemblerAccurateScope aas(GetVIXLAssembler(),
-                             vixl32::kMaxInstructionSizeInBytes,
-                             CodeBufferCheckScope::kMaximumSize);
+  ExactAssemblyScope aas(GetVIXLAssembler(),
+                         vixl32::kMaxInstructionSizeInBytes,
+                         CodeBufferCheckScope::kMaximumSize);
   __ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0)));
   RecordPcInfo(instruction, instruction->GetDexPc());
 }
@@ -4873,7 +4888,7 @@
         object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
   }
   // We need a temporary register for the read barrier marking slow
-  // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier.
+  // path in CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier.
   // Also need for String compression feature.
   if ((object_array_get_with_read_barrier && kUseBakerReadBarrier)
       || (mirror::kUseStringCompression && instruction->IsStringCharAt())) {
@@ -4908,13 +4923,13 @@
         codegen_->MaybeRecordImplicitNullCheck(instruction);
       }
       if (index.IsConstant()) {
-        int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
+        int32_t const_index = Int32ConstantFrom(index);
         if (maybe_compressed_char_at) {
           vixl32::Label uncompressed_load, done;
           __ Lsrs(length, length, 1u);  // LSRS has a 16-bit encoding, TST (immediate) does not.
           static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
                         "Expecting 0=compressed, 1=uncompressed");
-          __ B(cs, &uncompressed_load);
+          __ B(cs, &uncompressed_load, /* far_target */ false);
           GetAssembler()->LoadFromOffset(kLoadUnsignedByte,
                                          RegisterFrom(out_loc),
                                          obj,
@@ -4942,7 +4957,7 @@
           // `TryExtractArrayAccessAddress()`.
           if (kIsDebugBuild) {
             HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
-            DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
+            DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
           }
           temp = obj;
         } else {
@@ -4953,7 +4968,7 @@
           __ Lsrs(length, length, 1u);  // LSRS has a 16-bit encoding, TST (immediate) does not.
           static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
                         "Expecting 0=compressed, 1=uncompressed");
-          __ B(cs, &uncompressed_load);
+          __ B(cs, &uncompressed_load, /* far_target */ false);
           __ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0));
           __ B(&done);
           __ Bind(&uncompressed_load);
@@ -4987,7 +5002,7 @@
         vixl32::Register out = OutputRegister(instruction);
         if (index.IsConstant()) {
           size_t offset =
-              (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+              (Int32ConstantFrom(index) << TIMES_4) + data_offset;
           GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
           // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method,
           // we should use a scope and the assembler to emit the load instruction to guarantee that
@@ -5009,7 +5024,7 @@
             // `TryExtractArrayAccessAddress()`.
             if (kIsDebugBuild) {
               HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
-              DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
+              DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
             }
             temp = obj;
           } else {
@@ -5034,7 +5049,7 @@
     case Primitive::kPrimLong: {
       if (index.IsConstant()) {
         size_t offset =
-            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+            (Int32ConstantFrom(index) << TIMES_8) + data_offset;
         GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset);
       } else {
         UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5048,7 +5063,7 @@
     case Primitive::kPrimFloat: {
       vixl32::SRegister out = SRegisterFrom(out_loc);
       if (index.IsConstant()) {
-        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+        size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
         GetAssembler()->LoadSFromOffset(out, obj, offset);
       } else {
         UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5061,7 +5076,7 @@
 
     case Primitive::kPrimDouble: {
       if (index.IsConstant()) {
-        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+        size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
         GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset);
       } else {
         UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5135,7 +5150,7 @@
     case Primitive::kPrimChar:
     case Primitive::kPrimInt: {
       if (index.IsConstant()) {
-        int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
+        int32_t const_index = Int32ConstantFrom(index);
         uint32_t full_offset =
             data_offset + (const_index << Primitive::ComponentSizeShift(value_type));
         StoreOperandType store_type = GetStoreOperandType(value_type);
@@ -5150,7 +5165,7 @@
           // `TryExtractArrayAccessAddress()`.
           if (kIsDebugBuild) {
             HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
-            DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == data_offset);
+            DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
           }
           temp = array;
         } else {
@@ -5171,7 +5186,7 @@
         // Just setting null.
         if (index.IsConstant()) {
           size_t offset =
-              (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+              (Int32ConstantFrom(index) << TIMES_4) + data_offset;
           GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
         } else {
           DCHECK(index.IsRegister()) << index;
@@ -5207,7 +5222,7 @@
           __ CompareAndBranchIfNonZero(value, &non_zero);
           if (index.IsConstant()) {
             size_t offset =
-               (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+               (Int32ConstantFrom(index) << TIMES_4) + data_offset;
             GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
           } else {
             DCHECK(index.IsRegister()) << index;
@@ -5233,9 +5248,9 @@
 
         {
           // Ensure we record the pc position immediately after the `ldr` instruction.
-          AssemblerAccurateScope aas(GetVIXLAssembler(),
-                                     vixl32::kMaxInstructionSizeInBytes,
-                                     CodeBufferCheckScope::kMaximumSize);
+          ExactAssemblyScope aas(GetVIXLAssembler(),
+                                 vixl32::kMaxInstructionSizeInBytes,
+                                 CodeBufferCheckScope::kMaximumSize);
           // /* HeapReference<Class> */ temp1 = array->klass_
           __ ldr(temp1, MemOperand(array, class_offset));
           codegen_->MaybeRecordImplicitNullCheck(instruction);
@@ -5252,7 +5267,7 @@
 
         if (instruction->StaticTypeOfArrayIsObjectArray()) {
           vixl32::Label do_put;
-          __ B(eq, &do_put);
+          __ B(eq, &do_put, /* far_target */ false);
           // If heap poisoning is enabled, the `temp1` reference has
           // not been unpoisoned yet; unpoison it now.
           GetAssembler()->MaybeUnpoisonHeapReference(temp1);
@@ -5281,7 +5296,7 @@
 
       if (index.IsConstant()) {
         size_t offset =
-            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+            (Int32ConstantFrom(index) << TIMES_4) + data_offset;
         GetAssembler()->StoreToOffset(kStoreWord, source, array, offset);
       } else {
         DCHECK(index.IsRegister()) << index;
@@ -5318,7 +5333,7 @@
       Location value = locations->InAt(2);
       if (index.IsConstant()) {
         size_t offset =
-            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+            (Int32ConstantFrom(index) << TIMES_8) + data_offset;
         GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset);
       } else {
         UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5333,7 +5348,7 @@
       Location value = locations->InAt(2);
       DCHECK(value.IsFpuRegister());
       if (index.IsConstant()) {
-        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+        size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
         GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset);
       } else {
         UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5348,7 +5363,7 @@
       Location value = locations->InAt(2);
       DCHECK(value.IsFpuRegisterPair());
       if (index.IsConstant()) {
-        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+        size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
         GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset);
       } else {
         UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5384,9 +5399,9 @@
   vixl32::Register obj = InputRegisterAt(instruction, 0);
   vixl32::Register out = OutputRegister(instruction);
   {
-    AssemblerAccurateScope aas(GetVIXLAssembler(),
-                               vixl32::kMaxInstructionSizeInBytes,
-                               CodeBufferCheckScope::kMaximumSize);
+    ExactAssemblyScope aas(GetVIXLAssembler(),
+                           vixl32::kMaxInstructionSizeInBytes,
+                           CodeBufferCheckScope::kMaximumSize);
     __ ldr(out, MemOperand(obj, offset));
     codegen_->MaybeRecordImplicitNullCheck(instruction);
   }
@@ -5413,7 +5428,7 @@
   if (second.IsRegister()) {
     __ Add(out, first, RegisterFrom(second));
   } else {
-    __ Add(out, first, second.GetConstant()->AsIntConstant()->GetValue());
+    __ Add(out, first, Int32ConstantFrom(second));
   }
 }
 
@@ -5609,7 +5624,7 @@
         GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
       }
     } else if (constant->IsLongConstant()) {
-      int64_t value = constant->AsLongConstant()->GetValue();
+      int64_t value = Int64ConstantFrom(source);
       if (destination.IsRegisterPair()) {
         __ Mov(LowRegisterFrom(destination), Low32Bits(value));
         __ Mov(HighRegisterFrom(destination), High32Bits(value));
@@ -5768,17 +5783,15 @@
     case HLoadClass::LoadKind::kReferrersClass:
       break;
     case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
-      // TODO(VIXL): Enable it back when literal pools are fixed in VIXL.
-      return HLoadClass::LoadKind::kDexCacheViaMethod;
+      DCHECK(!GetCompilerOptions().GetCompilePic());
+      break;
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
       DCHECK(GetCompilerOptions().GetCompilePic());
       break;
     case HLoadClass::LoadKind::kBootImageAddress:
-      // TODO(VIXL): Enable it back when literal pools are fixed in VIXL.
-      return HLoadClass::LoadKind::kDexCacheViaMethod;
+      break;
     case HLoadClass::LoadKind::kJitTableAddress:
-      // TODO(VIXL): Enable it back when literal pools are fixed in VIXL.
-      return HLoadClass::LoadKind::kDexCacheViaMethod;
+      break;
     case HLoadClass::LoadKind::kDexCachePcRelative:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       // We disable pc-relative load when there is an irreducible loop, as the optimization
@@ -5854,7 +5867,9 @@
       break;
     }
     case HLoadClass::LoadKind::kBootImageLinkTimeAddress: {
-      TODO_VIXL32(FATAL);
+      DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
+      __ Ldr(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(),
+                                                            cls->GetTypeIndex()));
       break;
     }
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
@@ -5865,11 +5880,18 @@
       break;
     }
     case HLoadClass::LoadKind::kBootImageAddress: {
-      TODO_VIXL32(FATAL);
+      DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
+      DCHECK_NE(cls->GetAddress(), 0u);
+      uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+      __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
       break;
     }
     case HLoadClass::LoadKind::kJitTableAddress: {
-      TODO_VIXL32(FATAL);
+      __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
+                                                       cls->GetTypeIndex(),
+                                                       cls->GetAddress()));
+      // /* GcRoot<mirror::Class> */ out = *out
+      GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
       break;
     }
     case HLoadClass::LoadKind::kDexCachePcRelative: {
@@ -5954,21 +5976,19 @@
     HLoadString::LoadKind desired_string_load_kind) {
   switch (desired_string_load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimeAddress:
-      // TODO(VIXL): Implement missing optimization.
-      return HLoadString::LoadKind::kDexCacheViaMethod;
+      DCHECK(!GetCompilerOptions().GetCompilePic());
+      break;
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
       DCHECK(GetCompilerOptions().GetCompilePic());
       break;
     case HLoadString::LoadKind::kBootImageAddress:
-      // TODO(VIXL): Implement missing optimization.
-      return HLoadString::LoadKind::kDexCacheViaMethod;
+      break;
     case HLoadString::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
     case HLoadString::LoadKind::kJitTableAddress:
       DCHECK(Runtime::Current()->UseJitCompilation());
-      // TODO(VIXL): Implement missing optimization.
-      return HLoadString::LoadKind::kDexCacheViaMethod;
+      break;
     case HLoadString::LoadKind::kDexCacheViaMethod:
       break;
   }
@@ -6010,8 +6030,9 @@
 
   switch (load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimeAddress: {
-      TODO_VIXL32(FATAL);
-      break;
+      __ Ldr(out, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
+                                                              load->GetStringIndex()));
+      return;  // No dex cache slow path.
     }
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
       DCHECK(codegen_->GetCompilerOptions().IsBootImage());
@@ -6021,8 +6042,10 @@
       return;  // No dex cache slow path.
     }
     case HLoadString::LoadKind::kBootImageAddress: {
-      TODO_VIXL32(FATAL);
-      break;
+      DCHECK_NE(load->GetAddress(), 0u);
+      uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+      __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
+      return;  // No dex cache slow path.
     }
     case HLoadString::LoadKind::kBssEntry: {
       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
@@ -6039,8 +6062,11 @@
       return;
     }
     case HLoadString::LoadKind::kJitTableAddress: {
-      TODO_VIXL32(FATAL);
-      break;
+      __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
+                                                        load->GetStringIndex()));
+      // /* GcRoot<mirror::String> */ out = *out
+      GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
+      return;
     }
     default:
       break;
@@ -6182,7 +6208,7 @@
                                         kCompilerReadBarrierOption);
       __ Cmp(out, cls);
       // Classes must be equal for the instanceof to succeed.
-      __ B(ne, &zero);
+      __ B(ne, &zero, /* far_target */ false);
       __ Mov(out, 1);
       __ B(&done);
       break;
@@ -6209,7 +6235,7 @@
       // If `out` is null, we use it for the result, and jump to `done`.
       __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
       __ Cmp(out, cls);
-      __ B(ne, &loop);
+      __ B(ne, &loop, /* far_target */ false);
       __ Mov(out, 1);
       if (zero.IsReferenced()) {
         __ B(&done);
@@ -6229,7 +6255,7 @@
       vixl32::Label loop, success;
       __ Bind(&loop);
       __ Cmp(out, cls);
-      __ B(eq, &success);
+      __ B(eq, &success, /* far_target */ false);
       // /* HeapReference<Class> */ out = out->super_class_
       GenerateReferenceLoadOneRegister(instruction,
                                        out_loc,
@@ -6258,7 +6284,7 @@
       // Do an exact check.
       vixl32::Label exact_check;
       __ Cmp(out, cls);
-      __ B(eq, &exact_check);
+      __ B(eq, &exact_check, /* far_target */ false);
       // Otherwise, we need to check that the object's class is a non-primitive array.
       // /* HeapReference<Class> */ out = out->component_type_
       GenerateReferenceLoadOneRegister(instruction,
@@ -6460,7 +6486,7 @@
 
       // Otherwise, compare the classes.
       __ Cmp(temp, cls);
-      __ B(ne, &loop);
+      __ B(ne, &loop, /* far_target */ false);
       break;
     }
 
@@ -6477,7 +6503,7 @@
       vixl32::Label loop;
       __ Bind(&loop);
       __ Cmp(temp, cls);
-      __ B(eq, &done);
+      __ B(eq, &done, /* far_target */ false);
 
       // /* HeapReference<Class> */ temp = temp->super_class_
       GenerateReferenceLoadOneRegister(instruction,
@@ -6505,7 +6531,7 @@
 
       // Do an exact check.
       __ Cmp(temp, cls);
-      __ B(eq, &done);
+      __ B(eq, &done, /* far_target */ false);
 
       // Otherwise, we need to check that the object's class is a non-primitive array.
       // /* HeapReference<Class> */ temp = temp->component_type_
@@ -6569,7 +6595,7 @@
       __ Sub(RegisterFrom(maybe_temp2_loc), RegisterFrom(maybe_temp2_loc), 2);
       // Compare the classes and continue the loop if they do not match.
       __ Cmp(cls, RegisterFrom(maybe_temp3_loc));
-      __ B(ne, &start_loop);
+      __ B(ne, &start_loop, /* far_target */ false);
       break;
     }
   }
@@ -7202,20 +7228,7 @@
 // otherwise return a fall-back info that should be used instead.
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
     const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-    HInvokeStaticOrDirect* invoke) {
-  // TODO(VIXL): Implement optimized code paths.
-  if (desired_dispatch_info.method_load_kind ==
-          HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup ||
-      desired_dispatch_info.code_ptr_location ==
-          HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup) {
-    return {
-      HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
-      HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
-      0u,
-      0u
-    };
-  }
-
+    HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
   HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
   // We disable pc-relative load when there is an irreducible loop, as the optimization
   // is incompatible with it.
@@ -7227,24 +7240,6 @@
     dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
   }
 
-  if (dispatch_info.code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) {
-    const DexFile& outer_dex_file = GetGraph()->GetDexFile();
-    if (&outer_dex_file != invoke->GetTargetMethod().dex_file) {
-      // Calls across dex files are more likely to exceed the available BL range,
-      // so use absolute patch with fixup if available and kCallArtMethod otherwise.
-      HInvokeStaticOrDirect::CodePtrLocation code_ptr_location =
-          (desired_dispatch_info.method_load_kind ==
-           HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup)
-          ? HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup
-          : HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
-      return HInvokeStaticOrDirect::DispatchInfo {
-        dispatch_info.method_load_kind,
-        code_ptr_location,
-        dispatch_info.method_load_data,
-        0u
-      };
-    }
-  }
   return dispatch_info;
 }
 
@@ -7276,20 +7271,6 @@
 
 void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(
     HInvokeStaticOrDirect* invoke, Location temp) {
-  // For better instruction scheduling we load the direct code pointer before the method pointer.
-  switch (invoke->GetCodePtrLocation()) {
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
-      // LR = code address from literal pool with link-time patch.
-      TODO_VIXL32(FATAL);
-      break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
-      // LR = invoke->GetDirectCodePtr();
-      __ Mov(lr, Operand::From(invoke->GetDirectCodePtr()));
-      break;
-    default:
-      break;
-  }
-
   Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
   switch (invoke->GetMethodLoadKind()) {
     case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
@@ -7305,9 +7286,6 @@
     case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
       __ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress()));
       break;
-    case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
-      TODO_VIXL32(FATAL);
-      break;
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
       HArmDexCacheArraysBase* base =
           invoke->InputAt(invoke->GetSpecialInputIndex())->AsArmDexCacheArraysBase();
@@ -7347,30 +7325,6 @@
     case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
       __ Bl(GetFrameEntryLabel());
       break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
-      relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
-                                          invoke->GetTargetMethod().dex_method_index);
-      {
-        AssemblerAccurateScope aas(GetVIXLAssembler(),
-                                   vixl32::kMaxInstructionSizeInBytes,
-                                   CodeBufferCheckScope::kMaximumSize);
-        __ bind(&relative_call_patches_.back().label);
-        // Arbitrarily branch to the BL itself, override at link time.
-        __ bl(&relative_call_patches_.back().label);
-      }
-      break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
-      // LR prepared above for better instruction scheduling.
-      // LR()
-      {
-        // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
-        AssemblerAccurateScope aas(GetVIXLAssembler(),
-                                   vixl32::k16BitT32InstructionSizeInBytes,
-                                   CodeBufferCheckScope::kExactSize);
-        __ blx(lr);
-      }
-      break;
     case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
       // LR = callee_method->entry_point_from_quick_compiled_code_
       GetAssembler()->LoadFromOffset(
@@ -7380,9 +7334,9 @@
             ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
       {
         // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
-        AssemblerAccurateScope aas(GetVIXLAssembler(),
-                                   vixl32::k16BitT32InstructionSizeInBytes,
-                                   CodeBufferCheckScope::kExactSize);
+        ExactAssemblyScope aas(GetVIXLAssembler(),
+                               vixl32::k16BitT32InstructionSizeInBytes,
+                               CodeBufferCheckScope::kExactSize);
         // LR()
         __ blx(lr);
       }
@@ -7406,9 +7360,9 @@
   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
   {
     // Make sure the pc is recorded immediately after the `ldr` instruction.
-    AssemblerAccurateScope aas(GetVIXLAssembler(),
-                               vixl32::kMaxInstructionSizeInBytes,
-                               CodeBufferCheckScope::kMaximumSize);
+    ExactAssemblyScope aas(GetVIXLAssembler(),
+                           vixl32::kMaxInstructionSizeInBytes,
+                           CodeBufferCheckScope::kMaximumSize);
     // /* HeapReference<Class> */ temp = receiver->klass_
     __ ldr(temp, MemOperand(receiver, class_offset));
     MaybeRecordImplicitNullCheck(invoke);
@@ -7433,9 +7387,9 @@
   // `RecordPcInfo()` immediately following record the correct pc. Use a scope to help guarantee
   // that.
   // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
-  AssemblerAccurateScope aas(GetVIXLAssembler(),
-                             vixl32::k16BitT32InstructionSizeInBytes,
-                             CodeBufferCheckScope::kExactSize);
+  ExactAssemblyScope aas(GetVIXLAssembler(),
+                         vixl32::k16BitT32InstructionSizeInBytes,
+                         CodeBufferCheckScope::kExactSize);
   __ blx(lr);
 }
 
@@ -7460,6 +7414,57 @@
   return &patches->back();
 }
 
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageStringLiteral(
+    const DexFile& dex_file,
+    dex::StringIndex string_index) {
+  return boot_image_string_patches_.GetOrCreate(
+      StringReference(&dex_file, string_index),
+      [this]() {
+        return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
+      });
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageTypeLiteral(
+    const DexFile& dex_file,
+    dex::TypeIndex type_index) {
+  return boot_image_type_patches_.GetOrCreate(
+      TypeReference(&dex_file, type_index),
+      [this]() {
+        return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
+      });
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageAddressLiteral(uint32_t address) {
+  bool needs_patch = GetCompilerOptions().GetIncludePatchInformation();
+  Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_;
+  return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map);
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateDexCacheAddressLiteral(uint32_t address) {
+  return DeduplicateUint32Literal(address, &uint32_literals_);
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral(const DexFile& dex_file,
+                                                       dex::StringIndex string_index) {
+  jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u);
+  return jit_string_patches_.GetOrCreate(
+      StringReference(&dex_file, string_index),
+      [this]() {
+        return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
+      });
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFile& dex_file,
+                                                      dex::TypeIndex type_index,
+                                                      uint64_t address) {
+  jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), address);
+  return jit_class_patches_.GetOrCreate(
+      TypeReference(&dex_file, type_index),
+      [this]() {
+        return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
+      });
+}
+
 template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
 inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches(
     const ArenaDeque<PcRelativePatchInfo>& infos,
@@ -7483,18 +7488,24 @@
 void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   size_t size =
-      relative_call_patches_.size() +
       /* MOVW+MOVT for each entry */ 2u * pc_relative_dex_cache_patches_.size() +
+      boot_image_string_patches_.size() +
       /* MOVW+MOVT for each entry */ 2u * pc_relative_string_patches_.size() +
-      /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size();
+      boot_image_type_patches_.size() +
+      /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size() +
+      boot_image_address_patches_.size();
   linker_patches->reserve(size);
-  for (const PatchInfo<vixl32::Label>& info : relative_call_patches_) {
-    uint32_t literal_offset = info.label.GetLocation();
-    linker_patches->push_back(
-        LinkerPatch::RelativeCodePatch(literal_offset, &info.dex_file, info.index));
-  }
   EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
                                                                linker_patches);
+  for (const auto& entry : boot_image_string_patches_) {
+    const StringReference& target_string = entry.first;
+    VIXLUInt32Literal* literal = entry.second;
+    DCHECK(literal->IsBound());
+    uint32_t literal_offset = literal->GetLocation();
+    linker_patches->push_back(LinkerPatch::StringPatch(literal_offset,
+                                                       target_string.dex_file,
+                                                       target_string.string_index.index_));
+  }
   if (!GetCompilerOptions().IsBootImage()) {
     EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_,
                                                                   linker_patches);
@@ -7502,8 +7513,44 @@
     EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_,
                                                                   linker_patches);
   }
+  for (const auto& entry : boot_image_type_patches_) {
+    const TypeReference& target_type = entry.first;
+    VIXLUInt32Literal* literal = entry.second;
+    DCHECK(literal->IsBound());
+    uint32_t literal_offset = literal->GetLocation();
+    linker_patches->push_back(LinkerPatch::TypePatch(literal_offset,
+                                                     target_type.dex_file,
+                                                     target_type.type_index.index_));
+  }
   EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_,
                                                               linker_patches);
+  for (const auto& entry : boot_image_address_patches_) {
+    DCHECK(GetCompilerOptions().GetIncludePatchInformation());
+    VIXLUInt32Literal* literal = entry.second;
+    DCHECK(literal->IsBound());
+    uint32_t literal_offset = literal->GetLocation();
+    linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset));
+  }
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal(
+    uint32_t value,
+    Uint32ToLiteralMap* map) {
+  return map->GetOrCreate(
+      value,
+      [this, value]() {
+        return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ value);
+      });
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateMethodLiteral(
+    MethodReference target_method,
+    MethodToLiteralMap* map) {
+  return map->GetOrCreate(
+      target_method,
+      [this]() {
+        return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
+      });
 }
 
 void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
@@ -7699,12 +7746,37 @@
   }
 }
 
+static void PatchJitRootUse(uint8_t* code,
+                            const uint8_t* roots_data,
+                            VIXLUInt32Literal* literal,
+                            uint64_t index_in_table) {
+  DCHECK(literal->IsBound());
+  uint32_t literal_offset = literal->GetLocation();
+  uintptr_t address =
+      reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
+  uint8_t* data = code + literal_offset;
+  reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address);
+}
+
+void CodeGeneratorARMVIXL::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
+  for (const auto& entry : jit_string_patches_) {
+    const auto& it = jit_string_roots_.find(entry.first);
+    DCHECK(it != jit_string_roots_.end());
+    PatchJitRootUse(code, roots_data, entry.second, it->second);
+  }
+  for (const auto& entry : jit_class_patches_) {
+    const auto& it = jit_class_roots_.find(entry.first);
+    DCHECK(it != jit_class_roots_.end());
+    PatchJitRootUse(code, roots_data, entry.second, it->second);
+  }
+}
+
 void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder(
     CodeGeneratorARMVIXL::PcRelativePatchInfo* labels,
     vixl32::Register out) {
-  AssemblerAccurateScope aas(GetVIXLAssembler(),
-                             3 * vixl32::kMaxInstructionSizeInBytes,
-                             CodeBufferCheckScope::kMaximumSize);
+  ExactAssemblyScope aas(GetVIXLAssembler(),
+                         3 * vixl32::kMaxInstructionSizeInBytes,
+                         CodeBufferCheckScope::kMaximumSize);
   // TODO(VIXL): Think about using mov instead of movw.
   __ bind(&labels->movw_label);
   __ movw(out, /* placeholder */ 0u);
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 5ec3da4..297d63c 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -107,20 +107,20 @@
     arraysize(kRuntimeParameterFpuRegistersVIXL);
 
 class LoadClassSlowPathARMVIXL;
-
 class CodeGeneratorARMVIXL;
 
+using VIXLInt32Literal = vixl::aarch32::Literal<int32_t>;
+using VIXLUInt32Literal = vixl::aarch32::Literal<uint32_t>;
+
 class JumpTableARMVIXL : public DeletableArenaObject<kArenaAllocSwitchTable> {
  public:
-  typedef vixl::aarch32::Literal<int32_t> IntLiteral;
-
   explicit JumpTableARMVIXL(HPackedSwitch* switch_instr)
       : switch_instr_(switch_instr),
         table_start_(),
         bb_addresses_(switch_instr->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
     uint32_t num_entries = switch_instr_->GetNumEntries();
     for (uint32_t i = 0; i < num_entries; i++) {
-      IntLiteral *lit = new IntLiteral(0);
+      VIXLInt32Literal *lit = new VIXLInt32Literal(0, vixl32::RawLiteral::kManuallyPlaced);
       bb_addresses_.emplace_back(lit);
     }
   }
@@ -133,7 +133,7 @@
  private:
   HPackedSwitch* const switch_instr_;
   vixl::aarch32::Label table_start_;
-  ArenaVector<std::unique_ptr<IntLiteral>> bb_addresses_;
+  ArenaVector<std::unique_ptr<VIXLInt32Literal>> bb_addresses_;
 
   DISALLOW_COPY_AND_ASSIGN(JumpTableARMVIXL);
 };
@@ -566,8 +566,22 @@
   PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index);
   PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
                                                        uint32_t element_offset);
+  VIXLUInt32Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file,
+                                                       dex::StringIndex string_index);
+  VIXLUInt32Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file,
+                                                     dex::TypeIndex type_index);
+  VIXLUInt32Literal* DeduplicateBootImageAddressLiteral(uint32_t address);
+  VIXLUInt32Literal* DeduplicateDexCacheAddressLiteral(uint32_t address);
+  VIXLUInt32Literal* DeduplicateJitStringLiteral(const DexFile& dex_file,
+                                                 dex::StringIndex string_index);
+  VIXLUInt32Literal* DeduplicateJitClassLiteral(const DexFile& dex_file,
+                                                dex::TypeIndex type_index,
+                                                uint64_t address);
+
   void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
 
+  void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE;
+
   // Fast path implementation of ReadBarrier::Barrier for a heap
   // reference field load when Baker's read barriers are used.
   void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -673,10 +687,19 @@
   vixl::aarch32::Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke,
                                                                 vixl::aarch32::Register temp);
 
-  using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, vixl::aarch32::Literal<uint32_t>*>;
+  using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, VIXLUInt32Literal*>;
   using MethodToLiteralMap =
-      ArenaSafeMap<MethodReference, vixl::aarch32::Literal<uint32_t>*, MethodReferenceComparator>;
+      ArenaSafeMap<MethodReference, VIXLUInt32Literal*, MethodReferenceComparator>;
+  using StringToLiteralMap = ArenaSafeMap<StringReference,
+                                          VIXLUInt32Literal*,
+                                          StringReferenceValueComparator>;
+  using TypeToLiteralMap = ArenaSafeMap<TypeReference,
+                                        VIXLUInt32Literal*,
+                                        TypeReferenceValueComparator>;
 
+  VIXLUInt32Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
+  VIXLUInt32Literal* DeduplicateMethodLiteral(MethodReference target_method,
+                                              MethodToLiteralMap* map);
   PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file,
                                           uint32_t offset_or_index,
                                           ArenaDeque<PcRelativePatchInfo>* patches);
@@ -697,15 +720,25 @@
   ArmVIXLAssembler assembler_;
   const ArmInstructionSetFeatures& isa_features_;
 
-  // Relative call patch info.
-  // Using ArenaDeque<> which retains element addresses on push/emplace_back().
-  ArenaDeque<PatchInfo<vixl::aarch32::Label>> relative_call_patches_;
+  // Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
+  Uint32ToLiteralMap uint32_literals_;
   // PC-relative patch info for each HArmDexCacheArraysBase.
   ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
+  // Deduplication map for boot string literals for kBootImageLinkTimeAddress.
+  StringToLiteralMap boot_image_string_patches_;
   // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC).
   ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
+  // Deduplication map for boot type literals for kBootImageLinkTimeAddress.
+  TypeToLiteralMap boot_image_type_patches_;
   // PC-relative type patch info.
   ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_;
+  // Deduplication map for patchable boot image addresses.
+  Uint32ToLiteralMap boot_image_address_patches_;
+
+  // Patches for string literals in JIT compiled code.
+  StringToLiteralMap jit_string_patches_;
+  // Patches for class literals in JIT compiled code.
+  TypeToLiteralMap jit_class_patches_;
 
   DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARMVIXL);
 };
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index cae4161..01e0dac 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -458,10 +458,6 @@
       isa_features_(isa_features),
       uint32_literals_(std::less<uint32_t>(),
                        graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      method_patches_(MethodReferenceComparator(),
-                      graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      call_patches_(MethodReferenceComparator(),
-                    graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       boot_image_string_patches_(StringReferenceValueComparator(),
                                  graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
@@ -757,6 +753,11 @@
   if (RequiresCurrentMethod()) {
     __ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, kCurrentMethodStackOffset);
   }
+
+  if (GetGraph()->HasShouldDeoptimizeFlag()) {
+    // Initialize should deoptimize flag to 0.
+    __ StoreToOffset(kStoreWord, ZERO, SP, GetStackOffsetOfShouldDeoptimizeFlag());
+  }
 }
 
 void CodeGeneratorMIPS::GenerateFrameExit() {
@@ -1003,8 +1004,6 @@
 void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   size_t size =
-      method_patches_.size() +
-      call_patches_.size() +
       pc_relative_dex_cache_patches_.size() +
       pc_relative_string_patches_.size() +
       pc_relative_type_patches_.size() +
@@ -1012,24 +1011,6 @@
       boot_image_type_patches_.size() +
       boot_image_address_patches_.size();
   linker_patches->reserve(size);
-  for (const auto& entry : method_patches_) {
-    const MethodReference& target_method = entry.first;
-    Literal* literal = entry.second;
-    DCHECK(literal->GetLabel()->IsBound());
-    uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel());
-    linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset,
-                                                       target_method.dex_file,
-                                                       target_method.dex_method_index));
-  }
-  for (const auto& entry : call_patches_) {
-    const MethodReference& target_method = entry.first;
-    Literal* literal = entry.second;
-    DCHECK(literal->GetLabel()->IsBound());
-    uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel());
-    linker_patches->push_back(LinkerPatch::CodePatch(literal_offset,
-                                                     target_method.dex_file,
-                                                     target_method.dex_method_index));
-  }
   EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
                                                                linker_patches);
   if (!GetCompilerOptions().IsBootImage()) {
@@ -1102,14 +1083,6 @@
       [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
 }
 
-Literal* CodeGeneratorMIPS::DeduplicateMethodAddressLiteral(MethodReference target_method) {
-  return DeduplicateMethodLiteral(target_method, &method_patches_);
-}
-
-Literal* CodeGeneratorMIPS::DeduplicateMethodCodeLiteral(MethodReference target_method) {
-  return DeduplicateMethodLiteral(target_method, &call_patches_);
-}
-
 Literal* CodeGeneratorMIPS::DeduplicateBootImageStringLiteral(const DexFile& dex_file,
                                                               dex::StringIndex string_index) {
   return boot_image_string_patches_.GetOrCreate(
@@ -1160,11 +1133,15 @@
   __ SetReorder(reordering);
 }
 
-void CodeGeneratorMIPS::MarkGCCard(Register object, Register value) {
+void CodeGeneratorMIPS::MarkGCCard(Register object,
+                                   Register value,
+                                   bool value_can_be_null) {
   MipsLabel done;
   Register card = AT;
   Register temp = TMP;
-  __ Beqz(value, &done);
+  if (value_can_be_null) {
+    __ Beqz(value, &done);
+  }
   __ LoadFromOffset(kLoadWord,
                     card,
                     TR,
@@ -1172,7 +1149,9 @@
   __ Srl(temp, object, gc::accounting::CardTable::kCardShift);
   __ Addu(temp, card, temp);
   __ Sb(card, temp, 0);
-  __ Bind(&done);
+  if (value_can_be_null) {
+    __ Bind(&done);
+  }
 }
 
 void CodeGeneratorMIPS::SetupBlockedRegisters() const {
@@ -2091,7 +2070,7 @@
           __ StoreToOffset(kStoreWord, value, base_reg, data_offset, null_checker);
           if (needs_write_barrier) {
             DCHECK_EQ(value_type, Primitive::kPrimNot);
-            codegen_->MarkGCCard(obj, value);
+            codegen_->MarkGCCard(obj, value, instruction->GetValueCanBeNull());
           }
         }
       } else {
@@ -4689,14 +4668,17 @@
   }
 }
 
-void LocationsBuilderMIPS::VisitShouldDeoptimizeFlag(
-    HShouldDeoptimizeFlag* flag ATTRIBUTE_UNUSED) {
-  // TODO: to be implemented.
+void LocationsBuilderMIPS::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
+  LocationSummary* locations = new (GetGraph()->GetArena())
+      LocationSummary(flag, LocationSummary::kNoCall);
+  locations->SetOut(Location::RequiresRegister());
 }
 
-void InstructionCodeGeneratorMIPS::VisitShouldDeoptimizeFlag(
-    HShouldDeoptimizeFlag* flag ATTRIBUTE_UNUSED) {
-  // TODO: to be implemented.
+void InstructionCodeGeneratorMIPS::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
+  __ LoadFromOffset(kLoadWord,
+                    flag->GetLocations()->Out().AsRegister<Register>(),
+                    SP,
+                    codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
 }
 
 void LocationsBuilderMIPS::VisitSelect(HSelect* select) {
@@ -4892,7 +4874,8 @@
 
 void InstructionCodeGeneratorMIPS::HandleFieldSet(HInstruction* instruction,
                                                   const FieldInfo& field_info,
-                                                  uint32_t dex_pc) {
+                                                  uint32_t dex_pc,
+                                                  bool value_can_be_null) {
   Primitive::Type type = field_info.GetFieldType();
   LocationSummary* locations = instruction->GetLocations();
   Register obj = locations->InAt(0).AsRegister<Register>();
@@ -4987,7 +4970,7 @@
   // TODO: memory barriers?
   if (CodeGenerator::StoreNeedsWriteBarrier(type, instruction->InputAt(1))) {
     Register src = value_location.AsRegister<Register>();
-    codegen_->MarkGCCard(obj, src);
+    codegen_->MarkGCCard(obj, src, value_can_be_null);
   }
 
   if (is_volatile) {
@@ -5008,7 +4991,10 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
-  HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetDexPc());
+  HandleFieldSet(instruction,
+                 instruction->GetFieldInfo(),
+                 instruction->GetDexPc(),
+                 instruction->GetValueCanBeNull());
 }
 
 void InstructionCodeGeneratorMIPS::GenerateGcRootFieldLoad(
@@ -5149,22 +5135,7 @@
   // art::PrepareForRegisterAllocation.
   DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
 
-  HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind();
-  HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation();
-  bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
-
-  // kDirectAddressWithFixup and kCallDirectWithFixup need no extra input on R6 because
-  // R6 has PC-relative addressing.
-  bool has_extra_input = !isR6 &&
-      ((method_load_kind == HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) ||
-       (code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup));
-
-  if (invoke->HasPcRelativeDexCache()) {
-    // kDexCachePcRelative is mutually exclusive with
-    // kDirectAddressWithFixup/kCallDirectWithFixup.
-    CHECK(!has_extra_input);
-    has_extra_input = true;
-  }
+  bool has_extra_input = invoke->HasPcRelativeDexCache();
 
   IntrinsicLocationsBuilderMIPS intrinsic(codegen_);
   if (intrinsic.TryDispatch(invoke)) {
@@ -5304,9 +5275,7 @@
   // is incompatible with it.
   bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops();
   bool fallback_load = true;
-  bool fallback_call = true;
   switch (dispatch_info.method_load_kind) {
-    case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
       fallback_load = has_irreducible_loops;
       break;
@@ -5314,25 +5283,10 @@
       fallback_load = false;
       break;
   }
-  switch (dispatch_info.code_ptr_location) {
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
-      fallback_call = has_irreducible_loops;
-      break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
-      // TODO: Implement this type.
-      break;
-    default:
-      fallback_call = false;
-      break;
-  }
   if (fallback_load) {
     dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
     dispatch_info.method_load_data = 0;
   }
-  if (fallback_call) {
-    dispatch_info.code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
-    dispatch_info.direct_code_ptr = 0;
-  }
   return dispatch_info;
 }
 
@@ -5341,31 +5295,10 @@
   Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
   HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind();
   HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation();
-  bool isR6 = isa_features_.IsR6();
-  // kDirectAddressWithFixup and kCallDirectWithFixup have no extra input on R6 because
-  // R6 has PC-relative addressing.
-  bool has_extra_input = invoke->HasPcRelativeDexCache() ||
-      (!isR6 &&
-       ((method_load_kind == HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) ||
-        (code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup)));
-  Register base_reg = has_extra_input
+  Register base_reg = invoke->HasPcRelativeDexCache()
       ? GetInvokeStaticOrDirectExtraParameter(invoke, temp.AsRegister<Register>())
       : ZERO;
 
-  // For better instruction scheduling we load the direct code pointer before the method pointer.
-  switch (code_ptr_location) {
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
-      // T9 = invoke->GetDirectCodePtr();
-      __ LoadConst32(T9, invoke->GetDirectCodePtr());
-      break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
-      // T9 = code address from literal pool with link-time patch.
-      __ LoadLiteral(T9, base_reg, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod()));
-      break;
-    default:
-      break;
-  }
-
   switch (method_load_kind) {
     case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
       // temp = thread->string_init_entrypoint
@@ -5383,11 +5316,6 @@
     case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
       __ LoadConst32(temp.AsRegister<Register>(), invoke->GetMethodAddress());
       break;
-    case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
-      __ LoadLiteral(temp.AsRegister<Register>(),
-                     base_reg,
-                     DeduplicateMethodAddressLiteral(invoke->GetTargetMethod()));
-      break;
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
       HMipsDexCacheArraysBase* base =
           invoke->InputAt(invoke->GetSpecialInputIndex())->AsMipsDexCacheArraysBase();
@@ -5430,18 +5358,6 @@
     case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
       __ Bal(&frame_entry_label_);
       break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
-      // T9 prepared above for better instruction scheduling.
-      // T9()
-      __ Jalr(T9);
-      __ NopIfNoReordering();
-      break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
-      // TODO: Implement this type.
-      // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch().
-      LOG(FATAL) << "Unsupported";
-      UNREACHABLE();
     case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
       // T9 = callee_method->entry_point_from_quick_compiled_code_;
       __ LoadFromOffset(kLoadWord,
@@ -5681,11 +5597,7 @@
 }
 
 void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) {
-  LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
-      ? ((load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod)
-          ? LocationSummary::kCallOnMainOnly
-          : LocationSummary::kCallOnSlowPath)
-      : LocationSummary::kNoCall;
+  LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
   HLoadString::LoadKind load_kind = load->GetLoadKind();
   switch (load_kind) {
@@ -5735,14 +5647,12 @@
 
   switch (load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimeAddress:
-      DCHECK(!kEmitCompilerReadBarrier);
       __ LoadLiteral(out,
                      base_or_current_method_reg,
                      codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
                                                                  load->GetStringIndex()));
       return;  // No dex cache slow path.
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
-      DCHECK(!kEmitCompilerReadBarrier);
       DCHECK(codegen_->GetCompilerOptions().IsBootImage());
       CodeGeneratorMIPS::PcRelativePatchInfo* info =
           codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
@@ -5750,7 +5660,6 @@
       return;  // No dex cache slow path.
     }
     case HLoadString::LoadKind::kBootImageAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
       DCHECK_NE(load->GetAddress(), 0u);
       uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
       __ LoadLiteral(out,
@@ -6276,7 +6185,10 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitStaticFieldSet(HStaticFieldSet* instruction) {
-  HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetDexPc());
+  HandleFieldSet(instruction,
+                 instruction->GetFieldInfo(),
+                 instruction->GetDexPc(),
+                 instruction->GetValueCanBeNull());
 }
 
 void LocationsBuilderMIPS::VisitUnresolvedInstanceFieldGet(
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index f03f29c..7b0812c 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -236,7 +236,10 @@
   void HandleBinaryOp(HBinaryOperation* operation);
   void HandleCondition(HCondition* instruction);
   void HandleShift(HBinaryOperation* operation);
-  void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc);
+  void HandleFieldSet(HInstruction* instruction,
+                      const FieldInfo& field_info,
+                      uint32_t dex_pc,
+                      bool value_can_be_null);
   void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc);
   // Generate a GC root reference load:
   //
@@ -350,7 +353,7 @@
   // Emit linker patches.
   void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
 
-  void MarkGCCard(Register object, Register value);
+  void MarkGCCard(Register object, Register value, bool value_can_be_null);
 
   // Register allocation.
 
@@ -474,8 +477,6 @@
 
   Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
   Literal* DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map);
-  Literal* DeduplicateMethodAddressLiteral(MethodReference target_method);
-  Literal* DeduplicateMethodCodeLiteral(MethodReference target_method);
   PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file,
                                           uint32_t offset_or_index,
                                           ArenaDeque<PcRelativePatchInfo>* patches);
@@ -495,9 +496,6 @@
 
   // Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
   Uint32ToLiteralMap uint32_literals_;
-  // Method patch info, map MethodReference to a literal for method address and method code.
-  MethodToLiteralMap method_patches_;
-  MethodToLiteralMap call_patches_;
   // PC-relative patch info for each HMipsDexCacheArraysBase.
   ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
   // Deduplication map for boot string literals for kBootImageLinkTimeAddress.
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index b1f9b1d..36690c0 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -18,6 +18,7 @@
 
 #include "art_method.h"
 #include "code_generator_utils.h"
+#include "compiled_method.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "gc/accounting/card_table.h"
@@ -90,8 +91,6 @@
   // Space on the stack is reserved for all arguments.
   stack_index_ += Primitive::Is64BitType(type) ? 2 : 1;
 
-  // TODO: review
-
   // TODO: shouldn't we use a whole machine word per argument on the stack?
   // Implicit 4-byte method pointer (and such) will cause misalignment.
 
@@ -234,6 +233,7 @@
     SaveLiveRegisters(codegen, locations);
 
     InvokeRuntimeCallingConvention calling_convention;
+    HLoadString* load = instruction_->AsLoadString();
     const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex().index_;
     __ LoadConst32(calling_convention.GetRegisterAt(0), string_index);
     mips64_codegen->InvokeRuntime(kQuickResolveString,
@@ -247,6 +247,17 @@
                                  type);
 
     RestoreLiveRegisters(codegen, locations);
+
+    // Store the resolved String to the BSS entry.
+    // TODO: Change art_quick_resolve_string to kSaveEverything and use a temporary for the
+    // .bss entry address in the fast path, so that we can avoid another calculation here.
+    GpuRegister out = locations->Out().AsRegister<GpuRegister>();
+    DCHECK_NE(out, AT);
+    CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+        mips64_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
+    mips64_codegen->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+    __ Sw(out, AT, /* placeholder */ 0x5678);
+
     __ Bc(GetExitLabel());
   }
 
@@ -399,7 +410,20 @@
       instruction_visitor_(graph, this),
       move_resolver_(graph->GetArena(), this),
       assembler_(graph->GetArena()),
-      isa_features_(isa_features) {
+      isa_features_(isa_features),
+      uint32_literals_(std::less<uint32_t>(),
+                       graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      uint64_literals_(std::less<uint64_t>(),
+                       graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      boot_image_string_patches_(StringReferenceValueComparator(),
+                                 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      boot_image_type_patches_(TypeReferenceValueComparator(),
+                               graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      boot_image_address_patches_(std::less<uint32_t>(),
+                                  graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
   // Save RA (containing the return address) to mimic Quick.
   AddAllocatedRegister(Location::RegisterLocation(RA));
 }
@@ -510,8 +534,6 @@
     RecordPcInfo(nullptr, 0);
   }
 
-  // TODO: anything related to T9/GP/GOT/PIC/.so's?
-
   if (HasEmptyFrame()) {
     return;
   }
@@ -562,13 +584,16 @@
                   "kCurrentMethodStackOffset must fit into int16_t");
     __ Sd(kMethodRegisterArgument, SP, kCurrentMethodStackOffset);
   }
+
+  if (GetGraph()->HasShouldDeoptimizeFlag()) {
+    // Initialize should_deoptimize flag to 0.
+    __ StoreToOffset(kStoreWord, ZERO, SP, GetStackOffsetOfShouldDeoptimizeFlag());
+  }
 }
 
 void CodeGeneratorMIPS64::GenerateFrameExit() {
   __ cfi().RememberState();
 
-  // TODO: anything related to T9/GP/GOT/PIC/.so's?
-
   if (!HasEmptyFrame()) {
     // Deallocate the rest of the frame.
 
@@ -878,6 +903,136 @@
   }
 }
 
+template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
+inline void CodeGeneratorMIPS64::EmitPcRelativeLinkerPatches(
+    const ArenaDeque<PcRelativePatchInfo>& infos,
+    ArenaVector<LinkerPatch>* linker_patches) {
+  for (const PcRelativePatchInfo& info : infos) {
+    const DexFile& dex_file = info.target_dex_file;
+    size_t offset_or_index = info.offset_or_index;
+    DCHECK(info.pc_rel_label.IsBound());
+    uint32_t pc_rel_offset = __ GetLabelLocation(&info.pc_rel_label);
+    linker_patches->push_back(Factory(pc_rel_offset, &dex_file, pc_rel_offset, offset_or_index));
+  }
+}
+
+void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
+  DCHECK(linker_patches->empty());
+  size_t size =
+      pc_relative_dex_cache_patches_.size() +
+      pc_relative_string_patches_.size() +
+      pc_relative_type_patches_.size() +
+      boot_image_string_patches_.size() +
+      boot_image_type_patches_.size() +
+      boot_image_address_patches_.size();
+  linker_patches->reserve(size);
+  EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
+                                                               linker_patches);
+  if (!GetCompilerOptions().IsBootImage()) {
+    EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_,
+                                                                  linker_patches);
+  } else {
+    EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_,
+                                                                  linker_patches);
+  }
+  EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_,
+                                                              linker_patches);
+  for (const auto& entry : boot_image_string_patches_) {
+    const StringReference& target_string = entry.first;
+    Literal* literal = entry.second;
+    DCHECK(literal->GetLabel()->IsBound());
+    uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel());
+    linker_patches->push_back(LinkerPatch::StringPatch(literal_offset,
+                                                       target_string.dex_file,
+                                                       target_string.string_index.index_));
+  }
+  for (const auto& entry : boot_image_type_patches_) {
+    const TypeReference& target_type = entry.first;
+    Literal* literal = entry.second;
+    DCHECK(literal->GetLabel()->IsBound());
+    uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel());
+    linker_patches->push_back(LinkerPatch::TypePatch(literal_offset,
+                                                     target_type.dex_file,
+                                                     target_type.type_index.index_));
+  }
+  for (const auto& entry : boot_image_address_patches_) {
+    DCHECK(GetCompilerOptions().GetIncludePatchInformation());
+    Literal* literal = entry.second;
+    DCHECK(literal->GetLabel()->IsBound());
+    uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel());
+    linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset));
+  }
+}
+
+CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeStringPatch(
+    const DexFile& dex_file, uint32_t string_index) {
+  return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_);
+}
+
+CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeTypePatch(
+    const DexFile& dex_file, dex::TypeIndex type_index) {
+  return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_);
+}
+
+CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeDexCacheArrayPatch(
+    const DexFile& dex_file, uint32_t element_offset) {
+  return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_);
+}
+
+CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativePatch(
+    const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
+  patches->emplace_back(dex_file, offset_or_index);
+  return &patches->back();
+}
+
+Literal* CodeGeneratorMIPS64::DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map) {
+  return map->GetOrCreate(
+      value,
+      [this, value]() { return __ NewLiteral<uint32_t>(value); });
+}
+
+Literal* CodeGeneratorMIPS64::DeduplicateUint64Literal(uint64_t value) {
+  return uint64_literals_.GetOrCreate(
+      value,
+      [this, value]() { return __ NewLiteral<uint64_t>(value); });
+}
+
+Literal* CodeGeneratorMIPS64::DeduplicateMethodLiteral(MethodReference target_method,
+                                                       MethodToLiteralMap* map) {
+  return map->GetOrCreate(
+      target_method,
+      [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
+}
+
+Literal* CodeGeneratorMIPS64::DeduplicateBootImageStringLiteral(const DexFile& dex_file,
+                                                                dex::StringIndex string_index) {
+  return boot_image_string_patches_.GetOrCreate(
+      StringReference(&dex_file, string_index),
+      [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
+}
+
+Literal* CodeGeneratorMIPS64::DeduplicateBootImageTypeLiteral(const DexFile& dex_file,
+                                                              dex::TypeIndex type_index) {
+  return boot_image_type_patches_.GetOrCreate(
+      TypeReference(&dex_file, type_index),
+      [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
+}
+
+Literal* CodeGeneratorMIPS64::DeduplicateBootImageAddressLiteral(uint64_t address) {
+  bool needs_patch = GetCompilerOptions().GetIncludePatchInformation();
+  Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_;
+  return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map);
+}
+
+void CodeGeneratorMIPS64::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info,
+                                                               GpuRegister out) {
+  __ Bind(&info->pc_rel_label);
+  // Add the high half of a 32-bit offset to PC.
+  __ Auipc(out, /* placeholder */ 0x1234);
+  // The immediately following instruction will add the sign-extended low half of the 32-bit
+  // offset to `out` (e.g. ld, jialc, daddiu).
+}
+
 void CodeGeneratorMIPS64::SetupBlockedRegisters() const {
   // ZERO, K0, K1, GP, SP, RA are always reserved and can't be allocated.
   blocked_core_registers_[ZERO] = true;
@@ -901,8 +1056,6 @@
   // Reserve T9 for function calls
   blocked_core_registers_[T9] = true;
 
-  // TODO: review; anything else?
-
   if (GetGraph()->IsDebuggable()) {
     // Stubs do not save callee-save floating point registers. If the graph
     // is debuggable, we need to deal with these registers differently. For
@@ -946,7 +1099,6 @@
                                         uint32_t dex_pc,
                                         SlowPathCode* slow_path) {
   ValidateInvokeRuntime(entrypoint, instruction, slow_path);
-  // TODO: anything related to T9/GP/GOT/PIC/.so's?
   __ LoadFromOffset(kLoadDoubleword,
                     T9,
                     TR,
@@ -1777,9 +1929,6 @@
 
   Primitive::Type type = instruction->InputAt(0)->GetType();
   LocationSummary* locations = instruction->GetLocations();
-  GpuRegister dst = locations->Out().AsRegister<GpuRegister>();
-  Mips64Label true_label;
-
   switch (type) {
     default:
       // Integer case.
@@ -1788,29 +1937,11 @@
     case Primitive::kPrimLong:
       GenerateIntLongCompare(instruction->GetCondition(), /* is64bit */ true, locations);
       return;
-
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble:
-      // TODO: don't use branches.
-      GenerateFpCompareAndBranch(instruction->GetCondition(),
-                                 instruction->IsGtBias(),
-                                 type,
-                                 locations,
-                                 &true_label);
-      break;
+      GenerateFpCompare(instruction->GetCondition(), instruction->IsGtBias(), type, locations);
+     return;
   }
-
-  // Convert the branches into the result.
-  Mips64Label done;
-
-  // False case: result = 0.
-  __ LoadConst32(dst, 0);
-  __ Bc(&done);
-
-  // True case: result = 1.
-  __ Bind(&true_label);
-  __ LoadConst32(dst, 1);
-  __ Bind(&done);
 }
 
 void InstructionCodeGeneratorMIPS64::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
@@ -2225,19 +2356,40 @@
   switch (cond) {
     case kCondEQ:
     case kCondNE:
-      if (use_imm && IsUint<16>(rhs_imm)) {
-        __ Xori(dst, lhs, rhs_imm);
-      } else {
-        if (use_imm) {
-          rhs_reg = TMP;
-          __ LoadConst64(rhs_reg, rhs_imm);
+      if (use_imm && IsInt<16>(-rhs_imm)) {
+        if (rhs_imm == 0) {
+          if (cond == kCondEQ) {
+            __ Sltiu(dst, lhs, 1);
+          } else {
+            __ Sltu(dst, ZERO, lhs);
+          }
+        } else {
+          if (is64bit) {
+            __ Daddiu(dst, lhs, -rhs_imm);
+          } else {
+            __ Addiu(dst, lhs, -rhs_imm);
+          }
+          if (cond == kCondEQ) {
+            __ Sltiu(dst, dst, 1);
+          } else {
+            __ Sltu(dst, ZERO, dst);
+          }
         }
-        __ Xor(dst, lhs, rhs_reg);
-      }
-      if (cond == kCondEQ) {
-        __ Sltiu(dst, dst, 1);
       } else {
-        __ Sltu(dst, ZERO, dst);
+        if (use_imm && IsUint<16>(rhs_imm)) {
+          __ Xori(dst, lhs, rhs_imm);
+        } else {
+          if (use_imm) {
+            rhs_reg = TMP;
+            __ LoadConst64(rhs_reg, rhs_imm);
+          }
+          __ Xor(dst, lhs, rhs_reg);
+        }
+        if (cond == kCondEQ) {
+          __ Sltiu(dst, dst, 1);
+        } else {
+          __ Sltu(dst, ZERO, dst);
+        }
       }
       break;
 
@@ -2424,6 +2576,121 @@
   }
 }
 
+void InstructionCodeGeneratorMIPS64::GenerateFpCompare(IfCondition cond,
+                                                       bool gt_bias,
+                                                       Primitive::Type type,
+                                                       LocationSummary* locations) {
+  GpuRegister dst = locations->Out().AsRegister<GpuRegister>();
+  FpuRegister lhs = locations->InAt(0).AsFpuRegister<FpuRegister>();
+  FpuRegister rhs = locations->InAt(1).AsFpuRegister<FpuRegister>();
+  if (type == Primitive::kPrimFloat) {
+    switch (cond) {
+      case kCondEQ:
+        __ CmpEqS(FTMP, lhs, rhs);
+        __ Mfc1(dst, FTMP);
+        __ Andi(dst, dst, 1);
+        break;
+      case kCondNE:
+        __ CmpEqS(FTMP, lhs, rhs);
+        __ Mfc1(dst, FTMP);
+        __ Addiu(dst, dst, 1);
+        break;
+      case kCondLT:
+        if (gt_bias) {
+          __ CmpLtS(FTMP, lhs, rhs);
+        } else {
+          __ CmpUltS(FTMP, lhs, rhs);
+        }
+        __ Mfc1(dst, FTMP);
+        __ Andi(dst, dst, 1);
+        break;
+      case kCondLE:
+        if (gt_bias) {
+          __ CmpLeS(FTMP, lhs, rhs);
+        } else {
+          __ CmpUleS(FTMP, lhs, rhs);
+        }
+        __ Mfc1(dst, FTMP);
+        __ Andi(dst, dst, 1);
+        break;
+      case kCondGT:
+        if (gt_bias) {
+          __ CmpUltS(FTMP, rhs, lhs);
+        } else {
+          __ CmpLtS(FTMP, rhs, lhs);
+        }
+        __ Mfc1(dst, FTMP);
+        __ Andi(dst, dst, 1);
+        break;
+      case kCondGE:
+        if (gt_bias) {
+          __ CmpUleS(FTMP, rhs, lhs);
+        } else {
+          __ CmpLeS(FTMP, rhs, lhs);
+        }
+        __ Mfc1(dst, FTMP);
+        __ Andi(dst, dst, 1);
+        break;
+      default:
+        LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
+        UNREACHABLE();
+    }
+  } else {
+    DCHECK_EQ(type, Primitive::kPrimDouble);
+    switch (cond) {
+      case kCondEQ:
+        __ CmpEqD(FTMP, lhs, rhs);
+        __ Mfc1(dst, FTMP);
+        __ Andi(dst, dst, 1);
+        break;
+      case kCondNE:
+        __ CmpEqD(FTMP, lhs, rhs);
+        __ Mfc1(dst, FTMP);
+        __ Addiu(dst, dst, 1);
+        break;
+      case kCondLT:
+        if (gt_bias) {
+          __ CmpLtD(FTMP, lhs, rhs);
+        } else {
+          __ CmpUltD(FTMP, lhs, rhs);
+        }
+        __ Mfc1(dst, FTMP);
+        __ Andi(dst, dst, 1);
+        break;
+      case kCondLE:
+        if (gt_bias) {
+          __ CmpLeD(FTMP, lhs, rhs);
+        } else {
+          __ CmpUleD(FTMP, lhs, rhs);
+        }
+        __ Mfc1(dst, FTMP);
+        __ Andi(dst, dst, 1);
+        break;
+      case kCondGT:
+        if (gt_bias) {
+          __ CmpUltD(FTMP, rhs, lhs);
+        } else {
+          __ CmpLtD(FTMP, rhs, lhs);
+        }
+        __ Mfc1(dst, FTMP);
+        __ Andi(dst, dst, 1);
+        break;
+      case kCondGE:
+        if (gt_bias) {
+          __ CmpUleD(FTMP, rhs, lhs);
+        } else {
+          __ CmpLeD(FTMP, rhs, lhs);
+        }
+        __ Mfc1(dst, FTMP);
+        __ Andi(dst, dst, 1);
+        break;
+      default:
+        LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
+        UNREACHABLE();
+    }
+  }
+}
+
 void InstructionCodeGeneratorMIPS64::GenerateFpCompareAndBranch(IfCondition cond,
                                                                 bool gt_bias,
                                                                 Primitive::Type type,
@@ -2636,14 +2903,17 @@
                         /* false_target */ nullptr);
 }
 
-void LocationsBuilderMIPS64::VisitShouldDeoptimizeFlag(
-    HShouldDeoptimizeFlag* flag ATTRIBUTE_UNUSED) {
-  // TODO: to be implemented.
+void LocationsBuilderMIPS64::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
+  LocationSummary* locations = new (GetGraph()->GetArena())
+      LocationSummary(flag, LocationSummary::kNoCall);
+  locations->SetOut(Location::RequiresRegister());
 }
 
-void InstructionCodeGeneratorMIPS64::VisitShouldDeoptimizeFlag(
-    HShouldDeoptimizeFlag* flag ATTRIBUTE_UNUSED) {
-  // TODO: to be implemented.
+void InstructionCodeGeneratorMIPS64::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
+  __ LoadFromOffset(kLoadWord,
+                    flag->GetLocations()->Out().AsRegister<GpuRegister>(),
+                    SP,
+                    codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
 }
 
 void LocationsBuilderMIPS64::VisitSelect(HSelect* select) {
@@ -2820,6 +3090,31 @@
   HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
 }
 
+void InstructionCodeGeneratorMIPS64::GenerateGcRootFieldLoad(
+    HInstruction* instruction ATTRIBUTE_UNUSED,
+    Location root,
+    GpuRegister obj,
+    uint32_t offset) {
+  // When handling HLoadClass::LoadKind::kDexCachePcRelative, the caller calls
+  // EmitPcRelativeAddressPlaceholderHigh() and then GenerateGcRootFieldLoad().
+  // The relative patcher expects the two methods to emit the following patchable
+  // sequence of instructions in this case:
+  //   auipc reg1, 0x1234  // 0x1234 is a placeholder for offset_high.
+  //   lwu   reg2, 0x5678(reg1)  // 0x5678 is a placeholder for offset_low.
+  // TODO: Adjust GenerateGcRootFieldLoad() and its caller when this method is
+  // extended (e.g. for read barriers) so as not to break the relative patcher.
+  GpuRegister root_reg = root.AsRegister<GpuRegister>();
+  if (kEmitCompilerReadBarrier) {
+    UNIMPLEMENTED(FATAL) << "for read barrier";
+  } else {
+    // Plain GC root load with no read barrier.
+    // /* GcRoot<mirror::Object> */ root = *(obj + offset)
+    __ LoadFromOffset(kLoadUnsignedWord, root_reg, obj, offset);
+    // Note that GC roots are not affected by heap poisoning, thus we
+    // do not have to unpoison `root_reg` here.
+  }
+}
+
 void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
   LocationSummary::CallKind call_kind =
       instruction->IsExactCheck() ? LocationSummary::kNoCall : LocationSummary::kCallOnSlowPath;
@@ -2971,54 +3266,85 @@
 }
 
 HLoadString::LoadKind CodeGeneratorMIPS64::GetSupportedLoadStringKind(
-    HLoadString::LoadKind desired_string_load_kind ATTRIBUTE_UNUSED) {
-  // TODO: Implement other kinds.
-  return HLoadString::LoadKind::kDexCacheViaMethod;
+    HLoadString::LoadKind desired_string_load_kind) {
+  if (kEmitCompilerReadBarrier) {
+    UNIMPLEMENTED(FATAL) << "for read barrier";
+  }
+  bool fallback_load = false;
+  switch (desired_string_load_kind) {
+    case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+      DCHECK(!GetCompilerOptions().GetCompilePic());
+      break;
+    case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+      DCHECK(GetCompilerOptions().GetCompilePic());
+      break;
+    case HLoadString::LoadKind::kBootImageAddress:
+      break;
+    case HLoadString::LoadKind::kBssEntry:
+      DCHECK(!Runtime::Current()->UseJitCompilation());
+      break;
+    case HLoadString::LoadKind::kDexCacheViaMethod:
+      break;
+    case HLoadString::LoadKind::kJitTableAddress:
+      DCHECK(Runtime::Current()->UseJitCompilation());
+      // TODO: implement.
+      fallback_load = true;
+      break;
+  }
+  if (fallback_load) {
+    desired_string_load_kind = HLoadString::LoadKind::kDexCacheViaMethod;
+  }
+  return desired_string_load_kind;
 }
 
 HLoadClass::LoadKind CodeGeneratorMIPS64::GetSupportedLoadClassKind(
     HLoadClass::LoadKind desired_class_load_kind) {
-  DCHECK_NE(desired_class_load_kind, HLoadClass::LoadKind::kReferrersClass);
-  // TODO: Implement other kinds.
-  return HLoadClass::LoadKind::kDexCacheViaMethod;
+  if (kEmitCompilerReadBarrier) {
+    UNIMPLEMENTED(FATAL) << "for read barrier";
+  }
+  bool fallback_load = false;
+  switch (desired_class_load_kind) {
+    case HLoadClass::LoadKind::kReferrersClass:
+      break;
+    case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+      DCHECK(!GetCompilerOptions().GetCompilePic());
+      break;
+    case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+      DCHECK(GetCompilerOptions().GetCompilePic());
+      break;
+    case HLoadClass::LoadKind::kBootImageAddress:
+      break;
+    case HLoadClass::LoadKind::kJitTableAddress:
+      DCHECK(Runtime::Current()->UseJitCompilation());
+      // TODO: implement.
+      fallback_load = true;
+      break;
+    case HLoadClass::LoadKind::kDexCachePcRelative:
+      DCHECK(!Runtime::Current()->UseJitCompilation());
+      break;
+    case HLoadClass::LoadKind::kDexCacheViaMethod:
+      break;
+  }
+  if (fallback_load) {
+    desired_class_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
+  }
+  return desired_class_load_kind;
 }
 
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS64::GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
       HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
-  switch (desired_dispatch_info.method_load_kind) {
-    case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
-    case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
-      // TODO: Implement these types. For the moment, we fall back to kDexCacheViaMethod.
-      return HInvokeStaticOrDirect::DispatchInfo {
-        HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
-        HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
-        0u,
-        0u
-      };
-    default:
-      break;
-  }
-  switch (desired_dispatch_info.code_ptr_location) {
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
-      // TODO: Implement these types. For the moment, we fall back to kCallArtMethod.
-      return HInvokeStaticOrDirect::DispatchInfo {
-        desired_dispatch_info.method_load_kind,
-        HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
-        desired_dispatch_info.method_load_data,
-        0u
-      };
-    default:
-      return desired_dispatch_info;
-  }
+  // On MIPS64 we support all dispatch types.
+  return desired_dispatch_info;
 }
 
 void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
   // All registers are assumed to be correctly set up per the calling convention.
-
   Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
-  switch (invoke->GetMethodLoadKind()) {
+  HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind();
+  HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation();
+
+  switch (method_load_kind) {
     case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
       // temp = thread->string_init_entrypoint
       uint32_t offset =
@@ -3033,14 +3359,18 @@
       callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
       break;
     case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
-      __ LoadConst64(temp.AsRegister<GpuRegister>(), invoke->GetMethodAddress());
+      __ LoadLiteral(temp.AsRegister<GpuRegister>(),
+                     kLoadDoubleword,
+                     DeduplicateUint64Literal(invoke->GetMethodAddress()));
       break;
-    case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
-    case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
-      // TODO: Implement these types.
-      // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch().
-      LOG(FATAL) << "Unsupported";
-      UNREACHABLE();
+    case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
+      uint32_t offset = invoke->GetDexCacheArrayOffset();
+      CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+          NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset);
+      EmitPcRelativeAddressPlaceholderHigh(info, AT);
+      __ Ld(temp.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678);
+      break;
+    }
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
       Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
       GpuRegister reg = temp.AsRegister<GpuRegister>();
@@ -3071,23 +3401,10 @@
     }
   }
 
-  switch (invoke->GetCodePtrLocation()) {
+  switch (code_ptr_location) {
     case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
-      __ Jialc(&frame_entry_label_, T9);
+      __ Balc(&frame_entry_label_);
       break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
-      // LR = invoke->GetDirectCodePtr();
-      __ LoadConst64(T9, invoke->GetDirectCodePtr());
-      // LR()
-      __ Jalr(T9);
-      __ Nop();
-      break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
-      // TODO: Implement these types.
-      // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch().
-      LOG(FATAL) << "Unsupported";
-      UNREACHABLE();
     case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
       // T9 = callee_method->entry_point_from_quick_compiled_code_;
       __ LoadFromOffset(kLoadDoubleword,
@@ -3157,11 +3474,26 @@
 }
 
 void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) {
-  InvokeRuntimeCallingConvention calling_convention;
-  CodeGenerator::CreateLoadClassLocationSummary(
-      cls,
-      Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
-      calling_convention.GetReturnLocation(cls->GetType()));
+  if (cls->NeedsAccessCheck()) {
+    InvokeRuntimeCallingConvention calling_convention;
+    CodeGenerator::CreateLoadClassLocationSummary(
+        cls,
+        Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+        calling_convention.GetReturnLocation(Primitive::kPrimNot),
+        /* code_generator_supports_read_barrier */ false);
+    return;
+  }
+
+  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+  HLoadClass::LoadKind load_kind = cls->GetLoadKind();
+  if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
+      load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
+    locations->SetInAt(0, Location::RequiresRegister());
+  }
+  locations->SetOut(Location::RequiresRegister());
 }
 
 void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) {
@@ -3173,35 +3505,90 @@
     return;
   }
 
-  GpuRegister out = locations->Out().AsRegister<GpuRegister>();
-  GpuRegister current_method = locations->InAt(0).AsRegister<GpuRegister>();
-  if (cls->IsReferrersClass()) {
-    DCHECK(!cls->CanCallRuntime());
-    DCHECK(!cls->MustGenerateClinitCheck());
-    __ LoadFromOffset(kLoadUnsignedWord, out, current_method,
-                      ArtMethod::DeclaringClassOffset().Int32Value());
-  } else {
-    __ LoadFromOffset(kLoadDoubleword, out, current_method,
-                      ArtMethod::DexCacheResolvedTypesOffset(kMips64PointerSize).Int32Value());
-    __ LoadFromOffset(
-        kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_));
-    // TODO: We will need a read barrier here.
-    if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) {
-      DCHECK(cls->CanCallRuntime());
-      SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathMIPS64(
-          cls,
-          cls,
-          cls->GetDexPc(),
-          cls->MustGenerateClinitCheck());
-      codegen_->AddSlowPath(slow_path);
-      if (!cls->IsInDexCache()) {
-        __ Beqzc(out, slow_path->GetEntryLabel());
-      }
-      if (cls->MustGenerateClinitCheck()) {
-        GenerateClassInitializationCheck(slow_path, out);
-      } else {
-        __ Bind(slow_path->GetExitLabel());
-      }
+  HLoadClass::LoadKind load_kind = cls->GetLoadKind();
+  Location out_loc = locations->Out();
+  GpuRegister out = out_loc.AsRegister<GpuRegister>();
+  GpuRegister current_method_reg = ZERO;
+  if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
+      load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
+      current_method_reg = locations->InAt(0).AsRegister<GpuRegister>();
+  }
+
+  bool generate_null_check = false;
+  switch (load_kind) {
+    case HLoadClass::LoadKind::kReferrersClass:
+      DCHECK(!cls->CanCallRuntime());
+      DCHECK(!cls->MustGenerateClinitCheck());
+      // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              current_method_reg,
+                              ArtMethod::DeclaringClassOffset().Int32Value());
+      break;
+    case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+      DCHECK(!kEmitCompilerReadBarrier);
+      __ LoadLiteral(out,
+                     kLoadUnsignedWord,
+                     codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(),
+                                                               cls->GetTypeIndex()));
+      break;
+    case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
+      DCHECK(!kEmitCompilerReadBarrier);
+      CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+          codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
+      codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+      __ Daddiu(out, AT, /* placeholder */ 0x5678);
+      break;
+    }
+    case HLoadClass::LoadKind::kBootImageAddress: {
+      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK_NE(cls->GetAddress(), 0u);
+      uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+      __ LoadLiteral(out,
+                     kLoadUnsignedWord,
+                     codegen_->DeduplicateBootImageAddressLiteral(address));
+      break;
+    }
+    case HLoadClass::LoadKind::kJitTableAddress: {
+      LOG(FATAL) << "Unimplemented";
+      break;
+    }
+    case HLoadClass::LoadKind::kDexCachePcRelative: {
+      uint32_t element_offset = cls->GetDexCacheElementOffset();
+      CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+          codegen_->NewPcRelativeDexCacheArrayPatch(cls->GetDexFile(), element_offset);
+      codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+      // /* GcRoot<mirror::Class> */ out = *address  /* PC-relative */
+      GenerateGcRootFieldLoad(cls, out_loc, AT, /* placeholder */ 0x5678);
+      generate_null_check = !cls->IsInDexCache();
+      break;
+    }
+    case HLoadClass::LoadKind::kDexCacheViaMethod: {
+      // /* GcRoot<mirror::Class>[] */ out =
+      //        current_method.ptr_sized_fields_->dex_cache_resolved_types_
+      __ LoadFromOffset(kLoadDoubleword,
+                        out,
+                        current_method_reg,
+                        ArtMethod::DexCacheResolvedTypesOffset(kMips64PointerSize).Int32Value());
+      // /* GcRoot<mirror::Class> */ out = out[type_index]
+      size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_);
+      GenerateGcRootFieldLoad(cls, out_loc, out, offset);
+      generate_null_check = !cls->IsInDexCache();
+    }
+  }
+
+  if (generate_null_check || cls->MustGenerateClinitCheck()) {
+    DCHECK(cls->CanCallRuntime());
+    SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathMIPS64(
+        cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
+    codegen_->AddSlowPath(slow_path);
+    if (generate_null_check) {
+      __ Beqzc(out, slow_path->GetEntryLabel());
+    }
+    if (cls->MustGenerateClinitCheck()) {
+      GenerateClassInitializationCheck(slow_path, out);
+    } else {
+      __ Bind(slow_path->GetExitLabel());
     }
   }
 }
@@ -3230,20 +3617,68 @@
 }
 
 void LocationsBuilderMIPS64::VisitLoadString(HLoadString* load) {
-  LocationSummary::CallKind call_kind = load->NeedsEnvironment()
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
+  HLoadString::LoadKind load_kind = load->GetLoadKind();
+  LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister());
+  if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
+    InvokeRuntimeCallingConvention calling_convention;
+    locations->SetOut(calling_convention.GetReturnLocation(load->GetType()));
+  } else {
+    locations->SetOut(Location::RequiresRegister());
+  }
 }
 
 void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) {
+  HLoadString::LoadKind load_kind = load->GetLoadKind();
+  LocationSummary* locations = load->GetLocations();
+  Location out_loc = locations->Out();
+  GpuRegister out = out_loc.AsRegister<GpuRegister>();
+
+  switch (load_kind) {
+    case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+      __ LoadLiteral(out,
+                     kLoadUnsignedWord,
+                     codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
+                                                                 load->GetStringIndex()));
+      return;  // No dex cache slow path.
+    case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
+      DCHECK(codegen_->GetCompilerOptions().IsBootImage());
+      CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+          codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
+      codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+      __ Daddiu(out, AT, /* placeholder */ 0x5678);
+      return;  // No dex cache slow path.
+    }
+    case HLoadString::LoadKind::kBootImageAddress: {
+      DCHECK_NE(load->GetAddress(), 0u);
+      uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+      __ LoadLiteral(out,
+                     kLoadUnsignedWord,
+                     codegen_->DeduplicateBootImageAddressLiteral(address));
+      return;  // No dex cache slow path.
+    }
+    case HLoadString::LoadKind::kBssEntry: {
+      DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+      CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+          codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
+      codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+      __ Lwu(out, AT, /* placeholder */ 0x5678);
+      SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load);
+      codegen_->AddSlowPath(slow_path);
+      __ Beqzc(out, slow_path->GetEntryLabel());
+      __ Bind(slow_path->GetExitLabel());
+      return;
+    }
+    default:
+      break;
+  }
+
   // TODO: Re-add the compiler code to do string dex cache lookup again.
-  SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load);
-  codegen_->AddSlowPath(slow_path);
-  __ Bc(slow_path->GetEntryLabel());
-  __ Bind(slow_path->GetExitLabel());
+  DCHECK(load_kind == HLoadString::LoadKind::kDexCacheViaMethod);
+  InvokeRuntimeCallingConvention calling_convention;
+  __ LoadConst32(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
+  codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
+  CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
 }
 
 void LocationsBuilderMIPS64::VisitLongConstant(HLongConstant* constant) {
@@ -3840,9 +4275,12 @@
         break;
       case Primitive::kPrimInt:
       case Primitive::kPrimLong:
-        // Sign-extend 32-bit int into bits 32 through 63 for
-        // int-to-long and long-to-int conversions
-        __ Sll(dst, src, 0);
+        // Sign-extend 32-bit int into bits 32 through 63 for int-to-long and long-to-int
+        // conversions, except when the input and output registers are the same and we are not
+        // converting longs to shorter types. In these cases, do nothing.
+        if ((input_type == Primitive::kPrimLong) || (dst != src)) {
+          __ Sll(dst, src, 0);
+        }
         break;
 
       default:
@@ -4079,27 +4517,20 @@
   locations->SetInAt(0, Location::RequiresRegister());
 }
 
-void InstructionCodeGeneratorMIPS64::VisitPackedSwitch(HPackedSwitch* switch_instr) {
-  int32_t lower_bound = switch_instr->GetStartValue();
-  int32_t num_entries = switch_instr->GetNumEntries();
-  LocationSummary* locations = switch_instr->GetLocations();
-  GpuRegister value_reg = locations->InAt(0).AsRegister<GpuRegister>();
-  HBasicBlock* default_block = switch_instr->GetDefaultBlock();
-
+void InstructionCodeGeneratorMIPS64::GenPackedSwitchWithCompares(GpuRegister value_reg,
+                                                                 int32_t lower_bound,
+                                                                 uint32_t num_entries,
+                                                                 HBasicBlock* switch_block,
+                                                                 HBasicBlock* default_block) {
   // Create a set of compare/jumps.
   GpuRegister temp_reg = TMP;
-  if (IsInt<16>(-lower_bound)) {
-    __ Addiu(temp_reg, value_reg, -lower_bound);
-  } else {
-    __ LoadConst32(AT, -lower_bound);
-    __ Addu(temp_reg, value_reg, AT);
-  }
+  __ Addiu32(temp_reg, value_reg, -lower_bound);
   // Jump to default if index is negative
   // Note: We don't check the case that index is positive while value < lower_bound, because in
   // this case, index >= num_entries must be true. So that we can save one branch instruction.
   __ Bltzc(temp_reg, codegen_->GetLabelOf(default_block));
 
-  const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
+  const ArenaVector<HBasicBlock*>& successors = switch_block->GetSuccessors();
   // Jump to successors[0] if value == lower_bound.
   __ Beqzc(temp_reg, codegen_->GetLabelOf(successors[0]));
   int32_t last_index = 0;
@@ -4117,11 +4548,66 @@
   }
 
   // And the default for any other value.
-  if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
+  if (!codegen_->GoesToNextBlock(switch_block, default_block)) {
     __ Bc(codegen_->GetLabelOf(default_block));
   }
 }
 
+void InstructionCodeGeneratorMIPS64::GenTableBasedPackedSwitch(GpuRegister value_reg,
+                                                               int32_t lower_bound,
+                                                               uint32_t num_entries,
+                                                               HBasicBlock* switch_block,
+                                                               HBasicBlock* default_block) {
+  // Create a jump table.
+  std::vector<Mips64Label*> labels(num_entries);
+  const ArenaVector<HBasicBlock*>& successors = switch_block->GetSuccessors();
+  for (uint32_t i = 0; i < num_entries; i++) {
+    labels[i] = codegen_->GetLabelOf(successors[i]);
+  }
+  JumpTable* table = __ CreateJumpTable(std::move(labels));
+
+  // Is the value in range?
+  __ Addiu32(TMP, value_reg, -lower_bound);
+  __ LoadConst32(AT, num_entries);
+  __ Bgeuc(TMP, AT, codegen_->GetLabelOf(default_block));
+
+  // We are in the range of the table.
+  // Load the target address from the jump table, indexing by the value.
+  __ LoadLabelAddress(AT, table->GetLabel());
+  __ Sll(TMP, TMP, 2);
+  __ Daddu(TMP, TMP, AT);
+  __ Lw(TMP, TMP, 0);
+  // Compute the absolute target address by adding the table start address
+  // (the table contains offsets to targets relative to its start).
+  __ Daddu(TMP, TMP, AT);
+  // And jump.
+  __ Jr(TMP);
+  __ Nop();
+}
+
+void InstructionCodeGeneratorMIPS64::VisitPackedSwitch(HPackedSwitch* switch_instr) {
+  int32_t lower_bound = switch_instr->GetStartValue();
+  uint32_t num_entries = switch_instr->GetNumEntries();
+  LocationSummary* locations = switch_instr->GetLocations();
+  GpuRegister value_reg = locations->InAt(0).AsRegister<GpuRegister>();
+  HBasicBlock* switch_block = switch_instr->GetBlock();
+  HBasicBlock* default_block = switch_instr->GetDefaultBlock();
+
+  if (num_entries > kPackedSwitchJumpTableThreshold) {
+    GenTableBasedPackedSwitch(value_reg,
+                              lower_bound,
+                              num_entries,
+                              switch_block,
+                              default_block);
+  } else {
+    GenPackedSwitchWithCompares(value_reg,
+                                lower_bound,
+                                num_entries,
+                                switch_block,
+                                default_block);
+  }
+}
+
 void LocationsBuilderMIPS64::VisitClassTableGet(HClassTableGet*) {
   UNIMPLEMENTED(FATAL) << "ClassTableGet is unimplemented on mips64";
 }
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 690eccb..8ac919f 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -22,6 +22,7 @@
 #include "nodes.h"
 #include "parallel_move_resolver.h"
 #include "utils/mips64/assembler_mips64.h"
+#include "utils/type_reference.h"
 
 namespace art {
 namespace mips64 {
@@ -216,6 +217,14 @@
 
   Mips64Assembler* GetAssembler() const { return assembler_; }
 
+  // Compare-and-jump packed switch generates approx. 3 + 2.5 * N 32-bit
+  // instructions for N cases.
+  // Table-based packed switch generates approx. 11 32-bit instructions
+  // and N 32-bit data words for N cases.
+  // At N = 6 they come out as 18 and 17 32-bit words respectively.
+  // We switch to the table-based method starting with 7 cases.
+  static constexpr uint32_t kPackedSwitchJumpTableThreshold = 6;
+
  private:
   void GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path, GpuRegister class_reg);
   void GenerateMemoryBarrier(MemBarrierKind kind);
@@ -227,6 +236,15 @@
                       const FieldInfo& field_info,
                       bool value_can_be_null);
   void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+  // Generate a GC root reference load:
+  //
+  //   root <- *(obj + offset)
+  //
+  // while honoring read barriers (if any).
+  void GenerateGcRootFieldLoad(HInstruction* instruction,
+                               Location root,
+                               GpuRegister obj,
+                               uint32_t offset);
   void GenerateTestAndBranch(HInstruction* instruction,
                              size_t condition_input_index,
                              Mips64Label* true_target,
@@ -240,12 +258,26 @@
                                        bool is64bit,
                                        LocationSummary* locations,
                                        Mips64Label* label);
+  void GenerateFpCompare(IfCondition cond,
+                         bool gt_bias,
+                         Primitive::Type type,
+                         LocationSummary* locations);
   void GenerateFpCompareAndBranch(IfCondition cond,
                                   bool gt_bias,
                                   Primitive::Type type,
                                   LocationSummary* locations,
                                   Mips64Label* label);
   void HandleGoto(HInstruction* got, HBasicBlock* successor);
+  void GenPackedSwitchWithCompares(GpuRegister value_reg,
+                                   int32_t lower_bound,
+                                   uint32_t num_entries,
+                                   HBasicBlock* switch_block,
+                                   HBasicBlock* default_block);
+  void GenTableBasedPackedSwitch(GpuRegister value_reg,
+                                 int32_t lower_bound,
+                                 uint32_t num_entries,
+                                 HBasicBlock* switch_block,
+                                 HBasicBlock* default_block);
 
   Mips64Assembler* const assembler_;
   CodeGeneratorMIPS64* const codegen_;
@@ -279,6 +311,9 @@
   Mips64Assembler* GetAssembler() OVERRIDE { return &assembler_; }
   const Mips64Assembler& GetAssembler() const OVERRIDE { return assembler_; }
 
+  // Emit linker patches.
+  void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
+
   void MarkGCCard(GpuRegister object, GpuRegister value, bool value_can_be_null);
 
   // Register allocation.
@@ -307,6 +342,10 @@
     block_labels_ = CommonInitializeLabels<Mips64Label>();
   }
 
+  // We prefer aligned loads and stores (less code), so spill and restore registers in slow paths
+  // at aligned locations.
+  uint32_t GetPreferredSlotsAlignment() const OVERRIDE { return kMips64DoublewordSize; }
+
   void Finalize(CodeAllocator* allocator) OVERRIDE;
 
   // Code generation helpers.
@@ -357,7 +396,57 @@
   void GenerateImplicitNullCheck(HNullCheck* instruction) OVERRIDE;
   void GenerateExplicitNullCheck(HNullCheck* instruction) OVERRIDE;
 
+  // The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays,
+  // boot image strings and method calls. The only difference is the interpretation of
+  // the offset_or_index.
+  struct PcRelativePatchInfo {
+    PcRelativePatchInfo(const DexFile& dex_file, uint32_t off_or_idx)
+        : target_dex_file(dex_file), offset_or_index(off_or_idx) { }
+    PcRelativePatchInfo(PcRelativePatchInfo&& other) = default;
+
+    const DexFile& target_dex_file;
+    // Either the dex cache array element offset or the string/type/method index.
+    uint32_t offset_or_index;
+    // Label for the auipc instruction.
+    Mips64Label pc_rel_label;
+  };
+
+  PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index);
+  PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index);
+  PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
+                                                       uint32_t element_offset);
+  PcRelativePatchInfo* NewPcRelativeCallPatch(const DexFile& dex_file,
+                                              uint32_t method_index);
+  Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file,
+                                             dex::StringIndex string_index);
+  Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index);
+  Literal* DeduplicateBootImageAddressLiteral(uint64_t address);
+
+  void EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info, GpuRegister out);
+
  private:
+  using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, Literal*>;
+  using Uint64ToLiteralMap = ArenaSafeMap<uint64_t, Literal*>;
+  using MethodToLiteralMap = ArenaSafeMap<MethodReference, Literal*, MethodReferenceComparator>;
+  using BootStringToLiteralMap = ArenaSafeMap<StringReference,
+                                              Literal*,
+                                              StringReferenceValueComparator>;
+  using BootTypeToLiteralMap = ArenaSafeMap<TypeReference,
+                                            Literal*,
+                                            TypeReferenceValueComparator>;
+
+  Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
+  Literal* DeduplicateUint64Literal(uint64_t value);
+  Literal* DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map);
+
+  PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file,
+                                          uint32_t offset_or_index,
+                                          ArenaDeque<PcRelativePatchInfo>* patches);
+
+  template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
+  void EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo>& infos,
+                                   ArenaVector<LinkerPatch>* linker_patches);
+
   // Labels for each block that will be compiled.
   Mips64Label* block_labels_;  // Indexed by block id.
   Mips64Label frame_entry_label_;
@@ -367,6 +456,24 @@
   Mips64Assembler assembler_;
   const Mips64InstructionSetFeatures& isa_features_;
 
+  // Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
+  Uint32ToLiteralMap uint32_literals_;
+  // Deduplication map for 64-bit literals, used for non-patchable method address or method code
+  // address.
+  Uint64ToLiteralMap uint64_literals_;
+  // PC-relative patch info.
+  ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
+  // Deduplication map for boot string literals for kBootImageLinkTimeAddress.
+  BootStringToLiteralMap boot_image_string_patches_;
+  // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC).
+  ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
+  // Deduplication map for boot type literals for kBootImageLinkTimeAddress.
+  BootTypeToLiteralMap boot_image_type_patches_;
+  // PC-relative type patch info.
+  ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_;
+  // Deduplication map for patchable boot image addresses.
+  Uint32ToLiteralMap boot_image_address_patches_;
+
   DISALLOW_COPY_AND_ASSIGN(CodeGeneratorMIPS64);
 };
 
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 8612a67..0abe855 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1006,8 +1006,6 @@
       move_resolver_(graph->GetArena(), this),
       assembler_(graph->GetArena()),
       isa_features_(isa_features),
-      method_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
@@ -4454,20 +4452,7 @@
           HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) {
     dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
   }
-  switch (dispatch_info.code_ptr_location) {
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
-      // For direct code, we actually prefer to call via the code pointer from ArtMethod*.
-      // (Though the direct CALL ptr16:32 is available for consideration).
-      return HInvokeStaticOrDirect::DispatchInfo {
-        dispatch_info.method_load_kind,
-        HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
-        dispatch_info.method_load_data,
-        0u
-      };
-    default:
-      return dispatch_info;
-  }
+  return dispatch_info;
 }
 
 Register CodeGeneratorX86::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke,
@@ -4514,12 +4499,6 @@
     case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
       __ movl(temp.AsRegister<Register>(), Immediate(invoke->GetMethodAddress()));
       break;
-    case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
-      __ movl(temp.AsRegister<Register>(), Immediate(/* placeholder */ 0));
-      method_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
-                                   invoke->GetTargetMethod().dex_method_index);
-      __ Bind(&method_patches_.back().label);  // Bind the label at the end of the "movl" insn.
-      break;
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
       Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke,
                                                                 temp.AsRegister<Register>());
@@ -4561,19 +4540,6 @@
     case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
       __ call(GetFrameEntryLabel());
       break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: {
-      relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
-                                          invoke->GetTargetMethod().dex_method_index);
-      Label* label = &relative_call_patches_.back().label;
-      __ call(label);  // Bind to the patch label, override at link time.
-      __ Bind(label);  // Bind the label at the end of the "call" insn.
-      break;
-    }
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
-      // Filtered out by GetSupportedInvokeStaticOrDirectDispatch().
-      LOG(FATAL) << "Unsupported";
-      UNREACHABLE();
     case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
       // (callee_method + offset_of_quick_compiled_code)()
       __ call(Address(callee_method.AsRegister<Register>(),
@@ -4664,22 +4630,11 @@
 void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   size_t size =
-      method_patches_.size() +
-      relative_call_patches_.size() +
       pc_relative_dex_cache_patches_.size() +
       simple_patches_.size() +
       string_patches_.size() +
       type_patches_.size();
   linker_patches->reserve(size);
-  for (const PatchInfo<Label>& info : method_patches_) {
-    uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
-    linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset, &info.dex_file, info.index));
-  }
-  for (const PatchInfo<Label>& info : relative_call_patches_) {
-    uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
-    linker_patches->push_back(
-        LinkerPatch::RelativeCodePatch(literal_offset, &info.dex_file, info.index));
-  }
   EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
                                                                linker_patches);
   for (const Label& label : simple_patches_) {
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index c44da97..1af6850 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -613,9 +613,6 @@
   X86Assembler assembler_;
   const X86InstructionSetFeatures& isa_features_;
 
-  // Method patch info. Using ArenaDeque<> which retains element addresses on push/emplace_back().
-  ArenaDeque<PatchInfo<Label>> method_patches_;
-  ArenaDeque<PatchInfo<Label>> relative_call_patches_;
   // PC-relative DexCache access info.
   ArenaDeque<PatchInfo<Label>> pc_relative_dex_cache_patches_;
   // Patch locations for patchoat where the linker doesn't do any other work.
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 7dfc736..903844f 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -960,19 +960,7 @@
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorX86_64::GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
       HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
-  switch (desired_dispatch_info.code_ptr_location) {
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
-      // For direct code, we actually prefer to call via the code pointer from ArtMethod*.
-      return HInvokeStaticOrDirect::DispatchInfo {
-        desired_dispatch_info.method_load_kind,
-        HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
-        desired_dispatch_info.method_load_data,
-        0u
-      };
-    default:
-      return desired_dispatch_info;
-  }
+  return desired_dispatch_info;
 }
 
 Location CodeGeneratorX86_64::GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
@@ -993,12 +981,6 @@
     case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
       __ movq(temp.AsRegister<CpuRegister>(), Immediate(invoke->GetMethodAddress()));
       break;
-    case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
-      __ movl(temp.AsRegister<CpuRegister>(), Immediate(0));  // Placeholder.
-      method_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
-                                   invoke->GetTargetMethod().dex_method_index);
-      __ Bind(&method_patches_.back().label);  // Bind the label at the end of the "movl" insn.
-      break;
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
       __ movq(temp.AsRegister<CpuRegister>(),
               Address::Absolute(kDummy32BitOffset, /* no_rip */ false));
@@ -1042,19 +1024,6 @@
     case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
       __ call(&frame_entry_label_);
       break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: {
-      relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
-                                          invoke->GetTargetMethod().dex_method_index);
-      Label* label = &relative_call_patches_.back().label;
-      __ call(label);  // Bind to the patch label, override at link time.
-      __ Bind(label);  // Bind the label at the end of the "call" insn.
-      break;
-    }
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
-      // Filtered out by GetSupportedInvokeStaticOrDirectDispatch().
-      LOG(FATAL) << "Unsupported";
-      UNREACHABLE();
     case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
       // (callee_method + offset_of_quick_compiled_code)()
       __ call(Address(callee_method.AsRegister<CpuRegister>(),
@@ -1146,22 +1115,11 @@
 void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
   DCHECK(linker_patches->empty());
   size_t size =
-      method_patches_.size() +
-      relative_call_patches_.size() +
       pc_relative_dex_cache_patches_.size() +
       simple_patches_.size() +
       string_patches_.size() +
       type_patches_.size();
   linker_patches->reserve(size);
-  for (const PatchInfo<Label>& info : method_patches_) {
-    uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
-    linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset, &info.dex_file, info.index));
-  }
-  for (const PatchInfo<Label>& info : relative_call_patches_) {
-    uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
-    linker_patches->push_back(
-        LinkerPatch::RelativeCodePatch(literal_offset, &info.dex_file, info.index));
-  }
   EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
                                                                linker_patches);
   for (const Label& label : simple_patches_) {
@@ -1253,8 +1211,6 @@
         assembler_(graph->GetArena()),
         isa_features_(isa_features),
         constant_area_start_(0),
-        method_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-        relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
         pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
         simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
         string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 391a23b..f827e79 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -596,9 +596,6 @@
   // Used for fixups to the constant area.
   int constant_area_start_;
 
-  // Method patch info. Using ArenaDeque<> which retains element addresses on push/emplace_back().
-  ArenaDeque<PatchInfo<Label>> method_patches_;
-  ArenaDeque<PatchInfo<Label>> relative_call_patches_;
   // PC-relative DexCache access info.
   ArenaDeque<PatchInfo<Label>> pc_relative_dex_cache_patches_;
   // Patch locations for patchoat where the linker doesn't do any other work.
diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h
index eabdbad..21c3ae6 100644
--- a/compiler/optimizing/common_arm.h
+++ b/compiler/optimizing/common_arm.h
@@ -122,10 +122,16 @@
   if (type == Primitive::kPrimFloat) {
     return InputSRegisterAt(instr, input_index);
   } else {
+    DCHECK_EQ(type, Primitive::kPrimDouble);
     return InputDRegisterAt(instr, input_index);
   }
 }
 
+inline vixl::aarch32::VRegister InputVRegister(HInstruction* instr) {
+  DCHECK_EQ(instr->InputCount(), 1u);
+  return InputVRegisterAt(instr, 0);
+}
+
 inline vixl::aarch32::Register OutputRegister(HInstruction* instr) {
   return RegisterFrom(instr->GetLocations()->Out(), instr->GetType());
 }
@@ -140,8 +146,7 @@
   return InputRegisterAt(instr, 0);
 }
 
-inline int32_t Int32ConstantFrom(Location location) {
-  HConstant* instr = location.GetConstant();
+inline int32_t Int32ConstantFrom(HInstruction* instr) {
   if (instr->IsIntConstant()) {
     return instr->AsIntConstant()->GetValue();
   } else if (instr->IsNullConstant()) {
@@ -155,6 +160,10 @@
   }
 }
 
+inline int32_t Int32ConstantFrom(Location location) {
+  return Int32ConstantFrom(location.GetConstant());
+}
+
 inline int64_t Int64ConstantFrom(Location location) {
   HConstant* instr = location.GetConstant();
   if (instr->IsIntConstant()) {
@@ -167,6 +176,11 @@
   }
 }
 
+inline uint64_t Uint64ConstantFrom(HInstruction* instr) {
+  DCHECK(instr->IsConstant()) << instr->DebugName();
+  return instr->AsConstant()->GetValueAsUint64();
+}
+
 inline vixl::aarch32::Operand OperandFrom(Location location, Primitive::Type type) {
   if (location.IsRegister()) {
     return vixl::aarch32::Operand(RegisterFrom(location, type));
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 188ee3a..34b52a8 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -20,12 +20,15 @@
 #include <string>
 #include <sstream>
 
+#include "android-base/stringprintf.h"
+
 #include "base/arena_containers.h"
 #include "base/bit_vector-inl.h"
-#include "base/stringprintf.h"
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static bool IsAllowedToJumpToExitBlock(HInstruction* instruction) {
   return instruction->IsThrow() || instruction->IsReturn() || instruction->IsReturnVoid();
 }
diff --git a/compiler/optimizing/graph_test.cc b/compiler/optimizing/graph_test.cc
index d530564..28ee3a5 100644
--- a/compiler/optimizing/graph_test.cc
+++ b/compiler/optimizing/graph_test.cc
@@ -15,7 +15,6 @@
  */
 
 #include "base/arena_allocator.h"
-#include "base/stringprintf.h"
 #include "builder.h"
 #include "nodes.h"
 #include "optimizing_unit_test.h"
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index c240c67..88473f02 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -73,10 +73,18 @@
 }
 
 /**
- * Returns narrowest data type.
+ * Returns result of implicit widening type conversion done in HIR.
  */
-static Primitive::Type Narrowest(Primitive::Type type1, Primitive::Type type2) {
-  return Primitive::ComponentSize(type1) <= Primitive::ComponentSize(type2) ? type1 : type2;
+static Primitive::Type ImplicitConversion(Primitive::Type type) {
+  switch (type) {
+    case Primitive::kPrimShort:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimBoolean:
+      return Primitive::kPrimInt;
+    default:
+      return type;
+  }
 }
 
 //
@@ -211,7 +219,7 @@
 void HInductionVarAnalysis::ClassifyTrivial(HLoopInformation* loop, HInstruction* instruction) {
   InductionInfo* info = nullptr;
   if (instruction->IsPhi()) {
-    info = TransferPhi(loop, instruction, /* input_index */ 0);
+    info = TransferPhi(loop, instruction, /*input_index*/ 0, /*adjust_input_size*/ 0);
   } else if (instruction->IsAdd()) {
     info = TransferAddSub(LookupInfo(loop, instruction->InputAt(0)),
                           LookupInfo(loop, instruction->InputAt(1)), kAdd);
@@ -224,15 +232,17 @@
     info = TransferMul(LookupInfo(loop, instruction->InputAt(0)),
                        LookupInfo(loop, instruction->InputAt(1)));
   } else if (instruction->IsShl()) {
-    HInstruction* mulc = GetMultConstantForShift(loop, instruction);
+    HInstruction* mulc = GetShiftConstant(loop, instruction, /*initial*/ nullptr);
     if (mulc != nullptr) {
       info = TransferMul(LookupInfo(loop, instruction->InputAt(0)),
                          LookupInfo(loop, mulc));
     }
+  } else if (instruction->IsSelect()) {
+    info = TransferPhi(loop, instruction, /*input_index*/ 0, /*adjust_input_size*/ 1);
   } else if (instruction->IsTypeConversion()) {
-    info = TransferCnv(LookupInfo(loop, instruction->InputAt(0)),
-                       instruction->AsTypeConversion()->GetInputType(),
-                       instruction->AsTypeConversion()->GetResultType());
+    info = TransferConversion(LookupInfo(loop, instruction->InputAt(0)),
+                              instruction->AsTypeConversion()->GetInputType(),
+                              instruction->AsTypeConversion()->GetResultType());
   } else if (instruction->IsBoundsCheck()) {
     info = LookupInfo(loop, instruction->InputAt(0));  // Pass-through.
   }
@@ -265,12 +275,16 @@
     return;
   }
 
-  // Store interesting cycle.
-  AssignCycle(phi->AsPhi());
+  // Store interesting cycle in each loop phi.
+  for (size_t i = 0; i < size; i++) {
+    if (scc_[i]->IsLoopHeaderPhi()) {
+      AssignCycle(scc_[i]->AsPhi());
+    }
+  }
 
   // Singleton is wrap-around induction if all internal links have the same meaning.
   if (size == 1) {
-    InductionInfo* update = TransferPhi(loop, phi, /* input_index */ 1);
+    InductionInfo* update = TransferPhi(loop, phi, /*input_index*/ 1, /*adjust_input_size*/ 0);
     if (update != nullptr) {
       AssignInfo(loop, phi, CreateInduction(kWrapAround,
                                             kNop,
@@ -305,10 +319,15 @@
       update = SolveOp(
           loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kRem);
     } else if (instruction->IsShl()) {
-      HInstruction* mulc = GetMultConstantForShift(loop, instruction);
+      HInstruction* mulc = GetShiftConstant(loop, instruction, /*initial*/ nullptr);
       if (mulc != nullptr) {
         update = SolveOp(loop, phi, instruction, instruction->InputAt(0), mulc, kMul);
       }
+    } else if (instruction->IsShr() || instruction->IsUShr()) {
+      HInstruction* divc = GetShiftConstant(loop, instruction, initial);
+      if (divc != nullptr) {
+        update = SolveOp(loop, phi, instruction, instruction->InputAt(0), divc, kDiv);
+      }
     } else if (instruction->IsXor()) {
       update = SolveOp(
           loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kXor);
@@ -316,8 +335,10 @@
       update = SolveTest(loop, phi, instruction, 0);
     } else if (instruction->IsNotEqual()) {
       update = SolveTest(loop, phi, instruction, 1);
+    } else if (instruction->IsSelect()) {
+      update = SolvePhi(instruction, /*input_index*/ 0, /*adjust_input_size*/ 1);  // acts like Phi
     } else if (instruction->IsTypeConversion()) {
-      update = SolveCnv(instruction->AsTypeConversion());
+      update = SolveConversion(loop, phi, instruction->AsTypeConversion());
     }
     if (update == nullptr) {
       return;
@@ -326,7 +347,7 @@
   }
 
   // Success if all internal links received the same temporary meaning.
-  InductionInfo* induction = SolvePhi(phi, /* input_index */ 1);
+  InductionInfo* induction = SolvePhi(phi, /*input_index*/ 1, /*adjust_input_size*/ 0);
   if (induction != nullptr) {
     switch (induction->induction_class) {
       case kInvariant:
@@ -385,12 +406,13 @@
 
 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(HLoopInformation* loop,
                                                                          HInstruction* phi,
-                                                                         size_t input_index) {
+                                                                         size_t input_index,
+                                                                         size_t adjust_input_size) {
   // Match all phi inputs from input_index onwards exactly.
   HInputsRef inputs = phi->GetInputs();
   DCHECK_LT(input_index, inputs.size());
   InductionInfo* a = LookupInfo(loop, inputs[input_index]);
-  for (size_t i = input_index + 1; i < inputs.size(); i++) {
+  for (size_t i = input_index + 1, n = inputs.size() - adjust_input_size; i < n; i++) {
     InductionInfo* b = LookupInfo(loop, inputs[i]);
     if (!InductionEqual(a, b)) {
       return nullptr;
@@ -406,18 +428,20 @@
   // wrap-around, or periodic can be combined with an invariant to yield a similar result.
   // Two linear or two polynomial inputs can be combined too. Other combinations fail.
   if (a != nullptr && b != nullptr) {
-    type_ = Narrowest(type_, Narrowest(a->type, b->type));
-    if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
-      return CreateInvariantOp(op, a, b);
+    if (IsNarrowingLinear(a) || IsNarrowingLinear(b)) {
+      return nullptr;  // no transfer
+    } else if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
+      return CreateInvariantOp(op, a, b);  // direct invariant
     } else if ((a->induction_class == kLinear && b->induction_class == kLinear) ||
                (a->induction_class == kPolynomial && b->induction_class == kPolynomial)) {
-      return CreateInduction(a->induction_class,
-                             a->operation,
-                             TransferAddSub(a->op_a, b->op_a, op),
-                             TransferAddSub(a->op_b, b->op_b, op),
-                             /*fetch*/ nullptr,
-                             type_);
+      // Rule induc(a, b) + induc(a', b') -> induc(a + a', b + b').
+      InductionInfo* new_a = TransferAddSub(a->op_a, b->op_a, op);
+      InductionInfo* new_b = TransferAddSub(a->op_b, b->op_b, op);
+      if (new_a != nullptr && new_b != nullptr)  {
+        return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_);
+      }
     } else if (a->induction_class == kInvariant) {
+      // Rule a + induc(a', b') -> induc(a', a + b') or induc(a + a', a + b').
       InductionInfo* new_a = b->op_a;
       InductionInfo* new_b = TransferAddSub(a, b->op_b, op);
       if (b->induction_class == kWrapAround || b->induction_class == kPeriodic) {
@@ -425,14 +449,19 @@
       } else if (op == kSub) {  // Negation required.
         new_a = TransferNeg(new_a);
       }
-      return CreateInduction(b->induction_class, b->operation, new_a, new_b, b->fetch, type_);
+      if (new_a != nullptr && new_b != nullptr)  {
+        return CreateInduction(b->induction_class, b->operation, new_a, new_b, b->fetch, type_);
+      }
     } else if (b->induction_class == kInvariant) {
+      // Rule induc(a, b) + b' -> induc(a, b + b') or induc(a + b', b + b').
       InductionInfo* new_a = a->op_a;
       InductionInfo* new_b = TransferAddSub(a->op_b, b, op);
       if (a->induction_class == kWrapAround || a->induction_class == kPeriodic) {
         new_a = TransferAddSub(new_a, b, op);
       }
-      return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_);
+      if (new_a != nullptr && new_b != nullptr)  {
+        return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_);
+      }
     }
   }
   return nullptr;
@@ -442,16 +471,17 @@
   // Transfer over a unary negation: an invariant, linear, polynomial, geometric (mul),
   // wrap-around, or periodic input yields a similar but negated induction as result.
   if (a != nullptr) {
-    type_ = Narrowest(type_, a->type);
-    if (a->induction_class == kInvariant) {
-      return CreateInvariantOp(kNeg, nullptr, a);
+    if (IsNarrowingLinear(a)) {
+      return nullptr;  // no transfer
+    } else if (a->induction_class == kInvariant) {
+      return CreateInvariantOp(kNeg, nullptr, a);  // direct invariant
     } else if (a->induction_class != kGeometric || a->operation == kMul) {
-      return CreateInduction(a->induction_class,
-                             a->operation,
-                             TransferNeg(a->op_a),
-                             TransferNeg(a->op_b),
-                             a->fetch,
-                             type_);
+      // Rule - induc(a, b) -> induc(-a, -b).
+      InductionInfo* new_a = TransferNeg(a->op_a);
+      InductionInfo* new_b = TransferNeg(a->op_b);
+      if (new_a != nullptr && new_b != nullptr) {
+        return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_);
+      }
     }
   }
   return nullptr;
@@ -463,54 +493,56 @@
   // wrap-around, or periodic can be multiplied with an invariant to yield a similar
   // but multiplied result. Two non-invariant inputs cannot be multiplied, however.
   if (a != nullptr && b != nullptr) {
-    type_ = Narrowest(type_, Narrowest(a->type, b->type));
-    if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
-      return CreateInvariantOp(kMul, a, b);
+    if (IsNarrowingLinear(a) || IsNarrowingLinear(b)) {
+      return nullptr;  // no transfer
+    } else if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
+      return CreateInvariantOp(kMul, a, b);  // direct invariant
     } else if (a->induction_class == kInvariant && (b->induction_class != kGeometric ||
                                                     b->operation == kMul)) {
-      return CreateInduction(b->induction_class,
-                             b->operation,
-                             TransferMul(a, b->op_a),
-                             TransferMul(a, b->op_b),
-                             b->fetch,
-                             type_);
+      // Rule a * induc(a', b') -> induc(a * a', b * b').
+      InductionInfo* new_a = TransferMul(a, b->op_a);
+      InductionInfo* new_b = TransferMul(a, b->op_b);
+      if (new_a != nullptr && new_b != nullptr) {
+        return CreateInduction(b->induction_class, b->operation, new_a, new_b, b->fetch, type_);
+      }
     } else if (b->induction_class == kInvariant && (a->induction_class != kGeometric ||
                                                     a->operation == kMul)) {
-      return CreateInduction(a->induction_class,
-                             a->operation,
-                             TransferMul(a->op_a, b),
-                             TransferMul(a->op_b, b),
-                             a->fetch,
-                             type_);
-    }
-  }
-  return nullptr;
-}
-
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCnv(InductionInfo* a,
-                                                                         Primitive::Type from,
-                                                                         Primitive::Type to) {
-  if (a != nullptr) {
-    // Allow narrowing conversion on linear induction in certain cases.
-    if (IsNarrowingIntegralConversion(from, to)) {
-      if (a->induction_class == kLinear) {
-        if (a->type == to || (a->type == from && IsNarrowingIntegralConversion(from, to))) {
-          return CreateInduction(kLinear, kNop, a->op_a, a->op_b, /*fetch*/ nullptr, to);
-        }
+      // Rule induc(a, b) * b' -> induc(a * b', b * b').
+      InductionInfo* new_a = TransferMul(a->op_a, b);
+      InductionInfo* new_b = TransferMul(a->op_b, b);
+      if (new_a != nullptr && new_b != nullptr) {
+        return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_);
       }
     }
   }
   return nullptr;
 }
 
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferConversion(
+    InductionInfo* a,
+    Primitive::Type from,
+    Primitive::Type to) {
+  if (a != nullptr) {
+    // Allow narrowing conversion on linear induction in certain cases:
+    // induction is already at narrow type, or can be made narrower.
+    if (IsNarrowingIntegralConversion(from, to) &&
+        a->induction_class == kLinear &&
+        (a->type == to || IsNarrowingIntegralConversion(a->type, to))) {
+      return CreateInduction(kLinear, kNop, a->op_a, a->op_b, a->fetch, to);
+    }
+  }
+  return nullptr;
+}
+
 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhi(HInstruction* phi,
-                                                                      size_t input_index) {
+                                                                      size_t input_index,
+                                                                      size_t adjust_input_size) {
   // Match all phi inputs from input_index onwards exactly.
   HInputsRef inputs = phi->GetInputs();
   DCHECK_LT(input_index, inputs.size());
   auto ita = cycle_.find(inputs[input_index]);
   if (ita != cycle_.end()) {
-    for (size_t i = input_index + 1; i < inputs.size(); i++) {
+    for (size_t i = input_index + 1, n = inputs.size() - adjust_input_size; i < n; i++) {
       auto itb = cycle_.find(inputs[i]);
       if (itb == cycle_.end() ||
           !HInductionVarAnalysis::InductionEqual(ita->second, itb->second)) {
@@ -527,7 +559,7 @@
     HInstruction* entry_phi,
     HInstruction* phi) {
   // Match all phi inputs.
-  InductionInfo* match = SolvePhi(phi, /* input_index */ 0);
+  InductionInfo* match = SolvePhi(phi, /*input_index*/ 0, /*adjust_input_size*/ 0);
   if (match != nullptr) {
     return match;
   }
@@ -542,7 +574,7 @@
         InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
         return CreateInduction(kPeriodic, kNop, a, initial, /*fetch*/ nullptr, type_);
       }
-      InductionInfo* b = SolvePhi(phi, /* input_index */ 1);
+      InductionInfo* b = SolvePhi(phi, /*input_index*/ 1, /*adjust_input_size*/ 0);
       if (b != nullptr && b->induction_class == kPeriodic) {
         return CreateInduction(kPeriodic, kNop, a, b, /*fetch*/ nullptr, type_);
       }
@@ -574,17 +606,15 @@
           return CreateInvariantOp(op, a, b);
         }
       }
-    } else if (op == kAdd && b->induction_class == kLinear) {
+    } else if (b->induction_class == kLinear && b->type == type_) {
       // Solve within a tight cycle that adds a term that is already classified as a linear
       // induction for a polynomial induction k = k + i (represented as sum over linear terms).
       if (x == entry_phi && entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) {
         InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
-        return CreateInduction(kPolynomial,
-                               kNop,
-                               b,
-                               initial,
-                               /*fetch*/ nullptr,
-                               type_);
+        InductionInfo* new_a = op == kAdd ? b : TransferNeg(b);
+        if (new_a != nullptr) {
+          return CreateInduction(kPolynomial, kNop, new_a, initial, /*fetch*/ nullptr, type_);
+        }
       }
     }
   }
@@ -689,16 +719,29 @@
   return nullptr;
 }
 
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveCnv(HTypeConversion* conversion) {
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveConversion(
+    HLoopInformation* loop,
+    HInstruction* entry_phi,
+    HTypeConversion* conversion) {
   Primitive::Type from = conversion->GetInputType();
   Primitive::Type to = conversion->GetResultType();
-  // A narrowing conversion is allowed within the cycle of a linear induction, provided that the
-  // narrowest encountered type is recorded with the induction to account for the precision loss.
-  if (IsNarrowingIntegralConversion(from, to)) {
-    auto it = cycle_.find(conversion->GetInput());
-    if (it != cycle_.end() && it->second->induction_class == kInvariant) {
-      type_ = Narrowest(type_, to);
-      return it->second;
+  // A narrowing conversion is allowed as *last* operation of the cycle of a linear induction
+  // with an initial value that fits the type, provided that the narrowest encountered type is
+  // recorded with the induction to account for the precision loss. The narrower induction does
+  // *not* transfer to any wider operations, however, since these may yield out-of-type values
+  if (entry_phi->InputCount() == 2 && conversion == entry_phi->InputAt(1)) {
+    int64_t min = Primitive::MinValueOfIntegralType(to);
+    int64_t max = Primitive::MaxValueOfIntegralType(to);
+    int64_t value = 0;
+    InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
+    if (IsNarrowingIntegralConversion(from, to) &&
+        IsAtLeast(initial, &value) && value >= min &&
+        IsAtMost(initial, &value)  && value <= max) {
+      auto it = cycle_.find(conversion->GetInput());
+      if (it != cycle_.end() && it->second->induction_class == kInvariant) {
+        type_ = to;
+        return it->second;
+      }
     }
   }
   return nullptr;
@@ -718,7 +761,7 @@
       HCondition* condition = if_expr->AsCondition();
       InductionInfo* a = LookupInfo(loop, condition->InputAt(0));
       InductionInfo* b = LookupInfo(loop, condition->InputAt(1));
-      Primitive::Type type = condition->InputAt(0)->GetType();
+      Primitive::Type type = ImplicitConversion(condition->InputAt(0)->GetType());
       // Determine if the loop control uses a known sequence on an if-exit (X outside) or on
       // an if-iterate (X inside), expressed as if-iterate when passed into VisitCondition().
       if (a == nullptr || b == nullptr) {
@@ -890,8 +933,8 @@
                                      int64_t stride_value,
                                      Primitive::Type type,
                                      IfCondition cmp) {
-  const int64_t min = Primitive::MinValueOfIntegralType(type);
-  const int64_t max = Primitive::MaxValueOfIntegralType(type);
+  int64_t min = Primitive::MinValueOfIntegralType(type);
+  int64_t max = Primitive::MaxValueOfIntegralType(type);
   // Some rules under which it is certain at compile-time that the loop is finite.
   int64_t value;
   switch (cmp) {
@@ -927,8 +970,6 @@
     min++;
   }
   // Do both bounds fit the range?
-  // Note: The `value` is initialized to please valgrind - the compiler can reorder
-  // the return value check with the `value` check, b/27651442 .
   int64_t value = 0;
   return IsAtLeast(lower_expr, &value) && value >= min &&
          IsAtMost(lower_expr, &value)  && value <= max &&
@@ -1035,16 +1076,27 @@
       return CreateSimplifiedInvariant(kSub, b->op_b, b->op_a);
     }
   }
-  return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr, b->type);
+  return new (graph_->GetArena()) InductionInfo(
+      kInvariant, op, a, b, nullptr, ImplicitConversion(b->type));
 }
 
-HInstruction* HInductionVarAnalysis::GetMultConstantForShift(HLoopInformation* loop,
-                                                             HInstruction* instruction) {
-  // Obtain the constant needed to treat shift as equivalent multiplication. This yields an
-  // existing instruction if the constant is already there. Otherwise, this has a side effect
-  // on the HIR. The restriction on the shift factor avoids generating a negative constant
-  // (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that generalization for
-  // shift factors outside [0,32) and [0,64) ranges is done by earlier simplification.
+HInstruction* HInductionVarAnalysis::GetShiftConstant(HLoopInformation* loop,
+                                                      HInstruction* instruction,
+                                                      InductionInfo* initial) {
+  DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr());
+  // Shift-rights are only the same as division for non-negative initial inputs.
+  // Otherwise we would round incorrectly.
+  if (initial != nullptr) {
+    int64_t value = -1;
+    if (!IsAtLeast(initial, &value) || value < 0) {
+      return nullptr;
+    }
+  }
+  // Obtain the constant needed to treat shift as equivalent multiplication or division.
+  // This yields an existing instruction if the constant is already there. Otherwise, this
+  // has a side effect on the HIR. The restriction on the shift factor avoids generating a
+  // negative constant (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that
+  // generalization for shift factors outside [0,32) and [0,64) ranges is done earlier.
   InductionInfo* b = LookupInfo(loop, instruction->InputAt(1));
   int64_t value = -1;
   if (IsExact(b, &value)) {
@@ -1087,6 +1139,16 @@
   return InductionVarRange(this).IsConstant(info, InductionVarRange::kAtLeast, value);
 }
 
+bool HInductionVarAnalysis::IsNarrowingLinear(InductionInfo* info) {
+  return info != nullptr &&
+      info->induction_class == kLinear &&
+      (info->type == Primitive::kPrimByte ||
+       info->type == Primitive::kPrimShort ||
+       info->type == Primitive::kPrimChar ||
+       (info->type == Primitive::kPrimInt && (info->op_a->type == Primitive::kPrimLong ||
+                                              info->op_b->type == Primitive::kPrimLong)));
+}
+
 bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1,
                                            InductionInfo* info2) {
   // Test structural equality only, without accounting for simplifications.
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 4720f2d..39b39cd 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -115,7 +115,7 @@
     InductionInfo* op_a;
     InductionInfo* op_b;
     HInstruction* fetch;
-    Primitive::Type type;  // precision of induction
+    Primitive::Type type;  // precision of operation
   };
 
   bool IsVisitedNode(HInstruction* instruction) const {
@@ -160,14 +160,17 @@
   InductionInfo* RotatePeriodicInduction(InductionInfo* induction, InductionInfo* last);
 
   // Transfer operations.
-  InductionInfo* TransferPhi(HLoopInformation* loop, HInstruction* phi, size_t input_index);
+  InductionInfo* TransferPhi(HLoopInformation* loop,
+                             HInstruction* phi,
+                             size_t input_index,
+                             size_t adjust_input_size);
   InductionInfo* TransferAddSub(InductionInfo* a, InductionInfo* b, InductionOp op);
   InductionInfo* TransferNeg(InductionInfo* a);
   InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b);
-  InductionInfo* TransferCnv(InductionInfo* a, Primitive::Type from, Primitive::Type to);
+  InductionInfo* TransferConversion(InductionInfo* a, Primitive::Type from, Primitive::Type to);
 
   // Solvers.
-  InductionInfo* SolvePhi(HInstruction* phi, size_t input_index);
+  InductionInfo* SolvePhi(HInstruction* phi, size_t input_index, size_t adjust_input_size);
   InductionInfo* SolvePhiAllInputs(HLoopInformation* loop,
                                    HInstruction* entry_phi,
                                    HInstruction* phi);
@@ -188,7 +191,9 @@
                            HInstruction* entry_phi,
                            HInstruction* instruction,
                            int64_t oppositive_value);
-  InductionInfo* SolveCnv(HTypeConversion* conversion);
+  InductionInfo* SolveConversion(HLoopInformation* loop,
+                                 HInstruction* entry_phi,
+                                 HTypeConversion* conversion);
 
   // Trip count information.
   void VisitControl(HLoopInformation* loop);
@@ -220,7 +225,9 @@
   InductionInfo* LookupInfo(HLoopInformation* loop, HInstruction* instruction);
   InductionInfo* CreateConstant(int64_t value, Primitive::Type type);
   InductionInfo* CreateSimplifiedInvariant(InductionOp op, InductionInfo* a, InductionInfo* b);
-  HInstruction* GetMultConstantForShift(HLoopInformation* loop, HInstruction* instruction);
+  HInstruction* GetShiftConstant(HLoopInformation* loop,
+                                 HInstruction* instruction,
+                                 InductionInfo* initial);
   void AssignCycle(HPhi* phi);
   ArenaSet<HInstruction*>* LookupCycle(HPhi* phi);
 
@@ -230,6 +237,7 @@
   bool IsAtLeast(InductionInfo* info, /*out*/ int64_t* value);
 
   // Helpers.
+  static bool IsNarrowingLinear(InductionInfo* info);
   static bool InductionEqual(InductionInfo* info1, InductionInfo* info2);
   static std::string FetchToString(HInstruction* fetch);
   static std::string InductionToString(InductionInfo* info);
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 2d182f6..82ee93d 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -87,6 +87,7 @@
     constant2_ = graph_->GetIntConstant(2);
     constant7_ = graph_->GetIntConstant(7);
     constant100_ = graph_->GetIntConstant(100);
+    constantm1_ = graph_->GetIntConstant(-1);
     float_constant0_ = graph_->GetFloatConstant(0.0f);
     return_->AddInstruction(new (&allocator_) HReturnVoid());
     exit_->AddInstruction(new (&allocator_) HExit());
@@ -173,6 +174,12 @@
       iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction2));
   }
 
+  // Returns true for narrowing linear induction.
+  bool IsNarrowingLinear(HInstruction* instruction) {
+    return HInductionVarAnalysis::IsNarrowingLinear(
+        iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction));
+  }
+
   // Performs InductionVarAnalysis (after proper set up).
   void PerformInductionVarAnalysis() {
     graph_->BuildDominatorTree();
@@ -196,6 +203,7 @@
   HInstruction* constant2_;
   HInstruction* constant7_;
   HInstruction* constant100_;
+  HInstruction* constantm1_;
   HInstruction* float_constant0_;
 
   // Loop specifics.
@@ -612,6 +620,45 @@
   EXPECT_STREQ("", GetInductionInfo(div, 0).c_str());
 }
 
+TEST_F(InductionVarAnalysisTest, FindGeometricShrInduction) {
+  // Setup:
+  // k = 100;
+  // for (int i = 0; i < 100; i++) {
+  //   k = k >> 1;  // geometric (/ 2)
+  // }
+  BuildLoopNest(1);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant100_);
+
+  HInstruction* shr = InsertInstruction(
+      new (&allocator_) HShr(Primitive::kPrimInt, k_header, constant1_), 0);
+  k_header->AddInput(shr);
+  PerformInductionVarAnalysis();
+
+  // Note, only the phi in the cycle is classified.
+  EXPECT_STREQ("geo((100) * 2 ^ -i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(shr, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindNotGeometricShrInduction) {
+  // Setup:
+  // k = -1;
+  // for (int i = 0; i < 100; i++) {
+  //   k = k >> 1;  // initial value is negative
+  // }
+  BuildLoopNest(1);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constantm1_);
+
+  HInstruction* shr = InsertInstruction(
+      new (&allocator_) HShr(Primitive::kPrimInt, k_header, constant1_), 0);
+  k_header->AddInput(shr);
+  PerformInductionVarAnalysis();
+
+  EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(shr, 0).c_str());
+}
+
 TEST_F(InductionVarAnalysisTest, FindRemWrapAroundInductionAndDerived) {
   // Setup:
   // k = 100;
@@ -1025,16 +1072,20 @@
   // }
   BuildLoopNest(1);
   HInstruction* conv = InsertInstruction(
-      new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0);
+      new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], kNoDexPc), 0);
   HInstruction* store1 = InsertArrayStore(conv, 0);
   HInstruction* store2 = InsertArrayStore(basic_[0], 0);
   PerformInductionVarAnalysis();
 
-  // Regular int induction (i) is "transferred" over conversion into byte induction (k).
+  // Regular int induction (i) is transferred over conversion into byte induction (k).
   EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
   EXPECT_STREQ("((1) * i + (0)):PrimInt",  GetInductionInfo(store2->InputAt(1), 0).c_str());
   EXPECT_STREQ("((1) * i + (1)):PrimInt",  GetInductionInfo(increment_[0], 0).c_str());
 
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(store1->InputAt(1)));
+  EXPECT_FALSE(IsNarrowingLinear(store2->InputAt(1)));
+
   // Type matters!
   EXPECT_FALSE(HaveSameInduction(store1->InputAt(1), store2->InputAt(1)));
 
@@ -1052,7 +1103,7 @@
   // }
   BuildLoopNest(1);
   HInstruction* conv = InsertInstruction(
-      new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0);
+      new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], kNoDexPc), 0);
   HInstruction* store1 = InsertArrayStore(conv, 0);
   HInstruction* add = InsertInstruction(
       new (&allocator_) HAdd(Primitive::kPrimInt, conv, constant1_), 0);
@@ -1060,11 +1111,86 @@
 
   PerformInductionVarAnalysis();
 
-  // Byte induction (k) is "transferred" over conversion into addition (k + 1).
-  // This means only values within byte range can be trusted (even though
-  // addition can jump out of the range of course).
+  // Byte induction (k) is detected, but it does not transfer over the addition,
+  // since this may yield out-of-type values.
   EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
-  EXPECT_STREQ("((1) * i + (1)):PrimByte", GetInductionInfo(store2->InputAt(1), 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(store2->InputAt(1), 0).c_str());
+
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(store1->InputAt(1)));
+  EXPECT_FALSE(IsNarrowingLinear(store2->InputAt(1)));  // works for null
+}
+
+TEST_F(InductionVarAnalysisTest, ByteInduction) {
+  // Setup:
+  // k = -128;
+  // for (int i = 0; i < 100; i++) {
+  //   k = k + 1;
+  //   k = (byte) k;
+  // }
+  BuildLoopNest(1);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(graph_->GetIntConstant(-128));
+
+  HInstruction* add = InsertInstruction(
+      new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0);
+  HInstruction* conv = InsertInstruction(
+      new (&allocator_) HTypeConversion(Primitive::kPrimByte, add, kNoDexPc), 0);
+  k_header->AddInput(conv);
+  PerformInductionVarAnalysis();
+
+  // Byte induction (k) is detected, but it does not transfer over the addition,
+  // since this may yield out-of-type values.
+  EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(k_header, 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(add, 0).c_str());
+
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(k_header));
+  EXPECT_FALSE(IsNarrowingLinear(add));  // works for null
+}
+
+TEST_F(InductionVarAnalysisTest, NoByteInduction1) {
+  // Setup:
+  // k = -129;  / does not fit!
+  // for (int i = 0; i < 100; i++) {
+  //   k = k + 1;
+  //   k = (byte) k;
+  // }
+  BuildLoopNest(1);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(graph_->GetIntConstant(-129));
+
+  HInstruction* add = InsertInstruction(
+      new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0);
+  HInstruction* conv = InsertInstruction(
+      new (&allocator_) HTypeConversion(Primitive::kPrimByte, add, kNoDexPc), 0);
+  k_header->AddInput(conv);
+  PerformInductionVarAnalysis();
+
+  EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(add, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, NoByteInduction2) {
+  // Setup:
+  // k = 0;
+  // for (int i = 0; i < 100; i++) {
+  //   k = (byte) k;   // conversion not done last!
+  //   k = k + 1;
+  // }
+  BuildLoopNest(1);
+  HPhi* k_header = InsertLoopPhi(0, 0);
+  k_header->AddInput(constant0_);
+
+  HInstruction* conv = InsertInstruction(
+      new (&allocator_) HTypeConversion(Primitive::kPrimByte, k_header, kNoDexPc), 0);
+  HInstruction* add = InsertInstruction(
+      new (&allocator_) HAdd(Primitive::kPrimInt, conv, constant1_), 0);
+  k_header->AddInput(add);
+  PerformInductionVarAnalysis();
+
+  EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(add, 0).c_str());
 }
 
 TEST_F(InductionVarAnalysisTest, ByteLoopControl1) {
@@ -1075,12 +1201,20 @@
   basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
   HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
   ifs->ReplaceInput(graph_->GetIntConstant(127), 1);
-  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
+  HInstruction* conv =
+      new (&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], kNoDexPc);
   loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
   basic_[0]->ReplaceInput(conv, 1);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
+  // Recorded at the phi, but not transferred to increment.
+  EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(basic_[0], 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+  EXPECT_FALSE(IsNarrowingLinear(increment_[0]));  // works for null
+
   // Trip-count.
   EXPECT_STREQ("(((127) - (-128)) (TC-loop) ((-128) < (127)))", GetTripCount(0).c_str());
 }
@@ -1093,12 +1227,20 @@
   basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
   HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
   ifs->ReplaceInput(graph_->GetIntConstant(128), 1);
-  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
+  HInstruction* conv =
+      new (&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], kNoDexPc);
   loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
   basic_[0]->ReplaceInput(conv, 1);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
+  // Recorded at the phi, but not transferred to increment.
+  EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(basic_[0], 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+  EXPECT_FALSE(IsNarrowingLinear(increment_[0]));  // works for null
+
   // Trip-count undefined.
   EXPECT_STREQ("", GetTripCount(0).c_str());
 }
@@ -1111,13 +1253,20 @@
   basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
   HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
   ifs->ReplaceInput(graph_->GetIntConstant(32767), 1);
-  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
+  HInstruction* conv =
+      new (&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], kNoDexPc);
   loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
   basic_[0]->ReplaceInput(conv, 1);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
-               GetInductionInfo(increment_[0], 0).c_str());
+  // Recorded at the phi, but not transferred to increment.
+  EXPECT_STREQ("((1) * i + (-32768)):PrimShort", GetInductionInfo(basic_[0], 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+  EXPECT_FALSE(IsNarrowingLinear(increment_[0]));  // works for null
+
   // Trip-count.
   EXPECT_STREQ("(((32767) - (-32768)) (TC-loop) ((-32768) < (32767)))", GetTripCount(0).c_str());
 }
@@ -1130,13 +1279,20 @@
   basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
   HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
   ifs->ReplaceInput(graph_->GetIntConstant(32768), 1);
-  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
+  HInstruction* conv =
+      new (&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], kNoDexPc);
   loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
   basic_[0]->ReplaceInput(conv, 1);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
-               GetInductionInfo(increment_[0], 0).c_str());
+  // Recorded at the phi, but not transferred to increment.
+  EXPECT_STREQ("((1) * i + (-32768)):PrimShort", GetInductionInfo(basic_[0], 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+  EXPECT_FALSE(IsNarrowingLinear(increment_[0]));  // works for null
+
   // Trip-count undefined.
   EXPECT_STREQ("", GetTripCount(0).c_str());
 }
@@ -1148,12 +1304,20 @@
   BuildLoopNest(1);
   HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
   ifs->ReplaceInput(graph_->GetIntConstant(65535), 1);
-  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
+  HInstruction* conv =
+      new (&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], kNoDexPc);
   loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
   basic_[0]->ReplaceInput(conv, 1);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
+  // Recorded at the phi, but not transferred to increment.
+  EXPECT_STREQ("((1) * i + (0)):PrimChar", GetInductionInfo(basic_[0], 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+  EXPECT_FALSE(IsNarrowingLinear(increment_[0]));  // works for null
+
   // Trip-count.
   EXPECT_STREQ("((65535) (TC-loop) ((0) < (65535)))", GetTripCount(0).c_str());
 }
@@ -1165,12 +1329,20 @@
   BuildLoopNest(1);
   HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
   ifs->ReplaceInput(graph_->GetIntConstant(65536), 1);
-  HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
+  HInstruction* conv =
+      new (&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], kNoDexPc);
   loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
   basic_[0]->ReplaceInput(conv, 1);
   PerformInductionVarAnalysis();
 
-  EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
+  // Recorded at the phi, but not transferred to increment.
+  EXPECT_STREQ("((1) * i + (0)):PrimChar", GetInductionInfo(basic_[0], 0).c_str());
+  EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+  // Narrowing detected.
+  EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+  EXPECT_FALSE(IsNarrowingLinear(increment_[0]));  // works for null
+
   // Trip-count undefined.
   EXPECT_STREQ("", GetTripCount(0).c_str());
 }
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index e665551..d5c4c2f 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -169,8 +169,8 @@
     case Primitive::kPrimByte: {
       // Constants within range only.
       // TODO: maybe some room for improvement, like allowing widening conversions
-      const int32_t min = Primitive::MinValueOfIntegralType(type);
-      const int32_t max = Primitive::MaxValueOfIntegralType(type);
+      int32_t min = Primitive::MinValueOfIntegralType(type);
+      int32_t max = Primitive::MaxValueOfIntegralType(type);
       return (IsConstantValue(v) && min <= v.b_constant && v.b_constant <= max)
           ? v
           : InductionVarRange::Value();
@@ -551,7 +551,7 @@
   int64_t b = 0;
   if (IsConstant(info->op_a->op_a, kExact, &a) && CanLongValueFitIntoInt(a) && a >= 0 &&
       IsConstant(info->op_a->op_b, kExact, &b) && CanLongValueFitIntoInt(b) && b >= 0) {
-    // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for known
+    // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for
     // maximum index value m as a * (m * (m-1)) / 2 + b * m + c.
     Value c = GetVal(info->op_b, trip, in_body, is_min);
     if (is_min) {
@@ -629,6 +629,7 @@
     }
   } else if (instruction->IsTypeConversion()) {
     // Since analysis is 32-bit (or narrower), chase beyond widening along the path.
+    // For example, this discovers the length in: for (long i = 0; i < a.length; i++);
     if (instruction->AsTypeConversion()->GetInputType() == Primitive::kPrimInt &&
         instruction->AsTypeConversion()->GetResultType() == Primitive::kPrimLong) {
       return GetFetch(instruction->InputAt(0), trip, in_body, is_min);
@@ -843,7 +844,7 @@
 
 InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const {
   if (v1.is_known && v2.is_known && IsSafeAdd(v1.b_constant, v2.b_constant)) {
-    const int32_t b = v1.b_constant + v2.b_constant;
+    int32_t b = v1.b_constant + v2.b_constant;
     if (v1.a_constant == 0) {
       return Value(v2.instruction, v2.a_constant, b);
     } else if (v2.a_constant == 0) {
@@ -857,7 +858,7 @@
 
 InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2) const {
   if (v1.is_known && v2.is_known && IsSafeSub(v1.b_constant, v2.b_constant)) {
-    const int32_t b = v1.b_constant - v2.b_constant;
+    int32_t b = v1.b_constant - v2.b_constant;
     if (v1.a_constant == 0 && IsSafeSub(0, v2.a_constant)) {
       return Value(v2.instruction, -v2.a_constant, b);
     } else if (v2.a_constant == 0) {
@@ -983,18 +984,21 @@
   int64_t a = 0;
   int64_t b = 0;
   int64_t m = 0;
-  if (IsConstant(info->op_a->op_a, kExact, &a) && a >= 0 &&
-      IsConstant(info->op_a->op_b, kExact, &b) && b >= 0 &&
+  if (IsConstant(info->op_a->op_a, kExact, &a) &&
+      IsConstant(info->op_a->op_b, kExact, &b) &&
       IsConstant(trip->op_a, kExact, &m) && m >= 1) {
-    // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for known
+    // Evaluate bounds on sum_i=0^m-1(a * i + b) + c for known
     // maximum index value m as a * (m * (m-1)) / 2 + b * m + c.
-    // TODO: generalize
-    HInstruction* c_instr = nullptr;
-    if (GenerateCode(info->op_b, nullptr, graph, block, graph ? &c_instr : nullptr, false, false)) {
+    HInstruction* c = nullptr;
+    if (GenerateCode(info->op_b, nullptr, graph, block, graph ? &c : nullptr, false, false)) {
       if (graph != nullptr) {
+        Primitive::Type type = info->type;
         int64_t sum = a * ((m * (m - 1)) / 2) + b * m;
-        *result = Insert(block, new (graph->GetArena()) HAdd(info->type,
-                                                             graph->GetIntConstant(sum), c_instr));
+        if (type != Primitive::kPrimLong) {
+          sum = static_cast<int32_t>(sum);  // okay to truncate
+        }
+        *result =
+            Insert(block, new (graph->GetArena()) HAdd(type, graph->GetConstant(type, sum), c));
       }
       return true;
     }
@@ -1011,35 +1015,33 @@
   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kGeometric);
   // Detect known base and trip count (always taken).
   int64_t f = 0;
-  int64_t t = 0;
-  if (IsIntAndGet(info->fetch, &f) && f >= 1 && IsConstant(trip->op_a, kExact, &t) && t >= 1) {
+  int64_t m = 0;
+  if (IsIntAndGet(info->fetch, &f) && f >= 1 && IsConstant(trip->op_a, kExact, &m) && m >= 1) {
     HInstruction* opa = nullptr;
     HInstruction* opb = nullptr;
     if (GenerateCode(info->op_a, nullptr, graph, block, &opa, false, false) &&
         GenerateCode(info->op_b, nullptr, graph, block, &opb, false, false)) {
-      // Compute f ^ t.
-      int64_t fpowt = IntPow(f, t);
+      // Compute f ^ m for known maximum index value m.
+      int64_t fpow = IntPow(f, m);
       if (graph != nullptr) {
-        DCHECK(info->type == Primitive::kPrimInt);  // due to codegen, generalize?
-        if (fpowt == 0) {
+        DCHECK(info->operation == HInductionVarAnalysis::kMul ||
+               info->operation == HInductionVarAnalysis::kDiv);
+        Primitive::Type type = info->type;
+        if (fpow == 0) {
           // Special case: repeated mul/div always yields zero.
-          *result = graph->GetIntConstant(0);
-        } else if (info->operation == HInductionVarAnalysis::kMul) {
-          // Last value multiplication: a * f ^ t + b.
-          HInstruction* mul = Insert(block,
-                                     new (graph->GetArena()) HMul(info->type,
-                                                                  opa,
-                                                                  graph->GetIntConstant(fpowt)));
-          *result = Insert(block, new (graph->GetArena()) HAdd(info->type, mul, opb));
+          *result = graph->GetConstant(type, 0);
         } else {
-          // Last value multiplication: a * f ^ -t + b.
-          DCHECK_EQ(info->operation, HInductionVarAnalysis::kDiv);
-          HInstruction* div = Insert(block,
-                                     new (graph->GetArena()) HDiv(info->type,
-                                                                  opa,
-                                                                  graph->GetIntConstant(fpowt),
-                                                                  kNoDexPc));
-          *result = Insert(block, new (graph->GetArena()) HAdd(info->type, div, opb));
+          // Last value: a * f ^ m + b or a * f ^ -m + b.
+          if (type != Primitive::kPrimLong) {
+            fpow = static_cast<int32_t>(fpow);  // okay to truncate
+          }
+          HInstruction* e = nullptr;
+          if (info->operation == HInductionVarAnalysis::kMul) {
+            e = new (graph->GetArena()) HMul(type, opa, graph->GetConstant(type, fpow));
+          } else {
+            e = new (graph->GetArena()) HDiv(type, opa, graph->GetConstant(type, fpow), kNoDexPc);
+          }
+          *result = Insert(block, new (graph->GetArena()) HAdd(type, Insert(block, e), opb));
         }
       }
       return true;
@@ -1060,12 +1062,11 @@
   for (; info->induction_class == HInductionVarAnalysis::kWrapAround;
        info = info->op_b, ++depth) {}
   // Handle wrap(x, wrap(.., y)) if trip count reaches an invariant at end.
-  // TODO: generalize
-  int64_t t = 0;
+  // TODO: generalize, but be careful to adjust the terminal.
+  int64_t m = 0;
   if (info->induction_class == HInductionVarAnalysis::kInvariant &&
-      IsConstant(trip->op_a, kExact, &t) && t >= depth &&
-      GenerateCode(info, nullptr, graph, block, result, false, false)) {
-    return true;
+      IsConstant(trip->op_a, kExact, &m) && m >= depth) {
+    return GenerateCode(info, nullptr, graph, block, result, false, false);
   }
   return false;
 }
@@ -1079,43 +1080,49 @@
   DCHECK(info != nullptr);
   DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPeriodic);
   // Count period.
-  int32_t period = 1;
+  int64_t period = 1;
   for (HInductionVarAnalysis::InductionInfo* p = info;
        p->induction_class == HInductionVarAnalysis::kPeriodic;
        p = p->op_b, ++period) {}
-  // Handle periodic(x, y) case for restricted types.
-  // TODO: generalize
-  if (period != 2 ||
-      trip->op_a->type != Primitive::kPrimInt ||
-      (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean)) {
-    return false;
+  // Handle any periodic(x, periodic(.., y)) for known maximum index value m.
+  int64_t m = 0;
+  if (IsConstant(trip->op_a, kExact, &m) && m >= 1) {
+    int64_t li = m % period;
+    for (int64_t i = 0; i < li; info = info->op_b, i++) {}
+    if (info->induction_class == HInductionVarAnalysis::kPeriodic) {
+      info = info->op_a;
+    }
+    return GenerateCode(info, nullptr, graph, block, result, false, false);
   }
-  HInstruction* x_instr = nullptr;
-  HInstruction* y_instr = nullptr;
-  HInstruction* trip_expr = nullptr;
-  if (GenerateCode(info->op_a, nullptr, graph, block, graph ? &x_instr   : nullptr, false, false) &&
-      GenerateCode(info->op_b, nullptr, graph, block, graph ? &y_instr   : nullptr, false, false) &&
-      GenerateCode(trip->op_a, nullptr, graph, block, graph ? &trip_expr : nullptr, false, false)) {
-    // During actual code generation (graph != nullptr),
-    // generate is_even ? x : y select instruction.
+  // Handle periodic(x, y) using even/odd-select on trip count. Enter trip count expression
+  // directly to obtain the maximum index value t even if taken test is needed.
+  HInstruction* x = nullptr;
+  HInstruction* y = nullptr;
+  HInstruction* t = nullptr;
+  if (period == 2 &&
+      GenerateCode(info->op_a, nullptr, graph, block, graph ? &x : nullptr, false, false) &&
+      GenerateCode(info->op_b, nullptr, graph, block, graph ? &y : nullptr, false, false) &&
+      GenerateCode(trip->op_a, nullptr, graph, block, graph ? &t : nullptr, false, false)) {
+    // During actual code generation (graph != nullptr), generate is_even ? x : y.
     if (graph != nullptr) {
-      HInstruction* is_even = Insert(block, new (graph->GetArena()) HEqual(
-          Insert(block, new (graph->GetArena()) HAnd(
-              Primitive::kPrimInt, trip_expr, graph->GetIntConstant(1))),
-          graph->GetIntConstant(0), kNoDexPc));
-      *result = Insert(block, new (graph->GetArena()) HSelect(is_even, x_instr, y_instr, kNoDexPc));
+      Primitive::Type type = trip->type;
+      HInstruction* msk =
+          Insert(block, new (graph->GetArena()) HAnd(type, t, graph->GetConstant(type, 1)));
+      HInstruction* is_even =
+          Insert(block, new (graph->GetArena()) HEqual(msk, graph->GetConstant(type, 0), kNoDexPc));
+      *result = Insert(block, new (graph->GetArena()) HSelect(is_even, x, y, kNoDexPc));
     }
     // Guard select with taken test if needed.
     if (*needs_taken_test) {
-      HInstruction* taken_test = nullptr;
-      if (!GenerateCode(
-          trip->op_b, nullptr, graph, block, graph ? &taken_test : nullptr, false, false)) {
+      HInstruction* is_taken = nullptr;
+      if (GenerateCode(trip->op_b, nullptr, graph, block, graph ? &is_taken : nullptr, false, false)) {
+        if (graph != nullptr) {
+          *result = Insert(block, new (graph->GetArena()) HSelect(is_taken, *result, x, kNoDexPc));
+        }
+        *needs_taken_test = false;  // taken care of
+      } else {
         return false;
-      } else if (graph != nullptr) {
-         *result = Insert(block,
-                          new (graph->GetArena()) HSelect(taken_test, *result, x_instr, kNoDexPc));
       }
-      *needs_taken_test = false;  // taken care of
     }
     return true;
   }
@@ -1134,13 +1141,8 @@
     if (graph != nullptr && result == nullptr) {
       return true;
     }
-    // Verify type safety.
-    // TODO: generalize
-    Primitive::Type type = Primitive::kPrimInt;
-    if (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean) {
-      return false;
-    }
     // Handle current operation.
+    Primitive::Type type = info->type;
     HInstruction* opa = nullptr;
     HInstruction* opb = nullptr;
     switch (info->induction_class) {
@@ -1214,15 +1216,15 @@
           case HInductionVarAnalysis::kTripCountInBodyUnsafe:
             if (is_min) {
               if (graph != nullptr) {
-                *result = graph->GetIntConstant(0);
+                *result = graph->GetConstant(type, 0);
               }
               return true;
             } else if (in_body) {
               if (GenerateCode(info->op_a, trip, graph, block, &opb, in_body, is_min)) {
                 if (graph != nullptr) {
-                  *result = Insert(block,
-                                   new (graph->GetArena())
-                                       HSub(type, opb, graph->GetIntConstant(1)));
+                  *result =
+                      Insert(block,
+                             new (graph->GetArena()) HSub(type, opb, graph->GetConstant(type, 1)));
                 }
                 return true;
               }
@@ -1236,26 +1238,31 @@
         // Linear induction a * i + b, for normalized 0 <= i < TC. For ranges, this should
         // be restricted to a unit stride to avoid arithmetic wrap-around situations that
         // are harder to guard against. For a last value, requesting min/max based on any
-        // stride yields right value.
-        int64_t stride_value = 0;
-        if (IsConstant(info->op_a, kExact, &stride_value)) {
-          const bool is_min_a = stride_value >= 0 ? is_min : !is_min;
-          if (GenerateCode(trip,       trip, graph, block, &opa, in_body, is_min_a) &&
-              GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) {
-            if (graph != nullptr) {
-              HInstruction* oper;
-              if (stride_value == 1) {
-                oper = new (graph->GetArena()) HAdd(type, opa, opb);
-              } else if (stride_value == -1) {
-                oper = new (graph->GetArena()) HSub(type, opb, opa);
-              } else {
-                HInstruction* mul = new (graph->GetArena()) HMul(
-                    type, graph->GetIntConstant(stride_value), opa);
-                oper = new (graph->GetArena()) HAdd(type, Insert(block, mul), opb);
+        // known stride yields right value. Always avoid any narrowing linear induction or
+        // any type mismatch between the linear induction and the trip count expression.
+        // TODO: careful runtime type conversions could generalize this latter restriction.
+        if (!HInductionVarAnalysis::IsNarrowingLinear(info) && trip->type == type) {
+          int64_t stride_value = 0;
+          if (IsConstant(info->op_a, kExact, &stride_value) &&
+              CanLongValueFitIntoInt(stride_value)) {
+            const bool is_min_a = stride_value >= 0 ? is_min : !is_min;
+            if (GenerateCode(trip,       trip, graph, block, &opa, in_body, is_min_a) &&
+                GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) {
+              if (graph != nullptr) {
+                HInstruction* oper;
+                if (stride_value == 1) {
+                  oper = new (graph->GetArena()) HAdd(type, opa, opb);
+                } else if (stride_value == -1) {
+                  oper = new (graph->GetArena()) HSub(type, opb, opa);
+                } else {
+                  HInstruction* mul =
+                      new (graph->GetArena()) HMul(type, graph->GetConstant(type, stride_value), opa);
+                  oper = new (graph->GetArena()) HAdd(type, Insert(block, mul), opb);
+                }
+                *result = Insert(block, oper);
               }
-              *result = Insert(block, oper);
+              return true;
             }
-            return true;
           }
         }
         break;
@@ -1270,7 +1277,7 @@
         Value extreme = GetVal(info, trip, in_body, is_min);
         if (IsConstantValue(extreme)) {
           if (graph != nullptr) {
-            *result = graph->GetIntConstant(extreme.b_constant);
+            *result = graph->GetConstant(type, extreme.b_constant);
           }
           return true;
         }
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index fe4662a..3b83e95 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -344,6 +344,7 @@
   if (actual_method != nullptr) {
     bool result = TryInlineAndReplace(invoke_instruction,
                                       actual_method,
+                                      ReferenceTypeInfo::CreateInvalid(),
                                       /* do_rtp */ true,
                                       cha_devirtualize);
     if (result && !invoke_instruction->IsInvokeStaticOrDirect()) {
@@ -471,22 +472,21 @@
   HInstruction* receiver = invoke_instruction->InputAt(0);
   HInstruction* cursor = invoke_instruction->GetPrevious();
   HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
-
+  Handle<mirror::Class> handle = handles_->NewHandle(GetMonomorphicType(classes));
   if (!TryInlineAndReplace(invoke_instruction,
                            resolved_method,
+                           ReferenceTypeInfo::Create(handle, /* is_exact */ true),
                            /* do_rtp */ false,
                            /* cha_devirtualize */ false)) {
     return false;
   }
 
   // We successfully inlined, now add a guard.
-  bool is_referrer =
-      (GetMonomorphicType(classes) == outermost_graph_->GetArtMethod()->GetDeclaringClass());
   AddTypeGuard(receiver,
                cursor,
                bb_cursor,
                class_index,
-               is_referrer,
+               GetMonomorphicType(classes),
                invoke_instruction,
                /* with_deoptimization */ true);
 
@@ -506,52 +506,62 @@
                            uint32_t dex_pc,
                            HInstruction* cursor,
                            HBasicBlock* bb_cursor) {
-  HInstruction* deopt_flag = new (graph_->GetArena()) HShouldDeoptimizeFlag(dex_pc);
-  HInstruction* should_deopt = new (graph_->GetArena()) HNotEqual(
+  HShouldDeoptimizeFlag* deopt_flag = new (graph_->GetArena())
+      HShouldDeoptimizeFlag(graph_->GetArena(), dex_pc);
+  HInstruction* compare = new (graph_->GetArena()) HNotEqual(
       deopt_flag, graph_->GetIntConstant(0, dex_pc));
-  HInstruction* deopt = new (graph_->GetArena()) HDeoptimize(should_deopt, dex_pc);
+  HInstruction* deopt = new (graph_->GetArena()) HDeoptimize(compare, dex_pc);
 
   if (cursor != nullptr) {
     bb_cursor->InsertInstructionAfter(deopt_flag, cursor);
   } else {
     bb_cursor->InsertInstructionBefore(deopt_flag, bb_cursor->GetFirstInstruction());
   }
-  bb_cursor->InsertInstructionAfter(should_deopt, deopt_flag);
-  bb_cursor->InsertInstructionAfter(deopt, should_deopt);
+  bb_cursor->InsertInstructionAfter(compare, deopt_flag);
+  bb_cursor->InsertInstructionAfter(deopt, compare);
+
+  // Add receiver as input to aid CHA guard optimization later.
+  deopt_flag->AddInput(invoke_instruction->InputAt(0));
+  DCHECK_EQ(deopt_flag->InputCount(), 1u);
   deopt->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+  outermost_graph_->IncrementNumberOfCHAGuards();
 }
 
 HInstruction* HInliner::AddTypeGuard(HInstruction* receiver,
                                      HInstruction* cursor,
                                      HBasicBlock* bb_cursor,
                                      dex::TypeIndex class_index,
-                                     bool is_referrer,
+                                     mirror::Class* klass,
                                      HInstruction* invoke_instruction,
                                      bool with_deoptimization) {
+  ScopedAssertNoThreadSuspension sants("Adding compiler type guard");
+
   ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
   HInstanceFieldGet* receiver_class = BuildGetReceiverClass(
       class_linker, receiver, invoke_instruction->GetDexPc());
-
-  const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
-  // Note that we will just compare the classes, so we don't need Java semantics access checks.
-  // Also, the caller of `AddTypeGuard` must have guaranteed that the class is in the dex cache.
-  HLoadClass* load_class = new (graph_->GetArena()) HLoadClass(graph_->GetCurrentMethod(),
-                                                               class_index,
-                                                               caller_dex_file,
-                                                               is_referrer,
-                                                               invoke_instruction->GetDexPc(),
-                                                               /* needs_access_check */ false,
-                                                               /* is_in_dex_cache */ true,
-                                                               /* is_in_boot_image */ false);
-
-  HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
-  // TODO: Extend reference type propagation to understand the guard.
   if (cursor != nullptr) {
     bb_cursor->InsertInstructionAfter(receiver_class, cursor);
   } else {
     bb_cursor->InsertInstructionBefore(receiver_class, bb_cursor->GetFirstInstruction());
   }
+
+  const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
+  bool is_referrer = (klass == outermost_graph_->GetArtMethod()->GetDeclaringClass());
+  // Note that we will just compare the classes, so we don't need Java semantics access checks.
+  // Note that the type index and the dex file are relative to the method this type guard is
+  // inlined into.
+  HLoadClass* load_class = new (graph_->GetArena()) HLoadClass(graph_->GetCurrentMethod(),
+                                                               class_index,
+                                                               caller_dex_file,
+                                                               is_referrer,
+                                                               invoke_instruction->GetDexPc(),
+                                                               /* needs_access_check */ false);
   bb_cursor->InsertInstructionAfter(load_class, receiver_class);
+  // Sharpen after adding the instruction, as the sharpening may remove inputs.
+  HSharpening::SharpenClass(load_class, klass, handles_, codegen_, compiler_driver_);
+
+  // TODO: Extend reference type propagation to understand the guard.
+  HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
   bb_cursor->InsertInstructionAfter(compare, load_class);
   if (with_deoptimization) {
     HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
@@ -583,13 +593,13 @@
       break;
     }
     ArtMethod* method = nullptr;
+
+    Handle<mirror::Class> handle = handles_->NewHandle(classes->Get(i));
     if (invoke_instruction->IsInvokeInterface()) {
-      method = classes->Get(i)->FindVirtualMethodForInterface(
-          resolved_method, pointer_size);
+      method = handle->FindVirtualMethodForInterface(resolved_method, pointer_size);
     } else {
       DCHECK(invoke_instruction->IsInvokeVirtual());
-      method = classes->Get(i)->FindVirtualMethodForVirtual(
-          resolved_method, pointer_size);
+      method = handle->FindVirtualMethodForVirtual(resolved_method, pointer_size);
     }
 
     HInstruction* receiver = invoke_instruction->InputAt(0);
@@ -597,14 +607,16 @@
     HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
 
     dex::TypeIndex class_index = FindClassIndexIn(
-        classes->Get(i), caller_dex_file, caller_compilation_unit_.GetDexCache());
+        handle.Get(), caller_dex_file, caller_compilation_unit_.GetDexCache());
     HInstruction* return_replacement = nullptr;
     if (!class_index.IsValid() ||
-        !TryBuildAndInline(invoke_instruction, method, &return_replacement)) {
+        !TryBuildAndInline(invoke_instruction,
+                           method,
+                           ReferenceTypeInfo::Create(handle, /* is_exact */ true),
+                           &return_replacement)) {
       all_targets_inlined = false;
     } else {
       one_target_inlined = true;
-      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.
@@ -616,8 +628,13 @@
         // We do not support HDeoptimize in OSR methods.
         deoptimize = false;
       }
-      HInstruction* compare = AddTypeGuard(
-          receiver, cursor, bb_cursor, class_index, is_referrer, invoke_instruction, deoptimize);
+      HInstruction* compare = AddTypeGuard(receiver,
+                                           cursor,
+                                           bb_cursor,
+                                           class_index,
+                                           handle.Get(),
+                                           invoke_instruction,
+                                           deoptimize);
       if (deoptimize) {
         if (return_replacement != nullptr) {
           invoke_instruction->ReplaceWith(return_replacement);
@@ -780,7 +797,10 @@
   HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
 
   HInstruction* return_replacement = nullptr;
-  if (!TryBuildAndInline(invoke_instruction, actual_method, &return_replacement)) {
+  if (!TryBuildAndInline(invoke_instruction,
+                         actual_method,
+                         ReferenceTypeInfo::CreateInvalid(),
+                         &return_replacement)) {
     return false;
   }
 
@@ -845,13 +865,14 @@
 
 bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction,
                                    ArtMethod* method,
+                                   ReferenceTypeInfo receiver_type,
                                    bool do_rtp,
                                    bool cha_devirtualize) {
   HInstruction* return_replacement = nullptr;
   uint32_t dex_pc = invoke_instruction->GetDexPc();
   HInstruction* cursor = invoke_instruction->GetPrevious();
   HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
-  if (!TryBuildAndInline(invoke_instruction, method, &return_replacement)) {
+  if (!TryBuildAndInline(invoke_instruction, method, receiver_type, &return_replacement)) {
     if (invoke_instruction->IsInvokeInterface()) {
       // Turn an invoke-interface into an invoke-virtual. An invoke-virtual is always
       // better than an invoke-interface because:
@@ -909,6 +930,7 @@
 
 bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction,
                                  ArtMethod* method,
+                                 ReferenceTypeInfo receiver_type,
                                  HInstruction** return_replacement) {
   if (method->IsProxyMethod()) {
     VLOG(compiler) << "Method " << method->PrettyMethod()
@@ -985,7 +1007,8 @@
     return false;
   }
 
-  if (!TryBuildAndInlineHelper(invoke_instruction, method, same_dex_file, return_replacement)) {
+  if (!TryBuildAndInlineHelper(
+          invoke_instruction, method, receiver_type, same_dex_file, return_replacement)) {
     return false;
   }
 
@@ -1182,8 +1205,10 @@
 
 bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
                                        ArtMethod* resolved_method,
+                                       ReferenceTypeInfo receiver_type,
                                        bool same_dex_file,
                                        HInstruction** return_replacement) {
+  DCHECK(!(resolved_method->IsStatic() && receiver_type.IsValid()));
   ScopedObjectAccess soa(Thread::Current());
   const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
   const DexFile& callee_dex_file = *resolved_method->GetDexFile();
@@ -1274,12 +1299,13 @@
   }
 
   size_t parameter_index = 0;
+  bool run_rtp = false;
   for (HInstructionIterator instructions(callee_graph->GetEntryBlock()->GetInstructions());
        !instructions.Done();
        instructions.Advance()) {
     HInstruction* current = instructions.Current();
     if (current->IsParameterValue()) {
-      HInstruction* argument = invoke_instruction->InputAt(parameter_index++);
+      HInstruction* argument = invoke_instruction->InputAt(parameter_index);
       if (argument->IsNullConstant()) {
         current->ReplaceWith(callee_graph->GetNullConstant());
       } else if (argument->IsIntConstant()) {
@@ -1293,15 +1319,21 @@
         current->ReplaceWith(
             callee_graph->GetDoubleConstant(argument->AsDoubleConstant()->GetValue()));
       } else if (argument->GetType() == Primitive::kPrimNot) {
-        current->SetReferenceTypeInfo(argument->GetReferenceTypeInfo());
+        if (!resolved_method->IsStatic() && parameter_index == 0 && receiver_type.IsValid()) {
+          run_rtp = true;
+          current->SetReferenceTypeInfo(receiver_type);
+        } else {
+          current->SetReferenceTypeInfo(argument->GetReferenceTypeInfo());
+        }
         current->AsParameterValue()->SetCanBeNull(argument->CanBeNull());
       }
+      ++parameter_index;
     }
   }
 
   // We have replaced formal arguments with actual arguments. If actual types
   // are more specific than the declared ones, run RTP again on the inner graph.
-  if (ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) {
+  if (run_rtp || ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) {
     ReferenceTypePropagation(callee_graph,
                              dex_compilation_unit.GetDexCache(),
                              handles_,
@@ -1490,7 +1522,7 @@
 
   ReferenceTypeInfo actual_rti = actual_obj->GetReferenceTypeInfo();
   return (actual_rti.IsExact() && !declared_rti.IsExact()) ||
-         declared_rti.IsStrictSupertypeOf(actual_rti);
+          declared_rti.IsStrictSupertypeOf(actual_rti);
 }
 
 ReferenceTypeInfo HInliner::GetClassRTI(mirror::Class* klass) {
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index ffebd97..4c0b990 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -66,17 +66,20 @@
   // a CHA guard needs to be added for the inlining.
   bool TryInlineAndReplace(HInvoke* invoke_instruction,
                            ArtMethod* resolved_method,
+                           ReferenceTypeInfo receiver_type,
                            bool do_rtp,
                            bool cha_devirtualize)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool TryBuildAndInline(HInvoke* invoke_instruction,
                          ArtMethod* resolved_method,
+                         ReferenceTypeInfo receiver_type,
                          HInstruction** return_replacement)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool TryBuildAndInlineHelper(HInvoke* invoke_instruction,
                                ArtMethod* resolved_method,
+                               ReferenceTypeInfo receiver_type,
                                bool same_dex_file,
                                HInstruction** return_replacement);
 
@@ -167,7 +170,7 @@
                              HInstruction* cursor,
                              HBasicBlock* bb_cursor,
                              dex::TypeIndex class_index,
-                             bool is_referrer,
+                             mirror::Class* klass,
                              HInstruction* invoke_instruction,
                              bool with_deoptimization)
     REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index b97581b..af8e2c8 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -816,8 +816,7 @@
     HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
         HInvokeStaticOrDirect::MethodLoadKind::kStringInit,
         HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
-        dchecked_integral_cast<uint64_t>(string_init_entry_point),
-        0U
+        dchecked_integral_cast<uint64_t>(string_init_entry_point)
     };
     MethodReference target_method(dex_file_, method_idx);
     HInvoke* invoke = new (arena_) HInvokeStaticOrDirect(
@@ -862,8 +861,7 @@
     HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
         HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
         HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
-        0u,
-        0U
+        0u
     };
     MethodReference target_method(resolved_method->GetDexFile(),
                                   resolved_method->GetDexMethodIndex());
@@ -937,9 +935,7 @@
       outer_dex_file,
       IsOutermostCompilingClass(type_index),
       dex_pc,
-      needs_access_check,
-      /* is_in_dex_cache */ false,
-      /* is_in_boot_image */ false);
+      needs_access_check);
 
   AppendInstruction(load_class);
   HInstruction* cls = load_class;
@@ -1029,9 +1025,7 @@
         outer_dex_file,
         is_outer_class,
         dex_pc,
-        /*needs_access_check*/ false,
-        /* is_in_dex_cache */ false,
-        /* is_in_boot_image */ false);
+        /*needs_access_check*/ false);
     AppendInstruction(load_class);
     clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
     AppendInstruction(clinit_check);
@@ -1388,9 +1382,7 @@
                                                  outer_dex_file,
                                                  is_outer_class,
                                                  dex_pc,
-                                                 /*needs_access_check*/ false,
-                                                 /* is_in_dex_cache */ false,
-                                                 /* is_in_boot_image */ false);
+                                                 /*needs_access_check*/ false);
   AppendInstruction(constant);
 
   HInstruction* cls = constant;
@@ -1664,9 +1656,7 @@
       dex_file,
       IsOutermostCompilingClass(type_index),
       dex_pc,
-      !can_access,
-      /* is_in_dex_cache */ false,
-      /* is_in_boot_image */ false);
+      !can_access);
   AppendInstruction(cls);
 
   TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class);
@@ -2656,9 +2646,7 @@
           *dex_file_,
           IsOutermostCompilingClass(type_index),
           dex_pc,
-          !can_access,
-          /* is_in_dex_cache */ false,
-          /* is_in_boot_image */ false));
+          !can_access));
       UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
       break;
     }
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index c615df1..439e3b6 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -1897,7 +1897,8 @@
 static bool NoEscapeForStringBufferReference(HInstruction* reference, HInstruction* user) {
   if (user->IsInvokeStaticOrDirect()) {
     // Any constructor on StringBuffer is okay.
-    return user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() &&
+    return user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr &&
+           user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() &&
            user->InputAt(0) == reference;
   } else if (user->IsInvokeVirtual()) {
     switch (user->AsInvokeVirtual()->GetIntrinsic()) {
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 433dced..85e84d8 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -47,6 +47,9 @@
 
 using namespace vixl::aarch32;  // NOLINT(build/namespaces)
 
+using vixl::ExactAssemblyScope;
+using vixl::CodeBufferCheckScope;
+
 ArmVIXLAssembler* IntrinsicCodeGeneratorARMVIXL::GetAssembler() {
   return codegen_->GetAssembler();
 }
@@ -184,7 +187,7 @@
     assembler->MaybePoisonHeapReference(tmp);
     __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
     __ Cmp(src_curr_addr, src_stop_addr);
-    __ B(ne, &loop);
+    __ B(ne, &loop, /* far_target */ false);
     __ B(GetExitLabel());
   }
 
@@ -467,9 +470,9 @@
   __ Cmp(op1, op2);
 
   {
-    AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
-                               3 * kMaxInstructionSizeInBytes,
-                               CodeBufferCheckScope::kMaximumSize);
+    ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+                           3 * kMaxInstructionSizeInBytes,
+                           CodeBufferCheckScope::kMaximumSize);
 
     __ ite(is_min ? lt : gt);
     __ mov(is_min ? lt : gt, out, op1);
@@ -848,7 +851,7 @@
       __ 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);
+      __ B(ne, &loop_head, /* far_target */ false);
     } else {
       __ Strd(value_lo, value_hi, MemOperand(base, offset));
     }
@@ -1050,25 +1053,25 @@
   __ Subs(tmp, tmp, expected);
 
   {
-    AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
-                               3 * kMaxInstructionSizeInBytes,
-                               CodeBufferCheckScope::kMaximumSize);
+    ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+                           3 * kMaxInstructionSizeInBytes,
+                           CodeBufferCheckScope::kMaximumSize);
 
     __ itt(eq);
     __ strex(eq, tmp, value, MemOperand(tmp_ptr));
     __ cmp(eq, tmp, 1);
   }
 
-  __ B(eq, &loop_head);
+  __ B(eq, &loop_head, /* far_target */ false);
 
   __ Dmb(vixl32::ISH);
 
   __ Rsbs(out, tmp, 1);
 
   {
-    AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
-                               2 * kMaxInstructionSizeInBytes,
-                               CodeBufferCheckScope::kMaximumSize);
+    ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+                           2 * kMaxInstructionSizeInBytes,
+                           CodeBufferCheckScope::kMaximumSize);
 
     __ it(cc);
     __ mov(cc, out, 0);
@@ -1185,9 +1188,9 @@
   // temp0 = min(len(str), len(arg)).
 
   {
-    AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
-                               2 * kMaxInstructionSizeInBytes,
-                               CodeBufferCheckScope::kMaximumSize);
+    ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+                           2 * kMaxInstructionSizeInBytes,
+                           CodeBufferCheckScope::kMaximumSize);
 
     __ it(gt);
     __ mov(gt, temp0, temp1);
@@ -1207,9 +1210,9 @@
     // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
     __ Lsls(temp3, temp3, 31u);  // Extract purely the compression flag.
 
-    AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
-                               2 * kMaxInstructionSizeInBytes,
-                               CodeBufferCheckScope::kMaximumSize);
+    ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+                           2 * kMaxInstructionSizeInBytes,
+                           CodeBufferCheckScope::kMaximumSize);
 
     __ it(ne);
     __ add(ne, temp0, temp0, temp0);
@@ -1235,23 +1238,23 @@
   __ Ldr(temp_reg, MemOperand(str, temp1));
   __ Ldr(temp2, MemOperand(arg, temp1));
   __ Cmp(temp_reg, temp2);
-  __ B(ne, &find_char_diff);
+  __ B(ne, &find_char_diff, /* far_target */ false);
   __ Add(temp1, temp1, char_size * 2);
 
   __ Ldr(temp_reg, MemOperand(str, temp1));
   __ Ldr(temp2, MemOperand(arg, temp1));
   __ Cmp(temp_reg, temp2);
-  __ B(ne, &find_char_diff_2nd_cmp);
+  __ B(ne, &find_char_diff_2nd_cmp, /* far_target */ false);
   __ Add(temp1, temp1, char_size * 2);
   // With string compression, we have compared 8 bytes, otherwise 4 chars.
   __ Subs(temp0, temp0, (mirror::kUseStringCompression ? 8 : 4));
-  __ B(hi, &loop);
+  __ B(hi, &loop, /* far_target */ false);
   __ B(&end);
 
   __ Bind(&find_char_diff_2nd_cmp);
   if (mirror::kUseStringCompression) {
     __ Subs(temp0, temp0, 4);  // 4 bytes previously compared.
-    __ B(ls, &end);  // Was the second comparison fully beyond the end?
+    __ B(ls, &end, /* far_target */ false);  // Was the second comparison fully beyond the end?
   } else {
     // Without string compression, we can start treating temp0 as signed
     // and rely on the signed comparison below.
@@ -1279,7 +1282,7 @@
   // the remaining string data, so just return length diff (out).
   // The comparison is unsigned for string compression, otherwise signed.
   __ Cmp(temp0, Operand(temp1, vixl32::LSR, (mirror::kUseStringCompression ? 3 : 4)));
-  __ B((mirror::kUseStringCompression ? ls : le), &end);
+  __ B((mirror::kUseStringCompression ? ls : le), &end, /* far_target */ false);
 
   // Extract the characters and calculate the difference.
   if (mirror::kUseStringCompression) {
@@ -1324,9 +1327,9 @@
     __ Mov(temp2, arg);
     __ Lsrs(temp3, temp3, 1u);                // Continue the move of the compression flag.
     {
-      AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
-                                 3 * kMaxInstructionSizeInBytes,
-                                 CodeBufferCheckScope::kMaximumSize);
+      ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+                             3 * kMaxInstructionSizeInBytes,
+                             CodeBufferCheckScope::kMaximumSize);
       __ itt(cs);                             // Interleave with selection of temp1 and temp2.
       __ mov(cs, temp1, arg);                 // Preserves flags.
       __ mov(cs, temp2, str);                 // Preserves flags.
@@ -1346,9 +1349,9 @@
     __ Ldrb(temp_reg, MemOperand(temp1, c_char_size, PostIndex));
     __ Ldrh(temp3, MemOperand(temp2, char_size, PostIndex));
     __ Cmp(temp_reg, temp3);
-    __ B(ne, &different_compression_diff);
+    __ B(ne, &different_compression_diff, /* far_target */ false);
     __ Subs(temp0, temp0, 2);
-    __ B(hi, &different_compression_loop);
+    __ B(hi, &different_compression_loop, /* far_target */ false);
     __ B(&end);
 
     // Calculate the difference.
@@ -1361,9 +1364,9 @@
     static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
                   "Expecting 0=compressed, 1=uncompressed");
 
-    AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
-                               2 * kMaxInstructionSizeInBytes,
-                               CodeBufferCheckScope::kMaximumSize);
+    ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+                           2 * kMaxInstructionSizeInBytes,
+                           CodeBufferCheckScope::kMaximumSize);
     __ it(cc);
     __ rsb(cc, out, out, 0);
   }
@@ -1424,7 +1427,7 @@
 
   // Reference equality check, return true if same reference.
   __ Cmp(str, arg);
-  __ B(eq, &return_true);
+  __ B(eq, &return_true, /* far_target */ false);
 
   if (!optimizations.GetArgumentIsString()) {
     // Instanceof check for the argument by comparing class fields.
@@ -1434,7 +1437,7 @@
     __ Ldr(temp, MemOperand(str, class_offset));
     __ Ldr(temp1, MemOperand(arg, class_offset));
     __ Cmp(temp, temp1);
-    __ B(ne, &return_false);
+    __ B(ne, &return_false, /* far_target */ false);
   }
 
   // Load `count` fields of this and argument strings.
@@ -1443,7 +1446,7 @@
   // Check if `count` fields are equal, return false if they're not.
   // Also compares the compression style, if differs return false.
   __ Cmp(temp, temp1);
-  __ B(ne, &return_false);
+  __ B(ne, &return_false, /* far_target */ false);
   // 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");
@@ -1457,9 +1460,9 @@
     // For string compression, calculate the number of bytes to compare (not chars).
     // This could in theory exceed INT32_MAX, so treat temp as unsigned.
     __ Lsrs(temp, temp, 1u);                        // Extract length and check compression flag.
-    AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
-                               2 * kMaxInstructionSizeInBytes,
-                               CodeBufferCheckScope::kMaximumSize);
+    ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+                           2 * kMaxInstructionSizeInBytes,
+                           CodeBufferCheckScope::kMaximumSize);
     __ it(cs);                                      // If uncompressed,
     __ add(cs, temp, temp, temp);                   //   double the byte count.
   }
@@ -1474,10 +1477,10 @@
   __ Ldr(temp2, MemOperand(arg, temp1));
   __ Add(temp1, temp1, Operand::From(sizeof(uint32_t)));
   __ Cmp(out, temp2);
-  __ B(ne, &return_false);
+  __ B(ne, &return_false, /* far_target */ false);
   // With string compression, we have compared 4 bytes, otherwise 2 chars.
   __ Subs(temp, temp, mirror::kUseStringCompression ? 4 : 2);
-  __ B(hi, &loop);
+  __ B(hi, &loop, /* far_target */ false);
 
   // Return true and exit the function.
   // If loop does not result in returning false, we return true.
@@ -1506,7 +1509,7 @@
   SlowPathCodeARMVIXL* slow_path = nullptr;
   HInstruction* code_point = invoke->InputAt(1);
   if (code_point->IsIntConstant()) {
-    if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) >
+    if (static_cast<uint32_t>(Int32ConstantFrom(code_point)) >
         std::numeric_limits<uint16_t>::max()) {
       // Always needs the slow-path. We could directly dispatch to it, but this case should be
       // rare, so for simplicity just put the full slow-path down and branch unconditionally.
@@ -1797,7 +1800,7 @@
     } else {
       if (!optimizations.GetDestinationIsSource()) {
         __ Cmp(src, dest);
-        __ B(ne, &conditions_on_positions_validated);
+        __ B(ne, &conditions_on_positions_validated, /* far_target */ false);
       }
       __ Cmp(RegisterFrom(dest_pos), src_pos_constant);
       __ B(gt, intrinsic_slow_path->GetEntryLabel());
@@ -1805,7 +1808,7 @@
   } else {
     if (!optimizations.GetDestinationIsSource()) {
       __ Cmp(src, dest);
-      __ B(ne, &conditions_on_positions_validated);
+      __ B(ne, &conditions_on_positions_validated, /* far_target */ false);
     }
     if (dest_pos.IsConstant()) {
       int32_t dest_pos_constant = Int32ConstantFrom(dest_pos);
@@ -1913,7 +1916,7 @@
 
       if (optimizations.GetDestinationIsTypedObjectArray()) {
         vixl32::Label do_copy;
-        __ B(eq, &do_copy);
+        __ B(eq, &do_copy, /* far_target */ false);
         // /* HeapReference<Class> */ temp1 = temp1->component_type_
         codegen_->GenerateFieldLoadWithBakerReadBarrier(
             invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
@@ -1973,7 +1976,7 @@
 
       if (optimizations.GetDestinationIsTypedObjectArray()) {
         vixl32::Label do_copy;
-        __ B(eq, &do_copy);
+        __ B(eq, &do_copy, /* far_target */ false);
         if (!did_unpoison) {
           assembler->MaybeUnpoisonHeapReference(temp1);
         }
@@ -2066,7 +2069,7 @@
 
     // Don't enter copy loop if `length == 0`.
     __ Cmp(temp1, temp3);
-    __ B(eq, &done);
+    __ B(eq, &done, /* far_target */ false);
 
     // /* int32_t */ monitor = src->monitor_
     __ Ldr(temp2, MemOperand(src, monitor_offset));
@@ -2119,7 +2122,7 @@
     }
 
     __ Cmp(temp1, temp3);
-    __ B(ne, &loop);
+    __ B(ne, &loop, /* far_target */ false);
 
     __ Bind(read_barrier_slow_path->GetExitLabel());
     __ Bind(&done);
@@ -2139,7 +2142,7 @@
     // poison/unpoison.
     vixl32::Label loop, done;
     __ Cmp(temp1, temp3);
-    __ B(eq, &done);
+    __ B(eq, &done, /* far_target */ false);
     __ Bind(&loop);
 
     {
@@ -2151,7 +2154,7 @@
     }
 
     __ Cmp(temp1, temp3);
-    __ B(ne, &loop);
+    __ B(ne, &loop, /* far_target */ false);
     __ Bind(&done);
   }
 
@@ -2557,7 +2560,7 @@
 
   __ Subs(num_chr, srcEnd, srcBegin);
   // Early out for valid zero-length retrievals.
-  __ B(eq, &done);
+  __ B(eq, &done, /* far_target */ false);
 
   // src range to copy.
   __ Add(src_ptr, srcObj, value_offset);
@@ -2573,7 +2576,7 @@
     __ Ldr(temp, MemOperand(srcObj, count_offset));
     __ Tst(temp, 1);
     temps.Release(temp);
-    __ B(eq, &compressed_string_preloop);
+    __ B(eq, &compressed_string_preloop, /* far_target */ false);
   }
   __ Add(src_ptr, src_ptr, Operand(srcBegin, vixl32::LSL, 1));
 
@@ -2583,7 +2586,7 @@
   temp = temps.Acquire();
   // Save repairing the value of num_chr on the < 4 character path.
   __ Subs(temp, num_chr, 4);
-  __ B(lt, &remainder);
+  __ B(lt, &remainder, /* far_target */ false);
 
   // Keep the result of the earlier subs, we are going to fetch at least 4 characters.
   __ Mov(num_chr, temp);
@@ -2598,10 +2601,10 @@
   __ Ldr(temp, MemOperand(src_ptr, char_size * 4, PostIndex));
   __ Str(temp, MemOperand(dst_ptr, char_size * 4, PostIndex));
   temps.Release(temp);
-  __ B(ge, &loop);
+  __ B(ge, &loop, /* far_target */ false);
 
   __ Adds(num_chr, num_chr, 4);
-  __ B(eq, &done);
+  __ B(eq, &done, /* far_target */ false);
 
   // Main loop for < 4 character case and remainder handling. Loads and stores one
   // 16-bit Java character at a time.
@@ -2611,7 +2614,7 @@
   __ Subs(num_chr, num_chr, 1);
   __ Strh(temp, MemOperand(dst_ptr, char_size, PostIndex));
   temps.Release(temp);
-  __ B(gt, &remainder);
+  __ B(gt, &remainder, /* far_target */ false);
 
   if (mirror::kUseStringCompression) {
     __ B(&done);
@@ -2627,7 +2630,7 @@
     __ Strh(temp, MemOperand(dst_ptr, char_size, PostIndex));
     temps.Release(temp);
     __ Subs(num_chr, num_chr, 1);
-    __ B(gt, &compressed_string_loop);
+    __ B(gt, &compressed_string_loop, /* far_target */ false);
   }
 
   __ Bind(&done);
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 9b5d7a0..e9c6615 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1648,7 +1648,8 @@
   }
 
   if (type == Primitive::kPrimNot) {
-    codegen->MarkGCCard(base, locations->InAt(3).AsRegister<Register>());
+    bool value_can_be_null = true;  // TODO: Worth finding out this information?
+    codegen->MarkGCCard(base, locations->InAt(3).AsRegister<Register>(), value_can_be_null);
   }
 }
 
@@ -1806,7 +1807,8 @@
 
   if (type == Primitive::kPrimNot) {
     // Mark card for object assuming new value is stored.
-    codegen->MarkGCCard(base, value);
+    bool value_can_be_null = true;  // TODO: Worth finding out this information?
+    codegen->MarkGCCard(base, value, value_can_be_null);
   }
 
   // do {
@@ -2464,6 +2466,94 @@
   __ Bind(&done);
 }
 
+// void java.lang.String.getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
+void IntrinsicLocationsBuilderMIPS::VisitStringGetCharsNoCheck(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                            LocationSummary::kCallOnMainOnly,
+                                                            kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetInAt(2, Location::RequiresRegister());
+  locations->SetInAt(3, Location::RequiresRegister());
+  locations->SetInAt(4, Location::RequiresRegister());
+
+  // We will call memcpy() to do the actual work. Allocate the temporary
+  // registers to use the correct input registers, and output register.
+  // memcpy() uses the normal MIPS calling convention.
+  InvokeRuntimeCallingConvention calling_convention;
+
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+
+  Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt);
+  locations->AddTemp(Location::RegisterLocation(outLocation.AsRegister<Register>()));
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitStringGetCharsNoCheck(HInvoke* invoke) {
+  MipsAssembler* assembler = GetAssembler();
+  LocationSummary* locations = invoke->GetLocations();
+
+  // Check assumption that sizeof(Char) is 2 (used in scaling below).
+  const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
+  DCHECK_EQ(char_size, 2u);
+  const size_t char_shift = Primitive::ComponentSizeShift(Primitive::kPrimChar);
+
+  Register srcObj = locations->InAt(0).AsRegister<Register>();
+  Register srcBegin = locations->InAt(1).AsRegister<Register>();
+  Register srcEnd = locations->InAt(2).AsRegister<Register>();
+  Register dstObj = locations->InAt(3).AsRegister<Register>();
+  Register dstBegin = locations->InAt(4).AsRegister<Register>();
+
+  Register dstPtr = locations->GetTemp(0).AsRegister<Register>();
+  DCHECK_EQ(dstPtr, A0);
+  Register srcPtr = locations->GetTemp(1).AsRegister<Register>();
+  DCHECK_EQ(srcPtr, A1);
+  Register numChrs = locations->GetTemp(2).AsRegister<Register>();
+  DCHECK_EQ(numChrs, A2);
+
+  Register dstReturn = locations->GetTemp(3).AsRegister<Register>();
+  DCHECK_EQ(dstReturn, V0);
+
+  MipsLabel done;
+
+  // Location of data in char array buffer.
+  const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
+
+  // Get offset of value field within a string object.
+  const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
+
+  __ Beq(srcEnd, srcBegin, &done);  // No characters to move.
+
+  // Calculate number of characters to be copied.
+  __ Subu(numChrs, srcEnd, srcBegin);
+
+  // Calculate destination address.
+  __ Addiu(dstPtr, dstObj, data_offset);
+  if (IsR6()) {
+    __ Lsa(dstPtr, dstBegin, dstPtr, char_shift);
+  } else {
+    __ Sll(AT, dstBegin, char_shift);
+    __ Addu(dstPtr, dstPtr, AT);
+  }
+
+  // Calculate source address.
+  __ Addiu(srcPtr, srcObj, value_offset);
+  if (IsR6()) {
+    __ Lsa(srcPtr, srcBegin, srcPtr, char_shift);
+  } else {
+    __ Sll(AT, srcBegin, char_shift);
+    __ Addu(srcPtr, srcPtr, AT);
+  }
+
+  // Calculate number of bytes to copy from number of characters.
+  __ Sll(numChrs, numChrs, char_shift);
+
+  codegen_->InvokeRuntime(kQuickMemcpy, invoke, invoke->GetDexPc(), nullptr);
+
+  __ Bind(&done);
+}
+
 // Unimplemented intrinsics.
 
 UNIMPLEMENTED_INTRINSIC(MIPS, MathCeil)
@@ -2473,7 +2563,6 @@
 UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong)
 
 UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(MIPS, StringGetCharsNoCheck)
 UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopyChar)
 UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy)
 
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 5a99886..3022e97 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1846,6 +1846,84 @@
   GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
 }
 
+// void java.lang.String.getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
+void IntrinsicLocationsBuilderMIPS64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                            LocationSummary::kCallOnMainOnly,
+                                                            kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetInAt(2, Location::RequiresRegister());
+  locations->SetInAt(3, Location::RequiresRegister());
+  locations->SetInAt(4, Location::RequiresRegister());
+
+  // We will call memcpy() to do the actual work. Allocate the temporary
+  // registers to use the correct input registers, and output register.
+  // memcpy() uses the normal MIPS calling conventions.
+  InvokeRuntimeCallingConvention calling_convention;
+
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+
+  Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimLong);
+  locations->AddTemp(Location::RegisterLocation(outLocation.AsRegister<GpuRegister>()));
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
+  Mips64Assembler* assembler = GetAssembler();
+  LocationSummary* locations = invoke->GetLocations();
+
+  // Check assumption that sizeof(Char) is 2 (used in scaling below).
+  const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
+  DCHECK_EQ(char_size, 2u);
+  const size_t char_shift = Primitive::ComponentSizeShift(Primitive::kPrimChar);
+
+  GpuRegister srcObj = locations->InAt(0).AsRegister<GpuRegister>();
+  GpuRegister srcBegin = locations->InAt(1).AsRegister<GpuRegister>();
+  GpuRegister srcEnd = locations->InAt(2).AsRegister<GpuRegister>();
+  GpuRegister dstObj = locations->InAt(3).AsRegister<GpuRegister>();
+  GpuRegister dstBegin = locations->InAt(4).AsRegister<GpuRegister>();
+
+  GpuRegister dstPtr = locations->GetTemp(0).AsRegister<GpuRegister>();
+  DCHECK_EQ(dstPtr, A0);
+  GpuRegister srcPtr = locations->GetTemp(1).AsRegister<GpuRegister>();
+  DCHECK_EQ(srcPtr, A1);
+  GpuRegister numChrs = locations->GetTemp(2).AsRegister<GpuRegister>();
+  DCHECK_EQ(numChrs, A2);
+
+  GpuRegister dstReturn = locations->GetTemp(3).AsRegister<GpuRegister>();
+  DCHECK_EQ(dstReturn, V0);
+
+  Mips64Label done;
+
+  // Location of data in char array buffer.
+  const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
+
+  // Get offset of value field within a string object.
+  const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
+
+  __ Beqc(srcEnd, srcBegin, &done);  // No characters to move.
+
+  // Calculate number of characters to be copied.
+  __ Dsubu(numChrs, srcEnd, srcBegin);
+
+  // Calculate destination address.
+  __ Daddiu(dstPtr, dstObj, data_offset);
+  __ Dlsa(dstPtr, dstBegin, dstPtr, char_shift);
+
+  // Calculate source address.
+  __ Daddiu(srcPtr, srcObj, value_offset);
+  __ Dlsa(srcPtr, srcBegin, srcPtr, char_shift);
+
+  // Calculate number of bytes to copy from number of characters.
+  __ Dsll(numChrs, numChrs, char_shift);
+
+  codegen_->InvokeRuntime(kQuickMemcpy, invoke, invoke->GetDexPc(), nullptr);
+
+  __ Bind(&done);
+}
+
 static void GenHighestOneBit(LocationSummary* locations,
                              Primitive::Type type,
                              Mips64Assembler* assembler) {
@@ -1925,7 +2003,6 @@
 }
 
 UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(MIPS64, StringGetCharsNoCheck)
 UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopyChar)
 UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy)
 
diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc
index 13e14c5..3831aa6 100644
--- a/compiler/optimizing/linearize_test.cc
+++ b/compiler/optimizing/linearize_test.cc
@@ -18,7 +18,6 @@
 
 #include "arch/x86/instruction_set_features_x86.h"
 #include "base/arena_allocator.h"
-#include "base/stringprintf.h"
 #include "builder.h"
 #include "code_generator.h"
 #include "code_generator_x86.h"
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 925d4f1..a599c2a 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1108,13 +1108,23 @@
   return HasEnvironment() ? environment_->Size() : 0;
 }
 
-void HPhi::AddInput(HInstruction* input) {
+void HVariableInputSizeInstruction::AddInput(HInstruction* input) {
   DCHECK(input->GetBlock() != nullptr);
   inputs_.push_back(HUserRecord<HInstruction*>(input));
   input->AddUseAt(this, inputs_.size() - 1);
 }
 
-void HPhi::RemoveInputAt(size_t index) {
+void HVariableInputSizeInstruction::InsertInputAt(size_t index, HInstruction* input) {
+  inputs_.insert(inputs_.begin() + index, HUserRecord<HInstruction*>(input));
+  input->AddUseAt(this, index);
+  // Update indexes in use nodes of inputs that have been pushed further back by the insert().
+  for (size_t i = index + 1u, e = inputs_.size(); i < e; ++i) {
+    DCHECK_EQ(inputs_[i].GetUseNode()->GetIndex(), i - 1u);
+    inputs_[i].GetUseNode()->SetIndex(i);
+  }
+}
+
+void HVariableInputSizeInstruction::RemoveInputAt(size_t index) {
   RemoveAsUserOfInput(index);
   inputs_.erase(inputs_.begin() + index);
   // Update indexes in use nodes of inputs that have been pulled forward by the erase().
@@ -1347,7 +1357,9 @@
 void HInstruction::MoveBefore(HInstruction* cursor) {
   DCHECK(!IsPhi());
   DCHECK(!IsControlFlow());
-  DCHECK(CanBeMoved());
+  DCHECK(CanBeMoved() ||
+         // HShouldDeoptimizeFlag can only be moved by CHAGuardOptimization.
+         IsShouldDeoptimizeFlag());
   DCHECK(!cursor->IsPhi());
 
   next_->previous_ = previous_;
@@ -2386,26 +2398,6 @@
   return !opt.GetDoesNotNeedDexCache();
 }
 
-void HInvokeStaticOrDirect::InsertInputAt(size_t index, HInstruction* input) {
-  inputs_.insert(inputs_.begin() + index, HUserRecord<HInstruction*>(input));
-  input->AddUseAt(this, index);
-  // Update indexes in use nodes of inputs that have been pushed further back by the insert().
-  for (size_t i = index + 1u, e = inputs_.size(); i < e; ++i) {
-    DCHECK_EQ(inputs_[i].GetUseNode()->GetIndex(), i - 1u);
-    inputs_[i].GetUseNode()->SetIndex(i);
-  }
-}
-
-void HInvokeStaticOrDirect::RemoveInputAt(size_t index) {
-  RemoveAsUserOfInput(index);
-  inputs_.erase(inputs_.begin() + index);
-  // Update indexes in use nodes of inputs that have been pulled forward by the erase().
-  for (size_t i = index, e = inputs_.size(); i < e; ++i) {
-    DCHECK_EQ(inputs_[i].GetUseNode()->GetIndex(), i + 1u);
-    inputs_[i].GetUseNode()->SetIndex(i);
-  }
-}
-
 std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::MethodLoadKind rhs) {
   switch (rhs) {
     case HInvokeStaticOrDirect::MethodLoadKind::kStringInit:
@@ -2414,8 +2406,6 @@
       return os << "recursive";
     case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
       return os << "direct";
-    case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
-      return os << "direct_fixup";
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
       return os << "dex_cache_pc_relative";
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod:
@@ -2440,6 +2430,17 @@
   }
 }
 
+// Helper for InstructionDataEquals to fetch the mirror Class out
+// from a kJitTableAddress LoadClass kind.
+// NO_THREAD_SAFETY_ANALYSIS because even though we're accessing
+// mirrors, they are stored in a variable size handle scope which is always
+// visited during a pause. Also, the only caller of this helper
+// only uses the mirror for pointer comparison.
+static inline mirror::Class* AsMirrorInternal(uint64_t address)
+    NO_THREAD_SAFETY_ANALYSIS {
+  return reinterpret_cast<StackReference<mirror::Class>*>(address)->AsMirrorPtr();
+}
+
 bool HLoadClass::InstructionDataEquals(const HInstruction* other) const {
   const HLoadClass* other_load_class = other->AsLoadClass();
   // TODO: To allow GVN for HLoadClass from different dex files, we should compare the type
@@ -2448,16 +2449,14 @@
       GetPackedFields() != other_load_class->GetPackedFields()) {
     return false;
   }
-  LoadKind load_kind = GetLoadKind();
-  if (HasAddress(load_kind)) {
-    return GetAddress() == other_load_class->GetAddress();
-  } else if (HasTypeReference(load_kind)) {
-    return IsSameDexFile(GetDexFile(), other_load_class->GetDexFile());
-  } else {
-    DCHECK(HasDexCacheReference(load_kind)) << load_kind;
-    // If the type indexes and dex files are the same, dex cache element offsets
-    // must also be the same, so we don't need to compare them.
-    return IsSameDexFile(GetDexFile(), other_load_class->GetDexFile());
+  switch (GetLoadKind()) {
+    case LoadKind::kBootImageAddress:
+      return GetAddress() == other_load_class->GetAddress();
+    case LoadKind::kJitTableAddress:
+      return AsMirrorInternal(GetAddress()) == AsMirrorInternal(other_load_class->GetAddress());
+    default:
+      DCHECK(HasTypeReference(GetLoadKind()) || HasDexCacheReference(GetLoadKind()));
+      return IsSameDexFile(GetDexFile(), other_load_class->GetDexFile());
   }
 }
 
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 4a8cfcb..afa17ce 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -330,6 +330,7 @@
         invoke_type_(invoke_type),
         in_ssa_form_(false),
         should_generate_constructor_barrier_(should_generate_constructor_barrier),
+        number_of_cha_guards_(0),
         instruction_set_(instruction_set),
         cached_null_constant_(nullptr),
         cached_int_constants_(std::less<int32_t>(), arena->Adapter(kArenaAllocConstantsMap)),
@@ -551,9 +552,7 @@
   }
 
   bool HasShouldDeoptimizeFlag() const {
-    // TODO: if all CHA guards can be eliminated, there is no need for the flag
-    // even if cha_single_implementation_list_ is not empty.
-    return !cha_single_implementation_list_.empty();
+    return number_of_cha_guards_ != 0;
   }
 
   bool HasTryCatch() const { return has_try_catch_; }
@@ -572,6 +571,10 @@
 
   ReferenceTypeInfo GetInexactObjectRti() const { return inexact_object_rti_; }
 
+  uint32_t GetNumberOfCHAGuards() { return number_of_cha_guards_; }
+  void SetNumberOfCHAGuards(uint32_t num) { number_of_cha_guards_ = num; }
+  void IncrementNumberOfCHAGuards() { number_of_cha_guards_++; }
+
  private:
   void RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const;
   void RemoveDeadBlocks(const ArenaBitVector& visited);
@@ -667,6 +670,10 @@
 
   const bool should_generate_constructor_barrier_;
 
+  // Number of CHA guards in the graph. Used to short-circuit the
+  // CHA guard optimization pass when there is no CHA guard left.
+  uint32_t number_of_cha_guards_;
+
   const InstructionSet instruction_set_;
 
   // Cached constants.
@@ -2347,6 +2354,32 @@
   DISALLOW_COPY_AND_ASSIGN(HBackwardInstructionIterator);
 };
 
+class HVariableInputSizeInstruction : public HInstruction {
+ public:
+  using HInstruction::GetInputRecords;  // Keep the const version visible.
+  ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE {
+    return ArrayRef<HUserRecord<HInstruction*>>(inputs_);
+  }
+
+  void AddInput(HInstruction* input);
+  void InsertInputAt(size_t index, HInstruction* input);
+  void RemoveInputAt(size_t index);
+
+ protected:
+  HVariableInputSizeInstruction(SideEffects side_effects,
+                                uint32_t dex_pc,
+                                ArenaAllocator* arena,
+                                size_t number_of_inputs,
+                                ArenaAllocKind kind)
+      : HInstruction(side_effects, dex_pc),
+        inputs_(number_of_inputs, arena->Adapter(kind)) {}
+
+  ArenaVector<HUserRecord<HInstruction*>> inputs_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVariableInputSizeInstruction);
+};
+
 template<size_t N>
 class HTemplateInstruction: public HInstruction {
  public:
@@ -2438,15 +2471,19 @@
   DISALLOW_COPY_AND_ASSIGN(HReturn);
 };
 
-class HPhi FINAL : public HInstruction {
+class HPhi FINAL : public HVariableInputSizeInstruction {
  public:
   HPhi(ArenaAllocator* arena,
        uint32_t reg_number,
        size_t number_of_inputs,
        Primitive::Type type,
        uint32_t dex_pc = kNoDexPc)
-      : HInstruction(SideEffects::None(), dex_pc),
-        inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)),
+      : HVariableInputSizeInstruction(
+            SideEffects::None(),
+            dex_pc,
+            arena,
+            number_of_inputs,
+            kArenaAllocPhiInputs),
         reg_number_(reg_number) {
     SetPackedField<TypeField>(ToPhiType(type));
     DCHECK_NE(GetType(), Primitive::kPrimVoid);
@@ -2464,14 +2501,6 @@
 
   bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); }
 
-  using HInstruction::GetInputRecords;  // Keep the const version visible.
-  ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL {
-    return ArrayRef<HUserRecord<HInstruction*>>(inputs_);
-  }
-
-  void AddInput(HInstruction* input);
-  void RemoveInputAt(size_t index);
-
   Primitive::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); }
   void SetType(Primitive::Type new_type) {
     // Make sure that only valid type changes occur. The following are allowed:
@@ -2527,7 +2556,6 @@
   static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
   using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>;
 
-  ArenaVector<HUserRecord<HInstruction*>> inputs_;
   const uint32_t reg_number_;
 
   DISALLOW_COPY_AND_ASSIGN(HPhi);
@@ -2904,14 +2932,20 @@
 // if it's true, starts to do deoptimization.
 // It has a 4-byte slot on stack.
 // TODO: allocate a register for this flag.
-class HShouldDeoptimizeFlag FINAL : public HExpression<0> {
+class HShouldDeoptimizeFlag FINAL : public HVariableInputSizeInstruction {
  public:
-  // TODO: use SideEffects to aid eliminating some CHA guards.
-  explicit HShouldDeoptimizeFlag(uint32_t dex_pc)
-      : HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) {
+  // CHA guards are only optimized in a separate pass and it has no side effects
+  // with regard to other passes.
+  HShouldDeoptimizeFlag(ArenaAllocator* arena, uint32_t dex_pc)
+      : HVariableInputSizeInstruction(SideEffects::None(), dex_pc, arena, 0, kArenaAllocCHA) {
   }
 
-  // We don't eliminate CHA guards yet.
+  Primitive::Type GetType() const OVERRIDE { return Primitive::kPrimInt; }
+
+  // We do all CHA guard elimination/motion in a single pass, after which there is no
+  // further guard elimination/motion since a guard might have been used for justification
+  // of the elimination of another guard. Therefore, we pretend this guard cannot be moved
+  // to avoid other optimizations trying to move it.
   bool CanBeMoved() const OVERRIDE { return false; }
 
   DECLARE_INSTRUCTION(ShouldDeoptimizeFlag);
@@ -3791,15 +3825,10 @@
   kCanThrow  // Intrinsic may throw exceptions.
 };
 
-class HInvoke : public HInstruction {
+class HInvoke : public HVariableInputSizeInstruction {
  public:
   bool NeedsEnvironment() const OVERRIDE;
 
-  using HInstruction::GetInputRecords;  // Keep the const version visible.
-  ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE {
-    return ArrayRef<HUserRecord<HInstruction*>>(inputs_);
-  }
-
   void SetArgumentAt(size_t index, HInstruction* argument) {
     SetRawInputAt(index, argument);
   }
@@ -3878,12 +3907,14 @@
           uint32_t dex_method_index,
           ArtMethod* resolved_method,
           InvokeType invoke_type)
-    : HInstruction(
-          SideEffects::AllExceptGCDependency(), dex_pc),  // Assume write/read on all fields/arrays.
+    : HVariableInputSizeInstruction(
+          SideEffects::AllExceptGCDependency(),  // Assume write/read on all fields/arrays.
+          dex_pc,
+          arena,
+          number_of_arguments + number_of_other_inputs,
+          kArenaAllocInvokeInputs),
       number_of_arguments_(number_of_arguments),
       resolved_method_(resolved_method),
-      inputs_(number_of_arguments + number_of_other_inputs,
-              arena->Adapter(kArenaAllocInvokeInputs)),
       dex_method_index_(dex_method_index),
       intrinsic_(Intrinsics::kNone),
       intrinsic_optimizations_(0) {
@@ -3894,7 +3925,6 @@
 
   uint32_t number_of_arguments_;
   ArtMethod* const resolved_method_;
-  ArenaVector<HUserRecord<HInstruction*>> inputs_;
   const uint32_t dex_method_index_;
   Intrinsics intrinsic_;
 
@@ -3952,12 +3982,6 @@
     // Used for app->boot calls with non-relocatable image and for JIT-compiled calls.
     kDirectAddress,
 
-    // Use ArtMethod* at an address that will be known at link time, embed the direct
-    // address in the code. If the image is relocatable, emit .patch_oat entry.
-    // Used for app->boot calls with relocatable image and boot->boot calls, whether
-    // the image relocatable or not.
-    kDirectAddressWithFixup,
-
     // Load from resolved methods array in the dex cache using a PC-relative load.
     // Used when we need to use the dex cache, for example for invoke-static that
     // may cause class initialization (the entry may point to a resolution method),
@@ -3976,20 +4000,6 @@
     // Recursive call, use local PC-relative call instruction.
     kCallSelf,
 
-    // Use PC-relative call instruction patched at link time.
-    // Used for calls within an oat file, boot->boot or app->app.
-    kCallPCRelative,
-
-    // Call to a known target address, embed the direct address in code.
-    // Used for app->boot call with non-relocatable image and for JIT-compiled calls.
-    kCallDirect,
-
-    // Call to a target address that will be known at link time, embed the direct
-    // address in code. If the image is relocatable, emit .patch_oat entry.
-    // Used for app->boot calls with relocatable image and boot->boot calls, whether
-    // the image relocatable or not.
-    kCallDirectWithFixup,
-
     // Use code pointer from the ArtMethod*.
     // Used when we don't know the target code. This is also the last-resort-kind used when
     // other kinds are unimplemented or impractical (i.e. slow) on a particular architecture.
@@ -4005,7 +4015,6 @@
     //   - the method address for kDirectAddress
     //   - the dex cache arrays offset for kDexCachePcRel.
     uint64_t method_load_data;
-    uint64_t direct_code_ptr;
   };
 
   HInvokeStaticOrDirect(ArenaAllocator* arena,
@@ -4115,7 +4124,6 @@
       return false;
     }
   }
-  bool HasDirectCodePtr() const { return GetCodePtrLocation() == CodePtrLocation::kCallDirect; }
 
   QuickEntrypointEnum GetStringInitEntryPoint() const {
     DCHECK(IsStringInit());
@@ -4132,11 +4140,6 @@
     return dispatch_info_.method_load_data;
   }
 
-  uint64_t GetDirectCodePtr() const {
-    DCHECK(HasDirectCodePtr());
-    return dispatch_info_.direct_code_ptr;
-  }
-
   ClinitCheckRequirement GetClinitCheckRequirement() const {
     return GetPackedField<ClinitCheckRequirementField>();
   }
@@ -4184,10 +4187,6 @@
 
   DECLARE_INSTRUCTION(InvokeStaticOrDirect);
 
- protected:
-  void InsertInputAt(size_t index, HInstruction* input);
-  void RemoveInputAt(size_t index);
-
  private:
   static constexpr size_t kFieldClinitCheckRequirement = kNumberOfInvokePackedBits;
   static constexpr size_t kFieldClinitCheckRequirementSize =
@@ -5519,9 +5518,7 @@
              const DexFile& dex_file,
              bool is_referrers_class,
              uint32_t dex_pc,
-             bool needs_access_check,
-             bool is_in_dex_cache,
-             bool is_in_boot_image)
+             bool needs_access_check)
       : HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc),
         special_input_(HUserRecord<HInstruction*>(current_method)),
         type_index_(type_index),
@@ -5534,8 +5531,8 @@
     SetPackedField<LoadKindField>(
         is_referrers_class ? LoadKind::kReferrersClass : LoadKind::kDexCacheViaMethod);
     SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);
-    SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache);
-    SetPackedFlag<kFlagIsInBootImage>(is_in_boot_image);
+    SetPackedFlag<kFlagIsInDexCache>(false);
+    SetPackedFlag<kFlagIsInBootImage>(false);
     SetPackedFlag<kFlagGenerateClInitCheck>(false);
   }
 
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index ba7012a..4bf5b08 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -54,6 +54,7 @@
 #include "base/timing_logger.h"
 #include "bounds_check_elimination.h"
 #include "builder.h"
+#include "cha_guard_optimization.h"
 #include "code_generator.h"
 #include "compiled_method.h"
 #include "compiler.h"
@@ -517,6 +518,8 @@
     return new (arena) SideEffectsAnalysis(graph);
   } else if (opt_name == HLoopOptimization::kLoopOptimizationPassName) {
     return new (arena) HLoopOptimization(graph, most_recent_induction);
+  } else if (opt_name == CHAGuardOptimization::kCHAGuardOptimizationPassName) {
+    return new (arena) CHAGuardOptimization(graph);
 #ifdef ART_ENABLE_CODEGEN_arm
   } else if (opt_name == arm::DexCacheArrayFixups::kDexCacheArrayFixupsArmPassName) {
     return new (arena) arm::DexCacheArrayFixups(graph, codegen, stats);
@@ -779,6 +782,7 @@
   InstructionSimplifier* simplify4 = new (arena) InstructionSimplifier(
       graph, stats, "instruction_simplifier$before_codegen");
   IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, stats);
+  CHAGuardOptimization* cha_guard = new (arena) CHAGuardOptimization(graph);
 
   HOptimization* optimizations1[] = {
     intrinsics,
@@ -807,6 +811,7 @@
     fold3,  // evaluates code generated by dynamic bce
     simplify3,
     lse,
+    cha_guard,
     dce3,
     // The codegen has a few assumptions that only the instruction simplifier
     // can satisfy. For example, the code generator does not expect to see a
@@ -1193,7 +1198,7 @@
   }
   uint8_t* stack_map_data = nullptr;
   uint8_t* roots_data = nullptr;
-  code_cache->ReserveData(
+  uint32_t data_size = code_cache->ReserveData(
       self, stack_map_size, number_of_roots, method, &stack_map_data, &roots_data);
   if (stack_map_data == nullptr || roots_data == nullptr) {
     return false;
@@ -1212,6 +1217,7 @@
       codegen->GetFpuSpillMask(),
       code_allocator.GetMemory().data(),
       code_allocator.GetSize(),
+      data_size,
       osr,
       roots,
       codegen->GetGraph()->HasShouldDeoptimizeFlag(),
diff --git a/compiler/optimizing/pc_relative_fixups_mips.cc b/compiler/optimizing/pc_relative_fixups_mips.cc
index 82feb95..e321b9e 100644
--- a/compiler/optimizing/pc_relative_fixups_mips.cc
+++ b/compiler/optimizing/pc_relative_fixups_mips.cc
@@ -45,10 +45,6 @@
   }
 
  private:
-  void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE {
-    HandleInvoke(invoke);
-  }
-
   void InitializePCRelativeBasePointer() {
     // Ensure we only initialize the pointer once.
     if (base_ != nullptr) {
@@ -112,38 +108,6 @@
     block->ReplaceAndRemoveInstructionWith(switch_insn, mips_switch);
   }
 
-  void HandleInvoke(HInvoke* invoke) {
-    // If this is an invoke-static/-direct with PC-relative dex cache array
-    // addressing, we need the PC-relative address base.
-    HInvokeStaticOrDirect* invoke_static_or_direct = invoke->AsInvokeStaticOrDirect();
-    if (invoke_static_or_direct != nullptr) {
-      HInvokeStaticOrDirect::MethodLoadKind method_load_kind =
-          invoke_static_or_direct->GetMethodLoadKind();
-      HInvokeStaticOrDirect::CodePtrLocation code_ptr_location =
-          invoke_static_or_direct->GetCodePtrLocation();
-
-      bool has_extra_input =
-          (method_load_kind == HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) ||
-          (code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup);
-
-      // We can't add a pointer to the constant area if we already have a current
-      // method pointer. This may arise when sharpening doesn't remove the current
-      // method pointer from the invoke.
-      if (invoke_static_or_direct->HasCurrentMethodInput()) {
-        DCHECK(!invoke_static_or_direct->HasPcRelativeDexCache());
-        CHECK(!has_extra_input);
-        return;
-      }
-
-      if (has_extra_input &&
-          !IsCallFreeIntrinsic<IntrinsicLocationsBuilderMIPS>(invoke, codegen_)) {
-        InitializePCRelativeBasePointer();
-        // Add the extra parameter base_.
-        invoke_static_or_direct->AddSpecialInput(base_);
-      }
-    }
-  }
-
   CodeGeneratorMIPS* codegen_;
 
   // The generated HMipsComputeBaseMethodAddress in the entry block needed as an
diff --git a/compiler/optimizing/pretty_printer.h b/compiler/optimizing/pretty_printer.h
index 5891350..c6579dc 100644
--- a/compiler/optimizing/pretty_printer.h
+++ b/compiler/optimizing/pretty_printer.h
@@ -17,7 +17,8 @@
 #ifndef ART_COMPILER_OPTIMIZING_PRETTY_PRINTER_H_
 #define ART_COMPILER_OPTIMIZING_PRETTY_PRINTER_H_
 
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
 #include "nodes.h"
 
 namespace art {
@@ -108,7 +109,7 @@
       : HPrettyPrinter(graph), str_(""), current_block_(nullptr) { }
 
   void PrintInt(int value) OVERRIDE {
-    str_ += StringPrintf("%d", value);
+    str_ += android::base::StringPrintf("%d", value);
   }
 
   void PrintString(const char* value) OVERRIDE {
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index 951cdfb..1af94f3 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -15,7 +15,6 @@
  */
 
 #include "base/arena_allocator.h"
-#include "base/stringprintf.h"
 #include "builder.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index c191c66..33b3875 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -270,7 +270,7 @@
         ScopedObjectAccess soa(Thread::Current());
         HInstruction* insert_point = notNullBlock->GetFirstInstruction();
         ReferenceTypeInfo object_rti = ReferenceTypeInfo::Create(
-            handle_cache_.GetObjectClassHandle(), /* is_exact */ true);
+            handle_cache_.GetObjectClassHandle(), /* is_exact */ false);
         if (ShouldCreateBoundType(insert_point, obj, object_rti, nullptr, notNullBlock)) {
           bound_type = new (graph_->GetArena()) HBoundType(obj);
           bound_type->SetUpperBound(object_rti, /* bound_can_be_null */ false);
@@ -411,7 +411,9 @@
         HInstruction* insert_point = instanceOfTrueBlock->GetFirstInstruction();
         if (ShouldCreateBoundType(insert_point, obj, class_rti, nullptr, instanceOfTrueBlock)) {
           bound_type = new (graph_->GetArena()) HBoundType(obj);
-          bound_type->SetUpperBound(class_rti, /* InstanceOf fails for null. */ false);
+          bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes();
+          bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact),
+                                    /* InstanceOf fails for null. */ false);
           instanceOfTrueBlock->InsertInstructionBefore(bound_type, insert_point);
         } else {
           // We already have a bound type on the position we would need to insert
@@ -605,15 +607,17 @@
     // Narrow the type as much as possible.
     HInstruction* obj = instr->InputAt(0);
     ReferenceTypeInfo obj_rti = obj->GetReferenceTypeInfo();
-    if (class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes()) {
-      instr->SetReferenceTypeInfo(
-          ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ true));
+    if (class_rti.IsExact()) {
+      instr->SetReferenceTypeInfo(class_rti);
     } else if (obj_rti.IsValid()) {
       if (class_rti.IsSupertypeOf(obj_rti)) {
         // Object type is more specific.
         instr->SetReferenceTypeInfo(obj_rti);
       } else {
-        // Upper bound is more specific.
+        // Upper bound is more specific, or unrelated to the object's type.
+        // Note that the object might then be exact, and we know the code dominated by this
+        // bound type is dead. To not confuse potential other optimizations, we mark
+        // the bound as non-exact.
         instr->SetReferenceTypeInfo(
             ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ false));
       }
@@ -644,8 +648,11 @@
 
   if (class_rti.IsValid()) {
     DCHECK(is_first_run_);
+    ScopedObjectAccess soa(Thread::Current());
     // This is the first run of RTP and class is resolved.
-    bound_type->SetUpperBound(class_rti, /* CheckCast succeeds for nulls. */ true);
+    bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes();
+    bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact),
+                              /* CheckCast succeeds for nulls. */ true);
   } else {
     // This is the first run of RTP and class is unresolved. Remove the binding.
     // The instruction itself is removed in VisitBoundType so as to not
@@ -795,21 +802,25 @@
 }
 
 void ReferenceTypePropagation::UpdateBoundType(HBoundType* instr) {
-  ReferenceTypeInfo new_rti = instr->InputAt(0)->GetReferenceTypeInfo();
-  if (!new_rti.IsValid()) {
+  ReferenceTypeInfo input_rti = instr->InputAt(0)->GetReferenceTypeInfo();
+  if (!input_rti.IsValid()) {
     return;  // No new info yet.
   }
 
-  // Make sure that we don't go over the bounded type.
   ReferenceTypeInfo upper_bound_rti = instr->GetUpperBound();
-  if (!upper_bound_rti.IsSupertypeOf(new_rti)) {
-    // Note that the input might be exact, in which case we know the branch leading
-    // to the bound type is dead. We play it safe by not marking the bound type as
-    // exact.
-    bool is_exact = upper_bound_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes();
-    new_rti = ReferenceTypeInfo::Create(upper_bound_rti.GetTypeHandle(), is_exact);
+  if (upper_bound_rti.IsExact()) {
+    instr->SetReferenceTypeInfo(upper_bound_rti);
+  } else if (upper_bound_rti.IsSupertypeOf(input_rti)) {
+    // input is more specific.
+    instr->SetReferenceTypeInfo(input_rti);
+  } else {
+    // upper_bound is more specific or unrelated.
+    // Note that the object might then be exact, and we know the code dominated by this
+    // bound type is dead. To not confuse potential other optimizations, we mark
+    // the bound as non-exact.
+    instr->SetReferenceTypeInfo(
+        ReferenceTypeInfo::Create(upper_bound_rti.GetTypeHandle(), /* is_exact */ false));
   }
-  instr->SetReferenceTypeInfo(new_rti);
 }
 
 // NullConstant inputs are ignored during merging as they do not provide any useful information.
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index bbbb1a1..9fdeccf 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -54,6 +54,24 @@
   }
 }
 
+static bool IsInBootImage(ArtMethod* method) {
+  const std::vector<gc::space::ImageSpace*>& image_spaces =
+      Runtime::Current()->GetHeap()->GetBootImageSpaces();
+  for (gc::space::ImageSpace* image_space : image_spaces) {
+    const auto& method_section = image_space->GetImageHeader().GetMethodsSection();
+    if (method_section.Contains(reinterpret_cast<uint8_t*>(method) - image_space->Begin())) {
+      return true;
+    }
+  }
+  return false;
+}
+
+static bool AOTCanEmbedMethod(ArtMethod* method, const CompilerOptions& options) {
+  // Including patch information means the AOT code will be patched, which we don't
+  // support in the compiler, and is anyways moving away b/33192586.
+  return IsInBootImage(method) && !options.GetCompilePic() && !options.GetIncludePatchInformation();
+}
+
 void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
   if (invoke->IsStringInit()) {
     // Not using the dex cache arrays. But we could still try to use a better dispatch...
@@ -61,68 +79,42 @@
     return;
   }
 
-  HGraph* outer_graph = codegen_->GetGraph();
-  ArtMethod* compiling_method = graph_->GetArtMethod();
+  ArtMethod* callee = invoke->GetResolvedMethod();
+  DCHECK(callee != nullptr);
 
   HInvokeStaticOrDirect::MethodLoadKind method_load_kind;
   HInvokeStaticOrDirect::CodePtrLocation code_ptr_location;
   uint64_t method_load_data = 0u;
-  uint64_t direct_code_ptr = 0u;
 
-  if (invoke->GetResolvedMethod() == outer_graph->GetArtMethod()) {
-    DCHECK(outer_graph->GetArtMethod() != nullptr);
+  // Note: we never call an ArtMethod through a known code pointer, as
+  // we do not want to keep on invoking it if it gets deoptimized. This
+  // applies to both AOT and JIT.
+  // This also avoids having to find out if the code pointer of an ArtMethod
+  // is the resolution trampoline (for ensuring the class is initialized), or
+  // the interpreter entrypoint. Such code pointers we do not want to call
+  // directly.
+  // Only in the case of a recursive call can we call directly, as we know the
+  // class is initialized already or being initialized, and the call will not
+  // be invoked once the method is deoptimized.
+
+  if (callee == codegen_->GetGraph()->GetArtMethod()) {
+    // Recursive call.
     method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kRecursive;
     code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallSelf;
+  } else if (Runtime::Current()->UseJitCompilation() ||
+      AOTCanEmbedMethod(callee, codegen_->GetCompilerOptions())) {
+    // JIT or on-device AOT compilation referencing a boot image method.
+    // Use the method address directly.
+    method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress;
+    method_load_data = reinterpret_cast<uintptr_t>(callee);
+    code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
   } else {
-    uintptr_t direct_code, direct_method;
-    {
-      ScopedObjectAccess soa(Thread::Current());
-      compiler_driver_->GetCodeAndMethodForDirectCall(
-          (compiling_method == nullptr) ? nullptr : compiling_method->GetDeclaringClass(),
-          invoke->GetResolvedMethod(),
-          &direct_code,
-          &direct_method);
-    }
-    if (direct_method != 0u) {  // Should we use a direct pointer to the method?
-      // Note: For JIT, kDirectAddressWithFixup doesn't make sense at all and while
-      // kDirectAddress would be fine for image methods, we don't support it at the moment.
-      DCHECK(!Runtime::Current()->UseJitCompilation());
-      if (direct_method != static_cast<uintptr_t>(-1)) {  // Is the method pointer known now?
-        method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress;
-        method_load_data = direct_method;
-      } else {  // The direct pointer will be known at link time.
-        method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup;
-      }
-    } else {  // Use dex cache.
-      if (!Runtime::Current()->UseJitCompilation()) {
-        // Use PC-relative access to the dex cache arrays.
-        method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative;
-        DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen_->GetInstructionSet()),
-                                    &graph_->GetDexFile());
-        method_load_data = layout.MethodOffset(invoke->GetDexMethodIndex());
-      } else {  // We must go through the ArtMethod's pointer to resolved methods.
-        method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
-      }
-    }
-    if (direct_code != 0u) {  // Should we use a direct pointer to the code?
-      // Note: For JIT, kCallPCRelative and kCallDirectWithFixup don't make sense at all and
-      // while kCallDirect would be fine for image methods, we don't support it at the moment.
-      DCHECK(!Runtime::Current()->UseJitCompilation());
-      const DexFile* dex_file_of_callee = invoke->GetTargetMethod().dex_file;
-      if (direct_code != static_cast<uintptr_t>(-1)) {  // Is the code pointer known now?
-        code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirect;
-        direct_code_ptr = direct_code;
-      } else if (ContainsElement(compiler_driver_->GetDexFilesForOatFile(), dex_file_of_callee)) {
-        // Use PC-relative calls for invokes within a multi-dex oat file.
-        code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative;
-      } else {  // The direct pointer will be known at link time.
-        // NOTE: This is used for app->boot calls when compiling an app against
-        // a relocatable but not yet relocated image.
-        code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup;
-      }
-    } else {  // We must use the code pointer from the ArtMethod.
-      code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
-    }
+    // Use PC-relative access to the dex cache arrays.
+    method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative;
+    DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen_->GetInstructionSet()),
+                                &graph_->GetDexFile());
+    method_load_data = layout.MethodOffset(invoke->GetDexMethodIndex());
+    code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
   }
 
   if (graph_->IsDebuggable()) {
@@ -132,7 +124,7 @@
   }
 
   HInvokeStaticOrDirect::DispatchInfo desired_dispatch_info = {
-      method_load_kind, code_ptr_location, method_load_data, direct_code_ptr
+      method_load_kind, code_ptr_location, method_load_data
   };
   HInvokeStaticOrDirect::DispatchInfo dispatch_info =
       codegen_->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info, invoke);
@@ -140,6 +132,25 @@
 }
 
 void HSharpening::ProcessLoadClass(HLoadClass* load_class) {
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<1> hs(soa.Self());
+  Runtime* runtime = Runtime::Current();
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  const DexFile& dex_file = load_class->GetDexFile();
+  dex::TypeIndex type_index = load_class->GetTypeIndex();
+  Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile())
+      ? compilation_unit_.GetDexCache()
+      : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file));
+  mirror::Class* cls = dex_cache->GetResolvedType(type_index);
+  SharpenClass(load_class, cls, handles_, codegen_, compiler_driver_);
+}
+
+void HSharpening::SharpenClass(HLoadClass* load_class,
+                               mirror::Class* klass,
+                               VariableSizedHandleScope* handles,
+                               CodeGenerator* codegen,
+                               CompilerDriver* compiler_driver) {
+  ScopedAssertNoThreadSuspension sants("Sharpening class in compiler");
   DCHECK(load_class->GetLoadKind() == HLoadClass::LoadKind::kDexCacheViaMethod ||
          load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass)
       << load_class->GetLoadKind();
@@ -153,67 +164,60 @@
   bool is_in_boot_image = false;
   HLoadClass::LoadKind desired_load_kind = static_cast<HLoadClass::LoadKind>(-1);
   uint64_t address = 0u;  // Class or dex cache element address.
-  {
-    ScopedObjectAccess soa(Thread::Current());
-    StackHandleScope<1> hs(soa.Self());
-    Runtime* runtime = Runtime::Current();
-    ClassLinker* class_linker = runtime->GetClassLinker();
-    Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile())
-        ? compilation_unit_.GetDexCache()
-        : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file));
-    mirror::Class* klass = dex_cache->GetResolvedType(type_index);
-    if (codegen_->GetCompilerOptions().IsBootImage()) {
-      // Compiling boot image. Check if the class is a boot image class.
-      DCHECK(!runtime->UseJitCompilation());
-      if (!compiler_driver_->GetSupportBootImageFixup()) {
-        // MIPS64 or compiler_driver_test. Do not sharpen.
-        desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
-      } else if ((klass != nullptr) && compiler_driver_->IsImageClass(
-          dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) {
-        is_in_boot_image = true;
-        is_in_dex_cache = true;
-        desired_load_kind = codegen_->GetCompilerOptions().GetCompilePic()
-            ? HLoadClass::LoadKind::kBootImageLinkTimePcRelative
-            : HLoadClass::LoadKind::kBootImageLinkTimeAddress;
-      } else {
-        // Not a boot image class. We must go through the dex cache.
-        DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file));
-        desired_load_kind = HLoadClass::LoadKind::kDexCachePcRelative;
-      }
+  Runtime* runtime = Runtime::Current();
+  if (codegen->GetCompilerOptions().IsBootImage()) {
+    // Compiling boot image. Check if the class is a boot image class.
+    DCHECK(!runtime->UseJitCompilation());
+    if (!compiler_driver->GetSupportBootImageFixup()) {
+      // MIPS64 or compiler_driver_test. Do not sharpen.
+      desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
+    } else if ((klass != nullptr) && compiler_driver->IsImageClass(
+        dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) {
+      is_in_boot_image = true;
+      is_in_dex_cache = true;
+      desired_load_kind = codegen->GetCompilerOptions().GetCompilePic()
+          ? HLoadClass::LoadKind::kBootImageLinkTimePcRelative
+          : HLoadClass::LoadKind::kBootImageLinkTimeAddress;
     } else {
-      is_in_boot_image = (klass != nullptr) && runtime->GetHeap()->ObjectIsInBootImageSpace(klass);
-      if (runtime->UseJitCompilation()) {
-        // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
-        // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
-        is_in_dex_cache = (klass != nullptr);
-        if (is_in_boot_image) {
-          // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787
-          desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
-          address = reinterpret_cast64<uint64_t>(klass);
-        } else if (is_in_dex_cache) {
-          desired_load_kind = HLoadClass::LoadKind::kJitTableAddress;
-          // We store in the address field the location of the stack reference maintained
-          // by the handle. We do this now so that the code generation does not need to figure
-          // out which class loader to use.
-          address = reinterpret_cast<uint64_t>(handles_->NewHandle(klass).GetReference());
-        } else {
-          // Class not loaded yet. Fallback to the dex cache.
-          // TODO(ngeoffray): Generate HDeoptimize instead.
-          desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
-        }
-      } else if (is_in_boot_image && !codegen_->GetCompilerOptions().GetCompilePic()) {
-        // AOT app compilation. Check if the class is in the boot image.
+      // Not a boot image class. We must go through the dex cache.
+      DCHECK(ContainsElement(compiler_driver->GetDexFilesForOatFile(), &dex_file));
+      desired_load_kind = HLoadClass::LoadKind::kDexCachePcRelative;
+    }
+  } else {
+    is_in_boot_image = (klass != nullptr) && runtime->GetHeap()->ObjectIsInBootImageSpace(klass);
+    if (runtime->UseJitCompilation()) {
+      // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
+      // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
+      is_in_dex_cache = (klass != nullptr);
+      if (is_in_boot_image) {
+        // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787
         desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
         address = reinterpret_cast64<uint64_t>(klass);
+      } else if (is_in_dex_cache) {
+        desired_load_kind = HLoadClass::LoadKind::kJitTableAddress;
+        // We store in the address field the location of the stack reference maintained
+        // by the handle. We do this now so that the code generation does not need to figure
+        // out which class loader to use.
+        address = reinterpret_cast<uint64_t>(handles->NewHandle(klass).GetReference());
       } else {
-        // Not JIT and either the klass is not in boot image or we are compiling in PIC mode.
-        // Use PC-relative load from the dex cache if the dex file belongs
-        // to the oat file that we're currently compiling.
-        desired_load_kind =
-            ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &load_class->GetDexFile())
-                ? HLoadClass::LoadKind::kDexCachePcRelative
-                : HLoadClass::LoadKind::kDexCacheViaMethod;
+        // Class not loaded yet. This happens when the dex code requesting
+        // this `HLoadClass` hasn't been executed in the interpreter.
+        // Fallback to the dex cache.
+        // TODO(ngeoffray): Generate HDeoptimize instead.
+        desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
       }
+    } else if (is_in_boot_image && !codegen->GetCompilerOptions().GetCompilePic()) {
+      // AOT app compilation. Check if the class is in the boot image.
+      desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
+      address = reinterpret_cast64<uint64_t>(klass);
+    } else {
+      // Not JIT and either the klass is not in boot image or we are compiling in PIC mode.
+      // Use PC-relative load from the dex cache if the dex file belongs
+      // to the oat file that we're currently compiling.
+      desired_load_kind =
+          ContainsElement(compiler_driver->GetDexFilesForOatFile(), &load_class->GetDexFile())
+              ? HLoadClass::LoadKind::kDexCachePcRelative
+              : HLoadClass::LoadKind::kDexCacheViaMethod;
     }
   }
   DCHECK_NE(desired_load_kind, static_cast<HLoadClass::LoadKind>(-1));
@@ -239,7 +243,7 @@
     load_class->MarkInDexCache();
   }
 
-  HLoadClass::LoadKind load_kind = codegen_->GetSupportedLoadClassKind(desired_load_kind);
+  HLoadClass::LoadKind load_kind = codegen->GetSupportedLoadClassKind(desired_load_kind);
   switch (load_kind) {
     case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
@@ -252,7 +256,7 @@
       load_class->SetLoadKindWithAddress(load_kind, address);
       break;
     case HLoadClass::LoadKind::kDexCachePcRelative: {
-      PointerSize pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+      PointerSize pointer_size = InstructionSetPointerSize(codegen->GetInstructionSet());
       DexCacheArraysLayout layout(pointer_size, &dex_file);
       size_t element_index = layout.TypeOffset(type_index);
       load_class->SetLoadKindWithDexCacheReference(load_kind, dex_file, element_index);
diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h
index 7418954..ae5ccb3 100644
--- a/compiler/optimizing/sharpening.h
+++ b/compiler/optimizing/sharpening.h
@@ -47,6 +47,14 @@
 
   static constexpr const char* kSharpeningPassName = "sharpening";
 
+  // Used internally but also by the inliner.
+  static void SharpenClass(HLoadClass* load_class,
+                           mirror::Class* klass,
+                           VariableSizedHandleScope* handles,
+                           CodeGenerator* codegen,
+                           CompilerDriver* compiler_driver)
+    REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
   void ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke);
   void ProcessLoadClass(HLoadClass* load_class);
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index 4297634..f69f417 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
+#include "android-base/stringprintf.h"
+
 #include "base/arena_allocator.h"
-#include "base/stringprintf.h"
 #include "builder.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
@@ -35,7 +36,7 @@
   explicit SsaPrettyPrinter(HGraph* graph) : HPrettyPrinter(graph), str_("") {}
 
   void PrintInt(int value) OVERRIDE {
-    str_ += StringPrintf("%d", value);
+    str_ += android::base::StringPrintf("%d", value);
   }
 
   void PrintString(const char* value) OVERRIDE {
diff --git a/compiler/utils/arm/assembler_arm_vixl.cc b/compiler/utils/arm/assembler_arm_vixl.cc
index 1614d04..e5eef37 100644
--- a/compiler/utils/arm/assembler_arm_vixl.cc
+++ b/compiler/utils/arm/assembler_arm_vixl.cc
@@ -23,6 +23,9 @@
 
 using namespace vixl::aarch32;  // NOLINT(build/namespaces)
 
+using vixl::ExactAssemblyScope;
+using vixl::CodeBufferCheckScope;
+
 namespace art {
 namespace arm {
 
@@ -441,7 +444,7 @@
     return;
   }
   Cmp(rn, 0);
-  B(eq, label);
+  B(eq, label, is_far_target);
 }
 
 void ArmVIXLMacroAssembler::CompareAndBranchIfNonZero(vixl32::Register rn,
@@ -452,16 +455,16 @@
     return;
   }
   Cmp(rn, 0);
-  B(ne, label);
+  B(ne, label, is_far_target);
 }
 
 void ArmVIXLMacroAssembler::B(vixl32::Label* label) {
   if (!label->IsBound()) {
     // Try to use 16-bit T2 encoding of B instruction.
     DCHECK(OutsideITBlock());
-    AssemblerAccurateScope ass(this,
-                               kMaxInstructionSizeInBytes,
-                               CodeBufferCheckScope::kMaximumSize);
+    ExactAssemblyScope guard(this,
+                             k16BitT32InstructionSizeInBytes,
+                             CodeBufferCheckScope::kMaximumSize);
     b(al, Narrow, label);
     AddBranchLabel(label);
     return;
@@ -469,13 +472,22 @@
   MacroAssembler::B(label);
 }
 
-void ArmVIXLMacroAssembler::B(vixl32::Condition cond, vixl32::Label* label) {
+void ArmVIXLMacroAssembler::B(vixl32::Condition cond, vixl32::Label* label, bool is_far_target) {
+  if (!label->IsBound() && !is_far_target) {
+    // Try to use 16-bit T2 encoding of B instruction.
+    DCHECK(OutsideITBlock());
+    ExactAssemblyScope guard(this,
+                             k16BitT32InstructionSizeInBytes,
+                             CodeBufferCheckScope::kMaximumSize);
+    b(cond, Narrow, label);
+    AddBranchLabel(label);
+    return;
+  }
   // To further reduce the Bcc encoding size and use 16-bit T1 encoding,
   // we can provide a hint to this function: i.e. far_target=false.
   // By default this function uses 'EncodingSizeType::Best' which generates 32-bit T3 encoding.
   MacroAssembler::B(cond, label);
 }
 
-
 }  // namespace arm
 }  // namespace art
diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h
index 17cf106..322f6c4 100644
--- a/compiler/utils/arm/assembler_arm_vixl.h
+++ b/compiler/utils/arm/assembler_arm_vixl.h
@@ -39,6 +39,13 @@
 
 class ArmVIXLMacroAssembler FINAL : public vixl32::MacroAssembler {
  public:
+  // Most methods fit in a 1KB code buffer, which results in more optimal alloc/realloc and
+  // fewer system calls than a larger default capacity.
+  static constexpr size_t kDefaultCodeBufferCapacity = 1 * KB;
+
+  ArmVIXLMacroAssembler()
+      : vixl32::MacroAssembler(ArmVIXLMacroAssembler::kDefaultCodeBufferCapacity) {}
+
   // 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,
@@ -114,7 +121,7 @@
   // TODO: Remove when MacroAssembler::Add(FlagsUpdate, Condition, Register, Register, Operand)
   // makes the right decision about 16-bit encodings.
   void Add(vixl32::Register rd, vixl32::Register rn, const vixl32::Operand& operand) {
-    if (rd.Is(rn)) {
+    if (rd.Is(rn) && operand.IsPlainRegister()) {
       MacroAssembler::Add(rd, rn, operand);
     } else {
       MacroAssembler::Add(vixl32::DontCare, rd, rn, operand);
@@ -124,7 +131,10 @@
 
   // These interfaces try to use 16-bit T2 encoding of B instruction.
   void B(vixl32::Label* label);
-  void B(vixl32::Condition cond, vixl32::Label* label);
+  // For B(label), we always try to use Narrow encoding, because 16-bit T2 encoding supports
+  // jumping within 2KB range. For B(cond, label), because the supported branch range is 256
+  // bytes; we use the far_target hint to try to use 16-bit T1 encoding for short range jumps.
+  void B(vixl32::Condition cond, vixl32::Label* label, bool is_far_target = true);
 };
 
 class ArmVIXLAssembler FINAL : public Assembler {
@@ -205,6 +215,15 @@
                        int32_t value,
                        vixl32::Condition cond = vixl32::al);
 
+  template <typename T>
+  vixl::aarch32::Literal<T>* CreateLiteralDestroyedWithPool(T value) {
+    vixl::aarch32::Literal<T>* literal =
+        new vixl::aarch32::Literal<T>(value,
+                                      vixl32::RawLiteral::kPlacedWhenUsed,
+                                      vixl32::RawLiteral::kDeletedOnPoolDestruction);
+    return literal;
+  }
+
  private:
   // VIXL assembler.
   ArmVIXLMacroAssembler vixl_masm_;
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 30e8f4e..0147a76 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -16,12 +16,15 @@
 
 #include "assembler_thumb2.h"
 
+#include "android-base/stringprintf.h"
+
 #include "base/stl_util.h"
-#include "base/stringprintf.h"
 #include "utils/assembler_test.h"
 
 namespace art {
 
+using android::base::StringPrintf;
+
 class AssemblerThumb2Test : public AssemblerTest<arm::Thumb2Assembler,
                                                  arm::Register, arm::SRegister,
                                                  uint32_t> {
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index 2d026b8..d07c047 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -24,6 +24,9 @@
 using namespace vixl::aarch32;  // NOLINT(build/namespaces)
 namespace vixl32 = vixl::aarch32;
 
+using vixl::ExactAssemblyScope;
+using vixl::CodeBufferCheckScope;
+
 namespace art {
 namespace arm {
 
@@ -455,16 +458,16 @@
 
     if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value(), kCcDontCare)) {
       if (!out_reg.Equals(in_reg)) {
-        AssemblerAccurateScope guard(asm_.GetVIXLAssembler(),
-                                     3 * vixl32::kMaxInstructionSizeInBytes,
-                                     CodeBufferCheckScope::kMaximumSize);
+        ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
+                                 3 * vixl32::kMaxInstructionSizeInBytes,
+                                 CodeBufferCheckScope::kMaximumSize);
         ___ it(eq, 0xc);
         ___ mov(eq, out_reg.AsVIXLRegister(), 0);
         asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne);
       } else {
-        AssemblerAccurateScope guard(asm_.GetVIXLAssembler(),
-                                     2 * vixl32::kMaxInstructionSizeInBytes,
-                                     CodeBufferCheckScope::kMaximumSize);
+        ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
+                                 2 * vixl32::kMaxInstructionSizeInBytes,
+                                 CodeBufferCheckScope::kMaximumSize);
         ___ it(ne, 0x8);
         asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne);
       }
@@ -493,9 +496,9 @@
     ___ Cmp(scratch.AsVIXLRegister(), 0);
 
     if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value(), kCcDontCare)) {
-      AssemblerAccurateScope guard(asm_.GetVIXLAssembler(),
-                                   2 * vixl32::kMaxInstructionSizeInBytes,
-                                   CodeBufferCheckScope::kMaximumSize);
+      ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
+                               2 * vixl32::kMaxInstructionSizeInBytes,
+                               CodeBufferCheckScope::kMaximumSize);
       ___ it(ne, 0x8);
       asm_.AddConstantInIt(scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne);
     } else {
@@ -586,10 +589,12 @@
 
   ___ Cmp(scratch.AsVIXLRegister(), 0);
   {
-    AssemblerAccurateScope guard(asm_.GetVIXLAssembler(),
-                                 vixl32::kMaxInstructionSizeInBytes,
-                                 CodeBufferCheckScope::kMaximumSize);
-    ___ b(ne, Narrow, exception_blocks_.back()->Entry());
+    ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
+                             vixl32::kMaxInstructionSizeInBytes,
+                             CodeBufferCheckScope::kMaximumSize);
+    vixl32::Label* label = exception_blocks_.back()->Entry();
+    ___ b(ne, Narrow, label);
+    ___ AddBranchLabel(label);
   }
   // TODO: think about using CBNZ here.
 }
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index b34e125..5c48759 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -145,7 +145,8 @@
                                               const std::vector<Reg2*> reg2_registers,
                                               std::string (AssemblerTest::*GetName1)(const Reg1&),
                                               std::string (AssemblerTest::*GetName2)(const Reg2&),
-                                              const std::string& fmt) {
+                                              const std::string& fmt,
+                                              int bias = 0) {
     std::string str;
     std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0));
 
@@ -153,7 +154,7 @@
       for (auto reg2 : reg2_registers) {
         for (int64_t imm : imms) {
           ImmType new_imm = CreateImmediate(imm);
-          (assembler_.get()->*f)(*reg1, *reg2, new_imm);
+          (assembler_.get()->*f)(*reg1, *reg2, new_imm + bias);
           std::string base = fmt;
 
           std::string reg1_string = (this->*GetName1)(*reg1);
@@ -171,7 +172,7 @@
           size_t imm_index = base.find(IMM_TOKEN);
           if (imm_index != std::string::npos) {
             std::ostringstream sreg;
-            sreg << imm;
+            sreg << imm + bias;
             std::string imm_string = sreg.str();
             base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
           }
@@ -188,6 +189,67 @@
     return str;
   }
 
+  template <typename Reg1, typename Reg2, typename Reg3, typename ImmType>
+  std::string RepeatTemplatedRegistersImmBits(void (Ass::*f)(Reg1, Reg2, Reg3, ImmType),
+                                              int imm_bits,
+                                              const std::vector<Reg1*> reg1_registers,
+                                              const std::vector<Reg2*> reg2_registers,
+                                              const std::vector<Reg3*> reg3_registers,
+                                              std::string (AssemblerTest::*GetName1)(const Reg1&),
+                                              std::string (AssemblerTest::*GetName2)(const Reg2&),
+                                              std::string (AssemblerTest::*GetName3)(const Reg3&),
+                                              std::string fmt,
+                                              int bias) {
+    std::string str;
+    std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0));
+
+    for (auto reg1 : reg1_registers) {
+      for (auto reg2 : reg2_registers) {
+        for (auto reg3 : reg3_registers) {
+          for (int64_t imm : imms) {
+            ImmType new_imm = CreateImmediate(imm);
+            (assembler_.get()->*f)(*reg1, *reg2, *reg3, new_imm + bias);
+            std::string base = fmt;
+
+            std::string reg1_string = (this->*GetName1)(*reg1);
+            size_t reg1_index;
+            while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
+              base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
+            }
+
+            std::string reg2_string = (this->*GetName2)(*reg2);
+            size_t reg2_index;
+            while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
+              base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
+            }
+
+            std::string reg3_string = (this->*GetName3)(*reg3);
+            size_t reg3_index;
+            while ((reg3_index = base.find(REG3_TOKEN)) != std::string::npos) {
+              base.replace(reg3_index, ConstexprStrLen(REG3_TOKEN), reg3_string);
+            }
+
+            size_t imm_index = base.find(IMM_TOKEN);
+            if (imm_index != std::string::npos) {
+              std::ostringstream sreg;
+              sreg << imm + bias;
+              std::string imm_string = sreg.str();
+              base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
+            }
+
+            if (str.size() > 0) {
+              str += "\n";
+            }
+            str += base;
+          }
+        }
+      }
+    }
+    // Add a newline at the end.
+    str += "\n";
+    return str;
+  }
+
   template <typename ImmType, typename Reg1, typename Reg2>
   std::string RepeatTemplatedImmBitsRegisters(void (Ass::*f)(ImmType, Reg1, Reg2),
                                               const std::vector<Reg1*> reg1_registers,
@@ -245,14 +307,15 @@
                                              int imm_bits,
                                              const std::vector<Reg*> registers,
                                              std::string (AssemblerTest::*GetName)(const RegType&),
-                                             const std::string& fmt) {
+                                             const std::string& fmt,
+                                             int bias) {
     std::string str;
     std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0));
 
     for (auto reg : registers) {
       for (int64_t imm : imms) {
         ImmType new_imm = CreateImmediate(imm);
-        (assembler_.get()->*f)(*reg, new_imm);
+        (assembler_.get()->*f)(*reg, new_imm + bias);
         std::string base = fmt;
 
         std::string reg_string = (this->*GetName)(*reg);
@@ -264,7 +327,7 @@
         size_t imm_index = base.find(IMM_TOKEN);
         if (imm_index != std::string::npos) {
           std::ostringstream sreg;
-          sreg << imm;
+          sreg << imm + bias;
           std::string imm_string = sreg.str();
           base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
         }
@@ -281,36 +344,60 @@
   }
 
   template <typename ImmType>
-  std::string RepeatRRIb(void (Ass::*f)(Reg, Reg, ImmType), int imm_bits, const std::string& fmt) {
+  std::string RepeatRRIb(void (Ass::*f)(Reg, Reg, ImmType),
+                         int imm_bits,
+                         const std::string& fmt,
+                         int bias = 0) {
     return RepeatTemplatedRegistersImmBits<Reg, Reg, ImmType>(f,
         imm_bits,
         GetRegisters(),
         GetRegisters(),
         &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
         &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
-        fmt);
+        fmt,
+        bias);
   }
 
   template <typename ImmType>
-  std::string RepeatRIb(void (Ass::*f)(Reg, ImmType), int imm_bits, const std::string& fmt) {
+  std::string RepeatRRRIb(void (Ass::*f)(Reg, Reg, Reg, ImmType),
+                          int imm_bits,
+                          const std::string& fmt,
+                          int bias = 0) {
+    return RepeatTemplatedRegistersImmBits<Reg, Reg, Reg, ImmType>(f,
+        imm_bits,
+        GetRegisters(),
+        GetRegisters(),
+        GetRegisters(),
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        fmt,
+        bias);
+  }
+
+  template <typename ImmType>
+  std::string RepeatRIb(void (Ass::*f)(Reg, ImmType), int imm_bits, std::string fmt, int bias = 0) {
     return RepeatTemplatedRegisterImmBits<Reg, ImmType>(f,
         imm_bits,
         GetRegisters(),
         &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
-        fmt);
+        fmt,
+        bias);
   }
 
   template <typename ImmType>
   std::string RepeatFRIb(void (Ass::*f)(FPReg, Reg, ImmType),
                          int imm_bits,
-                         const std::string& fmt) {
+                         const std::string& fmt,
+                         int bias = 0) {
     return RepeatTemplatedRegistersImmBits<FPReg, Reg, ImmType>(f,
         imm_bits,
         GetFPRegisters(),
         GetRegisters(),
         &AssemblerTest::GetFPRegName,
         &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
-        fmt);
+        fmt,
+        bias);
   }
 
   std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), const std::string& fmt) {
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 50a1d9f..4e9b619 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -1717,6 +1717,11 @@
 
   __ ExceptionPoll(scratch_register, 0);
 
+  // Push the target out of range of branch emitted by ExceptionPoll.
+  for (int i = 0; i < 64; i++) {
+    __ Store(FrameOffset(2047), scratch_register, 4);
+  }
+
   __ DecreaseFrameSize(4096);
   __ DecreaseFrameSize(32);
   __ RemoveFrame(frame_size, callee_save_regs);
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 69e1d8f..ab4f9e9 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -5458,94 +5458,160 @@
 };
 
 const char* const VixlJniHelpersResults[] = {
-  "   0:  e92d 4de0   stmdb sp!, {r5, r6, r7, r8, sl, fp, lr}\n",
-  "   4:  ed2d 8a10   vpush {s16-s31}\n",
-  "   8:  b089        sub sp, #36 ; 0x24\n",
-  "   a:  9000        str r0, [sp, #0]\n",
-  "   c:  9121        str r1, [sp, #132]  ; 0x84\n",
-  "   e:  ed8d 0a22   vstr  s0, [sp, #136]  ; 0x88\n",
-  "  12:  9223        str r2, [sp, #140]  ; 0x8c\n",
-  "  14:  9324        str r3, [sp, #144]  ; 0x90\n",
-  "  16:  b088        sub sp, #32\n",
-  "  18:  f5ad 5d80   sub.w sp, sp, #4096 ; 0x1000\n",
-  "  1c:  9808        ldr r0, [sp, #32]\n",
-  "  1e:  981f        ldr r0, [sp, #124]  ; 0x7c\n",
-  "  20:  9821        ldr r0, [sp, #132]  ; 0x84\n",
-  "  22:  98ff        ldr r0, [sp, #1020] ; 0x3fc\n",
-  "  24:  f8dd 0400   ldr.w r0, [sp, #1024] ; 0x400\n",
-  "  28:  f8dd cffc   ldr.w ip, [sp, #4092] ; 0xffc\n",
-  "  2c:  f50d 5c80   add.w ip, sp, #4096 ; 0x1000\n",
-  "  30:  f8dc c000   ldr.w ip, [ip]\n",
-  "  34:  f8d9 c200   ldr.w ip, [r9, #512]  ; 0x200\n",
-  "  38:  f8dc 0080   ldr.w r0, [ip, #128]  ; 0x80\n",
-  "  3c:  9008        str r0, [sp, #32]\n",
-  "  3e:  901f        str r0, [sp, #124]  ; 0x7c\n",
-  "  40:  9021        str r0, [sp, #132]  ; 0x84\n",
-  "  42:  90ff        str r0, [sp, #1020] ; 0x3fc\n",
-  "  44:  f8cd 0400   str.w r0, [sp, #1024] ; 0x400\n",
-  "  48:  f8cd cffc   str.w ip, [sp, #4092] ; 0xffc\n",
-  "  4c:  f84d 5d04   str.w r5, [sp, #-4]!\n",
-  "  50:  f50d 5580   add.w r5, sp, #4096 ; 0x1000\n",
-  "  54:  f8c5 c004   str.w ip, [r5, #4]\n",
-  "  58:  f85d 5b04   ldr.w r5, [sp], #4\n",
-  "  5c:  f04f 0cff   mov.w ip, #255  ; 0xff\n",
-  "  60:  f8cd c030   str.w ip, [sp, #48] ; 0x30\n",
-  "  64:  f06f 4c7f   mvn.w ip, #4278190080 ; 0xff000000\n",
-  "  68:  f8cd c030   str.w ip, [sp, #48] ; 0x30\n",
-  "  6c:  f8cd c030   str.w ip, [sp, #48] ; 0x30\n",
-  "  70:  f8cd c030   str.w ip, [sp, #48] ; 0x30\n",
-  "  74:  900c        str r0, [sp, #48] ; 0x30\n",
-  "  76:  f8dd c030   ldr.w ip, [sp, #48] ; 0x30\n",
-  "  7a:  f8cd c034   str.w ip, [sp, #52] ; 0x34\n",
-  "  7e:  f50d 5c80   add.w ip, sp, #4096 ; 0x1000\n",
-  "  82:  f8c9 c200   str.w ip, [r9, #512]  ; 0x200\n",
-  "  86:  f8c9 d200   str.w sp, [r9, #512]  ; 0x200\n",
-  "  8a:  f8d0 c030   ldr.w ip, [r0, #48] ; 0x30\n",
-  "  8e:  47e0        blx ip\n",
-  "  90:  f8dd c02c   ldr.w ip, [sp, #44] ; 0x2c\n",
-  "  94:  f8cd c030   str.w ip, [sp, #48] ; 0x30\n",
-  "  98:  f8d9 c200   ldr.w ip, [r9, #512]  ; 0x200\n",
-  "  9c:  f8cd c02c   str.w ip, [sp, #44] ; 0x2c\n",
-  "  a0:  f8dd c02c   ldr.w ip, [sp, #44] ; 0x2c\n",
-  "  a4:  f8cd c030   str.w ip, [sp, #48] ; 0x30\n",
-  "  a8:  4648        mov r0, r9\n",
-  "  aa:  f8cd 9030   str.w r9, [sp, #48] ; 0x30\n",
-  "  ae:  4684        mov ip, r0\n",
-  "  b0:  f1bc 0f00   cmp.w ip, #0\n",
-  "  b4:  bf18        it  ne\n",
-  "  b6:  f10d 0c30   addne.w ip, sp, #48 ; 0x30\n",
-  "  ba:  f10d 0c30   add.w ip, sp, #48 ; 0x30\n",
-  "  be:  f1bc 0f00   cmp.w ip, #0\n",
-  "  c2:  bf0c        ite eq\n",
-  "  c4:  2000        moveq r0, #0\n",
-  "  c6:  a80c        addne r0, sp, #48 ; 0x30\n",
-  "  c8:  f8dd c040   ldr.w ip, [sp, #64] ; 0x40\n",
-  "  cc:  f1bc 0f00   cmp.w ip, #0\n",
-  "  d0:  bf18        it  ne\n",
-  "  d2:  f10d 0c40   addne.w ip, sp, #64 ; 0x40\n",
-  "  d6:  f8cd c030   str.w ip, [sp, #48] ; 0x30\n",
-  "  da:  f1bc 0f00   cmp.w ip, #0\n",
-  "  de:  bf0c        ite eq\n",
-  "  e0:  2000        moveq r0, #0\n",
-  "  e2:  4668        movne r0, sp\n",
-  "  e4:  f1bc 0f00   cmp.w ip, #0\n",
-  "  e8:  bf0c        ite eq\n",
-  "  ea:  2000        moveq r0, #0\n",
-  "  ec:  f20d 4001   addwne  r0, sp, #1025 ; 0x401\n",
-  "  f0:  f1bc 0f00   cmp.w ip, #0\n",
-  "  f4:  bf18        it  ne\n",
-  "  f6:  f20d 4c01   addwne  ip, sp, #1025 ; 0x401\n",
-  "  fa:  f8d9 c084   ldr.w ip, [r9, #132]  ; 0x84\n",
-  "  fe:  f1bc 0f00   cmp.w ip, #0\n",
-  " 102:  d107        bne.n 114 <VixlJniHelpers+0x114>\n",
-  " 104:  f50d 5d80   add.w sp, sp, #4096 ; 0x1000\n",
-  " 108:  b008        add sp, #32\n",
-  " 10a:  b009        add sp, #36 ; 0x24\n",
-  " 10c:  ecbd 8a10   vpop  {s16-s31}\n",
-  " 110:  e8bd 8de0   ldmia.w sp!, {r5, r6, r7, r8, sl, fp, pc}\n",
-  " 114:  4660        mov r0, ip\n",
-  " 116:  f8d9 c2b0   ldr.w ip, [r9, #688]  ; 0x2b0\n",
-  " 11a:  47e0        blx ip\n",
+  "   0:	e92d 4de0 	stmdb	sp!, {r5, r6, r7, r8, sl, fp, lr}\n",
+  "   4:	ed2d 8a10 	vpush	{s16-s31}\n",
+  "   8:	b089      	sub	sp, #36	; 0x24\n",
+  "   a:	9000      	str	r0, [sp, #0]\n",
+  "   c:	9121      	str	r1, [sp, #132]	; 0x84\n",
+  "   e:	ed8d 0a22 	vstr	s0, [sp, #136]	; 0x88\n",
+  "  12:	9223      	str	r2, [sp, #140]	; 0x8c\n",
+  "  14:	9324      	str	r3, [sp, #144]	; 0x90\n",
+  "  16:	b088      	sub	sp, #32\n",
+  "  18:	f5ad 5d80 	sub.w	sp, sp, #4096	; 0x1000\n",
+  "  1c:	9808      	ldr	r0, [sp, #32]\n",
+  "  1e:	981f      	ldr	r0, [sp, #124]	; 0x7c\n",
+  "  20:	9821      	ldr	r0, [sp, #132]	; 0x84\n",
+  "  22:	98ff      	ldr	r0, [sp, #1020]	; 0x3fc\n",
+  "  24:	f8dd 0400 	ldr.w	r0, [sp, #1024]	; 0x400\n",
+  "  28:	f8dd cffc 	ldr.w	ip, [sp, #4092]	; 0xffc\n",
+  "  2c:	f50d 5c80 	add.w	ip, sp, #4096	; 0x1000\n",
+  "  30:	f8dc c000 	ldr.w	ip, [ip]\n",
+  "  34:	f8d9 c200 	ldr.w	ip, [r9, #512]	; 0x200\n",
+  "  38:	f8dc 0080 	ldr.w	r0, [ip, #128]	; 0x80\n",
+  "  3c:	9008      	str	r0, [sp, #32]\n",
+  "  3e:	901f      	str	r0, [sp, #124]	; 0x7c\n",
+  "  40:	9021      	str	r0, [sp, #132]	; 0x84\n",
+  "  42:	90ff      	str	r0, [sp, #1020]	; 0x3fc\n",
+  "  44:	f8cd 0400 	str.w	r0, [sp, #1024]	; 0x400\n",
+  "  48:	f8cd cffc 	str.w	ip, [sp, #4092]	; 0xffc\n",
+  "  4c:	f84d 5d04 	str.w	r5, [sp, #-4]!\n",
+  "  50:	f50d 5580 	add.w	r5, sp, #4096	; 0x1000\n",
+  "  54:	f8c5 c004 	str.w	ip, [r5, #4]\n",
+  "  58:	f85d 5b04 	ldr.w	r5, [sp], #4\n",
+  "  5c:	f04f 0cff 	mov.w	ip, #255	; 0xff\n",
+  "  60:	f8cd c030 	str.w	ip, [sp, #48]	; 0x30\n",
+  "  64:	f06f 4c7f 	mvn.w	ip, #4278190080	; 0xff000000\n",
+  "  68:	f8cd c030 	str.w	ip, [sp, #48]	; 0x30\n",
+  "  6c:	f8cd c030 	str.w	ip, [sp, #48]	; 0x30\n",
+  "  70:	f8cd c030 	str.w	ip, [sp, #48]	; 0x30\n",
+  "  74:	900c      	str	r0, [sp, #48]	; 0x30\n",
+  "  76:	f8dd c030 	ldr.w	ip, [sp, #48]	; 0x30\n",
+  "  7a:	f8cd c034 	str.w	ip, [sp, #52]	; 0x34\n",
+  "  7e:	f50d 5c80 	add.w	ip, sp, #4096	; 0x1000\n",
+  "  82:	f8c9 c200 	str.w	ip, [r9, #512]	; 0x200\n",
+  "  86:	f8c9 d200 	str.w	sp, [r9, #512]	; 0x200\n",
+  "  8a:	f8d0 c030 	ldr.w	ip, [r0, #48]	; 0x30\n",
+  "  8e:	47e0      	blx	ip\n",
+  "  90:	f8dd c02c 	ldr.w	ip, [sp, #44]	; 0x2c\n",
+  "  94:	f8cd c030 	str.w	ip, [sp, #48]	; 0x30\n",
+  "  98:	f8d9 c200 	ldr.w	ip, [r9, #512]	; 0x200\n",
+  "  9c:	f8cd c02c 	str.w	ip, [sp, #44]	; 0x2c\n",
+  "  a0:	f8dd c02c 	ldr.w	ip, [sp, #44]	; 0x2c\n",
+  "  a4:	f8cd c030 	str.w	ip, [sp, #48]	; 0x30\n",
+  "  a8:	4648      	mov	r0, r9\n",
+  "  aa:	f8cd 9030 	str.w	r9, [sp, #48]	; 0x30\n",
+  "  ae:	4684      	mov	ip, r0\n",
+  "  b0:	f1bc 0f00 	cmp.w	ip, #0\n",
+  "  b4:	bf18      	it	ne\n",
+  "  b6:	f10d 0c30 	addne.w	ip, sp, #48	; 0x30\n",
+  "  ba:	f10d 0c30 	add.w	ip, sp, #48	; 0x30\n",
+  "  be:	f1bc 0f00 	cmp.w	ip, #0\n",
+  "  c2:	bf0c      	ite	eq\n",
+  "  c4:	2000      	moveq	r0, #0\n",
+  "  c6:	a80c      	addne	r0, sp, #48	; 0x30\n",
+  "  c8:	f8dd c040 	ldr.w	ip, [sp, #64]	; 0x40\n",
+  "  cc:	f1bc 0f00 	cmp.w	ip, #0\n",
+  "  d0:	bf18      	it	ne\n",
+  "  d2:	f10d 0c40 	addne.w	ip, sp, #64	; 0x40\n",
+  "  d6:	f8cd c030 	str.w	ip, [sp, #48]	; 0x30\n",
+  "  da:	f1bc 0f00 	cmp.w	ip, #0\n",
+  "  de:	bf0c      	ite	eq\n",
+  "  e0:	2000      	moveq	r0, #0\n",
+  "  e2:	4668      	movne	r0, sp\n",
+  "  e4:	f1bc 0f00 	cmp.w	ip, #0\n",
+  "  e8:	bf0c      	ite	eq\n",
+  "  ea:	2000      	moveq	r0, #0\n",
+  "  ec:	f20d 4001 	addwne	r0, sp, #1025	; 0x401\n",
+  "  f0:	f1bc 0f00 	cmp.w	ip, #0\n",
+  "  f4:	bf18      	it	ne\n",
+  "  f6:	f20d 4c01 	addwne	ip, sp, #1025	; 0x401\n",
+  "  fa:	f8d9 c084 	ldr.w	ip, [r9, #132]	; 0x84\n",
+  "  fe:	f1bc 0f00 	cmp.w	ip, #0\n",
+  " 102:	d171      	bne.n	1e8 <VixlJniHelpers+0x1e8>\n",
+  " 104:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 108:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 10c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 110:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 114:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 118:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 11c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 120:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 124:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 128:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 12c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 130:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 134:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 138:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 13c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 140:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 144:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 148:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 14c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 150:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 154:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 158:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 15c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 160:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 164:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 168:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 16c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 170:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 174:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 178:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 17c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 180:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 184:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 188:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 18c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 190:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 194:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 198:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 19c:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1a0:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1a4:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1a8:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1ac:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1b0:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1b4:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1b8:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1bc:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1c0:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1c4:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1c8:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1cc:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1d0:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1d4:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1d8:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1dc:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1e0:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1e4:	f000 b802 	b.w	1ec <VixlJniHelpers+0x1ec>\n",
+  " 1e8:	f000 b818 	b.w	21c <VixlJniHelpers+0x21c>\n",
+  " 1ec:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1f0:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1f4:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1f8:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 1fc:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 200:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 204:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 208:	f8cd c7ff 	str.w	ip, [sp, #2047]	; 0x7ff\n",
+  " 20c:	f50d 5d80 	add.w	sp, sp, #4096	; 0x1000\n",
+  " 210:	b008      	add	sp, #32\n",
+  " 212:	b009      	add	sp, #36	; 0x24\n",
+  " 214:	ecbd 8a10 	vpop	{s16-s31}\n",
+  " 218:	e8bd 8de0 	ldmia.w	sp!, {r5, r6, r7, r8, sl, fp, pc}\n",
+  " 21c:	4660      	mov	r0, ip\n",
+  " 21e:	f8d9 c2b0 	ldr.w	ip, [r9, #688]	; 0x2b0\n",
+  " 222:	47e0      	blx	ip\n",
   nullptr
 };
 
diff --git a/compiler/utils/dedupe_set-inl.h b/compiler/utils/dedupe_set-inl.h
index ac54813..c06e9ca 100644
--- a/compiler/utils/dedupe_set-inl.h
+++ b/compiler/utils/dedupe_set-inl.h
@@ -23,10 +23,11 @@
 #include <inttypes.h>
 #include <unordered_map>
 
+#include "android-base/stringprintf.h"
+
 #include "base/mutex.h"
 #include "base/hash_set.h"
 #include "base/stl_util.h"
-#include "base/stringprintf.h"
 #include "base/time_utils.h"
 
 namespace art {
@@ -238,13 +239,13 @@
   for (HashType shard = 0; shard < kShard; ++shard) {
     shards_[shard]->UpdateStats(self, &stats);
   }
-  return StringPrintf("%zu collisions, %zu max hash collisions, "
-                      "%zu/%zu probe distance, %" PRIu64 " ns hash time",
-                      stats.collision_sum,
-                      stats.collision_max,
-                      stats.total_probe_distance,
-                      stats.total_size,
-                      hash_time_);
+  return android::base::StringPrintf("%zu collisions, %zu max hash collisions, "
+                                     "%zu/%zu probe distance, %" PRIu64 " ns hash time",
+                                     stats.collision_sum,
+                                     stats.collision_max,
+                                     stats.total_probe_distance,
+                                     stats.total_size,
+                                     hash_time_);
 }
 
 
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 3dcad6a..5e83e82 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -635,6 +635,13 @@
   DsFsmInstrRrr(EmitR(0x1f, rt, rd, static_cast<Register>(pos + size - 1), pos, 0x04), rd, rd, rt);
 }
 
+void MipsAssembler::Lsa(Register rd, Register rs, Register rt, int saPlusOne) {
+  CHECK(IsR6());
+  CHECK(1 <= saPlusOne && saPlusOne <= 4) << saPlusOne;
+  int sa = saPlusOne - 1;
+  DsFsmInstrRrr(EmitR(0x0, rs, rt, rd, sa, 0x05), rd, rs, rt);
+}
+
 void MipsAssembler::Lb(Register rt, Register rs, uint16_t imm16) {
   DsFsmInstrRrr(EmitI(0x20, rs, rt, imm16), rt, rs, rs);
 }
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index 800dc5f..2fca185 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -262,6 +262,7 @@
   void Srav(Register rd, Register rt, Register rs);
   void Ext(Register rd, Register rt, int pos, int size);  // R2+
   void Ins(Register rd, Register rt, int pos, int size);  // R2+
+  void Lsa(Register rd, Register rs, Register rt, int saPlusOne);  // R6
 
   void Lb(Register rt, Register rs, uint16_t imm16);
   void Lh(Register rt, Register rs, uint16_t imm16);
diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc
index a52f519..30667ef 100644
--- a/compiler/utils/mips/assembler_mips32r6_test.cc
+++ b/compiler/utils/mips/assembler_mips32r6_test.cc
@@ -319,6 +319,14 @@
   DriverStr(RepeatRR(&mips::MipsAssembler::Bitswap, "bitswap ${reg1}, ${reg2}"), "bitswap");
 }
 
+TEST_F(AssemblerMIPS32r6Test, Lsa) {
+  DriverStr(RepeatRRRIb(&mips::MipsAssembler::Lsa,
+                        2,
+                        "lsa ${reg1}, ${reg2}, ${reg3}, {imm}",
+                        1),
+            "lsa");
+}
+
 TEST_F(AssemblerMIPS32r6Test, Seleqz) {
   DriverStr(RepeatRRR(&mips::MipsAssembler::Seleqz, "seleqz ${reg1}, ${reg2}, ${reg3}"),
             "seleqz");
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index 1a21df9..998f2c7 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -35,11 +35,14 @@
   for (auto& exception_block : exception_blocks_) {
     EmitExceptionPoll(&exception_block);
   }
+  ReserveJumpTableSpace();
+  EmitLiterals();
   PromoteBranches();
 }
 
 void Mips64Assembler::FinalizeInstructions(const MemoryRegion& region) {
   EmitBranches();
+  EmitJumpTables();
   Assembler::FinalizeInstructions(region);
   PatchCFI();
 }
@@ -318,6 +321,18 @@
   EmitR(0x1f, rs, rt, static_cast<GpuRegister>(pos + size - 33), pos - 32, 0x6);
 }
 
+void Mips64Assembler::Lsa(GpuRegister rd, GpuRegister rs, GpuRegister rt, int saPlusOne) {
+  CHECK(1 <= saPlusOne && saPlusOne <= 4) << saPlusOne;
+  int sa = saPlusOne - 1;
+  EmitR(0x0, rs, rt, rd, sa, 0x05);
+}
+
+void Mips64Assembler::Dlsa(GpuRegister rd, GpuRegister rs, GpuRegister rt, int saPlusOne) {
+  CHECK(1 <= saPlusOne && saPlusOne <= 4) << saPlusOne;
+  int sa = saPlusOne - 1;
+  EmitR(0x0, rs, rt, rd, sa, 0x15);
+}
+
 void Mips64Assembler::Wsbh(GpuRegister rd, GpuRegister rt) {
   EmitRtd(0x1f, rt, rd, 2, 0x20);
 }
@@ -450,10 +465,29 @@
   EmitI(0x27, rs, rt, imm16);
 }
 
+void Mips64Assembler::Lwpc(GpuRegister rs, uint32_t imm19) {
+  CHECK(IsUint<19>(imm19)) << imm19;
+  EmitI21(0x3B, rs, (0x01 << 19) | imm19);
+}
+
+void Mips64Assembler::Lwupc(GpuRegister rs, uint32_t imm19) {
+  CHECK(IsUint<19>(imm19)) << imm19;
+  EmitI21(0x3B, rs, (0x02 << 19) | imm19);
+}
+
+void Mips64Assembler::Ldpc(GpuRegister rs, uint32_t imm18) {
+  CHECK(IsUint<18>(imm18)) << imm18;
+  EmitI21(0x3B, rs, (0x06 << 18) | imm18);
+}
+
 void Mips64Assembler::Lui(GpuRegister rt, uint16_t imm16) {
   EmitI(0xf, static_cast<GpuRegister>(0), rt, imm16);
 }
 
+void Mips64Assembler::Aui(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0xf, rs, rt, imm16);
+}
+
 void Mips64Assembler::Dahi(GpuRegister rs, uint16_t imm16) {
   EmitI(1, rs, static_cast<GpuRegister>(6), imm16);
 }
@@ -548,6 +582,10 @@
   EmitI26(0x32, imm26);
 }
 
+void Mips64Assembler::Balc(uint32_t imm26) {
+  EmitI26(0x3A, imm26);
+}
+
 void Mips64Assembler::Jic(GpuRegister rt, uint16_t imm16) {
   EmitI(0x36, static_cast<GpuRegister>(0), rt, imm16);
 }
@@ -1049,6 +1087,20 @@
   TemplateLoadConst64(this, rd, value);
 }
 
+void Mips64Assembler::Addiu32(GpuRegister rt, GpuRegister rs, int32_t value) {
+  if (IsInt<16>(value)) {
+    Addiu(rt, rs, value);
+  } else {
+    int16_t high = High16Bits(value);
+    int16_t low = Low16Bits(value);
+    high += (low < 0) ? 1 : 0;  // Account for sign extension in addiu.
+    Aui(rt, rs, high);
+    if (low != 0) {
+      Addiu(rt, rt, low);
+    }
+  }
+}
+
 void Mips64Assembler::Daddiu64(GpuRegister rt, GpuRegister rs, int64_t value, GpuRegister rtmp) {
   if (IsInt<16>(value)) {
     Daddiu(rt, rs, value);
@@ -1064,19 +1116,37 @@
   type_ = (offset_size <= branch_info_[short_type].offset_size) ? short_type : long_type;
 }
 
-void Mips64Assembler::Branch::InitializeType(bool is_call) {
+void Mips64Assembler::Branch::InitializeType(Type initial_type) {
   OffsetBits offset_size = GetOffsetSizeNeeded(location_, target_);
-  if (is_call) {
-    InitShortOrLong(offset_size, kCall, kLongCall);
-  } else if (condition_ == kUncond) {
-    InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch);
-  } else {
-    if (condition_ == kCondEQZ || condition_ == kCondNEZ) {
-      // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions.
-      type_ = (offset_size <= kOffset23) ? kCondBranch : kLongCondBranch;
-    } else {
-      InitShortOrLong(offset_size, kCondBranch, kLongCondBranch);
-    }
+  switch (initial_type) {
+    case kLabel:
+    case kLiteral:
+    case kLiteralUnsigned:
+    case kLiteralLong:
+      CHECK(!IsResolved());
+      type_ = initial_type;
+      break;
+    case kCall:
+      InitShortOrLong(offset_size, kCall, kLongCall);
+      break;
+    case kCondBranch:
+      switch (condition_) {
+        case kUncond:
+          InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch);
+          break;
+        case kCondEQZ:
+        case kCondNEZ:
+          // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions.
+          type_ = (offset_size <= kOffset23) ? kCondBranch : kLongCondBranch;
+          break;
+        default:
+          InitShortOrLong(offset_size, kCondBranch, kLongCondBranch);
+          break;
+      }
+      break;
+    default:
+      LOG(FATAL) << "Unexpected branch type " << initial_type;
+      UNREACHABLE();
   }
   old_type_ = type_;
 }
@@ -1109,14 +1179,14 @@
   }
 }
 
-Mips64Assembler::Branch::Branch(uint32_t location, uint32_t target)
+Mips64Assembler::Branch::Branch(uint32_t location, uint32_t target, bool is_call)
     : old_location_(location),
       location_(location),
       target_(target),
       lhs_reg_(ZERO),
       rhs_reg_(ZERO),
       condition_(kUncond) {
-  InitializeType(false);
+  InitializeType(is_call ? kCall : kCondBranch);
 }
 
 Mips64Assembler::Branch::Branch(uint32_t location,
@@ -1164,19 +1234,18 @@
     // Branch condition is always true, make the branch unconditional.
     condition_ = kUncond;
   }
-  InitializeType(false);
+  InitializeType(kCondBranch);
 }
 
-Mips64Assembler::Branch::Branch(uint32_t location, uint32_t target, GpuRegister indirect_reg)
+Mips64Assembler::Branch::Branch(uint32_t location, GpuRegister dest_reg, Type label_or_literal_type)
     : old_location_(location),
       location_(location),
-      target_(target),
-      lhs_reg_(indirect_reg),
+      target_(kUnresolved),
+      lhs_reg_(dest_reg),
       rhs_reg_(ZERO),
       condition_(kUncond) {
-  CHECK_NE(indirect_reg, ZERO);
-  CHECK_NE(indirect_reg, AT);
-  InitializeType(true);
+  CHECK_NE(dest_reg, ZERO);
+  InitializeType(label_or_literal_type);
 }
 
 Mips64Assembler::BranchCondition Mips64Assembler::Branch::OppositeCondition(
@@ -1278,11 +1347,23 @@
     case kUncondBranch:
     case kCondBranch:
     case kCall:
+    // Near label.
+    case kLabel:
+    // Near literals.
+    case kLiteral:
+    case kLiteralUnsigned:
+    case kLiteralLong:
       return false;
     // Long branches.
     case kLongUncondBranch:
     case kLongCondBranch:
     case kLongCall:
+    // Far label.
+    case kFarLabel:
+    // Far literals.
+    case kFarLiteral:
+    case kFarLiteralUnsigned:
+    case kFarLiteralLong:
       return true;
   }
   UNREACHABLE();
@@ -1351,6 +1432,20 @@
     case kCall:
       type_ = kLongCall;
       break;
+    // Near label.
+    case kLabel:
+      type_ = kFarLabel;
+      break;
+    // Near literals.
+    case kLiteral:
+      type_ = kFarLiteral;
+      break;
+    case kLiteralUnsigned:
+      type_ = kFarLiteralUnsigned;
+      break;
+    case kLiteralLong:
+      type_ = kFarLiteralLong;
+      break;
     default:
       // Note: 'type_' is already long.
       break;
@@ -1397,7 +1492,15 @@
   uint32_t ofs_mask = 0xFFFFFFFF >> (32 - GetOffsetSize());
   // Calculate the byte distance between instructions and also account for
   // different PC-relative origins.
-  uint32_t offset = target_ - GetOffsetLocation() - branch_info_[type_].pc_org * sizeof(uint32_t);
+  uint32_t offset_location = GetOffsetLocation();
+  if (type_ == kLiteralLong) {
+    // Special case for the ldpc instruction, whose address (PC) is rounded down to
+    // a multiple of 8 before adding the offset.
+    // Note, branch promotion has already taken care of aligning `target_` to an
+    // address that's a multiple of 8.
+    offset_location = RoundDown(offset_location, sizeof(uint64_t));
+  }
+  uint32_t offset = target_ - offset_location - branch_info_[type_].pc_org * sizeof(uint32_t);
   // Prepare the offset for encoding into the instruction(s).
   offset = (offset & ofs_mask) >> branch_info_[type_].offset_shift;
   return offset;
@@ -1444,7 +1547,7 @@
   label->BindTo(bound_pc);
 }
 
-uint32_t Mips64Assembler::GetLabelLocation(Mips64Label* label) const {
+uint32_t Mips64Assembler::GetLabelLocation(const Mips64Label* label) const {
   CHECK(label->IsBound());
   uint32_t target = label->Position();
   if (label->prev_branch_id_plus_one_) {
@@ -1500,7 +1603,7 @@
 
 void Mips64Assembler::Buncond(Mips64Label* label) {
   uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
-  branches_.emplace_back(buffer_.Size(), target);
+  branches_.emplace_back(buffer_.Size(), target, /* is_call */ false);
   FinalizeLabeledBranch(label);
 }
 
@@ -1517,12 +1620,148 @@
   FinalizeLabeledBranch(label);
 }
 
-void Mips64Assembler::Call(Mips64Label* label, GpuRegister indirect_reg) {
+void Mips64Assembler::Call(Mips64Label* label) {
   uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
-  branches_.emplace_back(buffer_.Size(), target, indirect_reg);
+  branches_.emplace_back(buffer_.Size(), target, /* is_call */ true);
   FinalizeLabeledBranch(label);
 }
 
+void Mips64Assembler::LoadLabelAddress(GpuRegister dest_reg, Mips64Label* label) {
+  // Label address loads are treated as pseudo branches since they require very similar handling.
+  DCHECK(!label->IsBound());
+  branches_.emplace_back(buffer_.Size(), dest_reg, Branch::kLabel);
+  FinalizeLabeledBranch(label);
+}
+
+Literal* Mips64Assembler::NewLiteral(size_t size, const uint8_t* data) {
+  // We don't support byte and half-word literals.
+  if (size == 4u) {
+    literals_.emplace_back(size, data);
+    return &literals_.back();
+  } else {
+    DCHECK_EQ(size, 8u);
+    long_literals_.emplace_back(size, data);
+    return &long_literals_.back();
+  }
+}
+
+void Mips64Assembler::LoadLiteral(GpuRegister dest_reg,
+                                  LoadOperandType load_type,
+                                  Literal* literal) {
+  // Literal loads are treated as pseudo branches since they require very similar handling.
+  Branch::Type literal_type;
+  switch (load_type) {
+    case kLoadWord:
+      DCHECK_EQ(literal->GetSize(), 4u);
+      literal_type = Branch::kLiteral;
+      break;
+    case kLoadUnsignedWord:
+      DCHECK_EQ(literal->GetSize(), 4u);
+      literal_type = Branch::kLiteralUnsigned;
+      break;
+    case kLoadDoubleword:
+      DCHECK_EQ(literal->GetSize(), 8u);
+      literal_type = Branch::kLiteralLong;
+      break;
+    default:
+      LOG(FATAL) << "Unexpected literal load type " << load_type;
+      UNREACHABLE();
+  }
+  Mips64Label* label = literal->GetLabel();
+  DCHECK(!label->IsBound());
+  branches_.emplace_back(buffer_.Size(), dest_reg, literal_type);
+  FinalizeLabeledBranch(label);
+}
+
+JumpTable* Mips64Assembler::CreateJumpTable(std::vector<Mips64Label*>&& labels) {
+  jump_tables_.emplace_back(std::move(labels));
+  JumpTable* table = &jump_tables_.back();
+  DCHECK(!table->GetLabel()->IsBound());
+  return table;
+}
+
+void Mips64Assembler::ReserveJumpTableSpace() {
+  if (!jump_tables_.empty()) {
+    for (JumpTable& table : jump_tables_) {
+      Mips64Label* label = table.GetLabel();
+      Bind(label);
+
+      // Bulk ensure capacity, as this may be large.
+      size_t orig_size = buffer_.Size();
+      size_t required_capacity = orig_size + table.GetSize();
+      if (required_capacity > buffer_.Capacity()) {
+        buffer_.ExtendCapacity(required_capacity);
+      }
+#ifndef NDEBUG
+      buffer_.has_ensured_capacity_ = true;
+#endif
+
+      // Fill the space with dummy data as the data is not final
+      // until the branches have been promoted. And we shouldn't
+      // be moving uninitialized data during branch promotion.
+      for (size_t cnt = table.GetData().size(), i = 0; i < cnt; i++) {
+        buffer_.Emit<uint32_t>(0x1abe1234u);
+      }
+
+#ifndef NDEBUG
+      buffer_.has_ensured_capacity_ = false;
+#endif
+    }
+  }
+}
+
+void Mips64Assembler::EmitJumpTables() {
+  if (!jump_tables_.empty()) {
+    CHECK(!overwriting_);
+    // Switch from appending instructions at the end of the buffer to overwriting
+    // existing instructions (here, jump tables) in the buffer.
+    overwriting_ = true;
+
+    for (JumpTable& table : jump_tables_) {
+      Mips64Label* table_label = table.GetLabel();
+      uint32_t start = GetLabelLocation(table_label);
+      overwrite_location_ = start;
+
+      for (Mips64Label* target : table.GetData()) {
+        CHECK_EQ(buffer_.Load<uint32_t>(overwrite_location_), 0x1abe1234u);
+        // The table will contain target addresses relative to the table start.
+        uint32_t offset = GetLabelLocation(target) - start;
+        Emit(offset);
+      }
+    }
+
+    overwriting_ = false;
+  }
+}
+
+void Mips64Assembler::EmitLiterals() {
+  if (!literals_.empty()) {
+    for (Literal& literal : literals_) {
+      Mips64Label* label = literal.GetLabel();
+      Bind(label);
+      AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+      DCHECK_EQ(literal.GetSize(), 4u);
+      for (size_t i = 0, size = literal.GetSize(); i != size; ++i) {
+        buffer_.Emit<uint8_t>(literal.GetData()[i]);
+      }
+    }
+  }
+  if (!long_literals_.empty()) {
+    // Reserve 4 bytes for potential alignment. If after the branch promotion the 64-bit
+    // literals don't end up 8-byte-aligned, they will be moved down 4 bytes.
+    Emit(0);  // NOP.
+    for (Literal& literal : long_literals_) {
+      Mips64Label* label = literal.GetLabel();
+      Bind(label);
+      AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+      DCHECK_EQ(literal.GetSize(), 8u);
+      for (size_t i = 0, size = literal.GetSize(); i != size; ++i) {
+        buffer_.Emit<uint8_t>(literal.GetData()[i]);
+      }
+    }
+  }
+}
+
 void Mips64Assembler::PromoteBranches() {
   // Promote short branches to long as necessary.
   bool changed;
@@ -1561,6 +1800,35 @@
       end = branch.GetOldLocation();
     }
   }
+
+  // Align 64-bit literals by moving them down by 4 bytes if needed.
+  // This will reduce the PC-relative distance, which should be safe for both near and far literals.
+  if (!long_literals_.empty()) {
+    uint32_t first_literal_location = GetLabelLocation(long_literals_.front().GetLabel());
+    size_t lit_size = long_literals_.size() * sizeof(uint64_t);
+    size_t buf_size = buffer_.Size();
+    // 64-bit literals must be at the very end of the buffer.
+    CHECK_EQ(first_literal_location + lit_size, buf_size);
+    if (!IsAligned<sizeof(uint64_t)>(first_literal_location)) {
+      buffer_.Move(first_literal_location - sizeof(uint32_t), first_literal_location, lit_size);
+      // The 4 reserved bytes proved useless, reduce the buffer size.
+      buffer_.Resize(buf_size - sizeof(uint32_t));
+      // Reduce target addresses in literal and address loads by 4 bytes in order for correct
+      // offsets from PC to be generated.
+      for (auto& branch : branches_) {
+        uint32_t target = branch.GetTarget();
+        if (target >= first_literal_location) {
+          branch.Resolve(target - sizeof(uint32_t));
+        }
+      }
+      // If after this we ever call GetLabelLocation() to get the location of a 64-bit literal,
+      // we need to adjust the location of the literal's label as well.
+      for (Literal& literal : long_literals_) {
+        // Bound label's position is negative, hence incrementing it instead of decrementing.
+        literal.GetLabel()->position_ += sizeof(uint32_t);
+      }
+    }
+  }
 }
 
 // Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
@@ -1569,11 +1837,23 @@
   {  1, 0, 1, Mips64Assembler::Branch::kOffset28, 2 },  // kUncondBranch
   {  2, 0, 1, Mips64Assembler::Branch::kOffset18, 2 },  // kCondBranch
                                                         // Exception: kOffset23 for beqzc/bnezc
-  {  2, 0, 0, Mips64Assembler::Branch::kOffset21, 2 },  // kCall
+  {  1, 0, 1, Mips64Assembler::Branch::kOffset28, 2 },  // kCall
+  // Near label.
+  {  1, 0, 0, Mips64Assembler::Branch::kOffset21, 2 },  // kLabel
+  // Near literals.
+  {  1, 0, 0, Mips64Assembler::Branch::kOffset21, 2 },  // kLiteral
+  {  1, 0, 0, Mips64Assembler::Branch::kOffset21, 2 },  // kLiteralUnsigned
+  {  1, 0, 0, Mips64Assembler::Branch::kOffset21, 3 },  // kLiteralLong
   // Long branches.
   {  2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 },  // kLongUncondBranch
   {  3, 1, 0, Mips64Assembler::Branch::kOffset32, 0 },  // kLongCondBranch
-  {  3, 0, 0, Mips64Assembler::Branch::kOffset32, 0 },  // kLongCall
+  {  2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 },  // kLongCall
+  // Far label.
+  {  2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 },  // kFarLabel
+  // Far literals.
+  {  2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 },  // kFarLiteral
+  {  2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 },  // kFarLiteralUnsigned
+  {  2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 },  // kFarLiteralLong
 };
 
 // Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
@@ -1597,8 +1877,26 @@
       break;
     case Branch::kCall:
       CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
+      Balc(offset);
+      break;
+
+    // Near label.
+    case Branch::kLabel:
+      CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
       Addiupc(lhs, offset);
-      Jialc(lhs, 0);
+      break;
+    // Near literals.
+    case Branch::kLiteral:
+      CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
+      Lwpc(lhs, offset);
+      break;
+    case Branch::kLiteralUnsigned:
+      CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
+      Lwupc(lhs, offset);
+      break;
+    case Branch::kLiteralLong:
+      CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
+      Ldpc(lhs, offset);
       break;
 
     // Long branches.
@@ -1616,11 +1914,37 @@
       Jic(AT, Low16Bits(offset));
       break;
     case Branch::kLongCall:
+      offset += (offset & 0x8000) << 1;  // Account for sign extension in jialc.
+      CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
+      Auipc(AT, High16Bits(offset));
+      Jialc(AT, Low16Bits(offset));
+      break;
+
+    // Far label.
+    case Branch::kFarLabel:
       offset += (offset & 0x8000) << 1;  // Account for sign extension in daddiu.
       CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
-      Auipc(lhs, High16Bits(offset));
-      Daddiu(lhs, lhs, Low16Bits(offset));
-      Jialc(lhs, 0);
+      Auipc(AT, High16Bits(offset));
+      Daddiu(lhs, AT, Low16Bits(offset));
+      break;
+    // Far literals.
+    case Branch::kFarLiteral:
+      offset += (offset & 0x8000) << 1;  // Account for sign extension in lw.
+      CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
+      Auipc(AT, High16Bits(offset));
+      Lw(lhs, AT, Low16Bits(offset));
+      break;
+    case Branch::kFarLiteralUnsigned:
+      offset += (offset & 0x8000) << 1;  // Account for sign extension in lwu.
+      CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
+      Auipc(AT, High16Bits(offset));
+      Lwu(lhs, AT, Low16Bits(offset));
+      break;
+    case Branch::kFarLiteralLong:
+      offset += (offset & 0x8000) << 1;  // Account for sign extension in ld.
+      CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
+      Auipc(AT, High16Bits(offset));
+      Ld(lhs, AT, Low16Bits(offset));
       break;
   }
   CHECK_EQ(overwrite_location_, branch->GetEndLocation());
@@ -1631,8 +1955,8 @@
   Buncond(label);
 }
 
-void Mips64Assembler::Jialc(Mips64Label* label, GpuRegister indirect_reg) {
-  Call(label, indirect_reg);
+void Mips64Assembler::Balc(Mips64Label* label) {
+  Call(label);
 }
 
 void Mips64Assembler::Bltc(GpuRegister rs, GpuRegister rt, Mips64Label* label) {
diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h
index 238cb9d..a0a1db6 100644
--- a/compiler/utils/mips64/assembler_mips64.h
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -17,9 +17,11 @@
 #ifndef ART_COMPILER_UTILS_MIPS64_ASSEMBLER_MIPS64_H_
 #define ART_COMPILER_UTILS_MIPS64_ASSEMBLER_MIPS64_H_
 
+#include <deque>
 #include <utility>
 #include <vector>
 
+#include "base/arena_containers.h"
 #include "base/enums.h"
 #include "base/macros.h"
 #include "constants_mips64.h"
@@ -312,6 +314,79 @@
   DISALLOW_COPY_AND_ASSIGN(Mips64Label);
 };
 
+// Assembler literal is a value embedded in code, retrieved using a PC-relative load.
+class Literal {
+ public:
+  static constexpr size_t kMaxSize = 8;
+
+  Literal(uint32_t size, const uint8_t* data)
+      : label_(), size_(size) {
+    DCHECK_LE(size, Literal::kMaxSize);
+    memcpy(data_, data, size);
+  }
+
+  template <typename T>
+  T GetValue() const {
+    DCHECK_EQ(size_, sizeof(T));
+    T value;
+    memcpy(&value, data_, sizeof(T));
+    return value;
+  }
+
+  uint32_t GetSize() const {
+    return size_;
+  }
+
+  const uint8_t* GetData() const {
+    return data_;
+  }
+
+  Mips64Label* GetLabel() {
+    return &label_;
+  }
+
+  const Mips64Label* GetLabel() const {
+    return &label_;
+  }
+
+ private:
+  Mips64Label label_;
+  const uint32_t size_;
+  uint8_t data_[kMaxSize];
+
+  DISALLOW_COPY_AND_ASSIGN(Literal);
+};
+
+// Jump table: table of labels emitted after the code and before the literals. Similar to literals.
+class JumpTable {
+ public:
+  explicit JumpTable(std::vector<Mips64Label*>&& labels)
+      : label_(), labels_(std::move(labels)) {
+  }
+
+  size_t GetSize() const {
+    return labels_.size() * sizeof(uint32_t);
+  }
+
+  const std::vector<Mips64Label*>& GetData() const {
+    return labels_;
+  }
+
+  Mips64Label* GetLabel() {
+    return &label_;
+  }
+
+  const Mips64Label* GetLabel() const {
+    return &label_;
+  }
+
+ private:
+  Mips64Label label_;
+  std::vector<Mips64Label*> labels_;
+
+  DISALLOW_COPY_AND_ASSIGN(JumpTable);
+};
+
 // Slowpath entered when Thread::Current()->_exception is non-null.
 class Mips64ExceptionSlowPath {
  public:
@@ -341,6 +416,9 @@
       : Assembler(arena),
         overwriting_(false),
         overwrite_location_(0),
+        literals_(arena->Adapter(kArenaAllocAssembler)),
+        long_literals_(arena->Adapter(kArenaAllocAssembler)),
+        jump_tables_(arena->Adapter(kArenaAllocAssembler)),
         last_position_adjustment_(0),
         last_old_position_(0),
         last_branch_id_(0) {
@@ -386,18 +464,20 @@
   void Nor(GpuRegister rd, GpuRegister rs, GpuRegister rt);
 
   void Bitswap(GpuRegister rd, GpuRegister rt);
-  void Dbitswap(GpuRegister rd, GpuRegister rt);
+  void Dbitswap(GpuRegister rd, GpuRegister rt);  // MIPS64
   void Seb(GpuRegister rd, GpuRegister rt);
   void Seh(GpuRegister rd, GpuRegister rt);
-  void Dsbh(GpuRegister rd, GpuRegister rt);
-  void Dshd(GpuRegister rd, GpuRegister rt);
+  void Dsbh(GpuRegister rd, GpuRegister rt);  // MIPS64
+  void Dshd(GpuRegister rd, GpuRegister rt);  // MIPS64
   void Dext(GpuRegister rs, GpuRegister rt, int pos, int size);  // MIPS64
   void Dinsu(GpuRegister rt, GpuRegister rs, int pos, int size);  // MIPS64
+  void Lsa(GpuRegister rd, GpuRegister rs, GpuRegister rt, int saPlusOne);
+  void Dlsa(GpuRegister rd, GpuRegister rs, GpuRegister rt, int saPlusOne);  // MIPS64
   void Wsbh(GpuRegister rd, GpuRegister rt);
   void Sc(GpuRegister rt, GpuRegister base, int16_t imm9 = 0);
-  void Scd(GpuRegister rt, GpuRegister base, int16_t imm9 = 0);
+  void Scd(GpuRegister rt, GpuRegister base, int16_t imm9 = 0);  // MIPS64
   void Ll(GpuRegister rt, GpuRegister base, int16_t imm9 = 0);
-  void Lld(GpuRegister rt, GpuRegister base, int16_t imm9 = 0);
+  void Lld(GpuRegister rt, GpuRegister base, int16_t imm9 = 0);  // MIPS64
 
   void Sll(GpuRegister rd, GpuRegister rt, int shamt);
   void Srl(GpuRegister rd, GpuRegister rt, int shamt);
@@ -409,7 +489,7 @@
   void Srav(GpuRegister rd, GpuRegister rt, GpuRegister rs);
   void Dsll(GpuRegister rd, GpuRegister rt, int shamt);  // MIPS64
   void Dsrl(GpuRegister rd, GpuRegister rt, int shamt);  // MIPS64
-  void Drotr(GpuRegister rd, GpuRegister rt, int shamt);
+  void Drotr(GpuRegister rd, GpuRegister rt, int shamt);  // MIPS64
   void Dsra(GpuRegister rd, GpuRegister rt, int shamt);  // MIPS64
   void Dsll32(GpuRegister rd, GpuRegister rt, int shamt);  // MIPS64
   void Dsrl32(GpuRegister rd, GpuRegister rt, int shamt);  // MIPS64
@@ -427,7 +507,11 @@
   void Lbu(GpuRegister rt, GpuRegister rs, uint16_t imm16);
   void Lhu(GpuRegister rt, GpuRegister rs, uint16_t imm16);
   void Lwu(GpuRegister rt, GpuRegister rs, uint16_t imm16);  // MIPS64
+  void Lwpc(GpuRegister rs, uint32_t imm19);
+  void Lwupc(GpuRegister rs, uint32_t imm19);  // MIPS64
+  void Ldpc(GpuRegister rs, uint32_t imm18);  // MIPS64
   void Lui(GpuRegister rt, uint16_t imm16);
+  void Aui(GpuRegister rt, GpuRegister rs, uint16_t imm16);
   void Dahi(GpuRegister rs, uint16_t imm16);  // MIPS64
   void Dati(GpuRegister rs, uint16_t imm16);  // MIPS64
   void Sync(uint32_t stype);
@@ -445,8 +529,8 @@
   void Selnez(GpuRegister rd, GpuRegister rs, GpuRegister rt);
   void Clz(GpuRegister rd, GpuRegister rs);
   void Clo(GpuRegister rd, GpuRegister rs);
-  void Dclz(GpuRegister rd, GpuRegister rs);
-  void Dclo(GpuRegister rd, GpuRegister rs);
+  void Dclz(GpuRegister rd, GpuRegister rs);  // MIPS64
+  void Dclo(GpuRegister rd, GpuRegister rs);  // MIPS64
 
   void Jalr(GpuRegister rd, GpuRegister rs);
   void Jalr(GpuRegister rs);
@@ -454,6 +538,7 @@
   void Auipc(GpuRegister rs, uint16_t imm16);
   void Addiupc(GpuRegister rs, uint32_t imm19);
   void Bc(uint32_t imm26);
+  void Balc(uint32_t imm26);
   void Jic(GpuRegister rt, uint16_t imm16);
   void Jialc(GpuRegister rt, uint16_t imm16);
   void Bltc(GpuRegister rs, GpuRegister rt, uint16_t imm16);
@@ -566,6 +651,7 @@
   // This function is only used for testing purposes.
   void RecordLoadConst64Path(int value);
 
+  void Addiu32(GpuRegister rt, GpuRegister rs, int32_t value);
   void Daddiu64(GpuRegister rt, GpuRegister rs, int64_t value, GpuRegister rtmp = AT);  // MIPS64
 
   void Bind(Label* label) OVERRIDE {
@@ -605,8 +691,32 @@
     UNREACHABLE();
   }
 
+  // Create a new literal with a given value.
+  // NOTE: Force the template parameter to be explicitly specified.
+  template <typename T>
+  Literal* NewLiteral(typename Identity<T>::type value) {
+    static_assert(std::is_integral<T>::value, "T must be an integral type.");
+    return NewLiteral(sizeof(value), reinterpret_cast<const uint8_t*>(&value));
+  }
+
+  // Load label address using PC-relative loads. To be used with data labels in the literal /
+  // jump table area only and not with regular code labels.
+  void LoadLabelAddress(GpuRegister dest_reg, Mips64Label* label);
+
+  // Create a new literal with the given data.
+  Literal* NewLiteral(size_t size, const uint8_t* data);
+
+  // Load literal using PC-relative loads.
+  void LoadLiteral(GpuRegister dest_reg, LoadOperandType load_type, Literal* literal);
+
+  // Create a jump table for the given labels that will be emitted when finalizing.
+  // When the table is emitted, offsets will be relative to the location of the table.
+  // The table location is determined by the location of its label (the label precedes
+  // the table data) and should be loaded using LoadLabelAddress().
+  JumpTable* CreateJumpTable(std::vector<Mips64Label*>&& labels);
+
   void Bc(Mips64Label* label);
-  void Jialc(Mips64Label* label, GpuRegister indirect_reg);
+  void Balc(Mips64Label* label);
   void Bltc(GpuRegister rs, GpuRegister rt, Mips64Label* label);
   void Bltzc(GpuRegister rt, Mips64Label* label);
   void Bgtzc(GpuRegister rt, Mips64Label* label);
@@ -756,12 +866,15 @@
 
   // Returns the (always-)current location of a label (can be used in class CodeGeneratorMIPS64,
   // must be used instead of Mips64Label::GetPosition()).
-  uint32_t GetLabelLocation(Mips64Label* label) const;
+  uint32_t GetLabelLocation(const Mips64Label* label) const;
 
   // Get the final position of a label after local fixup based on the old position
   // recorded before FinalizeCode().
   uint32_t GetAdjustedPosition(uint32_t old_position);
 
+  // Note that PC-relative literal loads are handled as pseudo branches because they need very
+  // similar relocation and may similarly expand in size to accomodate for larger offsets relative
+  // to PC.
   enum BranchCondition {
     kCondLT,
     kCondGE,
@@ -791,10 +904,22 @@
       kUncondBranch,
       kCondBranch,
       kCall,
+      // Near label.
+      kLabel,
+      // Near literals.
+      kLiteral,
+      kLiteralUnsigned,
+      kLiteralLong,
       // Long branches.
       kLongUncondBranch,
       kLongCondBranch,
       kLongCall,
+      // Far label.
+      kFarLabel,
+      // Far literals.
+      kFarLiteral,
+      kFarLiteralUnsigned,
+      kFarLiteralLong,
     };
 
     // Bit sizes of offsets defined as enums to minimize chance of typos.
@@ -830,16 +955,16 @@
     };
     static const BranchInfo branch_info_[/* Type */];
 
-    // Unconditional branch.
-    Branch(uint32_t location, uint32_t target);
+    // Unconditional branch or call.
+    Branch(uint32_t location, uint32_t target, bool is_call);
     // Conditional branch.
     Branch(uint32_t location,
            uint32_t target,
            BranchCondition condition,
            GpuRegister lhs_reg,
-           GpuRegister rhs_reg = ZERO);
-    // Call (branch and link) that stores the target address in a given register (i.e. T9).
-    Branch(uint32_t location, uint32_t target, GpuRegister indirect_reg);
+           GpuRegister rhs_reg);
+    // Label address (in literal area) or literal.
+    Branch(uint32_t location, GpuRegister dest_reg, Type label_or_literal_type);
 
     // Some conditional branches with lhs = rhs are effectively NOPs, while some
     // others are effectively unconditional. MIPSR6 conditional branches require lhs != rhs.
@@ -923,7 +1048,7 @@
 
    private:
     // Completes branch construction by determining and recording its type.
-    void InitializeType(bool is_call);
+    void InitializeType(Type initial_type);
     // Helper for the above.
     void InitShortOrLong(OffsetBits ofs_size, Type short_type, Type long_type);
 
@@ -932,7 +1057,7 @@
     uint32_t target_;            // Offset into assembler buffer in bytes.
 
     GpuRegister lhs_reg_;        // Left-hand side register in conditional branches or
-                                 // indirect call register.
+                                 // destination register in literals.
     GpuRegister rhs_reg_;        // Right-hand side register in conditional branches.
     BranchCondition condition_;  // Condition for conditional branches.
 
@@ -957,12 +1082,15 @@
              BranchCondition condition,
              GpuRegister lhs,
              GpuRegister rhs = ZERO);
-  void Call(Mips64Label* label, GpuRegister indirect_reg);
+  void Call(Mips64Label* label);
   void FinalizeLabeledBranch(Mips64Label* label);
 
   Branch* GetBranch(uint32_t branch_id);
   const Branch* GetBranch(uint32_t branch_id) const;
 
+  void EmitLiterals();
+  void ReserveJumpTableSpace();
+  void EmitJumpTables();
   void PromoteBranches();
   void EmitBranch(Branch* branch);
   void EmitBranches();
@@ -981,6 +1109,14 @@
   // The current overwrite location.
   uint32_t overwrite_location_;
 
+  // Use std::deque<> for literal labels to allow insertions at the end
+  // without invalidating pointers and references to existing elements.
+  ArenaDeque<Literal> literals_;
+  ArenaDeque<Literal> long_literals_;  // 64-bit literals separated for alignment reasons.
+
+  // Jump table list.
+  ArenaDeque<JumpTable> jump_tables_;
+
   // Data for AdjustedPosition(), see the description there.
   uint32_t last_position_adjustment_;
   uint32_t last_old_position_;
diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc
index ba8f25e..f2cbebb 100644
--- a/compiler/utils/mips64/assembler_mips64_test.cc
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -576,83 +576,83 @@
             RepeatRRNoDupes(&mips64::Mips64Assembler::Jalr, "jalr ${reg1}, ${reg2}"), "jalr");
 }
 
-TEST_F(AssemblerMIPS64Test, Jialc) {
+TEST_F(AssemblerMIPS64Test, Balc) {
   mips64::Mips64Label label1, label2;
-  __ Jialc(&label1, mips64::T9);
+  __ Balc(&label1);
   constexpr size_t kAdduCount1 = 63;
   for (size_t i = 0; i != kAdduCount1; ++i) {
     __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
   }
   __ Bind(&label1);
-  __ Jialc(&label2, mips64::T9);
+  __ Balc(&label2);
   constexpr size_t kAdduCount2 = 64;
   for (size_t i = 0; i != kAdduCount2; ++i) {
     __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
   }
   __ Bind(&label2);
-  __ Jialc(&label1, mips64::T9);
+  __ Balc(&label1);
 
   std::string expected =
       ".set noreorder\n"
-      "lapc $t9, 1f\n"
-      "jialc $t9, 0\n" +
+      "balc 1f\n" +
       RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") +
       "1:\n"
-      "lapc $t9, 2f\n"
-      "jialc $t9, 0\n" +
+      "balc 2f\n" +
       RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") +
       "2:\n"
-      "lapc $t9, 1b\n"
-      "jialc $t9, 0\n";
-  DriverStr(expected, "Jialc");
+      "balc 1b\n";
+  DriverStr(expected, "Balc");
 }
 
-TEST_F(AssemblerMIPS64Test, LongJialc) {
+TEST_F(AssemblerMIPS64Test, LongBalc) {
+  constexpr uint32_t kNopCount1 = (1u << 25) + 1;
+  constexpr uint32_t kNopCount2 = (1u << 25) + 1;
+  constexpr uint32_t kRequiredCapacity = (kNopCount1 + kNopCount2 + 6u) * 4u;
+  ASSERT_LT(__ GetBuffer()->Capacity(), kRequiredCapacity);
+  __ GetBuffer()->ExtendCapacity(kRequiredCapacity);
   mips64::Mips64Label label1, label2;
-  __ Jialc(&label1, mips64::T9);
-  constexpr uint32_t kAdduCount1 = (1u << 18) + 1;
-  for (uint32_t i = 0; i != kAdduCount1; ++i) {
-    __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+  __ Balc(&label1);
+  for (uint32_t i = 0; i != kNopCount1; ++i) {
+    __ Nop();
   }
   __ Bind(&label1);
-  __ Jialc(&label2, mips64::T9);
-  constexpr uint32_t kAdduCount2 = (1u << 18) + 1;
-  for (uint32_t i = 0; i != kAdduCount2; ++i) {
-    __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+  __ Balc(&label2);
+  for (uint32_t i = 0; i != kNopCount2; ++i) {
+    __ Nop();
   }
   __ Bind(&label2);
-  __ Jialc(&label1, mips64::T9);
+  __ Balc(&label1);
 
-  uint32_t offset_forward1 = 3 + kAdduCount1;  // 3: account for auipc, daddiu and jic.
+  uint32_t offset_forward1 = 2 + kNopCount1;  // 2: account for auipc and jialc.
   offset_forward1 <<= 2;
-  offset_forward1 += (offset_forward1 & 0x8000) << 1;  // Account for sign extension in daddiu.
+  offset_forward1 += (offset_forward1 & 0x8000) << 1;  // Account for sign extension in jialc.
 
-  uint32_t offset_forward2 = 3 + kAdduCount2;  // 3: account for auipc, daddiu and jic.
+  uint32_t offset_forward2 = 2 + kNopCount2;  // 2: account for auipc and jialc.
   offset_forward2 <<= 2;
-  offset_forward2 += (offset_forward2 & 0x8000) << 1;  // Account for sign extension in daddiu.
+  offset_forward2 += (offset_forward2 & 0x8000) << 1;  // Account for sign extension in jialc.
 
-  uint32_t offset_back = -(3 + kAdduCount2);  // 3: account for auipc, daddiu and jic.
+  uint32_t offset_back = -(2 + kNopCount2);  // 2: account for auipc and jialc.
   offset_back <<= 2;
-  offset_back += (offset_back & 0x8000) << 1;  // Account for sign extension in daddiu.
+  offset_back += (offset_back & 0x8000) << 1;  // Account for sign extension in jialc.
 
+  // Note, we're using the ".fill" directive to tell the assembler to generate many NOPs
+  // instead of generating them ourselves in the source code. This saves a few minutes
+  // of test time.
   std::ostringstream oss;
   oss <<
       ".set noreorder\n"
-      "auipc $t9, 0x" << std::hex << High16Bits(offset_forward1) << "\n"
-      "daddiu $t9, 0x" << std::hex << Low16Bits(offset_forward1) << "\n"
-      "jialc $t9, 0\n" <<
-      RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") <<
+      "auipc $at, 0x" << std::hex << High16Bits(offset_forward1) << "\n"
+      "jialc $at, 0x" << std::hex << Low16Bits(offset_forward1) << "\n"
+      ".fill 0x" << std::hex << kNopCount1 << " , 4, 0\n"
       "1:\n"
-      "auipc $t9, 0x" << std::hex << High16Bits(offset_forward2) << "\n"
-      "daddiu $t9, 0x" << std::hex << Low16Bits(offset_forward2) << "\n"
-      "jialc $t9, 0\n" <<
-      RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") <<
+      "auipc $at, 0x" << std::hex << High16Bits(offset_forward2) << "\n"
+      "jialc $at, 0x" << std::hex << Low16Bits(offset_forward2) << "\n"
+      ".fill 0x" << std::hex << kNopCount2 << " , 4, 0\n"
       "2:\n"
-      "auipc $t9, 0x" << std::hex << High16Bits(offset_back) << "\n"
-      "daddiu $t9, 0x" << std::hex << Low16Bits(offset_back) << "\n"
-      "jialc $t9, 0\n";
+      "auipc $at, 0x" << std::hex << High16Bits(offset_back) << "\n"
+      "jialc $at, 0x" << std::hex << Low16Bits(offset_back) << "\n";
   std::string expected = oss.str();
-  DriverStr(expected, "LongJialc");
+  DriverStr(expected, "LongBalc");
 }
 
 TEST_F(AssemblerMIPS64Test, Bc) {
@@ -827,6 +827,258 @@
 // MISC //
 //////////
 
+TEST_F(AssemblerMIPS64Test, Lwpc) {
+  // Lwpc() takes an unsigned 19-bit immediate, while the GNU assembler needs a signed offset,
+  // hence the sign extension from bit 18 with `imm - ((imm & 0x40000) << 1)`.
+  // The GNU assembler also wants the offset to be a multiple of 4, which it will shift right
+  // by 2 positions when encoding, hence `<< 2` to compensate for that shift.
+  // We capture the value of the immediate with `.set imm, {imm}` because the value is needed
+  // twice for the sign extension, but `{imm}` is substituted only once.
+  const char* code = ".set imm, {imm}\nlw ${reg}, ((imm - ((imm & 0x40000) << 1)) << 2)($pc)";
+  DriverStr(RepeatRIb(&mips64::Mips64Assembler::Lwpc, 19, code), "Lwpc");
+}
+
+TEST_F(AssemblerMIPS64Test, Lwupc) {
+  // The comment for the Lwpc test applies here as well.
+  const char* code = ".set imm, {imm}\nlwu ${reg}, ((imm - ((imm & 0x40000) << 1)) << 2)($pc)";
+  DriverStr(RepeatRIb(&mips64::Mips64Assembler::Lwupc, 19, code), "Lwupc");
+}
+
+TEST_F(AssemblerMIPS64Test, Ldpc) {
+  // The comment for the Lwpc test applies here as well.
+  const char* code = ".set imm, {imm}\nld ${reg}, ((imm - ((imm & 0x20000) << 1)) << 3)($pc)";
+  DriverStr(RepeatRIb(&mips64::Mips64Assembler::Ldpc, 18, code), "Ldpc");
+}
+
+TEST_F(AssemblerMIPS64Test, LoadFarthestNearLabelAddress) {
+  mips64::Mips64Label label;
+  __ LoadLabelAddress(mips64::V0, &label);
+  constexpr uint32_t kAdduCount = 0x3FFDE;
+  for (uint32_t i = 0; i != kAdduCount; ++i) {
+    __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+  }
+  __ Bind(&label);
+
+  std::string expected =
+      "lapc $v0, 1f\n" +
+      RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+      "1:\n";
+  DriverStr(expected, "LoadFarthestNearLabelAddress");
+  EXPECT_EQ(__ GetLabelLocation(&label), (1 + kAdduCount) * 4);
+}
+
+TEST_F(AssemblerMIPS64Test, LoadNearestFarLabelAddress) {
+  mips64::Mips64Label label;
+  __ LoadLabelAddress(mips64::V0, &label);
+  constexpr uint32_t kAdduCount = 0x3FFDF;
+  for (uint32_t i = 0; i != kAdduCount; ++i) {
+    __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+  }
+  __ Bind(&label);
+
+  std::string expected =
+      "1:\n"
+      "auipc $at, %hi(2f - 1b)\n"
+      "daddiu $v0, $at, %lo(2f - 1b)\n" +
+      RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+      "2:\n";
+  DriverStr(expected, "LoadNearestFarLabelAddress");
+  EXPECT_EQ(__ GetLabelLocation(&label), (2 + kAdduCount) * 4);
+}
+
+TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteral) {
+  mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678);
+  __ LoadLiteral(mips64::V0, mips64::kLoadWord, literal);
+  constexpr uint32_t kAdduCount = 0x3FFDE;
+  for (uint32_t i = 0; i != kAdduCount; ++i) {
+    __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+  }
+
+  std::string expected =
+      "lwpc $v0, 1f\n" +
+      RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+      "1:\n"
+      ".word 0x12345678\n";
+  DriverStr(expected, "LoadFarthestNearLiteral");
+  EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4);
+}
+
+TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteral) {
+  mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678);
+  __ LoadLiteral(mips64::V0, mips64::kLoadWord, literal);
+  constexpr uint32_t kAdduCount = 0x3FFDF;
+  for (uint32_t i = 0; i != kAdduCount; ++i) {
+    __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+  }
+
+  std::string expected =
+      "1:\n"
+      "auipc $at, %hi(2f - 1b)\n"
+      "lw $v0, %lo(2f - 1b)($at)\n" +
+      RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+      "2:\n"
+      ".word 0x12345678\n";
+  DriverStr(expected, "LoadNearestFarLiteral");
+  EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4);
+}
+
+TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteralUnsigned) {
+  mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678);
+  __ LoadLiteral(mips64::V0, mips64::kLoadUnsignedWord, literal);
+  constexpr uint32_t kAdduCount = 0x3FFDE;
+  for (uint32_t i = 0; i != kAdduCount; ++i) {
+    __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+  }
+
+  std::string expected =
+      "lwupc $v0, 1f\n" +
+      RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+      "1:\n"
+      ".word 0x12345678\n";
+  DriverStr(expected, "LoadFarthestNearLiteralUnsigned");
+  EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4);
+}
+
+TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteralUnsigned) {
+  mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678);
+  __ LoadLiteral(mips64::V0, mips64::kLoadUnsignedWord, literal);
+  constexpr uint32_t kAdduCount = 0x3FFDF;
+  for (uint32_t i = 0; i != kAdduCount; ++i) {
+    __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+  }
+
+  std::string expected =
+      "1:\n"
+      "auipc $at, %hi(2f - 1b)\n"
+      "lwu $v0, %lo(2f - 1b)($at)\n" +
+      RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+      "2:\n"
+      ".word 0x12345678\n";
+  DriverStr(expected, "LoadNearestFarLiteralUnsigned");
+  EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4);
+}
+
+TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteralLong) {
+  mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF));
+  __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal);
+  constexpr uint32_t kAdduCount = 0x3FFDD;
+  for (uint32_t i = 0; i != kAdduCount; ++i) {
+    __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+  }
+
+  std::string expected =
+      "ldpc $v0, 1f\n" +
+      RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+      "1:\n"
+      ".dword 0x0123456789ABCDEF\n";
+  DriverStr(expected, "LoadFarthestNearLiteralLong");
+  EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4);
+}
+
+TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteralLong) {
+  mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF));
+  __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal);
+  constexpr uint32_t kAdduCount = 0x3FFDE;
+  for (uint32_t i = 0; i != kAdduCount; ++i) {
+    __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+  }
+
+  std::string expected =
+      "1:\n"
+      "auipc $at, %hi(2f - 1b)\n"
+      "ld $v0, %lo(2f - 1b)($at)\n" +
+      RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+      "2:\n"
+      ".dword 0x0123456789ABCDEF\n";
+  DriverStr(expected, "LoadNearestFarLiteralLong");
+  EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4);
+}
+
+TEST_F(AssemblerMIPS64Test, LongLiteralAlignmentNop) {
+  mips64::Literal* literal1 = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF));
+  mips64::Literal* literal2 = __ NewLiteral<uint64_t>(UINT64_C(0x5555555555555555));
+  mips64::Literal* literal3 = __ NewLiteral<uint64_t>(UINT64_C(0xAAAAAAAAAAAAAAAA));
+  __ LoadLiteral(mips64::A1, mips64::kLoadDoubleword, literal1);
+  __ LoadLiteral(mips64::A2, mips64::kLoadDoubleword, literal2);
+  __ LoadLiteral(mips64::A3, mips64::kLoadDoubleword, literal3);
+  __ LoadLabelAddress(mips64::V0, literal1->GetLabel());
+  __ LoadLabelAddress(mips64::V1, literal2->GetLabel());
+  // A nop will be inserted here before the 64-bit literals.
+
+  std::string expected =
+      "ldpc $a1, 1f\n"
+      // The GNU assembler incorrectly requires the ldpc instruction to be located
+      // at an address that's a multiple of 8. TODO: Remove this workaround if/when
+      // the assembler is fixed.
+      // "ldpc $a2, 2f\n"
+      ".word 0xECD80004\n"
+      "ldpc $a3, 3f\n"
+      "lapc $v0, 1f\n"
+      "lapc $v1, 2f\n"
+      "nop\n"
+      "1:\n"
+      ".dword 0x0123456789ABCDEF\n"
+      "2:\n"
+      ".dword 0x5555555555555555\n"
+      "3:\n"
+      ".dword 0xAAAAAAAAAAAAAAAA\n";
+  DriverStr(expected, "LongLiteralAlignmentNop");
+  EXPECT_EQ(__ GetLabelLocation(literal1->GetLabel()), 6 * 4u);
+  EXPECT_EQ(__ GetLabelLocation(literal2->GetLabel()), 8 * 4u);
+  EXPECT_EQ(__ GetLabelLocation(literal3->GetLabel()), 10 * 4u);
+}
+
+TEST_F(AssemblerMIPS64Test, LongLiteralAlignmentNoNop) {
+  mips64::Literal* literal1 = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF));
+  mips64::Literal* literal2 = __ NewLiteral<uint64_t>(UINT64_C(0x5555555555555555));
+  __ LoadLiteral(mips64::A1, mips64::kLoadDoubleword, literal1);
+  __ LoadLiteral(mips64::A2, mips64::kLoadDoubleword, literal2);
+  __ LoadLabelAddress(mips64::V0, literal1->GetLabel());
+  __ LoadLabelAddress(mips64::V1, literal2->GetLabel());
+
+  std::string expected =
+      "ldpc $a1, 1f\n"
+      // The GNU assembler incorrectly requires the ldpc instruction to be located
+      // at an address that's a multiple of 8. TODO: Remove this workaround if/when
+      // the assembler is fixed.
+      // "ldpc $a2, 2f\n"
+      ".word 0xECD80003\n"
+      "lapc $v0, 1f\n"
+      "lapc $v1, 2f\n"
+      "1:\n"
+      ".dword 0x0123456789ABCDEF\n"
+      "2:\n"
+      ".dword 0x5555555555555555\n";
+  DriverStr(expected, "LongLiteralAlignmentNoNop");
+  EXPECT_EQ(__ GetLabelLocation(literal1->GetLabel()), 4 * 4u);
+  EXPECT_EQ(__ GetLabelLocation(literal2->GetLabel()), 6 * 4u);
+}
+
+TEST_F(AssemblerMIPS64Test, FarLongLiteralAlignmentNop) {
+  mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF));
+  __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal);
+  __ LoadLabelAddress(mips64::V1, literal->GetLabel());
+  constexpr uint32_t kAdduCount = 0x3FFDF;
+  for (uint32_t i = 0; i != kAdduCount; ++i) {
+    __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+  }
+  // A nop will be inserted here before the 64-bit literal.
+
+  std::string expected =
+      "1:\n"
+      "auipc $at, %hi(3f - 1b)\n"
+      "ld $v0, %lo(3f - 1b)($at)\n"
+      "2:\n"
+      "auipc $at, %hi(3f - 2b)\n"
+      "daddiu $v1, $at, %lo(3f - 2b)\n" +
+      RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+      "nop\n"
+      "3:\n"
+      ".dword 0x0123456789ABCDEF\n";
+  DriverStr(expected, "FarLongLiteralAlignmentNop");
+  EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (5 + kAdduCount) * 4);
+}
+
 TEST_F(AssemblerMIPS64Test, Bitswap) {
   DriverStr(RepeatRR(&mips64::Mips64Assembler::Bitswap, "bitswap ${reg1}, ${reg2}"), "bitswap");
 }
@@ -889,6 +1141,22 @@
   DriverStr(expected.str(), "Dinsu");
 }
 
+TEST_F(AssemblerMIPS64Test, Lsa) {
+  DriverStr(RepeatRRRIb(&mips64::Mips64Assembler::Lsa,
+                        2,
+                        "lsa ${reg1}, ${reg2}, ${reg3}, {imm}",
+                        1),
+            "lsa");
+}
+
+TEST_F(AssemblerMIPS64Test, Dlsa) {
+  DriverStr(RepeatRRRIb(&mips64::Mips64Assembler::Dlsa,
+                        2,
+                        "dlsa ${reg1}, ${reg2}, ${reg3}, {imm}",
+                        1),
+            "dlsa");
+}
+
 TEST_F(AssemblerMIPS64Test, Wsbh) {
   DriverStr(RepeatRR(&mips64::Mips64Assembler::Wsbh, "wsbh ${reg1}, ${reg2}"), "wsbh");
 }
@@ -1636,9 +1904,9 @@
   DriverStr(expected, "StoreFpuToOffset");
 }
 
-///////////////////////
-// Loading Constants //
-///////////////////////
+//////////////////////////////
+// Loading/adding Constants //
+//////////////////////////////
 
 TEST_F(AssemblerMIPS64Test, LoadConst32) {
   // IsUint<16>(value)
@@ -1681,6 +1949,31 @@
   DriverStr(expected, "LoadConst32");
 }
 
+TEST_F(AssemblerMIPS64Test, Addiu32) {
+  __ Addiu32(mips64::A1, mips64::A2, -0x8000);
+  __ Addiu32(mips64::A1, mips64::A2, +0);
+  __ Addiu32(mips64::A1, mips64::A2, +0x7FFF);
+  __ Addiu32(mips64::A1, mips64::A2, -0x8001);
+  __ Addiu32(mips64::A1, mips64::A2, +0x8000);
+  __ Addiu32(mips64::A1, mips64::A2, -0x10000);
+  __ Addiu32(mips64::A1, mips64::A2, +0x10000);
+  __ Addiu32(mips64::A1, mips64::A2, +0x12345678);
+
+  const char* expected =
+      "addiu $a1, $a2, -0x8000\n"
+      "addiu $a1, $a2, 0\n"
+      "addiu $a1, $a2, 0x7FFF\n"
+      "aui $a1, $a2, 0xFFFF\n"
+      "addiu $a1, $a1, 0x7FFF\n"
+      "aui $a1, $a2, 1\n"
+      "addiu $a1, $a1, -0x8000\n"
+      "aui $a1, $a2, 0xFFFF\n"
+      "aui $a1, $a2, 1\n"
+      "aui $a1, $a2, 0x1234\n"
+      "addiu $a1, $a1, 0x5678\n";
+  DriverStr(expected, "Addiu32");
+}
+
 static uint64_t SignExtend16To64(uint16_t n) {
   return static_cast<int16_t>(n);
 }
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 52765f9..85ae61f 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -508,7 +508,7 @@
                                          /* src */ "LMySSLSocket;",
                                          /* is_strict */ true,
                                          /* is_assignable */ true));
-  ASSERT_TRUE(HasAssignable("Ljava/net/Socket;", "LMySSLSocket;", true));
+  ASSERT_TRUE(HasAssignable("Ljava/net/Socket;", "Ljavax/net/ssl/SSLSocket;", true));
 }
 
 TEST_F(VerifierDepsTest, Assignable_DestinationInBoot2) {
@@ -516,7 +516,7 @@
                                          /* src */ "LMySimpleTimeZone;",
                                          /* is_strict */ true,
                                          /* is_assignable */ true));
-  ASSERT_TRUE(HasAssignable("Ljava/util/TimeZone;", "LMySimpleTimeZone;", true));
+  ASSERT_TRUE(HasAssignable("Ljava/util/TimeZone;", "Ljava/util/SimpleTimeZone;", true));
 }
 
 TEST_F(VerifierDepsTest, Assignable_DestinationInBoot3) {
@@ -539,21 +539,9 @@
   ASSERT_TRUE(HasAssignable("Ljava/util/TimeZone;", "Ljava/util/SimpleTimeZone;", true));
 }
 
-TEST_F(VerifierDepsTest, Assignable_BothArrays_Erroneous) {
-  ASSERT_TRUE(TestAssignabilityRecording(/* dst */ "[[Ljava/util/TimeZone;",
-                                         /* src */ "[[LMyErroneousTimeZone;",
-                                         /* is_strict */ true,
-                                         /* is_assignable */ true));
-  // If the component type of an array is erroneous, we record the dependency on
-  // the array type.
-  ASSERT_FALSE(HasAssignable("[[Ljava/util/TimeZone;", "[[LMyErroneousTimeZone;", true));
-  ASSERT_TRUE(HasAssignable("[Ljava/util/TimeZone;", "[LMyErroneousTimeZone;", true));
-  ASSERT_FALSE(HasAssignable("Ljava/util/TimeZone;", "LMyErroneousTimeZone;", true));
-}
-
-  // We test that VerifierDeps does not try to optimize by storing assignability
-  // of the component types. This is due to the fact that the component type may
-  // be an erroneous class, even though the array type has resolved status.
+// We test that VerifierDeps does not try to optimize by storing assignability
+// of the component types. This is due to the fact that the component type may
+// be an erroneous class, even though the array type has resolved status.
 
 TEST_F(VerifierDepsTest, Assignable_ArrayToInterface1) {
   ASSERT_TRUE(TestAssignabilityRecording(/* dst */ "Ljava/io/Serializable;",
@@ -584,7 +572,7 @@
                                          /* src */ "LMySSLSocket;",
                                          /* is_strict */ true,
                                          /* is_assignable */ false));
-  ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "LMySSLSocket;", false));
+  ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "Ljavax/net/ssl/SSLSocket;", false));
 }
 
 TEST_F(VerifierDepsTest, NotAssignable_DestinationInBoot2) {
@@ -592,7 +580,7 @@
                                          /* src */ "LMySimpleTimeZone;",
                                          /* is_strict */ true,
                                          /* is_assignable */ false));
-  ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "LMySimpleTimeZone;", false));
+  ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "Ljava/util/SimpleTimeZone;", false));
 }
 
 TEST_F(VerifierDepsTest, NotAssignable_BothArrays) {
@@ -608,16 +596,6 @@
   ASSERT_TRUE(HasClass("Ljava/lang/Thread;", true, "public"));
 }
 
-TEST_F(VerifierDepsTest, ArgumentType_ResolvedReferenceArray) {
-  ASSERT_TRUE(VerifyMethod("ArgumentType_ResolvedReferenceArray"));
-  ASSERT_TRUE(HasClass("[Ljava/lang/Thread;", true, "public final abstract"));
-}
-
-TEST_F(VerifierDepsTest, ArgumentType_ResolvedPrimitiveArray) {
-  ASSERT_TRUE(VerifyMethod("ArgumentType_ResolvedPrimitiveArray"));
-  ASSERT_TRUE(HasClass("[B", true, "public final abstract"));
-}
-
 TEST_F(VerifierDepsTest, ArgumentType_UnresolvedClass) {
   ASSERT_TRUE(VerifyMethod("ArgumentType_UnresolvedClass"));
   ASSERT_TRUE(HasClass("LUnresolvedClass;", false));
@@ -654,7 +632,7 @@
 
 TEST_F(VerifierDepsTest, MergeTypes_RegisterLines) {
   ASSERT_TRUE(VerifyMethod("MergeTypes_RegisterLines"));
-  ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "LMySocketTimeoutException;", true));
+  ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "Ljava/net/SocketTimeoutException;", true));
   ASSERT_TRUE(HasAssignable(
       "Ljava/lang/Exception;", "Ljava/util/concurrent/TimeoutException;", true));
 }
@@ -714,11 +692,6 @@
   ASSERT_TRUE(HasClass("LUnresolvedClass;", false));
 }
 
-TEST_F(VerifierDepsTest, NewArray_Resolved) {
-  ASSERT_TRUE(VerifyMethod("NewArray_Resolved"));
-  ASSERT_TRUE(HasClass("[Ljava/lang/IllegalStateException;", true, "public final abstract"));
-}
-
 TEST_F(VerifierDepsTest, NewArray_Unresolved) {
   ASSERT_TRUE(VerifyMethod("NewArray_Unresolved"));
   ASSERT_TRUE(HasClass("[LUnresolvedClass;", false));
@@ -841,7 +814,7 @@
                        "public",
                        "Ljava/io/InterruptedIOException;"));
   ASSERT_TRUE(HasAssignable(
-      "Ljava/io/InterruptedIOException;", "LMySocketTimeoutException;", true));
+      "Ljava/io/InterruptedIOException;", "Ljava/net/SocketTimeoutException;", true));
 }
 
 TEST_F(VerifierDepsTest, InstanceField_Resolved_DeclaredInSuperclass1) {
@@ -854,7 +827,7 @@
                        "public",
                        "Ljava/io/InterruptedIOException;"));
   ASSERT_TRUE(HasAssignable(
-      "Ljava/io/InterruptedIOException;", "LMySocketTimeoutException;", true));
+      "Ljava/io/InterruptedIOException;", "Ljava/net/SocketTimeoutException;", true));
 }
 
 TEST_F(VerifierDepsTest, InstanceField_Resolved_DeclaredInSuperclass2) {
@@ -866,7 +839,7 @@
                        "public",
                        "Ljava/io/InterruptedIOException;"));
   ASSERT_TRUE(HasAssignable(
-      "Ljava/io/InterruptedIOException;", "LMySocketTimeoutException;", true));
+      "Ljava/io/InterruptedIOException;", "Ljava/net/SocketTimeoutException;", true));
 }
 
 TEST_F(VerifierDepsTest, InstanceField_Unresolved_ReferrerInBoot) {
@@ -995,7 +968,7 @@
                         "public",
                         "Ljava/lang/Throwable;"));
   // Type dependency on `this` argument.
-  ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "LMySocketTimeoutException;", true));
+  ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "Ljava/net/SocketTimeoutException;", true));
 }
 
 TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperclass1) {
@@ -1009,7 +982,7 @@
                         "public",
                         "Ljava/lang/Throwable;"));
   // Type dependency on `this` argument.
-  ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "LMySocketTimeoutException;", true));
+  ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "Ljava/net/SocketTimeoutException;", true));
 }
 
 TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperclass2) {
@@ -1123,7 +1096,7 @@
 TEST_F(VerifierDepsTest, InvokeSuper_ThisNotAssignable) {
   ASSERT_FALSE(VerifyMethod("InvokeSuper_ThisNotAssignable"));
   ASSERT_TRUE(HasClass("Ljava/lang/Integer;", true, "public final"));
-  ASSERT_TRUE(HasAssignable("Ljava/lang/Integer;", "LMain;", false));
+  ASSERT_TRUE(HasAssignable("Ljava/lang/Integer;", "Ljava/lang/Thread;", false));
   ASSERT_TRUE(HasMethod(
       "virtual", "Ljava/lang/Integer;", "intValue", "()I", true, "public", "Ljava/lang/Integer;"));
 }
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 5a0f0c6..ece81e3 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -33,6 +33,7 @@
 #include <sys/utsname.h>
 #endif
 
+#include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
 #include "arch/instruction_set_features.h"
@@ -87,6 +88,9 @@
 
 namespace art {
 
+using android::base::StringAppendV;
+using android::base::StringPrintf;
+
 static constexpr size_t kDefaultMinDexFilesForSwap = 2;
 static constexpr size_t kDefaultMinDexFileCumulativeSizeForSwap = 20 * MB;
 
@@ -1291,17 +1295,23 @@
 
         DCHECK_EQ(output_vdex_fd_, -1);
         std::string vdex_filename = ReplaceFileExtension(oat_filename, "vdex");
-        std::unique_ptr<File> vdex_file(OS::CreateEmptyFile(vdex_filename.c_str()));
-        if (vdex_file.get() == nullptr) {
-          PLOG(ERROR) << "Failed to open vdex file: " << vdex_filename;
-          return false;
+        if (vdex_filename == input_vdex_) {
+          update_input_vdex_ = true;
+          std::unique_ptr<File> vdex_file(OS::OpenFileReadWrite(vdex_filename.c_str()));
+          vdex_files_.push_back(std::move(vdex_file));
+        } else {
+          std::unique_ptr<File> vdex_file(OS::CreateEmptyFile(vdex_filename.c_str()));
+          if (vdex_file.get() == nullptr) {
+            PLOG(ERROR) << "Failed to open vdex file: " << vdex_filename;
+            return false;
+          }
+          if (fchmod(vdex_file->Fd(), 0644) != 0) {
+            PLOG(ERROR) << "Failed to make vdex file world readable: " << vdex_filename;
+            vdex_file->Erase();
+            return false;
+          }
+          vdex_files_.push_back(std::move(vdex_file));
         }
-        if (fchmod(vdex_file->Fd(), 0644) != 0) {
-          PLOG(ERROR) << "Failed to make vdex file world readable: " << vdex_filename;
-          vdex_file->Erase();
-          return false;
-        }
-        vdex_files_.push_back(std::move(vdex_file));
       }
     } else {
       std::unique_ptr<File> oat_file(new File(oat_fd_, oat_location_, /* check_usage */ true));
@@ -1315,7 +1325,6 @@
       }
       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));
@@ -1348,8 +1357,13 @@
         return false;
       }
       vdex_file->DisableAutoClose();
-      if (vdex_file->SetLength(0) != 0) {
-        PLOG(WARNING) << "Truncating vdex file " << vdex_location << " failed.";
+      if (input_vdex_file_ != nullptr && output_vdex_fd_ == input_vdex_fd_) {
+        update_input_vdex_ = true;
+      } else {
+        if (vdex_file->SetLength(0) != 0) {
+          PLOG(ERROR) << "Truncating vdex file " << vdex_location << " failed.";
+          return false;
+        }
       }
       vdex_files_.push_back(std::move(vdex_file));
 
@@ -1538,6 +1552,7 @@
             instruction_set_features_.get(),
             key_value_store_.get(),
             verify,
+            update_input_vdex_,
             &opened_dex_files_map,
             &opened_dex_files)) {
           return false;
@@ -2728,6 +2743,9 @@
   // See CompilerOptions.force_determinism_.
   bool force_determinism_;
 
+  // Whether the given input vdex is also the output.
+  bool update_input_vdex_ = false;
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
 };
 
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index b6b62a8..cdb3b9f 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -19,11 +19,15 @@
 #include <string>
 #include <vector>
 
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "android-base/stringprintf.h"
+
 #include "common_runtime_test.h"
 
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/stringprintf.h"
 #include "dex_file-inl.h"
 #include "dex2oat_environment_test.h"
 #include "jit/offline_profiling_info.h"
@@ -31,9 +35,6 @@
 #include "oat_file.h"
 #include "utils.h"
 
-#include <sys/wait.h>
-#include <unistd.h>
-
 namespace art {
 
 class Dex2oatTest : public Dex2oatEnvironmentTest {
@@ -217,7 +218,7 @@
     std::unique_ptr<ScratchFile> sf;
     if (use_fd) {
       sf.reset(new ScratchFile());
-      copy.push_back(StringPrintf("--swap-fd=%d", sf->GetFd()));
+      copy.push_back(android::base::StringPrintf("--swap-fd=%d", sf->GetFd()));
     } else {
       std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap";
       copy.push_back("--swap-file=" + swap_location);
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 916984c..d5776fa 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -42,7 +42,8 @@
 #include <sstream>
 #include <vector>
 
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
 #include "dexdump_cfg.h"
 #include "dex_file-inl.h"
 #include "dex_file_types.h"
@@ -887,8 +888,10 @@
           const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_);
           const Signature signature = pDexFile->GetMethodSignature(pMethodId);
           const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_);
-          method = StringPrintf("%s.%s:%s",
-                                backDescriptor, name, signature.ToString().c_str());
+          method = android::base::StringPrintf("%s.%s:%s",
+                                               backDescriptor,
+                                               name,
+                                               signature.ToString().c_str());
         }
         if (secondary_index < pDexFile->GetHeader().proto_ids_size_) {
           const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index);
diff --git a/dexdump/dexdump_test.cc b/dexdump/dexdump_test.cc
index d28ca28..53dda6a 100644
--- a/dexdump/dexdump_test.cc
+++ b/dexdump/dexdump_test.cc
@@ -21,7 +21,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include "base/stringprintf.h"
 #include "common_runtime_test.h"
 #include "runtime/arch/instruction_set.h"
 #include "runtime/os.h"
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index cfe4837..cac6090 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -30,7 +30,8 @@
 #include <sstream>
 #include <vector>
 
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
 #include "dex_ir_builder.h"
 #include "dex_file-inl.h"
 #include "dex_instruction-inl.h"
@@ -43,6 +44,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 /*
  * Flags for use with createAccessFlagStr().
  */
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index 665baa6..46a1c43 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -21,7 +21,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include "base/stringprintf.h"
 #include "base/unix_file/fd_file.h"
 #include "common_runtime_test.h"
 #include "utils.h"
diff --git a/dexlist/dexlist_test.cc b/dexlist/dexlist_test.cc
index da1dd7f..1320942 100644
--- a/dexlist/dexlist_test.cc
+++ b/dexlist/dexlist_test.cc
@@ -21,7 +21,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include "base/stringprintf.h"
 #include "common_runtime_test.h"
 #include "runtime/arch/instruction_set.h"
 #include "runtime/gc/heap.h"
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index 4787395..1f6b874 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -22,6 +22,8 @@
 #include "android-base/logging.h"
 #include "android-base/stringprintf.h"
 
+#include "base/bit_utils.h"
+
 using android::base::StringPrintf;
 
 namespace art {
@@ -137,6 +139,8 @@
   { kSpecial0Mask | (0x1f << 16) | 0x7ff, (0x01 << 6) | 0x11, "clo", "DS" },
   { kSpecial0Mask | (0x1f << 16) | 0x7ff, (0x01 << 6) | 0x12, "dclz", "DS" },
   { kSpecial0Mask | (0x1f << 16) | 0x7ff, (0x01 << 6) | 0x13, "dclo", "DS" },
+  { kSpecial0Mask | 0x73f, 0x05, "lsa", "DSTj" },
+  { kSpecial0Mask | 0x73f, 0x15, "dlsa", "DSTj" },
   // TODO: sdbbp
 
   // SPECIAL2
@@ -154,6 +158,7 @@
   { kSpecial3Mask | 0x3f, (31 << kOpcodeShift), "ext", "TSAZ", },
   { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 3, "dext", "TSAZ", },
   { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 4, "ins", "TSAz", },
+  { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 6, "dinsu", "TSFz", },
   { kSpecial3Mask | (0x1f << 21) | (0x1f << 6) | 0x3f,
     (31 << kOpcodeShift) | (16 << 6) | 32,
     "seb",
@@ -218,8 +223,8 @@
   { kITypeMask, 12 << kOpcodeShift, "andi", "TSi", },
   { kITypeMask, 13 << kOpcodeShift, "ori", "TSi", },
   { kITypeMask, 14 << kOpcodeShift, "xori", "TSi", },
-  { kITypeMask | (0x1f << 21), 15 << kOpcodeShift, "lui", "TI", },
-  { kITypeMask, 15 << kOpcodeShift, "aui", "TSI", },
+  { kITypeMask | (0x1f << 21), 15 << kOpcodeShift, "lui", "Ti", },
+  { kITypeMask, 15 << kOpcodeShift, "aui", "TSi", },
 
   { kITypeMask | (0x3e3 << 16), (17 << kOpcodeShift) | (8 << 21), "bc1f", "cB" },
   { kITypeMask | (0x3e3 << 16), (17 << kOpcodeShift) | (8 << 21) | (1 << 16), "bc1t", "cB" },
@@ -335,6 +340,8 @@
   { kITypeMask | (0x1f << 16), (59u << kOpcodeShift) | (30 << 16), "auipc", "Si" },
   { kITypeMask | (0x3 << 19), (59u << kOpcodeShift) | (0 << 19), "addiupc", "Sp" },
   { kITypeMask | (0x3 << 19), (59u << kOpcodeShift) | (1 << 19), "lwpc", "So" },
+  { kITypeMask | (0x3 << 19), (59u << kOpcodeShift) | (2 << 19), "lwupc", "So" },
+  { kITypeMask | (0x7 << 18), (59u << kOpcodeShift) | (6 << 18), "ldpc", "S0" },
   { kITypeMask, 61u << kOpcodeShift, "sdc1", "tO", },
   { kITypeMask | (0x1f << 21), 62u << kOpcodeShift, "jialc", "Ti" },
   { kITypeMask | (1 << 21), (62u << kOpcodeShift) | (1 << 21), "bnezc", "Sb" },  // TODO: de-dup?
@@ -468,6 +475,7 @@
           case 'D': args << 'r' << rd; break;
           case 'd': args << 'f' << rd; break;
           case 'a': args << 'f' << sa; break;
+          case 'F': args << (sa + 32); break;  // dinsu position.
           case 'f':  // Floating point "fmt".
             {
               size_t fmt = (instruction >> 21) & 0x7;  // TODO: other fmts?
@@ -481,12 +489,12 @@
               }
               continue;  // No ", ".
             }
-          case 'I':  // Upper 16-bit immediate.
-            args << reinterpret_cast<void*>((instruction & 0xffff) << 16);
-            break;
           case 'i':  // Sign-extended lower 16-bit immediate.
             args << static_cast<int16_t>(instruction & 0xffff);
             break;
+          case 'j':  // sa value for lsa/dlsa.
+            args << (sa + 1);
+            break;
           case 'L':  // Jump label.
             {
               // TODO: is this right?
@@ -512,7 +520,7 @@
               }
             }
             break;
-          case 'o':  // 19-bit offset in lwpc.
+          case 'o':  // 19-bit offset in lwpc and lwupc.
             {
               int32_t offset = (instruction & 0x7ffff) - ((instruction & 0x40000) << 1);
               offset <<= 2;
@@ -520,6 +528,15 @@
               args << StringPrintf("  ; %+d", offset);
             }
             break;
+          case '0':  // 18-bit offset in ldpc.
+            {
+              int32_t offset = (instruction & 0x3ffff) - ((instruction & 0x20000) << 1);
+              offset <<= 3;
+              uintptr_t ptr = RoundDown(reinterpret_cast<uintptr_t>(instr_ptr), 8);
+              args << FormatInstructionPointer(reinterpret_cast<const uint8_t*>(ptr + offset));
+              args << StringPrintf("  ; %+d", offset);
+            }
+            break;
           case 'P':  // 26-bit offset in bc and balc.
             {
               int32_t offset = (instruction & 0x3ffffff) - ((instruction & 0x2000000) << 1);
@@ -541,7 +558,7 @@
           case 'T': args << 'r' << rt; break;
           case 't': args << 'f' << rt; break;
           case 'Z': args << (rd + 1); break;  // sz ([d]ext size).
-          case 'z': args << (rd - sa + 1); break;  // sz ([d]ins size).
+          case 'z': args << (rd - sa + 1); break;  // sz ([d]ins, dinsu size).
         }
         if (*(args_fmt + 1)) {
           args << ", ";
@@ -551,17 +568,14 @@
     }
   }
 
-  // TODO: Simplify this once these sequences are simplified in the compiler.
   // Special cases for sequences of:
   //   pc-relative +/- 2GB branch:
   //     auipc  reg, imm
   //     jic    reg, imm
   //   pc-relative +/- 2GB branch and link:
   //     auipc  reg, imm
-  //     daddiu reg, reg, imm
-  //     jialc  reg, 0
-  if (((op == 0x36 && rs == 0 && rt != 0) ||  // jic
-       (op == 0x19 && rs == rt && rt != 0)) &&  // daddiu
+  //     jialc  reg, imm
+  if (((op == 0x36 || op == 0x3E) && rs == 0 && rt != 0) &&  // ji[al]c
       last_ptr_ && (intptr_t)instr_ptr - (intptr_t)last_ptr_ == 4 &&
       (last_instr_ & 0xFC1F0000) == 0xEC1E0000 &&  // auipc
       ((last_instr_ >> 21) & 0x1F) == rt) {
@@ -569,9 +583,9 @@
     offset -= (offset & 0x8000) << 1;
     offset -= 4;
     if (op == 0x36) {
-      args << "  ; b ";
+      args << "  ; bc ";
     } else {
-      args << "  ; move r" << rt << ", ";
+      args << "  ; balc ";
     }
     args << FormatInstructionPointer(instr_ptr + (int32_t)offset);
     args << StringPrintf("  ; %+d", (int32_t)offset);
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index a374686..f307cbc 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -26,9 +26,10 @@
 #include <map>
 #include <unordered_set>
 
+#include "android-base/stringprintf.h"
+
 #include "art_method-inl.h"
 #include "base/unix_file/fd_file.h"
-#include "base/stringprintf.h"
 #include "gc/space/image_space.h"
 #include "gc/heap.h"
 #include "mirror/class-inl.h"
@@ -46,6 +47,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 class ImgDiagDumper {
  public:
   explicit ImgDiagDumper(std::ostream* os,
diff --git a/imgdiag/imgdiag_test.cc b/imgdiag/imgdiag_test.cc
index 9f771ba..3f2afc0 100644
--- a/imgdiag/imgdiag_test.cc
+++ b/imgdiag/imgdiag_test.cc
@@ -20,12 +20,13 @@
 
 #include "common_runtime_test.h"
 
+#include "android-base/stringprintf.h"
+
 #include "runtime/os.h"
 #include "runtime/arch/instruction_set.h"
 #include "runtime/utils.h"
 #include "runtime/gc/space/image_space.h"
 #include "runtime/gc/heap.h"
-#include "base/stringprintf.h"
 
 #include <sys/types.h>
 #include <unistd.h>
@@ -57,7 +58,7 @@
 
   virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
     // Needs to live until CommonRuntimeTest::SetUp finishes, since we pass it a cstring.
-    runtime_args_image_ = StringPrintf("-Ximage:%s", GetCoreArtLocation().c_str());
+    runtime_args_image_ = android::base::StringPrintf("-Ximage:%s", GetCoreArtLocation().c_str());
     options->push_back(std::make_pair(runtime_args_image_, nullptr));
   }
 
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index e4462d8..148ee88 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -26,6 +26,7 @@
 #include <unordered_set>
 #include <vector>
 
+#include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
 #include "arch/instruction_set_features.h"
@@ -76,6 +77,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 const char* image_methods_descriptions_[] = {
   "kResolutionMethod",
   "kImtConflictMethod",
@@ -89,6 +92,7 @@
 const char* image_roots_descriptions_[] = {
   "kDexCaches",
   "kClassRoots",
+  "kClassLoader",
 };
 
 // Map is so that we don't allocate multiple dex files for the same OatDexFile.
@@ -1506,12 +1510,13 @@
       os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
       static_assert(arraysize(image_roots_descriptions_) ==
           static_cast<size_t>(ImageHeader::kImageRootsMax), "sizes must match");
-      for (int i = 0; i < ImageHeader::kImageRootsMax; i++) {
+      DCHECK_LE(image_header_.GetImageRoots()->GetLength(), ImageHeader::kImageRootsMax);
+      for (int32_t i = 0, size = image_header_.GetImageRoots()->GetLength(); i != size; ++i) {
         ImageHeader::ImageRoot image_root = static_cast<ImageHeader::ImageRoot>(i);
         const char* image_root_description = image_roots_descriptions_[i];
         mirror::Object* image_root_object = image_header_.GetImageRoot(image_root);
         indent_os << StringPrintf("%s: %p\n", image_root_description, image_root_object);
-        if (image_root_object->IsObjectArray()) {
+        if (image_root_object != nullptr && image_root_object->IsObjectArray()) {
           mirror::ObjectArray<mirror::Object>* image_root_object_array
               = image_root_object->AsObjectArray<mirror::Object>();
           ScopedIndentation indent2(&vios_);
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index a2eba45..e77d03b 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -22,7 +22,6 @@
 
 #include "common_runtime_test.h"
 
-#include "base/stringprintf.h"
 #include "base/unix_file/fd_file.h"
 #include "runtime/arch/instruction_set.h"
 #include "runtime/gc/heap.h"
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 62d1ddf..7ae13a5 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -24,6 +24,7 @@
 #include <string>
 #include <vector>
 
+#include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
 #include "art_field-inl.h"
@@ -31,7 +32,6 @@
 #include "base/dumpable.h"
 #include "base/scoped_flock.h"
 #include "base/stringpiece.h"
-#include "base/stringprintf.h"
 #include "base/unix_file/fd_file.h"
 #include "base/unix_file/random_access_file_utils.h"
 #include "elf_utils.h"
@@ -939,7 +939,7 @@
 
 static void UsageErrorV(const char* fmt, va_list ap) {
   std::string error;
-  StringAppendV(&error, fmt, ap);
+  android::base::StringAppendV(&error, fmt, ap);
   LOG(ERROR) << error;
 }
 
diff --git a/profman/profman.cc b/profman/profman.cc
index 0b2d172..e538407 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -25,12 +25,12 @@
 #include <string>
 #include <vector>
 
+#include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
 #include "base/dumpable.h"
 #include "base/scoped_flock.h"
 #include "base/stringpiece.h"
-#include "base/stringprintf.h"
 #include "base/time_utils.h"
 #include "base/unix_file/fd_file.h"
 #include "dex_file.h"
@@ -61,7 +61,7 @@
 
 static void UsageErrorV(const char* fmt, va_list ap) {
   std::string error;
-  StringAppendV(&error, fmt, ap);
+  android::base::StringAppendV(&error, fmt, ap);
   LOG(ERROR) << error;
 }
 
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 32ebee2..86019bf 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -40,7 +40,6 @@
         "base/scoped_arena_allocator.cc",
         "base/scoped_flock.cc",
         "base/stringpiece.cc",
-        "base/stringprintf.cc",
         "base/time_utils.cc",
         "base/timing_logger.cc",
         "base/unix_file/fd_file.cc",
@@ -508,7 +507,6 @@
         "base/histogram_test.cc",
         "base/mutex_test.cc",
         "base/scoped_flock_test.cc",
-        "base/stringprintf_test.cc",
         "base/time_utils_test.cc",
         "base/timing_logger_test.cc",
         "base/transform_array_ref_test.cc",
diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc
index f264b82..181b2ed 100644
--- a/runtime/arch/arm/instruction_set_features_arm.cc
+++ b/runtime/arch/arm/instruction_set_features_arm.cc
@@ -24,10 +24,10 @@
 #include "signal.h"
 #include <fstream>
 
+#include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
-#include "base/stringprintf.h"
-#include "utils.h"  // For Trim.
+#include "base/logging.h"
 
 #if defined(__arm__)
 extern "C" bool artCheckForArmSdivInstruction();
@@ -35,6 +35,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromVariant(
     const std::string& variant, std::string* error_msg) {
   // Assume all ARM processors are SMP.
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index f7b5a76..52d8b3e 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -19,14 +19,16 @@
 #include <fstream>
 #include <sstream>
 
+#include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
+#include "base/logging.h"
 #include "base/stl_util.h"
-#include "base/stringprintf.h"
-#include "utils.h"  // For Trim.
 
 namespace art {
 
+using android::base::StringPrintf;
+
 Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant(
     const std::string& variant, std::string* error_msg) {
   const bool smp = true;  // Conservative default.
diff --git a/runtime/arch/instruction_set_features_test.cc b/runtime/arch/instruction_set_features_test.cc
index 0b8e531..d489392 100644
--- a/runtime/arch/instruction_set_features_test.cc
+++ b/runtime/arch/instruction_set_features_test.cc
@@ -22,11 +22,14 @@
 #include "android-base/properties.h"
 #endif
 
+#include "android-base/stringprintf.h"
+
 #include "base/logging.h"
-#include "base/stringprintf.h"
 
 namespace art {
 
+using android::base::StringPrintf;
+
 #ifdef ART_TARGET_ANDROID
 #if defined(__aarch64__)
 TEST(InstructionSetFeaturesTest, DISABLED_FeaturesFromSystemPropertyVariant) {
diff --git a/runtime/arch/instruction_set_test.cc b/runtime/arch/instruction_set_test.cc
index 5dfc4b4..5aae93a 100644
--- a/runtime/arch/instruction_set_test.cc
+++ b/runtime/arch/instruction_set_test.cc
@@ -19,7 +19,6 @@
 #include <gtest/gtest.h>
 
 #include "base/enums.h"
-#include "base/stringprintf.h"
 
 namespace art {
 
diff --git a/runtime/arch/mips/instruction_set_features_mips.cc b/runtime/arch/mips/instruction_set_features_mips.cc
index a65c967..5b50573 100644
--- a/runtime/arch/mips/instruction_set_features_mips.cc
+++ b/runtime/arch/mips/instruction_set_features_mips.cc
@@ -19,14 +19,16 @@
 #include <fstream>
 #include <sstream>
 
+#include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
+#include "base/logging.h"
 #include "base/stl_util.h"
-#include "base/stringprintf.h"
-#include "utils.h"  // For Trim.
 
 namespace art {
 
+using android::base::StringPrintf;
+
 // An enum for the Mips revision.
 enum class MipsLevel {
   kBase,
diff --git a/runtime/arch/mips64/instruction_set_features_mips64.cc b/runtime/arch/mips64/instruction_set_features_mips64.cc
index e564d1e..92c44e8 100644
--- a/runtime/arch/mips64/instruction_set_features_mips64.cc
+++ b/runtime/arch/mips64/instruction_set_features_mips64.cc
@@ -19,13 +19,15 @@
 #include <fstream>
 #include <sstream>
 
+#include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
-#include "base/stringprintf.h"
-#include "utils.h"  // For Trim.
+#include "base/logging.h"
 
 namespace art {
 
+using android::base::StringPrintf;
+
 Mips64FeaturesUniquePtr Mips64InstructionSetFeatures::FromVariant(
     const std::string& variant, std::string* error_msg ATTRIBUTE_UNUSED) {
   if (variant != "default" && variant != "mips64r6") {
diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc
index cc102ec..c520d63 100644
--- a/runtime/arch/x86/instruction_set_features_x86.cc
+++ b/runtime/arch/x86/instruction_set_features_x86.cc
@@ -19,14 +19,16 @@
 #include <fstream>
 #include <sstream>
 
+#include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
 #include "arch/x86_64/instruction_set_features_x86_64.h"
-#include "base/stringprintf.h"
-#include "utils.h"  // For Trim.
+#include "base/logging.h"
 
 namespace art {
 
+using android::base::StringPrintf;
+
 // Feature-support arrays.
 
 static constexpr const char* x86_known_variants[] = {
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index ef03bb3..96976d9 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -134,8 +134,7 @@
   // NOTE: Unchecked, i.e. not throwing AIOOB. We don't even know the length here
   // without accessing the DexCache and we don't want to do that in release build.
   DCHECK_LT(method_index,
-            GetInterfaceMethodIfProxy(pointer_size)->GetDeclaringClass()
-                ->GetDexCache()->NumResolvedMethods());
+            GetInterfaceMethodIfProxy(pointer_size)->GetDexCache()->NumResolvedMethods());
   ArtMethod* method = mirror::DexCache::GetElementPtrSize(GetDexCacheResolvedMethods(pointer_size),
                                                           method_index,
                                                           pointer_size);
@@ -154,8 +153,7 @@
   // NOTE: Unchecked, i.e. not throwing AIOOB. We don't even know the length here
   // without accessing the DexCache and we don't want to do that in release build.
   DCHECK_LT(method_index,
-            GetInterfaceMethodIfProxy(pointer_size)->GetDeclaringClass()
-                ->GetDexCache()->NumResolvedMethods());
+            GetInterfaceMethodIfProxy(pointer_size)->GetDexCache()->NumResolvedMethods());
   DCHECK(new_method == nullptr || new_method->GetDeclaringClass() != nullptr);
   mirror::DexCache::SetElementPtrSize(GetDexCacheResolvedMethods(pointer_size),
                                       method_index,
@@ -186,8 +184,7 @@
 inline mirror::Class* ArtMethod::GetDexCacheResolvedType(dex::TypeIndex type_index,
                                                          PointerSize pointer_size) {
   if (kWithCheck) {
-    mirror::DexCache* dex_cache =
-        GetInterfaceMethodIfProxy(pointer_size)->GetDeclaringClass()->GetDexCache();
+    mirror::DexCache* dex_cache = GetInterfaceMethodIfProxy(pointer_size)->GetDexCache();
     if (UNLIKELY(type_index.index_ >= dex_cache->NumResolvedTypes())) {
       ThrowArrayIndexOutOfBoundsException(type_index.index_, dex_cache->NumResolvedTypes());
       return nullptr;
@@ -333,7 +330,7 @@
 }
 
 inline const DexFile::CodeItem* ArtMethod::GetCodeItem() {
-  return GetDeclaringClass()->GetDexFile().GetCodeItem(GetCodeItemOffset());
+  return GetDexFile()->GetCodeItem(GetCodeItemOffset());
 }
 
 inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size) {
@@ -398,11 +395,11 @@
 }
 
 inline mirror::DexCache* ArtMethod::GetDexCache() {
-  DCHECK(!IsProxyMethod());
-  if (UNLIKELY(IsObsolete())) {
-    return GetObsoleteDexCache();
-  } else {
+  if (LIKELY(!IsObsolete())) {
     return GetDeclaringClass()->GetDexCache();
+  } else {
+    DCHECK(!IsProxyMethod());
+    return GetObsoleteDexCache();
   }
 }
 
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 96b6f18..dfc7837 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -18,6 +18,8 @@
 
 #include <cstddef>
 
+#include "android-base/stringprintf.h"
+
 #include "arch/context.h"
 #include "art_field-inl.h"
 #include "art_method-inl.h"
@@ -46,6 +48,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 extern "C" void art_quick_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
                                       const char*);
 extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 3bc6f5d..963a541 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -27,6 +27,7 @@
 #include "invoke_type.h"
 #include "method_reference.h"
 #include "modifiers.h"
+#include "mirror/dex_cache.h"
 #include "mirror/object.h"
 #include "obj_ptr.h"
 #include "read_barrier_option.h"
@@ -220,6 +221,12 @@
     return !IsIntrinsic() && (GetAccessFlags() & kAccObsoleteMethod) != 0;
   }
 
+  void SetIsObsolete() {
+    // TODO We should really support redefining intrinsic if possible.
+    DCHECK(!IsIntrinsic());
+    SetAccessFlags(GetAccessFlags() | kAccObsoleteMethod);
+  }
+
   template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   bool IsNative() {
     return (GetAccessFlags<kReadBarrierOption>() & kAccNative) != 0;
@@ -326,6 +333,7 @@
   ALWAYS_INLINE ArtMethod* GetDexCacheResolvedMethod(uint16_t method_index,
                                                      PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
+
   ALWAYS_INLINE void SetDexCacheResolvedMethod(uint16_t method_index,
                                                ArtMethod* new_method,
                                                PointerSize pointer_size)
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 5ef1f06..e4972da 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -175,19 +175,19 @@
 #define MIRROR_CLASS_IF_TABLE_OFFSET (16 + MIRROR_OBJECT_HEADER_SIZE)
 ADD_TEST_EQ(MIRROR_CLASS_IF_TABLE_OFFSET,
             art::mirror::Class::IfTableOffset().Int32Value())
-#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (64 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (56 + MIRROR_OBJECT_HEADER_SIZE)
 ADD_TEST_EQ(MIRROR_CLASS_ACCESS_FLAGS_OFFSET,
             art::mirror::Class::AccessFlagsOffset().Int32Value())
-#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (96 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (88 + MIRROR_OBJECT_HEADER_SIZE)
 ADD_TEST_EQ(MIRROR_CLASS_OBJECT_SIZE_OFFSET,
             art::mirror::Class::ObjectSizeOffset().Int32Value())
-#define MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET (100 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET (92 + MIRROR_OBJECT_HEADER_SIZE)
 ADD_TEST_EQ(MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET,
             art::mirror::Class::ObjectSizeAllocFastPathOffset().Int32Value())
-#define MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET (104 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET (96 + MIRROR_OBJECT_HEADER_SIZE)
 ADD_TEST_EQ(MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET,
             art::mirror::Class::PrimitiveTypeOffset().Int32Value())
-#define MIRROR_CLASS_STATUS_OFFSET (112 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_STATUS_OFFSET (104 + MIRROR_OBJECT_HEADER_SIZE)
 ADD_TEST_EQ(MIRROR_CLASS_STATUS_OFFSET,
             art::mirror::Class::StatusOffset().Int32Value())
 
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 5cdf671..61e0aab 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -83,18 +83,19 @@
   "GraphChecker ",
   "Verifier     ",
   "CallingConv  ",
+  "CHA          ",
 };
 
 template <bool kCount>
 ArenaAllocatorStatsImpl<kCount>::ArenaAllocatorStatsImpl()
-    : num_allocations_(0u) {
-  std::fill_n(alloc_stats_, arraysize(alloc_stats_), 0u);
+    : num_allocations_(0u),
+      alloc_stats_(kNumArenaAllocKinds, 0u) {
 }
 
 template <bool kCount>
 void ArenaAllocatorStatsImpl<kCount>::Copy(const ArenaAllocatorStatsImpl& other) {
   num_allocations_ = other.num_allocations_;
-  std::copy(other.alloc_stats_, other.alloc_stats_ + arraysize(alloc_stats_), alloc_stats_);
+  std::copy_n(other.alloc_stats_.begin(), kNumArenaAllocKinds, alloc_stats_.begin());
 }
 
 template <bool kCount>
@@ -111,7 +112,7 @@
 template <bool kCount>
 size_t ArenaAllocatorStatsImpl<kCount>::BytesAllocated() const {
   const size_t init = 0u;  // Initial value of the correct type.
-  return std::accumulate(alloc_stats_, alloc_stats_ + arraysize(alloc_stats_), init);
+  return std::accumulate(alloc_stats_.begin(), alloc_stats_.end(), init);
 }
 
 template <bool kCount>
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index 2feb28a..6c764cb 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -21,6 +21,7 @@
 #include <stddef.h>
 
 #include "base/bit_utils.h"
+#include "base/dchecked_vector.h"
 #include "base/memory_tool.h"
 #include "debug_stack.h"
 #include "macros.h"
@@ -132,8 +133,7 @@
 
  private:
   size_t num_allocations_;
-  // TODO: Use std::array<size_t, kNumArenaAllocKinds> from C++11 when we upgrade the STL.
-  size_t alloc_stats_[kNumArenaAllocKinds];  // Bytes used by various allocation kinds.
+  dchecked_vector<size_t> alloc_stats_;  // Bytes used by various allocation kinds.
 
   static const char* const kAllocNames[];
 };
diff --git a/runtime/base/file_magic.cc b/runtime/base/file_magic.cc
index de6f423..568a7ae 100644
--- a/runtime/base/file_magic.cc
+++ b/runtime/base/file_magic.cc
@@ -20,13 +20,16 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include "android-base/stringprintf.h"
+
 #include "base/logging.h"
 #include "base/unix_file/fd_file.h"
 #include "dex_file.h"
-#include "stringprintf.h"
 
 namespace art {
 
+using android::base::StringPrintf;
+
 File OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) {
   CHECK(magic != nullptr);
   File fd(filename, O_RDONLY, /* check_usage */ false);
diff --git a/runtime/base/hash_set.h b/runtime/base/hash_set.h
index f24a862..a22efcf 100644
--- a/runtime/base/hash_set.h
+++ b/runtime/base/hash_set.h
@@ -672,6 +672,8 @@
   T* data_;  // Backing storage.
   double min_load_factor_;
   double max_load_factor_;
+
+  ART_FRIEND_TEST(InternTableTest, CrossHash);
 };
 
 template <class T, class EmptyFn, class HashFn, class Pred, class Alloc>
diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h
index 92b7c65..44a84c8 100644
--- a/runtime/base/mutex-inl.h
+++ b/runtime/base/mutex-inl.h
@@ -21,7 +21,6 @@
 
 #include "mutex.h"
 
-#include "base/stringprintf.h"
 #include "base/value_object.h"
 #include "thread.h"
 #include "utils.h"
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index ce452cb..9116097 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -19,6 +19,8 @@
 #include <errno.h>
 #include <sys/time.h>
 
+#include "android-base/stringprintf.h"
+
 #include "atomic.h"
 #include "base/logging.h"
 #include "base/time_utils.h"
@@ -30,6 +32,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static Atomic<Locks::ClientCallback*> safe_to_call_abort_callback(nullptr);
 
 Mutex* Locks::abort_lock_ = nullptr;
diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc
index 0e8031f..d4bb56b 100644
--- a/runtime/base/scoped_flock.cc
+++ b/runtime/base/scoped_flock.cc
@@ -19,12 +19,15 @@
 #include <sys/file.h>
 #include <sys/stat.h>
 
+#include "android-base/stringprintf.h"
+
 #include "base/logging.h"
-#include "base/stringprintf.h"
 #include "base/unix_file/fd_file.h"
 
 namespace art {
 
+using android::base::StringPrintf;
+
 bool ScopedFlock::Init(const char* filename, std::string* error_msg) {
   return Init(filename, O_CREAT | O_RDWR, true, error_msg);
 }
diff --git a/runtime/base/stringprintf.cc b/runtime/base/stringprintf.cc
deleted file mode 100644
index 8fd9257..0000000
--- a/runtime/base/stringprintf.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2011 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 "stringprintf.h"
-
-#include <stdio.h>
-
-namespace art {
-
-void StringAppendV(std::string* dst, const char* format, va_list ap) {
-  // First try with a small fixed size buffer
-  char space[1024];
-
-  // It's possible for methods that use a va_list to invalidate
-  // the data in it upon use.  The fix is to make a copy
-  // of the structure before using it and use that copy instead.
-  va_list backup_ap;
-  va_copy(backup_ap, ap);
-  int result = vsnprintf(space, sizeof(space), format, backup_ap);
-  va_end(backup_ap);
-
-  if (result < static_cast<int>(sizeof(space))) {
-    if (result >= 0) {
-      // Normal case -- everything fit.
-      dst->append(space, result);
-      return;
-    }
-
-    if (result < 0) {
-      // Just an error.
-      return;
-    }
-  }
-
-  // Increase the buffer size to the size requested by vsnprintf,
-  // plus one for the closing \0.
-  int length = result+1;
-  char* buf = new char[length];
-
-  // Restore the va_list before we use it again
-  va_copy(backup_ap, ap);
-  result = vsnprintf(buf, length, format, backup_ap);
-  va_end(backup_ap);
-
-  if (result >= 0 && result < length) {
-    // It fit
-    dst->append(buf, result);
-  }
-  delete[] buf;
-}
-
-std::string StringPrintf(const char* fmt, ...) {
-  va_list ap;
-  va_start(ap, fmt);
-  std::string result;
-  StringAppendV(&result, fmt, ap);
-  va_end(ap);
-  return result;
-}
-
-void StringAppendF(std::string* dst, const char* format, ...) {
-  va_list ap;
-  va_start(ap, format);
-  StringAppendV(dst, format, ap);
-  va_end(ap);
-}
-
-}  // namespace art
diff --git a/runtime/base/stringprintf.h b/runtime/base/stringprintf.h
deleted file mode 100644
index 4767a75..0000000
--- a/runtime/base/stringprintf.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_BASE_STRINGPRINTF_H_
-#define ART_RUNTIME_BASE_STRINGPRINTF_H_
-
-#include <stdarg.h>
-#include <string>
-
-namespace art {
-
-// Returns a string corresponding to printf-like formatting of the arguments.
-std::string StringPrintf(const char* fmt, ...)
-        __attribute__((__format__(__printf__, 1, 2)));
-
-// Appends a printf-like formatting of the arguments to 'dst'.
-void StringAppendF(std::string* dst, const char* fmt, ...)
-        __attribute__((__format__(__printf__, 2, 3)));
-
-// Appends a printf-like formatting of the arguments to 'dst'.
-void StringAppendV(std::string* dst, const char* format, va_list ap);
-
-}  // namespace art
-
-#endif  // ART_RUNTIME_BASE_STRINGPRINTF_H_
diff --git a/runtime/base/time_utils.cc b/runtime/base/time_utils.cc
index 57f198d..3c09d5a 100644
--- a/runtime/base/time_utils.cc
+++ b/runtime/base/time_utils.cc
@@ -20,8 +20,9 @@
 
 #include "time_utils.h"
 
+#include "android-base/stringprintf.h"
+
 #include "base/logging.h"
-#include "base/stringprintf.h"
 
 #if defined(__APPLE__)
 #include <sys/time.h>
@@ -29,6 +30,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 std::string PrettyDuration(uint64_t nano_duration, size_t max_fraction_digits) {
   if (nano_duration == 0) {
     return "0";
diff --git a/runtime/cha.cc b/runtime/cha.cc
index be675a8..d94b091 100644
--- a/runtime/cha.cc
+++ b/runtime/cha.cc
@@ -100,7 +100,11 @@
 
   bool VisitFrame() OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
     ArtMethod* method = GetMethod();
-    if (method == nullptr || method->IsRuntimeMethod() || method->IsNative()) {
+    // Avoid types of methods that do not have an oat quick method header.
+    if (method == nullptr ||
+        method->IsRuntimeMethod() ||
+        method->IsNative() ||
+        method->IsProxyMethod()) {
       return true;
     }
     if (GetCurrentQuickFrame() == nullptr) {
@@ -110,6 +114,7 @@
     // Method may have multiple versions of compiled code. Check
     // the method header to see if it has should_deoptimize flag.
     const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
+    DCHECK(method_header != nullptr);
     if (!method_header->HasShouldDeoptimizeFlag()) {
       // This compiled version doesn't have should_deoptimize flag. Skip.
       return true;
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 6c27bc6..1c3328e 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -20,6 +20,8 @@
 #include <sys/mman.h>
 #include <zlib.h>
 
+#include "android-base/stringprintf.h"
+
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "base/logging.h"
@@ -42,6 +44,9 @@
 
 namespace art {
 
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
 /*
  * ===========================================================================
  *      JNI function helpers
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 0a65cd1..5fc5f1a 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -25,6 +25,7 @@
 #include "mirror/class_loader.h"
 #include "mirror/dex_cache-inl.h"
 #include "mirror/iftable.h"
+#include "mirror/throwable.h"
 #include "mirror/object_array.h"
 #include "handle_scope-inl.h"
 #include "scoped_thread_state_change-inl.h"
@@ -72,7 +73,7 @@
   // MethodVerifier refuses methods with string_idx out of bounds.
   DCHECK_LT(string_idx.index_, declaring_class->GetDexFile().NumStringIds());
   ObjPtr<mirror::String> string =
-        mirror::StringDexCachePair::Lookup(declaring_class->GetDexCacheStrings(),
+        mirror::StringDexCachePair::Lookup(declaring_class->GetDexCache()->GetStrings(),
                                            string_idx.index_,
                                            mirror::DexCache::kDexCacheStringCacheSize).Read();
   if (UNLIKELY(string == nullptr)) {
@@ -80,26 +81,34 @@
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
     const DexFile& dex_file = *dex_cache->GetDexFile();
     string = ResolveString(dex_file, string_idx, dex_cache);
-    if (string != nullptr) {
-      DCHECK_EQ(dex_cache->GetResolvedString(string_idx), string);
-    }
   }
   return string.Ptr();
 }
 
 inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) {
   Thread::PoisonObjectPointersIfDebug();
+  if (kIsDebugBuild) {
+    Thread::Current()->AssertNoPendingException();
+  }
   ObjPtr<mirror::Class> resolved_type =
       referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_);
   if (UNLIKELY(resolved_type == nullptr)) {
-    ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
     StackHandleScope<2> hs(Thread::Current());
-    Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
-    Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
-    const DexFile& dex_file = *dex_cache->GetDexFile();
-    resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader);
-    // Note: We cannot check here to see whether we added the type to the cache. The type
-    //       might be an erroneous class, which results in it being hidden from us.
+    // There could be an out of bounds exception from GetDexCacheResolvedType, don't call
+    // ResolveType for this case.
+    if (LIKELY(!hs.Self()->IsExceptionPending())) {
+      ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
+      Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
+      Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
+      const DexFile& dex_file = *dex_cache->GetDexFile();
+      resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader);
+      // Note: We cannot check here to see whether we added the type to the cache. The type
+      //       might be an erroneous class, which results in it being hidden from us.
+    } else {
+      // Make sure its an array out of bounds exception.
+      DCHECK(hs.Self()->GetException()->GetClass()->DescriptorEquals(
+          "Ljava/lang/ArrayIndexOutOfBoundsException;"));
+    }
   }
   return resolved_type.Ptr();
 }
@@ -180,20 +189,15 @@
   return dex_cache->GetResolvedField(field_idx, image_pointer_size_);
 }
 
-inline ArtField* ClassLinker::GetResolvedField(uint32_t field_idx,
-                                               ObjPtr<mirror::Class> field_declaring_class) {
-  return GetResolvedField(field_idx, MakeObjPtr(field_declaring_class->GetDexCache()));
-}
-
 inline ArtField* ClassLinker::ResolveField(uint32_t field_idx,
                                            ArtMethod* referrer,
                                            bool is_static) {
   Thread::PoisonObjectPointersIfDebug();
   ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
-  ArtField* resolved_field = GetResolvedField(field_idx, declaring_class);
+  ArtField* resolved_field = GetResolvedField(field_idx, referrer->GetDexCache());
   if (UNLIKELY(resolved_field == nullptr)) {
     StackHandleScope<2> hs(Thread::Current());
-    Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
+    Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
     Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
     const DexFile& dex_file = *dex_cache->GetDexFile();
     resolved_field = ResolveField(dex_file, field_idx, dex_cache, class_loader, is_static);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f3a5be2..5b8d4e4 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -28,6 +28,8 @@
 #include <utility>
 #include <vector>
 
+#include "android-base/stringprintf.h"
+
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "base/arena_allocator.h"
@@ -106,6 +108,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static constexpr bool kSanityCheckObjects = kIsDebugBuild;
 static constexpr bool kVerifyArtMethodDeclaringClasses = kIsDebugBuild;
 
@@ -1401,39 +1405,20 @@
   return true;
 }
 
-// Update the class loader and resolved string dex cache array of classes. Should only be used on
-// classes in the image space.
-class UpdateClassLoaderAndResolvedStringsVisitor {
+// Update the class loader. Should only be used on classes in the image space.
+class UpdateClassLoaderVisitor {
  public:
-  UpdateClassLoaderAndResolvedStringsVisitor(gc::space::ImageSpace* space,
-                                             ObjPtr<mirror::ClassLoader> class_loader,
-                                             bool forward_strings)
+  UpdateClassLoaderVisitor(gc::space::ImageSpace* space, ObjPtr<mirror::ClassLoader> class_loader)
       : space_(space),
-        class_loader_(class_loader),
-        forward_strings_(forward_strings) {}
+        class_loader_(class_loader) {}
 
   bool operator()(ObjPtr<mirror::Class> klass) const REQUIRES_SHARED(Locks::mutator_lock_) {
-    if (forward_strings_) {
-      mirror::StringDexCacheType* strings = klass->GetDexCacheStrings();
-      if (strings != nullptr) {
-        DCHECK(
-            space_->GetImageHeader().GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
-                reinterpret_cast<uint8_t*>(strings) - space_->Begin()))
-            << "String dex cache array for " << klass->PrettyClass() << " is not in app image";
-        // Dex caches have already been updated, so take the strings pointer from there.
-        mirror::StringDexCacheType* new_strings = klass->GetDexCache()->GetStrings();
-        DCHECK_NE(strings, new_strings);
-        klass->SetDexCacheStrings(new_strings);
-      }
-    }
-    // Finally, update class loader.
     klass->SetClassLoader(class_loader_);
     return true;
   }
 
   gc::space::ImageSpace* const space_;
   ObjPtr<mirror::ClassLoader> const class_loader_;
-  const bool forward_strings_;
 };
 
 static std::unique_ptr<const DexFile> OpenOatDexFile(const OatFile* oat_file,
@@ -1655,13 +1640,6 @@
   Runtime* const runtime = Runtime::Current();
   gc::Heap* const heap = runtime->GetHeap();
   Thread* const self = Thread::Current();
-  StackHandleScope<2> hs(self);
-  Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(
-      hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>()));
-  Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
-      header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>()));
-  const OatFile* oat_file = space->GetOatFile();
-  std::unordered_set<mirror::ClassLoader*> image_class_loaders;
   // Check that the image is what we are expecting.
   if (image_pointer_size_ != space->GetImageHeader().GetPointerSize()) {
     *error_msg = StringPrintf("Application image pointer size does not match runtime: %zu vs %zu",
@@ -1669,6 +1647,22 @@
                               image_pointer_size_);
     return false;
   }
+  size_t expected_image_roots = ImageHeader::NumberOfImageRoots(app_image);
+  if (static_cast<size_t>(header.GetImageRoots()->GetLength()) != expected_image_roots) {
+    *error_msg = StringPrintf("Expected %zu image roots but got %d",
+                              expected_image_roots,
+                              header.GetImageRoots()->GetLength());
+    return false;
+  }
+  StackHandleScope<3> hs(self);
+  Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(
+      hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>()));
+  Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
+      header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>()));
+  static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax,
+                "Class loader should be the last image root.");
+  MutableHandle<mirror::ClassLoader> image_class_loader(hs.NewHandle(
+      app_image ? header.GetImageRoot(ImageHeader::kClassLoader)->AsClassLoader() : nullptr));
   DCHECK(class_roots.Get() != nullptr);
   if (class_roots->GetLength() != static_cast<int32_t>(kClassRootsMax)) {
     *error_msg = StringPrintf("Expected %d class roots but got %d",
@@ -1683,6 +1677,7 @@
       return false;
     }
   }
+  const OatFile* oat_file = space->GetOatFile();
   if (oat_file->GetOatHeader().GetDexFileCount() !=
       static_cast<uint32_t>(dex_caches->GetLength())) {
     *error_msg = "Dex cache count and dex file count mismatch while trying to initialize from "
@@ -1715,15 +1710,11 @@
       // The current dex file field is bogus, overwrite it so that we can get the dex file in the
       // loop below.
       h_dex_cache->SetDexFile(dex_file.get());
-      // Check that each class loader resolved the same way.
-      // TODO: Store image class loaders as image roots.
       GcRoot<mirror::Class>* const types = h_dex_cache->GetResolvedTypes();
       for (int32_t j = 0, num_types = h_dex_cache->NumResolvedTypes(); j < num_types; j++) {
         ObjPtr<mirror::Class> klass = types[j].Read();
         if (klass != nullptr) {
           DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
-          ObjPtr<mirror::ClassLoader> image_class_loader = klass->GetClassLoader();
-          image_class_loaders.insert(image_class_loader.Ptr());
         }
       }
     } else {
@@ -1749,59 +1740,57 @@
     // for PathClassLoader does this by looping through the array of dex files. To ensure they
     // resolve the same way, simply flatten the hierarchy in the way the resolution order would be,
     // and check that the dex file names are the same.
-    for (ObjPtr<mirror::ClassLoader> image_class_loader : image_class_loaders) {
-      if (IsBootClassLoader(soa, image_class_loader)) {
-        // The dex cache can reference types from the boot class loader.
-        continue;
+    if (IsBootClassLoader(soa, image_class_loader.Get())) {
+      *error_msg = "Unexpected BootClassLoader in app image";
+      return false;
+    }
+    std::list<mirror::String*> image_dex_file_names;
+    std::string temp_error_msg;
+    if (!FlattenPathClassLoader(image_class_loader.Get(), &image_dex_file_names, &temp_error_msg)) {
+      *error_msg = StringPrintf("Failed to flatten image class loader hierarchy '%s'",
+                                temp_error_msg.c_str());
+      return false;
+    }
+    std::list<mirror::String*> loader_dex_file_names;
+    if (!FlattenPathClassLoader(class_loader.Get(), &loader_dex_file_names, &temp_error_msg)) {
+      *error_msg = StringPrintf("Failed to flatten class loader hierarchy '%s'",
+                                temp_error_msg.c_str());
+      return false;
+    }
+    // Add the temporary dex path list elements at the end.
+    auto elements = soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements);
+    for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) {
+      ObjPtr<mirror::Object> element = elements->GetWithoutChecks(i);
+      if (element != nullptr) {
+        // If we are somewhere in the middle of the array, there may be nulls at the end.
+        loader_dex_file_names.push_back(GetDexPathListElementName(element));
       }
-      std::list<mirror::String*> image_dex_file_names;
-      std::string temp_error_msg;
-      if (!FlattenPathClassLoader(image_class_loader, &image_dex_file_names, &temp_error_msg)) {
-        *error_msg = StringPrintf("Failed to flatten image class loader hierarchy '%s'",
-                                  temp_error_msg.c_str());
-        return false;
+    }
+    // Ignore the number of image dex files since we are adding those to the class loader anyways.
+    CHECK_GE(static_cast<size_t>(image_dex_file_names.size()),
+             static_cast<size_t>(dex_caches->GetLength()));
+    size_t image_count = image_dex_file_names.size() - dex_caches->GetLength();
+    // Check that the dex file names match.
+    bool equal = image_count == loader_dex_file_names.size();
+    if (equal) {
+      auto it1 = image_dex_file_names.begin();
+      auto it2 = loader_dex_file_names.begin();
+      for (size_t i = 0; equal && i < image_count; ++i, ++it1, ++it2) {
+        equal = equal && (*it1)->Equals(*it2);
       }
-      std::list<mirror::String*> loader_dex_file_names;
-      if (!FlattenPathClassLoader(class_loader.Get(), &loader_dex_file_names, &temp_error_msg)) {
-        *error_msg = StringPrintf("Failed to flatten class loader hierarchy '%s'",
-                                  temp_error_msg.c_str());
-        return false;
+    }
+    if (!equal) {
+      VLOG(image) << "Image dex files " << image_dex_file_names.size();
+      for (ObjPtr<mirror::String> name : image_dex_file_names) {
+        VLOG(image) << name->ToModifiedUtf8();
       }
-      // Add the temporary dex path list elements at the end.
-      auto elements = soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements);
-      for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) {
-        ObjPtr<mirror::Object> element = elements->GetWithoutChecks(i);
-        if (element != nullptr) {
-          // If we are somewhere in the middle of the array, there may be nulls at the end.
-          loader_dex_file_names.push_back(GetDexPathListElementName(element));
-        }
+      VLOG(image) << "Loader dex files " << loader_dex_file_names.size();
+      for (ObjPtr<mirror::String> name : loader_dex_file_names) {
+        VLOG(image) << name->ToModifiedUtf8();
       }
-      // Ignore the number of image dex files since we are adding those to the class loader anyways.
-      CHECK_GE(static_cast<size_t>(image_dex_file_names.size()),
-               static_cast<size_t>(dex_caches->GetLength()));
-      size_t image_count = image_dex_file_names.size() - dex_caches->GetLength();
-      // Check that the dex file names match.
-      bool equal = image_count == loader_dex_file_names.size();
-      if (equal) {
-        auto it1 = image_dex_file_names.begin();
-        auto it2 = loader_dex_file_names.begin();
-        for (size_t i = 0; equal && i < image_count; ++i, ++it1, ++it2) {
-          equal = equal && (*it1)->Equals(*it2);
-        }
-      }
-      if (!equal) {
-        VLOG(image) << "Image dex files " << image_dex_file_names.size();
-        for (ObjPtr<mirror::String> name : image_dex_file_names) {
-          VLOG(image) << name->ToModifiedUtf8();
-        }
-        VLOG(image) << "Loader dex files " << loader_dex_file_names.size();
-        for (ObjPtr<mirror::String> name : loader_dex_file_names) {
-          VLOG(image) << name->ToModifiedUtf8();
-        }
-        *error_msg = "Rejecting application image due to class loader mismatch";
-        // Ignore class loader mismatch for now since these would just use possibly incorrect
-        // oat code anyways. The structural class check should be done in the parent.
-      }
+      *error_msg = "Rejecting application image due to class loader mismatch";
+      // Ignore class loader mismatch for now since these would just use possibly incorrect
+      // oat code anyways. The structural class check should be done in the parent.
     }
   }
 
@@ -1856,10 +1845,8 @@
     }
     // Update class loader and resolved strings. If added_class_table is false, the resolved
     // strings were forwarded UpdateAppImageClassLoadersAndDexCaches.
-    UpdateClassLoaderAndResolvedStringsVisitor visitor(space,
-                                                       class_loader.Get(),
-                                                       forward_dex_cache_arrays);
-    for (ClassTable::TableSlot& root : temp_set) {
+    UpdateClassLoaderVisitor visitor(space, class_loader.Get());
+    for (const ClassTable::TableSlot& root : temp_set) {
       visitor(root.Read());
     }
     // forward_dex_cache_arrays is true iff we copied all of the dex cache arrays into the .bss.
@@ -2516,8 +2503,9 @@
       // the Java-side could still succeed for racy programs if another thread is actively
       // modifying the class loader's path list.
 
-      if (Runtime::Current()->IsAotCompiler()) {
-        // Oops, compile-time, can't run actual class-loader code.
+      if (!self->CanCallIntoJava()) {
+        // Oops, we can't call into java so we can't run actual class-loader code.
+        // This is true for e.g. for the compiler (jit or aot).
         ObjPtr<mirror::Throwable> pre_allocated =
             Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
         self->SetException(pre_allocated);
@@ -2659,6 +2647,8 @@
 
   ObjectLock<mirror::Class> lock(self, klass);
   klass->SetClinitThreadId(self->GetTid());
+  // Make sure we have a valid empty iftable even if there are errors.
+  klass->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable());
 
   // Add the newly loaded class to the loaded classes table.
   ObjPtr<mirror::Class> existing = InsertClass(descriptor, klass.Get(), hash);
@@ -3010,7 +3000,6 @@
 
   klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def));
   klass->SetDexTypeIndex(dex_class_def.class_idx_);
-  CHECK(klass->GetDexCacheStrings() != nullptr);
 }
 
 void ClassLinker::LoadClass(Thread* self,
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 9b25303..6ef882a 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -327,8 +327,6 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
 
-  ArtField* GetResolvedField(uint32_t field_idx, ObjPtr<mirror::Class> field_declaring_class)
-      REQUIRES_SHARED(Locks::mutator_lock_);
   ArtField* GetResolvedField(uint32_t field_idx, ObjPtr<mirror::DexCache> dex_cache)
       REQUIRES_SHARED(Locks::mutator_lock_);
   ArtField* ResolveField(uint32_t field_idx, ArtMethod* referrer, bool is_static)
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 685677b..42108d8 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -278,8 +278,6 @@
     EXPECT_FALSE(klass->IsArrayClass());
     EXPECT_TRUE(klass->GetComponentType() == nullptr);
     EXPECT_TRUE(klass->IsInSamePackage(klass.Get()));
-    EXPECT_TRUE(klass->GetDexCacheStrings() != nullptr);
-    EXPECT_EQ(klass->GetDexCacheStrings(), klass->GetDexCache()->GetStrings());
     std::string temp2;
     EXPECT_TRUE(mirror::Class::IsInSamePackage(klass->GetDescriptor(&temp),
                                                klass->GetDescriptor(&temp2)));
@@ -590,7 +588,6 @@
     addOffset(OFFSETOF_MEMBER(mirror::Class, component_type_), "componentType");
     addOffset(OFFSETOF_MEMBER(mirror::Class, copied_methods_offset_), "copiedMethodsOffset");
     addOffset(OFFSETOF_MEMBER(mirror::Class, dex_cache_), "dexCache");
-    addOffset(OFFSETOF_MEMBER(mirror::Class, dex_cache_strings_), "dexCacheStrings");
     addOffset(OFFSETOF_MEMBER(mirror::Class, dex_class_def_idx_), "dexClassDefIndex");
     addOffset(OFFSETOF_MEMBER(mirror::Class, dex_type_idx_), "dexTypeIndex");
     addOffset(OFFSETOF_MEMBER(mirror::Class, ext_data_), "extData");
@@ -1175,6 +1172,24 @@
   EXPECT_TRUE(init->IsInitialized());
 }
 
+TEST_F(ClassLinkerTest, ErroneousClass) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject jclass_loader = LoadMultiDex("ErroneousA", "ErroneousB");
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
+  hs.Self()->AssertNoPendingException();
+  const char* descriptor = "LErroneous;";
+  ObjPtr<mirror::Class> klass = class_linker_->FindClass(soa.Self(), descriptor, class_loader);
+  // Erronenous since we are extending final class.
+  hs.Self()->AssertPendingException();
+  EXPECT_TRUE(klass == nullptr);
+  klass = class_linker_->LookupClass(soa.Self(), descriptor, class_loader.Get());
+  EXPECT_FALSE(klass == nullptr);
+  EXPECT_TRUE(klass->IsErroneous());
+  EXPECT_TRUE(klass->GetIfTable() != nullptr);
+}
+
 TEST_F(ClassLinkerTest, FinalizableBit) {
   ScopedObjectAccess soa(Thread::Current());
   mirror::Class* c;
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 8226e60..743fcc8 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -24,11 +24,12 @@
 #include <stdlib.h>
 
 #include "../../external/icu/icu4c/source/common/unicode/uvernum.h"
+#include "android-base/stringprintf.h"
+
 #include "art_field-inl.h"
 #include "base/macros.h"
 #include "base/logging.h"
 #include "base/stl_util.h"
-#include "base/stringprintf.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
 #include "compiler_callbacks.h"
@@ -65,6 +66,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 ScratchFile::ScratchFile() {
   // ANDROID_DATA needs to be set
   CHECK_NE(static_cast<char*>(nullptr), getenv("ANDROID_DATA")) <<
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 9f0dbbb..c30272e 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -18,6 +18,7 @@
 
 #include <sstream>
 
+#include "android-base/stringprintf.h"
 #include "ScopedLocalRef.h"
 
 #include "art_field-inl.h"
@@ -37,6 +38,9 @@
 
 namespace art {
 
+using android::base::StringAppendV;
+using android::base::StringPrintf;
+
 static void AddReferrerLocation(std::ostream& os, ObjPtr<mirror::Class> referrer)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (referrer != nullptr) {
diff --git a/runtime/compiler_filter.cc b/runtime/compiler_filter.cc
index 6e3e1d8..dc89d32 100644
--- a/runtime/compiler_filter.cc
+++ b/runtime/compiler_filter.cc
@@ -60,6 +60,26 @@
   UNREACHABLE();
 }
 
+bool CompilerFilter::IsAnyMethodCompilationEnabled(Filter filter) {
+  switch (filter) {
+    case CompilerFilter::kVerifyNone:
+    case CompilerFilter::kVerifyAtRuntime:
+    case CompilerFilter::kVerifyProfile: return false;
+
+    case CompilerFilter::kInterpretOnly:
+    case CompilerFilter::kSpaceProfile:
+    case CompilerFilter::kSpace:
+    case CompilerFilter::kBalanced:
+    case CompilerFilter::kTime:
+    case CompilerFilter::kSpeedProfile:
+    case CompilerFilter::kSpeed:
+    case CompilerFilter::kLayoutProfile:
+    case CompilerFilter::kEverythingProfile:
+    case CompilerFilter::kEverything: return true;
+  }
+  UNREACHABLE();
+}
+
 bool CompilerFilter::IsVerificationEnabled(Filter filter) {
   switch (filter) {
     case CompilerFilter::kVerifyNone:
diff --git a/runtime/compiler_filter.h b/runtime/compiler_filter.h
index 781d43a..7eb5f9a 100644
--- a/runtime/compiler_filter.h
+++ b/runtime/compiler_filter.h
@@ -52,6 +52,11 @@
   static bool IsBytecodeCompilationEnabled(Filter filter);
 
   // Returns true if an oat file with this compiler filter contains
+  // compiled executable code for bytecode, JNI methods, or quickened dex
+  // bytecode.
+  static bool IsAnyMethodCompilationEnabled(Filter filter);
+
+  // Returns true if an oat file with this compiler filter contains
   // compiled executable code for JNI methods.
   static bool IsJniCompilationEnabled(Filter filter);
 
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index e339666..df4413d 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -20,6 +20,8 @@
 
 #include <set>
 
+#include "android-base/stringprintf.h"
+
 #include "arch/context.h"
 #include "art_field-inl.h"
 #include "art_method-inl.h"
@@ -61,6 +63,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 // The key identifying the debugger to update instrumentation.
 static constexpr const char* kDbgInstrumentationKey = "Debugger";
 
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index aa8fb38..7d704ad 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -28,10 +28,11 @@
 #include <sstream>
 #include <type_traits>
 
+#include "android-base/stringprintf.h"
+
 #include "base/enums.h"
 #include "base/file_magic.h"
 #include "base/logging.h"
-#include "base/stringprintf.h"
 #include "base/systrace.h"
 #include "base/unix_file/fd_file.h"
 #include "dex_file-inl.h"
@@ -45,6 +46,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static_assert(sizeof(dex::StringIndex) == sizeof(uint32_t), "StringIndex size is wrong");
 static_assert(std::is_trivially_copyable<dex::StringIndex>::value, "StringIndex not trivial");
 static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong");
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index 52b9f11..9504e8b 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -18,6 +18,8 @@
 
 #include <stdlib.h>
 
+#include "android-base/stringprintf.h"
+
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "class_linker-inl.h"
@@ -31,6 +33,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 struct DexFile::AnnotationValue {
   JValue value_;
   uint8_t type_;
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 07f0fca..a3ab9fa 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -22,7 +22,8 @@
 #include <limits>
 #include <memory>
 
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
 #include "dex_file-inl.h"
 #include "experimental_flags.h"
 #include "leb128.h"
@@ -32,6 +33,9 @@
 
 namespace art {
 
+using android::base::StringAppendV;
+using android::base::StringPrintf;
+
 static constexpr uint32_t kTypeIdLimit = std::numeric_limits<uint16_t>::max();
 
 static bool IsValidOrNoTypeId(uint16_t low, uint16_t high) {
diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc
index 9902389..7b8974f 100644
--- a/runtime/dex_instruction.cc
+++ b/runtime/dex_instruction.cc
@@ -21,12 +21,15 @@
 #include <iomanip>
 #include <sstream>
 
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
 #include "dex_file-inl.h"
 #include "utils.h"
 
 namespace art {
 
+using android::base::StringPrintf;
+
 const char* const Instruction::kInstructionNames[] = {
 #define INSTRUCTION_NAME(o, c, pname, f, i, a, v) pname,
 #include "dex_instruction_list.h"
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index ee0f340..59b734f 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -20,11 +20,11 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
 #include "arch/instruction_set.h"
 #include "base/logging.h"
-#include "base/stringprintf.h"
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "elf_file_impl.h"
@@ -34,6 +34,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 template <typename ElfTypes>
 ElfFileImpl<ElfTypes>::ElfFileImpl(File* file, bool writable,
                                    bool program_header_only,
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index f6eeffc..14c9c21 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -563,7 +563,7 @@
       HandleWrapperObjPtr<mirror::Object> h_this(hs2.NewHandleWrapper(this_object));
       Handle<mirror::Class> h_referring_class(hs2.NewHandle(referrer->GetDeclaringClass()));
       const dex::TypeIndex method_type_idx =
-          h_referring_class->GetDexFile().GetMethodId(method_idx).class_idx_;
+          referrer->GetDexFile()->GetMethodId(method_idx).class_idx_;
       mirror::Class* method_reference_class = class_linker->ResolveType(method_type_idx, referrer);
       if (UNLIKELY(method_reference_class == nullptr)) {
         // Bad type idx.
@@ -673,8 +673,7 @@
                                size_t expected_size) {
   ScopedAssertNoThreadSuspension ants(__FUNCTION__);
   ArtField* resolved_field =
-      referrer->GetDeclaringClass()->GetDexCache()->GetResolvedField(field_idx,
-                                                                     kRuntimePointerSize);
+      referrer->GetDexCache()->GetResolvedField(field_idx, kRuntimePointerSize);
   if (UNLIKELY(resolved_field == nullptr)) {
     return nullptr;
   }
@@ -733,7 +732,7 @@
   }
   mirror::Class* referring_class = referrer->GetDeclaringClass();
   ArtMethod* resolved_method =
-      referring_class->GetDexCache()->GetResolvedMethod(method_idx, kRuntimePointerSize);
+      referrer->GetDexCache()->GetResolvedMethod(method_idx, kRuntimePointerSize);
   if (UNLIKELY(resolved_method == nullptr)) {
     return nullptr;
   }
@@ -759,9 +758,9 @@
   } else if (type == kSuper) {
     // TODO This lookup is rather slow.
     dex::TypeIndex method_type_idx =
-        referring_class->GetDexFile().GetMethodId(method_idx).class_idx_;
+        referrer->GetDexFile()->GetMethodId(method_idx).class_idx_;
     mirror::Class* method_reference_class =
-        referring_class->GetDexCache()->GetResolvedType(method_type_idx);
+        referrer->GetDexCache()->GetResolvedType(method_type_idx);
     if (method_reference_class == nullptr) {
       // Need to do full type resolution...
       return nullptr;
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index f4d0bc7..eb9f039 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -16,8 +16,9 @@
 
 #include "space_bitmap-inl.h"
 
+#include "android-base/stringprintf.h"
+
 #include "art_field-inl.h"
-#include "base/stringprintf.h"
 #include "dex_file-inl.h"
 #include "mem_map.h"
 #include "mirror/object-inl.h"
@@ -28,6 +29,8 @@
 namespace gc {
 namespace accounting {
 
+using android::base::StringPrintf;
+
 template<size_t kAlignment>
 size_t SpaceBitmap<kAlignment>::ComputeBitmapSize(uint64_t capacity) {
   const uint64_t kBytesCoveredPerWord = kAlignment * kBitsPerIntPtrT;
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index 2e4475f..35a251f 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -16,6 +16,13 @@
 
 #include "rosalloc.h"
 
+#include <map>
+#include <list>
+#include <sstream>
+#include <vector>
+
+#include "android-base/stringprintf.h"
+
 #include "base/memory_tool.h"
 #include "base/mutex-inl.h"
 #include "gc/space/memory_tool_settings.h"
@@ -26,15 +33,12 @@
 #include "thread-inl.h"
 #include "thread_list.h"
 
-#include <map>
-#include <list>
-#include <sstream>
-#include <vector>
-
 namespace art {
 namespace gc {
 namespace allocator {
 
+using android::base::StringPrintf;
+
 static constexpr bool kUsePrefetchDuringAllocRun = false;
 static constexpr bool kPrefetchNewRunDataByZeroing = false;
 static constexpr size_t kPrefetchStride = 64;
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index ed16854..01bcb7d 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -18,6 +18,8 @@
 
 #include "garbage_collector.h"
 
+#include "android-base/stringprintf.h"
+
 #include "base/dumpable.h"
 #include "base/histogram-inl.h"
 #include "base/logging.h"
@@ -81,7 +83,7 @@
 }
 
 void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) {
-  ScopedTrace trace(StringPrintf("%s %s GC", PrettyCause(gc_cause), GetName()));
+  ScopedTrace trace(android::base::StringPrintf("%s %s GC", PrettyCause(gc_cause), GetName()));
   Thread* self = Thread::Current();
   uint64_t start_time = NanoTime();
   Iteration* current_iteration = GetCurrentIteration();
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 3ed138c..34d8284 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -21,6 +21,8 @@
 #include <unwind.h>  // For GC verification.
 #include <vector>
 
+#include "android-base/stringprintf.h"
+
 #include "allocation_listener.h"
 #include "art_field-inl.h"
 #include "base/allocator.h"
@@ -82,6 +84,8 @@
 
 namespace gc {
 
+using android::base::StringPrintf;
+
 static constexpr size_t kCollectorTransitionStressIterations = 0;
 static constexpr size_t kCollectorTransitionStressWait = 10 * 1000;  // Microseconds
 // Minimum amount of remaining bytes before a concurrent GC is triggered.
@@ -3919,7 +3923,7 @@
       ScopedObjectAccess soa(env);
       env->ThrowNew(WellKnownClasses::java_lang_RuntimeException,
                     StringPrintf("Attempted to free %zd native bytes with only %zd native bytes "
-                                 "registered as allocated", bytes, expected_size).c_str());
+                    "registered as allocated", bytes, expected_size).c_str());
       break;
     }
   } while (!native_bytes_allocated_.CompareExchangeWeakRelaxed(expected_size,
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index 2cde7d5..081be96 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -75,11 +75,10 @@
   MutexLock mu(self, *Locks::reference_processor_lock_);
   while ((!kUseReadBarrier && SlowPathEnabled()) ||
          (kUseReadBarrier && !self->GetWeakRefAccessEnabled())) {
-    mirror::HeapReference<mirror::Object>* const referent_addr =
-        reference->GetReferentReferenceAddr();
+    ObjPtr<mirror::Object> referent = reference->GetReferent<kWithoutReadBarrier>();
     // If the referent became cleared, return it. Don't need barrier since thread roots can't get
     // updated until after we leave the function due to holding the mutator lock.
-    if (referent_addr->AsMirrorPtr() == nullptr) {
+    if (referent == nullptr) {
       return nullptr;
     }
     // Try to see if the referent is already marked by using the is_marked_callback. We can return
@@ -91,10 +90,15 @@
       // case only black nodes can be safely returned. If the GC is preserving references, the
       // mutator could take a white field from a grey or white node and move it somewhere else
       // in the heap causing corruption since this field would get swept.
-      if (collector_->IsMarkedHeapReference(referent_addr)) {
+      // Use the cached referent instead of calling GetReferent since other threads could call
+      // Reference.clear() after we did the null check resulting in a null pointer being
+      // incorrectly passed to IsMarked. b/33569625
+      ObjPtr<mirror::Object> forwarded_ref = collector_->IsMarked(referent.Ptr());
+      if (forwarded_ref != nullptr) {
+        // Non null means that it is marked.
         if (!preserving_references_ ||
            (LIKELY(!reference->IsFinalizerReferenceInstance()) && reference->IsUnprocessed())) {
-          return referent_addr->AsMirrorPtr();
+          return forwarded_ref;
         }
       }
     }
@@ -265,11 +269,23 @@
   }
 }
 
-bool ReferenceProcessor::MakeCircularListIfUnenqueued(
-    ObjPtr<mirror::FinalizerReference> reference) {
+void ReferenceProcessor::ClearReferent(ObjPtr<mirror::Reference> ref) {
   Thread* self = Thread::Current();
   MutexLock mu(self, *Locks::reference_processor_lock_);
-  // Wait untul we are done processing reference.
+  // Need to wait until reference processing is done since IsMarkedHeapReference does not have a
+  // CAS. If we do not wait, it can result in the GC un-clearing references due to race conditions.
+  // This also handles the race where the referent gets cleared after a null check but before
+  // IsMarkedHeapReference is called.
+  WaitUntilDoneProcessingReferences(self);
+  if (Runtime::Current()->IsActiveTransaction()) {
+    ref->ClearReferent<true>();
+  } else {
+    ref->ClearReferent<false>();
+  }
+}
+
+void ReferenceProcessor::WaitUntilDoneProcessingReferences(Thread* self) {
+  // Wait until we are done processing reference.
   while ((!kUseReadBarrier && SlowPathEnabled()) ||
          (kUseReadBarrier && !self->GetWeakRefAccessEnabled())) {
     // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
@@ -277,6 +293,13 @@
     self->CheckEmptyCheckpoint();
     condition_.WaitHoldingLocks(self);
   }
+}
+
+bool ReferenceProcessor::MakeCircularListIfUnenqueued(
+    ObjPtr<mirror::FinalizerReference> reference) {
+  Thread* self = Thread::Current();
+  MutexLock mu(self, *Locks::reference_processor_lock_);
+  WaitUntilDoneProcessingReferences(self);
   // At this point, since the sentinel of the reference is live, it is guaranteed to not be
   // enqueued if we just finished processing references. Otherwise, we may be doing the main GC
   // phase. Since we are holding the reference processor lock, it guarantees that reference
diff --git a/runtime/gc/reference_processor.h b/runtime/gc/reference_processor.h
index 759b7e1..b15544d 100644
--- a/runtime/gc/reference_processor.h
+++ b/runtime/gc/reference_processor.h
@@ -73,6 +73,9 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::reference_processor_lock_,
                !Locks::reference_queue_finalizer_references_lock_);
+  void ClearReferent(ObjPtr<mirror::Reference> ref)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!Locks::reference_processor_lock_);
 
  private:
   bool SlowPathEnabled() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -84,6 +87,10 @@
   // referents.
   void StartPreservingReferences(Thread* self) REQUIRES(!Locks::reference_processor_lock_);
   void StopPreservingReferences(Thread* self) REQUIRES(!Locks::reference_processor_lock_);
+  // Wait until reference processing is done.
+  void WaitUntilDoneProcessingReferences(Thread* self)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(Locks::reference_processor_lock_);
   // Collector which is clearing references, used by the GetReferent to return referents which are
   // already marked.
   collector::GarbageCollector* collector_ GUARDED_BY(Locks::reference_processor_lock_);
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 76f3692..e03958d 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -22,6 +22,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
 #include "art_method.h"
@@ -45,6 +46,9 @@
 namespace gc {
 namespace space {
 
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
 Atomic<uint32_t> ImageSpace::bitmap_index_(0);
 
 ImageSpace::ImageSpace(const std::string& image_filename,
diff --git a/runtime/gc/space/image_space_fs.h b/runtime/gc/space/image_space_fs.h
index fa941c0..5999548 100644
--- a/runtime/gc/space/image_space_fs.h
+++ b/runtime/gc/space/image_space_fs.h
@@ -20,9 +20,10 @@
 #include <dirent.h>
 #include <dlfcn.h>
 
+#include "android-base/stringprintf.h"
+
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/stringprintf.h"
 #include "base/unix_file/fd_file.h"
 #include "globals.h"
 #include "os.h"
@@ -56,7 +57,7 @@
       continue;
     }
     // We only want to delete regular files and symbolic links.
-    std::string file = StringPrintf("%s/%s", dir.c_str(), name);
+    std::string file = android::base::StringPrintf("%s/%s", dir.c_str(), name);
     if (de->d_type != DT_REG && de->d_type != DT_LNK) {
       if (de->d_type == DT_DIR) {
         if (recurse) {
diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc
index b1572cc..a186f4c 100644
--- a/runtime/gc/space/malloc_space.cc
+++ b/runtime/gc/space/malloc_space.cc
@@ -16,6 +16,8 @@
 
 #include "malloc_space.h"
 
+#include "android-base/stringprintf.h"
+
 #include "gc/accounting/card_table-inl.h"
 #include "gc/accounting/space_bitmap-inl.h"
 #include "gc/heap.h"
@@ -33,6 +35,8 @@
 namespace gc {
 namespace space {
 
+using android::base::StringPrintf;
+
 size_t MallocSpace::bitmap_index_ = 0;
 
 MallocSpace::MallocSpace(const std::string& name, MemMap* mem_map,
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index f13ff8c..bebcd71 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -74,8 +74,6 @@
 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_QUICK_CODE_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k64).Int32Value())))
 #define ART_METHOD_DECLARING_CLASS_OFFSET 0
 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DECLARING_CLASS_OFFSET), (static_cast<int32_t>(art::ArtMethod:: DeclaringClassOffset().Int32Value())))
-#define DECLARING_CLASS_DEX_CACHE_STRINGS_OFFSET 40
-DEFINE_CHECK_EQ(static_cast<int32_t>(DECLARING_CLASS_DEX_CACHE_STRINGS_OFFSET), (static_cast<int32_t>(art::mirror::Class:: DexCacheStringsOffset().Int32Value())))
 #define STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT 3
 DEFINE_CHECK_EQ(static_cast<int32_t>(STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT), (static_cast<int32_t>(art::WhichPowerOf2(sizeof(art::mirror::StringDexCachePair)))))
 #define STRING_DEX_CACHE_SIZE_MINUS_ONE 1023
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 8cbe491..fe6a6e9 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -37,9 +37,10 @@
 
 #include <set>
 
+#include "android-base/stringprintf.h"
+
 #include "art_field-inl.h"
 #include "base/logging.h"
-#include "base/stringprintf.h"
 #include "base/time_utils.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
@@ -797,8 +798,9 @@
       file->Erase();
     }
     if (!okay) {
-      std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s",
-                                   filename_.c_str(), strerror(errno)));
+      std::string msg(android::base::StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s",
+                                                  filename_.c_str(),
+                                                  strerror(errno)));
       ThrowRuntimeException("%s", msg.c_str());
       LOG(ERROR) << msg;
     }
diff --git a/runtime/image.cc b/runtime/image.cc
index 52c9f4e..2ef60c3 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -25,7 +25,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '3', '\0' };
+const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '4', '\0' };  // mirror::Class update
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/image.h b/runtime/image.h
index da9976a..6c76f49 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -189,6 +189,7 @@
   enum ImageRoot {
     kDexCaches,
     kClassRoots,
+    kClassLoader,  // App image only.
     kImageRootsMax,
   };
 
@@ -206,6 +207,10 @@
     kSectionCount,  // Number of elements in enum.
   };
 
+  static size_t NumberOfImageRoots(bool app_image) {
+    return app_image ? kImageRootsMax : kImageRootsMax - 1u;
+  }
+
   ArtMethod* GetImageMethod(ImageMethod index) const;
   void SetImageMethod(ImageMethod index, ArtMethod* method);
 
diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h
index 9c634fa..0e66ae9 100644
--- a/runtime/indirect_reference_table-inl.h
+++ b/runtime/indirect_reference_table-inl.h
@@ -19,6 +19,8 @@
 
 #include "indirect_reference_table.h"
 
+#include "android-base/stringprintf.h"
+
 #include "base/dumpable.h"
 #include "gc_root-inl.h"
 #include "obj_ptr-inl.h"
@@ -38,15 +40,15 @@
     return false;
   }
   if (UNLIKELY(GetIndirectRefKind(iref) == kHandleScopeOrInvalid)) {
-    AbortIfNoCheckJNI(StringPrintf("JNI ERROR (app bug): invalid %s %p",
-                                   GetIndirectRefKindString(kind_),
-                                   iref));
+    AbortIfNoCheckJNI(android::base::StringPrintf("JNI ERROR (app bug): invalid %s %p",
+                                                  GetIndirectRefKindString(kind_),
+                                                  iref));
     return false;
   }
   const uint32_t top_index = segment_state_.top_index;
   uint32_t idx = ExtractIndex(iref);
   if (UNLIKELY(idx >= top_index)) {
-    std::string msg = StringPrintf(
+    std::string msg = android::base::StringPrintf(
         "JNI ERROR (app bug): accessed stale %s %p  (index %d in a table of size %d)",
         GetIndirectRefKindString(kind_),
         iref,
@@ -56,9 +58,9 @@
     return false;
   }
   if (UNLIKELY(table_[idx].GetReference()->IsNull())) {
-    AbortIfNoCheckJNI(StringPrintf("JNI ERROR (app bug): accessed deleted %s %p",
-                                   GetIndirectRefKindString(kind_),
-                                   iref));
+    AbortIfNoCheckJNI(android::base::StringPrintf("JNI ERROR (app bug): accessed deleted %s %p",
+                                                  GetIndirectRefKindString(kind_),
+                                                  iref));
     return false;
   }
   if (UNLIKELY(!CheckEntry("use", iref, idx))) {
@@ -73,7 +75,7 @@
                                                uint32_t idx) const {
   IndirectRef checkRef = ToIndirectRef(idx);
   if (UNLIKELY(checkRef != iref)) {
-    std::string msg = StringPrintf(
+    std::string msg = android::base::StringPrintf(
         "JNI ERROR (app bug): attempt to %s stale %s %p (should be %p)",
         what,
         GetIndirectRefKindString(kind_),
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index 722b411..bf4cab2 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -16,6 +16,8 @@
 
 #include "indirect_reference_table-inl.h"
 
+#include "android-base/stringprintf.h"
+
 #include "class_linker-inl.h"
 #include "common_runtime_test.h"
 #include "mirror/object-inl.h"
@@ -23,6 +25,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 class IndirectReferenceTableTest : public CommonRuntimeTest {};
 
 static void CheckDump(IndirectReferenceTable* irt, size_t num_objects, size_t num_unique)
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 870d1ae..4ea1130 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -557,8 +557,10 @@
 }
 
 Instrumentation::InstrumentationLevel Instrumentation::GetCurrentInstrumentationLevel() const {
-  if (interpreter_stubs_installed_) {
+  if (interpreter_stubs_installed_ && interpret_only_) {
     return InstrumentationLevel::kInstrumentWithInterpreter;
+  } else if (interpreter_stubs_installed_) {
+    return InstrumentationLevel::kInstrumentWithInterpreterAndJit;
   } else if (entry_exit_stubs_installed_) {
     return InstrumentationLevel::kInstrumentWithInstrumentationStubs;
   } else {
@@ -566,6 +568,14 @@
   }
 }
 
+bool Instrumentation::RequiresInstrumentationInstallation(InstrumentationLevel new_level) const {
+  // We need to reinstall instrumentation if we go to a different level or if the current level is
+  // kInstrumentWithInterpreterAndJit since that level does not force all code to always use the
+  // interpreter and so we might have started running optimized code again.
+  return new_level == InstrumentationLevel::kInstrumentWithInterpreterAndJit ||
+      GetCurrentInstrumentationLevel() != new_level;
+}
+
 void Instrumentation::ConfigureStubs(const char* key, InstrumentationLevel desired_level) {
   // Store the instrumentation level for this key or remove it.
   if (desired_level == InstrumentationLevel::kInstrumentNothing) {
@@ -585,8 +595,7 @@
   interpret_only_ = (requested_level == InstrumentationLevel::kInstrumentWithInterpreter) ||
                     forced_interpret_only_;
 
-  InstrumentationLevel current_level = GetCurrentInstrumentationLevel();
-  if (requested_level == current_level) {
+  if (!RequiresInstrumentationInstallation(requested_level)) {
     // We're already set.
     return;
   }
@@ -595,7 +604,7 @@
   Locks::mutator_lock_->AssertExclusiveHeld(self);
   Locks::thread_list_lock_->AssertNotHeld(self);
   if (requested_level > InstrumentationLevel::kInstrumentNothing) {
-    if (requested_level == InstrumentationLevel::kInstrumentWithInterpreter) {
+    if (requested_level >= InstrumentationLevel::kInstrumentWithInterpreterAndJit) {
       interpreter_stubs_installed_ = true;
       entry_exit_stubs_installed_ = true;
     } else {
@@ -842,7 +851,8 @@
 void Instrumentation::DisableDeoptimization(const char* key) {
   CHECK_EQ(deoptimization_enabled_, true);
   // If we deoptimized everything, undo it.
-  if (interpreter_stubs_installed_) {
+  InstrumentationLevel level = GetCurrentInstrumentationLevel();
+  if (level == InstrumentationLevel::kInstrumentWithInterpreter) {
     UndeoptimizeEverything(key);
   }
   // Undeoptimized selected methods.
@@ -869,6 +879,14 @@
   return !deoptimization_enabled_ && !interpreter_stubs_installed_;
 }
 
+// TODO we don't check deoptimization_enabled_ because currently there isn't really any support for
+// multiple users of instrumentation. Since this is just a temporary state anyway pending work to
+// ensure that the current_method doesn't get kept across suspend points this should be okay.
+// TODO Remove once b/33630159 is resolved.
+void Instrumentation::ReJitEverything(const char* key) {
+  ConfigureStubs(key, InstrumentationLevel::kInstrumentWithInterpreterAndJit);
+}
+
 void Instrumentation::DeoptimizeEverything(const char* key) {
   CHECK(deoptimization_enabled_);
   ConfigureStubs(key, InstrumentationLevel::kInstrumentWithInterpreter);
@@ -1098,10 +1116,14 @@
                     Dbg::IsForcedInterpreterNeededForUpcall(self, visitor.caller));
   if (deoptimize && Runtime::Current()->IsDeoptimizeable(*return_pc)) {
     if (kVerboseInstrumentation) {
-      LOG(INFO) << StringPrintf("Deoptimizing %s by returning from %s with result %#" PRIx64 " in ",
-                                visitor.caller->PrettyMethod().c_str(),
-                                method->PrettyMethod().c_str(),
-                                return_value.GetJ()) << *self;
+      LOG(INFO) << "Deoptimizing "
+                << visitor.caller->PrettyMethod()
+                << " by returning from "
+                << method->PrettyMethod()
+                << " with result "
+                << std::hex << return_value.GetJ() << std::dec
+                << " in "
+                << *self;
     }
     self->PushDeoptimizationContext(return_value,
                                     return_shorty == 'L',
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 1e5fcf2..05c0aaa 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -133,6 +133,9 @@
   enum class InstrumentationLevel {
     kInstrumentNothing,                   // execute without instrumentation
     kInstrumentWithInstrumentationStubs,  // execute with instrumentation entry/exit stubs
+    kInstrumentWithInterpreterAndJit,     // execute with interpreter initially and later the JIT
+                                          // (if it is enabled). This level is special in that it
+                                          // always requires re-instrumentation.
     kInstrumentWithInterpreter            // execute with interpreter
   };
 
@@ -163,6 +166,13 @@
   }
   bool ShouldNotifyMethodEnterExitEvents() const REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Executes everything with the interpreter/jit (if available).
+  void ReJitEverything(const char* key)
+      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
+      REQUIRES(!Locks::thread_list_lock_,
+               !Locks::classlinker_classes_lock_,
+               !deoptimized_methods_lock_);
+
   // Executes everything with interpreter.
   void DeoptimizeEverything(const char* key)
       REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
@@ -432,9 +442,13 @@
     return alloc_entrypoints_instrumented_;
   }
 
- private:
   InstrumentationLevel GetCurrentInstrumentationLevel() const;
 
+ private:
+  // Returns true if moving to the given instrumentation level requires the installation of stubs.
+  // False otherwise.
+  bool RequiresInstrumentationInstallation(InstrumentationLevel new_level) const;
+
   // Does the job of installing or removing instrumentation code within methods.
   // In order to support multiple clients using instrumentation at the same time,
   // the caller must pass a unique key (a string) identifying it so we remind which
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 9c05d3c..3e19146 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -319,7 +319,9 @@
   if (kIsDebugBuild) {
     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
   }
-  return static_cast<size_t>(root.Read<kWithoutReadBarrier>()->GetHashCode());
+  // An additional cast to prevent undesired sign extension.
+  return static_cast<size_t>(
+      static_cast<uint32_t>(root.Read<kWithoutReadBarrier>()->GetHashCode()));
 }
 
 bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index f661d9f..68454fb 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -163,7 +163,11 @@
         NO_THREAD_SAFETY_ANALYSIS;
 
     // Utf8String can be used for lookup.
-    std::size_t operator()(const Utf8String& key) const { return key.GetHash(); }
+    std::size_t operator()(const Utf8String& key) const {
+      // A cast to prevent undesired sign extension.
+      return static_cast<uint32_t>(key.GetHash());
+    }
+
     bool operator()(const GcRoot<mirror::String>& a, const Utf8String& b) const
         NO_THREAD_SAFETY_ANALYSIS;
   };
@@ -217,6 +221,8 @@
     // We call AddNewTable when we create the zygote to reduce private dirty pages caused by
     // modifying the zygote intern table. The back of table is modified when strings are interned.
     std::vector<UnorderedSet> tables_;
+
+    ART_FRIEND_TEST(InternTableTest, CrossHash);
   };
 
   // Insert if non null, otherwise return null. Must be called holding the mutator lock.
@@ -276,6 +282,7 @@
   gc::WeakRootState weak_root_state_ GUARDED_BY(Locks::intern_table_lock_);
 
   friend class Transaction;
+  ART_FRIEND_TEST(InternTableTest, CrossHash);
   DISALLOW_COPY_AND_ASSIGN(InternTable);
 };
 
diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc
index b91d946..3991d65 100644
--- a/runtime/intern_table_test.cc
+++ b/runtime/intern_table_test.cc
@@ -16,6 +16,7 @@
 
 #include "intern_table.h"
 
+#include "base/hash_set.h"
 #include "common_runtime_test.h"
 #include "mirror/object.h"
 #include "handle_scope-inl.h"
@@ -62,6 +63,25 @@
   EXPECT_EQ(2U, t.Size());
 }
 
+// Check if table indexes match on 64 and 32 bit machines.
+// This is done by ensuring hash values are the same on every machine and limited to 32-bit wide.
+// Otherwise cross compilation can cause a table to be filled on host using one indexing algorithm
+// and later on a device with different sizeof(size_t) can use another indexing algorithm.
+// Thus the table may provide wrong data.
+TEST_F(InternTableTest, CrossHash) {
+  ScopedObjectAccess soa(Thread::Current());
+  InternTable t;
+
+  // A string that has a negative hash value.
+  GcRoot<mirror::String> str(mirror::String::AllocFromModifiedUtf8(soa.Self(), "00000000"));
+
+  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
+  for (InternTable::Table::UnorderedSet& table : t.strong_interns_.tables_) {
+    // The negative hash value shall be 32-bit wide on every host.
+    ASSERT_TRUE(IsUint<32>(table.hashfn_(str)));
+  }
+}
+
 class TestPredicate : public IsMarkedVisitor {
  public:
   mirror::Object* IsMarked(mirror::Object* s) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 22da07d..a09e71b 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -676,7 +676,7 @@
   CHECK(Runtime::Current()->IsActiveTransaction());
   // Constructs abort message.
   std::string abort_msg;
-  StringAppendV(&abort_msg, fmt, args);
+  android::base::StringAppendV(&abort_msg, fmt, args);
   // Throws an exception so we can abort the transaction and rollback every change.
   Runtime::Current()->AbortTransactionAndThrowAbortError(self, abort_msg);
 }
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index c9a5b44..b599949 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -25,6 +25,8 @@
 #include <sstream>
 #include <atomic>
 
+#include "android-base/stringprintf.h"
+
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "base/enums.h"
@@ -249,17 +251,16 @@
     }
   }
   ArtMethod* method = shadow_frame.GetMethod();
-  ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass();
   // MethodVerifier refuses methods with string_idx out of bounds.
   DCHECK_LT(string_idx.index_ % mirror::DexCache::kDexCacheStringCacheSize,
-            declaring_class->GetDexFile().NumStringIds());
+            method->GetDexFile()->NumStringIds());
   ObjPtr<mirror::String> string_ptr =
-      mirror::StringDexCachePair::Lookup(declaring_class->GetDexCacheStrings(),
+      mirror::StringDexCachePair::Lookup(method->GetDexCache()->GetStrings(),
                                          string_idx.index_,
                                          mirror::DexCache::kDexCacheStringCacheSize).Read();
   if (UNLIKELY(string_ptr == nullptr)) {
     StackHandleScope<1> hs(self);
-    Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
+    Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
     string_ptr = Runtime::Current()->GetClassLinker()->ResolveString(*method->GetDexFile(),
                                                                      string_idx,
                                                                      dex_cache);
@@ -430,12 +431,12 @@
 #define TRACE_LOG std::cerr
     std::ostringstream oss;
     oss << shadow_frame.GetMethod()->PrettyMethod()
-        << StringPrintf("\n0x%x: ", dex_pc)
+        << android::base::StringPrintf("\n0x%x: ", dex_pc)
         << inst->DumpString(shadow_frame.GetMethod()->GetDexFile()) << "\n";
     for (uint32_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) {
       uint32_t raw_value = shadow_frame.GetVReg(i);
       ObjPtr<mirror::Object> ref_value = shadow_frame.GetVRegReference(i);
-      oss << StringPrintf(" vreg%u=0x%08X", i, raw_value);
+      oss << android::base::StringPrintf(" vreg%u=0x%08X", i, raw_value);
       if (ref_value != nullptr) {
         if (ref_value->GetClass()->IsStringClass() &&
             !ref_value->AsString()->IsValueNull()) {
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 52eacd5..b0d7fb2 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -92,6 +92,16 @@
     }                                                                                          \
   } while (false)
 
+#define HANDLE_BACKWARD_BRANCH(offset)                                                         \
+  do {                                                                                         \
+    if (IsBackwardBranch(offset)) {                                                            \
+      HOTNESS_UPDATE();                                                                        \
+      /* Record new dex pc early to have consistent suspend point at loop header. */           \
+      shadow_frame.SetDexPC(inst->GetDexPc(insns));                                            \
+      self->AllowThreadSuspension();                                                           \
+    }                                                                                          \
+  } while (false)
+
 template<bool do_access_check, bool transaction_active>
 JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
                          ShadowFrame& shadow_frame, JValue result_register,
@@ -594,55 +604,40 @@
         PREAMBLE();
         int8_t offset = inst->VRegA_10t(inst_data);
         BRANCH_INSTRUMENTATION(offset);
-        if (IsBackwardBranch(offset)) {
-          HOTNESS_UPDATE();
-          self->AllowThreadSuspension();
-        }
         inst = inst->RelativeAt(offset);
+        HANDLE_BACKWARD_BRANCH(offset);
         break;
       }
       case Instruction::GOTO_16: {
         PREAMBLE();
         int16_t offset = inst->VRegA_20t();
         BRANCH_INSTRUMENTATION(offset);
-        if (IsBackwardBranch(offset)) {
-          HOTNESS_UPDATE();
-          self->AllowThreadSuspension();
-        }
         inst = inst->RelativeAt(offset);
+        HANDLE_BACKWARD_BRANCH(offset);
         break;
       }
       case Instruction::GOTO_32: {
         PREAMBLE();
         int32_t offset = inst->VRegA_30t();
         BRANCH_INSTRUMENTATION(offset);
-        if (IsBackwardBranch(offset)) {
-          HOTNESS_UPDATE();
-          self->AllowThreadSuspension();
-        }
         inst = inst->RelativeAt(offset);
+        HANDLE_BACKWARD_BRANCH(offset);
         break;
       }
       case Instruction::PACKED_SWITCH: {
         PREAMBLE();
         int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
         BRANCH_INSTRUMENTATION(offset);
-        if (IsBackwardBranch(offset)) {
-          HOTNESS_UPDATE();
-          self->AllowThreadSuspension();
-        }
         inst = inst->RelativeAt(offset);
+        HANDLE_BACKWARD_BRANCH(offset);
         break;
       }
       case Instruction::SPARSE_SWITCH: {
         PREAMBLE();
         int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
         BRANCH_INSTRUMENTATION(offset);
-        if (IsBackwardBranch(offset)) {
-          HOTNESS_UPDATE();
-          self->AllowThreadSuspension();
-        }
         inst = inst->RelativeAt(offset);
+        HANDLE_BACKWARD_BRANCH(offset);
         break;
       }
 
@@ -739,11 +734,8 @@
             shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
           int16_t offset = inst->VRegC_22t();
           BRANCH_INSTRUMENTATION(offset);
-          if (IsBackwardBranch(offset)) {
-            HOTNESS_UPDATE();
-            self->AllowThreadSuspension();
-          }
           inst = inst->RelativeAt(offset);
+          HANDLE_BACKWARD_BRANCH(offset);
         } else {
           BRANCH_INSTRUMENTATION(2);
           inst = inst->Next_2xx();
@@ -756,11 +748,8 @@
             shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
           int16_t offset = inst->VRegC_22t();
           BRANCH_INSTRUMENTATION(offset);
-          if (IsBackwardBranch(offset)) {
-            HOTNESS_UPDATE();
-            self->AllowThreadSuspension();
-          }
           inst = inst->RelativeAt(offset);
+          HANDLE_BACKWARD_BRANCH(offset);
         } else {
           BRANCH_INSTRUMENTATION(2);
           inst = inst->Next_2xx();
@@ -773,11 +762,8 @@
             shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
           int16_t offset = inst->VRegC_22t();
           BRANCH_INSTRUMENTATION(offset);
-          if (IsBackwardBranch(offset)) {
-            HOTNESS_UPDATE();
-            self->AllowThreadSuspension();
-          }
           inst = inst->RelativeAt(offset);
+          HANDLE_BACKWARD_BRANCH(offset);
         } else {
           BRANCH_INSTRUMENTATION(2);
           inst = inst->Next_2xx();
@@ -790,11 +776,8 @@
             shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
           int16_t offset = inst->VRegC_22t();
           BRANCH_INSTRUMENTATION(offset);
-          if (IsBackwardBranch(offset)) {
-            HOTNESS_UPDATE();
-            self->AllowThreadSuspension();
-          }
           inst = inst->RelativeAt(offset);
+          HANDLE_BACKWARD_BRANCH(offset);
         } else {
           BRANCH_INSTRUMENTATION(2);
           inst = inst->Next_2xx();
@@ -807,11 +790,8 @@
         shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
           int16_t offset = inst->VRegC_22t();
           BRANCH_INSTRUMENTATION(offset);
-          if (IsBackwardBranch(offset)) {
-            HOTNESS_UPDATE();
-            self->AllowThreadSuspension();
-          }
           inst = inst->RelativeAt(offset);
+          HANDLE_BACKWARD_BRANCH(offset);
         } else {
           BRANCH_INSTRUMENTATION(2);
           inst = inst->Next_2xx();
@@ -824,11 +804,8 @@
             shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
           int16_t offset = inst->VRegC_22t();
           BRANCH_INSTRUMENTATION(offset);
-          if (IsBackwardBranch(offset)) {
-            HOTNESS_UPDATE();
-            self->AllowThreadSuspension();
-          }
           inst = inst->RelativeAt(offset);
+          HANDLE_BACKWARD_BRANCH(offset);
         } else {
           BRANCH_INSTRUMENTATION(2);
           inst = inst->Next_2xx();
@@ -840,11 +817,8 @@
         if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) {
           int16_t offset = inst->VRegB_21t();
           BRANCH_INSTRUMENTATION(offset);
-          if (IsBackwardBranch(offset)) {
-            HOTNESS_UPDATE();
-            self->AllowThreadSuspension();
-          }
           inst = inst->RelativeAt(offset);
+          HANDLE_BACKWARD_BRANCH(offset);
         } else {
           BRANCH_INSTRUMENTATION(2);
           inst = inst->Next_2xx();
@@ -856,11 +830,8 @@
         if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) {
           int16_t offset = inst->VRegB_21t();
           BRANCH_INSTRUMENTATION(offset);
-          if (IsBackwardBranch(offset)) {
-            HOTNESS_UPDATE();
-            self->AllowThreadSuspension();
-          }
           inst = inst->RelativeAt(offset);
+          HANDLE_BACKWARD_BRANCH(offset);
         } else {
           BRANCH_INSTRUMENTATION(2);
           inst = inst->Next_2xx();
@@ -872,11 +843,8 @@
         if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) {
           int16_t offset = inst->VRegB_21t();
           BRANCH_INSTRUMENTATION(offset);
-          if (IsBackwardBranch(offset)) {
-            HOTNESS_UPDATE();
-            self->AllowThreadSuspension();
-          }
           inst = inst->RelativeAt(offset);
+          HANDLE_BACKWARD_BRANCH(offset);
         } else {
           BRANCH_INSTRUMENTATION(2);
           inst = inst->Next_2xx();
@@ -888,11 +856,8 @@
         if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) {
           int16_t offset = inst->VRegB_21t();
           BRANCH_INSTRUMENTATION(offset);
-          if (IsBackwardBranch(offset)) {
-            HOTNESS_UPDATE();
-            self->AllowThreadSuspension();
-          }
           inst = inst->RelativeAt(offset);
+          HANDLE_BACKWARD_BRANCH(offset);
         } else {
           BRANCH_INSTRUMENTATION(2);
           inst = inst->Next_2xx();
@@ -904,11 +869,8 @@
         if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) {
           int16_t offset = inst->VRegB_21t();
           BRANCH_INSTRUMENTATION(offset);
-          if (IsBackwardBranch(offset)) {
-            HOTNESS_UPDATE();
-            self->AllowThreadSuspension();
-          }
           inst = inst->RelativeAt(offset);
+          HANDLE_BACKWARD_BRANCH(offset);
         } else {
           BRANCH_INSTRUMENTATION(2);
           inst = inst->Next_2xx();
@@ -920,11 +882,8 @@
         if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) {
           int16_t offset = inst->VRegB_21t();
           BRANCH_INSTRUMENTATION(offset);
-          if (IsBackwardBranch(offset)) {
-            HOTNESS_UPDATE();
-            self->AllowThreadSuspension();
-          }
           inst = inst->RelativeAt(offset);
+          HANDLE_BACKWARD_BRANCH(offset);
         } else {
           BRANCH_INSTRUMENTATION(2);
           inst = inst->Next_2xx();
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index a5b1038..7dd3d3d 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -25,6 +25,7 @@
 #include <locale>
 #include <unordered_map>
 
+#include "android-base/stringprintf.h"
 #include "ScopedLocalRef.h"
 
 #include "art_method-inl.h"
@@ -56,6 +57,9 @@
 namespace art {
 namespace interpreter {
 
+using android::base::StringAppendV;
+using android::base::StringPrintf;
+
 static void AbortTransactionOrFail(Thread* self, const char* fmt, ...)
     __attribute__((__format__(__printf__, 2, 3)))
     REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index caf705a..f80c43d 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -18,6 +18,8 @@
 
 #include <dlfcn.h>
 
+#include "android-base/stringprintf.h"
+
 #include "art_method.h"
 #include "base/dumpable.h"
 #include "base/mutex.h"
@@ -42,6 +44,9 @@
 
 namespace art {
 
+using android::base::StringAppendF;
+using android::base::StringAppendV;
+
 static constexpr size_t kGlobalsMax = 51200;  // Arbitrary sanity check. (Must fit in 16 bits.)
 
 static constexpr size_t kWeakGlobalsMax = 51200;  // Arbitrary sanity check. (Must fit in 16 bits.)
diff --git a/runtime/jdwp/jdwp_adb.cc b/runtime/jdwp/jdwp_adb.cc
index 16e8050..d8869ad 100644
--- a/runtime/jdwp/jdwp_adb.cc
+++ b/runtime/jdwp/jdwp_adb.cc
@@ -20,8 +20,9 @@
 #include <sys/un.h>
 #include <unistd.h>
 
+#include "android-base/stringprintf.h"
+
 #include "base/logging.h"
-#include "base/stringprintf.h"
 #include "jdwp/jdwp_priv.h"
 #include "thread-inl.h"
 
@@ -53,6 +54,8 @@
 
 namespace JDWP {
 
+using android::base::StringPrintf;
+
 struct JdwpAdbState : public JdwpNetStateBase {
  public:
   explicit JdwpAdbState(JdwpState* state)
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index 5574a11..172f52a 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -21,10 +21,11 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "android-base/stringprintf.h"
+
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "base/logging.h"
-#include "base/stringprintf.h"
 #include "debugger.h"
 #include "jdwp/jdwp_constants.h"
 #include "jdwp/jdwp_expand_buf.h"
@@ -103,6 +104,8 @@
 
 namespace JDWP {
 
+using android::base::StringPrintf;
+
 /*
  * Stuff to compare against when deciding if a mod matches.  Only the
  * values for mods valid for the event being evaluated will be filled in.
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 0f2d188..964c567 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -20,11 +20,12 @@
 #include <memory>
 #include <string>
 
+#include "android-base/stringprintf.h"
+
 #include "atomic.h"
 #include "base/hex_dump.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/stringprintf.h"
 #include "debugger.h"
 #include "jdwp/jdwp_constants.h"
 #include "jdwp/jdwp_event.h"
@@ -39,6 +40,8 @@
 
 namespace JDWP {
 
+using android::base::StringPrintf;
+
 std::string DescribeField(const FieldId& field_id) {
   return StringPrintf("%#" PRIx64 " (%s)", field_id, Dbg::GetFieldName(field_id).c_str());
 }
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index e3bf3e5..7707ba4 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -20,6 +20,8 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "android-base/stringprintf.h"
+
 #include "atomic.h"
 #include "base/logging.h"
 #include "base/time_utils.h"
@@ -31,6 +33,8 @@
 
 namespace JDWP {
 
+using android::base::StringPrintf;
+
 static void* StartJdwpThread(void* arg);
 
 /*
diff --git a/runtime/jdwp/jdwp_request.cc b/runtime/jdwp/jdwp_request.cc
index 18f40a1..6af267e 100644
--- a/runtime/jdwp/jdwp_request.cc
+++ b/runtime/jdwp/jdwp_request.cc
@@ -18,7 +18,8 @@
 
 #include <inttypes.h>
 
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
 #include "jdwp/jdwp_priv.h"
 
 namespace art {
@@ -100,7 +101,7 @@
 
 ObjectId Request::ReadObjectId(const char* specific_kind) {
   ObjectId id = Read8BE();
-  VLOG(jdwp) << StringPrintf("    %s id %#" PRIx64, specific_kind, id);
+  VLOG(jdwp) << android::base::StringPrintf("    %s id %#" PRIx64, specific_kind, id);
   return id;
 }
 
diff --git a/runtime/jdwp/jdwp_socket.cc b/runtime/jdwp/jdwp_socket.cc
index 3be7fd6..97662f0 100644
--- a/runtime/jdwp/jdwp_socket.cc
+++ b/runtime/jdwp/jdwp_socket.cc
@@ -26,8 +26,9 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include "android-base/stringprintf.h"
+
 #include "base/logging.h"
-#include "base/stringprintf.h"
 #include "jdwp/jdwp_priv.h"
 
 namespace art {
@@ -500,7 +501,7 @@
    */
   if (IsAwaitingHandshake()) {
     if (memcmp(input_buffer_, kMagicHandshake, kMagicHandshakeLen) != 0) {
-      LOG(ERROR) << StringPrintf("ERROR: bad handshake '%.14s'", input_buffer_);
+      LOG(ERROR) << android::base::StringPrintf("ERROR: bad handshake '%.14s'", input_buffer_);
       goto fail;
     }
 
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index dac2e60..b7125a8 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -291,7 +291,7 @@
   // is not null when we instrument.
   thread_pool_.reset(new ThreadPool("Jit thread pool", 1));
   thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority);
-  thread_pool_->StartWorkers(Thread::Current());
+  Start();
 }
 
 void Jit::DeleteThreadPool() {
@@ -710,14 +710,23 @@
   }
 }
 
+void Jit::Stop() {
+  Thread* self = Thread::Current();
+  // TODO(ngeoffray): change API to not require calling WaitForCompilationToFinish twice.
+  WaitForCompilationToFinish(self);
+  GetThreadPool()->StopWorkers(self);
+  WaitForCompilationToFinish(self);
+}
+
+void Jit::Start() {
+  GetThreadPool()->StartWorkers(Thread::Current());
+}
+
 ScopedJitSuspend::ScopedJitSuspend() {
   jit::Jit* jit = Runtime::Current()->GetJit();
   was_on_ = (jit != nullptr) && (jit->GetThreadPool() != nullptr);
   if (was_on_) {
-    Thread* self = Thread::Current();
-    jit->WaitForCompilationToFinish(self);
-    jit->GetThreadPool()->StopWorkers(self);
-    jit->WaitForCompilationToFinish(self);
+    jit->Stop();
   }
 }
 
@@ -725,7 +734,7 @@
   if (was_on_) {
     DCHECK(Runtime::Current()->GetJit() != nullptr);
     DCHECK(Runtime::Current()->GetJit()->GetThreadPool() != nullptr);
-    Runtime::Current()->GetJit()->GetThreadPool()->StartWorkers(Thread::Current());
+    Runtime::Current()->GetJit()->Start();
   }
 }
 
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index a230c78..05c3905 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -179,6 +179,12 @@
     return thread_pool_.get();
   }
 
+  // Stop the JIT by waiting for all current compilations and enqueued compilations to finish.
+  void Stop();
+
+  // Start JIT threads.
+  void Start();
+
  private:
   Jit();
 
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 1b0ad83..6336cdd 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -217,6 +217,7 @@
                                   size_t fp_spill_mask,
                                   const uint8_t* code,
                                   size_t code_size,
+                                  size_t data_size,
                                   bool osr,
                                   Handle<mirror::ObjectArray<mirror::Object>> roots,
                                   bool has_should_deoptimize_flag,
@@ -230,6 +231,7 @@
                                        fp_spill_mask,
                                        code,
                                        code_size,
+                                       data_size,
                                        osr,
                                        roots,
                                        has_should_deoptimize_flag,
@@ -246,6 +248,7 @@
                                 fp_spill_mask,
                                 code,
                                 code_size,
+                                data_size,
                                 osr,
                                 roots,
                                 has_should_deoptimize_flag,
@@ -513,6 +516,7 @@
                                           size_t fp_spill_mask,
                                           const uint8_t* code,
                                           size_t code_size,
+                                          size_t data_size,
                                           bool osr,
                                           Handle<mirror::ObjectArray<mirror::Object>> roots,
                                           bool has_should_deoptimize_flag,
@@ -547,6 +551,11 @@
           core_spill_mask,
           fp_spill_mask,
           code_size);
+      DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data);
+      DCHECK_LE(roots_data, stack_map);
+      // Flush data cache, as compiled code references literals in it.
+      FlushDataCache(reinterpret_cast<char*>(roots_data),
+                     reinterpret_cast<char*>(roots_data + data_size));
       // Flush caches before we remove write permission because on some ARMv8 hardware,
       // flushing caches require write permissions.
       //
@@ -585,6 +594,9 @@
       VLOG(jit) << "JIT discarded jitted code due to invalid single-implementation assumptions.";
       return nullptr;
     }
+    DCHECK(cha_single_implementation_list.empty() || !Runtime::Current()->IsDebuggable())
+        << "Should not be using cha on debuggable apps/runs!";
+
     for (ArtMethod* single_impl : cha_single_implementation_list) {
       Runtime::Current()->GetClassHierarchyAnalysis()->AddDependency(
           single_impl, method, method_header);
@@ -636,6 +648,69 @@
   return CodeCacheSizeLocked();
 }
 
+// This notifies the code cache that the given method has been redefined and that it should remove
+// any cached information it has on the method. All threads must be suspended before calling this
+// method. The compiled code for the method (if there is any) must not be in any threads call stack.
+void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) {
+  MutexLock mu(Thread::Current(), lock_);
+  if (method->IsNative()) {
+    return;
+  }
+  ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize);
+  if (info != nullptr) {
+    auto profile = std::find(profiling_infos_.begin(), profiling_infos_.end(), info);
+    DCHECK(profile != profiling_infos_.end());
+    profiling_infos_.erase(profile);
+  }
+  method->SetProfilingInfo(nullptr);
+  ScopedCodeCacheWrite ccw(code_map_.get());
+  for (auto code_iter = method_code_map_.begin();
+       code_iter != method_code_map_.end();
+       ++code_iter) {
+    if (code_iter->second == method) {
+      FreeCode(code_iter->first);
+      method_code_map_.erase(code_iter);
+    }
+  }
+  auto code_map = osr_code_map_.find(method);
+  if (code_map != osr_code_map_.end()) {
+    osr_code_map_.erase(code_map);
+  }
+}
+
+// This invalidates old_method. Once this function returns one can no longer use old_method to
+// execute code unless it is fixed up. This fixup will happen later in the process of installing a
+// class redefinition.
+// TODO We should add some info to ArtMethod to note that 'old_method' has been invalidated and
+// shouldn't be used since it is no longer logically in the jit code cache.
+// TODO We should add DCHECKS that validate that the JIT is paused when this method is entered.
+void JitCodeCache::MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) {
+  MutexLock mu(Thread::Current(), lock_);
+  // Update ProfilingInfo to the new one and remove it from the old_method.
+  if (old_method->GetProfilingInfo(kRuntimePointerSize) != nullptr) {
+    DCHECK_EQ(old_method->GetProfilingInfo(kRuntimePointerSize)->GetMethod(), old_method);
+    ProfilingInfo* info = old_method->GetProfilingInfo(kRuntimePointerSize);
+    old_method->SetProfilingInfo(nullptr);
+    // Since the JIT should be paused and all threads suspended by the time this is called these
+    // checks should always pass.
+    DCHECK(!info->IsInUseByCompiler());
+    new_method->SetProfilingInfo(info);
+    info->method_ = new_method;
+  }
+  // Update method_code_map_ to point to the new method.
+  for (auto& it : method_code_map_) {
+    if (it.second == old_method) {
+      it.second = new_method;
+    }
+  }
+  // Update osr_code_map_ to point to the new method.
+  auto code_map = osr_code_map_.find(old_method);
+  if (code_map != osr_code_map_.end()) {
+    osr_code_map_.Put(new_method, code_map->second);
+    osr_code_map_.erase(old_method);
+  }
+}
+
 size_t JitCodeCache::CodeCacheSizeLocked() {
   return used_memory_for_code_;
 }
@@ -657,12 +732,12 @@
   FreeData(reinterpret_cast<uint8_t*>(roots_data));
 }
 
-void JitCodeCache::ReserveData(Thread* self,
-                               size_t stack_map_size,
-                               size_t number_of_roots,
-                               ArtMethod* method,
-                               uint8_t** stack_map_data,
-                               uint8_t** roots_data) {
+size_t JitCodeCache::ReserveData(Thread* self,
+                                 size_t stack_map_size,
+                                 size_t number_of_roots,
+                                 ArtMethod* method,
+                                 uint8_t** stack_map_data,
+                                 uint8_t** roots_data) {
   size_t table_size = ComputeRootTableSize(number_of_roots);
   size_t size = RoundUp(stack_map_size + table_size, sizeof(void*));
   uint8_t* result = nullptr;
@@ -695,9 +770,11 @@
     *roots_data = result;
     *stack_map_data = result + table_size;
     FillRootTableLength(*roots_data, number_of_roots);
+    return size;
   } else {
     *roots_data = nullptr;
     *stack_map_data = nullptr;
+    return 0;
   }
 }
 
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 30e2efb..b5e3176 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -75,6 +75,10 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!lock_);
 
+  void NotifyMethodRedefined(ArtMethod* method)
+      REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!lock_);
+
   // Notify to the code cache that the compiler wants to use the
   // profiling info of `method` to drive optimizations,
   // and therefore ensure the returned profiling info object is not
@@ -106,6 +110,7 @@
                       size_t fp_spill_mask,
                       const uint8_t* code,
                       size_t code_size,
+                      size_t data_size,
                       bool osr,
                       Handle<mirror::ObjectArray<mirror::Object>> roots,
                       bool has_should_deoptimize_flag,
@@ -121,12 +126,13 @@
 
   // Allocate a region of data that contain `size` bytes, and potentially space
   // for storing `number_of_roots` roots. Returns null if there is no more room.
-  void ReserveData(Thread* self,
-                   size_t size,
-                   size_t number_of_roots,
-                   ArtMethod* method,
-                   uint8_t** stack_map_data,
-                   uint8_t** roots_data)
+  // Return the number of bytes allocated.
+  size_t ReserveData(Thread* self,
+                     size_t size,
+                     size_t number_of_roots,
+                     ArtMethod* method,
+                     uint8_t** stack_map_data,
+                     uint8_t** roots_data)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!lock_);
 
@@ -217,6 +223,11 @@
   void DisallowInlineCacheAccess() REQUIRES(!lock_);
   void BroadcastForInlineCacheAccess() REQUIRES(!lock_);
 
+  // Notify the code cache that the method at the pointer 'old_method' is being moved to the pointer
+  // 'new_method' since it is being made obsolete.
+  void MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method)
+      REQUIRES(!lock_) REQUIRES(Locks::mutator_lock_);
+
  private:
   // Take ownership of maps.
   JitCodeCache(MemMap* code_map,
@@ -237,6 +248,7 @@
                               size_t fp_spill_mask,
                               const uint8_t* code,
                               size_t code_size,
+                              size_t data_size,
                               bool osr,
                               Handle<mirror::ObjectArray<mirror::Object>> roots,
                               bool has_should_deoptimize_flag,
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
index 9902bb5..9fbf2e3 100644
--- a/runtime/jit/profiling_info.h
+++ b/runtime/jit/profiling_info.h
@@ -128,7 +128,9 @@
   const uint32_t number_of_inline_caches_;
 
   // Method this profiling info is for.
-  ArtMethod* const method_;
+  // Not 'const' as JVMTI introduces obsolete methods that we implement by creating new ArtMethods.
+  // See JitCodeCache::MoveObsoleteMethod.
+  ArtMethod* method_;
 
   // Whether the ArtMethod is currently being compiled. This flag
   // is implicitly guarded by the JIT code cache lock.
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index 342e0d2..5a3fafa 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -19,6 +19,8 @@
 #include <algorithm>
 #include <vector>
 
+#include "android-base/stringprintf.h"
+
 #include "check_jni.h"
 #include "indirect_reference_table.h"
 #include "java_vm_ext.h"
@@ -30,6 +32,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static constexpr size_t kMonitorsInitial = 32;  // Arbitrary.
 static constexpr size_t kMonitorsMax = 4096;  // Arbitrary sanity check.
 
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index a421c34..4da5e23 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -16,6 +16,8 @@
 
 #include "jni_internal.h"
 
+#include "android-base/stringprintf.h"
+
 #include "art_method-inl.h"
 #include "common_compiler_test.h"
 #include "indirect_reference_table.h"
@@ -27,6 +29,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 // TODO: Convert to CommonRuntimeTest. Currently MakeExecutable is used.
 class JniInternalTest : public CommonCompilerTest {
  protected:
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 6da72e4..19a65bb 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -24,7 +24,8 @@
 #include <memory>
 #include <sstream>
 
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
 #include "base/unix_file/fd_file.h"
 #include "os.h"
 #include "thread-inl.h"
@@ -42,6 +43,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static std::ostream& operator<<(
     std::ostream& os,
     std::pair<BacktraceMap::const_iterator, BacktraceMap::const_iterator> iters) {
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 3c22d7f..da510ce 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-#include "method_handles.h"
-
 #include "method_handles-inl.h"
+
+#include "android-base/stringprintf.h"
+
 #include "jvalue.h"
 #include "jvalue-inl.h"
 #include "reflection.h"
@@ -25,6 +26,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 namespace {
 
 #define PRIMITIVES_LIST(V) \
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 7d7c1d7..a5db0c0 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -19,10 +19,11 @@
 
 #include "array.h"
 
+#include "android-base/stringprintf.h"
+
 #include "base/bit_utils.h"
 #include "base/casts.h"
 #include "base/logging.h"
-#include "base/stringprintf.h"
 #include "class-inl.h"
 #include "gc/heap-inl.h"
 #include "obj_ptr-inl.h"
@@ -167,9 +168,9 @@
 #else
   // 32-bit.
   if (UNLIKELY(size == 0)) {
-    self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
-                                             array_class->PrettyDescriptor().c_str(),
-                                             component_count).c_str());
+    self->ThrowOutOfMemoryError(android::base::StringPrintf("%s of length %d would overflow",
+                                                            array_class->PrettyDescriptor().c_str(),
+                                                            component_count).c_str());
     return nullptr;
   }
 #endif
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index 8afa4aa..cc548b9 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "array.h"
+#include "array-inl.h"
 
 #include "class.h"
 #include "class-inl.h"
@@ -23,7 +23,6 @@
 #include "dex_file-inl.h"
 #include "gc/accounting/card_table-inl.h"
 #include "object-inl.h"
-#include "object_array.h"
 #include "object_array-inl.h"
 #include "handle_scope-inl.h"
 #include "thread.h"
@@ -32,6 +31,8 @@
 namespace art {
 namespace mirror {
 
+using android::base::StringPrintf;
+
 // Create a multi-dimensional array of Objects or primitive types.
 //
 // We have to generate the names for X[], X[][], X[][][], and so on.  The
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 5fdf8f3..2fb8d28 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -937,14 +937,6 @@
   }
 }
 
-inline void Class::SetDexCacheStrings(StringDexCacheType* new_dex_cache_strings) {
-  SetFieldPtr<false>(DexCacheStringsOffset(), new_dex_cache_strings);
-}
-
-inline StringDexCacheType* Class::GetDexCacheStrings() {
-  return GetFieldPtr64<StringDexCacheType*>(DexCacheStringsOffset());
-}
-
 template<ReadBarrierOption kReadBarrierOption, class Visitor>
 void Class::VisitNativeRoots(Visitor& visitor, PointerSize pointer_size) {
   for (ArtField& field : GetSFieldsUnchecked()) {
@@ -1095,12 +1087,6 @@
   if (methods != new_methods) {
     dest->SetMethodsPtrInternal(new_methods);
   }
-  // Update dex cache strings.
-  StringDexCacheType* strings = GetDexCacheStrings();
-  StringDexCacheType* new_strings = visitor(strings);
-  if (strings != new_strings) {
-    dest->SetDexCacheStrings(new_strings);
-  }
   // Fix up embedded tables.
   if (!IsTemp() && ShouldHaveEmbeddedVTable<kVerifyNone, kReadBarrierOption>()) {
     for (int32_t i = 0, count = GetEmbeddedVTableLength(); i < count; ++i) {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index a862c97..9964b73 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -16,6 +16,8 @@
 
 #include "class.h"
 
+#include "android-base/stringprintf.h"
+
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "class_ext.h"
@@ -40,6 +42,8 @@
 namespace art {
 namespace mirror {
 
+using android::base::StringPrintf;
+
 GcRoot<Class> Class::java_lang_Class_;
 
 void Class::SetClassClass(ObjPtr<Class> java_lang_Class) {
@@ -187,7 +191,6 @@
 
 void Class::SetDexCache(ObjPtr<DexCache> new_dex_cache) {
   SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache);
-  SetDexCacheStrings(new_dex_cache != nullptr ? new_dex_cache->GetStrings() : nullptr);
 }
 
 void Class::SetClassSize(uint32_t new_class_size) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index d7449c8..fb2792a 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1247,13 +1247,6 @@
   bool GetSlowPathEnabled() REQUIRES_SHARED(Locks::mutator_lock_);
   void SetSlowPath(bool enabled) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  StringDexCacheType* GetDexCacheStrings() REQUIRES_SHARED(Locks::mutator_lock_);
-  void SetDexCacheStrings(StringDexCacheType* new_dex_cache_strings)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-  static MemberOffset DexCacheStringsOffset() {
-    return OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_strings_);
-  }
-
   // May cause thread suspension due to EqualParameters.
   ArtMethod* GetDeclaredConstructor(Thread* self,
                                     Handle<ObjectArray<Class>> args,
@@ -1438,9 +1431,6 @@
   // virtual_ methods_ for miranda methods.
   HeapReference<PointerArray> vtable_;
 
-  // Short cuts to dex_cache_ member for fast compiled code access.
-  uint64_t dex_cache_strings_;
-
   // instance fields
   //
   // These describe the layout of the contents of an Object.
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index ec265e5..6f88cc5 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -19,7 +19,6 @@
 
 #include "array.h"
 #include "art_field.h"
-#include "art_method.h"
 #include "class.h"
 #include "dex_file_types.h"
 #include "object.h"
@@ -27,6 +26,7 @@
 
 namespace art {
 
+class ArtMethod;
 struct DexCacheOffsets;
 class DexFile;
 class ImageWriter;
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index 0fdf132..3e04bf6 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -17,12 +17,13 @@
 #ifndef ART_RUNTIME_MIRROR_OBJECT_ARRAY_INL_H_
 #define ART_RUNTIME_MIRROR_OBJECT_ARRAY_INL_H_
 
-#include <string>
-
 #include "object_array.h"
 
+#include <string>
+
+#include "android-base/stringprintf.h"
+
 #include "array-inl.h"
-#include "base/stringprintf.h"
 #include "gc/heap.h"
 #include "mirror/class.h"
 #include "obj_ptr-inl.h"
@@ -330,13 +331,15 @@
     std::string actualSrcType(mirror::Object::PrettyTypeOf(o));
     std::string dstType(PrettyTypeOf());
     Thread* self = Thread::Current();
+    std::string msg = android::base::StringPrintf(
+        "source[%d] of type %s cannot be stored in destination array of type %s",
+        src_pos + i,
+        actualSrcType.c_str(),
+        dstType.c_str());
     if (throw_exception) {
-      self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
-                               "source[%d] of type %s cannot be stored in destination array of type %s",
-                               src_pos + i, actualSrcType.c_str(), dstType.c_str());
+      self->ThrowNewException("Ljava/lang/ArrayStoreException;", msg.c_str());
     } else {
-      LOG(FATAL) << StringPrintf("source[%d] of type %s cannot be stored in destination array of type %s",
-                                 src_pos + i, actualSrcType.c_str(), dstType.c_str());
+      LOG(FATAL) << msg;
     }
   }
 }
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 95516ac..9b8445d 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -16,6 +16,10 @@
 #ifndef ART_RUNTIME_MIRROR_STRING_INL_H_
 #define ART_RUNTIME_MIRROR_STRING_INL_H_
 
+#include "string.h"
+
+#include "android-base/stringprintf.h"
+
 #include "array.h"
 #include "base/bit_utils.h"
 #include "class.h"
@@ -24,7 +28,6 @@
 #include "globals.h"
 #include "intern_table.h"
 #include "runtime.h"
-#include "string.h"
 #include "thread.h"
 #include "utf.h"
 #include "utils.h"
@@ -228,9 +231,10 @@
                 "kObjectAlignment must be at least as big as Java char alignment");
   const size_t max_length = RoundDown(max_alloc_length, kObjectAlignment / block_size);
   if (UNLIKELY(length > max_length)) {
-    self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
-                                             Class::PrettyDescriptor(string_class).c_str(),
-                                             static_cast<int>(length)).c_str());
+    self->ThrowOutOfMemoryError(
+        android::base::StringPrintf("%s of length %d would overflow",
+                                    Class::PrettyDescriptor(string_class).c_str(),
+                                    static_cast<int>(length)).c_str());
     return nullptr;
   }
 
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index ade4e87..e50409f 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -16,6 +16,8 @@
 
 #include "throwable.h"
 
+#include "android-base/stringprintf.h"
+
 #include "art_method-inl.h"
 #include "base/enums.h"
 #include "class-inl.h"
@@ -31,6 +33,8 @@
 namespace art {
 namespace mirror {
 
+using android::base::StringPrintf;
+
 GcRoot<Class> Throwable::java_lang_Throwable_;
 
 void Throwable::SetDetailMessage(ObjPtr<String> new_detail_message) {
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index e7de7e6..222eb5c 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -18,6 +18,8 @@
 
 #include <vector>
 
+#include "android-base/stringprintf.h"
+
 #include "art_method-inl.h"
 #include "base/mutex.h"
 #include "base/stl_util.h"
@@ -38,6 +40,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static constexpr uint64_t kLongWaitMs = 100;
 
 /*
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 1a77072..cd0e55f 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -18,9 +18,10 @@
 
 #include <sstream>
 
+#include "android-base/stringprintf.h"
+
 #include "base/logging.h"
 #include "base/stl_util.h"
-#include "base/stringprintf.h"
 #include "class_linker.h"
 #include "common_throws.h"
 #include "compiler_filter.h"
@@ -43,6 +44,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static bool ConvertJavaArrayToDexFiles(
     JNIEnv* env,
     jobject arrayObject,
diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
index db245aa..981be68 100644
--- a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
+++ b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
@@ -16,6 +16,8 @@
 
 #include "dalvik_system_InMemoryDexClassLoader_DexData.h"
 
+#include "android-base/stringprintf.h"
+
 #include "class_linker.h"
 #include "common_throws.h"
 #include "dex_file.h"
@@ -29,6 +31,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static std::unique_ptr<MemMap> AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) {
   if (end <= start) {
     ScopedObjectAccess soa(env);
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 3058df4..1af8619 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -27,6 +27,8 @@
 #include "toStringArray.h"
 #pragma GCC diagnostic pop
 
+#include "android-base/stringprintf.h"
+
 #include "art_method-inl.h"
 #include "arch/instruction_set.h"
 #include "base/enums.h"
@@ -54,6 +56,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static jfloat VMRuntime_getTargetHeapUtilization(JNIEnv*, jobject) {
   return Runtime::Current()->GetHeap()->GetTargetHeapUtilization();
 }
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index a78909b..10fc90b 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -18,6 +18,8 @@
 
 #include <stdlib.h>
 
+#include "android-base/stringprintf.h"
+
 #include "arch/instruction_set.h"
 #include "debugger.h"
 #include "java_vm_ext.h"
@@ -37,6 +39,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static void EnableDebugger() {
 #if defined(__linux__)
   // To let a non-privileged gdbserver attach to this
diff --git a/runtime/native/java_lang_ref_Reference.cc b/runtime/native/java_lang_ref_Reference.cc
index bedca10..c778068 100644
--- a/runtime/native/java_lang_ref_Reference.cc
+++ b/runtime/native/java_lang_ref_Reference.cc
@@ -33,8 +33,15 @@
   return soa.AddLocalReference<jobject>(referent);
 }
 
+static void Reference_clearReferent(JNIEnv* env, jobject javaThis) {
+  ScopedFastNativeObjectAccess soa(env);
+  ObjPtr<mirror::Reference> ref = soa.Decode<mirror::Reference>(javaThis);
+  Runtime::Current()->GetHeap()->GetReferenceProcessor()->ClearReferent(ref);
+}
+
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(Reference, getReferent, "!()Ljava/lang/Object;"),
+  NATIVE_METHOD(Reference, clearReferent, "!()V"),
 };
 
 void register_java_lang_ref_Reference(JNIEnv* env) {
diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc
index 73b81a7..ee59c4a 100644
--- a/runtime/native/java_lang_reflect_Executable.cc
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -16,6 +16,8 @@
 
 #include "java_lang_reflect_Executable.h"
 
+#include "android-base/stringprintf.h"
+
 #include "art_method-inl.h"
 #include "dex_file_annotations.h"
 #include "handle.h"
@@ -30,6 +32,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static jobjectArray Executable_getDeclaredAnnotationsNative(JNIEnv* env, jobject javaMethod) {
   ScopedFastNativeObjectAccess soa(env);
   ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 6206948..374eeb5 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -16,6 +16,8 @@
 
 #include "java_lang_reflect_Field.h"
 
+#include "android-base/stringprintf.h"
+
 #include "class_linker.h"
 #include "class_linker-inl.h"
 #include "common_throws.h"
@@ -30,6 +32,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 template<bool kIsSet>
 ALWAYS_INLINE inline static bool VerifyFieldAccess(Thread* self,
                                                    ObjPtr<mirror::Field> field,
diff --git a/runtime/native/java_lang_reflect_Parameter.cc b/runtime/native/java_lang_reflect_Parameter.cc
index 16164d2..0bb9e38 100644
--- a/runtime/native/java_lang_reflect_Parameter.cc
+++ b/runtime/native/java_lang_reflect_Parameter.cc
@@ -16,6 +16,8 @@
 
 #include "java_lang_reflect_Parameter.h"
 
+#include "android-base/stringprintf.h"
+
 #include "art_method-inl.h"
 #include "common_throws.h"
 #include "dex_file-inl.h"
@@ -26,6 +28,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static jobject Parameter_getAnnotationNative(JNIEnv* env,
                                              jclass,
                                              jobject javaMethod,
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index 5565565..7460d62 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -37,10 +37,11 @@
 #include <sys/time.h>
 #include <sys/types.h>
 
+#include "android-base/stringprintf.h"
+
 #include "arch/instruction_set.h"
 #include "base/memory_tool.h"
 #include "base/mutex.h"
-#include "base/stringprintf.h"
 #include "base/unix_file/fd_file.h"
 #include "oat_quick_method_header.h"
 #include "os.h"
@@ -53,6 +54,8 @@
 
 #if defined(__linux__)
 
+using android::base::StringPrintf;
+
 static constexpr bool kUseAddr2line = !kIsTargetBuild;
 
 ALWAYS_INLINE
diff --git a/runtime/oat.cc b/runtime/oat.cc
index aab0e81..cebe765 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -19,12 +19,15 @@
 #include <string.h>
 #include <zlib.h>
 
+#include "android-base/stringprintf.h"
+
 #include "arch/instruction_set_features.h"
 #include "base/bit_utils.h"
-#include "base/stringprintf.h"
 
 namespace art {
 
+using android::base::StringPrintf;
+
 constexpr uint8_t OatHeader::kOatMagic[4];
 constexpr uint8_t OatHeader::kOatVersion[4];
 constexpr const char OatHeader::kTrueValue[];
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index bdf8b0e..0bf7136 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -32,6 +32,8 @@
 #include "android/dlext.h"
 #endif
 
+#include "android-base/stringprintf.h"
+
 #include "art_method-inl.h"
 #include "base/bit_vector.h"
 #include "base/enums.h"
@@ -58,6 +60,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 // Whether OatFile::Open will try dlopen. Fallback is our own ELF loader.
 static constexpr bool kUseDlopen = true;
 
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 7f7b1b5..ee7cf9d 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -23,7 +23,6 @@
 #include "android-base/strings.h"
 
 #include "base/logging.h"
-#include "base/stringprintf.h"
 #include "compiler_filter.h"
 #include "class_linker.h"
 #include "gc/heap.h"
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 5641459..33bd0f3 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -20,6 +20,8 @@
 #include <queue>
 #include <vector>
 
+#include "android-base/stringprintf.h"
+
 #include "base/logging.h"
 #include "base/stl_util.h"
 #include "base/systrace.h"
@@ -38,6 +40,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 // If true, then we attempt to load the application image if it exists.
 static constexpr bool kEnableAppImage = true;
 
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 1ad3f08..5f97b60 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -700,7 +700,7 @@
                                        jmethodID method,
                                        jint* entry_count_ptr,
                                        jvmtiLineNumberEntry** table_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return MethodUtil::GetLineNumberTable(env, method, entry_count_ptr, table_ptr);
   }
 
   static jvmtiError GetMethodLocation(jvmtiEnv* env,
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index ffa5ac7..a0a0923 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -130,4 +130,63 @@
   return ERR(NONE);
 }
 
+using LineNumberContext = std::vector<jvmtiLineNumberEntry>;
+
+static bool CollectLineNumbers(void* void_context, const art::DexFile::PositionInfo& entry) {
+  LineNumberContext* context = reinterpret_cast<LineNumberContext*>(void_context);
+  jvmtiLineNumberEntry jvmti_entry = { static_cast<jlocation>(entry.address_),
+                                       static_cast<jint>(entry.line_) };
+  context->push_back(jvmti_entry);
+  return false;  // Collect all, no early exit.
+}
+
+jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env,
+                                          jmethodID method,
+                                          jint* entry_count_ptr,
+                                          jvmtiLineNumberEntry** table_ptr) {
+  if (method == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+  art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+  DCHECK(!art_method->IsRuntimeMethod());
+
+  const art::DexFile::CodeItem* code_item;
+  const art::DexFile* dex_file;
+  {
+    art::ScopedObjectAccess soa(art::Thread::Current());
+
+    if (art_method->IsProxyMethod()) {
+      return ERR(ABSENT_INFORMATION);
+    }
+    if (art_method->IsNative()) {
+      return ERR(NATIVE_METHOD);
+    }
+    if (entry_count_ptr == nullptr || table_ptr == nullptr) {
+      return ERR(NULL_POINTER);
+    }
+
+    code_item = art_method->GetCodeItem();
+    dex_file = art_method->GetDexFile();
+    DCHECK(code_item != nullptr) << art_method->PrettyMethod() << " " << dex_file->GetLocation();
+  }
+
+  LineNumberContext context;
+  bool success = dex_file->DecodeDebugPositionInfo(code_item, CollectLineNumbers, &context);
+  if (!success) {
+    return ERR(ABSENT_INFORMATION);
+  }
+
+  unsigned char* data;
+  jlong mem_size = context.size() * sizeof(jvmtiLineNumberEntry);
+  jvmtiError alloc_error = env->Allocate(mem_size, &data);
+  if (alloc_error != ERR(NONE)) {
+    return alloc_error;
+  }
+  *table_ptr = reinterpret_cast<jvmtiLineNumberEntry*>(data);
+  memcpy(*table_ptr, context.data(), mem_size);
+  *entry_count_ptr = static_cast<jint>(context.size());
+
+  return ERR(NONE);
+}
+
 }  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
index 43f11f9..fb2fbb2 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -52,6 +52,11 @@
   static jvmtiError GetMethodModifiers(jvmtiEnv* env,
                                        jmethodID method,
                                        jint* modifiers_ptr);
+
+  static jvmtiError GetLineNumberTable(jvmtiEnv* env,
+                                       jmethodID method,
+                                       jint* entry_count_ptr,
+                                       jvmtiLineNumberEntry** table_ptr);
 };
 
 }  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index d0349b9..57cc938 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -33,11 +33,15 @@
 
 #include <limits>
 
+#include "android-base/stringprintf.h"
+
 #include "art_jvmti.h"
 #include "base/logging.h"
 #include "events-inl.h"
 #include "gc/allocation_listener.h"
 #include "instrumentation.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
 #include "jni_env_ext-inl.h"
 #include "jvmti_allocator.h"
 #include "mirror/class.h"
@@ -49,13 +53,152 @@
 
 namespace openjdkjvmti {
 
+using android::base::StringPrintf;
+
+// This visitor walks thread stacks and allocates and sets up the obsolete methods. It also does
+// some basic sanity checks that the obsolete method is sane.
+class ObsoleteMethodStackVisitor : public art::StackVisitor {
+ protected:
+  ObsoleteMethodStackVisitor(
+      art::Thread* thread,
+      art::LinearAlloc* allocator,
+      const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
+      /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps,
+      /*out*/bool* success,
+      /*out*/std::string* error_msg)
+        : StackVisitor(thread,
+                       /*context*/nullptr,
+                       StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+          allocator_(allocator),
+          obsoleted_methods_(obsoleted_methods),
+          obsolete_maps_(obsolete_maps),
+          success_(success),
+          is_runtime_frame_(false),
+          error_msg_(error_msg) {
+    *success_ = true;
+  }
+
+  ~ObsoleteMethodStackVisitor() OVERRIDE {}
+
+ public:
+  // Returns true if we successfully installed obsolete methods on this thread, filling
+  // obsolete_maps_ with the translations if needed. Returns false and fills error_msg if we fail.
+  // The stack is cleaned up when we fail.
+  static bool UpdateObsoleteFrames(
+      art::Thread* thread,
+      art::LinearAlloc* allocator,
+      const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
+      /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps,
+      /*out*/std::string* error_msg) REQUIRES(art::Locks::mutator_lock_) {
+    bool success = true;
+    ObsoleteMethodStackVisitor visitor(thread,
+                                       allocator,
+                                       obsoleted_methods,
+                                       obsolete_maps,
+                                       &success,
+                                       error_msg);
+    visitor.WalkStack();
+    if (!success) {
+      RestoreFrames(thread, *obsolete_maps, error_msg);
+      return false;
+    } else {
+      return true;
+    }
+  }
+
+  static void RestoreFrames(
+      art::Thread* thread ATTRIBUTE_UNUSED,
+      const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsolete_maps ATTRIBUTE_UNUSED,
+      std::string* error_msg)
+        REQUIRES(art::Locks::mutator_lock_) {
+    LOG(FATAL) << "Restoring stack frames is not yet supported. Error was: " << *error_msg;
+  }
+
+  bool VisitFrame() OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+    art::ArtMethod* old_method = GetMethod();
+    // TODO REMOVE once either current_method doesn't stick around through suspend points or deopt
+    // works through runtime methods.
+    bool prev_was_runtime_frame_ = is_runtime_frame_;
+    is_runtime_frame_ = old_method->IsRuntimeMethod();
+    if (obsoleted_methods_.find(old_method) != obsoleted_methods_.end()) {
+      // The check below works since when we deoptimize we set shadow frames for all frames until a
+      // native/runtime transition and for those set the return PC to a function that will complete
+      // the deoptimization. This does leave us with the unfortunate side-effect that frames just
+      // below runtime frames cannot be deoptimized at the moment.
+      // TODO REMOVE once either current_method doesn't stick around through suspend points or deopt
+      // works through runtime methods.
+      // TODO b/33616143
+      if (!IsShadowFrame() && prev_was_runtime_frame_) {
+        *error_msg_ = StringPrintf("Deoptimization failed due to runtime method in stack.");
+        *success_ = false;
+        return false;
+      }
+      // We cannot ensure that the right dex file is used in inlined frames so we don't support
+      // redefining them.
+      DCHECK(!IsInInlinedFrame()) << "Inlined frames are not supported when using redefinition";
+      // TODO We should really support intrinsic obsolete methods.
+      // TODO We should really support redefining intrinsics.
+      // We don't support intrinsics so check for them here.
+      DCHECK(!old_method->IsIntrinsic());
+      art::ArtMethod* new_obsolete_method = nullptr;
+      auto obsolete_method_pair = obsolete_maps_->find(old_method);
+      if (obsolete_method_pair == obsolete_maps_->end()) {
+        // Create a new Obsolete Method and put it in the list.
+        art::Runtime* runtime = art::Runtime::Current();
+        art::ClassLinker* cl = runtime->GetClassLinker();
+        auto ptr_size = cl->GetImagePointerSize();
+        const size_t method_size = art::ArtMethod::Size(ptr_size);
+        auto* method_storage = allocator_->Alloc(GetThread(), method_size);
+        if (method_storage == nullptr) {
+          *success_ = false;
+          *error_msg_ = StringPrintf("Unable to allocate storage for obsolete version of '%s'",
+                                     old_method->PrettyMethod().c_str());
+          return false;
+        }
+        new_obsolete_method = new (method_storage) art::ArtMethod();
+        new_obsolete_method->CopyFrom(old_method, ptr_size);
+        DCHECK_EQ(new_obsolete_method->GetDeclaringClass(), old_method->GetDeclaringClass());
+        new_obsolete_method->SetIsObsolete();
+        obsolete_maps_->insert({old_method, new_obsolete_method});
+        // Update JIT Data structures to point to the new method.
+        art::jit::Jit* jit = art::Runtime::Current()->GetJit();
+        if (jit != nullptr) {
+          // Notify the JIT we are making this obsolete method. It will update the jit's internal
+          // structures to keep track of the new obsolete method.
+          jit->GetCodeCache()->MoveObsoleteMethod(old_method, new_obsolete_method);
+        }
+      } else {
+        new_obsolete_method = obsolete_method_pair->second;
+      }
+      DCHECK(new_obsolete_method != nullptr);
+      SetMethod(new_obsolete_method);
+    }
+    return true;
+  }
+
+ private:
+  // The linear allocator we should use to make new methods.
+  art::LinearAlloc* allocator_;
+  // The set of all methods which could be obsoleted.
+  const std::unordered_set<art::ArtMethod*>& obsoleted_methods_;
+  // A map from the original to the newly allocated obsolete method for frames on this thread. The
+  // values in this map must be added to the obsolete_methods_ (and obsolete_dex_caches_) fields of
+  // the redefined classes ClassExt by the caller.
+  std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps_;
+  bool* success_;
+  // TODO REMOVE once either current_method doesn't stick around through suspend points or deopt
+  // works through runtime methods.
+  bool is_runtime_frame_;
+  std::string* error_msg_;
+};
+
 // Moves dex data to an anonymous, read-only mmap'd region.
 std::unique_ptr<art::MemMap> Redefiner::MoveDataToMemMap(const std::string& original_location,
                                                          jint data_len,
                                                          unsigned char* dex_data,
                                                          std::string* error_msg) {
   std::unique_ptr<art::MemMap> map(art::MemMap::MapAnonymous(
-      art::StringPrintf("%s-transformed", original_location.c_str()).c_str(),
+      StringPrintf("%s-transformed", original_location.c_str()).c_str(),
       nullptr,
       data_len,
       PROT_READ|PROT_WRITE,
@@ -72,6 +215,8 @@
   return map;
 }
 
+// TODO This should handle doing multiple classes at once so we need to do less cleanup when things
+// go wrong.
 jvmtiError Redefiner::RedefineClass(ArtJvmTiEnv* env,
                                     art::Runtime* runtime,
                                     art::Thread* self,
@@ -112,6 +257,9 @@
     *error_msg = os.str();
     return ERR(INVALID_CLASS_FORMAT);
   }
+  // Stop JIT for the duration of this redefine since the JIT might concurrently compile a method we
+  // are going to redefine.
+  art::jit::ScopedJitSuspend suspend_jit;
   // Get shared mutator lock.
   art::ScopedObjectAccess soa(self);
   art::StackHandleScope<1> hs(self);
@@ -246,9 +394,9 @@
 }
 
 void Redefiner::RecordFailure(jvmtiError result, const std::string& error_msg) {
-  *error_msg_ = art::StringPrintf("Unable to perform redefinition of '%s': %s",
-                                  class_sig_,
-                                  error_msg.c_str());
+  *error_msg_ = StringPrintf("Unable to perform redefinition of '%s': %s",
+                             class_sig_,
+                             error_msg.c_str());
   result_ = result;
 }
 
@@ -292,6 +440,107 @@
   return true;
 }
 
+struct CallbackCtx {
+  Redefiner* const r;
+  art::LinearAlloc* allocator;
+  std::unordered_map<art::ArtMethod*, art::ArtMethod*> obsolete_map;
+  std::unordered_set<art::ArtMethod*> obsolete_methods;
+  bool success;
+  std::string* error_msg;
+
+  CallbackCtx(Redefiner* self, art::LinearAlloc* alloc, std::string* error)
+      : r(self), allocator(alloc), success(true), error_msg(error) {}
+};
+
+void DoRestoreObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SAFETY_ANALYSIS {
+  CallbackCtx* data = reinterpret_cast<CallbackCtx*>(vdata);
+  ObsoleteMethodStackVisitor::RestoreFrames(t, data->obsolete_map, data->error_msg);
+}
+
+void DoAllocateObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SAFETY_ANALYSIS {
+  CallbackCtx* data = reinterpret_cast<CallbackCtx*>(vdata);
+  if (data->success) {
+    // Don't do anything if we already failed once.
+    data->success = ObsoleteMethodStackVisitor::UpdateObsoleteFrames(t,
+                                                                     data->allocator,
+                                                                     data->obsolete_methods,
+                                                                     &data->obsolete_map,
+                                                                     data->error_msg);
+  }
+}
+
+// This creates any ArtMethod* structures needed for obsolete methods and ensures that the stack is
+// updated so they will be run.
+bool Redefiner::FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass) {
+  art::ScopedAssertNoThreadSuspension ns("No thread suspension during thread stack walking");
+  art::mirror::ClassExt* ext = art_klass->GetExtData();
+  CHECK(ext->GetObsoleteMethods() != nullptr);
+  CallbackCtx ctx(this, art_klass->GetClassLoader()->GetAllocator(), error_msg_);
+  // Add all the declared methods to the map
+  for (auto& m : art_klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
+    ctx.obsolete_methods.insert(&m);
+  }
+  for (art::ArtMethod* old_method : ctx.obsolete_methods) {
+    if (old_method->IsIntrinsic()) {
+      *error_msg_ = StringPrintf("Method '%s' is intrinsic and cannot be made obsolete!",
+                                 old_method->PrettyMethod().c_str());
+      return false;
+    }
+  }
+  {
+    art::MutexLock mu(self_, *art::Locks::thread_list_lock_);
+    art::ThreadList* list = art::Runtime::Current()->GetThreadList();
+    list->ForEach(DoAllocateObsoleteMethodsCallback, static_cast<void*>(&ctx));
+    if (!ctx.success) {
+      list->ForEach(DoRestoreObsoleteMethodsCallback, static_cast<void*>(&ctx));
+      return false;
+    }
+  }
+  FillObsoleteMethodMap(art_klass, ctx.obsolete_map);
+  return true;
+}
+
+// Fills the obsolete method map in the art_klass's extData. This is so obsolete methods are able to
+// figure out their DexCaches.
+void Redefiner::FillObsoleteMethodMap(
+    art::mirror::Class* art_klass,
+    const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes) {
+  int32_t index = 0;
+  art::mirror::ClassExt* ext_data = art_klass->GetExtData();
+  art::mirror::PointerArray* obsolete_methods = ext_data->GetObsoleteMethods();
+  art::mirror::ObjectArray<art::mirror::DexCache>* obsolete_dex_caches =
+      ext_data->GetObsoleteDexCaches();
+  int32_t num_method_slots = obsolete_methods->GetLength();
+  // Find the first empty index.
+  for (; index < num_method_slots; index++) {
+    if (obsolete_methods->GetElementPtrSize<art::ArtMethod*>(
+          index, art::kRuntimePointerSize) == nullptr) {
+      break;
+    }
+  }
+  // Make sure we have enough space.
+  CHECK_GT(num_method_slots, static_cast<int32_t>(obsoletes.size() + index));
+  CHECK(obsolete_dex_caches->Get(index) == nullptr);
+  // Fill in the map.
+  for (auto& obs : obsoletes) {
+    obsolete_methods->SetElementPtrSize(index, obs.second, art::kRuntimePointerSize);
+    obsolete_dex_caches->Set(index, art_klass->GetDexCache());
+    index++;
+  }
+}
+
+// TODO It should be possible to only deoptimize the specific obsolete methods.
+// TODO ReJitEverything can (sort of) fail. In certain cases it will skip deoptimizing some frames.
+// If one of these frames is an obsolete method we have a problem. b/33616143
+// TODO This shouldn't be necessary once we can ensure that the current method is not kept in
+// registers across suspend points.
+// TODO Pending b/33630159
+void Redefiner::EnsureObsoleteMethodsAreDeoptimized() {
+  art::ScopedAssertNoThreadSuspension nts("Deoptimizing everything!");
+  art::instrumentation::Instrumentation* i = runtime_->GetInstrumentation();
+  i->ReJitEverything("libOpenJkdJvmti - Class Redefinition");
+}
+
 jvmtiError Redefiner::Run() {
   art::StackHandleScope<5> hs(self_);
   // TODO We might want to have a global lock (or one based on the class being redefined at least)
@@ -334,6 +583,11 @@
   self_->TransitionFromRunnableToSuspended(art::ThreadState::kNative);
   runtime_->GetThreadList()->SuspendAll(
       "Final installation of redefined Class!", /*long_suspend*/true);
+  // TODO We need to invalidate all breakpoints in the redefined class with the debugger.
+  // TODO We need to deal with any instrumentation/debugger deoptimized_methods_.
+  // TODO We need to update all debugger MethodIDs so they note the method they point to is
+  // obsolete or implement some other well defined semantics.
+  // TODO We need to decide on & implement semantics for JNI jmethodids when we redefine methods.
   // TODO Might want to move this into a different type.
   // Now we reach the part where we must do active cleanup if something fails.
   // TODO We should really Retry if this fails instead of simply aborting.
@@ -341,7 +595,8 @@
   art::ObjPtr<art::mirror::LongArray> original_dex_file_cookie(nullptr);
   if (!UpdateJavaDexFile(java_dex_file.Get(),
                          new_dex_file_cookie.Get(),
-                         &original_dex_file_cookie)) {
+                         &original_dex_file_cookie) ||
+      !FindAndAllocateObsoleteMethods(art_class.Get())) {
     // Release suspendAll
     runtime_->GetThreadList()->ResumeAll();
     // Get back shared mutator lock as expected for return.
@@ -357,23 +612,25 @@
     self_->TransitionFromSuspendedToRunnable();
     return result_;
   }
-  // Update the ClassObjects Keep the old DexCache (and other stuff) around so we can restore
-  // functions/fields.
-  // Verify the new Class.
-  //   Failure then undo updates to class
-  // Do stack walks and allocate obsolete methods
-  // Shrink the obsolete method maps if possible?
-  // TODO find appropriate class loader. Allocate new dex files array. Pause all java treads.
-  // Replace dex files array. Do stack scan + allocate obsoletes. Remove array if possible.
-  // TODO We might want to ensure that all threads are stopped for this!
-  // AddDexToClassPath();
-  // TODO
-  // Release suspendAll
+  // Ensure that obsolete methods are deoptimized. This is needed since optimized methods may have
+  // pointers to their ArtMethod's stashed in registers that they then use to attempt to hit the
+  // DexCache.
+  // TODO This can fail (leave some methods optimized) near runtime methods (including
+  // quick-to-interpreter transition function).
+  // TODO We probably don't need this at all once we have a way to ensure that the
+  // current_art_method is never stashed in a (physical) register by the JIT and lost to the
+  // stack-walker.
+  EnsureObsoleteMethodsAreDeoptimized();
+  // TODO Verify the new Class.
+  // TODO   Failure then undo updates to class
+  // TODO Shrink the obsolete method maps if possible?
+  // TODO find appropriate class loader.
   // TODO Put this into a scoped thing.
   runtime_->GetThreadList()->ResumeAll();
   // Get back shared mutator lock as expected for return.
   self_->TransitionFromSuspendedToRunnable();
-  // TODO Do this at a more reasonable place.
+  // TODO Do the dex_file_ release at a more reasonable place. This works but it muddles who really
+  // owns the DexFile.
   dex_file_.release();
   return OK;
 }
@@ -392,19 +649,14 @@
   }
 }
 
-// Performs updates to class that will allow us to verify it.
-bool Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
-                            art::ObjPtr<art::mirror::DexCache> new_dex_cache) {
+bool Redefiner::UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
+                              art::ObjPtr<art::mirror::DexCache> new_dex_cache,
+                              const art::DexFile::ClassDef& class_def) {
   art::ClassLinker* linker = runtime_->GetClassLinker();
   art::PointerSize image_pointer_size = linker->GetImagePointerSize();
-  const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef(
-      *dex_file_, class_sig_, art::ComputeModifiedUtf8Hash(class_sig_));
-  if (class_def == nullptr) {
-    RecordFailure(ERR(INVALID_CLASS_FORMAT), "Unable to find ClassDef!");
-    return false;
-  }
-  const art::DexFile::TypeId& declaring_class_id = dex_file_->GetTypeId(class_def->class_idx_);
+  const art::DexFile::TypeId& declaring_class_id = dex_file_->GetTypeId(class_def.class_idx_);
   const art::DexFile& old_dex_file = mclass->GetDexFile();
+  // Update methods.
   for (art::ArtMethod& method : mclass->GetMethods(image_pointer_size)) {
     const art::DexFile::StringId* new_name_id = dex_file_->FindStringId(method.GetName());
     art::dex::TypeIndex method_return_idx =
@@ -421,25 +673,73 @@
     }
     const art::DexFile::ProtoId* proto_id = dex_file_->FindProtoId(method_return_idx,
                                                                    new_type_list);
-    CHECK(proto_id != nullptr || old_type_list == nullptr);
     // TODO Return false, cleanup.
+    CHECK(proto_id != nullptr || old_type_list == nullptr);
     const art::DexFile::MethodId* method_id = dex_file_->FindMethodId(declaring_class_id,
                                                                       *new_name_id,
                                                                       *proto_id);
-    CHECK(method_id != nullptr);
     // TODO Return false, cleanup.
+    CHECK(method_id != nullptr);
     uint32_t dex_method_idx = dex_file_->GetIndexForMethodId(*method_id);
     method.SetDexMethodIndex(dex_method_idx);
     linker->SetEntryPointsToInterpreter(&method);
-    method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(*class_def, dex_method_idx));
+    method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(class_def, dex_method_idx));
     method.SetDexCacheResolvedMethods(new_dex_cache->GetResolvedMethods(), image_pointer_size);
     method.SetDexCacheResolvedTypes(new_dex_cache->GetResolvedTypes(), image_pointer_size);
+    // Notify the jit that this method is redefined.
+    art::jit::Jit* jit = runtime_->GetJit();
+    if (jit != nullptr) {
+      jit->GetCodeCache()->NotifyMethodRedefined(&method);
+    }
   }
+  return true;
+}
+
+bool Redefiner::UpdateFields(art::ObjPtr<art::mirror::Class> mclass) {
+  // TODO The IFields & SFields pointers should be combined like the methods_ arrays were.
+  for (auto fields_iter : {mclass->GetIFields(), mclass->GetSFields()}) {
+    for (art::ArtField& field : fields_iter) {
+      std::string declaring_class_name;
+      const art::DexFile::TypeId* new_declaring_id =
+          dex_file_->FindTypeId(field.GetDeclaringClass()->GetDescriptor(&declaring_class_name));
+      const art::DexFile::StringId* new_name_id = dex_file_->FindStringId(field.GetName());
+      const art::DexFile::TypeId* new_type_id = dex_file_->FindTypeId(field.GetTypeDescriptor());
+      // TODO Handle error, cleanup.
+      CHECK(new_name_id != nullptr && new_type_id != nullptr && new_declaring_id != nullptr);
+      const art::DexFile::FieldId* new_field_id =
+          dex_file_->FindFieldId(*new_declaring_id, *new_name_id, *new_type_id);
+      CHECK(new_field_id != nullptr);
+      // We only need to update the index since the other data in the ArtField cannot be updated.
+      field.SetDexFieldIndex(dex_file_->GetIndexForFieldId(*new_field_id));
+    }
+  }
+  return true;
+}
+
+// Performs updates to class that will allow us to verify it.
+bool Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
+                            art::ObjPtr<art::mirror::DexCache> new_dex_cache) {
+  const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef(
+      *dex_file_, class_sig_, art::ComputeModifiedUtf8Hash(class_sig_));
+  if (class_def == nullptr) {
+    RecordFailure(ERR(INVALID_CLASS_FORMAT), "Unable to find ClassDef!");
+    return false;
+  }
+  if (!UpdateMethods(mclass, new_dex_cache, *class_def)) {
+    // TODO Investigate appropriate error types.
+    RecordFailure(ERR(INTERNAL), "Unable to update class methods.");
+    return false;
+  }
+  if (!UpdateFields(mclass)) {
+    // TODO Investigate appropriate error types.
+    RecordFailure(ERR(INTERNAL), "Unable to update class fields.");
+    return false;
+  }
+
   // Update the class fields.
   // Need to update class last since the ArtMethod gets its DexFile from the class (which is needed
   // to call GetReturnTypeDescriptor and GetParameterTypeList above).
   mclass->SetDexCache(new_dex_cache.Ptr());
-  mclass->SetDexCacheStrings(new_dex_cache->GetStrings());
   mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(*class_def));
   mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_)));
   return true;
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index c819acd..9d23ce4 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -64,6 +64,8 @@
 namespace openjdkjvmti {
 
 // Class that can redefine a single class's methods.
+// TODO We should really make this be driven by an outside class so we can do multiple classes at
+// the same time and have less required cleanup.
 class Redefiner {
  public:
   // Redefine the given class with the given dex data. Note this function does not take ownership of
@@ -124,6 +126,14 @@
   // in the future. For now we will just take the memory hit.
   bool EnsureClassAllocationsFinished() REQUIRES_SHARED(art::Locks::mutator_lock_);
 
+  // Ensure that obsolete methods are deoptimized. This is needed since optimized methods may have
+  // pointers to their ArtMethods stashed in registers that they then use to attempt to hit the
+  // DexCache.
+  void EnsureObsoleteMethodsAreDeoptimized()
+      REQUIRES(art::Locks::mutator_lock_)
+      REQUIRES(!art::Locks::thread_list_lock_,
+               !art::Locks::classlinker_classes_lock_);
+
   art::mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(art::Locks::mutator_lock_);
 
   // This finds the java.lang.DexFile we will add the native DexFile to as part of the classpath.
@@ -159,9 +169,24 @@
                           art::ObjPtr<art::mirror::LongArray> original_cookie)
       REQUIRES(art::Locks::mutator_lock_);
 
+  bool UpdateFields(art::ObjPtr<art::mirror::Class> mclass)
+      REQUIRES(art::Locks::mutator_lock_);
+
+  bool UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
+                     art::ObjPtr<art::mirror::DexCache> new_dex_cache,
+                     const art::DexFile::ClassDef& class_def)
+      REQUIRES(art::Locks::mutator_lock_);
+
   bool UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
                    art::ObjPtr<art::mirror::DexCache> new_dex_cache)
       REQUIRES(art::Locks::mutator_lock_);
+
+  bool FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass)
+      REQUIRES(art::Locks::mutator_lock_);
+
+  void FillObsoleteMethodMap(art::mirror::Class* art_klass,
+                             const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes)
+      REQUIRES(art::Locks::mutator_lock_);
 };
 
 }  // namespace openjdkjvmti
diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc
index 5b90c6a..8948c71 100644
--- a/runtime/parsed_options_test.cc
+++ b/runtime/parsed_options_test.cc
@@ -19,7 +19,6 @@
 #include <memory>
 
 #include "arch/instruction_set.h"
-#include "base/stringprintf.h"
 #include "common_runtime_test.h"
 
 namespace art {
diff --git a/runtime/plugin.cc b/runtime/plugin.cc
index 481b1ca..731967c 100644
--- a/runtime/plugin.cc
+++ b/runtime/plugin.cc
@@ -17,11 +17,15 @@
 #include "plugin.h"
 
 #include <dlfcn.h>
-#include "base/stringprintf.h"
+
+#include "android-base/stringprintf.h"
+
 #include "base/logging.h"
 
 namespace art {
 
+using android::base::StringPrintf;
+
 const char* PLUGIN_INITIALIZATION_FUNCTION_NAME = "ArtPlugin_Initialize";
 const char* PLUGIN_DEINITIALIZATION_FUNCTION_NAME = "ArtPlugin_Deinitialize";
 
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index 1c975a4..d8b9dcc 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -16,6 +16,8 @@
 
 #include "reference_table.h"
 
+#include "android-base/stringprintf.h"
+
 #include "base/mutex.h"
 #include "indirect_reference_table.h"
 #include "mirror/array.h"
@@ -30,6 +32,9 @@
 
 namespace art {
 
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
 ReferenceTable::ReferenceTable(const char* name, size_t initial_size, size_t max_size)
     : name_(name), max_size_(max_size) {
   CHECK_LE(initial_size, max_size);
diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc
index d80a9b3..9523e92 100644
--- a/runtime/reference_table_test.cc
+++ b/runtime/reference_table_test.cc
@@ -16,6 +16,8 @@
 
 #include "reference_table.h"
 
+#include "android-base/stringprintf.h"
+
 #include "class_linker.h"
 #include "common_runtime_test.h"
 #include "handle_scope-inl.h"
@@ -30,6 +32,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 class ReferenceTableTest : public CommonRuntimeTest {};
 
 static mirror::Object* CreateWeakReference(mirror::Object* referent)
diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h
index 68e7a10..62ce9e9 100644
--- a/runtime/reflection-inl.h
+++ b/runtime/reflection-inl.h
@@ -19,7 +19,8 @@
 
 #include "reflection.h"
 
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
 #include "common_throws.h"
 #include "jvalue-inl.h"
 #include "mirror/object-inl.h"
@@ -103,13 +104,14 @@
   }
 
   if (!unbox_for_result) {
-    ThrowIllegalArgumentException(StringPrintf("Invalid primitive conversion from %s to %s",
-                                               PrettyDescriptor(srcType).c_str(),
-                                               PrettyDescriptor(dstType).c_str()).c_str());
+    ThrowIllegalArgumentException(
+        android::base::StringPrintf("Invalid primitive conversion from %s to %s",
+                                    PrettyDescriptor(srcType).c_str(),
+                                    PrettyDescriptor(dstType).c_str()).c_str());
   } else {
-    ThrowClassCastException(StringPrintf("Couldn't convert result of type %s to %s",
-                                         PrettyDescriptor(srcType).c_str(),
-                                         PrettyDescriptor(dstType).c_str()).c_str());
+    ThrowClassCastException(android::base::StringPrintf("Couldn't convert result of type %s to %s",
+                                                        PrettyDescriptor(srcType).c_str(),
+                                                        PrettyDescriptor(dstType).c_str()).c_str());
   }
   return false;
 }
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 8446b52..4d24501 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -34,6 +34,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 class ArgArray {
  public:
   ArgArray(const char* shorty, uint32_t shorty_len)
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 59c5961..2086d70 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1199,6 +1199,8 @@
   CHECK_EQ(self->GetThreadId(), ThreadList::kMainThreadId);
   CHECK(self != nullptr);
 
+  self->SetCanCallIntoJava(!IsAotCompiler());
+
   // Set us to runnable so tools using a runtime can allocate and GC by default
   self->TransitionFromSuspendedToRunnable();
 
@@ -1721,7 +1723,7 @@
     if (space->IsImageSpace()) {
       auto* image_space = space->AsImageSpace();
       const auto& image_header = image_space->GetImageHeader();
-      for (size_t i = 0; i < ImageHeader::kImageRootsMax; ++i) {
+      for (int32_t i = 0, size = image_header.GetImageRoots()->GetLength(); i != size; ++i) {
         auto* obj = image_header.GetImageRoot(static_cast<ImageHeader::ImageRoot>(i));
         if (obj != nullptr) {
           auto* after_obj = obj;
diff --git a/runtime/runtime_android.cc b/runtime/runtime_android.cc
index be97860..0a996a9 100644
--- a/runtime/runtime_android.cc
+++ b/runtime/runtime_android.cc
@@ -21,7 +21,6 @@
 
 #include "base/logging.h"
 #include "base/mutex.h"
-#include "base/stringprintf.h"
 #include "thread-inl.h"
 #include "utils.h"
 
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index 93704a9..b8894d2 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -24,11 +24,12 @@
 #include <iostream>
 #include <sstream>
 
+#include "android-base/stringprintf.h"
+
 #include "base/dumpable.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/mutex.h"
-#include "base/stringprintf.h"
 #include "native_stack_dump.h"
 #include "thread-inl.h"
 #include "thread_list.h"
@@ -36,6 +37,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static constexpr bool kUseSigRTTimeout = true;
 static constexpr bool kDumpNativeStackOnTimeout = true;
 
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 792da88..f9efc0b 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -16,6 +16,8 @@
 
 #include "stack.h"
 
+#include "android-base/stringprintf.h"
+
 #include "arch/context.h"
 #include "art_method-inl.h"
 #include "base/enums.h"
@@ -39,6 +41,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static constexpr bool kDebugStackWalk = false;
 
 mirror::Object* ShadowFrame::GetThisObject() const {
@@ -614,6 +618,17 @@
   return result;
 }
 
+void StackVisitor::SetMethod(ArtMethod* method) {
+  DCHECK(GetMethod() != nullptr);
+  if (cur_shadow_frame_ != nullptr) {
+    cur_shadow_frame_->SetMethod(method);
+  } else {
+    DCHECK(cur_quick_frame_ != nullptr);
+    CHECK(!IsInInlinedFrame()) << "We do not support setting inlined method's ArtMethod!";
+      *cur_quick_frame_ = method;
+  }
+}
+
 static void AssertPcIsWithinQuickCode(ArtMethod* method, uintptr_t pc)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (method->IsNative() || method->IsRuntimeMethod() || method->IsProxyMethod()) {
diff --git a/runtime/stack.h b/runtime/stack.h
index b1e99e5..9dceb29 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -327,6 +327,12 @@
     }
   }
 
+  void SetMethod(ArtMethod* method) REQUIRES(Locks::mutator_lock_) {
+    DCHECK(method != nullptr);
+    DCHECK(method_ != nullptr);
+    method_ = method;
+  }
+
   ArtMethod* GetMethod() const REQUIRES_SHARED(Locks::mutator_lock_) {
     DCHECK(method_ != nullptr);
     return method_;
@@ -610,6 +616,10 @@
 
   ArtMethod* GetMethod() const REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Sets this stack frame's method pointer. This requires a full lock of the MutatorLock. This
+  // doesn't work with inlined methods.
+  void SetMethod(ArtMethod* method) REQUIRES(Locks::mutator_lock_);
+
   ArtMethod* GetOuterMethod() const {
     return *GetCurrentQuickFrame();
   }
diff --git a/runtime/thread.cc b/runtime/thread.cc
index d79bf36..aff12ff 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -28,6 +28,8 @@
 #include <list>
 #include <sstream>
 
+#include "android-base/stringprintf.h"
+
 #include "arch/context.h"
 #include "art_field-inl.h"
 #include "art_method-inl.h"
@@ -88,6 +90,9 @@
 
 namespace art {
 
+using android::base::StringAppendV;
+using android::base::StringPrintf;
+
 extern "C" NO_RETURN void artDeoptimize(Thread* self);
 
 bool Thread::is_started_ = false;
@@ -1802,7 +1807,11 @@
   }
 }
 
-Thread::Thread(bool daemon) : tls32_(daemon), wait_monitor_(nullptr), interrupted_(false) {
+Thread::Thread(bool daemon)
+    : tls32_(daemon),
+      wait_monitor_(nullptr),
+      interrupted_(false),
+      can_call_into_java_(true) {
   wait_mutex_ = new Mutex("a thread wait mutex");
   wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_);
   tlsPtr_.instrumentation_stack = new std::deque<instrumentation::InstrumentationStackFrame>;
diff --git a/runtime/thread.h b/runtime/thread.h
index 31cd0eb..411d85f 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -881,6 +881,15 @@
     --tls32_.disable_thread_flip_count;
   }
 
+  // Returns true if the thread is allowed to call into java.
+  bool CanCallIntoJava() const {
+    return can_call_into_java_;
+  }
+
+  void SetCanCallIntoJava(bool can_call_into_java) {
+    can_call_into_java_ = can_call_into_java;
+  }
+
   // Activates single step control for debugging. The thread takes the
   // ownership of the given SingleStepControl*. It is deleted by a call
   // to DeactivateSingleStepControl or upon thread destruction.
@@ -1588,6 +1597,10 @@
   // Pending extra checkpoints if checkpoint_function_ is already used.
   std::list<Closure*> checkpoint_overflow_ GUARDED_BY(Locks::thread_suspend_count_lock_);
 
+  // True if the thread is allowed to call back into java (for e.g. during class resolution).
+  // By default this is true.
+  bool can_call_into_java_;
+
   friend class Dbg;  // For SetStateUnsafe.
   friend class gc::collector::SemiSpace;  // For getting stack traces.
   friend class Runtime;  // For CreatePeer.
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 664eeb4..bf9eef8 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -25,6 +25,8 @@
 
 #include <sstream>
 
+#include "android-base/stringprintf.h"
+
 #include "base/histogram-inl.h"
 #include "base/mutex-inl.h"
 #include "base/systrace.h"
@@ -52,6 +54,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static constexpr uint64_t kLongThreadSuspendThreshold = MsToNs(5);
 static constexpr uint64_t kThreadSuspendTimeoutMs = 30 * 1000;  // 30s.
 // Use 0 since we want to yield to prevent blocking for an unpredictable amount of time.
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index d9d2ea3..d9179c3 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -21,6 +21,8 @@
 #include <sys/time.h>
 #include <sys/resource.h>
 
+#include "android-base/stringprintf.h"
+
 #include "base/bit_utils.h"
 #include "base/casts.h"
 #include "base/logging.h"
@@ -31,6 +33,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static constexpr bool kMeasureWaitTime = false;
 
 ThreadPoolWorker::ThreadPoolWorker(ThreadPool* thread_pool, const std::string& name,
@@ -86,6 +90,8 @@
   Runtime* runtime = Runtime::Current();
   CHECK(runtime->AttachCurrentThread(worker->name_.c_str(), true, nullptr, false));
   worker->thread_ = Thread::Current();
+  // Thread pool workers cannot call into java.
+  worker->thread_->SetCanCallIntoJava(false);
   // Do work until its time to shut down.
   worker->Run();
   runtime->DetachCurrentThread();
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index eaadfe0..7ecfcd1 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -80,6 +80,7 @@
   DISALLOW_COPY_AND_ASSIGN(ThreadPoolWorker);
 };
 
+// Note that thread pool workers will set Thread#setCanCallIntoJava to false.
 class ThreadPool {
  public:
   // Returns the number of threads in the thread pool.
diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc
index d21ff77..0bba44c 100644
--- a/runtime/ti/agent.cc
+++ b/runtime/ti/agent.cc
@@ -15,12 +15,17 @@
  */
 
 #include "agent.h"
+
+#include "android-base/stringprintf.h"
+
 #include "java_vm_ext.h"
 #include "runtime.h"
 
 namespace art {
 namespace ti {
 
+using android::base::StringPrintf;
+
 const char* AGENT_ON_LOAD_FUNCTION_NAME = "Agent_OnLoad";
 const char* AGENT_ON_ATTACH_FUNCTION_NAME = "Agent_OnAttach";
 const char* AGENT_ON_UNLOAD_FUNCTION_NAME = "Agent_OnUnload";
diff --git a/runtime/ti/agent.h b/runtime/ti/agent.h
index 6561756..7408aee 100644
--- a/runtime/ti/agent.h
+++ b/runtime/ti/agent.h
@@ -20,7 +20,6 @@
 #include <dlfcn.h>
 #include <jni.h>  // for jint, JavaVM* etc declarations
 
-#include "base/stringprintf.h"
 #include "runtime.h"
 #include "utils.h"
 
diff --git a/runtime/trace.cc b/runtime/trace.cc
index f564de4..9d9360e 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -19,6 +19,8 @@
 #include <sys/uio.h>
 #include <unistd.h>
 
+#include "android-base/stringprintf.h"
+
 #include "art_method-inl.h"
 #include "base/casts.h"
 #include "base/enums.h"
@@ -46,6 +48,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 static constexpr size_t TraceActionBits = MinimumBitsToStore(
     static_cast<size_t>(kTraceMethodActionMask));
 static constexpr uint8_t kOpNewMethod = 1U;
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 4732f59..8867743 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -25,6 +25,7 @@
 #include <unistd.h>
 #include <memory>
 
+#include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
 #include "base/stl_util.h"
@@ -48,6 +49,9 @@
 
 namespace art {
 
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
 static const uint8_t kBase64Map[256] = {
   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
diff --git a/runtime/utils.h b/runtime/utils.h
index 04e0dde..16ef706 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -284,6 +284,12 @@
   __builtin___clear_cache(begin, end);
 }
 
+inline void FlushDataCache(char* begin, char* end) {
+  // Same as FlushInstructionCache for lack of other builtin. __builtin___clear_cache
+  // flushes both caches.
+  __builtin___clear_cache(begin, end);
+}
+
 template <typename T>
 constexpr PointerSize ConvertToPointerSize(T any) {
   if (any == 4 || any == 8) {
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index ebecc85..715b237 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -18,6 +18,8 @@
 
 #include <iostream>
 
+#include "android-base/stringprintf.h"
+
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "base/enums.h"
@@ -55,6 +57,8 @@
 namespace art {
 namespace verifier {
 
+using android::base::StringPrintf;
+
 static constexpr bool kTimeVerifyMethod = !kIsDebugBuild;
 static constexpr bool kDebugVerify = false;
 // TODO: Add a constant to method_verifier to turn on verbose logging?
@@ -417,6 +421,19 @@
     if (method != nullptr) {
       if (verifier.HasInstructionThatWillThrow()) {
         method->AddAccessFlags(kAccCompileDontBother);
+        if (Runtime::Current()->IsAotCompiler() &&
+            (callbacks != nullptr) && !callbacks->IsBootImage()) {
+          // When compiling apps, make HasInstructionThatWillThrow a soft error to trigger
+          // re-verification at runtime.
+          // The dead code after the throw is not verified and might be invalid. This may cause
+          // the JIT compiler to crash since it assumes that all the code is valid.
+          //
+          // There's a strong assumption that the entire boot image is verified and all its dex
+          // code is valid (even the dead and unverified one). As such this is done only for apps.
+          // (CompilerDriver DCHECKs in VerifyClassVisitor that methods from boot image are
+          // fully verified).
+          result.kind = kSoftFailure;
+        }
       }
       if ((verifier.encountered_failure_types_ & VerifyError::VERIFY_ERROR_LOCKING) != 0) {
         method->AddAccessFlags(kAccMustCountLocks);
diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h
index 10f1be5..aa4a259 100644
--- a/runtime/verifier/reg_type-inl.h
+++ b/runtime/verifier/reg_type-inl.h
@@ -112,7 +112,8 @@
       } else if (lhs.HasClass() && rhs.HasClass()) {
         // Test assignability from the Class point-of-view.
         bool result = lhs.GetClass()->IsAssignableFrom(rhs.GetClass());
-        // Record assignability dependency. The `verifier` is null during unit tests.
+        // Record assignability dependency. The `verifier` is null during unit tests and
+        // VerifiedMethod::GenerateSafeCastSet.
         if (verifier != nullptr) {
           VerifierDeps::MaybeRecordAssignability(
               verifier->GetDexFile(), lhs.GetClass(), rhs.GetClass(), strict, result);
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index ab23773..52f7e34 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -16,6 +16,8 @@
 
 #include "reg_type-inl.h"
 
+#include "android-base/stringprintf.h"
+
 #include "base/arena_bit_vector.h"
 #include "base/bit_vector-inl.h"
 #include "base/casts.h"
@@ -35,6 +37,8 @@
 namespace art {
 namespace verifier {
 
+using android::base::StringPrintf;
+
 const UndefinedType* UndefinedType::instance_ = nullptr;
 const ConflictType* ConflictType::instance_ = nullptr;
 const BooleanType* BooleanType::instance_ = nullptr;
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index a6088aa..383d890 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -16,7 +16,8 @@
 
 #include "register_line.h"
 
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
 #include "dex_instruction-inl.h"
 #include "method_verifier-inl.h"
 #include "register_line-inl.h"
@@ -25,6 +26,8 @@
 namespace art {
 namespace verifier {
 
+using android::base::StringPrintf;
+
 bool RegisterLine::CheckConstructorReturn(MethodVerifier* verifier) const {
   if (kIsDebugBuild && this_initialized_) {
     // Ensure that there is no UninitializedThisReference type anymore if this_initialized_ is true.
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index f9bff23..5f94a1b 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -249,11 +249,12 @@
   if (dex_cache == nullptr) {
     // This is a synthesized class, in this case always an array. They are not
     // defined in the compiled DEX files and therefore are part of the classpath.
-    // We could avoid recording dependencies on arrays with component types in
-    // the compiled DEX files but we choose to record them anyway so as to
-    // record the access flags VM sets for array classes.
+    // We do not record dependencies on arrays with component types in
+    // the compiled DEX files, as the only thing that might change is their
+    // access flags. If we were to change these flags in a breaking way, we would
+    // need to enforce full verification again anyways by updating the vdex version.
     DCHECK(klass->IsArrayClass()) << klass->PrettyDescriptor();
-    return true;
+    return false;
   }
 
   const DexFile* dex_file = dex_cache->GetDexFile();
@@ -345,8 +346,21 @@
   // merely on no issues with linking (valid access flags, superclass and
   // implemented interfaces). If the class at any point reached the IsResolved
   // status, the requirement holds. This is guaranteed by RegTypeCache::ResolveClass.
-  DCHECK(destination != nullptr && !destination->IsPrimitive());
-  DCHECK(source != nullptr && !source->IsPrimitive());
+  DCHECK(destination != nullptr);
+  DCHECK(source != nullptr);
+
+  if (destination->IsPrimitive() || source->IsPrimitive()) {
+    // Primitive types are trivially non-assignable to anything else.
+    // We do not need to record trivial assignability, as it will
+    // not change across releases.
+    return;
+  }
+
+  if (source->IsObjectClass() && !is_assignable) {
+    // j.l.Object is trivially non-assignable to other types, don't
+    // record it.
+    return;
+  }
 
   if (destination == source ||
       destination->IsObjectClass() ||
@@ -389,6 +403,21 @@
     return;
   }
 
+  if (!IsInClassPath(source) && !source->IsInterface() && !destination->IsInterface()) {
+    // Find the super class at the classpath boundary. Only that class
+    // can change the assignability.
+    // TODO: also chase the boundary for interfaces.
+    do {
+      source = source->GetSuperClass();
+    } while (!IsInClassPath(source));
+
+    // If that class is the actual destination, no need to record it.
+    if (source == destination) {
+      return;
+    }
+  }
+
+
   // Get string IDs for both descriptors and store in the appropriate set.
   dex::StringIndex destination_id = GetClassDescriptorStringId(dex_file, destination);
   dex::StringIndex source_id = GetClassDescriptorStringId(dex_file, source);
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 3549586..009170c 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -20,6 +20,8 @@
 
 #include <sstream>
 
+#include "android-base/stringprintf.h"
+
 #include "base/logging.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "jni_internal.h"
@@ -189,7 +191,7 @@
 static jmethodID CachePrimitiveBoxingMethod(JNIEnv* env, char prim_name, const char* boxed_name) {
   ScopedLocalRef<jclass> boxed_class(env, env->FindClass(boxed_name));
   return CacheMethod(env, boxed_class.get(), true, "valueOf",
-                     StringPrintf("(%c)L%s;", prim_name, boxed_name).c_str());
+                     android::base::StringPrintf("(%c)L%s;", prim_name, boxed_name).c_str());
 }
 
 #define STRING_INIT_LIST(V) \
diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc
index d96fb42..cd79bb6 100644
--- a/runtime/zip_archive.cc
+++ b/runtime/zip_archive.cc
@@ -23,7 +23,6 @@
 #include <unistd.h>
 #include <vector>
 
-#include "base/stringprintf.h"
 #include "base/unix_file/fd_file.h"
 
 namespace art {
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 113b35f..3b237f4 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -25,11 +25,11 @@
 
 #include "jni.h"
 
+#include "android-base/stringprintf.h"
 #include <backtrace/Backtrace.h>
 
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/stringprintf.h"
 #include "gc/heap.h"
 #include "gc/space/image_space.h"
 #include "oat_file.h"
@@ -91,7 +91,7 @@
 static void MoreErrorInfo(pid_t pid, bool sig_quit_on_fail) {
   printf("Secondary pid is %d\n", pid);
 
-  PrintFileToLog(StringPrintf("/proc/%d/maps", pid), ::android::base::ERROR);
+  PrintFileToLog(android::base::StringPrintf("/proc/%d/maps", pid), ::android::base::ERROR);
 
   if (sig_quit_on_fail) {
     int res = kill(pid, SIGQUIT);
diff --git a/test/141-class-unload/expected.txt b/test/141-class-unload/expected.txt
index 0a03ecb..a1c5fa8 100644
--- a/test/141-class-unload/expected.txt
+++ b/test/141-class-unload/expected.txt
@@ -20,5 +20,6 @@
 JNI_OnLoad called
 class null false test
 JNI_OnUnload called
+JNI_OnLoad called
 Number of loaded unload-ex maps 0
 Too small false
diff --git a/test/141-class-unload/jni_unload.cc b/test/141-class-unload/jni_unload.cc
index bbbb0a6..9b7e171 100644
--- a/test/141-class-unload/jni_unload.cc
+++ b/test/141-class-unload/jni_unload.cc
@@ -32,5 +32,19 @@
   }
 }
 
+extern "C" JNIEXPORT void JNICALL Java_Main_stopJit(JNIEnv*, jclass) {
+  jit::Jit* jit = Runtime::Current()->GetJit();
+  if (jit != nullptr) {
+    jit->Stop();
+  }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_startJit(JNIEnv*, jclass) {
+  jit::Jit* jit = Runtime::Current()->GetJit();
+  if (jit != nullptr) {
+    jit->Start();
+  }
+}
+
 }  // namespace
 }  // namespace art
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index 2a6e944..595c70d 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -55,11 +55,15 @@
     }
 
     private static void testOatFilesUnloaded(int pid) throws Exception {
+        System.loadLibrary(nativeLibraryName);
+        // Stop the JIT to ensure its threads and work queue are not keeping classes
+        // artifically alive.
+        stopJit();
+        Runtime.getRuntime().gc();
+        System.runFinalization();
         BufferedReader reader = new BufferedReader(new FileReader ("/proc/" + pid + "/maps"));
         String line;
         int count = 0;
-        Runtime.getRuntime().gc();
-        System.runFinalization();
         while ((line = reader.readLine()) != null) {
             if (line.contains("@141-class-unload-ex.jar")) {
                 System.out.println(line);
@@ -67,6 +71,7 @@
             }
         }
         System.out.println("Number of loaded unload-ex maps " + count);
+        startJit();
     }
 
     private static void stressTest(Constructor<?> constructor) throws Exception {
@@ -229,4 +234,7 @@
     private static int getPid() throws Exception {
       return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
     }
+
+    public static native void stopJit();
+    public static native void startJit();
 }
diff --git a/test/153-reference-stress/expected.txt b/test/153-reference-stress/expected.txt
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/test/153-reference-stress/expected.txt
@@ -0,0 +1 @@
+PASS
diff --git a/test/153-reference-stress/info.txt b/test/153-reference-stress/info.txt
new file mode 100644
index 0000000..6bc0040
--- /dev/null
+++ b/test/153-reference-stress/info.txt
@@ -0,0 +1 @@
+Tests java.lang.ref.Reference.get() and GC running in parallel.
diff --git a/test/153-reference-stress/src/Main.java b/test/153-reference-stress/src/Main.java
new file mode 100644
index 0000000..fc6f9cc
--- /dev/null
+++ b/test/153-reference-stress/src/Main.java
@@ -0,0 +1,73 @@
+/*
+ * 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;
+
+public class Main {
+    static final int numWeakReferences = 16 * 1024;
+    static WeakReference[] weakReferences = new WeakReference[numWeakReferences];
+    static volatile boolean done = false;
+    static Object keepAlive;
+
+    public static void main(String[] args) throws Exception {
+        // Try to call Reference.get repeatedly while the GC is running.
+        Thread gcThread = new GcThread();
+        Thread[] readerThread = new ReaderThread[4];
+        for (int i = 0; i < readerThread.length; ++i) {
+            readerThread[i] = new ReaderThread();
+        }
+        gcThread.start();
+        for (int i = 0; i < readerThread.length; ++i) {
+            readerThread[i].start();
+        }
+        gcThread.join();
+        for (int i = 0; i < readerThread.length; ++i) {
+            readerThread[i].join();
+        }
+        System.out.println("PASS");
+    }
+
+    static class GcThread extends Thread {
+        GcThread() {
+            Object temp = new Object();
+            for (int j = 0; j < weakReferences.length; ++j) {
+                weakReferences[j] = new WeakReference(temp);
+            }
+        }
+        public void run() {
+            for (int i = 0; i < 1000; ++i) {
+                Object o = new Object();
+                for (int j = 0; j < weakReferences.length; ++j) {
+                    weakReferences[j] = new WeakReference(o);
+                }
+            }
+            done = true;
+        }
+    }
+
+    static class ReaderThread extends Thread {
+        public void run() {
+            while (!done) {
+                for (int j = 0; j < weakReferences.length; ++j) {
+                    keepAlive = weakReferences[j].get();
+                }
+                for (int j = 0; j < weakReferences.length; ++j) {
+                    weakReferences[j].clear();
+                }
+            }
+        }
+    }
+}
diff --git a/test/530-checker-loops3/src/Main.java b/test/530-checker-loops3/src/Main.java
index 209786a..dfc4a5f 100644
--- a/test/530-checker-loops3/src/Main.java
+++ b/test/530-checker-loops3/src/Main.java
@@ -235,6 +235,59 @@
     }
   }
 
+  /// CHECK-START: void Main.shortBound1(int[], short) BCE (before)
+  /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+  //
+  /// CHECK-START: void Main.shortBound1(int[], short) BCE (after)
+  /// CHECK-DAG: Deoptimize loop:none
+  /// CHECK-DAG: Deoptimize loop:none
+  /// CHECK-DAG: Deoptimize loop:none
+  /// CHECK-NOT: Deoptimize
+  //
+  /// CHECK-START: void Main.shortBound1(int[], short) BCE (after)
+  /// CHECK-NOT: BoundsCheck
+  public static void shortBound1(int[] array, short s) {
+    // Lower precision bound will appear in deopt arithmetic
+    // and follows normal implicit widening conversion.
+    for (int i = 0; i < s; i++) {
+      array[i] = 222;
+    }
+  }
+
+  /// CHECK-START: void Main.shortBound2(int[], short) BCE (before)
+  /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+  //
+  /// CHECK-START: void Main.shortBound2(int[], short) BCE (after)
+  /// CHECK-DAG: Deoptimize loop:none
+  /// CHECK-DAG: Deoptimize loop:none
+  /// CHECK-DAG: Deoptimize loop:none
+  /// CHECK-NOT: Deoptimize
+  //
+  /// CHECK-START: void Main.shortBound2(int[], short) BCE (after)
+  /// CHECK-NOT: BoundsCheck
+  public static void shortBound2(int[] array, short s) {
+    // Lower precision bound will appear in deopt arithmetic
+    // and follows normal implicit widening conversion.
+    for (int i = 0; s > i; i++) {
+      array[i] = 444;
+    }
+  }
+
+  /// CHECK-START: void Main.narrowingFromLong(int[], int) BCE (before)
+  /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+  //
+  /// CHECK-START: void Main.narrowingFromLong(int[], int) BCE (after)
+  /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+  public static void narrowingFromLong(int[] array, int n) {
+    // Parallel induction in long precision that is narrowed provides type
+    // conversion challenges for BCE in deopt arithmetic when combined
+    // with the int loop induction. Therefore, currently skipped.
+    long l = 0;
+    for (int i = 0; i < n; i++, l++) {
+      array[(int)l] = 888;
+    }
+  }
+
   //
   // Verifier.
   //
@@ -316,6 +369,38 @@
     } catch (ArrayIndexOutOfBoundsException e) {
     }
 
+    shortBound1(a, (short)a.length);
+    for (int i = 0; i < a.length; i++) {
+      expectEquals(222, a[i]);
+    }
+    shortBound2(a, (short)a.length);
+    for (int i = 0; i < a.length; i++) {
+      expectEquals(444, a[i]);
+    }
+
+    try {
+      shortBound1(a, (short)(a.length + 1));
+      throw new Error("Should throw AIOOBE");
+    } catch (ArrayIndexOutOfBoundsException e) {
+    }
+    for (int i = 0; i < a.length; i++) {
+      expectEquals(222, a[i]);
+    }
+
+    try {
+      shortBound2(a, (short)(a.length + 1));
+      throw new Error("Should throw AIOOBE");
+    } catch (ArrayIndexOutOfBoundsException e) {
+    }
+    for (int i = 0; i < a.length; i++) {
+      expectEquals(444, a[i]);
+    }
+
+    narrowingFromLong(a, a.length);
+    for (int i = 0; i < a.length; i++) {
+      expectEquals(888, a[i]);
+    }
+
     System.out.println("passed");
   }
 
diff --git a/test/530-checker-loops4/src/Main.java b/test/530-checker-loops4/src/Main.java
index 7d3d7d9..91af1f4 100644
--- a/test/530-checker-loops4/src/Main.java
+++ b/test/530-checker-loops4/src/Main.java
@@ -96,7 +96,29 @@
   /// CHECK-NOT: Phi
   public static int geo4(int a) {
     for (int i = 0; i < 10; i++) {
-      a %= 7;
+      a %= 7;  // a wrap-around induction
+    }
+    return a;
+  }
+
+  /// CHECK-START: int Main.geo5() loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Shr loop:<<Loop>>
+  //
+  /// CHECK-START: int Main.geo5() loop_optimization (after)
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0          loop:none
+  /// CHECK-DAG: <<Int1:i\d+>> IntConstant 2147483647 loop:none
+  /// CHECK-DAG: <<Int2:i\d+>> IntConstant 1024       loop:none
+  /// CHECK-DAG: <<Div:i\d+>>  Div [<<Int1>>,<<Int2>>] loop:none
+  /// CHECK-DAG: <<Add:i\d+>>  Add [<<Div>>,<<Zero>>]  loop:none
+  /// CHECK-DAG:               Return [<<Add>>]        loop:none
+  //
+  /// CHECK-START: int Main.geo5() loop_optimization (after)
+  /// CHECK-NOT: Phi
+  public static int geo5() {
+    int a = 0x7fffffff;
+    for (int i = 0; i < 10; i++) {
+      a >>= 1;
     }
     return a;
   }
@@ -186,7 +208,7 @@
     int r = 0;
     for (int i = 0; i < 100; i++) {  // a converges to 0
       r += x[a];
-      a %= 5;
+      a %= 5;  // a wrap-around induction
     }
     return r;
   }
@@ -305,6 +327,8 @@
       expectEquals(i % 7, geo4(i));
     }
 
+    expectEquals(0x1fffff, geo5());
+
     expectEquals(34,  geo1BCE());
     expectEquals(36,  geo2BCE());
     expectEquals(131, geo3BCE());
diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java
index 9e475ab..fe6ff13 100644
--- a/test/552-checker-sharpening/src/Main.java
+++ b/test/552-checker-sharpening/src/Main.java
@@ -55,6 +55,9 @@
   /// CHECK-NOT:            MipsDexCacheArraysBase
   /// CHECK:                InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
 
+  /// CHECK-START-MIPS64: int Main.testSimple(int) sharpening (after)
+  /// CHECK:                InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
+
   /// CHECK-START-X86: int Main.testSimple(int) sharpening (after)
   /// CHECK-NOT:            X86ComputeBaseMethodAddress
   /// CHECK:                InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
@@ -96,6 +99,10 @@
   /// CHECK:                InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
   /// CHECK:                InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
 
+  /// CHECK-START-MIPS64: int Main.testDiamond(boolean, int) sharpening (after)
+  /// CHECK:                InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
+  /// CHECK:                InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
+
   /// CHECK-START-X86: int Main.testDiamond(boolean, int) sharpening (after)
   /// CHECK-NOT:            X86ComputeBaseMethodAddress
   /// CHECK:                InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
@@ -274,6 +281,11 @@
   // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress.
   /// CHECK:                LoadString load_kind:{{BootImageAddress|BssEntry|DexCacheViaMethod}}
 
+  /// CHECK-START-MIPS64: java.lang.String Main.$noinline$getBootImageString() sharpening (after)
+  // Note: load kind depends on PIC/non-PIC
+  // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress.
+  /// CHECK:                LoadString load_kind:{{BootImageAddress|BssEntry|DexCacheViaMethod}}
+
   public static String $noinline$getBootImageString() {
     // Prevent inlining to avoid the string comparison being optimized away.
     if (doThrow) { throw new Error(); }
@@ -303,6 +315,9 @@
   /// CHECK-START-MIPS: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after)
   /// CHECK:                LoadString load_kind:BssEntry
 
+  /// CHECK-START-MIPS64: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after)
+  /// CHECK:                LoadString load_kind:BssEntry
+
   public static String $noinline$getNonBootImageString() {
     // Prevent inlining to avoid the string comparison being optimized away.
     if (doThrow) { throw new Error(); }
@@ -338,6 +353,11 @@
   // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress.
   /// CHECK:                LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String
 
+  /// CHECK-START-MIPS64: java.lang.Class Main.$noinline$getStringClass() sharpening (after)
+  // Note: load kind depends on PIC/non-PIC
+  // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress.
+  /// CHECK:                LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String
+
   public static Class<?> $noinline$getStringClass() {
     // Prevent inlining to avoid the string comparison being optimized away.
     if (doThrow) { throw new Error(); }
@@ -375,6 +395,9 @@
   /// CHECK-DAG:            MipsDexCacheArraysBase
   /// CHECK-DAG:            LoadClass load_kind:DexCachePcRelative class_name:Other
 
+  /// CHECK-START-MIPS64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after)
+  /// CHECK:                LoadClass load_kind:DexCachePcRelative class_name:Other
+
   public static Class<?> $noinline$getOtherClass() {
     // Prevent inlining to avoid the string comparison being optimized away.
     if (doThrow) { throw new Error(); }
diff --git a/test/616-cha-regression-proxy-method/expected.txt b/test/616-cha-regression-proxy-method/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/616-cha-regression-proxy-method/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/616-cha-regression-proxy-method/info.txt b/test/616-cha-regression-proxy-method/info.txt
new file mode 100644
index 0000000..386a07f
--- /dev/null
+++ b/test/616-cha-regression-proxy-method/info.txt
@@ -0,0 +1 @@
+Regression test for Class Hierarchy Analysis (CHA) on visiting proxy method frame.
diff --git a/test/616-cha-regression-proxy-method/src/Main.java b/test/616-cha-regression-proxy-method/src/Main.java
new file mode 100644
index 0000000..19c92be
--- /dev/null
+++ b/test/616-cha-regression-proxy-method/src/Main.java
@@ -0,0 +1,131 @@
+/*
+ * 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.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+class Main1 {
+  void foo(int i) {
+    if (i != 1) {
+      printError("error1");
+    }
+  }
+
+  void printError(String msg) {
+    System.out.println(msg);
+  }
+}
+
+class Main2 extends Main1 {
+  void foo(int i) {
+    if (i != 2) {
+      printError("error2");
+    }
+  }
+}
+
+class Proxied implements Runnable {
+  public void run() {
+    synchronized(Main.class) {
+      Main.sOtherThreadStarted = true;
+      // Wait for Main2 to be linked and deoptimization is triggered.
+      try {
+        Main.class.wait();
+      } catch (Exception e) {
+      }
+    }
+  }
+}
+
+class MyInvocationHandler implements InvocationHandler {
+  private final Proxied proxied;
+
+  public MyInvocationHandler(Proxied proxied) {
+    this.proxied = proxied;
+  }
+
+  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+    return method.invoke(proxied, args);
+  }
+}
+
+public class Main {
+  static Main1 sMain1;
+  static Main1 sMain2;
+  static volatile boolean sOtherThreadStarted;
+
+  // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+  // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+  // After Dummy.createMain2() which links in Main2, live testOverride() on stack
+  // should be deoptimized.
+  static void testOverride() {
+    sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+
+    // Wait for the other thread to start.
+    while (!sOtherThreadStarted);
+    // Create an Main2 instance and assign it to sMain2.
+    // sMain1 is kept the same.
+    sMain2 = Dummy.createMain2();
+    // Wake up the other thread.
+    synchronized(Main.class) {
+      Main.class.notify();
+    }
+
+    // There should be a deoptimization here right after Main2 is linked by
+    // calling Dummy.createMain2(), even though sMain1 didn't change.
+    // The behavior here would be different if inline-cache is used, which
+    // doesn't deoptimize since sMain1 still hits the type cache.
+    sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+    if (sMain2 != null) {
+      sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+    }
+  }
+
+  // Test scenarios under which CHA-based devirtualization happens,
+  // and class loading that overrides a method can invalidate compiled code.
+  // Also create a proxy method such that a proxy method's frame is visited
+  // during stack walking.
+  public static void main(String[] args) {
+    System.loadLibrary(args[0]);
+    // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+    sMain1 = new Main1();
+
+    // Create another thread that calls a proxy method.
+    new Thread() {
+      public void run() {
+        Runnable proxy = (Runnable)Proxy.newProxyInstance(
+            Proxied.class.getClassLoader(),
+            new Class[] { Runnable.class },
+            new MyInvocationHandler(new Proxied()));
+        proxy.run();
+      }
+    }.start();
+
+    ensureJitCompiled(Main.class, "testOverride");
+    // This will create Main2 instance in the middle of testOverride().
+    testOverride();
+  }
+
+  private static native void ensureJitCompiled(Class<?> itf, String method_name);
+}
+
+// Put createMain2() in another class to avoid class loading due to verifier.
+class Dummy {
+  static Main1 createMain2() {
+    return new Main2();
+  }
+}
diff --git a/test/Android.arm_vixl.mk b/test/616-cha/run
similarity index 72%
rename from test/Android.arm_vixl.mk
rename to test/616-cha/run
index c89eb4a..9c64c7d 100644
--- a/test/Android.arm_vixl.mk
+++ b/test/616-cha/run
@@ -1,3 +1,4 @@
+#!/bin/bash
 #
 # Copyright (C) 2016 The Android Open Source Project
 #
@@ -5,16 +6,13 @@
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at
 #
-#      http://www.apache.org/licenses/LICENSE-2.0
+#     http://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # 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.
-#
 
-# Known broken tests for the ARM VIXL backend.
-TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
-  562-checker-no-intermediate \
-  624-checker-stringops \
+# Run without an app image to prevent the classes to be loaded at startup.
+exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha/src/Main.java b/test/616-cha/src/Main.java
index 787318d..b617944 100644
--- a/test/616-cha/src/Main.java
+++ b/test/616-cha/src/Main.java
@@ -179,7 +179,7 @@
     }
   }
 
-  // Test scanerios under which CHA-based devirtualization happens,
+  // Test scenarios under which CHA-based devirtualization happens,
   // and class loading that overrides a method can invalidate compiled code.
   // Also test pure non-overriding case, which is more for checking generated
   // code form.
@@ -206,11 +206,6 @@
     // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
     sMain1 = new Main1();
 
-    // Loop enough to get testOverride() JITed.
-    for (int i=0; i<100; i++) {
-      testOverride(false, false, false);
-    }
-
     ensureJitCompiled(Main.class, "testOverride");
     testOverride(false, false, true);
 
@@ -244,7 +239,7 @@
   private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
 }
 
-// Do it in another class to avoid class loading due to verifier.
+// Put createMain2() in another class to avoid class loading due to verifier.
 class Dummy {
   static Main1 createMain2() {
     return new Main2();
diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java
index 87a69b2..ad3ff44 100644
--- a/test/618-checker-induction/src/Main.java
+++ b/test/618-checker-induction/src/Main.java
@@ -248,6 +248,33 @@
     return closed;  // only needs last value
   }
 
+  /// CHECK-START: int Main.closedFormInductionTrivialIf() loop_optimization (before)
+  /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               Select            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               Return [<<Phi1>>] loop:none
+  //
+  /// CHECK-START: int Main.closedFormInductionTrivialIf() loop_optimization (after)
+  /// CHECK-NOT:               Phi
+  /// CHECK-NOT:               Select
+  //
+  /// CHECK-START: int Main.closedFormInductionTrivialIf() instruction_simplifier$after_bce (after)
+  /// CHECK-DAG: <<Int:i\d+>>  IntConstant 81    loop:none
+  /// CHECK-DAG:               Return [<<Int>>]  loop:none
+  static int closedFormInductionTrivialIf() {
+    int closed = 11;
+    for (int i = 0; i < 10; i++) {
+      // Trivial if becomes trivial select at HIR level.
+      // Make sure this is still recognized as induction.
+      if (i < 5) {
+        closed += 7;
+      } else {
+        closed += 7;
+      }
+    }
+    return closed;  // only needs last value
+  }
+
   /// CHECK-START: int Main.closedFormNested() loop_optimization (before)
   /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop1:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop1>>      outer_loop:none
@@ -438,6 +465,20 @@
     return i;
   }
 
+  // TODO: handle as closed/empty eventually?
+  static int mainIndexShort1(short s) {
+    int i = 0;
+    for (i = 0; i < s; i++) { }
+    return i;
+  }
+
+  // TODO: handle as closed/empty eventually?
+  static int mainIndexShort2(short s) {
+    int i = 0;
+    for (i = 0; s > i; i++) { }
+    return i;
+  }
+
   /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (before)
   /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop>>      outer_loop:none
@@ -666,6 +707,75 @@
     return x;
   }
 
+  /// CHECK-START: float Main.periodicFloat10() loop_optimization (before)
+  /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi3:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi4:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
+  //
+  /// CHECK-START: float Main.periodicFloat10() loop_optimization (after)
+  /// CHECK-NOT: Phi
+  //
+  /// CHECK-START: float Main.periodicFloat10() loop_optimization (after)
+  /// CHECK-DAG: <<Float:f\d+>>  FloatConstant 2    loop:none
+  /// CHECK-DAG:                 Return [<<Float>>] loop:none
+  private static float periodicFloat10() {
+    float r = 4.5f;
+    float s = 2.0f;
+    float t = -1.0f;
+    for (int i = 0; i < 10; i++) {
+      float tmp = t; t = r; r = s; s = tmp;
+    }
+    return r;
+  }
+
+  /// CHECK-START: float Main.periodicFloat11() loop_optimization (before)
+  /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi3:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi4:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
+  //
+  /// CHECK-START: float Main.periodicFloat11() loop_optimization (after)
+  /// CHECK-NOT: Phi
+  //
+  /// CHECK-START: float Main.periodicFloat11() loop_optimization (after)
+  /// CHECK-DAG: <<Float:f\d+>>  FloatConstant -1   loop:none
+  /// CHECK-DAG:                 Return [<<Float>>] loop:none
+  private static float periodicFloat11() {
+    float r = 4.5f;
+    float s = 2.0f;
+    float t = -1.0f;
+    for (int i = 0; i < 11; i++) {
+      float tmp = t; t = r; r = s; s = tmp;
+    }
+    return r;
+  }
+
+  /// CHECK-START: float Main.periodicFloat12() loop_optimization (before)
+  /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi3:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Phi4:f\d+>> Phi               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               Return [<<Phi2>>] loop:none
+  //
+  /// CHECK-START: float Main.periodicFloat12() loop_optimization (after)
+  /// CHECK-NOT: Phi
+  //
+  /// CHECK-START: float Main.periodicFloat12() loop_optimization (after)
+  /// CHECK-DAG: <<Float:f\d+>>  FloatConstant 4.5  loop:none
+  /// CHECK-DAG:                 Return [<<Float>>] loop:none
+  private static float periodicFloat12() {
+    float r = 4.5f;
+    float s = 2.0f;
+    float t = -1.0f;
+    for (int i = 0; i < 12; i++) {
+      float tmp = t; t = r; r = s; s = tmp;
+    }
+    return r;
+  }
+
   private static int exceptionExitBeforeAdd() {
     int k = 0;
     try {
@@ -732,6 +842,7 @@
 
     expectEquals(12395, closedFormInductionUp());
     expectEquals(12295, closedFormInductionInAndDown(12345));
+    expectEquals(81, closedFormInductionTrivialIf());
     expectEquals(10 * 10, closedFormNested());
     expectEquals(12345 + 17 * 23 * 7, closedFormNestedAlt());
     for (int n = -4; n < 10; n++) {
@@ -751,6 +862,8 @@
     for (int n = -4; n < 4; n++) {
       int tc = (n <= 0) ? 0 : n;
       expectEquals(tc, mainIndexReturnedN(n));
+      expectEquals(tc, mainIndexShort1((short) n));
+      expectEquals(tc, mainIndexShort2((short) n));
       expectEquals(tc & 1, periodicReturnedN(n));
       expectEquals((tc * (tc + 1)) / 2, getSumN(n));
     }
@@ -775,6 +888,10 @@
       expectEquals(!even, periodicBoolIdiom3N(false, n));
     }
 
+    expectEquals( 2.0f, periodicFloat10());
+    expectEquals(-1.0f, periodicFloat11());
+    expectEquals( 4.5f, periodicFloat12());
+
     expectEquals(100, exceptionExitBeforeAdd());
     expectEquals(100, exceptionExitAfterAdd());
     a = null;
@@ -787,6 +904,12 @@
     System.out.println("passed");
   }
 
+  private static void expectEquals(float expected, float result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
   private static void expectEquals(int expected, int result) {
     if (expected != result) {
       throw new Error("Expected: " + expected + ", found: " + result);
diff --git a/test/623-checker-loop-regressions/src/Main.java b/test/623-checker-loop-regressions/src/Main.java
index ce5bda1..7cc0b8b 100644
--- a/test/623-checker-loop-regressions/src/Main.java
+++ b/test/623-checker-loop-regressions/src/Main.java
@@ -82,6 +82,88 @@
     return 0;
   }
 
+  // Regression test for b/33774618: transfer operations involving
+  // narrowing linear induction should be done correctly.
+  //
+  /// CHECK-START: int Main.transferNarrowWrap() loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: Phi loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: Phi loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: Phi loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: int Main.transferNarrowWrap() loop_optimization (after)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: Phi loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: Phi loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: Phi loop:<<Loop>>      outer_loop:none
+  static int transferNarrowWrap() {
+    short x = 0;
+    int w = 10;
+    int v = 3;
+    for (int i = 0; i < 10; i++) {
+      v = w + 1;    // transfer on wrap-around
+      w = x;   // wrap-around
+      x += 2;  // narrowing linear
+    }
+    return v;
+  }
+
+  // Regression test for b/33774618: transfer operations involving
+  // narrowing linear induction should be done correctly
+  // (currently rejected, could be improved).
+  //
+  /// CHECK-START: int Main.polynomialShort() loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: Phi loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: int Main.polynomialShort() loop_optimization (after)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: Phi loop:<<Loop>>      outer_loop:none
+  static int polynomialShort() {
+    int x = 0;
+    for (short i = 0; i < 10; i++) {
+      x = x - i;  // polynomial on narrowing linear
+    }
+    return x;
+  }
+
+  // Regression test for b/33774618: transfer operations involving
+  // narrowing linear induction should be done correctly
+  // (currently rejected, could be improved).
+  //
+  /// CHECK-START: int Main.polynomialIntFromLong() loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: Phi loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: int Main.polynomialIntFromLong() loop_optimization (after)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: Phi loop:<<Loop>>      outer_loop:none
+  static int polynomialIntFromLong() {
+    int x = 0;
+    for (long i = 0; i < 10; i++) {
+      x = x - (int) i;  // polynomial on narrowing linear
+    }
+    return x;
+  }
+
+  /// CHECK-START: int Main.polynomialInt() loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: Phi loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: int Main.polynomialInt() loop_optimization (after)
+  /// CHECK-NOT: Phi
+  //
+  /// CHECK-START: int Main.polynomialInt() instruction_simplifier$after_bce (after)
+  /// CHECK-DAG: <<Int:i\d+>>  IntConstant -45 loop:none
+  /// CHECK-DAG:               Return [<<Int>>]  loop:none
+  static int polynomialInt() {
+    int x = 0;
+    for (int i = 0; i < 10; i++) {
+      x = x - i;
+    }
+    return x;
+  }
+
   public static void main(String[] args) {
     expectEquals(10, earlyExitFirst(-1));
     for (int i = 0; i <= 10; i++) {
@@ -98,6 +180,11 @@
 
     expectEquals(2, earlyExitNested());
 
+    expectEquals(17, transferNarrowWrap());
+    expectEquals(-45, polynomialShort());
+    expectEquals(-45, polynomialIntFromLong());
+    expectEquals(-45, polynomialInt());
+
     System.out.println("passed");
   }
 
diff --git a/test/624-checker-stringops/src/Main.java b/test/624-checker-stringops/src/Main.java
index d965e3f..75b782e 100644
--- a/test/624-checker-stringops/src/Main.java
+++ b/test/624-checker-stringops/src/Main.java
@@ -258,6 +258,20 @@
     return b.length();
   }
 
+  // Regression b/33656359: StringBuffer x is passed to constructor of String
+  // (this caused old code to crash due to missing nullptr check).
+  //
+  /// CHECK-START: void Main.doesNothing() instruction_simplifier (before)
+  /// CHECK-DAG: InvokeVirtual intrinsic:StringBufferToString
+  //
+  /// CHECK-START: void Main.doesNothing() instruction_simplifier (after)
+  /// CHECK-DAG: InvokeVirtual intrinsic:StringBufferToString
+  static void doesNothing() {
+    StringBuffer x = new StringBuffer();
+    String y = new String(x);
+    x.toString();
+  }
+
   public static void main(String[] args) {
     expectEquals(1865, liveIndexOf());
     expectEquals(29, deadIndexOf());
@@ -281,6 +295,8 @@
     expectEquals(0, bufferDeadLoop());
     expectEquals(0, builderDeadLoop());
 
+    doesNothing();
+
     System.out.println("passed");
   }
 
diff --git a/test/626-checker-arm64-scratch-register/expected.txt b/test/626-checker-arm64-scratch-register/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/626-checker-arm64-scratch-register/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/626-checker-arm64-scratch-register/info.txt b/test/626-checker-arm64-scratch-register/info.txt
new file mode 100644
index 0000000..8472131
--- /dev/null
+++ b/test/626-checker-arm64-scratch-register/info.txt
@@ -0,0 +1,2 @@
+Regression test checking that the ARM64 scratch register pool is not
+exhausted during moves between stack slots (b/32545705).
diff --git a/test/626-checker-arm64-scratch-register/src/Main.java b/test/626-checker-arm64-scratch-register/src/Main.java
new file mode 100644
index 0000000..aa211be
--- /dev/null
+++ b/test/626-checker-arm64-scratch-register/src/Main.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+  boolean b00;
+  boolean b01;
+  boolean b02;
+  boolean b03;
+  boolean b04;
+  boolean b05;
+  boolean b06;
+  boolean b07;
+  boolean b08;
+  boolean b09;
+  boolean b10;
+  boolean b11;
+  boolean b12;
+  boolean b13;
+  boolean b14;
+  boolean b15;
+  boolean b16;
+  boolean b17;
+  boolean b18;
+  boolean b19;
+  boolean b20;
+  boolean b21;
+  boolean b22;
+  boolean b23;
+  boolean b24;
+  boolean b25;
+  boolean b26;
+  boolean b27;
+  boolean b28;
+  boolean b29;
+  boolean b30;
+  boolean b31;
+  boolean b32;
+  boolean b33;
+  boolean b34;
+  boolean b35;
+  boolean b36;
+
+  boolean conditionA;
+  boolean conditionB;
+  boolean conditionC;
+
+  /// CHECK-START-ARM64: void Main.test() register (after)
+  /// CHECK: begin_block
+  /// CHECK:   name "B0"
+  /// CHECK:       <<This:l\d+>>  ParameterValue
+  /// CHECK: end_block
+  /// CHECK: begin_block
+  /// CHECK:   successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>"
+  /// CHECK:       <<CondB:z\d+>>  InstanceFieldGet [<<This>>] field_name:Main.conditionB
+  /// CHECK:                       If [<<CondB>>]
+  /// CHECK:  end_block
+  /// CHECK: begin_block
+  /// CHECK:   name "<<ElseBlock>>"
+  /// CHECK:                      ParallelMove moves:[#100->d17,32(sp)->d1,36(sp)->d2,d17->d3,d3->d4,d4->d5,d5->d6,d6->d7,d7->d18,d18->d19,d19->d20,d20->d21,d21->d22,d22->d23,d23->d10,d10->d11,d11->d12,24(sp)->d13,28(sp)->d14,d14->16(sp),d12->20(sp),d13->24(sp),d1->28(sp),d2->32(sp),16(sp)->36(sp),20(sp)->40(sp)]
+  /// CHECK: end_block
+
+  /// CHECK-START-ARM64: void Main.test() disassembly (after)
+  /// CHECK: begin_block
+  /// CHECK:   name "B0"
+  /// CHECK:       <<This:l\d+>>  ParameterValue
+  /// CHECK: end_block
+  /// CHECK: begin_block
+  /// CHECK:   successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>"
+  /// CHECK:       <<CondB:z\d+>>  InstanceFieldGet [<<This>>] field_name:Main.conditionB
+  /// CHECK:                       If [<<CondB>>]
+  /// CHECK:  end_block
+  /// CHECK: begin_block
+  /// CHECK:   name "<<ElseBlock>>"
+  /// CHECK:                      ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid]
+  /// CHECK:                        fmov d31, d2
+  /// CHECK:                        ldr s2, [sp, #36]
+  /// CHECK:                        ldr w16, [sp, #16]
+  /// CHECK:                        str w16, [sp, #36]
+  /// CHECK:                        str s14, [sp, #16]
+  /// CHECK:                        ldr s14, [sp, #28]
+  /// CHECK:                        str s1, [sp, #28]
+  /// CHECK:                        ldr s1, [sp, #32]
+  /// CHECK:                        str s31, [sp, #32]
+  /// CHECK:                        ldr w16, [sp, #20]
+  /// CHECK:                        str w16, [sp, #40]
+  /// CHECK:                        str s12, [sp, #20]
+  /// CHECK:                        fmov d12, d11
+  /// CHECK:                        fmov d11, d10
+  /// CHECK:                        fmov d10, d23
+  /// CHECK:                        fmov d23, d22
+  /// CHECK:                        fmov d22, d21
+  /// CHECK:                        fmov d21, d20
+  /// CHECK:                        fmov d20, d19
+  /// CHECK:                        fmov d19, d18
+  /// CHECK:                        fmov d18, d7
+  /// CHECK:                        fmov d7, d6
+  /// CHECK:                        fmov d6, d5
+  /// CHECK:                        fmov d5, d4
+  /// CHECK:                        fmov d4, d3
+  /// CHECK:                        fmov d3, d17
+  /// CHECK:                        fmov d17, d13
+  /// CHECK:                        ldr s13, [sp, #24]
+  /// CHECK:                        str s17, [sp, #24]
+  /// CHECK:                        ldr s17, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100)
+  /// CHECK: end_block
+
+  public void test() {
+    String r = "";
+
+    // For the purpose of this regression test, the order of
+    // definition of these float variable matters.  Likewise with the
+    // order of the instructions where these variables are used below.
+    // Reordering these lines make make the original (b/32545705)
+    // issue vanish.
+    float f17 = b17 ? 0.0f : 1.0f;
+    float f16 = b16 ? 0.0f : 1.0f;
+    float f18 = b18 ? 0.0f : 1.0f;
+    float f19 = b19 ? 0.0f : 1.0f;
+    float f20 = b20 ? 0.0f : 1.0f;
+    float f21 = b21 ? 0.0f : 1.0f;
+    float f15 = b15 ? 0.0f : 1.0f;
+    float f00 = b00 ? 0.0f : 1.0f;
+    float f22 = b22 ? 0.0f : 1.0f;
+    float f23 = b23 ? 0.0f : 1.0f;
+    float f24 = b24 ? 0.0f : 1.0f;
+    float f25 = b25 ? 0.0f : 1.0f;
+    float f26 = b26 ? 0.0f : 1.0f;
+    float f27 = b27 ? 0.0f : 1.0f;
+    float f29 = b29 ? 0.0f : 1.0f;
+    float f28 = b28 ? 0.0f : 1.0f;
+    float f01 = b01 ? 0.0f : 1.0f;
+    float f02 = b02 ? 0.0f : 1.0f;
+    float f03 = b03 ? 0.0f : 1.0f;
+    float f04 = b04 ? 0.0f : 1.0f;
+    float f05 = b05 ? 0.0f : 1.0f;
+    float f07 = b07 ? 0.0f : 1.0f;
+    float f06 = b06 ? 0.0f : 1.0f;
+    float f30 = b30 ? 0.0f : 1.0f;
+    float f31 = b31 ? 0.0f : 1.0f;
+    float f32 = b32 ? 0.0f : 1.0f;
+    float f33 = b33 ? 0.0f : 1.0f;
+    float f34 = b34 ? 0.0f : 1.0f;
+    float f36 = b36 ? 0.0f : 1.0f;
+    float f35 = b35 ? 0.0f : 1.0f;
+    float f08 = b08 ? 0.0f : 1.0f;
+    float f09 = b09 ? 0.0f : 1.0f;
+    float f10 = b10 ? 0.0f : 1.0f;
+    float f11 = b11 ? 0.0f : 1.0f;
+    float f12 = b12 ? 0.0f : 1.0f;
+    float f14 = b14 ? 0.0f : 1.0f;
+    float f13 = b13 ? 0.0f : 1.0f;
+
+    if (conditionA) {
+      f16 /= 1000.0f;
+      f17 /= 1000.0f;
+      f18 /= 1000.0f;
+      f19 /= 1000.0f;
+      f20 /= 1000.0f;
+      f21 /= 1000.0f;
+      f15 /= 1000.0f;
+      f08 /= 1000.0f;
+      f09 /= 1000.0f;
+      f10 /= 1000.0f;
+      f11 /= 1000.0f;
+      f12 /= 1000.0f;
+      f30 /= 1000.0f;
+      f31 /= 1000.0f;
+      f32 /= 1000.0f;
+      f33 /= 1000.0f;
+      f34 /= 1000.0f;
+      f01 /= 1000.0f;
+      f02 /= 1000.0f;
+      f03 /= 1000.0f;
+      f04 /= 1000.0f;
+      f05 /= 1000.0f;
+      f23 /= 1000.0f;
+      f24 /= 1000.0f;
+      f25 /= 1000.0f;
+      f26 /= 1000.0f;
+      f27 /= 1000.0f;
+      f22 /= 1000.0f;
+      f00 /= 1000.0f;
+      f14 /= 1000.0f;
+      f13 /= 1000.0f;
+      f36 /= 1000.0f;
+      f35 /= 1000.0f;
+      f07 /= 1000.0f;
+      f06 /= 1000.0f;
+      f29 /= 1000.0f;
+      f28 /= 1000.0f;
+    }
+    // The parallel move that used to exhaust the ARM64 parallel move
+    // resolver's scratch register pool (provided by VIXL) was in the
+    // "else" branch of the following condition generated by ART's
+    // compiler.
+    if (conditionB) {
+      f16 /= 100.0f;
+      f17 /= 100.0f;
+      f18 /= 100.0f;
+      f19 /= 100.0f;
+      f20 /= 100.0f;
+      f21 /= 100.0f;
+      f15 /= 100.0f;
+      f08 /= 100.0f;
+      f09 /= 100.0f;
+      f10 /= 100.0f;
+      f11 /= 100.0f;
+      f12 /= 100.0f;
+      f30 /= 100.0f;
+      f31 /= 100.0f;
+      f32 /= 100.0f;
+      f33 /= 100.0f;
+      f34 /= 100.0f;
+      f01 /= 100.0f;
+      f02 /= 100.0f;
+      f03 /= 100.0f;
+      f04 /= 100.0f;
+      f05 /= 100.0f;
+      f23 /= 100.0f;
+      f24 /= 100.0f;
+      f25 /= 100.0f;
+      f26 /= 100.0f;
+      f27 /= 100.0f;
+      f22 /= 100.0f;
+      f00 /= 100.0f;
+      f14 /= 100.0f;
+      f13 /= 100.0f;
+      f36 /= 100.0f;
+      f35 /= 100.0f;
+      f07 /= 100.0f;
+      f06 /= 100.0f;
+      f29 /= 100.0f;
+      f28 /= 100.0f;
+    }
+    if (conditionC) {
+      f16 /= 12.0f;
+      f17 /= 12.0f;
+      f18 /= 12.0f;
+      f19 /= 12.0f;
+      f20 /= 12.0f;
+      f21 /= 12.0f;
+      f15 /= 12.0f;
+      f08 /= 12.0f;
+      f09 /= 12.0f;
+      f10 /= 12.0f;
+      f11 /= 12.0f;
+      f12 /= 12.0f;
+      f30 /= 12.0f;
+      f31 /= 12.0f;
+      f32 /= 12.0f;
+      f33 /= 12.0f;
+      f34 /= 12.0f;
+      f01 /= 12.0f;
+      f02 /= 12.0f;
+      f03 /= 12.0f;
+      f04 /= 12.0f;
+      f05 /= 12.0f;
+      f23 /= 12.0f;
+      f24 /= 12.0f;
+      f25 /= 12.0f;
+      f26 /= 12.0f;
+      f27 /= 12.0f;
+      f22 /= 12.0f;
+      f00 /= 12.0f;
+      f14 /= 12.0f;
+      f13 /= 12.0f;
+      f36 /= 12.0f;
+      f35 /= 12.0f;
+      f07 /= 12.0f;
+      f06 /= 12.0f;
+      f29 /= 12.0f;
+      f28 /= 12.0f;
+    }
+    float s = 0.0f;
+    s = ((float) Math.round(100.0f * s)) / 100.0f;
+    String res = s + r;
+  }
+
+  public static void main(String[] args) {
+    Main main = new Main();
+    main.test();
+    System.out.println("passed");
+  }
+}
diff --git a/test/628-vdex/run b/test/628-vdex/run
index f1b0a95..4cbcea3 100644
--- a/test/628-vdex/run
+++ b/test/628-vdex/run
@@ -14,4 +14,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-exec ${RUN} --vdex "${@}"
+exec ${RUN} -Xcompiler-option --compiler-filter=verify-profile --vdex "${@}"
diff --git a/test/630-safecast-array/expected.txt b/test/630-safecast-array/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/630-safecast-array/expected.txt
diff --git a/test/630-safecast-array/info.txt b/test/630-safecast-array/info.txt
new file mode 100644
index 0000000..e105167
--- /dev/null
+++ b/test/630-safecast-array/info.txt
@@ -0,0 +1,3 @@
+Regression test for vdex, which used to crash in AddAssignability
+called by the dex2dex compiler, not anticipating arrays of primitive
+type.
diff --git a/test/630-safecast-array/smali/Main.smali b/test/630-safecast-array/smali/Main.smali
new file mode 100644
index 0000000..a50f37c
--- /dev/null
+++ b/test/630-safecast-array/smali/Main.smali
@@ -0,0 +1,33 @@
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class LMain;
+.super Ljava/lang/Object;
+
+.method public static main([Ljava/lang/String;)V
+.registers 1
+    return-void
+.end method
+
+.method public static testPrimitiveDestination([Ljava/lang/String;)V
+.registers 1
+    check-cast p0, [B
+    return-void
+.end method
+
+.method public static testPrimitiveSource([B)V
+.registers 1
+    check-cast p0, [Ljava/lang/String;
+    return-void
+.end method
diff --git a/test/706-jit-skip-compilation/expected.txt b/test/706-jit-skip-compilation/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/706-jit-skip-compilation/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/706-jit-skip-compilation/info.txt b/test/706-jit-skip-compilation/info.txt
new file mode 100644
index 0000000..e9ef86b
--- /dev/null
+++ b/test/706-jit-skip-compilation/info.txt
@@ -0,0 +1,4 @@
+Regression test for the JIT crashing when compiling a method with invalid
+dead dex code. For not compilable methods we don't gather samples and we don't
+trigger JIT compilation. However kAccDontBotherCompile is not persisted in the
+oat file and so we may end up compiling a method which we shouldn't.
diff --git a/test/Android.arm_vixl.mk b/test/706-jit-skip-compilation/run
similarity index 69%
copy from test/Android.arm_vixl.mk
copy to test/706-jit-skip-compilation/run
index c89eb4a..6c5720a 100644
--- a/test/Android.arm_vixl.mk
+++ b/test/706-jit-skip-compilation/run
@@ -1,3 +1,4 @@
+#!/bin/bash
 #
 # Copyright (C) 2016 The Android Open Source Project
 #
@@ -5,16 +6,14 @@
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at
 #
-#      http://www.apache.org/licenses/LICENSE-2.0
+#     http://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # 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.
-#
 
-# Known broken tests for the ARM VIXL backend.
-TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
-  562-checker-no-intermediate \
-  624-checker-stringops \
+# Run without the app image, otherwise the verification results will be cached
+# in the ArtMethod of the image and the test will be skewed.
+exec ${RUN} "${@}" --no-app-image
diff --git a/test/706-jit-skip-compilation/smali/errclass.smali b/test/706-jit-skip-compilation/smali/errclass.smali
new file mode 100644
index 0000000..410504c
--- /dev/null
+++ b/test/706-jit-skip-compilation/smali/errclass.smali
@@ -0,0 +1,34 @@
+# 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 public LErrClass;
+
+.super Ljava/lang/Object;
+
+.method public static errMethod()J
+   .registers 8
+   const/4 v0, 0x0
+   const/4 v3, 0x0
+   aget v1, v0, v3  # v0 is null, this will alays throw and the invalid code
+                    # below will not be verified.
+   move v3, v4
+   move-wide/from16 v6, v2  # should trigger a verification error if verified as
+                            # v3 is a single register but used as a pair here.
+   return v6
+.end method
+
+# Add a field to work around demerger bug b/18051191.
+#   Failure to verify dex file '...': Offset(552) should be zero when size is zero for field-ids.
+.field private a:I
diff --git a/test/706-jit-skip-compilation/src/Main.java b/test/706-jit-skip-compilation/src/Main.java
new file mode 100644
index 0000000..aa84724
--- /dev/null
+++ b/test/706-jit-skip-compilation/src/Main.java
@@ -0,0 +1,57 @@
+/*
+ * 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.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[0]);
+    Class<?> c = Class.forName("ErrClass");
+    Method m = c.getMethod("errMethod");
+
+    // Print the counter before invokes. The golden file expects this to be 0.
+    int hotnessCounter = getHotnessCounter(c, "errMethod");
+    if (hotnessCounter != 0) {
+      throw new RuntimeException("Unexpected hotnessCounter: " + hotnessCounter);
+    }
+
+    // Loop enough to make sure the interpreter reports invocations count.
+    long result = 0;
+    for (int i = 0; i < 10000; i++) {
+      try {
+        result += (Long)m.invoke(null);
+        hotnessCounter = getHotnessCounter(c, "errMethod");
+        if (hotnessCounter != 0) {
+          throw new RuntimeException(
+            "Unexpected hotnessCounter: " + hotnessCounter);
+        }
+
+      } catch (InvocationTargetException e) {
+        if (!(e.getCause() instanceof NullPointerException)) {
+          throw e;
+        }
+      }
+    }
+
+    // Not compilable methods should not increase their hotness counter.
+    if (hotnessCounter != 0) {
+      throw new RuntimeException("Unexpected hotnessCounter: " + hotnessCounter);
+    }
+  }
+
+  public static native int getHotnessCounter(Class cls, String method_name);
+}
diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt
index 77c77ca..f8c97ce 100644
--- a/test/911-get-stack-trace/expected.txt
+++ b/test/911-get-stack-trace/expected.txt
@@ -3,206 +3,206 @@
 ###################
 From top
 ---------
- getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1
- print (Ljava/lang/Thread;II)V 0
- printOrWait (IILMain$ControlData;)V 6
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- doTest ()V 38
- main ([Ljava/lang/String;)V 6
+ getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2
+ print (Ljava/lang/Thread;II)V 0 124
+ printOrWait (IILMain$ControlData;)V 6 151
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ doTest ()V 38 34
+ main ([Ljava/lang/String;)V 6 24
 ---------
- print (Ljava/lang/Thread;II)V 0
- printOrWait (IILMain$ControlData;)V 6
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- doTest ()V 42
- main ([Ljava/lang/String;)V 6
+ print (Ljava/lang/Thread;II)V 0 124
+ printOrWait (IILMain$ControlData;)V 6 151
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ doTest ()V 42 35
+ main ([Ljava/lang/String;)V 6 24
 ---------
- getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1
- print (Ljava/lang/Thread;II)V 0
- printOrWait (IILMain$ControlData;)V 6
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
+ getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2
+ print (Ljava/lang/Thread;II)V 0 124
+ printOrWait (IILMain$ControlData;)V 6 151
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
 ---------
- printOrWait (IILMain$ControlData;)V 6
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
+ printOrWait (IILMain$ControlData;)V 6 151
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
 From bottom
 ---------
- main ([Ljava/lang/String;)V 6
+ main ([Ljava/lang/String;)V 6 24
 ---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- doTest ()V 65
- main ([Ljava/lang/String;)V 6
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ doTest ()V 65 41
+ main ([Ljava/lang/String;)V 6 24
 ---------
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
 
 ################################
 ### Other thread (suspended) ###
 ################################
 From top
 ---------
- wait ()V -1
- printOrWait (IILMain$ControlData;)V 24
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 157
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 54
 ---------
- printOrWait (IILMain$ControlData;)V 24
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ printOrWait (IILMain$ControlData;)V 24 157
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 54
 ---------
- wait ()V -1
- printOrWait (IILMain$ControlData;)V 24
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 157
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
 ---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
 From bottom
 ---------
- run ()V 4
+ run ()V 4 54
 ---------
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 54
 ---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
 
 ###########################
 ### Other thread (live) ###
 ###########################
 From top
 ---------
- printOrWait (IILMain$ControlData;)V 44
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ printOrWait (IILMain$ControlData;)V 44 164
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 88
 ---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 88
 ---------
- printOrWait (IILMain$ControlData;)V 44
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
+ printOrWait (IILMain$ControlData;)V 44 164
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
 ---------
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
 From bottom
 ---------
- run ()V 4
+ run ()V 4 88
 ---------
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 88
 ---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc
index b5b5678..57f6a92 100644
--- a/test/911-get-stack-trace/stack_trace.cc
+++ b/test/911-get-stack-trace/stack_trace.cc
@@ -20,9 +20,10 @@
 #include <memory>
 #include <stdio.h>
 
+#include "android-base/stringprintf.h"
+
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/stringprintf.h"
 #include "jni.h"
 #include "openjdkjvmti/jvmti.h"
 #include "ScopedLocalRef.h"
@@ -32,6 +33,25 @@
 namespace art {
 namespace Test911GetStackTrace {
 
+using android::base::StringPrintf;
+
+static jint FindLineNumber(jint line_number_count,
+                           jvmtiLineNumberEntry* line_number_table,
+                           jlocation location) {
+  if (line_number_table == nullptr) {
+    return -2;
+  }
+
+  jint line_number = -1;
+  for (jint i = 0; i != line_number_count; ++i) {
+    if (line_number_table[i].start_location > location) {
+      return line_number;
+    }
+    line_number = line_number_table[i].line_number;
+  }
+  return line_number;
+}
+
 extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace(
     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jint start, jint max) {
   std::unique_ptr<jvmtiFrameInfo[]> frames(new jvmtiFrameInfo[max]);
@@ -61,6 +81,26 @@
       }
     }
 
+    jint line_number_count;
+    jvmtiLineNumberEntry* line_number_table;
+    {
+      jvmtiError line_result = jvmti_env->GetLineNumberTable(frames[method_index].method,
+                                                             &line_number_count,
+                                                             &line_number_table);
+      if (line_result != JVMTI_ERROR_NONE) {
+        // Accept absent info and native method errors.
+        if (line_result != JVMTI_ERROR_ABSENT_INFORMATION &&
+            line_result != JVMTI_ERROR_NATIVE_METHOD) {
+          char* err;
+          jvmti_env->GetErrorName(line_result, &err);
+          printf("Failure running GetLineNumberTable: %s\n", err);
+          return nullptr;
+        }
+        line_number_table = nullptr;
+        line_number_count = 0;
+      }
+    }
+
     auto inner_callback = [&](jint component_index) -> jstring {
       switch (component_index) {
         case 0:
@@ -69,11 +109,17 @@
           return (sig == nullptr) ? nullptr : env->NewStringUTF(sig);
         case 2:
           return env->NewStringUTF(StringPrintf("%" PRId64, frames[method_index].location).c_str());
+        case 3: {
+          jint line_number = FindLineNumber(line_number_count,
+                                            line_number_table,
+                                            frames[method_index].location);
+          return env->NewStringUTF(StringPrintf("%d", line_number).c_str());
+        }
       }
       LOG(FATAL) << "Unreachable";
       UNREACHABLE();
     };
-    jobjectArray inner_array = CreateObjectArray(env, 3, "java/lang/String", inner_callback);
+    jobjectArray inner_array = CreateObjectArray(env, 4, "java/lang/String", inner_callback);
 
     if (name != nullptr) {
       jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name));
@@ -84,6 +130,9 @@
     if (gen != nullptr) {
       jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen));
     }
+    if (line_number_table != nullptr) {
+      jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(line_number_table));
+    }
 
     return inner_array;
   };
diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt
index e5fa53f..7522a65 100644
--- a/test/913-heaps/expected.txt
+++ b/test/913-heaps/expected.txt
@@ -5,12 +5,12 @@
 root@root --(thread)--> 3000@0 [size=132, length=-1]
 0@0 --(array-element@0)--> 1@1000 [size=16, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
-1002@0 --(interface)--> 2001@0 [size=132, length=-1]
+1002@0 --(interface)--> 2001@0 [size=124, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
 1@1000 --(class)--> 1000@0 [size=123, length=-1]
 1@1000 --(field@12)--> 3@1001 [size=24, length=-1]
 1@1000 --(field@8)--> 2@1000 [size=16, length=-1]
-2001@0 --(interface)--> 2000@0 [size=132, length=-1]
+2001@0 --(interface)--> 2000@0 [size=124, length=-1]
 2@1000 --(class)--> 1000@0 [size=123, length=-1]
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 3@1001 --(field@16)--> 4@1000 [size=16, length=-1]
@@ -22,12 +22,12 @@
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
-1002@0 --(interface)--> 2001@0 [size=132, length=-1]
+1002@0 --(interface)--> 2001@0 [size=124, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
 1@1000 --(class)--> 1000@0 [size=123, length=-1]
 1@1000 --(field@12)--> 3@1001 [size=24, length=-1]
 1@1000 --(field@8)--> 2@1000 [size=16, length=-1]
-2001@0 --(interface)--> 2000@0 [size=132, length=-1]
+2001@0 --(interface)--> 2000@0 [size=124, length=-1]
 2@1000 --(class)--> 1000@0 [size=123, length=-1]
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 3@1001 --(field@16)--> 4@1000 [size=16, length=-1]
@@ -46,12 +46,12 @@
 root@root --(thread)--> 1@1000 [size=16, length=-1]
 root@root --(thread)--> 3000@0 [size=132, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
-1002@0 --(interface)--> 2001@0 [size=132, length=-1]
+1002@0 --(interface)--> 2001@0 [size=124, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
 1@1000 --(class)--> 1000@0 [size=123, length=-1]
 1@1000 --(field@12)--> 3@1001 [size=24, length=-1]
 1@1000 --(field@8)--> 2@1000 [size=16, length=-1]
-2001@0 --(interface)--> 2000@0 [size=132, length=-1]
+2001@0 --(interface)--> 2000@0 [size=124, length=-1]
 2@1000 --(class)--> 1000@0 [size=123, length=-1]
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 3@1001 --(field@16)--> 4@1000 [size=16, length=-1]
@@ -63,12 +63,12 @@
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
-1002@0 --(interface)--> 2001@0 [size=132, length=-1]
+1002@0 --(interface)--> 2001@0 [size=124, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
 1@1000 --(class)--> 1000@0 [size=123, length=-1]
 1@1000 --(field@12)--> 3@1001 [size=24, length=-1]
 1@1000 --(field@8)--> 2@1000 [size=16, length=-1]
-2001@0 --(interface)--> 2000@0 [size=132, length=-1]
+2001@0 --(interface)--> 2000@0 [size=124, length=-1]
 2@1000 --(class)--> 1000@0 [size=123, length=-1]
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 3@1001 --(field@16)--> 4@1000 [size=16, length=-1]
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 7b00fcd..49ab7dd 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -22,9 +22,10 @@
 
 #include <vector>
 
+#include "android-base/stringprintf.h"
+
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/stringprintf.h"
 #include "jit/jit.h"
 #include "jni.h"
 #include "native_stack_dump.h"
@@ -39,6 +40,8 @@
 namespace art {
 namespace Test913Heaps {
 
+using android::base::StringPrintf;
+
 extern "C" JNIEXPORT void JNICALL Java_Main_forceGarbageCollection(JNIEnv* env ATTRIBUTE_UNUSED,
                                                                    jclass klass ATTRIBUTE_UNUSED) {
   jvmtiError ret = jvmti_env->ForceGarbageCollection();
@@ -180,7 +183,7 @@
       if (*tag_ptr >= 1000) {
         // This is a class or interface, the size of which will be dependent on the architecture.
         // Do not print the size, but detect known values and "normalize" for the golden file.
-        if ((sizeof(void*) == 4 && size == 180) || (sizeof(void*) == 8 && size == 232)) {
+        if ((sizeof(void*) == 4 && size == 172) || (sizeof(void*) == 8 && size == 224)) {
           adapted_size = 123;
         }
       }
diff --git a/test/Android.arm_vixl.mk b/test/914-hello-obsolescence/build
old mode 100644
new mode 100755
similarity index 71%
copy from test/Android.arm_vixl.mk
copy to test/914-hello-obsolescence/build
index c89eb4a..898e2e5
--- a/test/Android.arm_vixl.mk
+++ b/test/914-hello-obsolescence/build
@@ -1,5 +1,6 @@
+#!/bin/bash
 #
-# Copyright (C) 2016 The Android Open Source Project
+# Copyright 2016 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -12,9 +13,5 @@
 # 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.
-#
 
-# Known broken tests for the ARM VIXL backend.
-TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
-  562-checker-no-intermediate \
-  624-checker-stringops \
+./default-build "$@" --experimental agents
diff --git a/test/914-hello-obsolescence/expected.txt b/test/914-hello-obsolescence/expected.txt
new file mode 100644
index 0000000..83efda1
--- /dev/null
+++ b/test/914-hello-obsolescence/expected.txt
@@ -0,0 +1,9 @@
+hello
+Not doing anything here
+goodbye
+hello
+transforming calling function
+goodbye
+Hello - Transformed
+Not doing anything here
+Goodbye - Transformed
diff --git a/test/914-hello-obsolescence/info.txt b/test/914-hello-obsolescence/info.txt
new file mode 100644
index 0000000..c8b892c
--- /dev/null
+++ b/test/914-hello-obsolescence/info.txt
@@ -0,0 +1 @@
+Tests basic obsolete method support
diff --git a/test/914-hello-obsolescence/run b/test/914-hello-obsolescence/run
new file mode 100755
index 0000000..b2f0b04
--- /dev/null
+++ b/test/914-hello-obsolescence/run
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if  [[ "$@" == *"-O"* ]]; then
+  agent=libtiagent.so
+  plugin=libopenjdkjvmti.so
+  lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+  arg="jvm"
+else
+  arg="art"
+  if [[ "$@" != *"--debuggable"* ]]; then
+    other_args=" -Xcompiler-option --debuggable "
+  else
+    other_args=""
+  fi
+fi
+
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --runtime-option -agentpath:${agent}=914-hello-obsolescence,${arg} \
+                   --android-runtime-option -Xplugin:${plugin} \
+                   --android-runtime-option -Xfully-deoptable \
+                   ${other_args} \
+                   --args ${lib}
diff --git a/test/914-hello-obsolescence/src/Main.java b/test/914-hello-obsolescence/src/Main.java
new file mode 100644
index 0000000..46266ef
--- /dev/null
+++ b/test/914-hello-obsolescence/src/Main.java
@@ -0,0 +1,73 @@
+/*
+ * 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.util.Base64;
+
+public class Main {
+  // class Transform {
+  //   public void sayHi(Runnable r) {
+  //     System.out.println("Hello - Transformed");
+  //     r.run();
+  //     System.out.println("Goodbye - Transformed");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAJAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
+    "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+    "KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAeAQATSGVsbG8gLSBU" +
+    "cmFuc2Zvcm1lZAcAHwwAIAAhBwAiDAAjAAoBABVHb29kYnllIC0gVHJhbnNmb3JtZWQBAAlUcmFu" +
+    "c2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZh" +
+    "L2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZh" +
+    "L2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAABwAIAAAAAAACAAAA" +
+    "CQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAAAQABAA0ADgABAAsAAAA7AAIA" +
+    "AgAAABeyAAISA7YABCu5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAMACAAEAA4ABQAWAAYA" +
+    "AQAPAAAAAgAQ");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQAYeAMMXgYWxoeSHAS9EWKCCtVRSAGpqZVQAwAAcAAAAHhWNBIAAAAAAAAAALACAAAR" +
+    "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAMAgAARAEAAKIB" +
+    "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAAB3AgAAfAIA" +
+    "AIUCAACKAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
+    "lAEAAAsAAAAGAAAAnAEAAAUAAQANAAAAAAAAAAAAAAAAAAEAEAAAAAEAAgAOAAAAAgAAAAAAAAAD" +
+    "AAAADwAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAJ8CAAAAAAAAAQABAAEAAACRAgAABAAAAHAQ" +
+    "AwAAAA4ABAACAAIAAACWAgAAFAAAAGIAAAAbAQIAAABuIAIAEAByEAQAAwBiAAAAGwEBAAAAbiAC" +
+    "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" +
+    "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" +
+    "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" +
+    "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00" +
+    "LjEzAANvdXQAB3ByaW50bG4AA3J1bgAFc2F5SGkAAQAHDgADAQAHDoc8hwAAAAEBAICABMQCAQHc" +
+    "AgAAAA0AAAAAAAAAAQAAAAAAAAABAAAAEQAAAHAAAAACAAAABwAAALQAAAADAAAAAwAAANAAAAAE" +
+    "AAAAAQAAAPQAAAAFAAAABQAAAPwAAAAGAAAAAQAAACQBAAABIAAAAgAAAEQBAAABEAAAAgAAAJQB" +
+    "AAACIAAAEQAAAKIBAAADIAAAAgAAAJECAAAAIAAAAQAAAJ8CAAAAEAAAAQAAALACAAA=");
+
+  public static void main(String[] args) {
+    System.loadLibrary(args[1]);
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+    t.sayHi(() -> {
+      System.out.println("transforming calling function");
+      doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    });
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+  }
+
+  // Transforms the class
+  private static native void doCommonClassRedefinition(Class<?> target,
+                                                       byte[] classfile,
+                                                       byte[] dexfile);
+}
diff --git a/test/914-hello-obsolescence/src/Transform.java b/test/914-hello-obsolescence/src/Transform.java
new file mode 100644
index 0000000..8cda6cd
--- /dev/null
+++ b/test/914-hello-obsolescence/src/Transform.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Transform {
+  public void sayHi(Runnable r) {
+    // Use lower 'h' to make sure the string will have a different string id
+    // than the transformation (the transformation code is the same except
+    // the actual printed String, which was making the test inacurately passing
+    // in JIT mode when loading the string from the dex cache, as the string ids
+    // of the two different strings were the same).
+    // We know the string ids will be different because lexicographically:
+    // "Hello" < "LTransform;" < "hello".
+    System.out.println("hello");
+    r.run();
+    System.out.println("goodbye");
+  }
+}
diff --git a/test/Android.arm_vixl.mk b/test/915-obsolete-2/build
old mode 100644
new mode 100755
similarity index 71%
copy from test/Android.arm_vixl.mk
copy to test/915-obsolete-2/build
index c89eb4a..898e2e5
--- a/test/Android.arm_vixl.mk
+++ b/test/915-obsolete-2/build
@@ -1,5 +1,6 @@
+#!/bin/bash
 #
-# Copyright (C) 2016 The Android Open Source Project
+# Copyright 2016 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -12,9 +13,5 @@
 # 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.
-#
 
-# Known broken tests for the ARM VIXL backend.
-TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
-  562-checker-no-intermediate \
-  624-checker-stringops \
+./default-build "$@" --experimental agents
diff --git a/test/915-obsolete-2/expected.txt b/test/915-obsolete-2/expected.txt
new file mode 100644
index 0000000..04aff3a
--- /dev/null
+++ b/test/915-obsolete-2/expected.txt
@@ -0,0 +1,21 @@
+Pre Start private method call
+hello - private
+Post Start private method call
+Not doing anything here
+Pre Finish private method call
+goodbye - private
+Post Finish private method call
+Pre Start private method call
+hello - private
+Post Start private method call
+transforming calling function
+Pre Finish private method call
+Goodbye - private - Transformed
+Post Finish private method call
+Pre Start private method call - Transformed
+Hello - private - Transformed
+Post Start private method call - Transformed
+Not doing anything here
+Pre Finish private method call - Transformed
+Goodbye - private - Transformed
+Post Finish private method call - Transformed
diff --git a/test/915-obsolete-2/info.txt b/test/915-obsolete-2/info.txt
new file mode 100644
index 0000000..c8b892c
--- /dev/null
+++ b/test/915-obsolete-2/info.txt
@@ -0,0 +1 @@
+Tests basic obsolete method support
diff --git a/test/915-obsolete-2/run b/test/915-obsolete-2/run
new file mode 100755
index 0000000..bfe227f
--- /dev/null
+++ b/test/915-obsolete-2/run
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if  [[ "$@" == *"-O"* ]]; then
+  agent=libtiagent.so
+  plugin=libopenjdkjvmti.so
+  lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+  arg="jvm"
+else
+  arg="art"
+  if [[ "$@" != *"--debuggable"* ]]; then
+    other_args=" -Xcompiler-option --debuggable "
+  else
+    other_args=""
+  fi
+fi
+
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --runtime-option -agentpath:${agent}=915-obsolete-2,${arg} \
+                   --android-runtime-option -Xplugin:${plugin} \
+                   --android-runtime-option -Xfully-deoptable \
+                   ${other_args} \
+                   --args ${lib}
diff --git a/test/915-obsolete-2/src/Main.java b/test/915-obsolete-2/src/Main.java
new file mode 100644
index 0000000..bbeb726
--- /dev/null
+++ b/test/915-obsolete-2/src/Main.java
@@ -0,0 +1,99 @@
+/*
+ * 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.util.Base64;
+
+public class Main {
+  // class Transform {
+  //   private void Start() {
+  //     System.out.println("Hello - private - Transformed");
+  //   }
+  //
+  //   private void Finish() {
+  //     System.out.println("Goodbye - private - Transformed");
+  //   }
+  //
+  //   public void sayHi(Runnable r) {
+  //     System.out.println("Pre Start private method call - Transformed");
+  //     Start();
+  //     System.out.println("Post Start private method call - Transformed");
+  //     r.run();
+  //     System.out.println("Pre Finish private method call - Transformed");
+  //     Finish();
+  //     System.out.println("Post Finish private method call - Transformed");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAMgoADgAZCQAaABsIABwKAB0AHggAHwgAIAoADQAhCAAiCwAjACQIACUKAA0AJggA" +
+    "JwcAKAcAKQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVTdGFydAEA" +
+    "BkZpbmlzaAEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7KVYBAApTb3VyY2VGaWxlAQAO" +
+    "VHJhbnNmb3JtLmphdmEMAA8AEAcAKgwAKwAsAQAdSGVsbG8gLSBwcml2YXRlIC0gVHJhbnNmb3Jt" +
+    "ZWQHAC0MAC4ALwEAH0dvb2RieWUgLSBwcml2YXRlIC0gVHJhbnNmb3JtZWQBACtQcmUgU3RhcnQg" +
+    "cHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAATABABACxQb3N0IFN0YXJ0IHByaXZh" +
+    "dGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAcAMAwAMQAQAQAsUHJlIEZpbmlzaCBwcml2YXRl" +
+    "IG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQMABQAEAEALVBvc3QgRmluaXNoIHByaXZhdGUgbWV0" +
+    "aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAEACVRyYW5zZm9ybQEAEGphdmEvbGFuZy9PYmplY3QBABBq" +
+    "YXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9Q" +
+    "cmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABJqYXZhL2xhbmcv" +
+    "UnVubmFibGUBAANydW4AIAANAA4AAAAAAAQAAAAPABAAAQARAAAAHQABAAEAAAAFKrcAAbEAAAAB" +
+    "ABIAAAAGAAEAAAABAAIAEwAQAAEAEQAAACUAAgABAAAACbIAAhIDtgAEsQAAAAEAEgAAAAoAAgAA" +
+    "AAMACAAEAAIAFAAQAAEAEQAAACUAAgABAAAACbIAAhIFtgAEsQAAAAEAEgAAAAoAAgAAAAcACAAI" +
+    "AAEAFQAWAAEAEQAAAGMAAgACAAAAL7IAAhIGtgAEKrcAB7IAAhIItgAEK7kACQEAsgACEgq2AAQq" +
+    "twALsgACEgy2AASxAAAAAQASAAAAIgAIAAAACwAIAAwADAANABQADgAaAA8AIgAQACYAEQAuABIA" +
+    "AQAXAAAAAgAY");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQCM0QYTJmX+NsZXkImojgSkJtXyuew3oaXcBAAAcAAAAHhWNBIAAAAAAAAAADwEAAAX" +
+    "AAAAcAAAAAcAAADMAAAAAwAAAOgAAAABAAAADAEAAAcAAAAUAQAAAQAAAEwBAABwAwAAbAEAAD4C" +
+    "AABGAgAATgIAAG8CAACOAgAAmwIAALICAADGAgAA3AIAAPACAAAEAwAAMwMAAGEDAACPAwAAvAMA" +
+    "AMMDAADTAwAA1gMAANoDAADuAwAA8wMAAPwDAAABBAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAA" +
+    "EAAAABAAAAAGAAAAAAAAABEAAAAGAAAAMAIAABEAAAAGAAAAOAIAAAUAAQATAAAAAAAAAAAAAAAA" +
+    "AAAAAQAAAAAAAAAOAAAAAAABABYAAAABAAIAFAAAAAIAAAAAAAAAAwAAABUAAAAAAAAAAAAAAAIA" +
+    "AAAAAAAADwAAAAAAAAAmBAAAAAAAAAEAAQABAAAACAQAAAQAAABwEAUAAAAOAAMAAQACAAAADQQA" +
+    "AAkAAABiAAAAGwECAAAAbiAEABAADgAAAAMAAQACAAAAEwQAAAkAAABiAAAAGwEDAAAAbiAEABAA" +
+    "DgAAAAQAAgACAAAAGQQAACoAAABiAAAAGwENAAAAbiAEABAAcBACAAIAYgAAABsBCwAAAG4gBAAQ" +
+    "AHIQBgADAGIAAAAbAQwAAABuIAQAEABwEAEAAgBiAAAAGwEKAAAAbiAEABAADgABAAAAAwAAAAEA" +
+    "AAAEAAY8aW5pdD4ABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBUcmFuc2Zvcm1lZAAdSGVs" +
+    "bG8gLSBwcml2YXRlIC0gVHJhbnNmb3JtZWQAC0xUcmFuc2Zvcm07ABVMamF2YS9pby9QcmludFN0" +
+    "cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAUTGphdmEvbGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xh" +
+    "bmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AC1Qb3N0IEZpbmlzaCBwcml2YXRlIG1ldGhv" +
+    "ZCBjYWxsIC0gVHJhbnNmb3JtZWQALFBvc3QgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRy" +
+    "YW5zZm9ybWVkACxQcmUgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAr" +
+    "UHJlIFN0YXJ0IHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAFU3RhcnQADlRyYW5z" +
+    "Zm9ybS5qYXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00LjEzAANvdXQAB3ByaW50bG4AA3J1bgAF" +
+    "c2F5SGkAAQAHDgAHAAcOhwADAAcOhwALAQAHDoc8hzyHPIcAAAADAQCAgATsAgEChAMBAqgDAwHM" +
+    "Aw0AAAAAAAAAAQAAAAAAAAABAAAAFwAAAHAAAAACAAAABwAAAMwAAAADAAAAAwAAAOgAAAAEAAAA" +
+    "AQAAAAwBAAAFAAAABwAAABQBAAAGAAAAAQAAAEwBAAABIAAABAAAAGwBAAABEAAAAgAAADACAAAC" +
+    "IAAAFwAAAD4CAAADIAAABAAAAAgEAAAAIAAAAQAAACYEAAAAEAAAAQAAADwEAAA=");
+
+  public static void main(String[] args) {
+    System.loadLibrary(args[1]);
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+    t.sayHi(() -> {
+      System.out.println("transforming calling function");
+      doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    });
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+  }
+
+  // Transforms the class
+  private static native void doCommonClassRedefinition(Class<?> target,
+                                                       byte[] classfile,
+                                                       byte[] dexfile);
+}
diff --git a/test/915-obsolete-2/src/Transform.java b/test/915-obsolete-2/src/Transform.java
new file mode 100644
index 0000000..e914e29
--- /dev/null
+++ b/test/915-obsolete-2/src/Transform.java
@@ -0,0 +1,35 @@
+/*
+ * 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 Transform {
+  private void Start() {
+    System.out.println("hello - private");
+  }
+
+  private void Finish() {
+    System.out.println("goodbye - private");
+  }
+
+  public void sayHi(Runnable r) {
+    System.out.println("Pre Start private method call");
+    Start();
+    System.out.println("Post Start private method call");
+    r.run();
+    System.out.println("Pre Finish private method call");
+    Finish();
+    System.out.println("Post Finish private method call");
+  }
+}
diff --git a/test/Android.arm_vixl.mk b/test/916-obsolete-jit/build
old mode 100644
new mode 100755
similarity index 71%
copy from test/Android.arm_vixl.mk
copy to test/916-obsolete-jit/build
index c89eb4a..898e2e5
--- a/test/Android.arm_vixl.mk
+++ b/test/916-obsolete-jit/build
@@ -1,5 +1,6 @@
+#!/bin/bash
 #
-# Copyright (C) 2016 The Android Open Source Project
+# Copyright 2016 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -12,9 +13,5 @@
 # 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.
-#
 
-# Known broken tests for the ARM VIXL backend.
-TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
-  562-checker-no-intermediate \
-  624-checker-stringops \
+./default-build "$@" --experimental agents
diff --git a/test/916-obsolete-jit/expected.txt b/test/916-obsolete-jit/expected.txt
new file mode 100644
index 0000000..4caefc6
--- /dev/null
+++ b/test/916-obsolete-jit/expected.txt
@@ -0,0 +1,21 @@
+Pre Start private method call
+hello - private
+Post Start private method call
+Not doing anything here
+Pre Finish private method call
+goodbye - private
+Post Finish private method call
+Pre Start private method call
+hello - private
+Post Start private method call
+transforming calling function
+Pre Finish private method call
+Goodbye - private - Transformed
+Post Finish private method call
+pre Start private method call - Transformed
+Hello - private - Transformed
+post Start private method call - Transformed
+Not doing anything here
+pre Finish private method call - Transformed
+Goodbye - private - Transformed
+post Finish private method call - Transformed
diff --git a/test/916-obsolete-jit/info.txt b/test/916-obsolete-jit/info.txt
new file mode 100644
index 0000000..c8b892c
--- /dev/null
+++ b/test/916-obsolete-jit/info.txt
@@ -0,0 +1 @@
+Tests basic obsolete method support
diff --git a/test/916-obsolete-jit/run b/test/916-obsolete-jit/run
new file mode 100755
index 0000000..25c2c07
--- /dev/null
+++ b/test/916-obsolete-jit/run
@@ -0,0 +1,47 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if  [[ "$@" == *"-O"* ]]; then
+  agent=libtiagent.so
+  plugin=libopenjdkjvmti.so
+  lib=tiagent
+fi
+
+if [[ "$@" == *"--jit"* ]]; then
+  other_args=""
+else
+  other_args="--jit"
+fi
+if [[ "$@" == *"--jvm"* ]]; then
+  arg="jvm"
+else
+  arg="art"
+  if [[ "$@" != *"--debuggable"* ]]; then
+    other_args="$other_args -Xcompiler-option --debuggable "
+  fi
+fi
+
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --runtime-option -agentpath:${agent}=915-obsolete-2,${arg} \
+                   --android-runtime-option -Xplugin:${plugin} \
+                   --android-runtime-option -Xfully-deoptable \
+                   ${other_args} \
+                   --args ${lib}
diff --git a/test/916-obsolete-jit/src/Main.java b/test/916-obsolete-jit/src/Main.java
new file mode 100644
index 0000000..74eb003
--- /dev/null
+++ b/test/916-obsolete-jit/src/Main.java
@@ -0,0 +1,210 @@
+/*
+ * 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.util.function.Consumer;
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Main {
+
+  // import java.util.function.Consumer;
+  //
+  // class Transform {
+  //   private void Start(Consumer<String> reporter) {
+  //     reporter.accept("Hello - private - Transformed");
+  //   }
+  //
+  //   private void Finish(Consumer<String> reporter) {
+  //     reporter.accept("Goodbye - private - Transformed");
+  //   }
+  //
+  //   public void sayHi(Runnable r, Consumer<String> reporter) {
+  //     reporter.accept("pre Start private method call - Transformed");
+  //     Start(reporter);
+  //     reporter.accept("post Start private method call - Transformed");
+  //     r.run();
+  //     reporter.accept("pre Finish private method call - Transformed");
+  //     Finish(reporter);
+  //     reporter.accept("post Finish private method call - Transformed");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAMAoADQAcCAAdCwAeAB8IACAIACEKAAwAIggAIwsAJAAlCAAmCgAMACcIACgHACkH" +
+    "ACoBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAFU3RhcnQBACAoTGph" +
+    "dmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjspVgEACVNpZ25hdHVyZQEANChMamF2YS91dGlsL2Z1" +
+    "bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xhbmcvU3RyaW5nOz47KVYBAAZGaW5pc2gBAAVzYXlIaQEA" +
+    "NChMamF2YS9sYW5nL1J1bm5hYmxlO0xqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7KVYBAEgo" +
+    "TGphdmEvbGFuZy9SdW5uYWJsZTtMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xh" +
+    "bmcvU3RyaW5nOz47KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAA4ADwEAHUhlbGxv" +
+    "IC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkBwArDAAsAC0BAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRy" +
+    "YW5zZm9ybWVkAQArcHJlIFN0YXJ0IHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAwA" +
+    "EgATAQAscG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQHAC4MAC8A" +
+    "DwEALHByZSBGaW5pc2ggcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAWABMBAC1w" +
+    "b3N0IEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQBAAlUcmFuc2Zvcm0B" +
+    "ABBqYXZhL2xhbmcvT2JqZWN0AQAbamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyAQAGYWNjZXB0" +
+    "AQAVKExqYXZhL2xhbmcvT2JqZWN0OylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAADAAN" +
+    "AAAAAAAEAAAADgAPAAEAEAAAAB0AAQABAAAABSq3AAGxAAAAAQARAAAABgABAAAAEwACABIAEwAC" +
+    "ABAAAAAlAAIAAgAAAAkrEgK5AAMCALEAAAABABEAAAAKAAIAAAAVAAgAFgAUAAAAAgAVAAIAFgAT" +
+    "AAIAEAAAACUAAgACAAAACSsSBLkAAwIAsQAAAAEAEQAAAAoAAgAAABkACAAaABQAAAACABUAAQAX" +
+    "ABgAAgAQAAAAZQACAAMAAAAxLBIFuQADAgAqLLcABiwSB7kAAwIAK7kACAEALBIJuQADAgAqLLcA" +
+    "CiwSC7kAAwIAsQAAAAEAEQAAACIACAAAAB0ACAAeAA0AHwAVACAAGwAhACMAIgAoACMAMAAkABQA" +
+    "AAACABkAAQAaAAAAAgAb");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQBc8wr9PcHqnOR61m+0kimXTSddVMToJPuYBQAAcAAAAHhWNBIAAAAAAAAAAOAEAAAc" +
+    "AAAAcAAAAAYAAADgAAAABAAAAPgAAAAAAAAAAAAAAAcAAAAoAQAAAQAAAGABAAAYBAAAgAEAAHoC" +
+    "AAB9AgAAgAIAAIgCAACOAgAAlgIAALcCAADWAgAA4wIAAAIDAAAWAwAALAMAAEADAABeAwAAfQMA" +
+    "AIQDAACUAwAAlwMAAJsDAACgAwAAqAMAALwDAADrAwAAGQQAAEcEAAB0BAAAeQQAAIAEAAAHAAAA" +
+    "CAAAAAkAAAAKAAAADQAAABAAAAAQAAAABQAAAAAAAAARAAAABQAAAGQCAAASAAAABQAAAGwCAAAR" +
+    "AAAABQAAAHQCAAAAAAAAAgAAAAAAAwAEAAAAAAADAA4AAAAAAAIAGgAAAAIAAAACAAAAAwAAABkA" +
+    "AAAEAAEAEwAAAAAAAAAAAAAAAgAAAAAAAAAPAAAAPAIAAMoEAAAAAAAAAQAAAKgEAAABAAAAuAQA" +
+    "AAEAAQABAAAAhwQAAAQAAABwEAQAAAAOAAMAAgACAAAAjAQAAAcAAAAbAAUAAAByIAYAAgAOAAAA" +
+    "AwACAAIAAACTBAAABwAAABsABgAAAHIgBgACAA4AAAAEAAMAAgAAAJoEAAAiAAAAGwAYAAAAciAG" +
+    "AAMAcCACADEAGwAWAAAAciAGAAMAchAFAAIAGwAXAAAAciAGAAMAcCABADEAGwAVAAAAciAGAAMA" +
+    "DgAAAAAAAAAAAAMAAAAAAAAAAQAAAIABAAACAAAAgAEAAAMAAACIAQAAAQAAAAIAAAACAAAAAwAE" +
+    "AAEAAAAEAAEoAAE8AAY8aW5pdD4ABD47KVYABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBU" +
+    "cmFuc2Zvcm1lZAAdSGVsbG8gLSBwcml2YXRlIC0gVHJhbnNmb3JtZWQAC0xUcmFuc2Zvcm07AB1M" +
+    "ZGFsdmlrL2Fubm90YXRpb24vU2lnbmF0dXJlOwASTGphdmEvbGFuZy9PYmplY3Q7ABRMamF2YS9s" +
+    "YW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABxMamF2YS91dGlsL2Z1bmN0aW9uL0Nv" +
+    "bnN1bWVyAB1MamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyOwAFU3RhcnQADlRyYW5zZm9ybS5q" +
+    "YXZhAAFWAAJWTAADVkxMAAZhY2NlcHQAEmVtaXR0ZXI6IGphY2stNC4xOQAtcG9zdCBGaW5pc2gg" +
+    "cHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkACxwb3N0IFN0YXJ0IHByaXZhdGUgbWV0" +
+    "aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAscHJlIEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0g" +
+    "VHJhbnNmb3JtZWQAK3ByZSBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQA" +
+    "A3J1bgAFc2F5SGkABXZhbHVlABMABw4AGQEABw5pABUBAAcOaQAdAgAABw5pPGk8aTxpAAIBARsc" +
+    "BRcAFwwXARcLFwMCAQEbHAYXABcKFwwXARcLFwMAAAMBAICABJADAQKoAwECyAMDAegDDwAAAAAA" +
+    "AAABAAAAAAAAAAEAAAAcAAAAcAAAAAIAAAAGAAAA4AAAAAMAAAAEAAAA+AAAAAUAAAAHAAAAKAEA" +
+    "AAYAAAABAAAAYAEAAAMQAAACAAAAgAEAAAEgAAAEAAAAkAEAAAYgAAABAAAAPAIAAAEQAAADAAAA" +
+    "ZAIAAAIgAAAcAAAAegIAAAMgAAAEAAAAhwQAAAQgAAACAAAAqAQAAAAgAAABAAAAygQAAAAQAAAB" +
+    "AAAA4AQAAA==");
+
+  // A class that we can use to keep track of the output of this test.
+  private static class TestWatcher implements Consumer<String> {
+    private StringBuilder sb;
+    public TestWatcher() {
+      sb = new StringBuilder();
+    }
+
+    @Override
+    public void accept(String s) {
+      sb.append(s);
+      sb.append('\n');
+    }
+
+    public String getOutput() {
+      return sb.toString();
+    }
+
+    public void clear() {
+      sb = new StringBuilder();
+    }
+  }
+
+  public static void main(String[] args) {
+    System.loadLibrary(args[1]);
+    doTest(new Transform(), new TestWatcher());
+  }
+
+  // TODO Workaround to (1) inability to ensure that current_method is not put into a register by
+  // the JIT and/or (2) inability to deoptimize frames near runtime functions.
+  // TODO Fix one/both of these issues.
+  public static void doCall(Runnable r) {
+      r.run();
+  }
+
+  private static boolean interpreting = true;
+  private static boolean retry = false;
+
+  public static void doTest(Transform t, TestWatcher w) {
+    // Get the methods that need to be optimized.
+    Method say_hi_method;
+    Method do_call_method;
+    // Figure out if we can even JIT at all.
+    final boolean has_jit = hasJit();
+    try {
+      say_hi_method = Transform.class.getDeclaredMethod(
+          "sayHi", Runnable.class, Consumer.class);
+      do_call_method = Main.class.getDeclaredMethod("doCall", Runnable.class);
+    } catch (Exception e) {
+      System.out.println("Unable to find methods!");
+      e.printStackTrace();
+      return;
+    }
+    // Makes sure the stack is the way we want it for the test and does the redefinition. It will
+    // set the retry boolean to true if we need to go around again due to a bad stack.
+    Runnable do_redefinition = () -> {
+      if (has_jit &&
+          (Main.isInterpretedFunction(say_hi_method, true) ||
+           Main.isInterpretedFunction(do_call_method, false))) {
+        // Try again. We are not running the right jitted methods/cannot redefine them now.
+        retry = true;
+      } else {
+        // Actually do the redefinition. The stack looks good.
+        retry = false;
+        w.accept("transforming calling function");
+        doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+      }
+    };
+    // This does nothing.
+    Runnable noop = () -> {};
+    // This just prints something out to show we are running the Runnable.
+    Runnable say_nothing = () -> { w.accept("Not doing anything here"); };
+    // This checks to see if we have jitted the methods we are testing.
+    Runnable check_interpreting = () -> {
+      // TODO remove the second check when we remove the doCall function. We need to check that
+      // both of these functions aren't being interpreted because if sayHi is the test doesn't do
+      // anything and if doCall is then there will be a runtime call right above the sayHi
+      // function preventing sayHi from being deoptimized.
+      interpreting = has_jit && (Main.isInterpretedFunction(say_hi_method, true) ||
+                                 Main.isInterpretedFunction(do_call_method, false));
+    };
+    do {
+      w.clear();
+      // Wait for the methods to be jitted
+      long j = 0;
+      do {
+        for (int i = 0; i < 10000; i++) {
+          t.sayHi(noop, w);
+          j++;
+          // Clear so that we won't OOM if we go around a few times.
+          w.clear();
+        }
+        t.sayHi(check_interpreting, w);
+        if (j >= 1000000) {
+          System.out.println("FAIL: Could not make sayHi be Jitted!");
+          return;
+        }
+        j++;
+      } while(interpreting);
+      // Clear output. Now we try for real.
+      w.clear();
+      // Try and redefine.
+      t.sayHi(say_nothing, w);
+      t.sayHi(do_redefinition, w);
+      t.sayHi(say_nothing, w);
+    } while (retry);
+    // Print output of last run.
+    System.out.print(w.getOutput());
+  }
+
+  private static native boolean hasJit();
+
+  private static native boolean isInterpretedFunction(Method m, boolean require_deoptimizable);
+
+  // Transforms the class
+  private static native void doCommonClassRedefinition(Class<?> target,
+                                                       byte[] classfile,
+                                                       byte[] dexfile);
+}
diff --git a/test/916-obsolete-jit/src/Transform.java b/test/916-obsolete-jit/src/Transform.java
new file mode 100644
index 0000000..f4dcf09
--- /dev/null
+++ b/test/916-obsolete-jit/src/Transform.java
@@ -0,0 +1,43 @@
+/*
+ * 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.util.function.Consumer;
+
+class Transform {
+  private void Start(Consumer<String> reporter) {
+    reporter.accept("hello - private");
+  }
+
+  private void Finish(Consumer<String> reporter) {
+    reporter.accept("goodbye - private");
+  }
+
+  public void sayHi(Runnable r, Consumer<String> reporter) {
+    reporter.accept("Pre Start private method call");
+    Start(reporter);
+    reporter.accept("Post Start private method call");
+    // TODO Revisit with b/33616143
+    // TODO Uncomment this once either b/33630159 or b/33616143 are resolved.
+    // r.run();
+    // TODO This doCall function is a very temporary fix until we get either deoptimization near
+    // runtime frames working, forcing current method to be always read from the stack or both
+    // working.
+    Main.doCall(r);
+    reporter.accept("Pre Finish private method call");
+    Finish(reporter);
+    reporter.accept("Post Finish private method call");
+  }
+}
diff --git a/test/Android.arm_vixl.mk b/test/917-fields-transformation/build
old mode 100644
new mode 100755
similarity index 71%
copy from test/Android.arm_vixl.mk
copy to test/917-fields-transformation/build
index c89eb4a..898e2e5
--- a/test/Android.arm_vixl.mk
+++ b/test/917-fields-transformation/build
@@ -1,5 +1,6 @@
+#!/bin/bash
 #
-# Copyright (C) 2016 The Android Open Source Project
+# Copyright 2016 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -12,9 +13,5 @@
 # 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.
-#
 
-# Known broken tests for the ARM VIXL backend.
-TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
-  562-checker-no-intermediate \
-  624-checker-stringops \
+./default-build "$@" --experimental agents
diff --git a/test/917-fields-transformation/expected.txt b/test/917-fields-transformation/expected.txt
new file mode 100644
index 0000000..bcdd201
--- /dev/null
+++ b/test/917-fields-transformation/expected.txt
@@ -0,0 +1,12 @@
+Result is Hello
+take1 is Hello
+take2 is Goodbye
+Result is start
+take1 is start
+take2 is end
+Result is Goodbye
+take1 is Hello
+take2 is Goodbye
+Result is end
+take1 is start
+take2 is end
diff --git a/test/917-fields-transformation/info.txt b/test/917-fields-transformation/info.txt
new file mode 100644
index 0000000..4cd1bd9
--- /dev/null
+++ b/test/917-fields-transformation/info.txt
@@ -0,0 +1 @@
+Tests field access after class redefinition support in the jvmti plugin.
diff --git a/test/917-fields-transformation/run b/test/917-fields-transformation/run
new file mode 100755
index 0000000..a434b63
--- /dev/null
+++ b/test/917-fields-transformation/run
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if  [[ "$@" == *"-O"* ]]; then
+  agent=libtiagent.so
+  plugin=libopenjdkjvmti.so
+  lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+  arg="jvm"
+else
+  arg="art"
+  if [[ "$@" != *"--debuggable"* ]]; then
+    other_args=" -Xcompiler-option --debuggable "
+  else
+    other_args=""
+  fi
+fi
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --runtime-option -agentpath:${agent}=917-fields-transformation,${arg} \
+                   --android-runtime-option -Xplugin:${plugin} \
+                   --android-runtime-option -Xfully-deoptable \
+                   ${other_args} \
+                   --args ${lib}
diff --git a/test/917-fields-transformation/src/Main.java b/test/917-fields-transformation/src/Main.java
new file mode 100644
index 0000000..5378bb7
--- /dev/null
+++ b/test/917-fields-transformation/src/Main.java
@@ -0,0 +1,80 @@
+/*
+ * 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.util.Base64;
+public class Main {
+
+  // base64 encoded class/dex file for
+  // class Transform {
+  //   public String take1;
+  //   public String take2;
+  //
+  //   public Transform(String a, String b) {
+  //     take1 = a;
+  //     take2 = b;
+  //   }
+  //
+  //   public String getResult() {
+  //     return take2;
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAFwoABQARCQAEABIJAAQAEwcAFAcAFQEABXRha2UxAQASTGphdmEvbGFuZy9TdHJp" +
+    "bmc7AQAFdGFrZTIBAAY8aW5pdD4BACcoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJp" +
+    "bmc7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAJZ2V0UmVzdWx0AQAUKClMamF2YS9sYW5n" +
+    "L1N0cmluZzsBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkAFgwABgAHDAAIAAcBAAlU" +
+    "cmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQADKClWACAABAAFAAAAAgABAAYABwAAAAEACAAH" +
+    "AAAAAgABAAkACgABAAsAAAAzAAIAAwAAAA8qtwABKiu1AAIqLLUAA7EAAAABAAwAAAASAAQAAAAU" +
+    "AAQAFQAJABYADgAXAAEADQAOAAEACwAAAB0AAQABAAAABSq0AAOwAAAAAQAMAAAABgABAAAAGgAB" +
+    "AA8AAAACABA=");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQAGUTBb4jIABRlaI9rejdk7RCfyqR2kmNSkAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAM" +
+    "AAAAcAAAAAQAAACgAAAAAwAAALAAAAACAAAA1AAAAAMAAADkAAAAAQAAAPwAAACIAQAAHAEAAFwB" +
+    "AABkAQAAZwEAAHQBAACIAQAAnAEAAKwBAACvAQAAtAEAAMgBAADTAQAA2gEAAAIAAAADAAAABAAA" +
+    "AAYAAAABAAAAAgAAAAAAAAAGAAAAAwAAAAAAAAAHAAAAAwAAAFQBAAAAAAIACgAAAAAAAgALAAAA" +
+    "AAACAAAAAAAAAAAACQAAAAEAAQAAAAAAAAAAAAAAAAABAAAAAAAAAAUAAAAAAAAA8AEAAAAAAAAD" +
+    "AAMAAQAAAOEBAAAIAAAAcBACAAAAWwEAAFsCAQAOAAIAAQAAAAAA6wEAAAMAAABUEAEAEQAAAAIA" +
+    "AAACAAIABjxpbml0PgABTAALTFRyYW5zZm9ybTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEv" +
+    "bGFuZy9TdHJpbmc7AA5UcmFuc2Zvcm0uamF2YQABVgADVkxMABJlbWl0dGVyOiBqYWNrLTQuMTkA" +
+    "CWdldFJlc3VsdAAFdGFrZTEABXRha2UyABQCAAAHDjwtLQAaAAcOAAACAQEAAQEBAIGABJwCAQG8" +
+    "AgAADQAAAAAAAAABAAAAAAAAAAEAAAAMAAAAcAAAAAIAAAAEAAAAoAAAAAMAAAADAAAAsAAAAAQA" +
+    "AAACAAAA1AAAAAUAAAADAAAA5AAAAAYAAAABAAAA/AAAAAEgAAACAAAAHAEAAAEQAAABAAAAVAEA" +
+    "AAIgAAAMAAAAXAEAAAMgAAACAAAA4QEAAAAgAAABAAAA8AEAAAAQAAABAAAABAIAAA==");
+
+  public static void main(String[] args) {
+    System.loadLibrary(args[1]);
+    doTest(new Transform("Hello", "Goodbye"),
+           new Transform("start", "end"));
+  }
+
+  private static void printTransform(Transform t) {
+    System.out.println("Result is " + t.getResult());
+    System.out.println("take1 is " + t.take1);
+    System.out.println("take2 is " + t.take2);
+  }
+  public static void doTest(Transform t1, Transform t2) {
+    printTransform(t1);
+    printTransform(t2);
+    doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    printTransform(t1);
+    printTransform(t2);
+  }
+
+  // Transforms the class
+  private static native void doCommonClassRedefinition(Class<?> target,
+                                                       byte[] class_file,
+                                                       byte[] dex_file);
+}
diff --git a/runtime/base/stringprintf_test.cc b/test/917-fields-transformation/src/Transform.java
similarity index 62%
rename from runtime/base/stringprintf_test.cc
rename to test/917-fields-transformation/src/Transform.java
index 0bfde33..6fe6223 100644
--- a/runtime/base/stringprintf_test.cc
+++ b/test/917-fields-transformation/src/Transform.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,16 +14,16 @@
  * limitations under the License.
  */
 
-#include "stringprintf.h"
+class Transform {
+  public String take1;
+  public String take2;
 
-#include "gtest/gtest.h"
+  public Transform(String take1, String take2) {
+    this.take1 = take1;
+    this.take2 = take2;
+  }
 
-namespace art {
-
-TEST(StringPrintfTest, HexSizeT) {
-  size_t size = 0x00107e59;
-  EXPECT_STREQ("00107e59", StringPrintf("%08zx", size).c_str());
-  EXPECT_STREQ("0x00107e59", StringPrintf("0x%08zx", size).c_str());
+  public String getResult() {
+    return take1;
+  }
 }
-
-}  // namespace art
diff --git a/test/Android.bp b/test/Android.bp
index 2625f56..5a2c902 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -243,6 +243,9 @@
     name: "libtiagent-defaults",
     defaults: ["libartagent-defaults"],
     srcs: [
+        // This is to get the IsInterpreted native method.
+        "common/stack_inspect.cc",
+        "common/runtime_state.cc",
         "ti-agent/common_load.cc",
         "ti-agent/common_helper.cc",
         "901-hello-ti-agent/basics.cc",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index b515130..ec1f6ba 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -226,6 +226,12 @@
 $(shell echo $(1) | tr '[:lower:]' '[:upper:]' | tr '-' '_')
 endef  # name-to-var
 
+# Disable 153-reference-stress temporarily until a fix arrives. b/33389022.
+# Disable 080-oom-fragmentation due to flakes. b/33795328
+ART_TEST_RUN_TEST_SKIP += \
+  153-reference-stress \
+  080-oom-fragmentation
+
 ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
         $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
         $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(ART_TEST_RUN_TEST_SKIP), $(ALL_ADDRESS_SIZES))
@@ -280,6 +286,10 @@
   911-get-stack-trace \
   912-classes \
   913-heaps \
+  914-hello-obsolescence \
+  915-obsolete-2 \
+  916-obsolete-jit \
+  917-fields-transformation \
 
 ifneq (,$(filter target,$(TARGET_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -492,6 +502,24 @@
       $(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_TRACING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
 endif
 
+TEST_ART_BROKEN_TRACING_RUN_TESTS :=
+
+# These tests expect JIT compilation, which is suppressed when tracing.
+TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS := \
+  604-hot-static-interface \
+  612-jit-dex-cache \
+  613-inlining-dex-cache \
+  616-cha \
+  626-set-resolved-string \
+
+ifneq (,$(filter trace stream,$(TRACE_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      jit,$(RELOCATE_TYPES),trace stream,$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \
+      $(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS :=
+
 # Known broken tests for the interpreter.
 # CFI unwinding expects managed frames.
 # 629 requires compilation.
@@ -514,15 +542,22 @@
 # Test 906 iterates the heap filtering with different options. No instances should be created
 # between those runs to be able to have precise checks.
 # Test 902 hits races with the JIT compiler. b/32821077
-# Test 626-const-class-linking can deadlock with JIT. b/33567581
 # Test 629 requires compilation.
+# Test 914, 915, 917, & 918 are very sensitive to the exact state of the stack,
+# including the jit-inserted runtime frames. This causes them to be somewhat
+# flaky as JIT tests. This should be fixed once b/33630159 or b/33616143 are
+# resolved but until then just disable them. Test 916 already checks this
+# feature for JIT use cases in a way that is resilient to the jit frames.
 TEST_ART_BROKEN_JIT_RUN_TESTS := \
   137-cfi \
-  626-const-class-linking \
   629-vdex-speed \
   902-hello-transformation \
   904-object-allocation \
   906-iterate-heap \
+  914-hello-obsolescence \
+  915-obsolete-2 \
+  917-fields-transformation \
+  918-obsolete-fields \
 
 ifneq (,$(filter jit,$(COMPILER_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -546,26 +581,6 @@
       $(TEST_ART_BROKEN_OPTIMIZING_GRAPH_COLOR),$(ALL_ADDRESS_SIZES))
 endif
 
-# Known broken tests for the ARM VIXL backend.
-# Android.arm_vixl.mk defines TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS.
-include $(LOCAL_PATH)/Android.arm_vixl.mk
-
-ifdef ART_USE_VIXL_ARM_BACKEND
-  ifeq (arm,$(filter arm,$(TARGET_ARCH) $(TARGET_2ND_ARCH)))
-    ifneq (,$(filter $(OPTIMIZING_COMPILER_TYPES),$(COMPILER_TYPES)))
-      ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
-          $(OPTIMIZING_COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
-          $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \
-          $(TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS),32)
-    endif
-  endif
-  # TODO(VIXL): These two tests currently fail, but adding them to `ART_TEST_KNOWN_BROKEN` breaks
-  # `export ART_USE_VIXL_ARM_BACKEND=true && mma -j6 test-art-target-gtest dist`
-  #ART_TEST_KNOWN_BROKEN += test-art-target-gtest-dex2oat_test32
-  #ART_TEST_KNOWN_BROKEN += test-art-target-gtest-image_test32
-endif
-
-
 # Known broken tests for the mips32 optimizing compiler backend.
 TEST_ART_BROKEN_OPTIMIZING_MIPS_RUN_TESTS := \
 
@@ -674,15 +689,6 @@
   endif
 endif
 
-# Tests disabled for GSS.
-TEST_ART_BROKEN_GSS_RUN_TESTS := 080-oom-fragmentation
-ifeq ($(ART_DEFAULT_GC_TYPE),GSS)
-  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \
-      $(PREBUILD_TYPES),$(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES), \
-      $(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \
-      $(TEST_ART_BROKEN_GSS_RUN_TESTS),$(ALL_ADDRESS_SIZES))
-endif
-
 TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS :=
 TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS :=
 
diff --git a/runtime/base/stringprintf_test.cc b/test/ErroneousA/ErroneousA.java
similarity index 61%
copy from runtime/base/stringprintf_test.cc
copy to test/ErroneousA/ErroneousA.java
index 0bfde33..49da544 100644
--- a/runtime/base/stringprintf_test.cc
+++ b/test/ErroneousA/ErroneousA.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,16 +14,4 @@
  * limitations under the License.
  */
 
-#include "stringprintf.h"
-
-#include "gtest/gtest.h"
-
-namespace art {
-
-TEST(StringPrintfTest, HexSizeT) {
-  size_t size = 0x00107e59;
-  EXPECT_STREQ("00107e59", StringPrintf("%08zx", size).c_str());
-  EXPECT_STREQ("0x00107e59", StringPrintf("0x%08zx", size).c_str());
-}
-
-}  // namespace art
+final class FinalSuper {}
diff --git a/runtime/base/stringprintf_test.cc b/test/ErroneousB/ErroneousB.java
similarity index 61%
copy from runtime/base/stringprintf_test.cc
copy to test/ErroneousB/ErroneousB.java
index 0bfde33..6c2902a 100644
--- a/runtime/base/stringprintf_test.cc
+++ b/test/ErroneousB/ErroneousB.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,16 +14,7 @@
  * limitations under the License.
  */
 
-#include "stringprintf.h"
+// Only final in first dex.
+class FinalSuper {}
 
-#include "gtest/gtest.h"
-
-namespace art {
-
-TEST(StringPrintfTest, HexSizeT) {
-  size_t size = 0x00107e59;
-  EXPECT_STREQ("00107e59", StringPrintf("%08zx", size).c_str());
-  EXPECT_STREQ("0x00107e59", StringPrintf("0x%08zx", size).c_str());
-}
-
-}  // namespace art
+class Erroneous extends FinalSuper {}
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 285f3aa..7451cf9 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -19,6 +19,7 @@
 #include "base/enums.h"
 #include "base/logging.h"
 #include "dex_file-inl.h"
+#include "instrumentation.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
 #include "mirror/class-inl.h"
@@ -30,6 +31,16 @@
 
 namespace art {
 
+// public static native boolean hasJit();
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJit(JNIEnv*, jclass) {
+  Runtime* runtime = Runtime::Current();
+  return runtime != nullptr
+      && runtime->GetJit() != nullptr
+      && runtime->GetInstrumentation()->GetCurrentInstrumentationLevel() !=
+            instrumentation::Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter;
+}
+
 // public static native boolean hasOatFile();
 
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasOatFile(JNIEnv* env, jclass cls) {
@@ -188,4 +199,28 @@
   return method->HasSingleImplementation();
 }
 
+extern "C" JNIEXPORT int JNICALL Java_Main_getHotnessCounter(JNIEnv* env,
+                                                             jclass,
+                                                             jclass cls,
+                                                             jstring method_name) {
+  jit::Jit* jit = Runtime::Current()->GetJit();
+  if (jit == nullptr) {
+    // The hotness counter is valid only under JIT.
+    // If we don't JIT return 0 to match test expectations.
+    return 0;
+  }
+
+  ArtMethod* method = nullptr;
+  {
+    ScopedObjectAccess soa(Thread::Current());
+
+    ScopedUtfChars chars(env, method_name);
+    CHECK(chars.c_str() != nullptr);
+    method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
+        chars.c_str(), kRuntimePointerSize);
+  }
+
+  return method->GetCounter();
+}
+
 }  // namespace art
diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc
index 4df2d47..df7fa20 100644
--- a/test/common/stack_inspect.cc
+++ b/test/common/stack_inspect.cc
@@ -18,6 +18,7 @@
 
 #include "base/logging.h"
 #include "dex_file-inl.h"
+#include "jni_internal.h"
 #include "mirror/class-inl.h"
 #include "nth_caller_visitor.h"
 #include "oat_file.h"
@@ -52,6 +53,89 @@
   return IsInterpreted(env, klass, 1);
 }
 
+// public static native boolean isInterpreted(int depth);
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInterpretedAt(JNIEnv* env,
+                                                                jclass klass,
+                                                                jint depth) {
+  return IsInterpreted(env, klass, depth);
+}
+
+
+// public static native boolean isInterpretedFunction(String smali);
+
+// TODO Remove 'allow_runtime_frames' option once we have deoptimization through runtime frames.
+struct MethodIsInterpretedVisitor : public StackVisitor {
+ public:
+  MethodIsInterpretedVisitor(Thread* thread, ArtMethod* goal, bool require_deoptable)
+      : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+        goal_(goal),
+        method_is_interpreted_(true),
+        method_found_(false),
+        prev_was_runtime_(true),
+        require_deoptable_(require_deoptable) {}
+
+  virtual bool VisitFrame() OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (goal_ == GetMethod()) {
+      method_is_interpreted_ = (require_deoptable_ && prev_was_runtime_) || IsShadowFrame();
+      method_found_ = true;
+      return false;
+    }
+    prev_was_runtime_ = GetMethod()->IsRuntimeMethod();
+    return true;
+  }
+
+  bool IsInterpreted() {
+    return method_is_interpreted_;
+  }
+
+  bool IsFound() {
+    return method_found_;
+  }
+
+ private:
+  const ArtMethod* goal_;
+  bool method_is_interpreted_;
+  bool method_found_;
+  bool prev_was_runtime_;
+  bool require_deoptable_;
+};
+
+// TODO Remove 'require_deoptimizable' option once we have deoptimization through runtime frames.
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInterpretedFunction(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method, jboolean require_deoptimizable) {
+  // Return false if this seems to not be an ART runtime.
+  if (Runtime::Current() == nullptr) {
+    return JNI_FALSE;
+  }
+  if (method == nullptr) {
+    env->ThrowNew(env->FindClass("java/lang/NullPointerException"), "method is null!");
+    return JNI_FALSE;
+  }
+  jmethodID id = env->FromReflectedMethod(method);
+  if (id == nullptr) {
+    env->ThrowNew(env->FindClass("java/lang/Error"), "Unable to interpret method argument!");
+    return JNI_FALSE;
+  }
+  bool result;
+  bool found;
+  {
+    ScopedObjectAccess soa(env);
+    ArtMethod* goal = jni::DecodeArtMethod(id);
+    MethodIsInterpretedVisitor v(soa.Self(), goal, require_deoptimizable);
+    v.WalkStack();
+    bool enters_interpreter = Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(
+        goal->GetEntryPointFromQuickCompiledCode());
+    result = (v.IsInterpreted() || enters_interpreter);
+    found = v.IsFound();
+  }
+  if (!found) {
+    env->ThrowNew(env->FindClass("java/lang/Error"), "Unable to find given method in stack!");
+    return JNI_FALSE;
+  }
+  return result;
+}
+
 // public static native void assertIsInterpreted();
 
 extern "C" JNIEXPORT void JNICALL Java_Main_assertIsInterpreted(JNIEnv* env, jclass klass) {
diff --git a/test/etc/default-build b/test/etc/default-build
index 51ae175..e9e3886 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -69,6 +69,7 @@
 
 # Setup experimental flag mappings in a bash associative array.
 declare -A JACK_EXPERIMENTAL_ARGS
+JACK_EXPERIMENTAL_ARGS["agents"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
 JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
 JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
 JACK_EXPERIMENTAL_ARGS["method-handles"]="-D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b1"
@@ -76,12 +77,14 @@
 declare -A SMALI_EXPERIMENTAL_ARGS
 SMALI_EXPERIMENTAL_ARGS["default-methods"]="--api-level 24"
 SMALI_EXPERIMENTAL_ARGS["method-handles"]="--api-level 26"
+SMALI_EXPERIMENTAL_ARGS["agents"]="--api-level 26"
 
 declare -A JAVAC_EXPERIMENTAL_ARGS
 JAVAC_EXPERIMENTAL_ARGS["default-methods"]="-source 1.8 -target 1.8"
 JAVAC_EXPERIMENTAL_ARGS["lambdas"]="-source 1.8 -target 1.8"
 JAVAC_EXPERIMENTAL_ARGS["method-handles"]="-source 1.8 -target 1.8"
 JAVAC_EXPERIMENTAL_ARGS[${DEFAULT_EXPERIMENT}]="-source 1.7 -target 1.7"
+JAVAC_EXPERIMENTAL_ARGS["agents"]="-source 1.8 -target 1.8"
 
 while true; do
   if [ "x$1" = "x--dx-option" ]; then
@@ -126,6 +129,16 @@
 # Be sure to get any default arguments if not doing any experiments.
 EXPERIMENTAL="${EXPERIMENTAL} ${DEFAULT_EXPERIMENT}"
 
+if [ "${JACK_SERVER}" = "false" ]; then
+  # Run in single-threaded mode for the continuous buildbot.
+  JACK_ARGS="${JACK_ARGS} -D sched.runner=single-threaded"
+else
+  # Run with 4 threads to reduce memory footprint and thread contention.
+  JACK_ARGS="${JACK_ARGS} -D sched.runner=multi-threaded"
+  JACK_ARGS="${JACK_ARGS} -D sched.runner.thread.kind=fixed"
+  JACK_ARGS="${JACK_ARGS} -D sched.runner.thread.fixed.count=4"
+fi
+
 # Add args from the experimental mappings.
 for experiment in ${EXPERIMENTAL}; do
   JACK_ARGS="${JACK_ARGS} ${JACK_EXPERIMENTAL_ARGS[${experiment}]}"
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index f0abb44..566f7ba 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -59,6 +59,7 @@
 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"
+APP_IMAGE="y"
 
 while true; do
     if [ "x$1" = "x--quiet" ]; then
@@ -132,6 +133,9 @@
     elif [ "x$1" = "x--prebuild" ]; then
         PREBUILD="y"
         shift
+    elif [ "x$1" = "x--no-app-image" ]; then
+        APP_IMAGE="n"
+        shift
     elif [ "x$1" = "x--strip-dex" ]; then
         STRIP_DEX="y"
         shift
@@ -369,8 +373,8 @@
 if [ "$JIT" = "y" ]; then
     INT_OPTS="-Xusejit:true"
     if [ "$VERIFY" = "y" ] ; then
-      INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=verify-at-runtime"
-      COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify-at-runtime"
+      INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=interpret-only"
+      COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=interpret-only"
     else
       INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=verify-none"
       COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify-none"
@@ -453,11 +457,14 @@
 mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA"
 strip_cmdline="true"
 
-# Pick a base that will force the app image to get relocated.
-app_image="--base=0x4000 --app-image-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.art"
 
 if [ "$PREBUILD" = "y" ]; then
   mkdir_locations="${mkdir_locations} ${DEX_LOCATION}/oat/$ISA"
+  if [ "$APP_IMAGE" = "y" ]; then
+    # Pick a base that will force the app image to get relocated.
+    app_image="--base=0x4000 --app-image-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.art"
+  fi
+
   dex2oat_cmdline="$INVOKE_WITH $ANDROID_ROOT/bin/dex2oatd \
                       $COMPILE_FLAGS \
                       --boot-image=${BOOT_IMAGE} \
diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc
index 3e2b168..ebf1e46 100644
--- a/test/ti-agent/common_helper.cc
+++ b/test/ti-agent/common_helper.cc
@@ -18,8 +18,11 @@
 
 #include <stdio.h>
 
+#include "art_method.h"
 #include "jni.h"
 #include "openjdkjvmti/jvmti.h"
+#include "scoped_thread_state_change-inl.h"
+#include "stack.h"
 #include "ti-agent/common_load.h"
 #include "utils.h"
 
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 2795cbc..79c17d7 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -66,6 +66,10 @@
   { "911-get-stack-trace", Test911GetStackTrace::OnLoad, nullptr },
   { "912-classes", Test912Classes::OnLoad, nullptr },
   { "913-heaps", Test913Heaps::OnLoad, nullptr },
+  { "914-hello-obsolescence", common_redefine::OnLoad, nullptr },
+  { "915-obsolete-2", common_redefine::OnLoad, nullptr },
+  { "916-obsolete-jit", common_redefine::OnLoad, nullptr },
+  { "917-fields-transformation", common_redefine::OnLoad, nullptr },
 };
 
 static AgentLib* FindAgent(char* name) {
diff --git a/tools/cpp-define-generator/offset_dexcache.def b/tools/cpp-define-generator/offset_dexcache.def
index 4b9d481..abb5e1e 100644
--- a/tools/cpp-define-generator/offset_dexcache.def
+++ b/tools/cpp-define-generator/offset_dexcache.def
@@ -38,7 +38,6 @@
 DEFINE_ART_METHOD_OFFSET_SIZED(JNI,                  EntryPointFromJni)
 DEFINE_ART_METHOD_OFFSET_SIZED(QUICK_CODE,           EntryPointFromQuickCompiledCode)
 DEFINE_ART_METHOD_OFFSET(DECLARING_CLASS,            DeclaringClass)
-DEFINE_DECLARING_CLASS_OFFSET(DEX_CACHE_STRINGS,     DexCacheStrings)
 
 #undef DEFINE_ART_METHOD_OFFSET
 #undef DEFINE_ART_METHOD_OFFSET_32
diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py
index fd8415d..b5f856f 100755
--- a/tools/jfuzz/run_jfuzz_test.py
+++ b/tools/jfuzz/run_jfuzz_test.py
@@ -43,10 +43,11 @@
 BISECTABLE_RET_CODES = (RetCode.SUCCESS, RetCode.ERROR, RetCode.TIMEOUT)
 
 
-def GetExecutionModeRunner(device, mode):
+def GetExecutionModeRunner(use_dx, device, mode):
   """Returns a runner for the given execution mode.
 
   Args:
+    use_dx: boolean, if True use dx rather than jack
     device: string, target device serial number (or None)
     mode: string, execution mode
   Returns:
@@ -57,13 +58,13 @@
   if mode == 'ri':
     return TestRunnerRIOnHost()
   if mode == 'hint':
-    return TestRunnerArtIntOnHost()
+    return TestRunnerArtIntOnHost(use_dx)
   if mode == 'hopt':
-    return TestRunnerArtOptOnHost()
+    return TestRunnerArtOptOnHost(use_dx)
   if mode == 'tint':
-    return TestRunnerArtIntOnTarget(device)
+    return TestRunnerArtIntOnTarget(use_dx, device)
   if mode == 'topt':
-    return TestRunnerArtOptOnTarget(device)
+    return TestRunnerArtOptOnTarget(use_dx, device)
   raise FatalError('Unknown execution mode')
 
 
@@ -113,6 +114,33 @@
     """
 
 
+class TestRunnerWithHostCompilation(TestRunner):
+  """Abstract test runner that supports compilation on host."""
+
+  def  __init__(self, use_dx):
+    """Constructor for the runner with host compilation.
+
+    Args:
+      use_dx: boolean, if True use dx rather than jack
+    """
+    self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.',
+                       'Test.java']
+    self._use_dx = use_dx
+
+  def CompileOnHost(self):
+    if self._use_dx:
+      if RunCommand(['javac', 'Test.java'],
+                    out=None, err=None, timeout=30) == RetCode.SUCCESS:
+        retc = RunCommand(['dx', '--dex', '--output=classes.dex'] + glob('*.class'),
+                          out=None, err='dxerr.txt', timeout=30)
+      else:
+        retc = RetCode.NOTCOMPILED
+    else:
+      retc = RunCommand(['jack'] + self._jack_args,
+                        out=None, err='jackerr.txt', timeout=30)
+    return retc
+
+
 class TestRunnerRIOnHost(TestRunner):
   """Concrete test runner of the reference implementation on host."""
 
@@ -136,25 +164,24 @@
     return None
 
 
-class TestRunnerArtOnHost(TestRunner):
+class TestRunnerArtOnHost(TestRunnerWithHostCompilation):
   """Abstract test runner of Art on host."""
 
-  def  __init__(self, extra_args=None):
+  def  __init__(self, use_dx, extra_args=None):
     """Constructor for the Art on host tester.
 
     Args:
+      use_dx: boolean, if True use dx rather than jack
       extra_args: list of strings, extra arguments for dalvikvm
     """
+    super().__init__(use_dx)
     self._art_cmd = ['/bin/bash', 'art', '-cp', 'classes.dex']
     if extra_args is not None:
       self._art_cmd += extra_args
     self._art_cmd.append('Test')
-    self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.',
-                       'Test.java']
 
   def CompileAndRunTest(self):
-    if RunCommand(['jack'] + self._jack_args, out=None, err='jackerr.txt',
-                  timeout=30) == RetCode.SUCCESS:
+    if self.CompileOnHost() == RetCode.SUCCESS:
       retc = RunCommand(self._art_cmd, self.output_file, 'arterr.txt')
     else:
       retc = RetCode.NOTCOMPILED
@@ -164,9 +191,13 @@
 class TestRunnerArtIntOnHost(TestRunnerArtOnHost):
   """Concrete test runner of interpreter mode Art on host."""
 
-  def  __init__(self):
-    """Constructor."""
-    super().__init__(['-Xint'])
+  def  __init__(self, use_dx):
+    """Constructor for the Art on host tester (interpreter).
+
+    Args:
+      use_dx: boolean, if True use dx rather than jack
+   """
+    super().__init__(use_dx, ['-Xint'])
 
   @property
   def description(self):
@@ -183,9 +214,13 @@
 class TestRunnerArtOptOnHost(TestRunnerArtOnHost):
   """Concrete test runner of optimizing compiler mode Art on host."""
 
-  def  __init__(self):
-    """Constructor."""
-    super().__init__(None)
+  def  __init__(self, use_dx):
+    """Constructor for the Art on host tester (optimizing).
+
+    Args:
+      use_dx: boolean, if True use dx rather than jack
+   """
+    super().__init__(use_dx, None)
 
   @property
   def description(self):
@@ -201,28 +236,27 @@
     return ['--raw-cmd={0}'.format(cmd_str), '--timeout', str(30)]
 
 
-class TestRunnerArtOnTarget(TestRunner):
+class TestRunnerArtOnTarget(TestRunnerWithHostCompilation):
   """Abstract test runner of Art on target."""
 
-  def  __init__(self, device, extra_args=None):
+  def  __init__(self, use_dx, device, extra_args=None):
     """Constructor for the Art on target tester.
 
     Args:
+      use_dx: boolean, if True use dx rather than jack
       device: string, target device serial number (or None)
       extra_args: list of strings, extra arguments for dalvikvm
     """
+    super().__init__(use_dx)
     self._test_env = DeviceTestEnv('jfuzz_', specific_device=device)
     self._dalvik_cmd = ['dalvikvm']
     if extra_args is not None:
       self._dalvik_cmd += extra_args
     self._device = device
-    self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.',
-                       'Test.java']
     self._device_classpath = None
 
   def CompileAndRunTest(self):
-    if RunCommand(['jack'] + self._jack_args, out=None, err='jackerr.txt',
-                   timeout=30) == RetCode.SUCCESS:
+    if self.CompileOnHost() == RetCode.SUCCESS:
       self._device_classpath = self._test_env.PushClasspath('classes.dex')
       cmd = self._dalvik_cmd + ['-cp', self._device_classpath, 'Test']
       (output, retc) = self._test_env.RunCommand(
@@ -247,13 +281,14 @@
 class TestRunnerArtIntOnTarget(TestRunnerArtOnTarget):
   """Concrete test runner of interpreter mode Art on target."""
 
-  def  __init__(self, device):
-    """Constructor.
+  def  __init__(self, use_dx, device):
+    """Constructor for the Art on target tester (interpreter).
 
     Args:
+      use_dx: boolean, if True use dx rather than jack
       device: string, target device serial number (or None)
     """
-    super().__init__(device, ['-Xint'])
+    super().__init__(use_dx, device, ['-Xint'])
 
   @property
   def description(self):
@@ -270,13 +305,14 @@
 class TestRunnerArtOptOnTarget(TestRunnerArtOnTarget):
   """Concrete test runner of optimizing compiler mode Art on target."""
 
-  def  __init__(self, device):
-    """Constructor.
+  def  __init__(self, use_dx, device):
+    """Constructor for the Art on target tester (optimizing).
 
     Args:
+      use_dx: boolean, if True use dx rather than jack
       device: string, target device serial number (or None)
     """
-    super().__init__(device, None)
+    super().__init__(use_dx, device, None)
 
   @property
   def description(self):
@@ -306,7 +342,7 @@
   """Tester that runs JFuzz many times and report divergences."""
 
   def  __init__(self, num_tests, device, mode1, mode2, jfuzz_args,
-                report_script, true_divergence_only):
+                report_script, true_divergence_only, use_dx):
     """Constructor for the tester.
 
     Args:
@@ -317,14 +353,16 @@
       jfuzz_args: list of strings, additional arguments for jfuzz
       report_script: string, path to script called for each divergence
       true_divergence_only: boolean, if True don't bisect timeout divergences
+      use_dx: boolean, if True use dx rather than jack
     """
     self._num_tests = num_tests
     self._device = device
-    self._runner1 = GetExecutionModeRunner(device, mode1)
-    self._runner2 = GetExecutionModeRunner(device, mode2)
+    self._runner1 = GetExecutionModeRunner(use_dx, device, mode1)
+    self._runner2 = GetExecutionModeRunner(use_dx, device, mode2)
     self._jfuzz_args = jfuzz_args
     self._report_script = report_script
     self._true_divergence_only = true_divergence_only
+    self._use_dx = use_dx
     self._save_dir = None
     self._results_dir = None
     self._jfuzz_dir = None
@@ -367,6 +405,7 @@
     print('Directory :', self._results_dir)
     print('Exec-mode1:', self._runner1.description)
     print('Exec-mode2:', self._runner2.description)
+    print('Compiler  :', 'dx' if self._use_dx else 'jack')
     print()
     self.ShowStats()
     for self._test in range(1, self._num_tests + 1):
@@ -414,14 +453,16 @@
       retc2: int, normalized return code of second runner
     """
     if retc1 == retc2:
-      # Non-divergent in return code.
+      # No divergence in return code.
       if retc1 == RetCode.SUCCESS:
         # Both compilations and runs were successful, inspect generated output.
         runner1_out = self._runner1.output_file
         runner2_out = self._runner2.output_file
         if not filecmp.cmp(runner1_out, runner2_out, shallow=False):
+          # Divergence in output.
           self.ReportDivergence(retc1, retc2, is_output_divergence=True)
         else:
+          # No divergence in output.
           self._num_success += 1
       elif retc1 == RetCode.TIMEOUT:
         self._num_timed_out += 1
@@ -429,8 +470,12 @@
         self._num_not_compiled += 1
       else:
         self._num_not_run += 1
+    elif self._true_divergence_only and RetCode.TIMEOUT in (retc1, retc2):
+      # When only true divergences are requested, any divergence in return
+      # code where one is a time out is treated as a regular time out.
+      self._num_timed_out += 1
     else:
-      # Divergent in return code.
+      # Divergence in return code.
       self.ReportDivergence(retc1, retc2, is_output_divergence=False)
 
   def GetCurrentDivergenceDir(self):
@@ -450,13 +495,12 @@
     os.mkdir(ddir)
     for f in glob('*.txt') + ['Test.java']:
       shutil.copy(f, ddir)
-    if not (self._true_divergence_only and RetCode.TIMEOUT in (retc1, retc2)):
-      # Maybe run bisection bug search.
-      if retc1 in BISECTABLE_RET_CODES and retc2 in BISECTABLE_RET_CODES:
-        self.MaybeBisectDivergence(retc1, retc2, is_output_divergence)
-      # Call reporting script.
-      if self._report_script:
-        self.RunReportScript(retc1, retc2, is_output_divergence)
+    # Maybe run bisection bug search.
+    if retc1 in BISECTABLE_RET_CODES and retc2 in BISECTABLE_RET_CODES:
+      self.MaybeBisectDivergence(retc1, retc2, is_output_divergence)
+    # Call reporting script.
+    if self._report_script:
+      self.RunReportScript(retc1, retc2, is_output_divergence)
 
   def RunReportScript(self, retc1, retc2, is_output_divergence):
     """Runs report script."""
@@ -546,18 +590,21 @@
   parser.add_argument('--mode2', default='hopt',
                       help='execution mode 2 (default: hopt)')
   parser.add_argument('--report_script', help='script called for each'
-                                              'divergence')
+                                              ' divergence')
   parser.add_argument('--jfuzz_arg', default=[], dest='jfuzz_args',
                       action='append', help='argument for jfuzz')
   parser.add_argument('--true_divergence', default=False, action='store_true',
                       help='don\'t bisect timeout divergences')
+  parser.add_argument('--use_dx', default=False, action='store_true',
+                      help='use old-style dx (rather than jack)')
   args = parser.parse_args()
   if args.mode1 == args.mode2:
     raise FatalError('Identical execution modes given')
   # Run the JFuzz tester.
-  with JFuzzTester(args.num_tests, args.device, args.mode1, args.mode2,
+  with JFuzzTester(args.num_tests,
+                   args.device, args.mode1, args.mode2,
                    args.jfuzz_args, args.report_script,
-                   args.true_divergence) as fuzzer:
+                   args.true_divergence, args.use_dx) as fuzzer:
     fuzzer.Run()
 
 if __name__ == '__main__':
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 53fe8fe..dcef8c0 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -169,7 +169,8 @@
   description: "Lack of IPv6 on some buildbot slaves",
   result: EXEC_FAILED,
   names: ["libcore.io.OsTest#test_byteBufferPositions_sendto_recvfrom_af_inet6",
-          "libcore.io.OsTest#test_sendtoSocketAddress_af_inet6"],
+          "libcore.io.OsTest#test_sendtoSocketAddress_af_inet6",
+          "libcore.io.OsTest#test_recvfrom_EmptyPacket"],
   bug: 25178637
 },
 {