Merge "A mutation that changes the length of an array."
diff --git a/Android.mk b/Android.mk
index 0402513..8735d7c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -81,7 +81,6 @@
 include $(art_path)/tools/ahat/Android.mk
 include $(art_path)/tools/dexfuzz/Android.mk
 include $(art_path)/libart_fake/Android.mk
-include $(art_path)/test/Android.run-test-jvmti-java-library.mk
 
 ART_HOST_DEPENDENCIES := \
   $(ART_HOST_EXECUTABLES) \
@@ -380,16 +379,22 @@
 # * We will never add them if PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD = false.
 # * We will always add them if PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD = true.
 # * Otherwise, we will add them by default to userdebug and eng builds.
-ifneq (false,$(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD))
-ifneq (,$(filter userdebug eng,$(PRODUCT_TARGET_BUILD_VARIANT)))
-  PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD := true
+art_target_include_debug_build := $(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD)
+ifneq (false,$(art_target_include_debug_build))
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+  art_target_include_debug_build := true
 endif
-ifeq (true,$(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD))
+ifeq (true,$(art_target_include_debug_build))
 LOCAL_REQUIRED_MODULES += \
+    dex2oatd \
+    dexoptanalyzerd \
     libartd \
     libartd-compiler \
+    libopenjdkd \
     libopenjdkjvmd \
     libopenjdkjvmtid \
+    patchoatd \
+    profmand \
 
 endif
 endif
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..7297a14
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,3 @@
+ngeoffray@google.com
+sehr@google.com
+*
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 0ed230c..8a8df36 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,3 +1,4 @@
 [Hook Scripts]
 check_generated_files_up_to_date = tools/cpp-define-generator/presubmit-check-files-up-to-date
+check_generated_tests_up_to_date = tools/test_presubmit.py
 check_cpplint_on_changed_files = tools/cpplint_presubmit.py
diff --git a/benchmark/Android.bp b/benchmark/Android.bp
index d0dfec9..606734b 100644
--- a/benchmark/Android.bp
+++ b/benchmark/Android.bp
@@ -58,9 +58,7 @@
     ],
     static_libs: [
     ],
-    include_dirs: [
-        "libnativehelper/include/nativehelper"  // only for jni.h
-    ],
+    header_libs: ["jni_headers"],
     stl: "libc++_static",
     clang: true,
     target: {
diff --git a/build/Android.cpplint.mk b/build/Android.cpplint.mk
index f924a85..66ac897 100644
--- a/build/Android.cpplint.mk
+++ b/build/Android.cpplint.mk
@@ -18,7 +18,8 @@
 
 ART_CPPLINT := $(LOCAL_PATH)/tools/cpplint.py
 ART_CPPLINT_FILTER := --filter=-whitespace/line_length,-build/include,-readability/function,-readability/streams,-readability/todo,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn,-runtime/printf
-ART_CPPLINT_FLAGS := --quiet --root=$(ANDROID_BUILD_TOP)
+ART_CPPLINT_FLAGS := --root=$(TOP)
+ART_CPPLINT_QUIET := --quiet
 ART_CPPLINT_INGORED := \
     runtime/elf.h \
     runtime/openjdkjvmti/include/jvmti.h
@@ -32,12 +33,12 @@
 # "mm cpplint-art" to verify we aren't regressing
 .PHONY: cpplint-art
 cpplint-art:
-	$(ART_CPPLINT) $(ART_CPPLINT_FILTER) $(ART_CPPLINT_SRC)
+	$(ART_CPPLINT) $(ART_CPPLINT_FLAGS) $(ART_CPPLINT_FILTER) $(ART_CPPLINT_SRC)
 
 # "mm cpplint-art-all" to see all warnings
 .PHONY: cpplint-art-all
 cpplint-art-all:
-	$(ART_CPPLINT) $(ART_CPPLINT_SRC)
+	$(ART_CPPLINT) $(ART_CPPLINT_FLAGS) $(ART_CPPLINT_SRC)
 
 OUT_CPPLINT := $(TARGET_COMMON_OUT_ROOT)/cpplint
 
@@ -48,7 +49,7 @@
 art_cpplint_touch := $$(OUT_CPPLINT)/$$(subst /,__,$$(art_cpplint_file))
 
 $$(art_cpplint_touch): $$(art_cpplint_file) $(ART_CPPLINT) art/build/Android.cpplint.mk
-	$(hide) $(ART_CPPLINT) $(ART_CPPLINT_FLAGS) $(ART_CPPLINT_FILTER) $$<
+	$(hide) $(ART_CPPLINT) $(ART_CPPLINT_QUIET) $(ART_CPPLINT_FLAGS) $(ART_CPPLINT_FILTER) $$<
 	$(hide) mkdir -p $$(dir $$@)
 	$(hide) touch $$@
 
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c87abe5..cf6d1ec 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -30,6 +30,10 @@
   ErroneousA \
   ErroneousB \
   ErroneousInit \
+  ForClassLoaderA \
+  ForClassLoaderB \
+  ForClassLoaderC \
+  ForClassLoaderD \
   ExceptionHandle \
   GetMethodSignature \
   ImageLayoutA \
@@ -98,8 +102,9 @@
 # Dex file dependencies for each gtest.
 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 := AllFields ErroneousA ErroneousB ErroneousInit Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
+ART_GTEST_atomic_dex_ref_map_test_DEX_DEPS := Interfaces
+ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit ForClassLoaderA ForClassLoaderB ForClassLoaderC ForClassLoaderD Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
+ART_GTEST_class_loader_context_test_DEX_DEPS := Main MultiDex MyClass
 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 1b9c646..6c9aa89 100644
--- a/build/art.go
+++ b/build/art.go
@@ -68,12 +68,6 @@
 			"-DART_READ_BARRIER_TYPE_IS_"+barrierType+"=1")
 	}
 
-	if envTrue(ctx, "ART_USE_OLD_ARM_BACKEND") {
-		// Used to enable the old, pre-VIXL ARM code generator.
-		cflags = append(cflags, "-DART_USE_OLD_ARM_BACKEND=1")
-		asflags = append(asflags, "-DART_USE_OLD_ARM_BACKEND=1")
-	}
-
 	// We need larger stack overflow guards for ASAN, as the compiled code will have
 	// larger frame sizes. For simplicity, just use global not-target-specific cflags.
 	// Note: We increase this for both debug and non-debug, as the overflow gap will
@@ -97,6 +91,12 @@
 			"-DART_STACK_OVERFLOW_GAP_x86_64=8192")
 	}
 
+	if envTrue(ctx, "ART_ENABLE_ADDRESS_SANITIZER") {
+		// Used to enable full sanitization, i.e., user poisoning, under ASAN.
+		cflags = append(cflags, "-DART_ENABLE_ADDRESS_SANITIZER=1")
+		asflags = append(asflags, "-DART_ENABLE_ADDRESS_SANITIZER=1")
+	}
+
 	return cflags, asflags
 }
 
diff --git a/compiler/Android.bp b/compiler/Android.bp
index a1269dc..b721d21 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -106,20 +106,15 @@
                 "jni/quick/arm/calling_convention_arm.cc",
                 "linker/arm/relative_patcher_arm_base.cc",
                 "linker/arm/relative_patcher_thumb2.cc",
-                "optimizing/code_generator_arm.cc",
-                "optimizing/code_generator_vector_arm.cc",
                 "optimizing/code_generator_arm_vixl.cc",
                 "optimizing/code_generator_vector_arm_vixl.cc",
                 "optimizing/instruction_simplifier_arm.cc",
                 "optimizing/instruction_simplifier_shared.cc",
-                "optimizing/intrinsics_arm.cc",
                 "optimizing/intrinsics_arm_vixl.cc",
                 "optimizing/nodes_shared.cc",
                 "optimizing/scheduler_arm.cc",
-                "utils/arm/assembler_arm.cc",
                 "utils/arm/assembler_arm_vixl.cc",
-                "utils/arm/assembler_thumb2.cc",
-                "utils/arm/jni_macro_assembler_arm.cc",
+                "utils/arm/constants_arm.cc",
                 "utils/arm/jni_macro_assembler_arm_vixl.cc",
                 "utils/arm/managed_register_arm.cc",
             ],
@@ -356,6 +351,7 @@
         "optimizing/live_interval_test.cc",
         "optimizing/loop_optimization_test.cc",
         "optimizing/nodes_test.cc",
+        "optimizing/nodes_vector_test.cc",
         "optimizing/parallel_move_test.cc",
         "optimizing/pretty_printer_test.cc",
         "optimizing/reference_type_propagation_test.cc",
@@ -364,7 +360,7 @@
         "optimizing/ssa_test.cc",
         "optimizing/stack_map_test.cc",
         "optimizing/suspend_check_test.cc",
-        "utils/atomic_method_ref_map_test.cc",
+        "utils/atomic_dex_ref_map_test.cc",
         "utils/dedupe_set_test.cc",
         "utils/intrusive_forward_list_test.cc",
         "utils/string_reference_test.cc",
@@ -429,13 +425,20 @@
 
     shared_libs: [
         "libartd-compiler",
-        "libartd-simulator",
         "libvixld-arm",
         "libvixld-arm64",
 
         "libbacktrace",
         "libnativeloader",
     ],
+
+    target: {
+        host: {
+            shared_libs: [
+                "libartd-simulator",
+            ],
+        },
+    },
 }
 
 art_cc_test {
@@ -447,7 +450,6 @@
     codegen: {
         arm: {
             srcs: [
-                "utils/arm/assembler_thumb2_test.cc",
                 "utils/assembler_thumb_test.cc",
             ],
         },
@@ -455,6 +457,7 @@
             srcs: [
                 "optimizing/emit_swap_mips_test.cc",
                 "utils/mips/assembler_mips_test.cc",
+                "utils/mips/assembler_mips32r5_test.cc",
                 "utils/mips/assembler_mips32r6_test.cc",
             ],
         },
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 2db99cd..fba1136 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -22,12 +22,14 @@
 #include "art_method-inl.h"
 #include "base/logging.h"
 #include "base/mutex.h"
+#include "bytecode_utils.h"
 #include "compiled_method.h"
 #include "dex_file-inl.h"
 #include "dex_instruction-inl.h"
 #include "driver/compiler_driver.h"
 #include "driver/dex_compilation_unit.h"
 #include "mirror/dex_cache.h"
+#include "quicken_info.h"
 #include "thread-current-inl.h"
 
 namespace art {
@@ -110,13 +112,9 @@
 
 void DexCompiler::Compile() {
   DCHECK_EQ(dex_to_dex_compilation_level_, DexToDexCompilationLevel::kOptimize);
-  const DexFile::CodeItem* code_item = unit_.GetCodeItem();
-  const uint16_t* insns = code_item->insns_;
-  const uint32_t insns_size = code_item->insns_size_in_code_units_;
-  Instruction* inst = const_cast<Instruction*>(Instruction::At(insns));
-
-  for (uint32_t dex_pc = 0; dex_pc < insns_size;
-       inst = const_cast<Instruction*>(inst->Next()), dex_pc = inst->GetDexPc(insns)) {
+  for (CodeItemIterator it(*unit_.GetCodeItem()); !it.Done(); it.Advance()) {
+    Instruction* inst = const_cast<Instruction*>(&it.CurrentInstruction());
+    const uint32_t dex_pc = it.CurrentDexPc();
     switch (inst->Opcode()) {
       case Instruction::RETURN_VOID:
         CompileReturnVoid(inst, dex_pc);
@@ -124,6 +122,11 @@
 
       case Instruction::CHECK_CAST:
         inst = CompileCheckCast(inst, dex_pc);
+        if (inst->Opcode() == Instruction::NOP) {
+          // We turned the CHECK_CAST into two NOPs, avoid visiting the second NOP twice since this
+          // would add 2 quickening info entries.
+          it.Advance();
+        }
         break;
 
       case Instruction::IGET:
@@ -190,7 +193,14 @@
         CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE_QUICK, true);
         break;
 
+      case Instruction::NOP:
+        // We need to differentiate between check cast inserted NOP and normal NOP, put an invalid
+        // index in the map for normal nops. This should be rare in real code.
+        quickened_info_.push_back(QuickenedInfo(dex_pc, DexFile::kDexNoIndex16));
+        break;
+
       default:
+        DCHECK(!inst->IsQuickened());
         // Nothing to do.
         break;
     }
@@ -348,10 +358,26 @@
     }
 
     // Create a `CompiledMethod`, with the quickened information in the vmap table.
-    Leb128EncodingVector<> builder;
+    if (kIsDebugBuild) {
+      // Double check that the counts line up with the size of the quicken info.
+      size_t quicken_count = 0;
+      for (CodeItemIterator it(*code_item); !it.Done(); it.Advance()) {
+        if (QuickenInfoTable::NeedsIndexForInstruction(&it.CurrentInstruction())) {
+          ++quicken_count;
+        }
+      }
+      CHECK_EQ(quicken_count, dex_compiler.GetQuickenedInfo().size());
+    }
+    std::vector<uint8_t> quicken_data;
     for (QuickenedInfo info : dex_compiler.GetQuickenedInfo()) {
-      builder.PushBackUnsigned(info.dex_pc);
-      builder.PushBackUnsigned(info.dex_member_index);
+      // Dex pc is not serialized, only used for checking the instructions. Since we access the
+      // array based on the index of the quickened instruction, the indexes must line up perfectly.
+      // The reader side uses the NeedsIndexForInstruction function too.
+      const Instruction* inst = Instruction::At(code_item->insns_ + info.dex_pc);
+      CHECK(QuickenInfoTable::NeedsIndexForInstruction(inst)) << inst->Opcode();
+      // Add the index.
+      quicken_data.push_back(static_cast<uint8_t>(info.dex_member_index >> 0));
+      quicken_data.push_back(static_cast<uint8_t>(info.dex_member_index >> 8));
     }
     InstructionSet instruction_set = driver->GetInstructionSet();
     if (instruction_set == kThumb2) {
@@ -366,7 +392,7 @@
         0,
         0,
         ArrayRef<const uint8_t>(),                   // method_info
-        ArrayRef<const uint8_t>(builder.GetData()),  // vmap_table
+        ArrayRef<const uint8_t>(quicken_data),       // vmap_table
         ArrayRef<const uint8_t>(),                   // cfi data
         ArrayRef<const LinkerPatch>());
   }
diff --git a/compiler/dex/quick_compiler_callbacks.cc b/compiler/dex/quick_compiler_callbacks.cc
index 932eb51..b1006b2 100644
--- a/compiler/dex/quick_compiler_callbacks.cc
+++ b/compiler/dex/quick_compiler_callbacks.cc
@@ -22,11 +22,15 @@
 namespace art {
 
 void QuickCompilerCallbacks::MethodVerified(verifier::MethodVerifier* verifier) {
-  verification_results_->ProcessVerifiedMethod(verifier);
+  if (verification_results_ != nullptr) {
+    verification_results_->ProcessVerifiedMethod(verifier);
+  }
 }
 
 void QuickCompilerCallbacks::ClassRejected(ClassReference ref) {
-  verification_results_->AddRejectedClass(ref);
+  if (verification_results_ != nullptr) {
+    verification_results_->AddRejectedClass(ref);
+  }
 }
 
 }  // namespace art
diff --git a/compiler/dex/quick_compiler_callbacks.h b/compiler/dex/quick_compiler_callbacks.h
index db0fdaa..2100522 100644
--- a/compiler/dex/quick_compiler_callbacks.h
+++ b/compiler/dex/quick_compiler_callbacks.h
@@ -30,9 +30,7 @@
                            CompilerCallbacks::CallbackMode mode)
         : CompilerCallbacks(mode),
           verification_results_(verification_results),
-          verifier_deps_(nullptr) {
-      CHECK(verification_results != nullptr);
-    }
+          verifier_deps_(nullptr) {}
 
     ~QuickCompilerCallbacks() { }
 
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 04ceca0..beb3439 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -24,7 +24,7 @@
 #include "runtime.h"
 #include "thread.h"
 #include "thread-current-inl.h"
-#include "utils/atomic_method_ref_map-inl.h"
+#include "utils/atomic_dex_ref_map-inl.h"
 #include "verified_method.h"
 #include "verifier/method_verifier-inl.h"
 
@@ -38,7 +38,7 @@
 VerificationResults::~VerificationResults() {
   WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
   STLDeleteValues(&verified_methods_);
-  atomic_verified_methods_.Visit([](const MethodReference& ref ATTRIBUTE_UNUSED,
+  atomic_verified_methods_.Visit([](const DexFileReference& ref ATTRIBUTE_UNUSED,
                                     const VerifiedMethod* method) {
     delete method;
   });
@@ -46,22 +46,28 @@
 
 void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
   DCHECK(method_verifier != nullptr);
+  if (!compiler_options_->IsAnyCompilationEnabled()) {
+    // Verified methods are only required for quickening and compilation.
+    return;
+  }
   MethodReference ref = method_verifier->GetMethodReference();
   std::unique_ptr<const VerifiedMethod> verified_method(VerifiedMethod::Create(method_verifier));
   if (verified_method == nullptr) {
     // We'll punt this later.
     return;
   }
-  AtomicMap::InsertResult result = atomic_verified_methods_.Insert(ref,
-                                                                   /*expected*/ nullptr,
-                                                                   verified_method.get());
+  AtomicMap::InsertResult result = atomic_verified_methods_.Insert(
+      DexFileReference(ref.dex_file, ref.dex_method_index),
+      /*expected*/ nullptr,
+      verified_method.get());
   const VerifiedMethod* existing = nullptr;
   bool inserted;
   if (result != AtomicMap::kInsertResultInvalidDexFile) {
     inserted = (result == AtomicMap::kInsertResultSuccess);
     if (!inserted) {
       // Rare case.
-      CHECK(atomic_verified_methods_.Get(ref, &existing));
+      CHECK(atomic_verified_methods_.Get(DexFileReference(ref.dex_file, ref.dex_method_index),
+                                         &existing));
       CHECK_NE(verified_method.get(), existing);
     }
   } else {
@@ -98,7 +104,8 @@
 
 const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) {
   const VerifiedMethod* ret = nullptr;
-  if (atomic_verified_methods_.Get(ref, &ret)) {
+  DCHECK(compiler_options_->IsAnyCompilationEnabled());
+  if (atomic_verified_methods_.Get(DexFileReference(ref.dex_file, ref.dex_method_index), &ret)) {
     return ret;
   }
   ReaderMutexLock mu(Thread::Current(), verified_methods_lock_);
@@ -112,7 +119,9 @@
   // at runtime.
   std::unique_ptr<VerifiedMethod> verified_method = std::make_unique<VerifiedMethod>(
       /* encountered_error_types */ 0, /* has_runtime_throw */ false);
-  if (atomic_verified_methods_.Insert(ref, /*expected*/ nullptr, verified_method.get()) ==
+  if (atomic_verified_methods_.Insert(DexFileReference(ref.dex_file, ref.dex_method_index),
+                                      /*expected*/ nullptr,
+                                      verified_method.get()) ==
           AtomicMap::InsertResult::kInsertResultSuccess) {
     verified_method.release();
   }
@@ -145,7 +154,7 @@
 }
 
 void VerificationResults::AddDexFile(const DexFile* dex_file) {
-  atomic_verified_methods_.AddDexFile(dex_file);
+  atomic_verified_methods_.AddDexFile(dex_file, dex_file->NumMethodIds());
   WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
   // There can be some verified methods that are already registered for the dex_file since we set
   // up well known classes earlier. Remove these and put them in the array so that we don't
@@ -153,7 +162,9 @@
   for (auto it = verified_methods_.begin(); it != verified_methods_.end(); ) {
     MethodReference ref = it->first;
     if (ref.dex_file == dex_file) {
-      CHECK(atomic_verified_methods_.Insert(ref, nullptr, it->second) ==
+      CHECK(atomic_verified_methods_.Insert(DexFileReference(ref.dex_file, ref.dex_method_index),
+                                            nullptr,
+                                            it->second) ==
           AtomicMap::kInsertResultSuccess);
       it = verified_methods_.erase(it);
     } else {
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index 22749fa..5a03599 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -26,7 +26,7 @@
 #include "class_reference.h"
 #include "method_reference.h"
 #include "safe_map.h"
-#include "utils/atomic_method_ref_map.h"
+#include "utils/atomic_dex_ref_map.h"
 
 namespace art {
 
@@ -64,7 +64,7 @@
 
  private:
   // Verified methods. The method array is fixed to avoid needing a lock to extend it.
-  using AtomicMap = AtomicMethodRefMap<const VerifiedMethod*>;
+  using AtomicMap = AtomicDexRefMap<const VerifiedMethod*>;
   using VerifiedMethodMap = SafeMap<MethodReference,
                                     const VerifiedMethod*,
                                     MethodReferenceComparator>;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index f834f30b..83d7a3d 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -71,7 +71,7 @@
 #include "thread_pool.h"
 #include "trampolines/trampoline_compiler.h"
 #include "transaction.h"
-#include "utils/atomic_method_ref_map-inl.h"
+#include "utils/atomic_dex_ref_map-inl.h"
 #include "utils/dex_cache_arrays_layout-inl.h"
 #include "utils/swap_space.h"
 #include "vdex_file.h"
@@ -87,6 +87,10 @@
 // Print additional info during profile guided compilation.
 static constexpr bool kDebugProfileGuidedCompilation = false;
 
+// Max encoded fields allowed for initializing app image. Hardcode the number for now
+// because 5000 should be large enough.
+static constexpr uint32_t kMaxEncodedFields = 5000;
+
 static double Percentage(size_t x, size_t y) {
   return 100.0 * (static_cast<double>(x)) / (static_cast<double>(x + y));
 }
@@ -317,7 +321,7 @@
 }
 
 CompilerDriver::~CompilerDriver() {
-  compiled_methods_.Visit([this](const MethodReference& ref ATTRIBUTE_UNUSED,
+  compiled_methods_.Visit([this](const DexFileReference& ref ATTRIBUTE_UNUSED,
                                  CompiledMethod* method) {
     if (method != nullptr) {
       CompiledMethod::ReleaseSwapAllocatedCompiledMethod(this, method);
@@ -510,8 +514,9 @@
     // TODO: Refactor the compilation to avoid having to distinguish the two passes
     // here. That should be done on a higher level. http://b/29089975
     if (driver->GetCurrentDexToDexMethods()->IsBitSet(method_idx)) {
-      const VerifiedMethod* verified_method =
-          driver->GetVerificationResults()->GetVerifiedMethod(method_ref);
+      VerificationResults* results = driver->GetVerificationResults();
+      DCHECK(results != nullptr);
+      const VerifiedMethod* verified_method = results->GetVerifiedMethod(method_ref);
       // Do not optimize if a VerifiedMethod is missing. SafeCast elision,
       // for example, relies on it.
       compiled_method = optimizer::ArtCompileDEX(
@@ -572,12 +577,12 @@
   } else if ((access_flags & kAccAbstract) != 0) {
     // Abstract methods don't have code.
   } else {
-    const VerifiedMethod* verified_method =
-        driver->GetVerificationResults()->GetVerifiedMethod(method_ref);
+    VerificationResults* results = driver->GetVerificationResults();
+    DCHECK(results != nullptr);
+    const VerifiedMethod* verified_method = results->GetVerifiedMethod(method_ref);
     bool compile = compilation_enabled &&
         // Basic checks, e.g., not <clinit>.
-        driver->GetVerificationResults()
-            ->IsCandidateForCompilation(method_ref, access_flags) &&
+        results->IsCandidateForCompilation(method_ref, access_flags) &&
         // Did not fail to create VerifiedMethod metadata.
         verified_method != nullptr &&
         // Do not have failures that should punt to the interpreter.
@@ -886,17 +891,18 @@
                                 TimingLogger* timings) {
   CheckThreadPools();
 
-  for (const DexFile* dex_file : dex_files) {
-    // Can be already inserted if the caller is CompileOne. This happens for gtests.
-    if (!compiled_methods_.HaveDexFile(dex_file)) {
-      compiled_methods_.AddDexFile(dex_file);
-    }
-  }
-
   LoadImageClasses(timings);
   VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false);
 
   if (compiler_options_->IsAnyCompilationEnabled()) {
+    // Avoid adding the dex files in the case where we aren't going to add compiled methods.
+    // This reduces RAM usage for this case.
+    for (const DexFile* dex_file : dex_files) {
+      // Can be already inserted if the caller is CompileOne. This happens for gtests.
+      if (!compiled_methods_.HaveDexFile(dex_file)) {
+        compiled_methods_.AddDexFile(dex_file, dex_file->NumMethodIds());
+      }
+    }
     // Resolve eagerly to prepare for compilation.
     Resolve(class_loader, dex_files, timings);
     VLOG(compiler) << "Resolve: " << GetMemoryUsageString(false);
@@ -2241,7 +2247,7 @@
     const bool is_boot_image = manager_->GetCompiler()->GetCompilerOptions().IsBootImage();
     const bool is_app_image = manager_->GetCompiler()->GetCompilerOptions().IsAppImage();
 
-    mirror::Class::Status old_status = klass->GetStatus();;
+    mirror::Class::Status old_status = klass->GetStatus();
     // Only try to initialize classes that were successfully verified.
     if (klass->IsVerified()) {
       // Don't initialize classes in boot space when compiling app image
@@ -2273,11 +2279,17 @@
         }
         // Otherwise it's in app image but superclasses can't be initialized, no need to proceed.
         old_status = klass->GetStatus();
+
+        bool too_many_encoded_fields = false;
+        if (!is_boot_image && klass->NumStaticFields() > kMaxEncodedFields) {
+          too_many_encoded_fields = true;
+        }
         // If the class was not initialized, we can proceed to see if we can initialize static
-        // fields.
+        // fields. Limit the max number of encoded fields.
         if (!klass->IsInitialized() &&
             (is_app_image || is_boot_image) &&
             is_superclass_initialized &&
+            !too_many_encoded_fields &&
             manager_->GetCompiler()->IsImageClass(descriptor)) {
           bool can_init_static_fields = false;
           if (is_boot_image) {
@@ -2353,6 +2365,14 @@
             }
           }
         }
+        // If the class still isn't initialized, at least try some checks that initialization
+        // would do so they can be skipped at runtime.
+        if (!klass->IsInitialized() &&
+            manager_->GetClassLinker()->ValidateSuperClassDescriptors(klass)) {
+          old_status = mirror::Class::kStatusSuperclassValidated;
+        } else {
+          soa.Self()->ClearException();
+        }
         soa.Self()->AssertNoPendingException();
       }
     }
@@ -2415,30 +2435,6 @@
     }
   }
 
-  bool NoPotentialInternStrings(Handle<mirror::Class> klass,
-                                Handle<mirror::ClassLoader>* class_loader)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    StackHandleScope<1> hs(Thread::Current());
-    Handle<mirror::DexCache> h_dex_cache = hs.NewHandle(klass->GetDexCache());
-    const DexFile* dex_file = h_dex_cache->GetDexFile();
-    const DexFile::ClassDef* class_def = klass->GetClassDef();
-    annotations::RuntimeEncodedStaticFieldValueIterator value_it(*dex_file,
-                                                                 &h_dex_cache,
-                                                                 class_loader,
-                                                                 manager_->GetClassLinker(),
-                                                                 *class_def);
-
-    const auto jString = annotations::RuntimeEncodedStaticFieldValueIterator::kString;
-    for ( ; value_it.HasNext(); value_it.Next()) {
-      if (value_it.GetValueType() == jString) {
-        // We don't want cache the static encoded strings which is a potential intern.
-        return false;
-      }
-    }
-
-    return true;
-  }
-
   bool ResolveTypesOfMethods(Thread* self, ArtMethod* m)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     auto rtn_type = m->GetReturnType(true);  // return value is discarded because resolve will be done internally.
@@ -2568,7 +2564,7 @@
       }
     }
 
-    return NoPotentialInternStrings(klass, class_loader);
+    return true;
   }
 
   const ParallelCompilationManager* const manager_;
@@ -2852,9 +2848,10 @@
                                        size_t non_relative_linker_patch_count) {
   DCHECK(GetCompiledMethod(method_ref) == nullptr)
       << method_ref.dex_file->PrettyMethod(method_ref.dex_method_index);
-  MethodTable::InsertResult result = compiled_methods_.Insert(method_ref,
-                                                              /*expected*/ nullptr,
-                                                              compiled_method);
+  MethodTable::InsertResult result = compiled_methods_.Insert(
+      DexFileReference(method_ref.dex_file, method_ref.dex_method_index),
+      /*expected*/ nullptr,
+      compiled_method);
   CHECK(result == MethodTable::kInsertResultSuccess);
   non_relative_linker_patch_count_.FetchAndAddRelaxed(non_relative_linker_patch_count);
   DCHECK(GetCompiledMethod(method_ref) != nullptr)
@@ -2874,13 +2871,14 @@
 
 void CompilerDriver::RecordClassStatus(ClassReference ref, mirror::Class::Status status) {
   switch (status) {
-    case mirror::Class::kStatusNotReady:
     case mirror::Class::kStatusErrorResolved:
     case mirror::Class::kStatusErrorUnresolved:
+    case mirror::Class::kStatusNotReady:
+    case mirror::Class::kStatusResolved:
     case mirror::Class::kStatusRetryVerificationAtRuntime:
     case mirror::Class::kStatusVerified:
+    case mirror::Class::kStatusSuperclassValidated:
     case mirror::Class::kStatusInitialized:
-    case mirror::Class::kStatusResolved:
       break;  // Expected states.
     default:
       LOG(FATAL) << "Unexpected class status for class "
@@ -2901,7 +2899,7 @@
 
 CompiledMethod* CompilerDriver::GetCompiledMethod(MethodReference ref) const {
   CompiledMethod* compiled_method = nullptr;
-  compiled_methods_.Get(ref, &compiled_method);
+  compiled_methods_.Get(DexFileReference(ref.dex_file, ref.dex_method_index), &compiled_method);
   return compiled_method;
 }
 
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index e9e7378..a3272d3 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -39,7 +39,7 @@
 #include "os.h"
 #include "safe_map.h"
 #include "thread_pool.h"
-#include "utils/atomic_method_ref_map.h"
+#include "utils/atomic_dex_ref_map.h"
 #include "utils/dex_cache_arrays_layout.h"
 
 namespace art {
@@ -489,7 +489,7 @@
   mutable Mutex compiled_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
   ClassStateTable compiled_classes_ GUARDED_BY(compiled_classes_lock_);
 
-  typedef AtomicMethodRefMap<CompiledMethod*> MethodTable;
+  typedef AtomicDexRefMap<CompiledMethod*> MethodTable;
 
  private:
   // All method references that this compiler has compiled.
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 1c73dfa..f92bf95 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -44,6 +44,7 @@
 #include "gc/accounting/space_bitmap-inl.h"
 #include "gc/collector/concurrent_copying.h"
 #include "gc/heap.h"
+#include "gc/heap-visit-objects-inl.h"
 #include "gc/space/large_object_space.h"
 #include "gc/space/space-inl.h"
 #include "gc/verification.h"
@@ -117,19 +118,17 @@
   return false;
 }
 
-static void ClearDexFileCookieCallback(Object* obj, void* arg ATTRIBUTE_UNUSED)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  DCHECK(obj != nullptr);
-  Class* klass = obj->GetClass();
-  if (klass == WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexFile)) {
-    ArtField* field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
-    // Null out the cookie to enable determinism. b/34090128
-    field->SetObject</*kTransactionActive*/false>(obj, nullptr);
-  }
-}
-
 static void ClearDexFileCookies() REQUIRES_SHARED(Locks::mutator_lock_) {
-  Runtime::Current()->GetHeap()->VisitObjects(ClearDexFileCookieCallback, nullptr);
+  auto visitor = [](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK(obj != nullptr);
+    Class* klass = obj->GetClass();
+    if (klass == WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexFile)) {
+      ArtField* field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
+      // Null out the cookie to enable determinism. b/34090128
+      field->SetObject</*kTransactionActive*/false>(obj, nullptr);
+    }
+  };
+  Runtime::Current()->GetHeap()->VisitObjects(visitor);
 }
 
 bool ImageWriter::PrepareImageAddressSpace() {
@@ -737,16 +736,82 @@
   return IsBootClassLoaderClass(klass) && !IsInBootImage(klass);
 }
 
+// This visitor follows the references of an instance, recursively then prune this class
+// if a type of any field is pruned.
+class ImageWriter::PruneObjectReferenceVisitor {
+ public:
+  PruneObjectReferenceVisitor(ImageWriter* image_writer,
+                        bool* early_exit,
+                        std::unordered_set<mirror::Object*>* visited,
+                        bool* result)
+      : image_writer_(image_writer), early_exit_(early_exit), visited_(visited), result_(result) {}
+
+  ALWAYS_INLINE void VisitRootIfNonNull(
+      mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const
+      REQUIRES_SHARED(Locks::mutator_lock_) { }
+
+  ALWAYS_INLINE void VisitRoot(
+      mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const
+      REQUIRES_SHARED(Locks::mutator_lock_) { }
+
+  ALWAYS_INLINE void operator() (ObjPtr<mirror::Object> obj,
+                                 MemberOffset offset,
+                                 bool is_static ATTRIBUTE_UNUSED) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    mirror::Object* ref =
+        obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(offset);
+    if (ref == nullptr || visited_->find(ref) != visited_->end()) {
+      return;
+    }
+
+    ObjPtr<mirror::Class> klass = ref->IsClass() ? ref->AsClass() : ref->GetClass();
+    if (klass == mirror::Method::StaticClass() || klass == mirror::Constructor::StaticClass()) {
+      // Prune all classes using reflection because the content they held will not be fixup.
+      *result_ = true;
+    }
+
+    // Record the object visited in case of circular reference.
+    visited_->emplace(ref);
+    if (ref->IsClass()) {
+      *result_ = *result_ ||
+          image_writer_->PruneAppImageClassInternal(ref->AsClass(), early_exit_, visited_);
+    } else {
+      *result_ = *result_ ||
+          image_writer_->PruneAppImageClassInternal(klass, early_exit_, visited_);
+      ref->VisitReferences(*this, *this);
+    }
+    // Clean up before exit for next call of this function.
+    visited_->erase(ref);
+  }
+
+  ALWAYS_INLINE void operator() (ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
+                                 ObjPtr<mirror::Reference> ref) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    operator()(ref, mirror::Reference::ReferentOffset(), /* is_static */ false);
+  }
+
+  ALWAYS_INLINE bool GetResult() const {
+    return result_;
+  }
+
+ private:
+  ImageWriter* image_writer_;
+  bool* early_exit_;
+  std::unordered_set<mirror::Object*>* visited_;
+  bool* const result_;
+};
+
+
 bool ImageWriter::PruneAppImageClass(ObjPtr<mirror::Class> klass) {
   bool early_exit = false;
-  std::unordered_set<mirror::Class*> visited;
+  std::unordered_set<mirror::Object*> visited;
   return PruneAppImageClassInternal(klass, &early_exit, &visited);
 }
 
 bool ImageWriter::PruneAppImageClassInternal(
     ObjPtr<mirror::Class> klass,
     bool* early_exit,
-    std::unordered_set<mirror::Class*>* visited) {
+    std::unordered_set<mirror::Object*>* visited) {
   DCHECK(early_exit != nullptr);
   DCHECK(visited != nullptr);
   DCHECK(compile_app_image_);
@@ -807,9 +872,18 @@
                                                         &my_early_exit,
                                                         visited);
         } else {
-          result = result || PruneAppImageClassInternal(ref->GetClass(),
+          mirror::Class* type = ref->GetClass();
+          result = result || PruneAppImageClassInternal(type,
                                                         &my_early_exit,
                                                         visited);
+          if (!result) {
+            // For non-class case, also go through all the types mentioned by it's fields'
+            // references recursively to decide whether to keep this class.
+            bool tmp = false;
+            PruneObjectReferenceVisitor visitor(this, &my_early_exit, visited, &tmp);
+            ref->VisitReferences(visitor, visitor);
+            result = result || tmp;
+          }
         }
       }
       field_offset = MemberOffset(field_offset.Uint32Value() +
@@ -1101,21 +1175,19 @@
 
 void ImageWriter::CheckNonImageClassesRemoved() {
   if (compiler_driver_.GetImageClasses() != nullptr) {
+    auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+      if (obj->IsClass() && !IsInBootImage(obj)) {
+        Class* klass = obj->AsClass();
+        if (!KeepClass(klass)) {
+          DumpImageClasses();
+          std::string temp;
+          CHECK(KeepClass(klass))
+              << Runtime::Current()->GetHeap()->GetVerification()->FirstPathFromRootSet(klass);
+        }
+      }
+    };
     gc::Heap* heap = Runtime::Current()->GetHeap();
-    heap->VisitObjects(CheckNonImageClassesRemovedCallback, this);
-  }
-}
-
-void ImageWriter::CheckNonImageClassesRemovedCallback(Object* obj, void* arg) {
-  ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg);
-  if (obj->IsClass() && !image_writer->IsInBootImage(obj)) {
-    Class* klass = obj->AsClass();
-    if (!image_writer->KeepClass(klass)) {
-      image_writer->DumpImageClasses();
-      std::string temp;
-      CHECK(image_writer->KeepClass(klass))
-          << Runtime::Current()->GetHeap()->GetVerification()->FirstPathFromRootSet(klass);
-    }
+    heap->VisitObjects(visitor);
   }
 }
 
@@ -1457,26 +1529,6 @@
   offset += ArtMethod::Size(target_ptr_size_);
 }
 
-void ImageWriter::EnsureBinSlotAssignedCallback(mirror::Object* obj, void* arg) {
-  ImageWriter* writer = reinterpret_cast<ImageWriter*>(arg);
-  DCHECK(writer != nullptr);
-  if (!Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(obj)) {
-    CHECK(writer->IsImageBinSlotAssigned(obj)) << mirror::Object::PrettyTypeOf(obj) << " " << obj;
-  }
-}
-
-void ImageWriter::DeflateMonitorCallback(mirror::Object* obj, void* arg ATTRIBUTE_UNUSED) {
-  Monitor::Deflate(Thread::Current(), obj);
-}
-
-void ImageWriter::UnbinObjectsIntoOffsetCallback(mirror::Object* obj, void* arg) {
-  ImageWriter* writer = reinterpret_cast<ImageWriter*>(arg);
-  DCHECK(writer != nullptr);
-  if (!writer->IsInBootImage(obj)) {
-    writer->UnbinObjectsIntoOffset(obj);
-  }
-}
-
 void ImageWriter::UnbinObjectsIntoOffset(mirror::Object* obj) {
   DCHECK(!IsInBootImage(obj));
   CHECK(obj != nullptr);
@@ -1611,7 +1663,12 @@
 
   // Deflate monitors before we visit roots since deflating acquires the monitor lock. Acquiring
   // this lock while holding other locks may cause lock order violations.
-  heap->VisitObjects(DeflateMonitorCallback, this);
+  {
+    auto deflate_monitor = [](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+      Monitor::Deflate(Thread::Current(), obj);
+    };
+    heap->VisitObjects(deflate_monitor);
+  }
 
   // Work list of <object, oat_index> for objects. Everything on the stack must already be
   // assigned a bin slot.
@@ -1673,7 +1730,15 @@
   }
 
   // Verify that all objects have assigned image bin slots.
-  heap->VisitObjects(EnsureBinSlotAssignedCallback, this);
+  {
+    auto ensure_bin_slots_assigned = [&](mirror::Object* obj)
+        REQUIRES_SHARED(Locks::mutator_lock_) {
+      if (!Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(obj)) {
+        CHECK(IsImageBinSlotAssigned(obj)) << mirror::Object::PrettyTypeOf(obj) << " " << obj;
+      }
+    };
+    heap->VisitObjects(ensure_bin_slots_assigned);
+  }
 
   // Calculate size of the dex cache arrays slot and prepare offsets.
   PrepareDexCacheArraySlots();
@@ -1737,7 +1802,15 @@
   }
 
   // Transform each object's bin slot into an offset which will be used to do the final copy.
-  heap->VisitObjects(UnbinObjectsIntoOffsetCallback, this);
+  {
+    auto unbin_objects_into_offset = [&](mirror::Object* obj)
+        REQUIRES_SHARED(Locks::mutator_lock_) {
+      if (!IsInBootImage(obj)) {
+        UnbinObjectsIntoOffset(obj);
+      }
+    };
+    heap->VisitObjects(unbin_objects_into_offset);
+  }
 
   size_t i = 0;
   for (ImageInfo& image_info : image_infos_) {
@@ -2044,8 +2117,11 @@
 }
 
 void ImageWriter::CopyAndFixupObjects() {
-  gc::Heap* heap = Runtime::Current()->GetHeap();
-  heap->VisitObjects(CopyAndFixupObjectsCallback, this);
+  auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK(obj != nullptr);
+    CopyAndFixupObject(obj);
+  };
+  Runtime::Current()->GetHeap()->VisitObjects(visitor);
   // Fix up the object previously had hash codes.
   for (const auto& hash_pair : saved_hashcode_map_) {
     Object* obj = hash_pair.first;
@@ -2055,12 +2131,6 @@
   saved_hashcode_map_.clear();
 }
 
-void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) {
-  DCHECK(obj != nullptr);
-  DCHECK(arg != nullptr);
-  reinterpret_cast<ImageWriter*>(arg)->CopyAndFixupObject(obj);
-}
-
 void ImageWriter::FixupPointerArray(mirror::Object* dst,
                                     mirror::PointerArray* arr,
                                     mirror::Class* klass,
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 5e2db7d..ee6fc1d 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -397,8 +397,6 @@
 
   // Verify unwanted classes removed.
   void CheckNonImageClassesRemoved() REQUIRES_SHARED(Locks::mutator_lock_);
-  static void CheckNonImageClassesRemovedCallback(mirror::Object* obj, void* arg)
-      REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Lays out where the image objects will be at runtime.
   void CalculateNewObjectOffsets()
@@ -414,18 +412,9 @@
   void UnbinObjectsIntoOffset(mirror::Object* obj)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  static void EnsureBinSlotAssignedCallback(mirror::Object* obj, void* arg)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-  static void DeflateMonitorCallback(mirror::Object* obj, void* arg)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-  static void UnbinObjectsIntoOffsetCallback(mirror::Object* obj, void* arg)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Creates the contiguous image in memory and adjusts pointers.
   void CopyAndFixupNativeData(size_t oat_index) REQUIRES_SHARED(Locks::mutator_lock_);
   void CopyAndFixupObjects() REQUIRES_SHARED(Locks::mutator_lock_);
-  static void CopyAndFixupObjectsCallback(mirror::Object* obj, void* arg)
-      REQUIRES_SHARED(Locks::mutator_lock_);
   void CopyAndFixupObject(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_);
   void CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy, const ImageInfo& image_info)
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -484,7 +473,7 @@
   // early_exit is true if we had a cyclic dependency anywhere down the chain.
   bool PruneAppImageClassInternal(ObjPtr<mirror::Class> klass,
                                   bool* early_exit,
-                                  std::unordered_set<mirror::Class*>* visited)
+                                  std::unordered_set<mirror::Object*>* visited)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool IsMultiImage() const {
@@ -621,6 +610,7 @@
   class PruneClassLoaderClassesVisitor;
   class RegisterBootClassPathClassesVisitor;
   class VisitReferencesVisitor;
+  class PruneObjectReferenceVisitor;
 
   DISALLOW_COPY_AND_ASSIGN(ImageWriter);
 };
diff --git a/compiler/jni/jni_cfi_test.cc b/compiler/jni/jni_cfi_test.cc
index 28b7290..b552a6e 100644
--- a/compiler/jni/jni_cfi_test.cc
+++ b/compiler/jni/jni_cfi_test.cc
@@ -110,20 +110,35 @@
   }
 
 #ifdef ART_ENABLE_CODEGEN_arm
+// Run the tests for ARM only with Baker read barriers, as the
+// expected generated code contains a Marking Register refresh
+// instruction.
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
 TEST_ISA(kThumb2)
 #endif
+#endif
+
 #ifdef ART_ENABLE_CODEGEN_arm64
+// Run the tests for ARM64 only with Baker read barriers, as the
+// expected generated code contains a Marking Register refresh
+// instruction.
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
 TEST_ISA(kArm64)
 #endif
+#endif
+
 #ifdef ART_ENABLE_CODEGEN_x86
 TEST_ISA(kX86)
 #endif
+
 #ifdef ART_ENABLE_CODEGEN_x86_64
 TEST_ISA(kX86_64)
 #endif
+
 #ifdef ART_ENABLE_CODEGEN_mips
 TEST_ISA(kMips)
 #endif
+
 #ifdef ART_ENABLE_CODEGEN_mips64
 TEST_ISA(kMips64)
 #endif
diff --git a/compiler/jni/jni_cfi_test_expected.inc b/compiler/jni/jni_cfi_test_expected.inc
index 2710ae9..d641fe4 100644
--- a/compiler/jni/jni_cfi_test_expected.inc
+++ b/compiler/jni/jni_cfi_test_expected.inc
@@ -1,7 +1,8 @@
 static constexpr uint8_t expected_asm_kThumb2[] = {
     0x2D, 0xE9, 0xE0, 0x4D, 0x2D, 0xED, 0x10, 0x8A, 0x89, 0xB0, 0x00, 0x90,
     0x21, 0x91, 0x8D, 0xED, 0x22, 0x0A, 0x23, 0x92, 0x24, 0x93, 0x88, 0xB0,
-    0x08, 0xB0, 0x09, 0xB0, 0xBD, 0xEC, 0x10, 0x8A, 0xBD, 0xE8, 0xE0, 0x8D,
+    0x08, 0xB0, 0x09, 0xB0, 0xBD, 0xEC, 0x10, 0x8A, 0xBD, 0xE8, 0xE0, 0x4D,
+    0xD9, 0xF8, 0x34, 0x80, 0x70, 0x47,
 };
 static constexpr uint8_t expected_cfi_kThumb2[] = {
     0x44, 0x0E, 0x1C, 0x85, 0x07, 0x86, 0x06, 0x87, 0x05, 0x88, 0x04, 0x8A,
@@ -13,10 +14,10 @@
     0x4E, 0x0E, 0xA0, 0x01, 0x42, 0x0E, 0x80, 0x01, 0x0A, 0x42, 0x0E, 0x5C,
     0x44, 0x0E, 0x1C, 0x06, 0x50, 0x06, 0x51, 0x06, 0x52, 0x06, 0x53, 0x06,
     0x54, 0x06, 0x55, 0x06, 0x56, 0x06, 0x57, 0x06, 0x58, 0x06, 0x59, 0x06,
-    0x5A, 0x06, 0x5B, 0x06, 0x5C, 0x06, 0x5D, 0x06, 0x5E, 0x06, 0x5F, 0x44,
+    0x5A, 0x06, 0x5B, 0x06, 0x5C, 0x06, 0x5D, 0x06, 0x5E, 0x06, 0x5F, 0x4A,
     0x0B, 0x0E, 0x80, 0x01,
 };
-// 0x00000000: push {r5, r6, r7, r8, r10, r11, lr}
+// 0x00000000: push {r5,r6,r7,r8,r10,r11,lr}
 // 0x00000004: .cfi_def_cfa_offset: 28
 // 0x00000004: .cfi_offset: r5 at cfa-28
 // 0x00000004: .cfi_offset: r6 at cfa-24
@@ -25,7 +26,7 @@
 // 0x00000004: .cfi_offset: r10 at cfa-12
 // 0x00000004: .cfi_offset: r11 at cfa-8
 // 0x00000004: .cfi_offset: r14 at cfa-4
-// 0x00000004: vpush.f32 {s16-s31}
+// 0x00000004: vpush {s16-s31}
 // 0x00000008: .cfi_def_cfa_offset: 92
 // 0x00000008: .cfi_offset_extended: r80 at cfa-92
 // 0x00000008: .cfi_offset_extended: r81 at cfa-88
@@ -43,21 +44,21 @@
 // 0x00000008: .cfi_offset_extended: r93 at cfa-40
 // 0x00000008: .cfi_offset_extended: r94 at cfa-36
 // 0x00000008: .cfi_offset_extended: r95 at cfa-32
-// 0x00000008: sub sp, sp, #36
+// 0x00000008: sub sp, #36
 // 0x0000000a: .cfi_def_cfa_offset: 128
-// 0x0000000a: str r0, [sp, #0]
+// 0x0000000a: str r0, [sp]
 // 0x0000000c: str r1, [sp, #132]
-// 0x0000000e: vstr.f32 s0, [sp, #136]
+// 0x0000000e: vstr s0, [sp, #136]
 // 0x00000012: str r2, [sp, #140]
 // 0x00000014: str r3, [sp, #144]
-// 0x00000016: sub sp, sp, #32
+// 0x00000016: sub sp, #32
 // 0x00000018: .cfi_def_cfa_offset: 160
-// 0x00000018: add sp, sp, #32
+// 0x00000018: add sp, #32
 // 0x0000001a: .cfi_def_cfa_offset: 128
 // 0x0000001a: .cfi_remember_state
-// 0x0000001a: add sp, sp, #36
+// 0x0000001a: add sp, #36
 // 0x0000001c: .cfi_def_cfa_offset: 92
-// 0x0000001c: vpop.f32 {s16-s31}
+// 0x0000001c: vpop {s16-s31}
 // 0x00000020: .cfi_def_cfa_offset: 28
 // 0x00000020: .cfi_restore_extended: r80
 // 0x00000020: .cfi_restore_extended: r81
@@ -75,9 +76,11 @@
 // 0x00000020: .cfi_restore_extended: r93
 // 0x00000020: .cfi_restore_extended: r94
 // 0x00000020: .cfi_restore_extended: r95
-// 0x00000020: pop {r5, r6, r7, r8, r10, r11, pc}
-// 0x00000024: .cfi_restore_state
-// 0x00000024: .cfi_def_cfa_offset: 128
+// 0x00000020: pop {r5,r6,r7,r8,r10,r11,lr}
+// 0x00000024: ldr r8, [tr, #52] ; is_gc_marking
+// 0x00000028: bx lr
+// 0x0000002a: .cfi_restore_state
+// 0x0000002a: .cfi_def_cfa_offset: 128
 
 static constexpr uint8_t expected_asm_kArm64[] = {
     0xFF, 0x03, 0x03, 0xD1, 0xF3, 0x53, 0x06, 0xA9, 0xF5, 0x5B, 0x07, 0xA9,
@@ -89,7 +92,8 @@
     0xF3, 0x53, 0x46, 0xA9, 0xF5, 0x5B, 0x47, 0xA9, 0xF7, 0x63, 0x48, 0xA9,
     0xF9, 0x6B, 0x49, 0xA9, 0xFB, 0x73, 0x4A, 0xA9, 0xFD, 0x7B, 0x4B, 0xA9,
     0xE8, 0x27, 0x42, 0x6D, 0xEA, 0x2F, 0x43, 0x6D, 0xEC, 0x37, 0x44, 0x6D,
-    0xEE, 0x3F, 0x45, 0x6D, 0xFF, 0x03, 0x03, 0x91, 0xC0, 0x03, 0x5F, 0xD6,
+    0xEE, 0x3F, 0x45, 0x6D, 0x74, 0x36, 0x40, 0xB9, 0xFF, 0x03, 0x03, 0x91,
+    0xC0, 0x03, 0x5F, 0xD6,
 };
 static constexpr uint8_t expected_cfi_kArm64[] = {
     0x44, 0x0E, 0xC0, 0x01, 0x44, 0x93, 0x18, 0x94, 0x16, 0x44, 0x95, 0x14,
@@ -101,7 +105,7 @@
     0xD3, 0xD4, 0x44, 0xD5, 0xD6, 0x44, 0xD7, 0xD8, 0x44, 0xD9, 0xDA, 0x44,
     0xDB, 0xDC, 0x44, 0xDD, 0xDE, 0x44, 0x06, 0x48, 0x06, 0x49, 0x44, 0x06,
     0x4A, 0x06, 0x4B, 0x44, 0x06, 0x4C, 0x06, 0x4D, 0x44, 0x06, 0x4E, 0x06,
-    0x4F, 0x44, 0x0E, 0x00, 0x44, 0x0B, 0x0E, 0xC0, 0x01,
+    0x4F, 0x48, 0x0E, 0x00, 0x44, 0x0B, 0x0E, 0xC0, 0x01,
 };
 // 0x00000000: sub sp, sp, #0xc0 (192)
 // 0x00000004: .cfi_def_cfa_offset: 192
@@ -175,11 +179,12 @@
 // 0x0000006c: ldp d14, d15, [sp, #80]
 // 0x00000070: .cfi_restore_extended: r78
 // 0x00000070: .cfi_restore_extended: r79
-// 0x00000070: add sp, sp, #0xc0 (192)
-// 0x00000074: .cfi_def_cfa_offset: 0
-// 0x00000074: ret
-// 0x00000078: .cfi_restore_state
-// 0x00000078: .cfi_def_cfa_offset: 192
+// 0x00000070: ldr w20, [tr, #52] ; is_gc_marking
+// 0x00000074: add sp, sp, #0xc0 (192)
+// 0x00000078: .cfi_def_cfa_offset: 0
+// 0x00000078: ret
+// 0x0000007c: .cfi_restore_state
+// 0x0000007c: .cfi_def_cfa_offset: 192
 
 static constexpr uint8_t expected_asm_kX86[] = {
     0x57, 0x56, 0x55, 0x83, 0xC4, 0xE4, 0x50, 0x89, 0x4C, 0x24, 0x34, 0xF3,
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index b34d938..6ce7d75 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -49,6 +49,9 @@
   return count + 1;
 }
 
+// TODO: In the Baker read barrier configuration, add checks to ensure
+// the Marking Register's value is correct.
+
 namespace art {
 
 enum class JniKind {
diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc
index 33f4d77..e086455 100644
--- a/compiler/jni/quick/arm64/calling_convention_arm64.cc
+++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc
@@ -108,11 +108,25 @@
 
 // Calling convention
 ManagedRegister Arm64ManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
-  return Arm64ManagedRegister::FromXRegister(X20);  // saved on entry restored on exit
+  // X20 is safe to use as a scratch register:
+  // - with Baker read barriers, it is reserved as Marking Register,
+  //   and thus does not actually need to be saved/restored; it is
+  //   refreshed on exit (see Arm64JNIMacroAssembler::RemoveFrame);
+  // - in other cases, it is saved on entry (in
+  //   Arm64JNIMacroAssembler::BuildFrame) and restored on exit (in
+  //   Arm64JNIMacroAssembler::RemoveFrame).
+  return Arm64ManagedRegister::FromXRegister(X20);
 }
 
 ManagedRegister Arm64JniCallingConvention::InterproceduralScratchRegister() {
-  return Arm64ManagedRegister::FromXRegister(X20);  // saved on entry restored on exit
+  // X20 is safe to use as a scratch register:
+  // - with Baker read barriers, it is reserved as Marking Register,
+  //   and thus does not actually need to be saved/restored; it is
+  //   refreshed on exit (see Arm64JNIMacroAssembler::RemoveFrame);
+  // - in other cases, it is saved on entry (in
+  //   Arm64JNIMacroAssembler::BuildFrame) and restored on exit (in
+  //   Arm64JNIMacroAssembler::RemoveFrame).
+  return Arm64ManagedRegister::FromXRegister(X20);
 }
 
 static ManagedRegister ReturnRegisterForShorty(const char* shorty) {
diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc
index aa5a945..18d6b9a 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2.cc
@@ -199,6 +199,24 @@
   // Note: The fake dependency is unnecessary for the slow path.
 }
 
+// Load the read barrier introspection entrypoint in register `entrypoint`
+static void LoadReadBarrierMarkIntrospectionEntrypoint(arm::ArmVIXLAssembler& assembler,
+                                                       vixl::aarch32::Register entrypoint) {
+  using vixl::aarch32::MemOperand;
+  using vixl::aarch32::ip;
+  // Thread Register.
+  const vixl::aarch32::Register tr = vixl::aarch32::r9;
+
+  // The register where the read barrier introspection entrypoint is loaded
+  // is fixed: `Thumb2RelativePatcher::kBakerCcEntrypointRegister` (R4).
+  DCHECK_EQ(entrypoint.GetCode(), Thumb2RelativePatcher::kBakerCcEntrypointRegister);
+  // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection.
+  DCHECK_EQ(ip.GetCode(), 12u);
+  const int32_t entry_point_offset =
+      Thread::ReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ip.GetCode());
+  __ Ldr(entrypoint, MemOperand(tr, entry_point_offset));
+}
+
 void Thumb2RelativePatcher::CompileBakerReadBarrierThunk(arm::ArmVIXLAssembler& assembler,
                                                          uint32_t encoded_data) {
   using namespace vixl::aarch32;  // NOLINT(build/namespaces)
@@ -233,6 +251,7 @@
       const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 +
                                  raw_ldr_offset;
       Register ep_reg(kBakerCcEntrypointRegister);
+      LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg);
       if (width == BakerReadBarrierWidth::kWide) {
         MemOperand ldr_half_address(lr, ldr_offset + 2);
         __ Ldrh(ip, ldr_half_address);        // Load the LDR immediate half-word with "Rt | imm12".
@@ -278,8 +297,10 @@
       MemOperand ldr_address(lr, ldr_offset + 2);
       __ Ldrb(ip, ldr_address);               // Load the LDR (register) byte with "00 | imm2 | Rm",
                                               // i.e. Rm+32 because the scale in imm2 is 2.
-      Register ep_reg(kBakerCcEntrypointRegister);  // Insert ip to the entrypoint address to create
-      __ Bfi(ep_reg, ip, 3, 6);               // a switch case target based on the index register.
+      Register ep_reg(kBakerCcEntrypointRegister);
+      LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg);
+      __ Bfi(ep_reg, ip, 3, 6);               // Insert ip to the entrypoint address to create
+                                              // a switch case target based on the index register.
       __ Mov(ip, base_reg);                   // Move the base register to ip0.
       __ Bx(ep_reg);                          // Jump to the entrypoint's array switch case.
       break;
@@ -309,9 +330,10 @@
                     " the highest bits and the 'forwarding address' state to have all bits set");
       __ Cmp(ip, Operand(0xc0000000));
       __ B(hs, &forwarding_address);
+      Register ep_reg(kBakerCcEntrypointRegister);
+      LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg);
       // Adjust the art_quick_read_barrier_mark_introspection address in kBakerCcEntrypointRegister
       // to art_quick_read_barrier_mark_introspection_gc_roots.
-      Register ep_reg(kBakerCcEntrypointRegister);
       int32_t entrypoint_offset = (width == BakerReadBarrierWidth::kWide)
           ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET
           : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET;
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index bc21607..38c732b 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -381,6 +381,21 @@
   // Note: The fake dependency is unnecessary for the slow path.
 }
 
+// Load the read barrier introspection entrypoint in register `entrypoint`.
+static void LoadReadBarrierMarkIntrospectionEntrypoint(arm64::Arm64Assembler& assembler,
+                                                       vixl::aarch64::Register entrypoint) {
+  using vixl::aarch64::MemOperand;
+  using vixl::aarch64::ip0;
+  // Thread Register.
+  const vixl::aarch64::Register tr = vixl::aarch64::x19;
+
+  // entrypoint = Thread::Current()->pReadBarrierMarkReg16, i.e. pReadBarrierMarkIntrospection.
+  DCHECK_EQ(ip0.GetCode(), 16u);
+  const int32_t entry_point_offset =
+      Thread::ReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(ip0.GetCode());
+  __ Ldr(entrypoint, MemOperand(tr, entry_point_offset));
+}
+
 void Arm64RelativePatcher::CompileBakerReadBarrierThunk(arm64::Arm64Assembler& assembler,
                                                         uint32_t encoded_data) {
   using namespace vixl::aarch64;  // NOLINT(build/namespaces)
@@ -412,6 +427,7 @@
       __ Bind(&slow_path);
       MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET);
       __ Ldr(ip0.W(), ldr_address);         // Load the LDR (immediate) unsigned offset.
+      LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1);
       __ Ubfx(ip0.W(), ip0.W(), 10, 12);    // Extract the offset.
       __ Ldr(ip0.W(), MemOperand(base_reg, ip0, LSL, 2));   // Load the reference.
       // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference.
@@ -441,6 +457,7 @@
       __ Bind(&slow_path);
       MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET);
       __ Ldr(ip0.W(), ldr_address);         // Load the LDR (register) unsigned offset.
+      LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1);
       __ Ubfx(ip0, ip0, 16, 6);             // Extract the index register, plus 32 (bit 21 is set).
       __ Bfi(ip1, ip0, 3, 6);               // Insert ip0 to the entrypoint address to create
                                             // a switch case target based on the index register.
@@ -469,6 +486,7 @@
       __ Bind(&not_marked);
       __ Tst(ip0.W(), Operand(ip0.W(), LSL, 1));
       __ B(&forwarding_address, mi);
+      LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1);
       // Adjust the art_quick_read_barrier_mark_introspection address in IP1 to
       // art_quick_read_barrier_mark_introspection_gc_roots.
       __ Add(ip1, ip1, Operand(BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET));
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index f7465c0..6120ed0 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -158,26 +158,61 @@
   const void* source_;
 };
 
+// OatClassHeader is the header only part of the oat class that is required even when compilation
+// is not enabled.
+class OatWriter::OatClassHeader {
+ public:
+  OatClassHeader(uint32_t offset,
+                 uint32_t num_non_null_compiled_methods,
+                 uint32_t num_methods,
+                 mirror::Class::Status status)
+      : status_(status),
+        offset_(offset) {
+    // We just arbitrarily say that 0 methods means kOatClassNoneCompiled and that we won't use
+    // kOatClassAllCompiled unless there is at least one compiled method. This means in an
+    // interpreter only system, we can assert that all classes are kOatClassNoneCompiled.
+    if (num_non_null_compiled_methods == 0) {
+      type_ = kOatClassNoneCompiled;
+    } else if (num_non_null_compiled_methods == num_methods) {
+      type_ = kOatClassAllCompiled;
+    } else {
+      type_ = kOatClassSomeCompiled;
+    }
+  }
+
+  bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
+
+  static size_t SizeOf() {
+    return sizeof(status_) + sizeof(type_);
+  }
+
+  // Data to write.
+  static_assert(mirror::Class::Status::kStatusMax < (1 << 16), "class status won't fit in 16bits");
+  int16_t status_;
+
+  static_assert(OatClassType::kOatClassMax < (1 << 16), "oat_class type won't fit in 16bits");
+  uint16_t type_;
+
+  // Offset of start of OatClass from beginning of OatHeader. It is
+  // used to validate file position when writing.
+  uint32_t offset_;
+};
+
+// The actual oat class body contains the information about compiled methods. It is only required
+// for compiler filters that have any compilation.
 class OatWriter::OatClass {
  public:
-  OatClass(size_t offset,
-           const dchecked_vector<CompiledMethod*>& compiled_methods,
-           uint32_t num_non_null_compiled_methods,
-           mirror::Class::Status status);
+  OatClass(const dchecked_vector<CompiledMethod*>& compiled_methods,
+           uint32_t compiled_methods_with_code,
+           uint16_t oat_class_type);
   OatClass(OatClass&& src) = default;
-  size_t GetOatMethodOffsetsOffsetFromOatHeader(size_t class_def_method_index_) const;
-  size_t GetOatMethodOffsetsOffsetFromOatClass(size_t class_def_method_index_) const;
   size_t SizeOf() const;
-  bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
+  bool Write(OatWriter* oat_writer, OutputStream* out) const;
 
   CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const {
     return compiled_methods_[class_def_method_index];
   }
 
-  // Offset of start of OatClass from beginning of OatHeader. It is
-  // used to validate file position when writing.
-  size_t offset_;
-
   // CompiledMethods for each class_def_method_index, or null if no method is available.
   dchecked_vector<CompiledMethod*> compiled_methods_;
 
@@ -188,13 +223,6 @@
   dchecked_vector<uint32_t> oat_method_offsets_offsets_from_oat_class_;
 
   // Data to write.
-
-  static_assert(mirror::Class::Status::kStatusMax < (1 << 16), "class status won't fit in 16bits");
-  int16_t status_;
-
-  static_assert(OatClassType::kOatClassMax < (1 << 16), "oat_class type won't fit in 16bits");
-  uint16_t type_;
-
   uint32_t method_bitmap_size_;
 
   // bit vector indexed by ClassDef method index. When
@@ -473,8 +501,8 @@
   return true;
 }
 
-dchecked_vector<const char*> OatWriter::GetSourceLocations() const {
-  dchecked_vector<const char*> locations;
+dchecked_vector<std::string> OatWriter::GetSourceLocations() const {
+  dchecked_vector<std::string> locations;
   locations.reserve(oat_dex_files_.size());
   for (const OatDexFile& oat_dex_file : oat_dex_files_) {
     locations.push_back(oat_dex_file.GetLocation());
@@ -482,6 +510,11 @@
   return locations;
 }
 
+bool OatWriter::MayHaveCompiledMethods() const {
+  return CompilerFilter::IsAnyCompilationEnabled(
+      GetCompilerDriver()->GetCompilerOptions().GetCompilerFilter());
+}
+
 bool OatWriter::WriteAndOpenDexFiles(
     File* vdex_file,
     OutputStream* oat_rodata,
@@ -663,7 +696,10 @@
 
   bool StartClass(const DexFile* dex_file, size_t class_def_index) OVERRIDE {
     DexMethodVisitor::StartClass(dex_file, class_def_index);
-    DCHECK_LT(oat_class_index_, writer_->oat_classes_.size());
+    if (kIsDebugBuild && writer_->MayHaveCompiledMethods()) {
+      // There are no oat classes if there aren't any compiled methods.
+      CHECK_LT(oat_class_index_, writer_->oat_classes_.size());
+    }
     method_offsets_index_ = 0u;
     return true;
   }
@@ -678,6 +714,17 @@
   size_t method_offsets_index_;
 };
 
+static bool HasCompiledCode(const CompiledMethod* method) {
+  // The dextodexcompiler puts the quickening info table into the CompiledMethod
+  // for simplicity. For such methods, we will emit an OatQuickMethodHeader
+  // only when vdex is disabled.
+  return method != nullptr && (!method->GetQuickCode().empty() || !kIsVdexEnabled);
+}
+
+static bool HasQuickeningInfo(const CompiledMethod* method) {
+  return method != nullptr && method->GetQuickCode().empty() && !method->GetVmapTable().empty();
+}
+
 class OatWriter::InitBssLayoutMethodVisitor : public DexMethodVisitor {
  public:
   explicit InitBssLayoutMethodVisitor(OatWriter* writer)
@@ -688,7 +735,7 @@
     // Look for patches with .bss references and prepare maps with placeholders for their offsets.
     CompiledMethod* compiled_method = writer_->compiler_driver_->GetCompiledMethod(
         MethodReference(dex_file_, it.GetMemberIndex()));
-    if (compiled_method != nullptr) {
+    if (HasCompiledCode(compiled_method)) {
       for (const LinkerPatch& patch : compiled_method->GetPatches()) {
         if (patch.GetType() == LinkerPatch::Type::kMethodBssEntry) {
           MethodReference target_method = patch.TargetMethod();
@@ -711,6 +758,8 @@
           writer_->bss_string_entries_.Overwrite(ref, /* placeholder */ 0u);
         }
       }
+    } else {
+      DCHECK(compiled_method == nullptr || compiled_method->GetPatches().empty());
     }
     return true;
   }
@@ -721,12 +770,16 @@
   InitOatClassesMethodVisitor(OatWriter* writer, size_t offset)
       : DexMethodVisitor(writer, offset),
         compiled_methods_(),
-        num_non_null_compiled_methods_(0u) {
+        compiled_methods_with_code_(0u) {
     size_t num_classes = 0u;
     for (const OatDexFile& oat_dex_file : writer_->oat_dex_files_) {
       num_classes += oat_dex_file.class_offsets_.size();
     }
-    writer_->oat_classes_.reserve(num_classes);
+    // If we aren't compiling only reserve headers.
+    writer_->oat_class_headers_.reserve(num_classes);
+    if (writer->MayHaveCompiledMethods()) {
+      writer->oat_classes_.reserve(num_classes);
+    }
     compiled_methods_.reserve(256u);
     // If there are any classes, the class offsets allocation aligns the offset.
     DCHECK(num_classes == 0u || IsAligned<4u>(offset));
@@ -735,7 +788,7 @@
   bool StartClass(const DexFile* dex_file, size_t class_def_index) OVERRIDE {
     DexMethodVisitor::StartClass(dex_file, class_def_index);
     compiled_methods_.clear();
-    num_non_null_compiled_methods_ = 0u;
+    compiled_methods_with_code_ = 0u;
     return true;
   }
 
@@ -743,14 +796,14 @@
                    const ClassDataItemIterator& it) OVERRIDE {
     // Fill in the compiled_methods_ array for methods that have a
     // CompiledMethod. We track the number of non-null entries in
-    // num_non_null_compiled_methods_ since we only want to allocate
+    // compiled_methods_with_code_ since we only want to allocate
     // OatMethodOffsets for the compiled methods.
     uint32_t method_idx = it.GetMemberIndex();
     CompiledMethod* compiled_method =
         writer_->compiler_driver_->GetCompiledMethod(MethodReference(dex_file_, method_idx));
     compiled_methods_.push_back(compiled_method);
-    if (compiled_method != nullptr) {
-      ++num_non_null_compiled_methods_;
+    if (HasCompiledCode(compiled_method)) {
+      ++compiled_methods_with_code_;
     }
     return true;
   }
@@ -760,7 +813,8 @@
     mirror::Class::Status status;
     bool found = writer_->compiler_driver_->GetCompiledClass(class_ref, &status);
     if (!found) {
-      if (writer_->compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) {
+      VerificationResults* results = writer_->compiler_driver_->GetVerificationResults();
+      if (results != nullptr && results->IsClassRejected(class_ref)) {
         // The oat class status is used only for verification of resolved classes,
         // so use kStatusErrorResolved whether the class was resolved or unresolved
         // during compile-time verification.
@@ -770,25 +824,31 @@
       }
     }
 
-    writer_->oat_classes_.emplace_back(offset_,
-                                       compiled_methods_,
-                                       num_non_null_compiled_methods_,
-                                       status);
-    offset_ += writer_->oat_classes_.back().SizeOf();
+    writer_->oat_class_headers_.emplace_back(offset_,
+                                             compiled_methods_with_code_,
+                                             compiled_methods_.size(),
+                                             status);
+    OatClassHeader& header = writer_->oat_class_headers_.back();
+    offset_ += header.SizeOf();
+    if (writer_->MayHaveCompiledMethods()) {
+      writer_->oat_classes_.emplace_back(compiled_methods_,
+                                         compiled_methods_with_code_,
+                                         header.type_);
+      offset_ += writer_->oat_classes_.back().SizeOf();
+    }
     return DexMethodVisitor::EndClass();
   }
 
  private:
   dchecked_vector<CompiledMethod*> compiled_methods_;
-  size_t num_non_null_compiled_methods_;
+  size_t compiled_methods_with_code_;
 };
 
 class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
  public:
-  InitCodeMethodVisitor(OatWriter* writer, size_t offset, size_t quickening_info_offset)
+  InitCodeMethodVisitor(OatWriter* writer, size_t offset)
       : OatDexMethodVisitor(writer, offset),
-        debuggable_(writer->GetCompilerDriver()->GetCompilerOptions().GetDebuggable()),
-        current_quickening_info_offset_(quickening_info_offset) {
+        debuggable_(writer->GetCompilerDriver()->GetCompilerOptions().GetDebuggable()) {
     writer_->absolute_patch_locations_.reserve(
         writer_->compiler_driver_->GetNonRelativeLinkerPatchCount());
   }
@@ -806,10 +866,7 @@
     OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
     CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
 
-    if (it.GetMethodCodeItem() != nullptr) {
-      current_quickening_info_offset_ += sizeof(uint32_t);
-    }
-    if (compiled_method != nullptr) {
+    if (HasCompiledCode(compiled_method)) {
       // Derived from CompiledMethod.
       uint32_t quick_code_offset = 0;
 
@@ -870,18 +927,10 @@
           DCHECK_LT(method_info_offset, code_offset);
         }
       } else {
-        CHECK(compiled_method->GetMethodInfo().empty());
-        if (kIsVdexEnabled) {
-          // We write the offset in the .vdex file.
-          DCHECK_EQ(vmap_table_offset, 0u);
-          vmap_table_offset = current_quickening_info_offset_;
-          ArrayRef<const uint8_t> vmap_table = compiled_method->GetVmapTable();
-          current_quickening_info_offset_ += vmap_table.size() * sizeof(vmap_table.front());
-        } else {
-          // We write the offset of the quickening info relative to the code.
-          vmap_table_offset += code_offset;
-          DCHECK_LT(vmap_table_offset, code_offset);
-        }
+        CHECK(!kIsVdexEnabled);
+        // We write the offset of the quickening info relative to the code.
+        vmap_table_offset += code_offset;
+        DCHECK_LT(vmap_table_offset, code_offset);
       }
       uint32_t frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
       uint32_t core_spill_mask = compiled_method->GetCoreSpillMask();
@@ -980,9 +1029,6 @@
 
   // Cache of compiler's --debuggable option.
   const bool debuggable_;
-
-  // Offset in the vdex file for the quickening info.
-  uint32_t current_quickening_info_offset_;
 };
 
 class OatWriter::InitMapMethodVisitor : public OatDexMethodVisitor {
@@ -995,27 +1041,23 @@
     OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
     CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
 
-    if (compiled_method != nullptr) {
+    if (HasCompiledCode(compiled_method)) {
       DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
-      // If vdex is enabled, we only emit the stack map of compiled code. The quickening info will
-      // be in the vdex file.
-      if (!compiled_method->GetQuickCode().empty() || !kIsVdexEnabled) {
-        DCHECK_EQ(oat_class->method_headers_[method_offsets_index_].GetVmapTableOffset(), 0u);
+      DCHECK_EQ(oat_class->method_headers_[method_offsets_index_].GetVmapTableOffset(), 0u);
 
-        ArrayRef<const uint8_t> map = compiled_method->GetVmapTable();
-        uint32_t map_size = map.size() * sizeof(map[0]);
-        if (map_size != 0u) {
-          size_t offset = dedupe_map_.GetOrCreate(
-              map.data(),
-              [this, map_size]() {
-                uint32_t new_offset = offset_;
-                offset_ += map_size;
-                return new_offset;
-              });
-          // Code offset is not initialized yet, so set the map offset to 0u-offset.
-          DCHECK_EQ(oat_class->method_offsets_[method_offsets_index_].code_offset_, 0u);
-          oat_class->method_headers_[method_offsets_index_].SetVmapTableOffset(0u - offset);
-        }
+      ArrayRef<const uint8_t> map = compiled_method->GetVmapTable();
+      uint32_t map_size = map.size() * sizeof(map[0]);
+      if (map_size != 0u) {
+        size_t offset = dedupe_map_.GetOrCreate(
+            map.data(),
+            [this, map_size]() {
+              uint32_t new_offset = offset_;
+              offset_ += map_size;
+              return new_offset;
+            });
+        // Code offset is not initialized yet, so set the map offset to 0u-offset.
+        DCHECK_EQ(oat_class->method_offsets_[method_offsets_index_].code_offset_, 0u);
+        oat_class->method_headers_[method_offsets_index_].SetVmapTableOffset(0u - offset);
       }
       ++method_offsets_index_;
     }
@@ -1038,7 +1080,7 @@
     OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
     CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
 
-    if (compiled_method != nullptr) {
+    if (HasCompiledCode(compiled_method)) {
       DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
       DCHECK_EQ(oat_class->method_headers_[method_offsets_index_].GetMethodInfoOffset(), 0u);
       ArrayRef<const uint8_t> map = compiled_method->GetMethodInfo();
@@ -1132,7 +1174,7 @@
     CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
 
     OatMethodOffsets offsets(0u);
-    if (compiled_method != nullptr) {
+    if (HasCompiledCode(compiled_method)) {
       DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
       offsets = oat_class->method_offsets_[method_offsets_index_];
       ++method_offsets_index_;
@@ -1266,7 +1308,7 @@
 
     // No thread suspension since dex_cache_ that may get invalidated if that occurs.
     ScopedAssertNoThreadSuspension tsc(__FUNCTION__);
-    if (compiled_method != nullptr) {  // ie. not an abstract method
+    if (HasCompiledCode(compiled_method)) {
       size_t file_offset = file_offset_;
       OutputStream* out = out_;
 
@@ -1557,7 +1599,7 @@
     OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
     const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
 
-    if (compiled_method != nullptr) {  // i.e. not an abstract method
+    if (HasCompiledCode(compiled_method)) {
       size_t file_offset = file_offset_;
       OutputStream* out = out_;
 
@@ -1572,8 +1614,7 @@
 
       // If vdex is enabled, only emit the map for compiled code. The quickening info
       // is emitted in the vdex already.
-      if (map_offset != 0u &&
-          !(kIsVdexEnabled && compiled_method->GetQuickCode().empty())) {
+      if (map_offset != 0u) {
         // Transform map_offset to actual oat data offset.
         map_offset = (code_offset - compiled_method->CodeDelta()) - map_offset;
         DCHECK_NE(map_offset, 0u);
@@ -1620,7 +1661,7 @@
     OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
     const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
 
-    if (compiled_method != nullptr) {  // i.e. not an abstract method
+    if (HasCompiledCode(compiled_method)) {
       size_t file_offset = file_offset_;
       OutputStream* out = out_;
       uint32_t map_offset = oat_class->method_headers_[method_offsets_index_].GetMethodInfoOffset();
@@ -1671,7 +1712,7 @@
       if (UNLIKELY(!visitor->StartClass(dex_file, class_def_index))) {
         return false;
       }
-      if (compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) {
+      if (MayHaveCompiledMethods()) {
         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
@@ -1739,21 +1780,21 @@
   offset = visitor.GetOffset();
 
   // Update oat_dex_files_.
-  auto oat_class_it = oat_classes_.begin();
+  auto oat_class_it = oat_class_headers_.begin();
   for (OatDexFile& oat_dex_file : oat_dex_files_) {
     for (uint32_t& class_offset : oat_dex_file.class_offsets_) {
-      DCHECK(oat_class_it != oat_classes_.end());
+      DCHECK(oat_class_it != oat_class_headers_.end());
       class_offset = oat_class_it->offset_;
       ++oat_class_it;
     }
   }
-  CHECK(oat_class_it == oat_classes_.end());
+  CHECK(oat_class_it == oat_class_headers_.end());
 
   return offset;
 }
 
 size_t OatWriter::InitOatMaps(size_t offset) {
-  if (!compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) {
+  if (!MayHaveCompiledMethods()) {
     return offset;
   }
   {
@@ -1856,7 +1897,7 @@
   if (!compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) {
     return offset;
   }
-  InitCodeMethodVisitor code_visitor(this, offset, vdex_quickening_info_offset_);
+  InitCodeMethodVisitor code_visitor(this, offset);
   bool success = VisitDexMethods(&code_visitor);
   DCHECK(success);
   offset = code_visitor.GetOffset();
@@ -1985,39 +2026,40 @@
 
 class OatWriter::WriteQuickeningInfoMethodVisitor : public DexMethodVisitor {
  public:
-  WriteQuickeningInfoMethodVisitor(OatWriter* writer, OutputStream* out, uint32_t offset)
+  WriteQuickeningInfoMethodVisitor(OatWriter* writer,
+                                   OutputStream* out,
+                                   uint32_t offset,
+                                   SafeMap<const uint8_t*, uint32_t>* offset_map)
       : DexMethodVisitor(writer, offset),
         out_(out),
-        written_bytes_(0u) {}
+        written_bytes_(0u),
+        offset_map_(offset_map) {}
 
-  bool VisitMethod(size_t class_def_method_index ATTRIBUTE_UNUSED,
-                   const ClassDataItemIterator& it) OVERRIDE {
-    if (it.GetMethodCodeItem() == nullptr) {
-      // No CodeItem. Native or abstract method.
-      return true;
-    }
-
+  bool VisitMethod(size_t class_def_method_index ATTRIBUTE_UNUSED, const ClassDataItemIterator& it)
+      OVERRIDE {
     uint32_t method_idx = it.GetMemberIndex();
     CompiledMethod* compiled_method =
         writer_->compiler_driver_->GetCompiledMethod(MethodReference(dex_file_, method_idx));
 
-    uint32_t length = 0;
-    const uint8_t* data = nullptr;
-    // VMap only contains quickening info if this method is not compiled.
-    if (compiled_method != nullptr && compiled_method->GetQuickCode().empty()) {
+    if (HasQuickeningInfo(compiled_method)) {
       ArrayRef<const uint8_t> map = compiled_method->GetVmapTable();
-      data = map.data();
-      length = map.size() * sizeof(map.front());
+      // Deduplication is already done on a pointer basis by the compiler driver,
+      // so we can simply compare the pointers to find out if things are duplicated.
+      if (offset_map_->find(map.data()) == offset_map_->end()) {
+        uint32_t length = map.size() * sizeof(map.front());
+        offset_map_->Put(map.data(), written_bytes_);
+        if (!out_->WriteFully(&length, sizeof(length)) ||
+            !out_->WriteFully(map.data(), length)) {
+          PLOG(ERROR) << "Failed to write quickening info for "
+                      << dex_file_->PrettyMethod(it.GetMemberIndex()) << " to "
+                      << out_->GetLocation();
+          return false;
+        }
+        written_bytes_ += sizeof(length) + length;
+        offset_ += sizeof(length) + length;
+      }
     }
 
-    if (!out_->WriteFully(&length, sizeof(length)) ||
-        !out_->WriteFully(data, length)) {
-      PLOG(ERROR) << "Failed to write quickening info for "
-          << dex_file_->PrettyMethod(it.GetMemberIndex()) << " to " << out_->GetLocation();
-      return false;
-    }
-    offset_ += sizeof(length) + length;
-    written_bytes_ += sizeof(length) + length;
     return true;
   }
 
@@ -2028,6 +2070,72 @@
  private:
   OutputStream* const out_;
   size_t written_bytes_;
+  // Maps quickening map to its offset in the file.
+  SafeMap<const uint8_t*, uint32_t>* offset_map_;
+};
+
+class OatWriter::WriteQuickeningIndicesMethodVisitor {
+ public:
+  WriteQuickeningIndicesMethodVisitor(OutputStream* out,
+                                      uint32_t indices_offset,
+                                      const SafeMap<const uint8_t*, uint32_t>& offset_map,
+                                      std::vector<uint32_t>* dex_files_offset)
+      : out_(out),
+        indices_offset_(indices_offset),
+        written_bytes_(0u),
+        dex_files_offset_(dex_files_offset),
+        offset_map_(offset_map) {}
+
+  bool VisitDexMethods(const std::vector<const DexFile*>& dex_files, const CompilerDriver& driver) {
+    for (const DexFile* dex_file : dex_files) {
+      // Record the offset for this current dex file. It will be written in the vdex file
+      // later.
+      dex_files_offset_->push_back(indices_offset_ + GetNumberOfWrittenBytes());
+      const size_t class_def_count = dex_file->NumClassDefs();
+      for (size_t class_def_index = 0; class_def_index != class_def_count; ++class_def_index) {
+        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) {
+          continue;
+        }
+        for (ClassDataItemIterator class_it(*dex_file, class_data);
+             class_it.HasNext();
+             class_it.Next()) {
+          if (!class_it.IsAtMethod()) {
+            continue;
+          }
+          uint32_t method_idx = class_it.GetMemberIndex();
+          CompiledMethod* compiled_method =
+              driver.GetCompiledMethod(MethodReference(dex_file, method_idx));
+          if (HasQuickeningInfo(compiled_method)) {
+            uint32_t code_item_offset = class_it.GetMethodCodeItemOffset();
+            uint32_t offset = offset_map_.Get(compiled_method->GetVmapTable().data());
+            if (!out_->WriteFully(&code_item_offset, sizeof(code_item_offset)) ||
+                !out_->WriteFully(&offset, sizeof(offset))) {
+              PLOG(ERROR) << "Failed to write quickening info for "
+                          << dex_file->PrettyMethod(method_idx) << " to "
+                          << out_->GetLocation();
+              return false;
+            }
+            written_bytes_ += sizeof(code_item_offset) + sizeof(offset);
+          }
+        }
+      }
+    }
+    return true;
+  }
+
+  size_t GetNumberOfWrittenBytes() const {
+    return written_bytes_;
+  }
+
+ private:
+  OutputStream* const out_;
+  const uint32_t indices_offset_;
+  size_t written_bytes_;
+  std::vector<uint32_t>* dex_files_offset_;
+  // Maps quickening map to its offset in the file.
+  const SafeMap<const uint8_t*, uint32_t>& offset_map_;
 };
 
 bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) {
@@ -2051,8 +2159,26 @@
   }
 
   if (compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) {
-    WriteQuickeningInfoMethodVisitor visitor(this, vdex_out, start_offset);
-    if (!VisitDexMethods(&visitor)) {
+    std::vector<uint32_t> dex_files_indices;
+    SafeMap<const uint8_t*, uint32_t> offset_map;
+    WriteQuickeningInfoMethodVisitor visitor1(this, vdex_out, start_offset, &offset_map);
+    if (!VisitDexMethods(&visitor1)) {
+      PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation();
+      return false;
+    }
+
+    WriteQuickeningIndicesMethodVisitor visitor2(vdex_out,
+                                                 visitor1.GetNumberOfWrittenBytes(),
+                                                 offset_map,
+                                                 &dex_files_indices);
+    if (!visitor2.VisitDexMethods(*dex_files_, *compiler_driver_)) {
+      PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation();
+      return false;
+    }
+
+    DCHECK_EQ(dex_files_->size(), dex_files_indices.size());
+    if (!vdex_out->WriteFully(
+            dex_files_indices.data(), sizeof(dex_files_indices[0]) * dex_files_indices.size())) {
       PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation();
       return false;
     }
@@ -2062,7 +2188,9 @@
                   << " File: " << vdex_out->GetLocation();
       return false;
     }
-    size_quickening_info_ = visitor.GetNumberOfWrittenBytes();
+    size_quickening_info_ = visitor1.GetNumberOfWrittenBytes() +
+                            visitor2.GetNumberOfWrittenBytes() +
+                            dex_files_->size() * sizeof(uint32_t);
   } else {
     // We know we did not quicken.
     size_quickening_info_ = 0;
@@ -2291,14 +2419,24 @@
 }
 
 size_t OatWriter::WriteClasses(OutputStream* out, size_t file_offset, size_t relative_offset) {
-  for (OatClass& oat_class : oat_classes_) {
+  const bool may_have_compiled = MayHaveCompiledMethods();
+  if (may_have_compiled) {
+    CHECK_EQ(oat_class_headers_.size(), oat_classes_.size());
+  }
+  for (size_t i = 0; i < oat_class_headers_.size(); ++i) {
     // If there are any classes, the class offsets allocation aligns the offset.
     DCHECK_ALIGNED(relative_offset, 4u);
     DCHECK_OFFSET();
-    if (!oat_class.Write(this, out, oat_data_offset_)) {
+    if (!oat_class_headers_[i].Write(this, out, oat_data_offset_)) {
       return 0u;
     }
-    relative_offset += oat_class.SizeOf();
+    relative_offset += oat_class_headers_[i].SizeOf();
+    if (may_have_compiled) {
+      if (!oat_classes_[i].Write(this, out)) {
+        return 0u;
+      }
+      relative_offset += oat_classes_[i].SizeOf();
+    }
   }
   return relative_offset;
 }
@@ -3181,37 +3319,21 @@
   return true;
 }
 
-OatWriter::OatClass::OatClass(size_t offset,
-                              const dchecked_vector<CompiledMethod*>& compiled_methods,
-                              uint32_t num_non_null_compiled_methods,
-                              mirror::Class::Status status)
+OatWriter::OatClass::OatClass(const dchecked_vector<CompiledMethod*>& compiled_methods,
+                              uint32_t compiled_methods_with_code,
+                              uint16_t oat_class_type)
     : compiled_methods_(compiled_methods) {
-  uint32_t num_methods = compiled_methods.size();
-  CHECK_LE(num_non_null_compiled_methods, num_methods);
+  const uint32_t num_methods = compiled_methods.size();
+  CHECK_LE(compiled_methods_with_code, num_methods);
 
-  offset_ = offset;
   oat_method_offsets_offsets_from_oat_class_.resize(num_methods);
 
-  // Since both kOatClassNoneCompiled and kOatClassAllCompiled could
-  // apply when there are 0 methods, we just arbitrarily say that 0
-  // methods means kOatClassNoneCompiled and that we won't use
-  // kOatClassAllCompiled unless there is at least one compiled
-  // method. This means in an interpretter only system, we can assert
-  // that all classes are kOatClassNoneCompiled.
-  if (num_non_null_compiled_methods == 0) {
-    type_ = kOatClassNoneCompiled;
-  } else if (num_non_null_compiled_methods == num_methods) {
-    type_ = kOatClassAllCompiled;
-  } else {
-    type_ = kOatClassSomeCompiled;
-  }
+  method_offsets_.resize(compiled_methods_with_code);
+  method_headers_.resize(compiled_methods_with_code);
 
-  status_ = status;
-  method_offsets_.resize(num_non_null_compiled_methods);
-  method_headers_.resize(num_non_null_compiled_methods);
-
-  uint32_t oat_method_offsets_offset_from_oat_class = sizeof(type_) + sizeof(status_);
-  if (type_ == kOatClassSomeCompiled) {
+  uint32_t oat_method_offsets_offset_from_oat_class = OatClassHeader::SizeOf();
+  // We only create this instance if there are at least some compiled.
+  if (oat_class_type == kOatClassSomeCompiled) {
     method_bitmap_.reset(new BitVector(num_methods, false, Allocator::GetMallocAllocator()));
     method_bitmap_size_ = method_bitmap_->GetSizeOf();
     oat_method_offsets_offset_from_oat_class += sizeof(method_bitmap_size_);
@@ -3223,43 +3345,27 @@
 
   for (size_t i = 0; i < num_methods; i++) {
     CompiledMethod* compiled_method = compiled_methods_[i];
-    if (compiled_method == nullptr) {
-      oat_method_offsets_offsets_from_oat_class_[i] = 0;
-    } else {
+    if (HasCompiledCode(compiled_method)) {
       oat_method_offsets_offsets_from_oat_class_[i] = oat_method_offsets_offset_from_oat_class;
       oat_method_offsets_offset_from_oat_class += sizeof(OatMethodOffsets);
-      if (type_ == kOatClassSomeCompiled) {
+      if (oat_class_type == kOatClassSomeCompiled) {
         method_bitmap_->SetBit(i);
       }
+    } else {
+      oat_method_offsets_offsets_from_oat_class_[i] = 0;
     }
   }
 }
 
-size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatHeader(
-    size_t class_def_method_index_) const {
-  uint32_t method_offset = GetOatMethodOffsetsOffsetFromOatClass(class_def_method_index_);
-  if (method_offset == 0) {
-    return 0;
-  }
-  return offset_ + method_offset;
-}
-
-size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatClass(
-    size_t class_def_method_index_) const {
-  return oat_method_offsets_offsets_from_oat_class_[class_def_method_index_];
-}
-
 size_t OatWriter::OatClass::SizeOf() const {
-  return sizeof(status_)
-          + sizeof(type_)
-          + ((method_bitmap_size_ == 0) ? 0 : sizeof(method_bitmap_size_))
+  return ((method_bitmap_size_ == 0) ? 0 : sizeof(method_bitmap_size_))
           + method_bitmap_size_
           + (sizeof(method_offsets_[0]) * method_offsets_.size());
 }
 
-bool OatWriter::OatClass::Write(OatWriter* oat_writer,
-                                OutputStream* out,
-                                const size_t file_offset) const {
+bool OatWriter::OatClassHeader::Write(OatWriter* oat_writer,
+                                      OutputStream* out,
+                                      const size_t file_offset) const {
   DCHECK_OFFSET_();
   if (!out->WriteFully(&status_, sizeof(status_))) {
     PLOG(ERROR) << "Failed to write class status to " << out->GetLocation();
@@ -3272,9 +3378,11 @@
     return false;
   }
   oat_writer->size_oat_class_type_ += sizeof(type_);
+  return true;
+}
 
+bool OatWriter::OatClass::Write(OatWriter* oat_writer, OutputStream* out) const {
   if (method_bitmap_size_ != 0) {
-    CHECK_EQ(kOatClassSomeCompiled, type_);
     if (!out->WriteFully(&method_bitmap_size_, sizeof(method_bitmap_size_))) {
       PLOG(ERROR) << "Failed to write method bitmap size to " << out->GetLocation();
       return false;
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 9217701..470d69e 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -153,7 +153,7 @@
       const VdexFile& vdex_file,
       const char* location,
       CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
-  dchecked_vector<const char*> GetSourceLocations() const;
+  dchecked_vector<std::string> GetSourceLocations() const;
 
   // Write raw dex files to the vdex file, mmap the file and open the dex files from it.
   // Supporting data structures are written into the .rodata section of the oat file.
@@ -239,12 +239,13 @@
     return ArrayRef<const debug::MethodDebugInfo>(method_info_);
   }
 
-  const CompilerDriver* GetCompilerDriver() {
+  const CompilerDriver* GetCompilerDriver() const {
     return compiler_driver_;
   }
 
  private:
   class DexFileSource;
+  class OatClassHeader;
   class OatClass;
   class OatDexFile;
 
@@ -265,6 +266,7 @@
   class WriteMapMethodVisitor;
   class WriteMethodInfoVisitor;
   class WriteQuickeningInfoMethodVisitor;
+  class WriteQuickeningIndicesMethodVisitor;
 
   // Visit all the methods in all the compiled dex files in their definition order
   // with a given DexMethodVisitor.
@@ -327,6 +329,8 @@
   void SetMultiOatRelativePatcherAdjustment();
   void CloseSources();
 
+  bool MayHaveCompiledMethods() const;
+
   enum class WriteState {
     kAddingDexFileSources,
     kPrepareLayout,
@@ -410,6 +414,7 @@
   // data to write
   std::unique_ptr<OatHeader> oat_header_;
   dchecked_vector<OatDexFile> oat_dex_files_;
+  dchecked_vector<OatClassHeader> oat_class_headers_;
   dchecked_vector<OatClass> oat_classes_;
   std::unique_ptr<const std::vector<uint8_t>> jni_dlsym_lookup_;
   std::unique_ptr<const std::vector<uint8_t>> quick_generic_jni_trampoline_;
diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc
index 1e75f10..fe7ecd1 100644
--- a/compiler/optimizing/block_builder.cc
+++ b/compiler/optimizing/block_builder.cc
@@ -17,6 +17,7 @@
 #include "block_builder.h"
 
 #include "bytecode_utils.h"
+#include "quicken_info.h"
 
 namespace art {
 
@@ -121,13 +122,18 @@
   HBasicBlock* block = graph_->GetEntryBlock();
   graph_->AddBlock(block);
 
+  size_t quicken_index = 0;
   bool is_throwing_block = false;
+  // Calculate the qucikening index here instead of CreateBranchTargets since it's easier to
+  // calculate in dex_pc order.
   for (CodeItemIterator it(code_item_); !it.Done(); it.Advance()) {
     uint32_t dex_pc = it.CurrentDexPc();
 
     // Check if this dex_pc address starts a new basic block.
     HBasicBlock* next_block = GetBlockAt(dex_pc);
     if (next_block != nullptr) {
+      // We only need quicken index entries for basic block boundaries.
+      quicken_index_for_dex_pc_.Put(dex_pc, quicken_index);
       if (block != nullptr) {
         // Last instruction did not end its basic block but a new one starts here.
         // It must have been a block falling through into the next one.
@@ -137,6 +143,10 @@
       is_throwing_block = false;
       graph_->AddBlock(block);
     }
+    // Make sure to increment this before the continues.
+    if (QuickenInfoTable::NeedsIndexForInstruction(&it.CurrentInstruction())) {
+      ++quicken_index;
+    }
 
     if (block == nullptr) {
       // Ignore dead code.
@@ -371,4 +381,8 @@
   return true;
 }
 
+size_t HBasicBlockBuilder::GetQuickenIndex(uint32_t dex_pc) const {
+  return quicken_index_for_dex_pc_.Get(dex_pc);
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/block_builder.h b/compiler/optimizing/block_builder.h
index 1be0b4c..6adce81 100644
--- a/compiler/optimizing/block_builder.h
+++ b/compiler/optimizing/block_builder.h
@@ -37,7 +37,8 @@
                         nullptr,
                         arena_->Adapter(kArenaAllocGraphBuilder)),
         throwing_blocks_(kDefaultNumberOfThrowingBlocks, arena_->Adapter(kArenaAllocGraphBuilder)),
-        number_of_branches_(0u) {}
+        number_of_branches_(0u),
+        quicken_index_for_dex_pc_(std::less<uint32_t>(), arena_->Adapter()) {}
 
   // Creates basic blocks in `graph_` at branch target dex_pc positions of the
   // `code_item_`. Blocks are connected but left unpopulated with instructions.
@@ -48,6 +49,8 @@
   size_t GetNumberOfBranches() const { return number_of_branches_; }
   HBasicBlock* GetBlockAt(uint32_t dex_pc) const { return branch_targets_[dex_pc]; }
 
+  size_t GetQuickenIndex(uint32_t dex_pc) const;
+
  private:
   // Creates a basic block starting at given `dex_pc`.
   HBasicBlock* MaybeCreateBlockAt(uint32_t dex_pc);
@@ -78,6 +81,9 @@
   ArenaVector<HBasicBlock*> throwing_blocks_;
   size_t number_of_branches_;
 
+  // A table to quickly find the quicken index for the first instruction of a basic block.
+  ArenaSafeMap<uint32_t, uint32_t> quicken_index_for_dex_pc_;
+
   static constexpr size_t kDefaultNumberOfThrowingBlocks = 2u;
 
   DISALLOW_COPY_AND_ASSIGN(HBasicBlockBuilder);
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 93234f9..2872cf74 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -17,7 +17,6 @@
 #include "code_generator.h"
 
 #ifdef ART_ENABLE_CODEGEN_arm
-#include "code_generator_arm.h"
 #include "code_generator_arm_vixl.h"
 #endif
 
@@ -627,19 +626,11 @@
 #ifdef ART_ENABLE_CODEGEN_arm
     case kArm:
     case kThumb2: {
-      if (kArmUseVIXL32) {
-        return std::unique_ptr<CodeGenerator>(
-            new (arena) arm::CodeGeneratorARMVIXL(graph,
-                                                  *isa_features.AsArmInstructionSetFeatures(),
-                                                  compiler_options,
-                                                  stats));
-      } else {
-          return std::unique_ptr<CodeGenerator>(
-            new (arena) arm::CodeGeneratorARM(graph,
-                                              *isa_features.AsArmInstructionSetFeatures(),
-                                              compiler_options,
-                                              stats));
-      }
+      return std::unique_ptr<CodeGenerator>(
+          new (arena) arm::CodeGeneratorARMVIXL(graph,
+                                                *isa_features.AsArmInstructionSetFeatures(),
+                                                compiler_options,
+                                                stats));
     }
 #endif
 #ifdef ART_ENABLE_CODEGEN_arm64
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 7bf43f7..73202b4 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -404,17 +404,6 @@
   // accessing the String's `value` field in String intrinsics.
   static uint32_t GetArrayDataOffset(HArrayGet* array_get);
 
-  // Return the entry point offset for ReadBarrierMarkRegX, where X is `reg`.
-  template <PointerSize pointer_size>
-  static int32_t GetReadBarrierMarkEntryPointsOffset(size_t reg) {
-    // The entry point list defines 30 ReadBarrierMarkRegX entry points.
-    DCHECK_LT(reg, 30u);
-    // The ReadBarrierMarkRegX entry points are ordered by increasing
-    // register number in Thread::tls_Ptr_.quick_entrypoints.
-    return QUICK_ENTRYPOINT_OFFSET(pointer_size, pReadBarrierMarkReg00).Int32Value()
-        + static_cast<size_t>(pointer_size) * reg;
-  }
-
   void EmitParallelMoves(Location from1,
                          Location to1,
                          Primitive::Type type1,
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
deleted file mode 100644
index 6b9f232..0000000
--- a/compiler/optimizing/code_generator_arm.cc
+++ /dev/null
@@ -1,9400 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "code_generator_arm.h"
-
-#include "arch/arm/asm_support_arm.h"
-#include "arch/arm/instruction_set_features_arm.h"
-#include "art_method.h"
-#include "base/bit_utils.h"
-#include "base/bit_utils_iterator.h"
-#include "code_generator_utils.h"
-#include "common_arm.h"
-#include "compiled_method.h"
-#include "entrypoints/quick/quick_entrypoints.h"
-#include "gc/accounting/card_table.h"
-#include "intrinsics.h"
-#include "intrinsics_arm.h"
-#include "linker/arm/relative_patcher_thumb2.h"
-#include "mirror/array-inl.h"
-#include "mirror/class-inl.h"
-#include "thread.h"
-#include "utils/arm/assembler_arm.h"
-#include "utils/arm/managed_register_arm.h"
-#include "utils/assembler.h"
-#include "utils/stack_checks.h"
-
-namespace art {
-
-template<class MirrorType>
-class GcRoot;
-
-namespace arm {
-
-static bool ExpectedPairLayout(Location location) {
-  // We expected this for both core and fpu register pairs.
-  return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
-}
-
-static constexpr Register kMethodRegisterArgument = R0;
-
-static constexpr Register kCoreAlwaysSpillRegister = R5;
-static constexpr Register kCoreCalleeSaves[] =
-    { R5, R6, R7, R8, R10, R11, LR };
-static constexpr SRegister kFpuCalleeSaves[] =
-    { S16, S17, S18, S19, S20, S21, S22, S23, S24, S25, S26, S27, S28, S29, S30, S31 };
-
-// D31 cannot be split into two S registers, and the register allocator only works on
-// S registers. Therefore there is no need to block it.
-static constexpr DRegister DTMP = D31;
-
-static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
-
-// Reference load (except object array loads) is using LDR Rt, [Rn, #offset] which can handle
-// offset < 4KiB. For offsets >= 4KiB, the load shall be emitted as two or more instructions.
-// For the Baker read barrier implementation using link-generated thunks we need to split
-// the offset explicitly.
-constexpr uint32_t kReferenceLoadMinFarOffset = 4 * KB;
-
-// Flags controlling the use of link-time generated thunks for Baker read barriers.
-constexpr bool kBakerReadBarrierLinkTimeThunksEnableForFields = true;
-constexpr bool kBakerReadBarrierLinkTimeThunksEnableForArrays = true;
-constexpr bool kBakerReadBarrierLinkTimeThunksEnableForGcRoots = true;
-
-// The reserved entrypoint register for link-time generated thunks.
-const Register kBakerCcEntrypointRegister = R4;
-
-// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
-#define __ down_cast<ArmAssembler*>(codegen->GetAssembler())->  // NOLINT
-#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
-
-static inline void CheckLastTempIsBakerCcEntrypointRegister(HInstruction* instruction) {
-  DCHECK_EQ(static_cast<uint32_t>(kBakerCcEntrypointRegister),
-            linker::Thumb2RelativePatcher::kBakerCcEntrypointRegister);
-  DCHECK_NE(instruction->GetLocations()->GetTempCount(), 0u);
-  DCHECK_EQ(kBakerCcEntrypointRegister,
-            instruction->GetLocations()->GetTemp(
-                instruction->GetLocations()->GetTempCount() - 1u).AsRegister<Register>());
-}
-
-static inline void EmitPlaceholderBne(CodeGeneratorARM* codegen, Label* bne_label) {
-  ScopedForce32Bit force_32bit(down_cast<Thumb2Assembler*>(codegen->GetAssembler()));
-  __ BindTrackedLabel(bne_label);
-  Label placeholder_label;
-  __ b(&placeholder_label, NE);  // Placeholder, patched at link-time.
-  __ Bind(&placeholder_label);
-}
-
-static inline bool CanEmitNarrowLdr(Register rt, Register rn, uint32_t offset) {
-  return ArmAssembler::IsLowRegister(rt) && ArmAssembler::IsLowRegister(rn) && offset < 32u;
-}
-
-static constexpr int kRegListThreshold = 4;
-
-// SaveLiveRegisters and RestoreLiveRegisters from SlowPathCodeARM operate on sets of S registers,
-// for each live D registers they treat two corresponding S registers as live ones.
-//
-// Two following functions (SaveContiguousSRegisterList, RestoreContiguousSRegisterList) build
-// from a list of contiguous S registers a list of contiguous D registers (processing first/last
-// S registers corner cases) and save/restore this new list treating them as D registers.
-// - decreasing code size
-// - avoiding hazards on Cortex-A57, when a pair of S registers for an actual live D register is
-//   restored and then used in regular non SlowPath code as D register.
-//
-// For the following example (v means the S register is live):
-//   D names: |    D0   |    D1   |    D2   |    D4   | ...
-//   S names: | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | ...
-//   Live?    |    |  v |  v |  v |  v |  v |  v |    | ...
-//
-// S1 and S6 will be saved/restored independently; D registers list (D1, D2) will be processed
-// as D registers.
-static size_t SaveContiguousSRegisterList(size_t first,
-                                          size_t last,
-                                          CodeGenerator* codegen,
-                                          size_t stack_offset) {
-  DCHECK_LE(first, last);
-  if ((first == last) && (first == 0)) {
-    stack_offset += codegen->SaveFloatingPointRegister(stack_offset, first);
-    return stack_offset;
-  }
-  if (first % 2 == 1) {
-    stack_offset += codegen->SaveFloatingPointRegister(stack_offset, first++);
-  }
-
-  bool save_last = false;
-  if (last % 2 == 0) {
-    save_last = true;
-    --last;
-  }
-
-  if (first < last) {
-    DRegister d_reg = static_cast<DRegister>(first / 2);
-    DCHECK_EQ((last - first + 1) % 2, 0u);
-    size_t number_of_d_regs = (last - first + 1) / 2;
-
-    if (number_of_d_regs == 1) {
-      __ StoreDToOffset(d_reg, SP, stack_offset);
-    } else if (number_of_d_regs > 1) {
-      __ add(IP, SP, ShifterOperand(stack_offset));
-      __ vstmiad(IP, d_reg, number_of_d_regs);
-    }
-    stack_offset += number_of_d_regs * kArmWordSize * 2;
-  }
-
-  if (save_last) {
-    stack_offset += codegen->SaveFloatingPointRegister(stack_offset, last + 1);
-  }
-
-  return stack_offset;
-}
-
-static size_t RestoreContiguousSRegisterList(size_t first,
-                                             size_t last,
-                                             CodeGenerator* codegen,
-                                             size_t stack_offset) {
-  DCHECK_LE(first, last);
-  if ((first == last) && (first == 0)) {
-    stack_offset += codegen->RestoreFloatingPointRegister(stack_offset, first);
-    return stack_offset;
-  }
-  if (first % 2 == 1) {
-    stack_offset += codegen->RestoreFloatingPointRegister(stack_offset, first++);
-  }
-
-  bool restore_last = false;
-  if (last % 2 == 0) {
-    restore_last = true;
-    --last;
-  }
-
-  if (first < last) {
-    DRegister d_reg = static_cast<DRegister>(first / 2);
-    DCHECK_EQ((last - first + 1) % 2, 0u);
-    size_t number_of_d_regs = (last - first + 1) / 2;
-    if (number_of_d_regs == 1) {
-      __ LoadDFromOffset(d_reg, SP, stack_offset);
-    } else if (number_of_d_regs > 1) {
-      __ add(IP, SP, ShifterOperand(stack_offset));
-      __ vldmiad(IP, d_reg, number_of_d_regs);
-    }
-    stack_offset += number_of_d_regs * kArmWordSize * 2;
-  }
-
-  if (restore_last) {
-    stack_offset += codegen->RestoreFloatingPointRegister(stack_offset, last + 1);
-  }
-
-  return stack_offset;
-}
-
-void SlowPathCodeARM::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
-  size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
-  size_t orig_offset = stack_offset;
-
-  const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
-  for (uint32_t i : LowToHighBits(core_spills)) {
-    // If the register holds an object, update the stack mask.
-    if (locations->RegisterContainsObject(i)) {
-      locations->SetStackBit(stack_offset / kVRegSize);
-    }
-    DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
-    DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
-    saved_core_stack_offsets_[i] = stack_offset;
-    stack_offset += kArmWordSize;
-  }
-
-  int reg_num = POPCOUNT(core_spills);
-  if (reg_num != 0) {
-    if (reg_num > kRegListThreshold) {
-      __ StoreList(RegList(core_spills), orig_offset);
-    } else {
-      stack_offset = orig_offset;
-      for (uint32_t i : LowToHighBits(core_spills)) {
-        stack_offset += codegen->SaveCoreRegister(stack_offset, i);
-      }
-    }
-  }
-
-  uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
-  orig_offset = stack_offset;
-  for (uint32_t i : LowToHighBits(fp_spills)) {
-    DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
-    saved_fpu_stack_offsets_[i] = stack_offset;
-    stack_offset += kArmWordSize;
-  }
-
-  stack_offset = orig_offset;
-  while (fp_spills != 0u) {
-    uint32_t begin = CTZ(fp_spills);
-    uint32_t tmp = fp_spills + (1u << begin);
-    fp_spills &= tmp;  // Clear the contiguous range of 1s.
-    uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp);  // CTZ(0) is undefined.
-    stack_offset = SaveContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
-  }
-  DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
-}
-
-void SlowPathCodeARM::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
-  size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
-  size_t orig_offset = stack_offset;
-
-  const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
-  for (uint32_t i : LowToHighBits(core_spills)) {
-    DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
-    DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
-    stack_offset += kArmWordSize;
-  }
-
-  int reg_num = POPCOUNT(core_spills);
-  if (reg_num != 0) {
-    if (reg_num > kRegListThreshold) {
-      __ LoadList(RegList(core_spills), orig_offset);
-    } else {
-      stack_offset = orig_offset;
-      for (uint32_t i : LowToHighBits(core_spills)) {
-        stack_offset += codegen->RestoreCoreRegister(stack_offset, i);
-      }
-    }
-  }
-
-  uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
-  while (fp_spills != 0u) {
-    uint32_t begin = CTZ(fp_spills);
-    uint32_t tmp = fp_spills + (1u << begin);
-    fp_spills &= tmp;  // Clear the contiguous range of 1s.
-    uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp);  // CTZ(0) is undefined.
-    stack_offset = RestoreContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
-  }
-  DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
-}
-
-class NullCheckSlowPathARM : public SlowPathCodeARM {
- public:
-  explicit NullCheckSlowPathARM(HNullCheck* instruction) : SlowPathCodeARM(instruction) {}
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    __ Bind(GetEntryLabel());
-    if (instruction_->CanThrowIntoCatchBlock()) {
-      // Live registers will be restored in the catch block if caught.
-      SaveLiveRegisters(codegen, instruction_->GetLocations());
-    }
-    arm_codegen->InvokeRuntime(kQuickThrowNullPointer,
-                               instruction_,
-                               instruction_->GetDexPc(),
-                               this);
-    CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
-  }
-
-  bool IsFatal() const OVERRIDE { return true; }
-
-  const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM"; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM);
-};
-
-class DivZeroCheckSlowPathARM : public SlowPathCodeARM {
- public:
-  explicit DivZeroCheckSlowPathARM(HDivZeroCheck* instruction) : SlowPathCodeARM(instruction) {}
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    __ Bind(GetEntryLabel());
-    arm_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
-    CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
-  }
-
-  bool IsFatal() const OVERRIDE { return true; }
-
-  const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM"; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM);
-};
-
-class SuspendCheckSlowPathARM : public SlowPathCodeARM {
- public:
-  SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor)
-      : SlowPathCodeARM(instruction), successor_(successor) {}
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    __ Bind(GetEntryLabel());
-    arm_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
-    CheckEntrypointTypes<kQuickTestSuspend, void, void>();
-    if (successor_ == nullptr) {
-      __ b(GetReturnLabel());
-    } else {
-      __ b(arm_codegen->GetLabelOf(successor_));
-    }
-  }
-
-  Label* GetReturnLabel() {
-    DCHECK(successor_ == nullptr);
-    return &return_label_;
-  }
-
-  HBasicBlock* GetSuccessor() const {
-    return successor_;
-  }
-
-  const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM"; }
-
- private:
-  // If not null, the block to branch to after the suspend check.
-  HBasicBlock* const successor_;
-
-  // If `successor_` is null, the label to branch to after the suspend check.
-  Label return_label_;
-
-  DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM);
-};
-
-class BoundsCheckSlowPathARM : public SlowPathCodeARM {
- public:
-  explicit BoundsCheckSlowPathARM(HBoundsCheck* instruction)
-      : SlowPathCodeARM(instruction) {}
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    LocationSummary* locations = instruction_->GetLocations();
-
-    __ Bind(GetEntryLabel());
-    if (instruction_->CanThrowIntoCatchBlock()) {
-      // Live registers will be restored in the catch block if caught.
-      SaveLiveRegisters(codegen, instruction_->GetLocations());
-    }
-    // We're moving two locations to locations that could overlap, so we need a parallel
-    // move resolver.
-    InvokeRuntimeCallingConvention calling_convention;
-    codegen->EmitParallelMoves(
-        locations->InAt(0),
-        Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
-        Primitive::kPrimInt,
-        locations->InAt(1),
-        Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
-        Primitive::kPrimInt);
-    QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
-        ? kQuickThrowStringBounds
-        : kQuickThrowArrayBounds;
-    arm_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
-    CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
-    CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
-  }
-
-  bool IsFatal() const OVERRIDE { return true; }
-
-  const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM"; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM);
-};
-
-class LoadClassSlowPathARM : public SlowPathCodeARM {
- public:
-  LoadClassSlowPathARM(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, bool do_clinit)
-      : SlowPathCodeARM(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) {
-    DCHECK(at->IsLoadClass() || at->IsClinitCheck());
-  }
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    LocationSummary* locations = instruction_->GetLocations();
-    Location out = locations->Out();
-    constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
-
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, locations);
-
-    InvokeRuntimeCallingConvention calling_convention;
-    // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry.
-    DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
-    bool is_load_class_bss_entry =
-        (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry);
-    Register entry_address = kNoRegister;
-    if (is_load_class_bss_entry && call_saves_everything_except_r0) {
-      Register temp = locations->GetTemp(0).AsRegister<Register>();
-      // In the unlucky case that the `temp` is R0, we preserve the address in `out` across
-      // the kSaveEverything call.
-      bool temp_is_r0 = (temp == calling_convention.GetRegisterAt(0));
-      entry_address = temp_is_r0 ? out.AsRegister<Register>() : temp;
-      DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0));
-      if (temp_is_r0) {
-        __ mov(entry_address, ShifterOperand(temp));
-      }
-    }
-    dex::TypeIndex type_index = cls_->GetTypeIndex();
-    __ LoadImmediate(calling_convention.GetRegisterAt(0), type_index.index_);
-    QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
-                                                : kQuickInitializeType;
-    arm_codegen->InvokeRuntime(entrypoint, instruction_, dex_pc_, this);
-    if (do_clinit_) {
-      CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>();
-    } else {
-      CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
-    }
-
-    // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry.
-    if (is_load_class_bss_entry) {
-      if (call_saves_everything_except_r0) {
-        // The class entry address was preserved in `entry_address` thanks to kSaveEverything.
-        __ str(R0, Address(entry_address));
-      } else {
-        // For non-Baker read barrier, we need to re-calculate the address of the string entry.
-        Register temp = IP;
-        CodeGeneratorARM::PcRelativePatchInfo* labels =
-            arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
-        __ BindTrackedLabel(&labels->movw_label);
-        __ movw(temp, /* placeholder */ 0u);
-        __ BindTrackedLabel(&labels->movt_label);
-        __ movt(temp, /* placeholder */ 0u);
-        __ BindTrackedLabel(&labels->add_pc_label);
-        __ add(temp, temp, ShifterOperand(PC));
-        __ str(R0, Address(temp));
-      }
-    }
-    // Move the class to the desired location.
-    if (out.IsValid()) {
-      DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
-      arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
-    }
-    RestoreLiveRegisters(codegen, locations);
-    __ b(GetExitLabel());
-  }
-
-  const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARM"; }
-
- private:
-  // The class this slow path will load.
-  HLoadClass* const cls_;
-
-  // The dex PC of `at_`.
-  const uint32_t dex_pc_;
-
-  // Whether to initialize the class.
-  const bool do_clinit_;
-
-  DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM);
-};
-
-class LoadStringSlowPathARM : public SlowPathCodeARM {
- public:
-  explicit LoadStringSlowPathARM(HLoadString* instruction) : SlowPathCodeARM(instruction) {}
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    DCHECK(instruction_->IsLoadString());
-    DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
-    LocationSummary* locations = instruction_->GetLocations();
-    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
-    HLoadString* load = instruction_->AsLoadString();
-    const dex::StringIndex string_index = load->GetStringIndex();
-    Register out = locations->Out().AsRegister<Register>();
-    constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
-
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, locations);
-
-    InvokeRuntimeCallingConvention calling_convention;
-    // In the unlucky case that the `temp` is R0, we preserve the address in `out` across
-    // the kSaveEverything call.
-    Register entry_address = kNoRegister;
-    if (call_saves_everything_except_r0) {
-      Register temp = locations->GetTemp(0).AsRegister<Register>();
-      bool temp_is_r0 = (temp == calling_convention.GetRegisterAt(0));
-      entry_address = temp_is_r0 ? out : temp;
-      DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0));
-      if (temp_is_r0) {
-        __ mov(entry_address, ShifterOperand(temp));
-      }
-    }
-
-    __ LoadImmediate(calling_convention.GetRegisterAt(0), string_index.index_);
-    arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
-    CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
-
-    // Store the resolved String to the .bss entry.
-    if (call_saves_everything_except_r0) {
-      // The string entry address was preserved in `entry_address` thanks to kSaveEverything.
-      __ str(R0, Address(entry_address));
-    } else {
-      // For non-Baker read barrier, we need to re-calculate the address of the string entry.
-      Register temp = IP;
-      CodeGeneratorARM::PcRelativePatchInfo* labels =
-          arm_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
-      __ BindTrackedLabel(&labels->movw_label);
-      __ movw(temp, /* placeholder */ 0u);
-      __ BindTrackedLabel(&labels->movt_label);
-      __ movt(temp, /* placeholder */ 0u);
-      __ BindTrackedLabel(&labels->add_pc_label);
-      __ add(temp, temp, ShifterOperand(PC));
-      __ str(R0, Address(temp));
-    }
-
-    arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
-    RestoreLiveRegisters(codegen, locations);
-
-    __ b(GetExitLabel());
-  }
-
-  const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM"; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM);
-};
-
-class TypeCheckSlowPathARM : public SlowPathCodeARM {
- public:
-  TypeCheckSlowPathARM(HInstruction* instruction, bool is_fatal)
-      : SlowPathCodeARM(instruction), is_fatal_(is_fatal) {}
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    LocationSummary* locations = instruction_->GetLocations();
-    DCHECK(instruction_->IsCheckCast()
-           || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
-
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    __ Bind(GetEntryLabel());
-
-    if (!is_fatal_) {
-      SaveLiveRegisters(codegen, locations);
-    }
-
-    // We're moving two locations to locations that could overlap, so we need a parallel
-    // move resolver.
-    InvokeRuntimeCallingConvention calling_convention;
-    codegen->EmitParallelMoves(locations->InAt(0),
-                               Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
-                               Primitive::kPrimNot,
-                               locations->InAt(1),
-                               Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
-                               Primitive::kPrimNot);
-    if (instruction_->IsInstanceOf()) {
-      arm_codegen->InvokeRuntime(kQuickInstanceofNonTrivial,
-                                 instruction_,
-                                 instruction_->GetDexPc(),
-                                 this);
-      CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
-      arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
-    } else {
-      DCHECK(instruction_->IsCheckCast());
-      arm_codegen->InvokeRuntime(kQuickCheckInstanceOf,
-                                 instruction_,
-                                 instruction_->GetDexPc(),
-                                 this);
-      CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
-    }
-
-    if (!is_fatal_) {
-      RestoreLiveRegisters(codegen, locations);
-      __ b(GetExitLabel());
-    }
-  }
-
-  const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARM"; }
-
-  bool IsFatal() const OVERRIDE { return is_fatal_; }
-
- private:
-  const bool is_fatal_;
-
-  DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM);
-};
-
-class DeoptimizationSlowPathARM : public SlowPathCodeARM {
- public:
-  explicit DeoptimizationSlowPathARM(HDeoptimize* instruction)
-    : SlowPathCodeARM(instruction) {}
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    __ Bind(GetEntryLabel());
-    LocationSummary* locations = instruction_->GetLocations();
-    SaveLiveRegisters(codegen, locations);
-    InvokeRuntimeCallingConvention calling_convention;
-    __ LoadImmediate(calling_convention.GetRegisterAt(0),
-                     static_cast<uint32_t>(instruction_->AsDeoptimize()->GetDeoptimizationKind()));
-    arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
-    CheckEntrypointTypes<kQuickDeoptimize, void, DeoptimizationKind>();
-  }
-
-  const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM"; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM);
-};
-
-class ArraySetSlowPathARM : public SlowPathCodeARM {
- public:
-  explicit ArraySetSlowPathARM(HInstruction* instruction) : SlowPathCodeARM(instruction) {}
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    LocationSummary* locations = instruction_->GetLocations();
-    __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, locations);
-
-    InvokeRuntimeCallingConvention calling_convention;
-    HParallelMove parallel_move(codegen->GetGraph()->GetArena());
-    parallel_move.AddMove(
-        locations->InAt(0),
-        Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
-        Primitive::kPrimNot,
-        nullptr);
-    parallel_move.AddMove(
-        locations->InAt(1),
-        Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
-        Primitive::kPrimInt,
-        nullptr);
-    parallel_move.AddMove(
-        locations->InAt(2),
-        Location::RegisterLocation(calling_convention.GetRegisterAt(2)),
-        Primitive::kPrimNot,
-        nullptr);
-    codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
-
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    arm_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
-    CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
-    RestoreLiveRegisters(codegen, locations);
-    __ b(GetExitLabel());
-  }
-
-  const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM"; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM);
-};
-
-// Abstract base class for read barrier slow paths marking a reference
-// `ref`.
-//
-// Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked.
-class ReadBarrierMarkSlowPathBaseARM : public SlowPathCodeARM {
- protected:
-  ReadBarrierMarkSlowPathBaseARM(HInstruction* instruction, Location ref, Location entrypoint)
-      : SlowPathCodeARM(instruction), ref_(ref), entrypoint_(entrypoint) {
-    DCHECK(kEmitCompilerReadBarrier);
-  }
-
-  const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathBaseARM"; }
-
-  // Generate assembly code calling the read barrier marking runtime
-  // entry point (ReadBarrierMarkRegX).
-  void GenerateReadBarrierMarkRuntimeCall(CodeGenerator* codegen) {
-    Register ref_reg = ref_.AsRegister<Register>();
-
-    // No need to save live registers; it's taken care of by the
-    // entrypoint. Also, there is no need to update the stack mask,
-    // as this runtime call will not trigger a garbage collection.
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    DCHECK_NE(ref_reg, SP);
-    DCHECK_NE(ref_reg, LR);
-    DCHECK_NE(ref_reg, PC);
-    // IP is used internally by the ReadBarrierMarkRegX entry point
-    // as a temporary, it cannot be the entry point's input/output.
-    DCHECK_NE(ref_reg, IP);
-    DCHECK(0 <= ref_reg && ref_reg < kNumberOfCoreRegisters) << ref_reg;
-    // "Compact" slow path, saving two moves.
-    //
-    // Instead of using the standard runtime calling convention (input
-    // and output in R0):
-    //
-    //   R0 <- ref
-    //   R0 <- ReadBarrierMark(R0)
-    //   ref <- R0
-    //
-    // we just use rX (the register containing `ref`) as input and output
-    // of a dedicated entrypoint:
-    //
-    //   rX <- ReadBarrierMarkRegX(rX)
-    //
-    if (entrypoint_.IsValid()) {
-      arm_codegen->ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction_, this);
-      __ blx(entrypoint_.AsRegister<Register>());
-    } else {
-      // Entrypoint is not already loaded, load from the thread.
-      int32_t entry_point_offset =
-          CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref_reg);
-      // This runtime call does not require a stack map.
-      arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
-    }
-  }
-
-  // The location (register) of the marked object reference.
-  const Location ref_;
-
-  // The location of the entrypoint if it is already loaded.
-  const Location entrypoint_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathBaseARM);
-};
-
-// Slow path marking an object reference `ref` during a read
-// barrier. The field `obj.field` in the object `obj` holding this
-// reference does not get updated by this slow path after marking.
-//
-// This means that after the execution of this slow path, `ref` will
-// always be up-to-date, but `obj.field` may not; i.e., after the
-// flip, `ref` will be a to-space reference, but `obj.field` will
-// probably still be a from-space reference (unless it gets updated by
-// another thread, or if another thread installed another object
-// reference (different from `ref`) in `obj.field`).
-//
-// If `entrypoint` is a valid location it is assumed to already be
-// holding the entrypoint. The case where the entrypoint is passed in
-// is when the decision to mark is based on whether the GC is marking.
-class ReadBarrierMarkSlowPathARM : public ReadBarrierMarkSlowPathBaseARM {
- public:
-  ReadBarrierMarkSlowPathARM(HInstruction* instruction,
-                             Location ref,
-                             Location entrypoint = Location::NoLocation())
-      : ReadBarrierMarkSlowPathBaseARM(instruction, ref, entrypoint) {
-    DCHECK(kEmitCompilerReadBarrier);
-  }
-
-  const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathARM"; }
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    LocationSummary* locations = instruction_->GetLocations();
-    DCHECK(locations->CanCall());
-    if (kIsDebugBuild) {
-      Register ref_reg = ref_.AsRegister<Register>();
-      DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
-    }
-    DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
-        << "Unexpected instruction in read barrier marking slow path: "
-        << instruction_->DebugName();
-
-    __ Bind(GetEntryLabel());
-    GenerateReadBarrierMarkRuntimeCall(codegen);
-    __ b(GetExitLabel());
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathARM);
-};
-
-// Slow path loading `obj`'s lock word, loading a reference from
-// object `*(obj + offset + (index << scale_factor))` into `ref`, and
-// marking `ref` if `obj` is gray according to the lock word (Baker
-// read barrier). The field `obj.field` in the object `obj` holding
-// this reference does not get updated by this slow path after marking
-// (see LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM
-// below for that).
-//
-// This means that after the execution of this slow path, `ref` will
-// always be up-to-date, but `obj.field` may not; i.e., after the
-// flip, `ref` will be a to-space reference, but `obj.field` will
-// probably still be a from-space reference (unless it gets updated by
-// another thread, or if another thread installed another object
-// reference (different from `ref`) in `obj.field`).
-//
-// Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked.
-class LoadReferenceWithBakerReadBarrierSlowPathARM : public ReadBarrierMarkSlowPathBaseARM {
- public:
-  LoadReferenceWithBakerReadBarrierSlowPathARM(HInstruction* instruction,
-                                               Location ref,
-                                               Register obj,
-                                               uint32_t offset,
-                                               Location index,
-                                               ScaleFactor scale_factor,
-                                               bool needs_null_check,
-                                               Register temp,
-                                               Location entrypoint)
-      : ReadBarrierMarkSlowPathBaseARM(instruction, ref, entrypoint),
-        obj_(obj),
-        offset_(offset),
-        index_(index),
-        scale_factor_(scale_factor),
-        needs_null_check_(needs_null_check),
-        temp_(temp) {
-    DCHECK(kEmitCompilerReadBarrier);
-    DCHECK(kUseBakerReadBarrier);
-  }
-
-  const char* GetDescription() const OVERRIDE {
-    return "LoadReferenceWithBakerReadBarrierSlowPathARM";
-  }
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    LocationSummary* locations = instruction_->GetLocations();
-    Register ref_reg = ref_.AsRegister<Register>();
-    DCHECK(locations->CanCall());
-    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
-    DCHECK_NE(ref_reg, temp_);
-    DCHECK(instruction_->IsInstanceFieldGet() ||
-           instruction_->IsStaticFieldGet() ||
-           instruction_->IsArrayGet() ||
-           instruction_->IsArraySet() ||
-           instruction_->IsInstanceOf() ||
-           instruction_->IsCheckCast() ||
-           (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()) ||
-           (instruction_->IsInvokeStaticOrDirect() && instruction_->GetLocations()->Intrinsified()))
-        << "Unexpected instruction in read barrier marking slow path: "
-        << instruction_->DebugName();
-    // The read barrier instrumentation of object ArrayGet
-    // instructions does not support the HIntermediateAddress
-    // instruction.
-    DCHECK(!(instruction_->IsArrayGet() &&
-             instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
-
-    __ Bind(GetEntryLabel());
-
-    // When using MaybeGenerateReadBarrierSlow, the read barrier call is
-    // inserted after the original load. However, in fast path based
-    // Baker's read barriers, we need to perform the load of
-    // mirror::Object::monitor_ *before* the original reference load.
-    // This load-load ordering is required by the read barrier.
-    // The slow path (for Baker's algorithm) should look like:
-    //
-    //   uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
-    //   lfence;  // Load fence or artificial data dependency to prevent load-load reordering
-    //   HeapReference<mirror::Object> ref = *src;  // Original reference load.
-    //   bool is_gray = (rb_state == ReadBarrier::GrayState());
-    //   if (is_gray) {
-    //     ref = entrypoint(ref);  // ref = ReadBarrier::Mark(ref);  // Runtime entry point call.
-    //   }
-    //
-    // Note: the original implementation in ReadBarrier::Barrier is
-    // slightly more complex as it performs additional checks that we do
-    // not do here for performance reasons.
-
-    // /* int32_t */ monitor = obj->monitor_
-    uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
-    __ LoadFromOffset(kLoadWord, temp_, obj_, monitor_offset);
-    if (needs_null_check_) {
-      codegen->MaybeRecordImplicitNullCheck(instruction_);
-    }
-    // /* LockWord */ lock_word = LockWord(monitor)
-    static_assert(sizeof(LockWord) == sizeof(int32_t),
-                  "art::LockWord and int32_t have different sizes.");
-
-    // Introduce a dependency on the lock_word including the rb_state,
-    // which shall prevent load-load reordering without using
-    // a memory barrier (which would be more expensive).
-    // `obj` is unchanged by this operation, but its value now depends
-    // on `temp`.
-    __ add(obj_, obj_, ShifterOperand(temp_, LSR, 32));
-
-    // The actual reference load.
-    // A possible implicit null check has already been handled above.
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    arm_codegen->GenerateRawReferenceLoad(
-        instruction_, ref_, obj_, offset_, index_, scale_factor_, /* needs_null_check */ false);
-
-    // Mark the object `ref` when `obj` is gray.
-    //
-    // if (rb_state == ReadBarrier::GrayState())
-    //   ref = ReadBarrier::Mark(ref);
-    //
-    // Given the numeric representation, it's enough to check the low bit of the
-    // rb_state. We do that by shifting the bit out of the lock word with LSRS
-    // which can be a 16-bit instruction unlike the TST immediate.
-    static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
-    static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
-    __ Lsrs(temp_, temp_, LockWord::kReadBarrierStateShift + 1);
-    __ b(GetExitLabel(), CC);  // Carry flag is the last bit shifted out by LSRS.
-    GenerateReadBarrierMarkRuntimeCall(codegen);
-
-    __ b(GetExitLabel());
-  }
-
- private:
-  // The register containing the object holding the marked object reference field.
-  Register obj_;
-  // The offset, index and scale factor to access the reference in `obj_`.
-  uint32_t offset_;
-  Location index_;
-  ScaleFactor scale_factor_;
-  // Is a null check required?
-  bool needs_null_check_;
-  // A temporary register used to hold the lock word of `obj_`.
-  Register temp_;
-
-  DISALLOW_COPY_AND_ASSIGN(LoadReferenceWithBakerReadBarrierSlowPathARM);
-};
-
-// Slow path loading `obj`'s lock word, loading a reference from
-// object `*(obj + offset + (index << scale_factor))` into `ref`, and
-// marking `ref` if `obj` is gray according to the lock word (Baker
-// read barrier). If needed, this slow path also atomically updates
-// the field `obj.field` in the object `obj` holding this reference
-// after marking (contrary to
-// LoadReferenceWithBakerReadBarrierSlowPathARM above, which never
-// tries to update `obj.field`).
-//
-// This means that after the execution of this slow path, both `ref`
-// and `obj.field` will be up-to-date; i.e., after the flip, both will
-// hold the same to-space reference (unless another thread installed
-// another object reference (different from `ref`) in `obj.field`).
-//
-// Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked.
-class LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM
-    : public ReadBarrierMarkSlowPathBaseARM {
- public:
-  LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM(HInstruction* instruction,
-                                                             Location ref,
-                                                             Register obj,
-                                                             uint32_t offset,
-                                                             Location index,
-                                                             ScaleFactor scale_factor,
-                                                             bool needs_null_check,
-                                                             Register temp1,
-                                                             Register temp2,
-                                                             Location entrypoint)
-      : ReadBarrierMarkSlowPathBaseARM(instruction, ref, entrypoint),
-        obj_(obj),
-        offset_(offset),
-        index_(index),
-        scale_factor_(scale_factor),
-        needs_null_check_(needs_null_check),
-        temp1_(temp1),
-        temp2_(temp2) {
-    DCHECK(kEmitCompilerReadBarrier);
-    DCHECK(kUseBakerReadBarrier);
-  }
-
-  const char* GetDescription() const OVERRIDE {
-    return "LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM";
-  }
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    LocationSummary* locations = instruction_->GetLocations();
-    Register ref_reg = ref_.AsRegister<Register>();
-    DCHECK(locations->CanCall());
-    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
-    DCHECK_NE(ref_reg, temp1_);
-
-    // This slow path is only used by the UnsafeCASObject intrinsic at the moment.
-    DCHECK((instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
-        << "Unexpected instruction in read barrier marking and field updating slow path: "
-        << instruction_->DebugName();
-    DCHECK(instruction_->GetLocations()->Intrinsified());
-    DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kUnsafeCASObject);
-    DCHECK_EQ(offset_, 0u);
-    DCHECK_EQ(scale_factor_, ScaleFactor::TIMES_1);
-    // The location of the offset of the marked reference field within `obj_`.
-    Location field_offset = index_;
-    DCHECK(field_offset.IsRegisterPair()) << field_offset;
-
-    __ Bind(GetEntryLabel());
-
-    // The implementation is similar to LoadReferenceWithBakerReadBarrierSlowPathARM's:
-    //
-    //   uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
-    //   lfence;  // Load fence or artificial data dependency to prevent load-load reordering
-    //   HeapReference<mirror::Object> ref = *src;  // Original reference load.
-    //   bool is_gray = (rb_state == ReadBarrier::GrayState());
-    //   if (is_gray) {
-    //     old_ref = ref;
-    //     ref = entrypoint(ref);  // ref = ReadBarrier::Mark(ref);  // Runtime entry point call.
-    //     compareAndSwapObject(obj, field_offset, old_ref, ref);
-    //   }
-
-    // /* int32_t */ monitor = obj->monitor_
-    uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
-    __ LoadFromOffset(kLoadWord, temp1_, obj_, monitor_offset);
-    if (needs_null_check_) {
-      codegen->MaybeRecordImplicitNullCheck(instruction_);
-    }
-    // /* LockWord */ lock_word = LockWord(monitor)
-    static_assert(sizeof(LockWord) == sizeof(int32_t),
-                  "art::LockWord and int32_t have different sizes.");
-
-    // Introduce a dependency on the lock_word including the rb_state,
-    // which shall prevent load-load reordering without using
-    // a memory barrier (which would be more expensive).
-    // `obj` is unchanged by this operation, but its value now depends
-    // on `temp1`.
-    __ add(obj_, obj_, ShifterOperand(temp1_, LSR, 32));
-
-    // The actual reference load.
-    // A possible implicit null check has already been handled above.
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    arm_codegen->GenerateRawReferenceLoad(
-        instruction_, ref_, obj_, offset_, index_, scale_factor_, /* needs_null_check */ false);
-
-    // Mark the object `ref` when `obj` is gray.
-    //
-    // if (rb_state == ReadBarrier::GrayState())
-    //   ref = ReadBarrier::Mark(ref);
-    //
-    // Given the numeric representation, it's enough to check the low bit of the
-    // rb_state. We do that by shifting the bit out of the lock word with LSRS
-    // which can be a 16-bit instruction unlike the TST immediate.
-    static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
-    static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
-    __ Lsrs(temp1_, temp1_, LockWord::kReadBarrierStateShift + 1);
-    __ b(GetExitLabel(), CC);  // Carry flag is the last bit shifted out by LSRS.
-
-    // Save the old value of the reference before marking it.
-    // Note that we cannot use IP to save the old reference, as IP is
-    // used internally by the ReadBarrierMarkRegX entry point, and we
-    // need the old reference after the call to that entry point.
-    DCHECK_NE(temp1_, IP);
-    __ Mov(temp1_, ref_reg);
-
-    GenerateReadBarrierMarkRuntimeCall(codegen);
-
-    // If the new reference is different from the old reference,
-    // update the field in the holder (`*(obj_ + field_offset)`).
-    //
-    // Note that this field could also hold a different object, if
-    // another thread had concurrently changed it. In that case, the
-    // LDREX/SUBS/ITNE sequence of instructions in the compare-and-set
-    // (CAS) operation below would abort the CAS, leaving the field
-    // as-is.
-    __ cmp(temp1_, ShifterOperand(ref_reg));
-    __ b(GetExitLabel(), EQ);
-
-    // Update the the holder's field atomically.  This may fail if
-    // mutator updates before us, but it's OK.  This is achieved
-    // using a strong compare-and-set (CAS) operation with relaxed
-    // memory synchronization ordering, where the expected value is
-    // the old reference and the desired value is the new reference.
-
-    // Convenience aliases.
-    Register base = obj_;
-    // The UnsafeCASObject intrinsic uses a register pair as field
-    // offset ("long offset"), of which only the low part contains
-    // data.
-    Register offset = field_offset.AsRegisterPairLow<Register>();
-    Register expected = temp1_;
-    Register value = ref_reg;
-    Register tmp_ptr = IP;       // Pointer to actual memory.
-    Register tmp = temp2_;       // Value in memory.
-
-    __ add(tmp_ptr, base, ShifterOperand(offset));
-
-    if (kPoisonHeapReferences) {
-      __ PoisonHeapReference(expected);
-      if (value == expected) {
-        // Do not poison `value`, as it is the same register as
-        // `expected`, which has just been poisoned.
-      } else {
-        __ PoisonHeapReference(value);
-      }
-    }
-
-    // do {
-    //   tmp = [r_ptr] - expected;
-    // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
-
-    Label loop_head, exit_loop;
-    __ Bind(&loop_head);
-
-    __ ldrex(tmp, tmp_ptr);
-
-    __ subs(tmp, tmp, ShifterOperand(expected));
-
-    __ it(NE);
-    __ clrex(NE);
-
-    __ b(&exit_loop, NE);
-
-    __ strex(tmp, value, tmp_ptr);
-    __ cmp(tmp, ShifterOperand(1));
-    __ b(&loop_head, EQ);
-
-    __ Bind(&exit_loop);
-
-    if (kPoisonHeapReferences) {
-      __ UnpoisonHeapReference(expected);
-      if (value == expected) {
-        // Do not unpoison `value`, as it is the same register as
-        // `expected`, which has just been unpoisoned.
-      } else {
-        __ UnpoisonHeapReference(value);
-      }
-    }
-
-    __ b(GetExitLabel());
-  }
-
- private:
-  // The register containing the object holding the marked object reference field.
-  const Register obj_;
-  // The offset, index and scale factor to access the reference in `obj_`.
-  uint32_t offset_;
-  Location index_;
-  ScaleFactor scale_factor_;
-  // Is a null check required?
-  bool needs_null_check_;
-  // A temporary register used to hold the lock word of `obj_`; and
-  // also to hold the original reference value, when the reference is
-  // marked.
-  const Register temp1_;
-  // A temporary register used in the implementation of the CAS, to
-  // update the object's reference field.
-  const Register temp2_;
-
-  DISALLOW_COPY_AND_ASSIGN(LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM);
-};
-
-// Slow path generating a read barrier for a heap reference.
-class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCodeARM {
- public:
-  ReadBarrierForHeapReferenceSlowPathARM(HInstruction* instruction,
-                                         Location out,
-                                         Location ref,
-                                         Location obj,
-                                         uint32_t offset,
-                                         Location index)
-      : SlowPathCodeARM(instruction),
-        out_(out),
-        ref_(ref),
-        obj_(obj),
-        offset_(offset),
-        index_(index) {
-    DCHECK(kEmitCompilerReadBarrier);
-    // If `obj` is equal to `out` or `ref`, it means the initial object
-    // has been overwritten by (or after) the heap object reference load
-    // to be instrumented, e.g.:
-    //
-    //   __ LoadFromOffset(kLoadWord, out, out, offset);
-    //   codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
-    //
-    // In that case, we have lost the information about the original
-    // object, and the emitted read barrier cannot work properly.
-    DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out;
-    DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref;
-  }
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    LocationSummary* locations = instruction_->GetLocations();
-    Register reg_out = out_.AsRegister<Register>();
-    DCHECK(locations->CanCall());
-    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
-    DCHECK(instruction_->IsInstanceFieldGet() ||
-           instruction_->IsStaticFieldGet() ||
-           instruction_->IsArrayGet() ||
-           instruction_->IsInstanceOf() ||
-           instruction_->IsCheckCast() ||
-           (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
-        << "Unexpected instruction in read barrier for heap reference slow path: "
-        << instruction_->DebugName();
-    // The read barrier instrumentation of object ArrayGet
-    // instructions does not support the HIntermediateAddress
-    // instruction.
-    DCHECK(!(instruction_->IsArrayGet() &&
-             instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
-
-    __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, locations);
-
-    // We may have to change the index's value, but as `index_` is a
-    // constant member (like other "inputs" of this slow path),
-    // introduce a copy of it, `index`.
-    Location index = index_;
-    if (index_.IsValid()) {
-      // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
-      if (instruction_->IsArrayGet()) {
-        // Compute the actual memory offset and store it in `index`.
-        Register index_reg = index_.AsRegister<Register>();
-        DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg));
-        if (codegen->IsCoreCalleeSaveRegister(index_reg)) {
-          // We are about to change the value of `index_reg` (see the
-          // calls to art::arm::Thumb2Assembler::Lsl and
-          // art::arm::Thumb2Assembler::AddConstant below), but it has
-          // not been saved by the previous call to
-          // art::SlowPathCode::SaveLiveRegisters, as it is a
-          // callee-save register --
-          // art::SlowPathCode::SaveLiveRegisters does not consider
-          // callee-save registers, as it has been designed with the
-          // assumption that callee-save registers are supposed to be
-          // handled by the called function.  So, as a callee-save
-          // register, `index_reg` _would_ eventually be saved onto
-          // the stack, but it would be too late: we would have
-          // changed its value earlier.  Therefore, we manually save
-          // it here into another freely available register,
-          // `free_reg`, chosen of course among the caller-save
-          // registers (as a callee-save `free_reg` register would
-          // exhibit the same problem).
-          //
-          // Note we could have requested a temporary register from
-          // the register allocator instead; but we prefer not to, as
-          // this is a slow path, and we know we can find a
-          // caller-save register that is available.
-          Register free_reg = FindAvailableCallerSaveRegister(codegen);
-          __ Mov(free_reg, index_reg);
-          index_reg = free_reg;
-          index = Location::RegisterLocation(index_reg);
-        } else {
-          // The initial register stored in `index_` has already been
-          // saved in the call to art::SlowPathCode::SaveLiveRegisters
-          // (as it is not a callee-save register), so we can freely
-          // use it.
-        }
-        // Shifting the index value contained in `index_reg` by the scale
-        // factor (2) cannot overflow in practice, as the runtime is
-        // unable to allocate object arrays with a size larger than
-        // 2^26 - 1 (that is, 2^28 - 4 bytes).
-        __ Lsl(index_reg, index_reg, TIMES_4);
-        static_assert(
-            sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
-            "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
-        __ AddConstant(index_reg, index_reg, offset_);
-      } else {
-        // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
-        // intrinsics, `index_` is not shifted by a scale factor of 2
-        // (as in the case of ArrayGet), as it is actually an offset
-        // to an object field within an object.
-        DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
-        DCHECK(instruction_->GetLocations()->Intrinsified());
-        DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
-               (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
-            << instruction_->AsInvoke()->GetIntrinsic();
-        DCHECK_EQ(offset_, 0U);
-        DCHECK(index_.IsRegisterPair());
-        // UnsafeGet's offset location is a register pair, the low
-        // part contains the correct offset.
-        index = index_.ToLow();
-      }
-    }
-
-    // We're moving two or three locations to locations that could
-    // overlap, so we need a parallel move resolver.
-    InvokeRuntimeCallingConvention calling_convention;
-    HParallelMove parallel_move(codegen->GetGraph()->GetArena());
-    parallel_move.AddMove(ref_,
-                          Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
-                          Primitive::kPrimNot,
-                          nullptr);
-    parallel_move.AddMove(obj_,
-                          Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
-                          Primitive::kPrimNot,
-                          nullptr);
-    if (index.IsValid()) {
-      parallel_move.AddMove(index,
-                            Location::RegisterLocation(calling_convention.GetRegisterAt(2)),
-                            Primitive::kPrimInt,
-                            nullptr);
-      codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
-    } else {
-      codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
-      __ LoadImmediate(calling_convention.GetRegisterAt(2), offset_);
-    }
-    arm_codegen->InvokeRuntime(kQuickReadBarrierSlow, instruction_, instruction_->GetDexPc(), this);
-    CheckEntrypointTypes<
-        kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
-    arm_codegen->Move32(out_, Location::RegisterLocation(R0));
-
-    RestoreLiveRegisters(codegen, locations);
-    __ b(GetExitLabel());
-  }
-
-  const char* GetDescription() const OVERRIDE { return "ReadBarrierForHeapReferenceSlowPathARM"; }
-
- private:
-  Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) {
-    size_t ref = static_cast<int>(ref_.AsRegister<Register>());
-    size_t obj = static_cast<int>(obj_.AsRegister<Register>());
-    for (size_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
-      if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) {
-        return static_cast<Register>(i);
-      }
-    }
-    // We shall never fail to find a free caller-save register, as
-    // there are more than two core caller-save registers on ARM
-    // (meaning it is possible to find one which is different from
-    // `ref` and `obj`).
-    DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u);
-    LOG(FATAL) << "Could not find a free caller-save register";
-    UNREACHABLE();
-  }
-
-  const Location out_;
-  const Location ref_;
-  const Location obj_;
-  const uint32_t offset_;
-  // An additional location containing an index to an array.
-  // Only used for HArrayGet and the UnsafeGetObject &
-  // UnsafeGetObjectVolatile intrinsics.
-  const Location index_;
-
-  DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARM);
-};
-
-// Slow path generating a read barrier for a GC root.
-class ReadBarrierForRootSlowPathARM : public SlowPathCodeARM {
- public:
-  ReadBarrierForRootSlowPathARM(HInstruction* instruction, Location out, Location root)
-      : SlowPathCodeARM(instruction), out_(out), root_(root) {
-    DCHECK(kEmitCompilerReadBarrier);
-  }
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    LocationSummary* locations = instruction_->GetLocations();
-    Register reg_out = out_.AsRegister<Register>();
-    DCHECK(locations->CanCall());
-    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
-    DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
-        << "Unexpected instruction in read barrier for GC root slow path: "
-        << instruction_->DebugName();
-
-    __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, locations);
-
-    InvokeRuntimeCallingConvention calling_convention;
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    arm_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), root_);
-    arm_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
-                               instruction_,
-                               instruction_->GetDexPc(),
-                               this);
-    CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
-    arm_codegen->Move32(out_, Location::RegisterLocation(R0));
-
-    RestoreLiveRegisters(codegen, locations);
-    __ b(GetExitLabel());
-  }
-
-  const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARM"; }
-
- private:
-  const Location out_;
-  const Location root_;
-
-  DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARM);
-};
-
-inline Condition ARMCondition(IfCondition cond) {
-  switch (cond) {
-    case kCondEQ: return EQ;
-    case kCondNE: return NE;
-    case kCondLT: return LT;
-    case kCondLE: return LE;
-    case kCondGT: return GT;
-    case kCondGE: return GE;
-    case kCondB:  return LO;
-    case kCondBE: return LS;
-    case kCondA:  return HI;
-    case kCondAE: return HS;
-  }
-  LOG(FATAL) << "Unreachable";
-  UNREACHABLE();
-}
-
-// Maps signed condition to unsigned condition.
-inline Condition ARMUnsignedCondition(IfCondition cond) {
-  switch (cond) {
-    case kCondEQ: return EQ;
-    case kCondNE: return NE;
-    // Signed to unsigned.
-    case kCondLT: return LO;
-    case kCondLE: return LS;
-    case kCondGT: return HI;
-    case kCondGE: return HS;
-    // Unsigned remain unchanged.
-    case kCondB:  return LO;
-    case kCondBE: return LS;
-    case kCondA:  return HI;
-    case kCondAE: return HS;
-  }
-  LOG(FATAL) << "Unreachable";
-  UNREACHABLE();
-}
-
-inline Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
-  // The ARM condition codes can express all the necessary branches, see the
-  // "Meaning (floating-point)" column in the table A8-1 of the ARMv7 reference manual.
-  // There is no dex instruction or HIR that would need the missing conditions
-  // "equal or unordered" or "not equal".
-  switch (cond) {
-    case kCondEQ: return EQ;
-    case kCondNE: return NE /* unordered */;
-    case kCondLT: return gt_bias ? CC : LT /* unordered */;
-    case kCondLE: return gt_bias ? LS : LE /* unordered */;
-    case kCondGT: return gt_bias ? HI /* unordered */ : GT;
-    case kCondGE: return gt_bias ? CS /* unordered */ : GE;
-    default:
-      LOG(FATAL) << "UNREACHABLE";
-      UNREACHABLE();
-  }
-}
-
-inline Shift ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
-  switch (op_kind) {
-    case HDataProcWithShifterOp::kASR: return ASR;
-    case HDataProcWithShifterOp::kLSL: return LSL;
-    case HDataProcWithShifterOp::kLSR: return LSR;
-    default:
-      LOG(FATAL) << "Unexpected op kind " << op_kind;
-      UNREACHABLE();
-  }
-}
-
-static void GenerateDataProcInstruction(HInstruction::InstructionKind kind,
-                                        Register out,
-                                        Register first,
-                                        const ShifterOperand& second,
-                                        CodeGeneratorARM* codegen) {
-  if (second.IsImmediate() && second.GetImmediate() == 0) {
-    const ShifterOperand in = kind == HInstruction::kAnd
-        ? ShifterOperand(0)
-        : ShifterOperand(first);
-
-    __ mov(out, in);
-  } else {
-    switch (kind) {
-      case HInstruction::kAdd:
-        __ add(out, first, second);
-        break;
-      case HInstruction::kAnd:
-        __ and_(out, first, second);
-        break;
-      case HInstruction::kOr:
-        __ orr(out, first, second);
-        break;
-      case HInstruction::kSub:
-        __ sub(out, first, second);
-        break;
-      case HInstruction::kXor:
-        __ eor(out, first, second);
-        break;
-      default:
-        LOG(FATAL) << "Unexpected instruction kind: " << kind;
-        UNREACHABLE();
-    }
-  }
-}
-
-static void GenerateDataProc(HInstruction::InstructionKind kind,
-                             const Location& out,
-                             const Location& first,
-                             const ShifterOperand& second_lo,
-                             const ShifterOperand& second_hi,
-                             CodeGeneratorARM* codegen) {
-  const Register first_hi = first.AsRegisterPairHigh<Register>();
-  const Register first_lo = first.AsRegisterPairLow<Register>();
-  const Register out_hi = out.AsRegisterPairHigh<Register>();
-  const Register out_lo = out.AsRegisterPairLow<Register>();
-
-  if (kind == HInstruction::kAdd) {
-    __ adds(out_lo, first_lo, second_lo);
-    __ adc(out_hi, first_hi, second_hi);
-  } else if (kind == HInstruction::kSub) {
-    __ subs(out_lo, first_lo, second_lo);
-    __ sbc(out_hi, first_hi, second_hi);
-  } else {
-    GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen);
-    GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen);
-  }
-}
-
-static ShifterOperand GetShifterOperand(Register rm, Shift shift, uint32_t shift_imm) {
-  return shift_imm == 0 ? ShifterOperand(rm) : ShifterOperand(rm, shift, shift_imm);
-}
-
-static void GenerateLongDataProc(HDataProcWithShifterOp* instruction, CodeGeneratorARM* codegen) {
-  DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong);
-  DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind()));
-
-  const LocationSummary* const locations = instruction->GetLocations();
-  const uint32_t shift_value = instruction->GetShiftAmount();
-  const HInstruction::InstructionKind kind = instruction->GetInstrKind();
-  const Location first = locations->InAt(0);
-  const Location second = locations->InAt(1);
-  const Location out = locations->Out();
-  const Register first_hi = first.AsRegisterPairHigh<Register>();
-  const Register first_lo = first.AsRegisterPairLow<Register>();
-  const Register out_hi = out.AsRegisterPairHigh<Register>();
-  const Register out_lo = out.AsRegisterPairLow<Register>();
-  const Register second_hi = second.AsRegisterPairHigh<Register>();
-  const Register second_lo = second.AsRegisterPairLow<Register>();
-  const Shift shift = ShiftFromOpKind(instruction->GetOpKind());
-
-  if (shift_value >= 32) {
-    if (shift == LSL) {
-      GenerateDataProcInstruction(kind,
-                                  out_hi,
-                                  first_hi,
-                                  ShifterOperand(second_lo, LSL, shift_value - 32),
-                                  codegen);
-      GenerateDataProcInstruction(kind,
-                                  out_lo,
-                                  first_lo,
-                                  ShifterOperand(0),
-                                  codegen);
-    } else if (shift == ASR) {
-      GenerateDataProc(kind,
-                       out,
-                       first,
-                       GetShifterOperand(second_hi, ASR, shift_value - 32),
-                       ShifterOperand(second_hi, ASR, 31),
-                       codegen);
-    } else {
-      DCHECK_EQ(shift, LSR);
-      GenerateDataProc(kind,
-                       out,
-                       first,
-                       GetShifterOperand(second_hi, LSR, shift_value - 32),
-                       ShifterOperand(0),
-                       codegen);
-    }
-  } else {
-    DCHECK_GT(shift_value, 1U);
-    DCHECK_LT(shift_value, 32U);
-
-    if (shift == LSL) {
-      // We are not doing this for HInstruction::kAdd because the output will require
-      // Location::kOutputOverlap; not applicable to other cases.
-      if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
-        GenerateDataProcInstruction(kind,
-                                    out_hi,
-                                    first_hi,
-                                    ShifterOperand(second_hi, LSL, shift_value),
-                                    codegen);
-        GenerateDataProcInstruction(kind,
-                                    out_hi,
-                                    out_hi,
-                                    ShifterOperand(second_lo, LSR, 32 - shift_value),
-                                    codegen);
-        GenerateDataProcInstruction(kind,
-                                    out_lo,
-                                    first_lo,
-                                    ShifterOperand(second_lo, LSL, shift_value),
-                                    codegen);
-      } else {
-        __ Lsl(IP, second_hi, shift_value);
-        __ orr(IP, IP, ShifterOperand(second_lo, LSR, 32 - shift_value));
-        GenerateDataProc(kind,
-                         out,
-                         first,
-                         ShifterOperand(second_lo, LSL, shift_value),
-                         ShifterOperand(IP),
-                         codegen);
-      }
-    } else {
-      DCHECK(shift == ASR || shift == LSR);
-
-      // We are not doing this for HInstruction::kAdd because the output will require
-      // Location::kOutputOverlap; not applicable to other cases.
-      if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
-        GenerateDataProcInstruction(kind,
-                                    out_lo,
-                                    first_lo,
-                                    ShifterOperand(second_lo, LSR, shift_value),
-                                    codegen);
-        GenerateDataProcInstruction(kind,
-                                    out_lo,
-                                    out_lo,
-                                    ShifterOperand(second_hi, LSL, 32 - shift_value),
-                                    codegen);
-        GenerateDataProcInstruction(kind,
-                                    out_hi,
-                                    first_hi,
-                                    ShifterOperand(second_hi, shift, shift_value),
-                                    codegen);
-      } else {
-        __ Lsr(IP, second_lo, shift_value);
-        __ orr(IP, IP, ShifterOperand(second_hi, LSL, 32 - shift_value));
-        GenerateDataProc(kind,
-                         out,
-                         first,
-                         ShifterOperand(IP),
-                         ShifterOperand(second_hi, shift, shift_value),
-                         codegen);
-      }
-    }
-  }
-}
-
-static void GenerateVcmp(HInstruction* instruction, CodeGeneratorARM* codegen) {
-  Primitive::Type type = instruction->InputAt(0)->GetType();
-  Location lhs_loc = instruction->GetLocations()->InAt(0);
-  Location rhs_loc = instruction->GetLocations()->InAt(1);
-  if (rhs_loc.IsConstant()) {
-    // 0.0 is the only immediate that can be encoded directly in
-    // a VCMP instruction.
-    //
-    // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
-    // specify that in a floating-point comparison, positive zero
-    // and negative zero are considered equal, so we can use the
-    // literal 0.0 for both cases here.
-    //
-    // Note however that some methods (Float.equal, Float.compare,
-    // Float.compareTo, Double.equal, Double.compare,
-    // Double.compareTo, Math.max, Math.min, StrictMath.max,
-    // StrictMath.min) consider 0.0 to be (strictly) greater than
-    // -0.0. So if we ever translate calls to these methods into a
-    // HCompare instruction, we must handle the -0.0 case with
-    // care here.
-    DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
-    if (type == Primitive::kPrimFloat) {
-      __ vcmpsz(lhs_loc.AsFpuRegister<SRegister>());
-    } else {
-      DCHECK_EQ(type, Primitive::kPrimDouble);
-      __ vcmpdz(FromLowSToD(lhs_loc.AsFpuRegisterPairLow<SRegister>()));
-    }
-  } else {
-    if (type == Primitive::kPrimFloat) {
-      __ vcmps(lhs_loc.AsFpuRegister<SRegister>(), rhs_loc.AsFpuRegister<SRegister>());
-    } else {
-      DCHECK_EQ(type, Primitive::kPrimDouble);
-      __ vcmpd(FromLowSToD(lhs_loc.AsFpuRegisterPairLow<SRegister>()),
-               FromLowSToD(rhs_loc.AsFpuRegisterPairLow<SRegister>()));
-    }
-  }
-}
-
-static int64_t AdjustConstantForCondition(int64_t value,
-                                          IfCondition* condition,
-                                          IfCondition* opposite) {
-  if (value == 1) {
-    if (*condition == kCondB) {
-      value = 0;
-      *condition = kCondEQ;
-      *opposite = kCondNE;
-    } else if (*condition == kCondAE) {
-      value = 0;
-      *condition = kCondNE;
-      *opposite = kCondEQ;
-    }
-  } else if (value == -1) {
-    if (*condition == kCondGT) {
-      value = 0;
-      *condition = kCondGE;
-      *opposite = kCondLT;
-    } else if (*condition == kCondLE) {
-      value = 0;
-      *condition = kCondLT;
-      *opposite = kCondGE;
-    }
-  }
-
-  return value;
-}
-
-static std::pair<Condition, Condition> GenerateLongTestConstant(HCondition* condition,
-                                                                bool invert,
-                                                                CodeGeneratorARM* codegen) {
-  DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong);
-
-  const LocationSummary* const locations = condition->GetLocations();
-  IfCondition cond = condition->GetCondition();
-  IfCondition opposite = condition->GetOppositeCondition();
-
-  if (invert) {
-    std::swap(cond, opposite);
-  }
-
-  std::pair<Condition, Condition> ret(EQ, NE);
-  const Location left = locations->InAt(0);
-  const Location right = locations->InAt(1);
-
-  DCHECK(right.IsConstant());
-
-  const Register left_high = left.AsRegisterPairHigh<Register>();
-  const Register left_low = left.AsRegisterPairLow<Register>();
-  int64_t value = AdjustConstantForCondition(right.GetConstant()->AsLongConstant()->GetValue(),
-                                             &cond,
-                                             &opposite);
-
-  // Comparisons against 0 are common enough to deserve special attention.
-  if (value == 0) {
-    switch (cond) {
-      case kCondNE:
-      // x > 0 iff x != 0 when the comparison is unsigned.
-      case kCondA:
-        ret = std::make_pair(NE, EQ);
-        FALLTHROUGH_INTENDED;
-      case kCondEQ:
-      // x <= 0 iff x == 0 when the comparison is unsigned.
-      case kCondBE:
-        __ orrs(IP, left_low, ShifterOperand(left_high));
-        return ret;
-      case kCondLT:
-      case kCondGE:
-        __ cmp(left_high, ShifterOperand(0));
-        return std::make_pair(ARMCondition(cond), ARMCondition(opposite));
-      // Trivially true or false.
-      case kCondB:
-        ret = std::make_pair(NE, EQ);
-        FALLTHROUGH_INTENDED;
-      case kCondAE:
-        __ cmp(left_low, ShifterOperand(left_low));
-        return ret;
-      default:
-        break;
-    }
-  }
-
-  switch (cond) {
-    case kCondEQ:
-    case kCondNE:
-    case kCondB:
-    case kCondBE:
-    case kCondA:
-    case kCondAE:
-      __ CmpConstant(left_high, High32Bits(value));
-      __ it(EQ);
-      __ cmp(left_low, ShifterOperand(Low32Bits(value)), EQ);
-      ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
-      break;
-    case kCondLE:
-    case kCondGT:
-      // Trivially true or false.
-      if (value == std::numeric_limits<int64_t>::max()) {
-        __ cmp(left_low, ShifterOperand(left_low));
-        ret = cond == kCondLE ? std::make_pair(EQ, NE) : std::make_pair(NE, EQ);
-        break;
-      }
-
-      if (cond == kCondLE) {
-        DCHECK_EQ(opposite, kCondGT);
-        cond = kCondLT;
-        opposite = kCondGE;
-      } else {
-        DCHECK_EQ(cond, kCondGT);
-        DCHECK_EQ(opposite, kCondLE);
-        cond = kCondGE;
-        opposite = kCondLT;
-      }
-
-      value++;
-      FALLTHROUGH_INTENDED;
-    case kCondGE:
-    case kCondLT:
-      __ CmpConstant(left_low, Low32Bits(value));
-      __ sbcs(IP, left_high, ShifterOperand(High32Bits(value)));
-      ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
-      break;
-    default:
-      LOG(FATAL) << "Unreachable";
-      UNREACHABLE();
-  }
-
-  return ret;
-}
-
-static std::pair<Condition, Condition> GenerateLongTest(HCondition* condition,
-                                                        bool invert,
-                                                        CodeGeneratorARM* codegen) {
-  DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong);
-
-  const LocationSummary* const locations = condition->GetLocations();
-  IfCondition cond = condition->GetCondition();
-  IfCondition opposite = condition->GetOppositeCondition();
-
-  if (invert) {
-    std::swap(cond, opposite);
-  }
-
-  std::pair<Condition, Condition> ret;
-  Location left = locations->InAt(0);
-  Location right = locations->InAt(1);
-
-  DCHECK(right.IsRegisterPair());
-
-  switch (cond) {
-    case kCondEQ:
-    case kCondNE:
-    case kCondB:
-    case kCondBE:
-    case kCondA:
-    case kCondAE:
-      __ cmp(left.AsRegisterPairHigh<Register>(),
-             ShifterOperand(right.AsRegisterPairHigh<Register>()));
-      __ it(EQ);
-      __ cmp(left.AsRegisterPairLow<Register>(),
-             ShifterOperand(right.AsRegisterPairLow<Register>()),
-             EQ);
-      ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
-      break;
-    case kCondLE:
-    case kCondGT:
-      if (cond == kCondLE) {
-        DCHECK_EQ(opposite, kCondGT);
-        cond = kCondGE;
-        opposite = kCondLT;
-      } else {
-        DCHECK_EQ(cond, kCondGT);
-        DCHECK_EQ(opposite, kCondLE);
-        cond = kCondLT;
-        opposite = kCondGE;
-      }
-
-      std::swap(left, right);
-      FALLTHROUGH_INTENDED;
-    case kCondGE:
-    case kCondLT:
-      __ cmp(left.AsRegisterPairLow<Register>(),
-             ShifterOperand(right.AsRegisterPairLow<Register>()));
-      __ sbcs(IP,
-              left.AsRegisterPairHigh<Register>(),
-              ShifterOperand(right.AsRegisterPairHigh<Register>()));
-      ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
-      break;
-    default:
-      LOG(FATAL) << "Unreachable";
-      UNREACHABLE();
-  }
-
-  return ret;
-}
-
-static std::pair<Condition, Condition> GenerateTest(HCondition* condition,
-                                                    bool invert,
-                                                    CodeGeneratorARM* codegen) {
-  const LocationSummary* const locations = condition->GetLocations();
-  const Primitive::Type type = condition->GetLeft()->GetType();
-  IfCondition cond = condition->GetCondition();
-  IfCondition opposite = condition->GetOppositeCondition();
-  std::pair<Condition, Condition> ret;
-  const Location right = locations->InAt(1);
-
-  if (invert) {
-    std::swap(cond, opposite);
-  }
-
-  if (type == Primitive::kPrimLong) {
-    ret = locations->InAt(1).IsConstant()
-        ? GenerateLongTestConstant(condition, invert, codegen)
-        : GenerateLongTest(condition, invert, codegen);
-  } else if (Primitive::IsFloatingPointType(type)) {
-    GenerateVcmp(condition, codegen);
-    __ vmstat();
-    ret = std::make_pair(ARMFPCondition(cond, condition->IsGtBias()),
-                         ARMFPCondition(opposite, condition->IsGtBias()));
-  } else {
-    DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
-
-    const Register left = locations->InAt(0).AsRegister<Register>();
-
-    if (right.IsRegister()) {
-      __ cmp(left, ShifterOperand(right.AsRegister<Register>()));
-    } else {
-      DCHECK(right.IsConstant());
-      __ CmpConstant(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
-    }
-
-    ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
-  }
-
-  return ret;
-}
-
-static bool CanGenerateTest(HCondition* condition, ArmAssembler* assembler) {
-  if (condition->GetLeft()->GetType() == Primitive::kPrimLong) {
-    const LocationSummary* const locations = condition->GetLocations();
-
-    if (locations->InAt(1).IsConstant()) {
-      IfCondition c = condition->GetCondition();
-      IfCondition opposite = condition->GetOppositeCondition();
-      const int64_t value = AdjustConstantForCondition(
-          Int64FromConstant(locations->InAt(1).GetConstant()),
-          &c,
-          &opposite);
-      ShifterOperand so;
-
-      if (c < kCondLT || c > kCondGE) {
-        // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
-        // we check that the least significant half of the first input to be compared
-        // is in a low register (the other half is read outside an IT block), and
-        // the constant fits in an 8-bit unsigned integer, so that a 16-bit CMP
-        // encoding can be used; 0 is always handled, no matter what registers are
-        // used by the first input.
-        if (value != 0 &&
-            (!ArmAssembler::IsLowRegister(locations->InAt(0).AsRegisterPairLow<Register>()) ||
-             !IsUint<8>(Low32Bits(value)))) {
-          return false;
-        }
-      } else if (c == kCondLE || c == kCondGT) {
-        if (value < std::numeric_limits<int64_t>::max() &&
-            !assembler->ShifterOperandCanHold(kNoRegister,
-                                              kNoRegister,
-                                              SBC,
-                                              High32Bits(value + 1),
-                                              kCcSet,
-                                              &so)) {
-          return false;
-        }
-      } else if (!assembler->ShifterOperandCanHold(kNoRegister,
-                                                   kNoRegister,
-                                                   SBC,
-                                                   High32Bits(value),
-                                                   kCcSet,
-                                                   &so)) {
-        return false;
-      }
-    }
-  }
-
-  return true;
-}
-
-static void GenerateConditionGeneric(HCondition* cond, CodeGeneratorARM* codegen) {
-  DCHECK(CanGenerateTest(cond, codegen->GetAssembler()));
-
-  const Register out = cond->GetLocations()->Out().AsRegister<Register>();
-  const auto condition = GenerateTest(cond, false, codegen);
-
-  __ mov(out, ShifterOperand(0), AL, kCcKeep);
-
-  if (ArmAssembler::IsLowRegister(out)) {
-    __ it(condition.first);
-    __ mov(out, ShifterOperand(1), condition.first);
-  } else {
-    Label done_label;
-    Label* const final_label = codegen->GetFinalLabel(cond, &done_label);
-
-    __ b(final_label, condition.second);
-    __ LoadImmediate(out, 1);
-
-    if (done_label.IsLinked()) {
-      __ Bind(&done_label);
-    }
-  }
-}
-
-static void GenerateEqualLong(HCondition* cond, CodeGeneratorARM* codegen) {
-  DCHECK_EQ(cond->GetLeft()->GetType(), Primitive::kPrimLong);
-
-  const LocationSummary* const locations = cond->GetLocations();
-  IfCondition condition = cond->GetCondition();
-  const Register out = locations->Out().AsRegister<Register>();
-  const Location left = locations->InAt(0);
-  const Location right = locations->InAt(1);
-  Register left_high = left.AsRegisterPairHigh<Register>();
-  Register left_low = left.AsRegisterPairLow<Register>();
-
-  if (right.IsConstant()) {
-    IfCondition opposite = cond->GetOppositeCondition();
-    const int64_t value = AdjustConstantForCondition(Int64FromConstant(right.GetConstant()),
-                                                     &condition,
-                                                     &opposite);
-    int32_t value_high = -High32Bits(value);
-    int32_t value_low = -Low32Bits(value);
-
-    // The output uses Location::kNoOutputOverlap.
-    if (out == left_high) {
-      std::swap(left_low, left_high);
-      std::swap(value_low, value_high);
-    }
-
-    __ AddConstant(out, left_low, value_low);
-    __ AddConstant(IP, left_high, value_high);
-  } else {
-    DCHECK(right.IsRegisterPair());
-    __ sub(IP, left_high, ShifterOperand(right.AsRegisterPairHigh<Register>()));
-    __ sub(out, left_low, ShifterOperand(right.AsRegisterPairLow<Register>()));
-  }
-
-  // Need to check after calling AdjustConstantForCondition().
-  DCHECK(condition == kCondEQ || condition == kCondNE) << condition;
-
-  if (condition == kCondNE && ArmAssembler::IsLowRegister(out)) {
-    __ orrs(out, out, ShifterOperand(IP));
-    __ it(NE);
-    __ mov(out, ShifterOperand(1), NE);
-  } else {
-    __ orr(out, out, ShifterOperand(IP));
-    codegen->GenerateConditionWithZero(condition, out, out, IP);
-  }
-}
-
-static void GenerateLongComparesAndJumps(HCondition* cond,
-                                         Label* true_label,
-                                         Label* false_label,
-                                         CodeGeneratorARM* codegen) {
-  LocationSummary* locations = cond->GetLocations();
-  Location left = locations->InAt(0);
-  Location right = locations->InAt(1);
-  IfCondition if_cond = cond->GetCondition();
-
-  Register left_high = left.AsRegisterPairHigh<Register>();
-  Register left_low = left.AsRegisterPairLow<Register>();
-  IfCondition true_high_cond = if_cond;
-  IfCondition false_high_cond = cond->GetOppositeCondition();
-  Condition final_condition = ARMUnsignedCondition(if_cond);  // unsigned on lower part
-
-  // Set the conditions for the test, remembering that == needs to be
-  // decided using the low words.
-  switch (if_cond) {
-    case kCondEQ:
-    case kCondNE:
-      // Nothing to do.
-      break;
-    case kCondLT:
-      false_high_cond = kCondGT;
-      break;
-    case kCondLE:
-      true_high_cond = kCondLT;
-      break;
-    case kCondGT:
-      false_high_cond = kCondLT;
-      break;
-    case kCondGE:
-      true_high_cond = kCondGT;
-      break;
-    case kCondB:
-      false_high_cond = kCondA;
-      break;
-    case kCondBE:
-      true_high_cond = kCondB;
-      break;
-    case kCondA:
-      false_high_cond = kCondB;
-      break;
-    case kCondAE:
-      true_high_cond = kCondA;
-      break;
-  }
-  if (right.IsConstant()) {
-    int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
-    int32_t val_low = Low32Bits(value);
-    int32_t val_high = High32Bits(value);
-
-    __ CmpConstant(left_high, val_high);
-    if (if_cond == kCondNE) {
-      __ b(true_label, ARMCondition(true_high_cond));
-    } else if (if_cond == kCondEQ) {
-      __ b(false_label, ARMCondition(false_high_cond));
-    } else {
-      __ b(true_label, ARMCondition(true_high_cond));
-      __ b(false_label, ARMCondition(false_high_cond));
-    }
-    // Must be equal high, so compare the lows.
-    __ CmpConstant(left_low, val_low);
-  } else {
-    Register right_high = right.AsRegisterPairHigh<Register>();
-    Register right_low = right.AsRegisterPairLow<Register>();
-
-    __ cmp(left_high, ShifterOperand(right_high));
-    if (if_cond == kCondNE) {
-      __ b(true_label, ARMCondition(true_high_cond));
-    } else if (if_cond == kCondEQ) {
-      __ b(false_label, ARMCondition(false_high_cond));
-    } else {
-      __ b(true_label, ARMCondition(true_high_cond));
-      __ b(false_label, ARMCondition(false_high_cond));
-    }
-    // Must be equal high, so compare the lows.
-    __ cmp(left_low, ShifterOperand(right_low));
-  }
-  // The last comparison might be unsigned.
-  // TODO: optimize cases where this is always true/false
-  __ b(true_label, final_condition);
-}
-
-static void GenerateConditionLong(HCondition* cond, CodeGeneratorARM* codegen) {
-  DCHECK_EQ(cond->GetLeft()->GetType(), Primitive::kPrimLong);
-
-  const LocationSummary* const locations = cond->GetLocations();
-  IfCondition condition = cond->GetCondition();
-  const Register out = locations->Out().AsRegister<Register>();
-  const Location left = locations->InAt(0);
-  const Location right = locations->InAt(1);
-
-  if (right.IsConstant()) {
-    IfCondition opposite = cond->GetOppositeCondition();
-
-    // Comparisons against 0 are common enough to deserve special attention.
-    if (AdjustConstantForCondition(Int64FromConstant(right.GetConstant()),
-                                   &condition,
-                                   &opposite) == 0) {
-      switch (condition) {
-        case kCondNE:
-        case kCondA:
-          if (ArmAssembler::IsLowRegister(out)) {
-            // We only care if both input registers are 0 or not.
-            __ orrs(out,
-                    left.AsRegisterPairLow<Register>(),
-                    ShifterOperand(left.AsRegisterPairHigh<Register>()));
-            __ it(NE);
-            __ mov(out, ShifterOperand(1), NE);
-            return;
-          }
-
-          FALLTHROUGH_INTENDED;
-        case kCondEQ:
-        case kCondBE:
-          // We only care if both input registers are 0 or not.
-          __ orr(out,
-                 left.AsRegisterPairLow<Register>(),
-                 ShifterOperand(left.AsRegisterPairHigh<Register>()));
-          codegen->GenerateConditionWithZero(condition, out, out);
-          return;
-        case kCondLT:
-        case kCondGE:
-          // We only care about the sign bit.
-          FALLTHROUGH_INTENDED;
-        case kCondAE:
-        case kCondB:
-          codegen->GenerateConditionWithZero(condition, out, left.AsRegisterPairHigh<Register>());
-          return;
-        case kCondLE:
-        case kCondGT:
-        default:
-          break;
-      }
-    }
-  }
-
-  if ((condition == kCondEQ || condition == kCondNE) &&
-      // If `out` is a low register, then the GenerateConditionGeneric()
-      // function generates a shorter code sequence that is still branchless.
-      (!ArmAssembler::IsLowRegister(out) || !CanGenerateTest(cond, codegen->GetAssembler()))) {
-    GenerateEqualLong(cond, codegen);
-    return;
-  }
-
-  if (CanGenerateTest(cond, codegen->GetAssembler())) {
-    GenerateConditionGeneric(cond, codegen);
-    return;
-  }
-
-  // Convert the jumps into the result.
-  Label done_label;
-  Label* const final_label = codegen->GetFinalLabel(cond, &done_label);
-  Label true_label, false_label;
-
-  GenerateLongComparesAndJumps(cond, &true_label, &false_label, codegen);
-
-  // False case: result = 0.
-  __ Bind(&false_label);
-  __ mov(out, ShifterOperand(0));
-  __ b(final_label);
-
-  // True case: result = 1.
-  __ Bind(&true_label);
-  __ mov(out, ShifterOperand(1));
-
-  if (done_label.IsLinked()) {
-    __ Bind(&done_label);
-  }
-}
-
-static void GenerateConditionIntegralOrNonPrimitive(HCondition* cond, CodeGeneratorARM* codegen) {
-  const Primitive::Type type = cond->GetLeft()->GetType();
-
-  DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
-
-  if (type == Primitive::kPrimLong) {
-    GenerateConditionLong(cond, codegen);
-    return;
-  }
-
-  const LocationSummary* const locations = cond->GetLocations();
-  IfCondition condition = cond->GetCondition();
-  Register in = locations->InAt(0).AsRegister<Register>();
-  const Register out = locations->Out().AsRegister<Register>();
-  const Location right = cond->GetLocations()->InAt(1);
-  int64_t value;
-
-  if (right.IsConstant()) {
-    IfCondition opposite = cond->GetOppositeCondition();
-
-    value = AdjustConstantForCondition(Int64FromConstant(right.GetConstant()),
-                                       &condition,
-                                       &opposite);
-
-    // Comparisons against 0 are common enough to deserve special attention.
-    if (value == 0) {
-      switch (condition) {
-        case kCondNE:
-        case kCondA:
-          if (ArmAssembler::IsLowRegister(out) && out == in) {
-            __ cmp(out, ShifterOperand(0));
-            __ it(NE);
-            __ mov(out, ShifterOperand(1), NE);
-            return;
-          }
-
-          FALLTHROUGH_INTENDED;
-        case kCondEQ:
-        case kCondBE:
-        case kCondLT:
-        case kCondGE:
-        case kCondAE:
-        case kCondB:
-          codegen->GenerateConditionWithZero(condition, out, in);
-          return;
-        case kCondLE:
-        case kCondGT:
-        default:
-          break;
-      }
-    }
-  }
-
-  if (condition == kCondEQ || condition == kCondNE) {
-    ShifterOperand operand;
-
-    if (right.IsConstant()) {
-      operand = ShifterOperand(value);
-    } else if (out == right.AsRegister<Register>()) {
-      // Avoid 32-bit instructions if possible.
-      operand = ShifterOperand(in);
-      in = right.AsRegister<Register>();
-    } else {
-      operand = ShifterOperand(right.AsRegister<Register>());
-    }
-
-    if (condition == kCondNE && ArmAssembler::IsLowRegister(out)) {
-      __ subs(out, in, operand);
-      __ it(NE);
-      __ mov(out, ShifterOperand(1), NE);
-    } else {
-      __ sub(out, in, operand);
-      codegen->GenerateConditionWithZero(condition, out, out);
-    }
-
-    return;
-  }
-
-  GenerateConditionGeneric(cond, codegen);
-}
-
-static bool CanEncodeConstantAs8BitImmediate(HConstant* constant) {
-  const Primitive::Type type = constant->GetType();
-  bool ret = false;
-
-  DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
-
-  if (type == Primitive::kPrimLong) {
-    const uint64_t value = constant->AsLongConstant()->GetValueAsUint64();
-
-    ret = IsUint<8>(Low32Bits(value)) && IsUint<8>(High32Bits(value));
-  } else {
-    ret = IsUint<8>(CodeGenerator::GetInt32ValueOf(constant));
-  }
-
-  return ret;
-}
-
-static Location Arm8BitEncodableConstantOrRegister(HInstruction* constant) {
-  DCHECK(!Primitive::IsFloatingPointType(constant->GetType()));
-
-  if (constant->IsConstant() && CanEncodeConstantAs8BitImmediate(constant->AsConstant())) {
-    return Location::ConstantLocation(constant->AsConstant());
-  }
-
-  return Location::RequiresRegister();
-}
-
-static bool CanGenerateConditionalMove(const Location& out, const Location& src) {
-  // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
-  // we check that we are not dealing with floating-point output (there is no
-  // 16-bit VMOV encoding).
-  if (!out.IsRegister() && !out.IsRegisterPair()) {
-    return false;
-  }
-
-  // For constants, we also check that the output is in one or two low registers,
-  // and that the constants fit in an 8-bit unsigned integer, so that a 16-bit
-  // MOV encoding can be used.
-  if (src.IsConstant()) {
-    if (!CanEncodeConstantAs8BitImmediate(src.GetConstant())) {
-      return false;
-    }
-
-    if (out.IsRegister()) {
-      if (!ArmAssembler::IsLowRegister(out.AsRegister<Register>())) {
-        return false;
-      }
-    } else {
-      DCHECK(out.IsRegisterPair());
-
-      if (!ArmAssembler::IsLowRegister(out.AsRegisterPairHigh<Register>())) {
-        return false;
-      }
-    }
-  }
-
-  return true;
-}
-
-#undef __
-// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
-#define __ down_cast<ArmAssembler*>(GetAssembler())->  // NOLINT
-
-Label* CodeGeneratorARM::GetFinalLabel(HInstruction* instruction, Label* final_label) {
-  DCHECK(!instruction->IsControlFlow() && !instruction->IsSuspendCheck());
-  DCHECK(!instruction->IsInvoke() || !instruction->GetLocations()->CanCall());
-
-  const HBasicBlock* const block = instruction->GetBlock();
-  const HLoopInformation* const info = block->GetLoopInformation();
-  HInstruction* const next = instruction->GetNext();
-
-  // Avoid a branch to a branch.
-  if (next->IsGoto() && (info == nullptr ||
-                         !info->IsBackEdge(*block) ||
-                         !info->HasSuspendCheck())) {
-    final_label = GetLabelOf(next->AsGoto()->GetSuccessor());
-  }
-
-  return final_label;
-}
-
-void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const {
-  stream << Register(reg);
-}
-
-void CodeGeneratorARM::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
-  stream << SRegister(reg);
-}
-
-size_t CodeGeneratorARM::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
-  __ StoreToOffset(kStoreWord, static_cast<Register>(reg_id), SP, stack_index);
-  return kArmWordSize;
-}
-
-size_t CodeGeneratorARM::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
-  __ LoadFromOffset(kLoadWord, static_cast<Register>(reg_id), SP, stack_index);
-  return kArmWordSize;
-}
-
-size_t CodeGeneratorARM::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
-  __ StoreSToOffset(static_cast<SRegister>(reg_id), SP, stack_index);
-  return kArmWordSize;
-}
-
-size_t CodeGeneratorARM::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
-  __ LoadSFromOffset(static_cast<SRegister>(reg_id), SP, stack_index);
-  return kArmWordSize;
-}
-
-CodeGeneratorARM::CodeGeneratorARM(HGraph* graph,
-                                   const ArmInstructionSetFeatures& isa_features,
-                                   const CompilerOptions& compiler_options,
-                                   OptimizingCompilerStats* stats)
-    : CodeGenerator(graph,
-                    kNumberOfCoreRegisters,
-                    kNumberOfSRegisters,
-                    kNumberOfRegisterPairs,
-                    ComputeRegisterMask(reinterpret_cast<const int*>(kCoreCalleeSaves),
-                                        arraysize(kCoreCalleeSaves)),
-                    ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves),
-                                        arraysize(kFpuCalleeSaves)),
-                    compiler_options,
-                    stats),
-      block_labels_(nullptr),
-      location_builder_(graph, this),
-      instruction_visitor_(graph, this),
-      move_resolver_(graph->GetArena(), this),
-      assembler_(graph->GetArena()),
-      isa_features_(isa_features),
-      uint32_literals_(std::less<uint32_t>(),
-                       graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      pc_relative_method_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      method_bss_entry_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      type_bss_entry_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-      baker_read_barrier_patches_(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));
-}
-
-void CodeGeneratorARM::Finalize(CodeAllocator* allocator) {
-  // Ensure that we fix up branches and literal loads and emit the literal pool.
-  __ FinalizeCode();
-
-  // Adjust native pc offsets in stack maps.
-  for (size_t i = 0, num = stack_map_stream_.GetNumberOfStackMaps(); i != num; ++i) {
-    uint32_t old_position =
-        stack_map_stream_.GetStackMap(i).native_pc_code_offset.Uint32Value(kThumb2);
-    uint32_t new_position = __ GetAdjustedPosition(old_position);
-    stack_map_stream_.SetStackMapNativePcOffset(i, new_position);
-  }
-  // Adjust pc offsets for the disassembly information.
-  if (disasm_info_ != nullptr) {
-    GeneratedCodeInterval* frame_entry_interval = disasm_info_->GetFrameEntryInterval();
-    frame_entry_interval->start = __ GetAdjustedPosition(frame_entry_interval->start);
-    frame_entry_interval->end = __ GetAdjustedPosition(frame_entry_interval->end);
-    for (auto& it : *disasm_info_->GetInstructionIntervals()) {
-      it.second.start = __ GetAdjustedPosition(it.second.start);
-      it.second.end = __ GetAdjustedPosition(it.second.end);
-    }
-    for (auto& it : *disasm_info_->GetSlowPathIntervals()) {
-      it.code_interval.start = __ GetAdjustedPosition(it.code_interval.start);
-      it.code_interval.end = __ GetAdjustedPosition(it.code_interval.end);
-    }
-  }
-
-  CodeGenerator::Finalize(allocator);
-}
-
-void CodeGeneratorARM::SetupBlockedRegisters() const {
-  // Stack register, LR and PC are always reserved.
-  blocked_core_registers_[SP] = true;
-  blocked_core_registers_[LR] = true;
-  blocked_core_registers_[PC] = true;
-
-  // Reserve thread register.
-  blocked_core_registers_[TR] = true;
-
-  // Reserve temp register.
-  blocked_core_registers_[IP] = true;
-
-  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
-    // now, just block them.
-    for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
-      blocked_fpu_registers_[kFpuCalleeSaves[i]] = true;
-    }
-  }
-}
-
-InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen)
-      : InstructionCodeGenerator(graph, codegen),
-        assembler_(codegen->GetAssembler()),
-        codegen_(codegen) {}
-
-void CodeGeneratorARM::ComputeSpillMask() {
-  core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
-  DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
-  // There is no easy instruction to restore just the PC on thumb2. We spill and
-  // restore another arbitrary register.
-  core_spill_mask_ |= (1 << kCoreAlwaysSpillRegister);
-  fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
-  // We use vpush and vpop for saving and restoring floating point registers, which take
-  // a SRegister and the number of registers to save/restore after that SRegister. We
-  // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
-  // but in the range.
-  if (fpu_spill_mask_ != 0) {
-    uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
-    uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
-    for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
-      fpu_spill_mask_ |= (1 << i);
-    }
-  }
-}
-
-static dwarf::Reg DWARFReg(Register reg) {
-  return dwarf::Reg::ArmCore(static_cast<int>(reg));
-}
-
-static dwarf::Reg DWARFReg(SRegister reg) {
-  return dwarf::Reg::ArmFp(static_cast<int>(reg));
-}
-
-void CodeGeneratorARM::GenerateFrameEntry() {
-  bool skip_overflow_check =
-      IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
-  DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
-  __ Bind(&frame_entry_label_);
-
-  if (HasEmptyFrame()) {
-    return;
-  }
-
-  if (!skip_overflow_check) {
-    __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
-    __ LoadFromOffset(kLoadWord, IP, IP, 0);
-    RecordPcInfo(nullptr, 0);
-  }
-
-  __ PushList(core_spill_mask_);
-  __ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
-  __ cfi().RelOffsetForMany(DWARFReg(kMethodRegisterArgument), 0, core_spill_mask_, kArmWordSize);
-  if (fpu_spill_mask_ != 0) {
-    SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_));
-    __ vpushs(start_register, POPCOUNT(fpu_spill_mask_));
-    __ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_));
-    __ cfi().RelOffsetForMany(DWARFReg(S0), 0, fpu_spill_mask_, kArmWordSize);
-  }
-
-  int adjust = GetFrameSize() - FrameEntrySpillSize();
-  __ AddConstant(SP, -adjust);
-  __ cfi().AdjustCFAOffset(adjust);
-
-  // Save the current method if we need it. Note that we do not
-  // do this in HCurrentMethod, as the instruction might have been removed
-  // in the SSA graph.
-  if (RequiresCurrentMethod()) {
-    __ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, 0);
-  }
-
-  if (GetGraph()->HasShouldDeoptimizeFlag()) {
-    // Initialize should_deoptimize flag to 0.
-    __ mov(IP, ShifterOperand(0));
-    __ StoreToOffset(kStoreWord, IP, SP, GetStackOffsetOfShouldDeoptimizeFlag());
-  }
-}
-
-void CodeGeneratorARM::GenerateFrameExit() {
-  if (HasEmptyFrame()) {
-    __ bx(LR);
-    return;
-  }
-  __ cfi().RememberState();
-  int adjust = GetFrameSize() - FrameEntrySpillSize();
-  __ AddConstant(SP, adjust);
-  __ cfi().AdjustCFAOffset(-adjust);
-  if (fpu_spill_mask_ != 0) {
-    SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_));
-    __ vpops(start_register, POPCOUNT(fpu_spill_mask_));
-    __ cfi().AdjustCFAOffset(-static_cast<int>(kArmPointerSize) * POPCOUNT(fpu_spill_mask_));
-    __ cfi().RestoreMany(DWARFReg(SRegister(0)), fpu_spill_mask_);
-  }
-  // Pop LR into PC to return.
-  DCHECK_NE(core_spill_mask_ & (1 << LR), 0U);
-  uint32_t pop_mask = (core_spill_mask_ & (~(1 << LR))) | 1 << PC;
-  __ PopList(pop_mask);
-  __ cfi().RestoreState();
-  __ cfi().DefCFAOffset(GetFrameSize());
-}
-
-void CodeGeneratorARM::Bind(HBasicBlock* block) {
-  Label* label = GetLabelOf(block);
-  __ BindTrackedLabel(label);
-}
-
-Location InvokeDexCallingConventionVisitorARM::GetNextLocation(Primitive::Type type) {
-  switch (type) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-    case Primitive::kPrimInt:
-    case Primitive::kPrimNot: {
-      uint32_t index = gp_index_++;
-      uint32_t stack_index = stack_index_++;
-      if (index < calling_convention.GetNumberOfRegisters()) {
-        return Location::RegisterLocation(calling_convention.GetRegisterAt(index));
-      } else {
-        return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
-      }
-    }
-
-    case Primitive::kPrimLong: {
-      uint32_t index = gp_index_;
-      uint32_t stack_index = stack_index_;
-      gp_index_ += 2;
-      stack_index_ += 2;
-      if (index + 1 < calling_convention.GetNumberOfRegisters()) {
-        if (calling_convention.GetRegisterAt(index) == R1) {
-          // Skip R1, and use R2_R3 instead.
-          gp_index_++;
-          index++;
-        }
-      }
-      if (index + 1 < calling_convention.GetNumberOfRegisters()) {
-        DCHECK_EQ(calling_convention.GetRegisterAt(index) + 1,
-                  calling_convention.GetRegisterAt(index + 1));
-
-        return Location::RegisterPairLocation(calling_convention.GetRegisterAt(index),
-                                              calling_convention.GetRegisterAt(index + 1));
-      } else {
-        return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
-      }
-    }
-
-    case Primitive::kPrimFloat: {
-      uint32_t stack_index = stack_index_++;
-      if (float_index_ % 2 == 0) {
-        float_index_ = std::max(double_index_, float_index_);
-      }
-      if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) {
-        return Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(float_index_++));
-      } else {
-        return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
-      }
-    }
-
-    case Primitive::kPrimDouble: {
-      double_index_ = std::max(double_index_, RoundUp(float_index_, 2));
-      uint32_t stack_index = stack_index_;
-      stack_index_ += 2;
-      if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) {
-        uint32_t index = double_index_;
-        double_index_ += 2;
-        Location result = Location::FpuRegisterPairLocation(
-          calling_convention.GetFpuRegisterAt(index),
-          calling_convention.GetFpuRegisterAt(index + 1));
-        DCHECK(ExpectedPairLayout(result));
-        return result;
-      } else {
-        return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
-      }
-    }
-
-    case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unexpected parameter type " << type;
-      break;
-  }
-  return Location::NoLocation();
-}
-
-Location InvokeDexCallingConventionVisitorARM::GetReturnLocation(Primitive::Type type) const {
-  switch (type) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-    case Primitive::kPrimInt:
-    case Primitive::kPrimNot: {
-      return Location::RegisterLocation(R0);
-    }
-
-    case Primitive::kPrimFloat: {
-      return Location::FpuRegisterLocation(S0);
-    }
-
-    case Primitive::kPrimLong: {
-      return Location::RegisterPairLocation(R0, R1);
-    }
-
-    case Primitive::kPrimDouble: {
-      return Location::FpuRegisterPairLocation(S0, S1);
-    }
-
-    case Primitive::kPrimVoid:
-      return Location::NoLocation();
-  }
-
-  UNREACHABLE();
-}
-
-Location InvokeDexCallingConventionVisitorARM::GetMethodLocation() const {
-  return Location::RegisterLocation(kMethodRegisterArgument);
-}
-
-void CodeGeneratorARM::Move32(Location destination, Location source) {
-  if (source.Equals(destination)) {
-    return;
-  }
-  if (destination.IsRegister()) {
-    if (source.IsRegister()) {
-      __ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>());
-    } else if (source.IsFpuRegister()) {
-      __ vmovrs(destination.AsRegister<Register>(), source.AsFpuRegister<SRegister>());
-    } else {
-      __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), SP, source.GetStackIndex());
-    }
-  } else if (destination.IsFpuRegister()) {
-    if (source.IsRegister()) {
-      __ vmovsr(destination.AsFpuRegister<SRegister>(), source.AsRegister<Register>());
-    } else if (source.IsFpuRegister()) {
-      __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>());
-    } else {
-      __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex());
-    }
-  } else {
-    DCHECK(destination.IsStackSlot()) << destination;
-    if (source.IsRegister()) {
-      __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), SP, destination.GetStackIndex());
-    } else if (source.IsFpuRegister()) {
-      __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex());
-    } else {
-      DCHECK(source.IsStackSlot()) << source;
-      __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex());
-      __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
-    }
-  }
-}
-
-void CodeGeneratorARM::Move64(Location destination, Location source) {
-  if (source.Equals(destination)) {
-    return;
-  }
-  if (destination.IsRegisterPair()) {
-    if (source.IsRegisterPair()) {
-      EmitParallelMoves(
-          Location::RegisterLocation(source.AsRegisterPairHigh<Register>()),
-          Location::RegisterLocation(destination.AsRegisterPairHigh<Register>()),
-          Primitive::kPrimInt,
-          Location::RegisterLocation(source.AsRegisterPairLow<Register>()),
-          Location::RegisterLocation(destination.AsRegisterPairLow<Register>()),
-          Primitive::kPrimInt);
-    } else if (source.IsFpuRegister()) {
-      UNIMPLEMENTED(FATAL);
-    } else if (source.IsFpuRegisterPair()) {
-      __ vmovrrd(destination.AsRegisterPairLow<Register>(),
-                 destination.AsRegisterPairHigh<Register>(),
-                 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
-    } else {
-      DCHECK(source.IsDoubleStackSlot());
-      DCHECK(ExpectedPairLayout(destination));
-      __ LoadFromOffset(kLoadWordPair, destination.AsRegisterPairLow<Register>(),
-                        SP, source.GetStackIndex());
-    }
-  } else if (destination.IsFpuRegisterPair()) {
-    if (source.IsDoubleStackSlot()) {
-      __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
-                         SP,
-                         source.GetStackIndex());
-    } else if (source.IsRegisterPair()) {
-      __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
-                 source.AsRegisterPairLow<Register>(),
-                 source.AsRegisterPairHigh<Register>());
-    } else {
-      UNIMPLEMENTED(FATAL);
-    }
-  } else {
-    DCHECK(destination.IsDoubleStackSlot());
-    if (source.IsRegisterPair()) {
-      // No conflict possible, so just do the moves.
-      if (source.AsRegisterPairLow<Register>() == R1) {
-        DCHECK_EQ(source.AsRegisterPairHigh<Register>(), R2);
-        __ StoreToOffset(kStoreWord, R1, SP, destination.GetStackIndex());
-        __ StoreToOffset(kStoreWord, R2, SP, destination.GetHighStackIndex(kArmWordSize));
-      } else {
-        __ StoreToOffset(kStoreWordPair, source.AsRegisterPairLow<Register>(),
-                         SP, destination.GetStackIndex());
-      }
-    } else if (source.IsFpuRegisterPair()) {
-      __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()),
-                        SP,
-                        destination.GetStackIndex());
-    } else {
-      DCHECK(source.IsDoubleStackSlot());
-      EmitParallelMoves(
-          Location::StackSlot(source.GetStackIndex()),
-          Location::StackSlot(destination.GetStackIndex()),
-          Primitive::kPrimInt,
-          Location::StackSlot(source.GetHighStackIndex(kArmWordSize)),
-          Location::StackSlot(destination.GetHighStackIndex(kArmWordSize)),
-          Primitive::kPrimInt);
-    }
-  }
-}
-
-void CodeGeneratorARM::MoveConstant(Location location, int32_t value) {
-  DCHECK(location.IsRegister());
-  __ LoadImmediate(location.AsRegister<Register>(), value);
-}
-
-void CodeGeneratorARM::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
-  HParallelMove move(GetGraph()->GetArena());
-  move.AddMove(src, dst, dst_type, nullptr);
-  GetMoveResolver()->EmitNativeCode(&move);
-}
-
-void CodeGeneratorARM::AddLocationAsTemp(Location location, LocationSummary* locations) {
-  if (location.IsRegister()) {
-    locations->AddTemp(location);
-  } else if (location.IsRegisterPair()) {
-    locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairLow<Register>()));
-    locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairHigh<Register>()));
-  } else {
-    UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
-  }
-}
-
-void CodeGeneratorARM::InvokeRuntime(QuickEntrypointEnum entrypoint,
-                                     HInstruction* instruction,
-                                     uint32_t dex_pc,
-                                     SlowPathCode* slow_path) {
-  ValidateInvokeRuntime(entrypoint, instruction, slow_path);
-  GenerateInvokeRuntime(GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value());
-  if (EntrypointRequiresStackMap(entrypoint)) {
-    RecordPcInfo(instruction, dex_pc, slow_path);
-  }
-}
-
-void CodeGeneratorARM::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
-                                                           HInstruction* instruction,
-                                                           SlowPathCode* slow_path) {
-  ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
-  GenerateInvokeRuntime(entry_point_offset);
-}
-
-void CodeGeneratorARM::GenerateInvokeRuntime(int32_t entry_point_offset) {
-  __ LoadFromOffset(kLoadWord, LR, TR, entry_point_offset);
-  __ blx(LR);
-}
-
-void InstructionCodeGeneratorARM::HandleGoto(HInstruction* got, HBasicBlock* successor) {
-  DCHECK(!successor->IsExitBlock());
-
-  HBasicBlock* block = got->GetBlock();
-  HInstruction* previous = got->GetPrevious();
-
-  HLoopInformation* info = block->GetLoopInformation();
-  if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
-    codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
-    GenerateSuspendCheck(info->GetSuspendCheck(), successor);
-    return;
-  }
-
-  if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
-    GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
-  }
-  if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
-    __ b(codegen_->GetLabelOf(successor));
-  }
-}
-
-void LocationsBuilderARM::VisitGoto(HGoto* got) {
-  got->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorARM::VisitGoto(HGoto* got) {
-  HandleGoto(got, got->GetSuccessor());
-}
-
-void LocationsBuilderARM::VisitTryBoundary(HTryBoundary* try_boundary) {
-  try_boundary->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorARM::VisitTryBoundary(HTryBoundary* try_boundary) {
-  HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
-  if (!successor->IsExitBlock()) {
-    HandleGoto(try_boundary, successor);
-  }
-}
-
-void LocationsBuilderARM::VisitExit(HExit* exit) {
-  exit->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorARM::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
-}
-
-void InstructionCodeGeneratorARM::GenerateCompareTestAndBranch(HCondition* condition,
-                                                               Label* true_target_in,
-                                                               Label* false_target_in) {
-  if (CanGenerateTest(condition, codegen_->GetAssembler())) {
-    Label* non_fallthrough_target;
-    bool invert;
-    bool emit_both_branches;
-
-    if (true_target_in == nullptr) {
-      // The true target is fallthrough.
-      DCHECK(false_target_in != nullptr);
-      non_fallthrough_target = false_target_in;
-      invert = true;
-      emit_both_branches = false;
-    } else {
-      // Either the false target is fallthrough, or there is no fallthrough
-      // and both branches must be emitted.
-      non_fallthrough_target = true_target_in;
-      invert = false;
-      emit_both_branches = (false_target_in != nullptr);
-    }
-
-    const auto cond = GenerateTest(condition, invert, codegen_);
-
-    __ b(non_fallthrough_target, cond.first);
-
-    if (emit_both_branches) {
-      // No target falls through, we need to branch.
-      __ b(false_target_in);
-    }
-
-    return;
-  }
-
-  // Generated branching requires both targets to be explicit. If either of the
-  // targets is nullptr (fallthrough) use and bind `fallthrough_target` instead.
-  Label fallthrough_target;
-  Label* true_target = true_target_in == nullptr ? &fallthrough_target : true_target_in;
-  Label* false_target = false_target_in == nullptr ? &fallthrough_target : false_target_in;
-
-  DCHECK_EQ(condition->InputAt(0)->GetType(), Primitive::kPrimLong);
-  GenerateLongComparesAndJumps(condition, true_target, false_target, codegen_);
-
-  if (false_target != &fallthrough_target) {
-    __ b(false_target);
-  }
-
-  if (fallthrough_target.IsLinked()) {
-    __ Bind(&fallthrough_target);
-  }
-}
-
-void InstructionCodeGeneratorARM::GenerateTestAndBranch(HInstruction* instruction,
-                                                        size_t condition_input_index,
-                                                        Label* true_target,
-                                                        Label* false_target) {
-  HInstruction* cond = instruction->InputAt(condition_input_index);
-
-  if (true_target == nullptr && false_target == nullptr) {
-    // Nothing to do. The code always falls through.
-    return;
-  } else if (cond->IsIntConstant()) {
-    // Constant condition, statically compared against "true" (integer value 1).
-    if (cond->AsIntConstant()->IsTrue()) {
-      if (true_target != nullptr) {
-        __ b(true_target);
-      }
-    } else {
-      DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
-      if (false_target != nullptr) {
-        __ b(false_target);
-      }
-    }
-    return;
-  }
-
-  // The following code generates these patterns:
-  //  (1) true_target == nullptr && false_target != nullptr
-  //        - opposite condition true => branch to false_target
-  //  (2) true_target != nullptr && false_target == nullptr
-  //        - condition true => branch to true_target
-  //  (3) true_target != nullptr && false_target != nullptr
-  //        - condition true => branch to true_target
-  //        - branch to false_target
-  if (IsBooleanValueOrMaterializedCondition(cond)) {
-    // Condition has been materialized, compare the output to 0.
-    Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
-    DCHECK(cond_val.IsRegister());
-    if (true_target == nullptr) {
-      __ CompareAndBranchIfZero(cond_val.AsRegister<Register>(), false_target);
-    } else {
-      __ CompareAndBranchIfNonZero(cond_val.AsRegister<Register>(), true_target);
-    }
-  } else {
-    // Condition has not been materialized. Use its inputs as the comparison and
-    // its condition as the branch condition.
-    HCondition* condition = cond->AsCondition();
-
-    // If this is a long or FP comparison that has been folded into
-    // the HCondition, generate the comparison directly.
-    Primitive::Type type = condition->InputAt(0)->GetType();
-    if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) {
-      GenerateCompareTestAndBranch(condition, true_target, false_target);
-      return;
-    }
-
-    Label* non_fallthrough_target;
-    Condition arm_cond;
-    LocationSummary* locations = cond->GetLocations();
-    DCHECK(locations->InAt(0).IsRegister());
-    Register left = locations->InAt(0).AsRegister<Register>();
-    Location right = locations->InAt(1);
-
-    if (true_target == nullptr) {
-      arm_cond = ARMCondition(condition->GetOppositeCondition());
-      non_fallthrough_target = false_target;
-    } else {
-      arm_cond = ARMCondition(condition->GetCondition());
-      non_fallthrough_target = true_target;
-    }
-
-    if (right.IsConstant() && (arm_cond == NE || arm_cond == EQ) &&
-        CodeGenerator::GetInt32ValueOf(right.GetConstant()) == 0) {
-      if (arm_cond == EQ) {
-        __ CompareAndBranchIfZero(left, non_fallthrough_target);
-      } else {
-        DCHECK_EQ(arm_cond, NE);
-        __ CompareAndBranchIfNonZero(left, non_fallthrough_target);
-      }
-    } else {
-      if (right.IsRegister()) {
-        __ cmp(left, ShifterOperand(right.AsRegister<Register>()));
-      } else {
-        DCHECK(right.IsConstant());
-        __ CmpConstant(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
-      }
-
-      __ b(non_fallthrough_target, arm_cond);
-    }
-  }
-
-  // If neither branch falls through (case 3), the conditional branch to `true_target`
-  // was already emitted (case 2) and we need to emit a jump to `false_target`.
-  if (true_target != nullptr && false_target != nullptr) {
-    __ b(false_target);
-  }
-}
-
-void LocationsBuilderARM::VisitIf(HIf* if_instr) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
-  if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
-    locations->SetInAt(0, Location::RequiresRegister());
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) {
-  HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
-  HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
-  Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
-      nullptr : codegen_->GetLabelOf(true_successor);
-  Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
-      nullptr : codegen_->GetLabelOf(false_successor);
-  GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target);
-}
-
-void LocationsBuilderARM::VisitDeoptimize(HDeoptimize* deoptimize) {
-  LocationSummary* locations = new (GetGraph()->GetArena())
-      LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
-  InvokeRuntimeCallingConvention calling_convention;
-  RegisterSet caller_saves = RegisterSet::Empty();
-  caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->SetCustomSlowPathCallerSaves(caller_saves);
-  if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
-    locations->SetInAt(0, Location::RequiresRegister());
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitDeoptimize(HDeoptimize* deoptimize) {
-  SlowPathCodeARM* slow_path = deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARM>(deoptimize);
-  GenerateTestAndBranch(deoptimize,
-                        /* condition_input_index */ 0,
-                        slow_path->GetEntryLabel(),
-                        /* false_target */ nullptr);
-}
-
-void LocationsBuilderARM::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
-  LocationSummary* locations = new (GetGraph()->GetArena())
-      LocationSummary(flag, LocationSummary::kNoCall);
-  locations->SetOut(Location::RequiresRegister());
-}
-
-void InstructionCodeGeneratorARM::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
-  __ LoadFromOffset(kLoadWord,
-                    flag->GetLocations()->Out().AsRegister<Register>(),
-                    SP,
-                    codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
-}
-
-void LocationsBuilderARM::VisitSelect(HSelect* select) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
-  const bool is_floating_point = Primitive::IsFloatingPointType(select->GetType());
-
-  if (is_floating_point) {
-    locations->SetInAt(0, Location::RequiresFpuRegister());
-    locations->SetInAt(1, Location::FpuRegisterOrConstant(select->GetTrueValue()));
-  } else {
-    locations->SetInAt(0, Location::RequiresRegister());
-    locations->SetInAt(1, Arm8BitEncodableConstantOrRegister(select->GetTrueValue()));
-  }
-
-  if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
-    locations->SetInAt(2, Location::RegisterOrConstant(select->GetCondition()));
-    // The code generator handles overlap with the values, but not with the condition.
-    locations->SetOut(Location::SameAsFirstInput());
-  } else if (is_floating_point) {
-    locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
-  } else {
-    if (!locations->InAt(1).IsConstant()) {
-      locations->SetInAt(0, Arm8BitEncodableConstantOrRegister(select->GetFalseValue()));
-    }
-
-    locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitSelect(HSelect* select) {
-  HInstruction* const condition = select->GetCondition();
-  const LocationSummary* const locations = select->GetLocations();
-  const Primitive::Type type = select->GetType();
-  const Location first = locations->InAt(0);
-  const Location out = locations->Out();
-  const Location second = locations->InAt(1);
-  Location src;
-
-  if (condition->IsIntConstant()) {
-    if (condition->AsIntConstant()->IsFalse()) {
-      src = first;
-    } else {
-      src = second;
-    }
-
-    codegen_->MoveLocation(out, src, type);
-    return;
-  }
-
-  if (!Primitive::IsFloatingPointType(type) &&
-      (IsBooleanValueOrMaterializedCondition(condition) ||
-       CanGenerateTest(condition->AsCondition(), codegen_->GetAssembler()))) {
-    bool invert = false;
-
-    if (out.Equals(second)) {
-      src = first;
-      invert = true;
-    } else if (out.Equals(first)) {
-      src = second;
-    } else if (second.IsConstant()) {
-      DCHECK(CanEncodeConstantAs8BitImmediate(second.GetConstant()));
-      src = second;
-    } else if (first.IsConstant()) {
-      DCHECK(CanEncodeConstantAs8BitImmediate(first.GetConstant()));
-      src = first;
-      invert = true;
-    } else {
-      src = second;
-    }
-
-    if (CanGenerateConditionalMove(out, src)) {
-      if (!out.Equals(first) && !out.Equals(second)) {
-        codegen_->MoveLocation(out, src.Equals(first) ? second : first, type);
-      }
-
-      std::pair<Condition, Condition> cond;
-
-      if (IsBooleanValueOrMaterializedCondition(condition)) {
-        __ CmpConstant(locations->InAt(2).AsRegister<Register>(), 0);
-        cond = invert ? std::make_pair(EQ, NE) : std::make_pair(NE, EQ);
-      } else {
-        cond = GenerateTest(condition->AsCondition(), invert, codegen_);
-      }
-
-      if (out.IsRegister()) {
-        ShifterOperand operand;
-
-        if (src.IsConstant()) {
-          operand = ShifterOperand(CodeGenerator::GetInt32ValueOf(src.GetConstant()));
-        } else {
-          DCHECK(src.IsRegister());
-          operand = ShifterOperand(src.AsRegister<Register>());
-        }
-
-        __ it(cond.first);
-        __ mov(out.AsRegister<Register>(), operand, cond.first);
-      } else {
-        DCHECK(out.IsRegisterPair());
-
-        ShifterOperand operand_high;
-        ShifterOperand operand_low;
-
-        if (src.IsConstant()) {
-          const int64_t value = src.GetConstant()->AsLongConstant()->GetValue();
-
-          operand_high = ShifterOperand(High32Bits(value));
-          operand_low = ShifterOperand(Low32Bits(value));
-        } else {
-          DCHECK(src.IsRegisterPair());
-          operand_high = ShifterOperand(src.AsRegisterPairHigh<Register>());
-          operand_low = ShifterOperand(src.AsRegisterPairLow<Register>());
-        }
-
-        __ it(cond.first);
-        __ mov(out.AsRegisterPairLow<Register>(), operand_low, cond.first);
-        __ it(cond.first);
-        __ mov(out.AsRegisterPairHigh<Register>(), operand_high, cond.first);
-      }
-
-      return;
-    }
-  }
-
-  Label* false_target = nullptr;
-  Label* true_target = nullptr;
-  Label select_end;
-  Label* target = codegen_->GetFinalLabel(select, &select_end);
-
-  if (out.Equals(second)) {
-    true_target = target;
-    src = first;
-  } else {
-    false_target = target;
-    src = second;
-
-    if (!out.Equals(first)) {
-      codegen_->MoveLocation(out, first, type);
-    }
-  }
-
-  GenerateTestAndBranch(select, 2, true_target, false_target);
-  codegen_->MoveLocation(out, src, type);
-
-  if (select_end.IsLinked()) {
-    __ Bind(&select_end);
-  }
-}
-
-void LocationsBuilderARM::VisitNativeDebugInfo(HNativeDebugInfo* info) {
-  new (GetGraph()->GetArena()) LocationSummary(info);
-}
-
-void InstructionCodeGeneratorARM::VisitNativeDebugInfo(HNativeDebugInfo*) {
-  // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
-}
-
-void CodeGeneratorARM::GenerateNop() {
-  __ nop();
-}
-
-// `temp` is an extra temporary register that is used for some conditions;
-// callers may not specify it, in which case the method will use a scratch
-// register instead.
-void CodeGeneratorARM::GenerateConditionWithZero(IfCondition condition,
-                                                 Register out,
-                                                 Register in,
-                                                 Register temp) {
-  switch (condition) {
-    case kCondEQ:
-    // x <= 0 iff x == 0 when the comparison is unsigned.
-    case kCondBE:
-      if (temp == kNoRegister || (ArmAssembler::IsLowRegister(out) && out != in)) {
-        temp = out;
-      }
-
-      // Avoid 32-bit instructions if possible; note that `in` and `temp` must be
-      // different as well.
-      if (ArmAssembler::IsLowRegister(in) && ArmAssembler::IsLowRegister(temp) && in != temp) {
-        // temp = - in; only 0 sets the carry flag.
-        __ rsbs(temp, in, ShifterOperand(0));
-
-        if (out == in) {
-          std::swap(in, temp);
-        }
-
-        // out = - in + in + carry = carry
-        __ adc(out, temp, ShifterOperand(in));
-      } else {
-        // If `in` is 0, then it has 32 leading zeros, and less than that otherwise.
-        __ clz(out, in);
-        // Any number less than 32 logically shifted right by 5 bits results in 0;
-        // the same operation on 32 yields 1.
-        __ Lsr(out, out, 5);
-      }
-
-      break;
-    case kCondNE:
-    // x > 0 iff x != 0 when the comparison is unsigned.
-    case kCondA:
-      if (out == in) {
-        if (temp == kNoRegister || in == temp) {
-          temp = IP;
-        }
-      } else if (temp == kNoRegister || !ArmAssembler::IsLowRegister(temp)) {
-        temp = out;
-      }
-
-      // temp = in - 1; only 0 does not set the carry flag.
-      __ subs(temp, in, ShifterOperand(1));
-      // out = in + ~temp + carry = in + (-(in - 1) - 1) + carry = in - in + 1 - 1 + carry = carry
-      __ sbc(out, in, ShifterOperand(temp));
-      break;
-    case kCondGE:
-      __ mvn(out, ShifterOperand(in));
-      in = out;
-      FALLTHROUGH_INTENDED;
-    case kCondLT:
-      // We only care about the sign bit.
-      __ Lsr(out, in, 31);
-      break;
-    case kCondAE:
-      // Trivially true.
-      __ mov(out, ShifterOperand(1));
-      break;
-    case kCondB:
-      // Trivially false.
-      __ mov(out, ShifterOperand(0));
-      break;
-    default:
-      LOG(FATAL) << "Unexpected condition " << condition;
-      UNREACHABLE();
-  }
-}
-
-void LocationsBuilderARM::HandleCondition(HCondition* cond) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
-  // Handle the long/FP comparisons made in instruction simplification.
-  switch (cond->InputAt(0)->GetType()) {
-    case Primitive::kPrimLong:
-      locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
-      if (!cond->IsEmittedAtUseSite()) {
-        locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-      }
-      break;
-
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      locations->SetInAt(0, Location::RequiresFpuRegister());
-      locations->SetInAt(1, ArithmeticZeroOrFpuRegister(cond->InputAt(1)));
-      if (!cond->IsEmittedAtUseSite()) {
-        locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-      }
-      break;
-
-    default:
-      locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
-      if (!cond->IsEmittedAtUseSite()) {
-        locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-      }
-  }
-}
-
-void InstructionCodeGeneratorARM::HandleCondition(HCondition* cond) {
-  if (cond->IsEmittedAtUseSite()) {
-    return;
-  }
-
-  const Primitive::Type type = cond->GetLeft()->GetType();
-
-  if (Primitive::IsFloatingPointType(type)) {
-    GenerateConditionGeneric(cond, codegen_);
-    return;
-  }
-
-  DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
-
-  const IfCondition condition = cond->GetCondition();
-
-  // A condition with only one boolean input, or two boolean inputs without being equality or
-  // inequality results from transformations done by the instruction simplifier, and is handled
-  // as a regular condition with integral inputs.
-  if (type == Primitive::kPrimBoolean &&
-      cond->GetRight()->GetType() == Primitive::kPrimBoolean &&
-      (condition == kCondEQ || condition == kCondNE)) {
-    const LocationSummary* const locations = cond->GetLocations();
-    Register left = locations->InAt(0).AsRegister<Register>();
-    const Register out = locations->Out().AsRegister<Register>();
-    const Location right_loc = locations->InAt(1);
-
-    // The constant case is handled by the instruction simplifier.
-    DCHECK(!right_loc.IsConstant());
-
-    Register right = right_loc.AsRegister<Register>();
-
-    // Avoid 32-bit instructions if possible.
-    if (out == right) {
-      std::swap(left, right);
-    }
-
-    __ eor(out, left, ShifterOperand(right));
-
-    if (condition == kCondEQ) {
-      __ eor(out, out, ShifterOperand(1));
-    }
-
-    return;
-  }
-
-  GenerateConditionIntegralOrNonPrimitive(cond, codegen_);
-}
-
-void LocationsBuilderARM::VisitEqual(HEqual* comp) {
-  HandleCondition(comp);
-}
-
-void InstructionCodeGeneratorARM::VisitEqual(HEqual* comp) {
-  HandleCondition(comp);
-}
-
-void LocationsBuilderARM::VisitNotEqual(HNotEqual* comp) {
-  HandleCondition(comp);
-}
-
-void InstructionCodeGeneratorARM::VisitNotEqual(HNotEqual* comp) {
-  HandleCondition(comp);
-}
-
-void LocationsBuilderARM::VisitLessThan(HLessThan* comp) {
-  HandleCondition(comp);
-}
-
-void InstructionCodeGeneratorARM::VisitLessThan(HLessThan* comp) {
-  HandleCondition(comp);
-}
-
-void LocationsBuilderARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
-  HandleCondition(comp);
-}
-
-void InstructionCodeGeneratorARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
-  HandleCondition(comp);
-}
-
-void LocationsBuilderARM::VisitGreaterThan(HGreaterThan* comp) {
-  HandleCondition(comp);
-}
-
-void InstructionCodeGeneratorARM::VisitGreaterThan(HGreaterThan* comp) {
-  HandleCondition(comp);
-}
-
-void LocationsBuilderARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
-  HandleCondition(comp);
-}
-
-void InstructionCodeGeneratorARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
-  HandleCondition(comp);
-}
-
-void LocationsBuilderARM::VisitBelow(HBelow* comp) {
-  HandleCondition(comp);
-}
-
-void InstructionCodeGeneratorARM::VisitBelow(HBelow* comp) {
-  HandleCondition(comp);
-}
-
-void LocationsBuilderARM::VisitBelowOrEqual(HBelowOrEqual* comp) {
-  HandleCondition(comp);
-}
-
-void InstructionCodeGeneratorARM::VisitBelowOrEqual(HBelowOrEqual* comp) {
-  HandleCondition(comp);
-}
-
-void LocationsBuilderARM::VisitAbove(HAbove* comp) {
-  HandleCondition(comp);
-}
-
-void InstructionCodeGeneratorARM::VisitAbove(HAbove* comp) {
-  HandleCondition(comp);
-}
-
-void LocationsBuilderARM::VisitAboveOrEqual(HAboveOrEqual* comp) {
-  HandleCondition(comp);
-}
-
-void InstructionCodeGeneratorARM::VisitAboveOrEqual(HAboveOrEqual* comp) {
-  HandleCondition(comp);
-}
-
-void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
-  locations->SetOut(Location::ConstantLocation(constant));
-}
-
-void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
-  // Will be generated at use site.
-}
-
-void LocationsBuilderARM::VisitNullConstant(HNullConstant* constant) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
-  locations->SetOut(Location::ConstantLocation(constant));
-}
-
-void InstructionCodeGeneratorARM::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
-  // Will be generated at use site.
-}
-
-void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
-  locations->SetOut(Location::ConstantLocation(constant));
-}
-
-void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
-  // Will be generated at use site.
-}
-
-void LocationsBuilderARM::VisitFloatConstant(HFloatConstant* constant) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
-  locations->SetOut(Location::ConstantLocation(constant));
-}
-
-void InstructionCodeGeneratorARM::VisitFloatConstant(HFloatConstant* constant ATTRIBUTE_UNUSED) {
-  // Will be generated at use site.
-}
-
-void LocationsBuilderARM::VisitDoubleConstant(HDoubleConstant* constant) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
-  locations->SetOut(Location::ConstantLocation(constant));
-}
-
-void InstructionCodeGeneratorARM::VisitDoubleConstant(HDoubleConstant* constant ATTRIBUTE_UNUSED) {
-  // Will be generated at use site.
-}
-
-void LocationsBuilderARM::VisitConstructorFence(HConstructorFence* constructor_fence) {
-  constructor_fence->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorARM::VisitConstructorFence(
-    HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
-  codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
-}
-
-void LocationsBuilderARM::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
-  memory_barrier->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorARM::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
-  codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
-}
-
-void LocationsBuilderARM::VisitReturnVoid(HReturnVoid* ret) {
-  ret->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorARM::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
-  codegen_->GenerateFrameExit();
-}
-
-void LocationsBuilderARM::VisitReturn(HReturn* ret) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
-  locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType()));
-}
-
-void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) {
-  codegen_->GenerateFrameExit();
-}
-
-void LocationsBuilderARM::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
-  // The trampoline uses the same calling convention as dex calling conventions,
-  // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
-  // the method_idx.
-  HandleInvoke(invoke);
-}
-
-void InstructionCodeGeneratorARM::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
-  codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
-}
-
-void LocationsBuilderARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
-  // Explicit clinit checks triggered by static invokes must have been pruned by
-  // art::PrepareForRegisterAllocation.
-  DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
-
-  IntrinsicLocationsBuilderARM intrinsic(codegen_);
-  if (intrinsic.TryDispatch(invoke)) {
-    return;
-  }
-
-  HandleInvoke(invoke);
-}
-
-static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM* codegen) {
-  if (invoke->GetLocations()->Intrinsified()) {
-    IntrinsicCodeGeneratorARM intrinsic(codegen);
-    intrinsic.Dispatch(invoke);
-    return true;
-  }
-  return false;
-}
-
-void InstructionCodeGeneratorARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
-  // Explicit clinit checks triggered by static invokes must have been pruned by
-  // art::PrepareForRegisterAllocation.
-  DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
-
-  if (TryGenerateIntrinsicCode(invoke, codegen_)) {
-    return;
-  }
-
-  LocationSummary* locations = invoke->GetLocations();
-  codegen_->GenerateStaticOrDirectCall(
-      invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
-}
-
-void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) {
-  InvokeDexCallingConventionVisitorARM calling_convention_visitor;
-  CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
-}
-
-void LocationsBuilderARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
-  IntrinsicLocationsBuilderARM intrinsic(codegen_);
-  if (intrinsic.TryDispatch(invoke)) {
-    return;
-  }
-
-  HandleInvoke(invoke);
-}
-
-void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
-  if (TryGenerateIntrinsicCode(invoke, codegen_)) {
-    return;
-  }
-
-  codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
-  DCHECK(!codegen_->IsLeafMethod());
-}
-
-void LocationsBuilderARM::VisitInvokeInterface(HInvokeInterface* invoke) {
-  HandleInvoke(invoke);
-  // Add the hidden argument.
-  invoke->GetLocations()->AddTemp(Location::RegisterLocation(R12));
-}
-
-void InstructionCodeGeneratorARM::VisitInvokeInterface(HInvokeInterface* invoke) {
-  // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
-  LocationSummary* locations = invoke->GetLocations();
-  Register temp = locations->GetTemp(0).AsRegister<Register>();
-  Register hidden_reg = locations->GetTemp(1).AsRegister<Register>();
-  Location receiver = locations->InAt(0);
-  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
-
-  // Set the hidden argument. This is safe to do this here, as R12
-  // won't be modified thereafter, before the `blx` (call) instruction.
-  DCHECK_EQ(R12, hidden_reg);
-  __ LoadImmediate(hidden_reg, invoke->GetDexMethodIndex());
-
-  if (receiver.IsStackSlot()) {
-    __ LoadFromOffset(kLoadWord, temp, SP, receiver.GetStackIndex());
-    // /* HeapReference<Class> */ temp = temp->klass_
-    __ LoadFromOffset(kLoadWord, temp, temp, class_offset);
-  } else {
-    // /* HeapReference<Class> */ temp = receiver->klass_
-    __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
-  }
-  codegen_->MaybeRecordImplicitNullCheck(invoke);
-  // Instead of simply (possibly) unpoisoning `temp` here, we should
-  // emit a read barrier for the previous class reference load.
-  // However this is not required in practice, as this is an
-  // intermediate/temporary reference and because the current
-  // concurrent copying collector keeps the from-space memory
-  // intact/accessible until the end of the marking phase (the
-  // concurrent copying collector may not in the future).
-  __ MaybeUnpoisonHeapReference(temp);
-  __ LoadFromOffset(kLoadWord, temp, temp,
-        mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
-  uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
-      invoke->GetImtIndex(), kArmPointerSize));
-  // temp = temp->GetImtEntryAt(method_offset);
-  __ LoadFromOffset(kLoadWord, temp, temp, method_offset);
-  uint32_t entry_point =
-      ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value();
-  // LR = temp->GetEntryPoint();
-  __ LoadFromOffset(kLoadWord, LR, temp, entry_point);
-  // LR();
-  __ blx(LR);
-  DCHECK(!codegen_->IsLeafMethod());
-  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
-}
-
-void LocationsBuilderARM::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
-  HandleInvoke(invoke);
-}
-
-void InstructionCodeGeneratorARM::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
-  codegen_->GenerateInvokePolymorphicCall(invoke);
-}
-
-void LocationsBuilderARM::VisitNeg(HNeg* neg) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
-  switch (neg->GetResultType()) {
-    case Primitive::kPrimInt: {
-      locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-      break;
-    }
-    case Primitive::kPrimLong: {
-      locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-      break;
-    }
-
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      locations->SetInAt(0, Location::RequiresFpuRegister());
-      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
-      break;
-
-    default:
-      LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitNeg(HNeg* neg) {
-  LocationSummary* locations = neg->GetLocations();
-  Location out = locations->Out();
-  Location in = locations->InAt(0);
-  switch (neg->GetResultType()) {
-    case Primitive::kPrimInt:
-      DCHECK(in.IsRegister());
-      __ rsb(out.AsRegister<Register>(), in.AsRegister<Register>(), ShifterOperand(0));
-      break;
-
-    case Primitive::kPrimLong:
-      DCHECK(in.IsRegisterPair());
-      // out.lo = 0 - in.lo (and update the carry/borrow (C) flag)
-      __ rsbs(out.AsRegisterPairLow<Register>(),
-              in.AsRegisterPairLow<Register>(),
-              ShifterOperand(0));
-      // We cannot emit an RSC (Reverse Subtract with Carry)
-      // instruction here, as it does not exist in the Thumb-2
-      // instruction set.  We use the following approach
-      // using SBC and SUB instead.
-      //
-      // out.hi = -C
-      __ sbc(out.AsRegisterPairHigh<Register>(),
-             out.AsRegisterPairHigh<Register>(),
-             ShifterOperand(out.AsRegisterPairHigh<Register>()));
-      // out.hi = out.hi - in.hi
-      __ sub(out.AsRegisterPairHigh<Register>(),
-             out.AsRegisterPairHigh<Register>(),
-             ShifterOperand(in.AsRegisterPairHigh<Register>()));
-      break;
-
-    case Primitive::kPrimFloat:
-      DCHECK(in.IsFpuRegister());
-      __ vnegs(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>());
-      break;
-
-    case Primitive::kPrimDouble:
-      DCHECK(in.IsFpuRegisterPair());
-      __ vnegd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
-               FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
-      break;
-
-    default:
-      LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
-  }
-}
-
-void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
-  Primitive::Type result_type = conversion->GetResultType();
-  Primitive::Type input_type = conversion->GetInputType();
-  DCHECK_NE(result_type, input_type);
-
-  // The float-to-long, double-to-long and long-to-float type conversions
-  // rely on a call to the runtime.
-  LocationSummary::CallKind call_kind =
-      (((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
-        && result_type == Primitive::kPrimLong)
-       || (input_type == Primitive::kPrimLong && result_type == Primitive::kPrimFloat))
-      ? LocationSummary::kCallOnMainOnly
-      : LocationSummary::kNoCall;
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
-
-  // The Java language does not allow treating boolean as an integral type but
-  // our bit representation makes it safe.
-
-  switch (result_type) {
-    case Primitive::kPrimByte:
-      switch (input_type) {
-        case Primitive::kPrimLong:
-          // Type conversion from long to byte is a result of code transformations.
-        case Primitive::kPrimBoolean:
-          // Boolean input is a result of code transformations.
-        case Primitive::kPrimShort:
-        case Primitive::kPrimInt:
-        case Primitive::kPrimChar:
-          // Processing a Dex `int-to-byte' instruction.
-          locations->SetInAt(0, Location::RequiresRegister());
-          locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-          break;
-
-        default:
-          LOG(FATAL) << "Unexpected type conversion from " << input_type
-                     << " to " << result_type;
-      }
-      break;
-
-    case Primitive::kPrimShort:
-      switch (input_type) {
-        case Primitive::kPrimLong:
-          // Type conversion from long to short is a result of code transformations.
-        case Primitive::kPrimBoolean:
-          // Boolean input is a result of code transformations.
-        case Primitive::kPrimByte:
-        case Primitive::kPrimInt:
-        case Primitive::kPrimChar:
-          // Processing a Dex `int-to-short' instruction.
-          locations->SetInAt(0, Location::RequiresRegister());
-          locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-          break;
-
-        default:
-          LOG(FATAL) << "Unexpected type conversion from " << input_type
-                     << " to " << result_type;
-      }
-      break;
-
-    case Primitive::kPrimInt:
-      switch (input_type) {
-        case Primitive::kPrimLong:
-          // Processing a Dex `long-to-int' instruction.
-          locations->SetInAt(0, Location::Any());
-          locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-          break;
-
-        case Primitive::kPrimFloat:
-          // Processing a Dex `float-to-int' instruction.
-          locations->SetInAt(0, Location::RequiresFpuRegister());
-          locations->SetOut(Location::RequiresRegister());
-          locations->AddTemp(Location::RequiresFpuRegister());
-          break;
-
-        case Primitive::kPrimDouble:
-          // Processing a Dex `double-to-int' instruction.
-          locations->SetInAt(0, Location::RequiresFpuRegister());
-          locations->SetOut(Location::RequiresRegister());
-          locations->AddTemp(Location::RequiresFpuRegister());
-          break;
-
-        default:
-          LOG(FATAL) << "Unexpected type conversion from " << input_type
-                     << " to " << result_type;
-      }
-      break;
-
-    case Primitive::kPrimLong:
-      switch (input_type) {
-        case Primitive::kPrimBoolean:
-          // Boolean input is a result of code transformations.
-        case Primitive::kPrimByte:
-        case Primitive::kPrimShort:
-        case Primitive::kPrimInt:
-        case Primitive::kPrimChar:
-          // Processing a Dex `int-to-long' instruction.
-          locations->SetInAt(0, Location::RequiresRegister());
-          locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-          break;
-
-        case Primitive::kPrimFloat: {
-          // Processing a Dex `float-to-long' instruction.
-          InvokeRuntimeCallingConvention calling_convention;
-          locations->SetInAt(0, Location::FpuRegisterLocation(
-              calling_convention.GetFpuRegisterAt(0)));
-          locations->SetOut(Location::RegisterPairLocation(R0, R1));
-          break;
-        }
-
-        case Primitive::kPrimDouble: {
-          // Processing a Dex `double-to-long' instruction.
-          InvokeRuntimeCallingConvention calling_convention;
-          locations->SetInAt(0, Location::FpuRegisterPairLocation(
-              calling_convention.GetFpuRegisterAt(0),
-              calling_convention.GetFpuRegisterAt(1)));
-          locations->SetOut(Location::RegisterPairLocation(R0, R1));
-          break;
-        }
-
-        default:
-          LOG(FATAL) << "Unexpected type conversion from " << input_type
-                     << " to " << result_type;
-      }
-      break;
-
-    case Primitive::kPrimChar:
-      switch (input_type) {
-        case Primitive::kPrimLong:
-          // Type conversion from long to char is a result of code transformations.
-        case Primitive::kPrimBoolean:
-          // Boolean input is a result of code transformations.
-        case Primitive::kPrimByte:
-        case Primitive::kPrimShort:
-        case Primitive::kPrimInt:
-          // Processing a Dex `int-to-char' instruction.
-          locations->SetInAt(0, Location::RequiresRegister());
-          locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-          break;
-
-        default:
-          LOG(FATAL) << "Unexpected type conversion from " << input_type
-                     << " to " << result_type;
-      }
-      break;
-
-    case Primitive::kPrimFloat:
-      switch (input_type) {
-        case Primitive::kPrimBoolean:
-          // Boolean input is a result of code transformations.
-        case Primitive::kPrimByte:
-        case Primitive::kPrimShort:
-        case Primitive::kPrimInt:
-        case Primitive::kPrimChar:
-          // Processing a Dex `int-to-float' instruction.
-          locations->SetInAt(0, Location::RequiresRegister());
-          locations->SetOut(Location::RequiresFpuRegister());
-          break;
-
-        case Primitive::kPrimLong: {
-          // Processing a Dex `long-to-float' instruction.
-          InvokeRuntimeCallingConvention calling_convention;
-          locations->SetInAt(0, Location::RegisterPairLocation(
-              calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
-          locations->SetOut(Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
-          break;
-        }
-
-        case Primitive::kPrimDouble:
-          // Processing a Dex `double-to-float' instruction.
-          locations->SetInAt(0, Location::RequiresFpuRegister());
-          locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
-          break;
-
-        default:
-          LOG(FATAL) << "Unexpected type conversion from " << input_type
-                     << " to " << result_type;
-      };
-      break;
-
-    case Primitive::kPrimDouble:
-      switch (input_type) {
-        case Primitive::kPrimBoolean:
-          // Boolean input is a result of code transformations.
-        case Primitive::kPrimByte:
-        case Primitive::kPrimShort:
-        case Primitive::kPrimInt:
-        case Primitive::kPrimChar:
-          // Processing a Dex `int-to-double' instruction.
-          locations->SetInAt(0, Location::RequiresRegister());
-          locations->SetOut(Location::RequiresFpuRegister());
-          break;
-
-        case Primitive::kPrimLong:
-          // Processing a Dex `long-to-double' instruction.
-          locations->SetInAt(0, Location::RequiresRegister());
-          locations->SetOut(Location::RequiresFpuRegister());
-          locations->AddTemp(Location::RequiresFpuRegister());
-          locations->AddTemp(Location::RequiresFpuRegister());
-          break;
-
-        case Primitive::kPrimFloat:
-          // Processing a Dex `float-to-double' instruction.
-          locations->SetInAt(0, Location::RequiresFpuRegister());
-          locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
-          break;
-
-        default:
-          LOG(FATAL) << "Unexpected type conversion from " << input_type
-                     << " to " << result_type;
-      };
-      break;
-
-    default:
-      LOG(FATAL) << "Unexpected type conversion from " << input_type
-                 << " to " << result_type;
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversion) {
-  LocationSummary* locations = conversion->GetLocations();
-  Location out = locations->Out();
-  Location in = locations->InAt(0);
-  Primitive::Type result_type = conversion->GetResultType();
-  Primitive::Type input_type = conversion->GetInputType();
-  DCHECK_NE(result_type, input_type);
-  switch (result_type) {
-    case Primitive::kPrimByte:
-      switch (input_type) {
-        case Primitive::kPrimLong:
-          // Type conversion from long to byte is a result of code transformations.
-          __ sbfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 8);
-          break;
-        case Primitive::kPrimBoolean:
-          // Boolean input is a result of code transformations.
-        case Primitive::kPrimShort:
-        case Primitive::kPrimInt:
-        case Primitive::kPrimChar:
-          // Processing a Dex `int-to-byte' instruction.
-          __ sbfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 8);
-          break;
-
-        default:
-          LOG(FATAL) << "Unexpected type conversion from " << input_type
-                     << " to " << result_type;
-      }
-      break;
-
-    case Primitive::kPrimShort:
-      switch (input_type) {
-        case Primitive::kPrimLong:
-          // Type conversion from long to short is a result of code transformations.
-          __ sbfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 16);
-          break;
-        case Primitive::kPrimBoolean:
-          // Boolean input is a result of code transformations.
-        case Primitive::kPrimByte:
-        case Primitive::kPrimInt:
-        case Primitive::kPrimChar:
-          // Processing a Dex `int-to-short' instruction.
-          __ sbfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 16);
-          break;
-
-        default:
-          LOG(FATAL) << "Unexpected type conversion from " << input_type
-                     << " to " << result_type;
-      }
-      break;
-
-    case Primitive::kPrimInt:
-      switch (input_type) {
-        case Primitive::kPrimLong:
-          // Processing a Dex `long-to-int' instruction.
-          DCHECK(out.IsRegister());
-          if (in.IsRegisterPair()) {
-            __ Mov(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>());
-          } else if (in.IsDoubleStackSlot()) {
-            __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), SP, in.GetStackIndex());
-          } else {
-            DCHECK(in.IsConstant());
-            DCHECK(in.GetConstant()->IsLongConstant());
-            int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
-            __ LoadImmediate(out.AsRegister<Register>(), static_cast<int32_t>(value));
-          }
-          break;
-
-        case Primitive::kPrimFloat: {
-          // Processing a Dex `float-to-int' instruction.
-          SRegister temp = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>();
-          __ vcvtis(temp, in.AsFpuRegister<SRegister>());
-          __ vmovrs(out.AsRegister<Register>(), temp);
-          break;
-        }
-
-        case Primitive::kPrimDouble: {
-          // Processing a Dex `double-to-int' instruction.
-          SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>();
-          __ vcvtid(temp_s, FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
-          __ vmovrs(out.AsRegister<Register>(), temp_s);
-          break;
-        }
-
-        default:
-          LOG(FATAL) << "Unexpected type conversion from " << input_type
-                     << " to " << result_type;
-      }
-      break;
-
-    case Primitive::kPrimLong:
-      switch (input_type) {
-        case Primitive::kPrimBoolean:
-          // Boolean input is a result of code transformations.
-        case Primitive::kPrimByte:
-        case Primitive::kPrimShort:
-        case Primitive::kPrimInt:
-        case Primitive::kPrimChar:
-          // Processing a Dex `int-to-long' instruction.
-          DCHECK(out.IsRegisterPair());
-          DCHECK(in.IsRegister());
-          __ Mov(out.AsRegisterPairLow<Register>(), in.AsRegister<Register>());
-          // Sign extension.
-          __ Asr(out.AsRegisterPairHigh<Register>(),
-                 out.AsRegisterPairLow<Register>(),
-                 31);
-          break;
-
-        case Primitive::kPrimFloat:
-          // Processing a Dex `float-to-long' instruction.
-          codegen_->InvokeRuntime(kQuickF2l, conversion, conversion->GetDexPc());
-          CheckEntrypointTypes<kQuickF2l, int64_t, float>();
-          break;
-
-        case Primitive::kPrimDouble:
-          // Processing a Dex `double-to-long' instruction.
-          codegen_->InvokeRuntime(kQuickD2l, conversion, conversion->GetDexPc());
-          CheckEntrypointTypes<kQuickD2l, int64_t, double>();
-          break;
-
-        default:
-          LOG(FATAL) << "Unexpected type conversion from " << input_type
-                     << " to " << result_type;
-      }
-      break;
-
-    case Primitive::kPrimChar:
-      switch (input_type) {
-        case Primitive::kPrimLong:
-          // Type conversion from long to char is a result of code transformations.
-          __ ubfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 16);
-          break;
-        case Primitive::kPrimBoolean:
-          // Boolean input is a result of code transformations.
-        case Primitive::kPrimByte:
-        case Primitive::kPrimShort:
-        case Primitive::kPrimInt:
-          // Processing a Dex `int-to-char' instruction.
-          __ ubfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 16);
-          break;
-
-        default:
-          LOG(FATAL) << "Unexpected type conversion from " << input_type
-                     << " to " << result_type;
-      }
-      break;
-
-    case Primitive::kPrimFloat:
-      switch (input_type) {
-        case Primitive::kPrimBoolean:
-          // Boolean input is a result of code transformations.
-        case Primitive::kPrimByte:
-        case Primitive::kPrimShort:
-        case Primitive::kPrimInt:
-        case Primitive::kPrimChar: {
-          // Processing a Dex `int-to-float' instruction.
-          __ vmovsr(out.AsFpuRegister<SRegister>(), in.AsRegister<Register>());
-          __ vcvtsi(out.AsFpuRegister<SRegister>(), out.AsFpuRegister<SRegister>());
-          break;
-        }
-
-        case Primitive::kPrimLong:
-          // Processing a Dex `long-to-float' instruction.
-          codegen_->InvokeRuntime(kQuickL2f, conversion, conversion->GetDexPc());
-          CheckEntrypointTypes<kQuickL2f, float, int64_t>();
-          break;
-
-        case Primitive::kPrimDouble:
-          // Processing a Dex `double-to-float' instruction.
-          __ vcvtsd(out.AsFpuRegister<SRegister>(),
-                    FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
-          break;
-
-        default:
-          LOG(FATAL) << "Unexpected type conversion from " << input_type
-                     << " to " << result_type;
-      };
-      break;
-
-    case Primitive::kPrimDouble:
-      switch (input_type) {
-        case Primitive::kPrimBoolean:
-          // Boolean input is a result of code transformations.
-        case Primitive::kPrimByte:
-        case Primitive::kPrimShort:
-        case Primitive::kPrimInt:
-        case Primitive::kPrimChar: {
-          // Processing a Dex `int-to-double' instruction.
-          __ vmovsr(out.AsFpuRegisterPairLow<SRegister>(), in.AsRegister<Register>());
-          __ vcvtdi(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
-                    out.AsFpuRegisterPairLow<SRegister>());
-          break;
-        }
-
-        case Primitive::kPrimLong: {
-          // Processing a Dex `long-to-double' instruction.
-          Register low = in.AsRegisterPairLow<Register>();
-          Register high = in.AsRegisterPairHigh<Register>();
-          SRegister out_s = out.AsFpuRegisterPairLow<SRegister>();
-          DRegister out_d = FromLowSToD(out_s);
-          SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>();
-          DRegister temp_d = FromLowSToD(temp_s);
-          SRegister constant_s = locations->GetTemp(1).AsFpuRegisterPairLow<SRegister>();
-          DRegister constant_d = FromLowSToD(constant_s);
-
-          // temp_d = int-to-double(high)
-          __ vmovsr(temp_s, high);
-          __ vcvtdi(temp_d, temp_s);
-          // constant_d = k2Pow32EncodingForDouble
-          __ LoadDImmediate(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
-          // out_d = unsigned-to-double(low)
-          __ vmovsr(out_s, low);
-          __ vcvtdu(out_d, out_s);
-          // out_d += temp_d * constant_d
-          __ vmlad(out_d, temp_d, constant_d);
-          break;
-        }
-
-        case Primitive::kPrimFloat:
-          // Processing a Dex `float-to-double' instruction.
-          __ vcvtds(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
-                    in.AsFpuRegister<SRegister>());
-          break;
-
-        default:
-          LOG(FATAL) << "Unexpected type conversion from " << input_type
-                     << " to " << result_type;
-      };
-      break;
-
-    default:
-      LOG(FATAL) << "Unexpected type conversion from " << input_type
-                 << " to " << result_type;
-  }
-}
-
-void LocationsBuilderARM::VisitAdd(HAdd* add) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
-  switch (add->GetResultType()) {
-    case Primitive::kPrimInt: {
-      locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
-      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, ArmEncodableConstantOrRegister(add->InputAt(1), ADD));
-      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-      break;
-    }
-
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble: {
-      locations->SetInAt(0, Location::RequiresFpuRegister());
-      locations->SetInAt(1, Location::RequiresFpuRegister());
-      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
-      break;
-    }
-
-    default:
-      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) {
-  LocationSummary* locations = add->GetLocations();
-  Location out = locations->Out();
-  Location first = locations->InAt(0);
-  Location second = locations->InAt(1);
-  switch (add->GetResultType()) {
-    case Primitive::kPrimInt:
-      if (second.IsRegister()) {
-        __ add(out.AsRegister<Register>(),
-               first.AsRegister<Register>(),
-               ShifterOperand(second.AsRegister<Register>()));
-      } else {
-        __ AddConstant(out.AsRegister<Register>(),
-                       first.AsRegister<Register>(),
-                       second.GetConstant()->AsIntConstant()->GetValue());
-      }
-      break;
-
-    case Primitive::kPrimLong: {
-      if (second.IsConstant()) {
-        uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
-        GenerateAddLongConst(out, first, value);
-      } else {
-        DCHECK(second.IsRegisterPair());
-        __ adds(out.AsRegisterPairLow<Register>(),
-                first.AsRegisterPairLow<Register>(),
-                ShifterOperand(second.AsRegisterPairLow<Register>()));
-        __ adc(out.AsRegisterPairHigh<Register>(),
-               first.AsRegisterPairHigh<Register>(),
-               ShifterOperand(second.AsRegisterPairHigh<Register>()));
-      }
-      break;
-    }
-
-    case Primitive::kPrimFloat:
-      __ vadds(out.AsFpuRegister<SRegister>(),
-               first.AsFpuRegister<SRegister>(),
-               second.AsFpuRegister<SRegister>());
-      break;
-
-    case Primitive::kPrimDouble:
-      __ vaddd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
-               FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()),
-               FromLowSToD(second.AsFpuRegisterPairLow<SRegister>()));
-      break;
-
-    default:
-      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
-  }
-}
-
-void LocationsBuilderARM::VisitSub(HSub* sub) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
-  switch (sub->GetResultType()) {
-    case Primitive::kPrimInt: {
-      locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
-      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, ArmEncodableConstantOrRegister(sub->InputAt(1), SUB));
-      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-      break;
-    }
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble: {
-      locations->SetInAt(0, Location::RequiresFpuRegister());
-      locations->SetInAt(1, Location::RequiresFpuRegister());
-      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
-      break;
-    }
-    default:
-      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitSub(HSub* sub) {
-  LocationSummary* locations = sub->GetLocations();
-  Location out = locations->Out();
-  Location first = locations->InAt(0);
-  Location second = locations->InAt(1);
-  switch (sub->GetResultType()) {
-    case Primitive::kPrimInt: {
-      if (second.IsRegister()) {
-        __ sub(out.AsRegister<Register>(),
-               first.AsRegister<Register>(),
-               ShifterOperand(second.AsRegister<Register>()));
-      } else {
-        __ AddConstant(out.AsRegister<Register>(),
-                       first.AsRegister<Register>(),
-                       -second.GetConstant()->AsIntConstant()->GetValue());
-      }
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      if (second.IsConstant()) {
-        uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
-        GenerateAddLongConst(out, first, -value);
-      } else {
-        DCHECK(second.IsRegisterPair());
-        __ subs(out.AsRegisterPairLow<Register>(),
-                first.AsRegisterPairLow<Register>(),
-                ShifterOperand(second.AsRegisterPairLow<Register>()));
-        __ sbc(out.AsRegisterPairHigh<Register>(),
-               first.AsRegisterPairHigh<Register>(),
-               ShifterOperand(second.AsRegisterPairHigh<Register>()));
-      }
-      break;
-    }
-
-    case Primitive::kPrimFloat: {
-      __ vsubs(out.AsFpuRegister<SRegister>(),
-               first.AsFpuRegister<SRegister>(),
-               second.AsFpuRegister<SRegister>());
-      break;
-    }
-
-    case Primitive::kPrimDouble: {
-      __ vsubd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
-               FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()),
-               FromLowSToD(second.AsFpuRegisterPairLow<SRegister>()));
-      break;
-    }
-
-
-    default:
-      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
-  }
-}
-
-void LocationsBuilderARM::VisitMul(HMul* mul) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall);
-  switch (mul->GetResultType()) {
-    case Primitive::kPrimInt:
-    case Primitive::kPrimLong:  {
-      locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, Location::RequiresRegister());
-      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-      break;
-    }
-
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble: {
-      locations->SetInAt(0, Location::RequiresFpuRegister());
-      locations->SetInAt(1, Location::RequiresFpuRegister());
-      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
-      break;
-    }
-
-    default:
-      LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitMul(HMul* mul) {
-  LocationSummary* locations = mul->GetLocations();
-  Location out = locations->Out();
-  Location first = locations->InAt(0);
-  Location second = locations->InAt(1);
-  switch (mul->GetResultType()) {
-    case Primitive::kPrimInt: {
-      __ mul(out.AsRegister<Register>(),
-             first.AsRegister<Register>(),
-             second.AsRegister<Register>());
-      break;
-    }
-    case Primitive::kPrimLong: {
-      Register out_hi = out.AsRegisterPairHigh<Register>();
-      Register out_lo = out.AsRegisterPairLow<Register>();
-      Register in1_hi = first.AsRegisterPairHigh<Register>();
-      Register in1_lo = first.AsRegisterPairLow<Register>();
-      Register in2_hi = second.AsRegisterPairHigh<Register>();
-      Register in2_lo = second.AsRegisterPairLow<Register>();
-
-      // 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, in1_lo);
-      DCHECK_NE(out_hi, in2_lo);
-
-      // input: in1 - 64 bits, in2 - 64 bits
-      // output: out
-      // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
-      // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
-      // parts: out.lo = (in1.lo * in2.lo)[31:0]
-
-      // IP <- in1.lo * in2.hi
-      __ mul(IP, in1_lo, in2_hi);
-      // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo
-      __ mla(out_hi, in1_hi, in2_lo, IP);
-      // out.lo <- (in1.lo * in2.lo)[31:0];
-      __ umull(out_lo, IP, in1_lo, in2_lo);
-      // out.hi <- in2.hi * in1.lo +  in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
-      __ add(out_hi, out_hi, ShifterOperand(IP));
-      break;
-    }
-
-    case Primitive::kPrimFloat: {
-      __ vmuls(out.AsFpuRegister<SRegister>(),
-               first.AsFpuRegister<SRegister>(),
-               second.AsFpuRegister<SRegister>());
-      break;
-    }
-
-    case Primitive::kPrimDouble: {
-      __ vmuld(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
-               FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()),
-               FromLowSToD(second.AsFpuRegisterPairLow<SRegister>()));
-      break;
-    }
-
-    default:
-      LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
-  }
-}
-
-void InstructionCodeGeneratorARM::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
-  DCHECK(instruction->IsDiv() || instruction->IsRem());
-  DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
-
-  LocationSummary* locations = instruction->GetLocations();
-  Location second = locations->InAt(1);
-  DCHECK(second.IsConstant());
-
-  Register out = locations->Out().AsRegister<Register>();
-  Register dividend = locations->InAt(0).AsRegister<Register>();
-  int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
-  DCHECK(imm == 1 || imm == -1);
-
-  if (instruction->IsRem()) {
-    __ LoadImmediate(out, 0);
-  } else {
-    if (imm == 1) {
-      __ Mov(out, dividend);
-    } else {
-      __ rsb(out, dividend, ShifterOperand(0));
-    }
-  }
-}
-
-void InstructionCodeGeneratorARM::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
-  DCHECK(instruction->IsDiv() || instruction->IsRem());
-  DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
-
-  LocationSummary* locations = instruction->GetLocations();
-  Location second = locations->InAt(1);
-  DCHECK(second.IsConstant());
-
-  Register out = locations->Out().AsRegister<Register>();
-  Register dividend = locations->InAt(0).AsRegister<Register>();
-  Register temp = locations->GetTemp(0).AsRegister<Register>();
-  int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
-  uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
-  int ctz_imm = CTZ(abs_imm);
-
-  if (ctz_imm == 1) {
-    __ Lsr(temp, dividend, 32 - ctz_imm);
-  } else {
-    __ Asr(temp, dividend, 31);
-    __ Lsr(temp, temp, 32 - ctz_imm);
-  }
-  __ add(out, temp, ShifterOperand(dividend));
-
-  if (instruction->IsDiv()) {
-    __ Asr(out, out, ctz_imm);
-    if (imm < 0) {
-      __ rsb(out, out, ShifterOperand(0));
-    }
-  } else {
-    __ ubfx(out, out, 0, ctz_imm);
-    __ sub(out, out, ShifterOperand(temp));
-  }
-}
-
-void InstructionCodeGeneratorARM::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
-  DCHECK(instruction->IsDiv() || instruction->IsRem());
-  DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
-
-  LocationSummary* locations = instruction->GetLocations();
-  Location second = locations->InAt(1);
-  DCHECK(second.IsConstant());
-
-  Register out = locations->Out().AsRegister<Register>();
-  Register dividend = locations->InAt(0).AsRegister<Register>();
-  Register temp1 = locations->GetTemp(0).AsRegister<Register>();
-  Register temp2 = locations->GetTemp(1).AsRegister<Register>();
-  int64_t imm = second.GetConstant()->AsIntConstant()->GetValue();
-
-  int64_t magic;
-  int shift;
-  CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
-
-  __ LoadImmediate(temp1, magic);
-  __ smull(temp2, temp1, dividend, temp1);
-
-  if (imm > 0 && magic < 0) {
-    __ add(temp1, temp1, ShifterOperand(dividend));
-  } else if (imm < 0 && magic > 0) {
-    __ sub(temp1, temp1, ShifterOperand(dividend));
-  }
-
-  if (shift != 0) {
-    __ Asr(temp1, temp1, shift);
-  }
-
-  if (instruction->IsDiv()) {
-    __ sub(out, temp1, ShifterOperand(temp1, ASR, 31));
-  } else {
-    __ sub(temp1, temp1, ShifterOperand(temp1, ASR, 31));
-    // TODO: Strength reduction for mls.
-    __ LoadImmediate(temp2, imm);
-    __ mls(out, temp1, temp2, dividend);
-  }
-}
-
-void InstructionCodeGeneratorARM::GenerateDivRemConstantIntegral(HBinaryOperation* instruction) {
-  DCHECK(instruction->IsDiv() || instruction->IsRem());
-  DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
-
-  LocationSummary* locations = instruction->GetLocations();
-  Location second = locations->InAt(1);
-  DCHECK(second.IsConstant());
-
-  int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
-  if (imm == 0) {
-    // Do not generate anything. DivZeroCheck would prevent any code to be executed.
-  } else if (imm == 1 || imm == -1) {
-    DivRemOneOrMinusOne(instruction);
-  } else if (IsPowerOfTwo(AbsOrMin(imm))) {
-    DivRemByPowerOfTwo(instruction);
-  } else {
-    DCHECK(imm <= -2 || imm >= 2);
-    GenerateDivRemWithAnyConstant(instruction);
-  }
-}
-
-void LocationsBuilderARM::VisitDiv(HDiv* div) {
-  LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
-  if (div->GetResultType() == Primitive::kPrimLong) {
-    // pLdiv runtime call.
-    call_kind = LocationSummary::kCallOnMainOnly;
-  } else if (div->GetResultType() == Primitive::kPrimInt && div->InputAt(1)->IsConstant()) {
-    // sdiv will be replaced by other instruction sequence.
-  } else if (div->GetResultType() == Primitive::kPrimInt &&
-             !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
-    // pIdivmod runtime call.
-    call_kind = LocationSummary::kCallOnMainOnly;
-  }
-
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, call_kind);
-
-  switch (div->GetResultType()) {
-    case Primitive::kPrimInt: {
-      if (div->InputAt(1)->IsConstant()) {
-        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();
-        if (value == 1 || value == 0 || value == -1) {
-          // No temp register required.
-        } else {
-          locations->AddTemp(Location::RequiresRegister());
-          if (!IsPowerOfTwo(AbsOrMin(value))) {
-            locations->AddTemp(Location::RequiresRegister());
-          }
-        }
-      } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
-        locations->SetInAt(0, Location::RequiresRegister());
-        locations->SetInAt(1, Location::RequiresRegister());
-        locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-      } else {
-        InvokeRuntimeCallingConvention calling_convention;
-        locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-        locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
-        // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
-        //       we only need the former.
-        locations->SetOut(Location::RegisterLocation(R0));
-      }
-      break;
-    }
-    case Primitive::kPrimLong: {
-      InvokeRuntimeCallingConvention calling_convention;
-      locations->SetInAt(0, Location::RegisterPairLocation(
-          calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
-      locations->SetInAt(1, Location::RegisterPairLocation(
-          calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
-      locations->SetOut(Location::RegisterPairLocation(R0, R1));
-      break;
-    }
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble: {
-      locations->SetInAt(0, Location::RequiresFpuRegister());
-      locations->SetInAt(1, Location::RequiresFpuRegister());
-      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
-      break;
-    }
-
-    default:
-      LOG(FATAL) << "Unexpected div type " << div->GetResultType();
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) {
-  LocationSummary* locations = div->GetLocations();
-  Location out = locations->Out();
-  Location first = locations->InAt(0);
-  Location second = locations->InAt(1);
-
-  switch (div->GetResultType()) {
-    case Primitive::kPrimInt: {
-      if (second.IsConstant()) {
-        GenerateDivRemConstantIntegral(div);
-      } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
-        __ sdiv(out.AsRegister<Register>(),
-                first.AsRegister<Register>(),
-                second.AsRegister<Register>());
-      } else {
-        InvokeRuntimeCallingConvention calling_convention;
-        DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>());
-        DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>());
-        DCHECK_EQ(R0, out.AsRegister<Register>());
-
-        codegen_->InvokeRuntime(kQuickIdivmod, div, div->GetDexPc());
-        CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
-      }
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      InvokeRuntimeCallingConvention calling_convention;
-      DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>());
-      DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>());
-      DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegisterPairLow<Register>());
-      DCHECK_EQ(calling_convention.GetRegisterAt(3), second.AsRegisterPairHigh<Register>());
-      DCHECK_EQ(R0, out.AsRegisterPairLow<Register>());
-      DCHECK_EQ(R1, out.AsRegisterPairHigh<Register>());
-
-      codegen_->InvokeRuntime(kQuickLdiv, div, div->GetDexPc());
-      CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>();
-      break;
-    }
-
-    case Primitive::kPrimFloat: {
-      __ vdivs(out.AsFpuRegister<SRegister>(),
-               first.AsFpuRegister<SRegister>(),
-               second.AsFpuRegister<SRegister>());
-      break;
-    }
-
-    case Primitive::kPrimDouble: {
-      __ vdivd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
-               FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()),
-               FromLowSToD(second.AsFpuRegisterPairLow<SRegister>()));
-      break;
-    }
-
-    default:
-      LOG(FATAL) << "Unexpected div type " << div->GetResultType();
-  }
-}
-
-void LocationsBuilderARM::VisitRem(HRem* rem) {
-  Primitive::Type type = rem->GetResultType();
-
-  // Most remainders are implemented in the runtime.
-  LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly;
-  if (rem->GetResultType() == Primitive::kPrimInt && rem->InputAt(1)->IsConstant()) {
-    // sdiv will be replaced by other instruction sequence.
-    call_kind = LocationSummary::kNoCall;
-  } else if ((rem->GetResultType() == Primitive::kPrimInt)
-             && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
-    // Have hardware divide instruction for int, do it with three instructions.
-    call_kind = LocationSummary::kNoCall;
-  }
-
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind);
-
-  switch (type) {
-    case Primitive::kPrimInt: {
-      if (rem->InputAt(1)->IsConstant()) {
-        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();
-        if (value == 1 || value == 0 || value == -1) {
-          // No temp register required.
-        } else {
-          locations->AddTemp(Location::RequiresRegister());
-          if (!IsPowerOfTwo(AbsOrMin(value))) {
-            locations->AddTemp(Location::RequiresRegister());
-          }
-        }
-      } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
-        locations->SetInAt(0, Location::RequiresRegister());
-        locations->SetInAt(1, Location::RequiresRegister());
-        locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-        locations->AddTemp(Location::RequiresRegister());
-      } else {
-        InvokeRuntimeCallingConvention calling_convention;
-        locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-        locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
-        // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
-        //       we only need the latter.
-        locations->SetOut(Location::RegisterLocation(R1));
-      }
-      break;
-    }
-    case Primitive::kPrimLong: {
-      InvokeRuntimeCallingConvention calling_convention;
-      locations->SetInAt(0, Location::RegisterPairLocation(
-          calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
-      locations->SetInAt(1, Location::RegisterPairLocation(
-          calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
-      // The runtime helper puts the output in R2,R3.
-      locations->SetOut(Location::RegisterPairLocation(R2, R3));
-      break;
-    }
-    case Primitive::kPrimFloat: {
-      InvokeRuntimeCallingConvention calling_convention;
-      locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
-      locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
-      locations->SetOut(Location::FpuRegisterLocation(S0));
-      break;
-    }
-
-    case Primitive::kPrimDouble: {
-      InvokeRuntimeCallingConvention calling_convention;
-      locations->SetInAt(0, Location::FpuRegisterPairLocation(
-          calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1)));
-      locations->SetInAt(1, Location::FpuRegisterPairLocation(
-          calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3)));
-      locations->SetOut(Location::Location::FpuRegisterPairLocation(S0, S1));
-      break;
-    }
-
-    default:
-      LOG(FATAL) << "Unexpected rem type " << type;
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitRem(HRem* rem) {
-  LocationSummary* locations = rem->GetLocations();
-  Location out = locations->Out();
-  Location first = locations->InAt(0);
-  Location second = locations->InAt(1);
-
-  Primitive::Type type = rem->GetResultType();
-  switch (type) {
-    case Primitive::kPrimInt: {
-        if (second.IsConstant()) {
-          GenerateDivRemConstantIntegral(rem);
-        } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
-        Register reg1 = first.AsRegister<Register>();
-        Register reg2 = second.AsRegister<Register>();
-        Register temp = locations->GetTemp(0).AsRegister<Register>();
-
-        // temp = reg1 / reg2  (integer division)
-        // dest = reg1 - temp * reg2
-        __ sdiv(temp, reg1, reg2);
-        __ mls(out.AsRegister<Register>(), temp, reg2, reg1);
-      } else {
-        InvokeRuntimeCallingConvention calling_convention;
-        DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>());
-        DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>());
-        DCHECK_EQ(R1, out.AsRegister<Register>());
-
-        codegen_->InvokeRuntime(kQuickIdivmod, rem, rem->GetDexPc());
-        CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
-      }
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      codegen_->InvokeRuntime(kQuickLmod, rem, rem->GetDexPc());
-        CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>();
-      break;
-    }
-
-    case Primitive::kPrimFloat: {
-      codegen_->InvokeRuntime(kQuickFmodf, rem, rem->GetDexPc());
-      CheckEntrypointTypes<kQuickFmodf, float, float, float>();
-      break;
-    }
-
-    case Primitive::kPrimDouble: {
-      codegen_->InvokeRuntime(kQuickFmod, rem, rem->GetDexPc());
-      CheckEntrypointTypes<kQuickFmod, double, double, double>();
-      break;
-    }
-
-    default:
-      LOG(FATAL) << "Unexpected rem type " << type;
-  }
-}
-
-void LocationsBuilderARM::VisitDivZeroCheck(HDivZeroCheck* instruction) {
-  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
-  locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
-}
-
-void InstructionCodeGeneratorARM::VisitDivZeroCheck(HDivZeroCheck* instruction) {
-  SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) DivZeroCheckSlowPathARM(instruction);
-  codegen_->AddSlowPath(slow_path);
-
-  LocationSummary* locations = instruction->GetLocations();
-  Location value = locations->InAt(0);
-
-  switch (instruction->GetType()) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-    case Primitive::kPrimInt: {
-      if (value.IsRegister()) {
-        __ CompareAndBranchIfZero(value.AsRegister<Register>(), slow_path->GetEntryLabel());
-      } else {
-        DCHECK(value.IsConstant()) << value;
-        if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
-          __ b(slow_path->GetEntryLabel());
-        }
-      }
-      break;
-    }
-    case Primitive::kPrimLong: {
-      if (value.IsRegisterPair()) {
-        __ orrs(IP,
-                value.AsRegisterPairLow<Register>(),
-                ShifterOperand(value.AsRegisterPairHigh<Register>()));
-        __ b(slow_path->GetEntryLabel(), EQ);
-      } else {
-        DCHECK(value.IsConstant()) << value;
-        if (value.GetConstant()->AsLongConstant()->GetValue() == 0) {
-          __ b(slow_path->GetEntryLabel());
-        }
-      }
-      break;
-    default:
-      LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType();
-    }
-  }
-}
-
-void InstructionCodeGeneratorARM::HandleIntegerRotate(LocationSummary* locations) {
-  Register in = locations->InAt(0).AsRegister<Register>();
-  Location rhs = locations->InAt(1);
-  Register out = locations->Out().AsRegister<Register>();
-
-  if (rhs.IsConstant()) {
-    // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
-    // so map all rotations to a +ve. equivalent in that range.
-    // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
-    uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F;
-    if (rot) {
-      // Rotate, mapping left rotations to right equivalents if necessary.
-      // (e.g. left by 2 bits == right by 30.)
-      __ Ror(out, in, rot);
-    } else if (out != in) {
-      __ Mov(out, in);
-    }
-  } else {
-    __ Ror(out, in, rhs.AsRegister<Register>());
-  }
-}
-
-// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
-// rotates by swapping input regs (effectively rotating by the first 32-bits of
-// a larger rotation) or flipping direction (thus treating larger right/left
-// rotations as sub-word sized rotations in the other direction) as appropriate.
-void InstructionCodeGeneratorARM::HandleLongRotate(HRor* ror) {
-  LocationSummary* locations = ror->GetLocations();
-  Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
-  Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
-  Location rhs = locations->InAt(1);
-  Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
-  Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
-
-  if (rhs.IsConstant()) {
-    uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
-    // Map all rotations to +ve. equivalents on the interval [0,63].
-    rot &= kMaxLongShiftDistance;
-    // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
-    // logic below to a simple pair of binary orr.
-    // (e.g. 34 bits == in_reg swap + 2 bits right.)
-    if (rot >= kArmBitsPerWord) {
-      rot -= kArmBitsPerWord;
-      std::swap(in_reg_hi, in_reg_lo);
-    }
-    // Rotate, or mov to out for zero or word size rotations.
-    if (rot != 0u) {
-      __ Lsr(out_reg_hi, in_reg_hi, rot);
-      __ orr(out_reg_hi, out_reg_hi, ShifterOperand(in_reg_lo, arm::LSL, kArmBitsPerWord - rot));
-      __ Lsr(out_reg_lo, in_reg_lo, rot);
-      __ orr(out_reg_lo, out_reg_lo, ShifterOperand(in_reg_hi, arm::LSL, kArmBitsPerWord - rot));
-    } else {
-      __ Mov(out_reg_lo, in_reg_lo);
-      __ Mov(out_reg_hi, in_reg_hi);
-    }
-  } else {
-    Register shift_right = locations->GetTemp(0).AsRegister<Register>();
-    Register shift_left = locations->GetTemp(1).AsRegister<Register>();
-    Label end;
-    Label shift_by_32_plus_shift_right;
-    Label* final_label = codegen_->GetFinalLabel(ror, &end);
-
-    __ and_(shift_right, rhs.AsRegister<Register>(), ShifterOperand(0x1F));
-    __ Lsrs(shift_left, rhs.AsRegister<Register>(), 6);
-    __ rsb(shift_left, shift_right, ShifterOperand(kArmBitsPerWord), AL, kCcKeep);
-    __ b(&shift_by_32_plus_shift_right, CC);
-
-    // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
-    // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
-    __ Lsl(out_reg_hi, in_reg_hi, shift_left);
-    __ Lsr(out_reg_lo, in_reg_lo, shift_right);
-    __ add(out_reg_hi, out_reg_hi, ShifterOperand(out_reg_lo));
-    __ Lsl(out_reg_lo, in_reg_lo, shift_left);
-    __ Lsr(shift_left, in_reg_hi, shift_right);
-    __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_left));
-    __ b(final_label);
-
-    __ Bind(&shift_by_32_plus_shift_right);  // Shift by 32+shift_right.
-    // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
-    // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
-    __ Lsr(out_reg_hi, in_reg_hi, shift_right);
-    __ Lsl(out_reg_lo, in_reg_lo, shift_left);
-    __ add(out_reg_hi, out_reg_hi, ShifterOperand(out_reg_lo));
-    __ Lsr(out_reg_lo, in_reg_lo, shift_right);
-    __ Lsl(shift_right, in_reg_hi, shift_left);
-    __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_right));
-
-    if (end.IsLinked()) {
-      __ Bind(&end);
-    }
-  }
-}
-
-void LocationsBuilderARM::VisitRor(HRor* ror) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(ror, LocationSummary::kNoCall);
-  switch (ror->GetResultType()) {
-    case Primitive::kPrimInt: {
-      locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1)));
-      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-      break;
-    }
-    case Primitive::kPrimLong: {
-      locations->SetInAt(0, Location::RequiresRegister());
-      if (ror->InputAt(1)->IsConstant()) {
-        locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant()));
-      } else {
-        locations->SetInAt(1, Location::RequiresRegister());
-        locations->AddTemp(Location::RequiresRegister());
-        locations->AddTemp(Location::RequiresRegister());
-      }
-      locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-      break;
-    }
-    default:
-      LOG(FATAL) << "Unexpected operation type " << ror->GetResultType();
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitRor(HRor* ror) {
-  LocationSummary* locations = ror->GetLocations();
-  Primitive::Type type = ror->GetResultType();
-  switch (type) {
-    case Primitive::kPrimInt: {
-      HandleIntegerRotate(locations);
-      break;
-    }
-    case Primitive::kPrimLong: {
-      HandleLongRotate(ror);
-      break;
-    }
-    default:
-      LOG(FATAL) << "Unexpected operation type " << type;
-      UNREACHABLE();
-  }
-}
-
-void LocationsBuilderARM::HandleShift(HBinaryOperation* op) {
-  DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
-
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall);
-
-  switch (op->GetResultType()) {
-    case Primitive::kPrimInt: {
-      locations->SetInAt(0, Location::RequiresRegister());
-      if (op->InputAt(1)->IsConstant()) {
-        locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
-        locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-      } else {
-        locations->SetInAt(1, Location::RequiresRegister());
-        // Make the output overlap, as it will be used to hold the masked
-        // second input.
-        locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-      }
-      break;
-    }
-    case Primitive::kPrimLong: {
-      locations->SetInAt(0, Location::RequiresRegister());
-      if (op->InputAt(1)->IsConstant()) {
-        locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
-        // For simplicity, use kOutputOverlap even though we only require that low registers
-        // don't clash with high registers which the register allocator currently guarantees.
-        locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-      } else {
-        locations->SetInAt(1, Location::RequiresRegister());
-        locations->AddTemp(Location::RequiresRegister());
-        locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-      }
-      break;
-    }
-    default:
-      LOG(FATAL) << "Unexpected operation type " << op->GetResultType();
-  }
-}
-
-void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) {
-  DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
-
-  LocationSummary* locations = op->GetLocations();
-  Location out = locations->Out();
-  Location first = locations->InAt(0);
-  Location second = locations->InAt(1);
-
-  Primitive::Type type = op->GetResultType();
-  switch (type) {
-    case Primitive::kPrimInt: {
-      Register out_reg = out.AsRegister<Register>();
-      Register first_reg = first.AsRegister<Register>();
-      if (second.IsRegister()) {
-        Register second_reg = second.AsRegister<Register>();
-        // ARM doesn't mask the shift count so we need to do it ourselves.
-        __ and_(out_reg, second_reg, ShifterOperand(kMaxIntShiftDistance));
-        if (op->IsShl()) {
-          __ Lsl(out_reg, first_reg, out_reg);
-        } else if (op->IsShr()) {
-          __ Asr(out_reg, first_reg, out_reg);
-        } else {
-          __ Lsr(out_reg, first_reg, out_reg);
-        }
-      } else {
-        int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
-        uint32_t shift_value = cst & kMaxIntShiftDistance;
-        if (shift_value == 0) {  // ARM does not support shifting with 0 immediate.
-          __ Mov(out_reg, first_reg);
-        } else if (op->IsShl()) {
-          __ Lsl(out_reg, first_reg, shift_value);
-        } else if (op->IsShr()) {
-          __ Asr(out_reg, first_reg, shift_value);
-        } else {
-          __ Lsr(out_reg, first_reg, shift_value);
-        }
-      }
-      break;
-    }
-    case Primitive::kPrimLong: {
-      Register o_h = out.AsRegisterPairHigh<Register>();
-      Register o_l = out.AsRegisterPairLow<Register>();
-
-      Register high = first.AsRegisterPairHigh<Register>();
-      Register low = first.AsRegisterPairLow<Register>();
-
-      if (second.IsRegister()) {
-        Register temp = locations->GetTemp(0).AsRegister<Register>();
-
-        Register second_reg = second.AsRegister<Register>();
-
-        if (op->IsShl()) {
-          __ and_(o_l, second_reg, ShifterOperand(kMaxLongShiftDistance));
-          // Shift the high part
-          __ Lsl(o_h, high, o_l);
-          // Shift the low part and `or` what overflew on the high part
-          __ rsb(temp, o_l, ShifterOperand(kArmBitsPerWord));
-          __ Lsr(temp, low, temp);
-          __ orr(o_h, o_h, ShifterOperand(temp));
-          // If the shift is > 32 bits, override the high part
-          __ subs(temp, o_l, ShifterOperand(kArmBitsPerWord));
-          __ it(PL);
-          __ Lsl(o_h, low, temp, PL);
-          // Shift the low part
-          __ Lsl(o_l, low, o_l);
-        } else if (op->IsShr()) {
-          __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftDistance));
-          // Shift the low part
-          __ Lsr(o_l, low, o_h);
-          // Shift the high part and `or` what underflew on the low part
-          __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord));
-          __ Lsl(temp, high, temp);
-          __ orr(o_l, o_l, ShifterOperand(temp));
-          // If the shift is > 32 bits, override the low part
-          __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord));
-          __ it(PL);
-          __ Asr(o_l, high, temp, PL);
-          // Shift the high part
-          __ Asr(o_h, high, o_h);
-        } else {
-          __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftDistance));
-          // same as Shr except we use `Lsr`s and not `Asr`s
-          __ Lsr(o_l, low, o_h);
-          __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord));
-          __ Lsl(temp, high, temp);
-          __ orr(o_l, o_l, ShifterOperand(temp));
-          __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord));
-          __ it(PL);
-          __ Lsr(o_l, high, temp, PL);
-          __ Lsr(o_h, high, o_h);
-        }
-      } else {
-        // Register allocator doesn't create partial overlap.
-        DCHECK_NE(o_l, high);
-        DCHECK_NE(o_h, low);
-        int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
-        uint32_t shift_value = cst & kMaxLongShiftDistance;
-        if (shift_value > 32) {
-          if (op->IsShl()) {
-            __ Lsl(o_h, low, shift_value - 32);
-            __ LoadImmediate(o_l, 0);
-          } else if (op->IsShr()) {
-            __ Asr(o_l, high, shift_value - 32);
-            __ Asr(o_h, high, 31);
-          } else {
-            __ Lsr(o_l, high, shift_value - 32);
-            __ LoadImmediate(o_h, 0);
-          }
-        } else if (shift_value == 32) {
-          if (op->IsShl()) {
-            __ mov(o_h, ShifterOperand(low));
-            __ LoadImmediate(o_l, 0);
-          } else if (op->IsShr()) {
-            __ mov(o_l, ShifterOperand(high));
-            __ Asr(o_h, high, 31);
-          } else {
-            __ mov(o_l, ShifterOperand(high));
-            __ LoadImmediate(o_h, 0);
-          }
-        } else if (shift_value == 1) {
-          if (op->IsShl()) {
-            __ Lsls(o_l, low, 1);
-            __ adc(o_h, high, ShifterOperand(high));
-          } else if (op->IsShr()) {
-            __ Asrs(o_h, high, 1);
-            __ Rrx(o_l, low);
-          } else {
-            __ Lsrs(o_h, high, 1);
-            __ Rrx(o_l, low);
-          }
-        } else {
-          DCHECK(2 <= shift_value && shift_value < 32) << shift_value;
-          if (op->IsShl()) {
-            __ Lsl(o_h, high, shift_value);
-            __ orr(o_h, o_h, ShifterOperand(low, LSR, 32 - shift_value));
-            __ Lsl(o_l, low, shift_value);
-          } else if (op->IsShr()) {
-            __ Lsr(o_l, low, shift_value);
-            __ orr(o_l, o_l, ShifterOperand(high, LSL, 32 - shift_value));
-            __ Asr(o_h, high, shift_value);
-          } else {
-            __ Lsr(o_l, low, shift_value);
-            __ orr(o_l, o_l, ShifterOperand(high, LSL, 32 - shift_value));
-            __ Lsr(o_h, high, shift_value);
-          }
-        }
-      }
-      break;
-    }
-    default:
-      LOG(FATAL) << "Unexpected operation type " << type;
-      UNREACHABLE();
-  }
-}
-
-void LocationsBuilderARM::VisitShl(HShl* shl) {
-  HandleShift(shl);
-}
-
-void InstructionCodeGeneratorARM::VisitShl(HShl* shl) {
-  HandleShift(shl);
-}
-
-void LocationsBuilderARM::VisitShr(HShr* shr) {
-  HandleShift(shr);
-}
-
-void InstructionCodeGeneratorARM::VisitShr(HShr* shr) {
-  HandleShift(shr);
-}
-
-void LocationsBuilderARM::VisitUShr(HUShr* ushr) {
-  HandleShift(ushr);
-}
-
-void InstructionCodeGeneratorARM::VisitUShr(HUShr* ushr) {
-  HandleShift(ushr);
-}
-
-void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
-  if (instruction->IsStringAlloc()) {
-    locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument));
-  } else {
-    InvokeRuntimeCallingConvention calling_convention;
-    locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  }
-  locations->SetOut(Location::RegisterLocation(R0));
-}
-
-void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) {
-  // Note: if heap poisoning is enabled, the entry point takes cares
-  // of poisoning the reference.
-  if (instruction->IsStringAlloc()) {
-    // String is allocated through StringFactory. Call NewEmptyString entry point.
-    Register temp = instruction->GetLocations()->GetTemp(0).AsRegister<Register>();
-    MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
-    __ LoadFromOffset(kLoadWord, temp, TR, QUICK_ENTRY_POINT(pNewEmptyString));
-    __ LoadFromOffset(kLoadWord, LR, temp, code_offset.Int32Value());
-    __ blx(LR);
-    codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
-  } else {
-    codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
-    CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
-  }
-}
-
-void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
-  InvokeRuntimeCallingConvention calling_convention;
-  locations->SetOut(Location::RegisterLocation(R0));
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
-}
-
-void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) {
-  // Note: if heap poisoning is enabled, the entry point takes cares
-  // of poisoning the reference.
-  QuickEntrypointEnum entrypoint =
-      CodeGenerator::GetArrayAllocationEntrypoint(instruction->GetLoadClass()->GetClass());
-  codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
-  CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
-  DCHECK(!codegen_->IsLeafMethod());
-}
-
-void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
-  if (location.IsStackSlot()) {
-    location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
-  } else if (location.IsDoubleStackSlot()) {
-    location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
-  }
-  locations->SetOut(location);
-}
-
-void InstructionCodeGeneratorARM::VisitParameterValue(
-    HParameterValue* instruction ATTRIBUTE_UNUSED) {
-  // Nothing to do, the parameter is already at its location.
-}
-
-void LocationsBuilderARM::VisitCurrentMethod(HCurrentMethod* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  locations->SetOut(Location::RegisterLocation(kMethodRegisterArgument));
-}
-
-void InstructionCodeGeneratorARM::VisitCurrentMethod(HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
-  // Nothing to do, the method is already at its location.
-}
-
-void LocationsBuilderARM::VisitNot(HNot* not_) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void InstructionCodeGeneratorARM::VisitNot(HNot* not_) {
-  LocationSummary* locations = not_->GetLocations();
-  Location out = locations->Out();
-  Location in = locations->InAt(0);
-  switch (not_->GetResultType()) {
-    case Primitive::kPrimInt:
-      __ mvn(out.AsRegister<Register>(), ShifterOperand(in.AsRegister<Register>()));
-      break;
-
-    case Primitive::kPrimLong:
-      __ mvn(out.AsRegisterPairLow<Register>(),
-             ShifterOperand(in.AsRegisterPairLow<Register>()));
-      __ mvn(out.AsRegisterPairHigh<Register>(),
-             ShifterOperand(in.AsRegisterPairHigh<Register>()));
-      break;
-
-    default:
-      LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
-  }
-}
-
-void LocationsBuilderARM::VisitBooleanNot(HBooleanNot* bool_not) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(bool_not, LocationSummary::kNoCall);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void InstructionCodeGeneratorARM::VisitBooleanNot(HBooleanNot* bool_not) {
-  LocationSummary* locations = bool_not->GetLocations();
-  Location out = locations->Out();
-  Location in = locations->InAt(0);
-  __ eor(out.AsRegister<Register>(), in.AsRegister<Register>(), ShifterOperand(1));
-}
-
-void LocationsBuilderARM::VisitCompare(HCompare* compare) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
-  switch (compare->InputAt(0)->GetType()) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimShort:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimInt:
-    case Primitive::kPrimLong: {
-      locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, Location::RequiresRegister());
-      // Output overlaps because it is written before doing the low comparison.
-      locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-      break;
-    }
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble: {
-      locations->SetInAt(0, Location::RequiresFpuRegister());
-      locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
-      locations->SetOut(Location::RequiresRegister());
-      break;
-    }
-    default:
-      LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) {
-  LocationSummary* locations = compare->GetLocations();
-  Register out = locations->Out().AsRegister<Register>();
-  Location left = locations->InAt(0);
-  Location right = locations->InAt(1);
-
-  Label less, greater, done;
-  Label* final_label = codegen_->GetFinalLabel(compare, &done);
-  Primitive::Type type = compare->InputAt(0)->GetType();
-  Condition less_cond;
-  switch (type) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimShort:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimInt: {
-      __ LoadImmediate(out, 0);
-      __ cmp(left.AsRegister<Register>(),
-             ShifterOperand(right.AsRegister<Register>()));  // Signed compare.
-      less_cond = LT;
-      break;
-    }
-    case Primitive::kPrimLong: {
-      __ cmp(left.AsRegisterPairHigh<Register>(),
-             ShifterOperand(right.AsRegisterPairHigh<Register>()));  // Signed compare.
-      __ b(&less, LT);
-      __ b(&greater, GT);
-      // Do LoadImmediate before the last `cmp`, as LoadImmediate might affect the status flags.
-      __ LoadImmediate(out, 0);
-      __ cmp(left.AsRegisterPairLow<Register>(),
-             ShifterOperand(right.AsRegisterPairLow<Register>()));  // Unsigned compare.
-      less_cond = LO;
-      break;
-    }
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble: {
-      __ LoadImmediate(out, 0);
-      GenerateVcmp(compare, codegen_);
-      __ vmstat();  // transfer FP status register to ARM APSR.
-      less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
-      break;
-    }
-    default:
-      LOG(FATAL) << "Unexpected compare type " << type;
-      UNREACHABLE();
-  }
-
-  __ b(final_label, EQ);
-  __ b(&less, less_cond);
-
-  __ Bind(&greater);
-  __ LoadImmediate(out, 1);
-  __ b(final_label);
-
-  __ Bind(&less);
-  __ LoadImmediate(out, -1);
-
-  if (done.IsLinked()) {
-    __ Bind(&done);
-  }
-}
-
-void LocationsBuilderARM::VisitPhi(HPhi* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
-    locations->SetInAt(i, Location::Any());
-  }
-  locations->SetOut(Location::Any());
-}
-
-void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
-  LOG(FATAL) << "Unreachable";
-}
-
-void CodeGeneratorARM::GenerateMemoryBarrier(MemBarrierKind kind) {
-  // TODO (ported from quick): revisit ARM barrier kinds.
-  DmbOptions flavor = DmbOptions::ISH;  // Quiet C++ warnings.
-  switch (kind) {
-    case MemBarrierKind::kAnyStore:
-    case MemBarrierKind::kLoadAny:
-    case MemBarrierKind::kAnyAny: {
-      flavor = DmbOptions::ISH;
-      break;
-    }
-    case MemBarrierKind::kStoreStore: {
-      flavor = DmbOptions::ISHST;
-      break;
-    }
-    default:
-      LOG(FATAL) << "Unexpected memory barrier " << kind;
-  }
-  __ dmb(flavor);
-}
-
-void InstructionCodeGeneratorARM::GenerateWideAtomicLoad(Register addr,
-                                                         uint32_t offset,
-                                                         Register out_lo,
-                                                         Register out_hi) {
-  if (offset != 0) {
-    // Ensure `out_lo` is different from `addr`, so that loading
-    // `offset` into `out_lo` does not clutter `addr`.
-    DCHECK_NE(out_lo, addr);
-    __ LoadImmediate(out_lo, offset);
-    __ add(IP, addr, ShifterOperand(out_lo));
-    addr = IP;
-  }
-  __ ldrexd(out_lo, out_hi, addr);
-}
-
-void InstructionCodeGeneratorARM::GenerateWideAtomicStore(Register addr,
-                                                          uint32_t offset,
-                                                          Register value_lo,
-                                                          Register value_hi,
-                                                          Register temp1,
-                                                          Register temp2,
-                                                          HInstruction* instruction) {
-  Label fail;
-  if (offset != 0) {
-    __ LoadImmediate(temp1, offset);
-    __ add(IP, addr, ShifterOperand(temp1));
-    addr = IP;
-  }
-  __ Bind(&fail);
-  // We need a load followed by store. (The address used in a STREX instruction must
-  // be the same as the address in the most recently executed LDREX instruction.)
-  __ ldrexd(temp1, temp2, addr);
-  codegen_->MaybeRecordImplicitNullCheck(instruction);
-  __ strexd(temp1, value_lo, value_hi, addr);
-  __ CompareAndBranchIfNonZero(temp1, &fail);
-}
-
-void LocationsBuilderARM::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info) {
-  DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
-
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  locations->SetInAt(0, Location::RequiresRegister());
-
-  Primitive::Type field_type = field_info.GetFieldType();
-  if (Primitive::IsFloatingPointType(field_type)) {
-    locations->SetInAt(1, Location::RequiresFpuRegister());
-  } else {
-    locations->SetInAt(1, Location::RequiresRegister());
-  }
-
-  bool is_wide = field_type == Primitive::kPrimLong || field_type == Primitive::kPrimDouble;
-  bool generate_volatile = field_info.IsVolatile()
-      && is_wide
-      && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
-  bool needs_write_barrier =
-      CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
-  // Temporary registers for the write barrier.
-  // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
-  if (needs_write_barrier) {
-    locations->AddTemp(Location::RequiresRegister());  // Possibly used for reference poisoning too.
-    locations->AddTemp(Location::RequiresRegister());
-  } else if (generate_volatile) {
-    // ARM encoding have some additional constraints for ldrexd/strexd:
-    // - registers need to be consecutive
-    // - the first register should be even but not R14.
-    // We don't test for ARM yet, and the assertion makes sure that we
-    // revisit this if we ever enable ARM encoding.
-    DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
-
-    locations->AddTemp(Location::RequiresRegister());
-    locations->AddTemp(Location::RequiresRegister());
-    if (field_type == Primitive::kPrimDouble) {
-      // For doubles we need two more registers to copy the value.
-      locations->AddTemp(Location::RegisterLocation(R2));
-      locations->AddTemp(Location::RegisterLocation(R3));
-    }
-  }
-}
-
-void InstructionCodeGeneratorARM::HandleFieldSet(HInstruction* instruction,
-                                                 const FieldInfo& field_info,
-                                                 bool value_can_be_null) {
-  DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
-
-  LocationSummary* locations = instruction->GetLocations();
-  Register base = locations->InAt(0).AsRegister<Register>();
-  Location value = locations->InAt(1);
-
-  bool is_volatile = field_info.IsVolatile();
-  bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
-  Primitive::Type field_type = field_info.GetFieldType();
-  uint32_t offset = field_info.GetFieldOffset().Uint32Value();
-  bool needs_write_barrier =
-      CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
-
-  if (is_volatile) {
-    codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
-  }
-
-  switch (field_type) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte: {
-      __ StoreToOffset(kStoreByte, value.AsRegister<Register>(), base, offset);
-      break;
-    }
-
-    case Primitive::kPrimShort:
-    case Primitive::kPrimChar: {
-      __ StoreToOffset(kStoreHalfword, value.AsRegister<Register>(), base, offset);
-      break;
-    }
-
-    case Primitive::kPrimInt:
-    case Primitive::kPrimNot: {
-      if (kPoisonHeapReferences && needs_write_barrier) {
-        // Note that in the case where `value` is a null reference,
-        // we do not enter this block, as a null reference does not
-        // need poisoning.
-        DCHECK_EQ(field_type, Primitive::kPrimNot);
-        Register temp = locations->GetTemp(0).AsRegister<Register>();
-        __ Mov(temp, value.AsRegister<Register>());
-        __ PoisonHeapReference(temp);
-        __ StoreToOffset(kStoreWord, temp, base, offset);
-      } else {
-        __ StoreToOffset(kStoreWord, value.AsRegister<Register>(), base, offset);
-      }
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      if (is_volatile && !atomic_ldrd_strd) {
-        GenerateWideAtomicStore(base, offset,
-                                value.AsRegisterPairLow<Register>(),
-                                value.AsRegisterPairHigh<Register>(),
-                                locations->GetTemp(0).AsRegister<Register>(),
-                                locations->GetTemp(1).AsRegister<Register>(),
-                                instruction);
-      } else {
-        __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), base, offset);
-        codegen_->MaybeRecordImplicitNullCheck(instruction);
-      }
-      break;
-    }
-
-    case Primitive::kPrimFloat: {
-      __ StoreSToOffset(value.AsFpuRegister<SRegister>(), base, offset);
-      break;
-    }
-
-    case Primitive::kPrimDouble: {
-      DRegister value_reg = FromLowSToD(value.AsFpuRegisterPairLow<SRegister>());
-      if (is_volatile && !atomic_ldrd_strd) {
-        Register value_reg_lo = locations->GetTemp(0).AsRegister<Register>();
-        Register value_reg_hi = locations->GetTemp(1).AsRegister<Register>();
-
-        __ vmovrrd(value_reg_lo, value_reg_hi, value_reg);
-
-        GenerateWideAtomicStore(base, offset,
-                                value_reg_lo,
-                                value_reg_hi,
-                                locations->GetTemp(2).AsRegister<Register>(),
-                                locations->GetTemp(3).AsRegister<Register>(),
-                                instruction);
-      } else {
-        __ StoreDToOffset(value_reg, base, offset);
-        codegen_->MaybeRecordImplicitNullCheck(instruction);
-      }
-      break;
-    }
-
-    case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unreachable type " << field_type;
-      UNREACHABLE();
-  }
-
-  // Longs and doubles are handled in the switch.
-  if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) {
-    codegen_->MaybeRecordImplicitNullCheck(instruction);
-  }
-
-  if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
-    Register temp = locations->GetTemp(0).AsRegister<Register>();
-    Register card = locations->GetTemp(1).AsRegister<Register>();
-    codegen_->MarkGCCard(
-        temp, card, base, value.AsRegister<Register>(), value_can_be_null);
-  }
-
-  if (is_volatile) {
-    codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
-  }
-}
-
-void LocationsBuilderARM::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) {
-  DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
-
-  bool object_field_get_with_read_barrier =
-      kEmitCompilerReadBarrier && (field_info.GetFieldType() == Primitive::kPrimNot);
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction,
-                                                   object_field_get_with_read_barrier ?
-                                                       LocationSummary::kCallOnSlowPath :
-                                                       LocationSummary::kNoCall);
-  if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
-  }
-  locations->SetInAt(0, Location::RequiresRegister());
-
-  bool volatile_for_double = field_info.IsVolatile()
-      && (field_info.GetFieldType() == Primitive::kPrimDouble)
-      && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
-  // The output overlaps in case of volatile long: we don't want the
-  // code generated by GenerateWideAtomicLoad to overwrite the
-  // object's location.  Likewise, in the case of an object field get
-  // with read barriers enabled, we do not want the load to overwrite
-  // the object's location, as we need it to emit the read barrier.
-  bool overlap = (field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong)) ||
-      object_field_get_with_read_barrier;
-
-  if (Primitive::IsFloatingPointType(instruction->GetType())) {
-    locations->SetOut(Location::RequiresFpuRegister());
-  } else {
-    locations->SetOut(Location::RequiresRegister(),
-                      (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
-  }
-  if (volatile_for_double) {
-    // ARM encoding have some additional constraints for ldrexd/strexd:
-    // - registers need to be consecutive
-    // - the first register should be even but not R14.
-    // We don't test for ARM yet, and the assertion makes sure that we
-    // revisit this if we ever enable ARM encoding.
-    DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
-    locations->AddTemp(Location::RequiresRegister());
-    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.
-    if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
-        !Runtime::Current()->UseJitCompilation()) {
-      // If link-time thunks for the Baker read barrier are enabled, for AOT
-      // loads we need a temporary only if the offset is too big.
-      if (field_info.GetFieldOffset().Uint32Value() >= kReferenceLoadMinFarOffset) {
-        locations->AddTemp(Location::RequiresRegister());
-      }
-      // And we always need the reserved entrypoint register.
-      locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister));
-    } else {
-      locations->AddTemp(Location::RequiresRegister());
-    }
-  }
-}
-
-Location LocationsBuilderARM::ArithmeticZeroOrFpuRegister(HInstruction* input) {
-  DCHECK(input->GetType() == Primitive::kPrimDouble || input->GetType() == Primitive::kPrimFloat)
-      << input->GetType();
-  if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
-      (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
-    return Location::ConstantLocation(input->AsConstant());
-  } else {
-    return Location::RequiresFpuRegister();
-  }
-}
-
-Location LocationsBuilderARM::ArmEncodableConstantOrRegister(HInstruction* constant,
-                                                             Opcode opcode) {
-  DCHECK(!Primitive::IsFloatingPointType(constant->GetType()));
-  if (constant->IsConstant() &&
-      CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) {
-    return Location::ConstantLocation(constant->AsConstant());
-  }
-  return Location::RequiresRegister();
-}
-
-bool LocationsBuilderARM::CanEncodeConstantAsImmediate(HConstant* input_cst,
-                                                       Opcode opcode) {
-  uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
-  if (Primitive::Is64BitType(input_cst->GetType())) {
-    Opcode high_opcode = opcode;
-    SetCc low_set_cc = kCcDontCare;
-    switch (opcode) {
-      case SUB:
-        // Flip the operation to an ADD.
-        value = -value;
-        opcode = ADD;
-        FALLTHROUGH_INTENDED;
-      case ADD:
-        if (Low32Bits(value) == 0u) {
-          return CanEncodeConstantAsImmediate(High32Bits(value), opcode, kCcDontCare);
-        }
-        high_opcode = ADC;
-        low_set_cc = kCcSet;
-        break;
-      default:
-        break;
-    }
-    return CanEncodeConstantAsImmediate(Low32Bits(value), opcode, low_set_cc) &&
-        CanEncodeConstantAsImmediate(High32Bits(value), high_opcode, kCcDontCare);
-  } else {
-    return CanEncodeConstantAsImmediate(Low32Bits(value), opcode);
-  }
-}
-
-bool LocationsBuilderARM::CanEncodeConstantAsImmediate(uint32_t value,
-                                                       Opcode opcode,
-                                                       SetCc set_cc) {
-  ShifterOperand so;
-  ArmAssembler* assembler = codegen_->GetAssembler();
-  if (assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, opcode, value, set_cc, &so)) {
-    return true;
-  }
-  Opcode neg_opcode = kNoOperand;
-  uint32_t neg_value = 0;
-  switch (opcode) {
-    case AND: neg_opcode = BIC; neg_value = ~value; break;
-    case ORR: neg_opcode = ORN; neg_value = ~value; break;
-    case ADD: neg_opcode = SUB; neg_value = -value; break;
-    case ADC: neg_opcode = SBC; neg_value = ~value; break;
-    case SUB: neg_opcode = ADD; neg_value = -value; break;
-    case SBC: neg_opcode = ADC; neg_value = ~value; break;
-    case MOV: neg_opcode = MVN; neg_value = ~value; break;
-    default:
-      return false;
-  }
-
-  if (assembler->ShifterOperandCanHold(kNoRegister,
-                                       kNoRegister,
-                                       neg_opcode,
-                                       neg_value,
-                                       set_cc,
-                                       &so)) {
-    return true;
-  }
-
-  return opcode == AND && IsPowerOfTwo(value + 1);
-}
-
-void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction,
-                                                 const FieldInfo& field_info) {
-  DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
-
-  LocationSummary* locations = instruction->GetLocations();
-  Location base_loc = locations->InAt(0);
-  Register base = base_loc.AsRegister<Register>();
-  Location out = locations->Out();
-  bool is_volatile = field_info.IsVolatile();
-  bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
-  Primitive::Type field_type = field_info.GetFieldType();
-  uint32_t offset = field_info.GetFieldOffset().Uint32Value();
-
-  switch (field_type) {
-    case Primitive::kPrimBoolean:
-      __ LoadFromOffset(kLoadUnsignedByte, out.AsRegister<Register>(), base, offset);
-      break;
-
-    case Primitive::kPrimByte:
-      __ LoadFromOffset(kLoadSignedByte, out.AsRegister<Register>(), base, offset);
-      break;
-
-    case Primitive::kPrimShort:
-      __ LoadFromOffset(kLoadSignedHalfword, out.AsRegister<Register>(), base, offset);
-      break;
-
-    case Primitive::kPrimChar:
-      __ LoadFromOffset(kLoadUnsignedHalfword, out.AsRegister<Register>(), base, offset);
-      break;
-
-    case Primitive::kPrimInt:
-      __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset);
-      break;
-
-    case Primitive::kPrimNot: {
-      // /* HeapReference<Object> */ out = *(base + offset)
-      if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
-        Location temp_loc = locations->GetTemp(0);
-        // Note that a potential implicit null check is handled in this
-        // CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier call.
-        codegen_->GenerateFieldLoadWithBakerReadBarrier(
-            instruction, out, base, offset, temp_loc, /* needs_null_check */ true);
-        if (is_volatile) {
-          codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
-        }
-      } else {
-        __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset);
-        codegen_->MaybeRecordImplicitNullCheck(instruction);
-        if (is_volatile) {
-          codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
-        }
-        // If read barriers are enabled, emit read barriers other than
-        // Baker's using a slow path (and also unpoison the loaded
-        // reference, if heap poisoning is enabled).
-        codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, base_loc, offset);
-      }
-      break;
-    }
-
-    case Primitive::kPrimLong:
-      if (is_volatile && !atomic_ldrd_strd) {
-        GenerateWideAtomicLoad(base, offset,
-                               out.AsRegisterPairLow<Register>(),
-                               out.AsRegisterPairHigh<Register>());
-      } else {
-        __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), base, offset);
-      }
-      break;
-
-    case Primitive::kPrimFloat:
-      __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), base, offset);
-      break;
-
-    case Primitive::kPrimDouble: {
-      DRegister out_reg = FromLowSToD(out.AsFpuRegisterPairLow<SRegister>());
-      if (is_volatile && !atomic_ldrd_strd) {
-        Register lo = locations->GetTemp(0).AsRegister<Register>();
-        Register hi = locations->GetTemp(1).AsRegister<Register>();
-        GenerateWideAtomicLoad(base, offset, lo, hi);
-        codegen_->MaybeRecordImplicitNullCheck(instruction);
-        __ vmovdrr(out_reg, lo, hi);
-      } else {
-        __ LoadDFromOffset(out_reg, base, offset);
-        codegen_->MaybeRecordImplicitNullCheck(instruction);
-      }
-      break;
-    }
-
-    case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unreachable type " << field_type;
-      UNREACHABLE();
-  }
-
-  if (field_type == Primitive::kPrimNot || field_type == Primitive::kPrimDouble) {
-    // Potential implicit null checks, in the case of reference or
-    // double fields, are handled in the previous switch statement.
-  } else {
-    codegen_->MaybeRecordImplicitNullCheck(instruction);
-  }
-
-  if (is_volatile) {
-    if (field_type == Primitive::kPrimNot) {
-      // Memory barriers, in the case of references, are also handled
-      // in the previous switch statement.
-    } else {
-      codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
-    }
-  }
-}
-
-void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
-  HandleFieldSet(instruction, instruction->GetFieldInfo());
-}
-
-void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
-  HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
-}
-
-void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
-  HandleFieldGet(instruction, instruction->GetFieldInfo());
-}
-
-void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
-  HandleFieldGet(instruction, instruction->GetFieldInfo());
-}
-
-void LocationsBuilderARM::VisitStaticFieldGet(HStaticFieldGet* instruction) {
-  HandleFieldGet(instruction, instruction->GetFieldInfo());
-}
-
-void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instruction) {
-  HandleFieldGet(instruction, instruction->GetFieldInfo());
-}
-
-void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
-  HandleFieldSet(instruction, instruction->GetFieldInfo());
-}
-
-void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
-  HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
-}
-
-void LocationsBuilderARM::VisitUnresolvedInstanceFieldGet(
-    HUnresolvedInstanceFieldGet* instruction) {
-  FieldAccessCallingConventionARM calling_convention;
-  codegen_->CreateUnresolvedFieldLocationSummary(
-      instruction, instruction->GetFieldType(), calling_convention);
-}
-
-void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldGet(
-    HUnresolvedInstanceFieldGet* instruction) {
-  FieldAccessCallingConventionARM calling_convention;
-  codegen_->GenerateUnresolvedFieldAccess(instruction,
-                                          instruction->GetFieldType(),
-                                          instruction->GetFieldIndex(),
-                                          instruction->GetDexPc(),
-                                          calling_convention);
-}
-
-void LocationsBuilderARM::VisitUnresolvedInstanceFieldSet(
-    HUnresolvedInstanceFieldSet* instruction) {
-  FieldAccessCallingConventionARM calling_convention;
-  codegen_->CreateUnresolvedFieldLocationSummary(
-      instruction, instruction->GetFieldType(), calling_convention);
-}
-
-void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldSet(
-    HUnresolvedInstanceFieldSet* instruction) {
-  FieldAccessCallingConventionARM calling_convention;
-  codegen_->GenerateUnresolvedFieldAccess(instruction,
-                                          instruction->GetFieldType(),
-                                          instruction->GetFieldIndex(),
-                                          instruction->GetDexPc(),
-                                          calling_convention);
-}
-
-void LocationsBuilderARM::VisitUnresolvedStaticFieldGet(
-    HUnresolvedStaticFieldGet* instruction) {
-  FieldAccessCallingConventionARM calling_convention;
-  codegen_->CreateUnresolvedFieldLocationSummary(
-      instruction, instruction->GetFieldType(), calling_convention);
-}
-
-void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldGet(
-    HUnresolvedStaticFieldGet* instruction) {
-  FieldAccessCallingConventionARM calling_convention;
-  codegen_->GenerateUnresolvedFieldAccess(instruction,
-                                          instruction->GetFieldType(),
-                                          instruction->GetFieldIndex(),
-                                          instruction->GetDexPc(),
-                                          calling_convention);
-}
-
-void LocationsBuilderARM::VisitUnresolvedStaticFieldSet(
-    HUnresolvedStaticFieldSet* instruction) {
-  FieldAccessCallingConventionARM calling_convention;
-  codegen_->CreateUnresolvedFieldLocationSummary(
-      instruction, instruction->GetFieldType(), calling_convention);
-}
-
-void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldSet(
-    HUnresolvedStaticFieldSet* instruction) {
-  FieldAccessCallingConventionARM calling_convention;
-  codegen_->GenerateUnresolvedFieldAccess(instruction,
-                                          instruction->GetFieldType(),
-                                          instruction->GetFieldIndex(),
-                                          instruction->GetDexPc(),
-                                          calling_convention);
-}
-
-void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
-  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
-  locations->SetInAt(0, Location::RequiresRegister());
-}
-
-void CodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) {
-  if (CanMoveNullCheckToUser(instruction)) {
-    return;
-  }
-  Location obj = instruction->GetLocations()->InAt(0);
-
-  __ LoadFromOffset(kLoadWord, IP, obj.AsRegister<Register>(), 0);
-  RecordPcInfo(instruction, instruction->GetDexPc());
-}
-
-void CodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruction) {
-  SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction);
-  AddSlowPath(slow_path);
-
-  LocationSummary* locations = instruction->GetLocations();
-  Location obj = locations->InAt(0);
-
-  __ CompareAndBranchIfZero(obj.AsRegister<Register>(), slow_path->GetEntryLabel());
-}
-
-void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
-  codegen_->GenerateNullCheck(instruction);
-}
-
-static LoadOperandType GetLoadOperandType(Primitive::Type type) {
-  switch (type) {
-    case Primitive::kPrimNot:
-      return kLoadWord;
-    case Primitive::kPrimBoolean:
-      return kLoadUnsignedByte;
-    case Primitive::kPrimByte:
-      return kLoadSignedByte;
-    case Primitive::kPrimChar:
-      return kLoadUnsignedHalfword;
-    case Primitive::kPrimShort:
-      return kLoadSignedHalfword;
-    case Primitive::kPrimInt:
-      return kLoadWord;
-    case Primitive::kPrimLong:
-      return kLoadWordPair;
-    case Primitive::kPrimFloat:
-      return kLoadSWord;
-    case Primitive::kPrimDouble:
-      return kLoadDWord;
-    default:
-      LOG(FATAL) << "Unreachable type " << type;
-      UNREACHABLE();
-  }
-}
-
-static StoreOperandType GetStoreOperandType(Primitive::Type type) {
-  switch (type) {
-    case Primitive::kPrimNot:
-      return kStoreWord;
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-      return kStoreByte;
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-      return kStoreHalfword;
-    case Primitive::kPrimInt:
-      return kStoreWord;
-    case Primitive::kPrimLong:
-      return kStoreWordPair;
-    case Primitive::kPrimFloat:
-      return kStoreSWord;
-    case Primitive::kPrimDouble:
-      return kStoreDWord;
-    default:
-      LOG(FATAL) << "Unreachable type " << type;
-      UNREACHABLE();
-  }
-}
-
-void CodeGeneratorARM::LoadFromShiftedRegOffset(Primitive::Type type,
-                                                Location out_loc,
-                                                Register base,
-                                                Register reg_offset,
-                                                Condition cond) {
-  uint32_t shift_count = Primitive::ComponentSizeShift(type);
-  Address mem_address(base, reg_offset, Shift::LSL, shift_count);
-
-  switch (type) {
-    case Primitive::kPrimByte:
-      __ ldrsb(out_loc.AsRegister<Register>(), mem_address, cond);
-      break;
-    case Primitive::kPrimBoolean:
-      __ ldrb(out_loc.AsRegister<Register>(), mem_address, cond);
-      break;
-    case Primitive::kPrimShort:
-      __ ldrsh(out_loc.AsRegister<Register>(), mem_address, cond);
-      break;
-    case Primitive::kPrimChar:
-      __ ldrh(out_loc.AsRegister<Register>(), mem_address, cond);
-      break;
-    case Primitive::kPrimNot:
-    case Primitive::kPrimInt:
-      __ ldr(out_loc.AsRegister<Register>(), mem_address, cond);
-      break;
-    // T32 doesn't support LoadFromShiftedRegOffset mem address mode for these types.
-    case Primitive::kPrimLong:
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-    default:
-      LOG(FATAL) << "Unreachable type " << type;
-      UNREACHABLE();
-  }
-}
-
-void CodeGeneratorARM::StoreToShiftedRegOffset(Primitive::Type type,
-                                               Location loc,
-                                               Register base,
-                                               Register reg_offset,
-                                               Condition cond) {
-  uint32_t shift_count = Primitive::ComponentSizeShift(type);
-  Address mem_address(base, reg_offset, Shift::LSL, shift_count);
-
-  switch (type) {
-    case Primitive::kPrimByte:
-    case Primitive::kPrimBoolean:
-      __ strb(loc.AsRegister<Register>(), mem_address, cond);
-      break;
-    case Primitive::kPrimShort:
-    case Primitive::kPrimChar:
-      __ strh(loc.AsRegister<Register>(), mem_address, cond);
-      break;
-    case Primitive::kPrimNot:
-    case Primitive::kPrimInt:
-      __ str(loc.AsRegister<Register>(), mem_address, cond);
-      break;
-    // T32 doesn't support StoreToShiftedRegOffset mem address mode for these types.
-    case Primitive::kPrimLong:
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-    default:
-      LOG(FATAL) << "Unreachable type " << type;
-      UNREACHABLE();
-  }
-}
-
-void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) {
-  bool object_array_get_with_read_barrier =
-      kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot);
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction,
-                                                   object_array_get_with_read_barrier ?
-                                                       LocationSummary::kCallOnSlowPath :
-                                                       LocationSummary::kNoCall);
-  if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
-  }
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
-  if (Primitive::IsFloatingPointType(instruction->GetType())) {
-    locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
-  } else {
-    // The output overlaps in the case of an object array get with
-    // read barriers enabled: we do not want the move to overwrite the
-    // array's location, as we need it to emit the read barrier.
-    locations->SetOut(
-        Location::RequiresRegister(),
-        object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
-  }
-  if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
-    // We need a temporary register for the read barrier marking slow
-    // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier.
-    if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
-        !Runtime::Current()->UseJitCompilation() &&
-        instruction->GetIndex()->IsConstant()) {
-      // Array loads with constant index are treated as field loads.
-      // If link-time thunks for the Baker read barrier are enabled, for AOT
-      // constant index loads we need a temporary only if the offset is too big.
-      uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction);
-      uint32_t index = instruction->GetIndex()->AsIntConstant()->GetValue();
-      offset += index << Primitive::ComponentSizeShift(Primitive::kPrimNot);
-      if (offset >= kReferenceLoadMinFarOffset) {
-        locations->AddTemp(Location::RequiresRegister());
-      }
-      // And we always need the reserved entrypoint register.
-      locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister));
-    } else if (kBakerReadBarrierLinkTimeThunksEnableForArrays &&
-               !Runtime::Current()->UseJitCompilation() &&
-               !instruction->GetIndex()->IsConstant()) {
-      // We need a non-scratch temporary for the array data pointer.
-      locations->AddTemp(Location::RequiresRegister());
-      // And we always need the reserved entrypoint register.
-      locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister));
-    } else {
-      locations->AddTemp(Location::RequiresRegister());
-    }
-  } else if (mirror::kUseStringCompression && instruction->IsStringCharAt()) {
-    // Also need a temporary for String compression feature.
-    locations->AddTemp(Location::RequiresRegister());
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  Location obj_loc = locations->InAt(0);
-  Register obj = obj_loc.AsRegister<Register>();
-  Location index = locations->InAt(1);
-  Location out_loc = locations->Out();
-  uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
-  Primitive::Type type = instruction->GetType();
-  const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
-                                        instruction->IsStringCharAt();
-  HInstruction* array_instr = instruction->GetArray();
-  bool has_intermediate_address = array_instr->IsIntermediateAddress();
-
-  switch (type) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimShort:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimInt: {
-      Register length;
-      if (maybe_compressed_char_at) {
-        length = locations->GetTemp(0).AsRegister<Register>();
-        uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
-        __ LoadFromOffset(kLoadWord, length, obj, count_offset);
-        codegen_->MaybeRecordImplicitNullCheck(instruction);
-      }
-      if (index.IsConstant()) {
-        int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
-        if (maybe_compressed_char_at) {
-          Label uncompressed_load, done;
-          Label* final_label = codegen_->GetFinalLabel(instruction, &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(&uncompressed_load, CS);
-          __ LoadFromOffset(kLoadUnsignedByte,
-                            out_loc.AsRegister<Register>(),
-                            obj,
-                            data_offset + const_index);
-          __ b(final_label);
-          __ Bind(&uncompressed_load);
-          __ LoadFromOffset(GetLoadOperandType(Primitive::kPrimChar),
-                            out_loc.AsRegister<Register>(),
-                            obj,
-                            data_offset + (const_index << 1));
-          if (done.IsLinked()) {
-            __ Bind(&done);
-          }
-        } else {
-          uint32_t full_offset = data_offset + (const_index << Primitive::ComponentSizeShift(type));
-
-          LoadOperandType load_type = GetLoadOperandType(type);
-          __ LoadFromOffset(load_type, out_loc.AsRegister<Register>(), obj, full_offset);
-        }
-      } else {
-        Register temp = IP;
-
-        if (has_intermediate_address) {
-          // We do not need to compute the intermediate address from the array: the
-          // input instruction has done it already. See the comment in
-          // `TryExtractArrayAccessAddress()`.
-          if (kIsDebugBuild) {
-            HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
-            DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
-          }
-          temp = obj;
-        } else {
-          __ add(temp, obj, ShifterOperand(data_offset));
-        }
-        if (maybe_compressed_char_at) {
-          Label uncompressed_load, done;
-          Label* final_label = codegen_->GetFinalLabel(instruction, &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(&uncompressed_load, CS);
-          __ ldrb(out_loc.AsRegister<Register>(),
-                  Address(temp, index.AsRegister<Register>(), Shift::LSL, 0));
-          __ b(final_label);
-          __ Bind(&uncompressed_load);
-          __ ldrh(out_loc.AsRegister<Register>(),
-                  Address(temp, index.AsRegister<Register>(), Shift::LSL, 1));
-          if (done.IsLinked()) {
-            __ Bind(&done);
-          }
-        } else {
-          codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, index.AsRegister<Register>());
-        }
-      }
-      break;
-    }
-
-    case Primitive::kPrimNot: {
-      // The read barrier instrumentation of object ArrayGet
-      // instructions does not support the HIntermediateAddress
-      // instruction.
-      DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
-
-      static_assert(
-          sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
-          "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
-      // /* HeapReference<Object> */ out =
-      //     *(obj + data_offset + index * sizeof(HeapReference<Object>))
-      if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
-        Location temp = locations->GetTemp(0);
-        // Note that a potential implicit null check is handled in this
-        // CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier call.
-        DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
-        if (index.IsConstant()) {
-          // Array load with a constant index can be treated as a field load.
-          data_offset += helpers::Int32ConstantFrom(index) << Primitive::ComponentSizeShift(type);
-          codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
-                                                          out_loc,
-                                                          obj,
-                                                          data_offset,
-                                                          locations->GetTemp(0),
-                                                          /* needs_null_check */ false);
-        } else {
-          codegen_->GenerateArrayLoadWithBakerReadBarrier(
-              instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ false);
-        }
-      } else {
-        Register out = out_loc.AsRegister<Register>();
-        if (index.IsConstant()) {
-          size_t offset =
-              (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
-          __ LoadFromOffset(kLoadWord, out, obj, offset);
-          codegen_->MaybeRecordImplicitNullCheck(instruction);
-          // If read barriers are enabled, emit read barriers other than
-          // Baker's using a slow path (and also unpoison the loaded
-          // reference, if heap poisoning is enabled).
-          codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
-        } else {
-          Register temp = IP;
-
-          if (has_intermediate_address) {
-            // We do not need to compute the intermediate address from the array: the
-            // input instruction has done it already. See the comment in
-            // `TryExtractArrayAccessAddress()`.
-            if (kIsDebugBuild) {
-              HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
-              DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
-            }
-            temp = obj;
-          } else {
-            __ add(temp, obj, ShifterOperand(data_offset));
-          }
-          codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, index.AsRegister<Register>());
-
-          codegen_->MaybeRecordImplicitNullCheck(instruction);
-          // If read barriers are enabled, emit read barriers other than
-          // Baker's using a slow path (and also unpoison the loaded
-          // reference, if heap poisoning is enabled).
-          codegen_->MaybeGenerateReadBarrierSlow(
-              instruction, out_loc, out_loc, obj_loc, data_offset, index);
-        }
-      }
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      if (index.IsConstant()) {
-        size_t offset =
-            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
-        __ LoadFromOffset(kLoadWordPair, out_loc.AsRegisterPairLow<Register>(), obj, offset);
-      } else {
-        __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
-        __ LoadFromOffset(kLoadWordPair, out_loc.AsRegisterPairLow<Register>(), IP, data_offset);
-      }
-      break;
-    }
-
-    case Primitive::kPrimFloat: {
-      SRegister out = out_loc.AsFpuRegister<SRegister>();
-      if (index.IsConstant()) {
-        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
-        __ LoadSFromOffset(out, obj, offset);
-      } else {
-        __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
-        __ LoadSFromOffset(out, IP, data_offset);
-      }
-      break;
-    }
-
-    case Primitive::kPrimDouble: {
-      SRegister out = out_loc.AsFpuRegisterPairLow<SRegister>();
-      if (index.IsConstant()) {
-        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
-        __ LoadDFromOffset(FromLowSToD(out), obj, offset);
-      } else {
-        __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
-        __ LoadDFromOffset(FromLowSToD(out), IP, data_offset);
-      }
-      break;
-    }
-
-    case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unreachable type " << type;
-      UNREACHABLE();
-  }
-
-  if (type == Primitive::kPrimNot) {
-    // Potential implicit null checks, in the case of reference
-    // arrays, are handled in the previous switch statement.
-  } else if (!maybe_compressed_char_at) {
-    codegen_->MaybeRecordImplicitNullCheck(instruction);
-  }
-}
-
-void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) {
-  Primitive::Type value_type = instruction->GetComponentType();
-
-  bool needs_write_barrier =
-      CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
-  bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
-
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
-      instruction,
-      may_need_runtime_call_for_type_check ?
-          LocationSummary::kCallOnSlowPath :
-          LocationSummary::kNoCall);
-
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
-  if (Primitive::IsFloatingPointType(value_type)) {
-    locations->SetInAt(2, Location::RequiresFpuRegister());
-  } else {
-    locations->SetInAt(2, Location::RequiresRegister());
-  }
-  if (needs_write_barrier) {
-    // Temporary registers for the write barrier.
-    locations->AddTemp(Location::RequiresRegister());  // Possibly used for ref. poisoning too.
-    locations->AddTemp(Location::RequiresRegister());
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  Location array_loc = locations->InAt(0);
-  Register array = array_loc.AsRegister<Register>();
-  Location index = locations->InAt(1);
-  Primitive::Type value_type = instruction->GetComponentType();
-  bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
-  bool needs_write_barrier =
-      CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
-  uint32_t data_offset =
-      mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value();
-  Location value_loc = locations->InAt(2);
-  HInstruction* array_instr = instruction->GetArray();
-  bool has_intermediate_address = array_instr->IsIntermediateAddress();
-
-  switch (value_type) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimShort:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimInt: {
-      if (index.IsConstant()) {
-        int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
-        uint32_t full_offset =
-            data_offset + (const_index << Primitive::ComponentSizeShift(value_type));
-        StoreOperandType store_type = GetStoreOperandType(value_type);
-        __ StoreToOffset(store_type, value_loc.AsRegister<Register>(), array, full_offset);
-      } else {
-        Register temp = IP;
-
-        if (has_intermediate_address) {
-          // We do not need to compute the intermediate address from the array: the
-          // input instruction has done it already. See the comment in
-          // `TryExtractArrayAccessAddress()`.
-          if (kIsDebugBuild) {
-            HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
-            DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == data_offset);
-          }
-          temp = array;
-        } else {
-          __ add(temp, array, ShifterOperand(data_offset));
-        }
-        codegen_->StoreToShiftedRegOffset(value_type,
-                                          value_loc,
-                                          temp,
-                                          index.AsRegister<Register>());
-      }
-      break;
-    }
-
-    case Primitive::kPrimNot: {
-      Register value = value_loc.AsRegister<Register>();
-      // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet.
-      // See the comment in instruction_simplifier_shared.cc.
-      DCHECK(!has_intermediate_address);
-
-      if (instruction->InputAt(2)->IsNullConstant()) {
-        // Just setting null.
-        if (index.IsConstant()) {
-          size_t offset =
-              (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
-          __ StoreToOffset(kStoreWord, value, array, offset);
-        } else {
-          DCHECK(index.IsRegister()) << index;
-          __ add(IP, array, ShifterOperand(data_offset));
-          codegen_->StoreToShiftedRegOffset(value_type,
-                                            value_loc,
-                                            IP,
-                                            index.AsRegister<Register>());
-        }
-        codegen_->MaybeRecordImplicitNullCheck(instruction);
-        DCHECK(!needs_write_barrier);
-        DCHECK(!may_need_runtime_call_for_type_check);
-        break;
-      }
-
-      DCHECK(needs_write_barrier);
-      Location temp1_loc = locations->GetTemp(0);
-      Register temp1 = temp1_loc.AsRegister<Register>();
-      Location temp2_loc = locations->GetTemp(1);
-      Register temp2 = temp2_loc.AsRegister<Register>();
-      uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
-      uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
-      uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
-      Label done;
-      Label* final_label = codegen_->GetFinalLabel(instruction, &done);
-      SlowPathCodeARM* slow_path = nullptr;
-
-      if (may_need_runtime_call_for_type_check) {
-        slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARM(instruction);
-        codegen_->AddSlowPath(slow_path);
-        if (instruction->GetValueCanBeNull()) {
-          Label non_zero;
-          __ CompareAndBranchIfNonZero(value, &non_zero);
-          if (index.IsConstant()) {
-            size_t offset =
-               (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
-            __ StoreToOffset(kStoreWord, value, array, offset);
-          } else {
-            DCHECK(index.IsRegister()) << index;
-            __ add(IP, array, ShifterOperand(data_offset));
-            codegen_->StoreToShiftedRegOffset(value_type,
-                                              value_loc,
-                                              IP,
-                                              index.AsRegister<Register>());
-          }
-          codegen_->MaybeRecordImplicitNullCheck(instruction);
-          __ b(final_label);
-          __ Bind(&non_zero);
-        }
-
-        // Note that when read barriers are enabled, the type checks
-        // are performed without read barriers.  This is fine, even in
-        // the case where a class object is in the from-space after
-        // the flip, as a comparison involving such a type would not
-        // produce a false positive; it may of course produce a false
-        // negative, in which case we would take the ArraySet slow
-        // path.
-
-        // /* HeapReference<Class> */ temp1 = array->klass_
-        __ LoadFromOffset(kLoadWord, temp1, array, class_offset);
-        codegen_->MaybeRecordImplicitNullCheck(instruction);
-        __ MaybeUnpoisonHeapReference(temp1);
-
-        // /* HeapReference<Class> */ temp1 = temp1->component_type_
-        __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
-        // /* HeapReference<Class> */ temp2 = value->klass_
-        __ LoadFromOffset(kLoadWord, temp2, value, class_offset);
-        // If heap poisoning is enabled, no need to unpoison `temp1`
-        // nor `temp2`, as we are comparing two poisoned references.
-        __ cmp(temp1, ShifterOperand(temp2));
-
-        if (instruction->StaticTypeOfArrayIsObjectArray()) {
-          Label do_put;
-          __ b(&do_put, EQ);
-          // If heap poisoning is enabled, the `temp1` reference has
-          // not been unpoisoned yet; unpoison it now.
-          __ MaybeUnpoisonHeapReference(temp1);
-
-          // /* HeapReference<Class> */ temp1 = temp1->super_class_
-          __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
-          // If heap poisoning is enabled, no need to unpoison
-          // `temp1`, as we are comparing against null below.
-          __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
-          __ Bind(&do_put);
-        } else {
-          __ b(slow_path->GetEntryLabel(), NE);
-        }
-      }
-
-      Register source = value;
-      if (kPoisonHeapReferences) {
-        // Note that in the case where `value` is a null reference,
-        // we do not enter this block, as a null reference does not
-        // need poisoning.
-        DCHECK_EQ(value_type, Primitive::kPrimNot);
-        __ Mov(temp1, value);
-        __ PoisonHeapReference(temp1);
-        source = temp1;
-      }
-
-      if (index.IsConstant()) {
-        size_t offset =
-            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
-        __ StoreToOffset(kStoreWord, source, array, offset);
-      } else {
-        DCHECK(index.IsRegister()) << index;
-
-        __ add(IP, array, ShifterOperand(data_offset));
-        codegen_->StoreToShiftedRegOffset(value_type,
-                                          Location::RegisterLocation(source),
-                                          IP,
-                                          index.AsRegister<Register>());
-      }
-
-      if (!may_need_runtime_call_for_type_check) {
-        codegen_->MaybeRecordImplicitNullCheck(instruction);
-      }
-
-      codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull());
-
-      if (done.IsLinked()) {
-        __ Bind(&done);
-      }
-
-      if (slow_path != nullptr) {
-        __ Bind(slow_path->GetExitLabel());
-      }
-
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      Location value = locations->InAt(2);
-      if (index.IsConstant()) {
-        size_t offset =
-            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
-        __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), array, offset);
-      } else {
-        __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
-        __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), IP, data_offset);
-      }
-      break;
-    }
-
-    case Primitive::kPrimFloat: {
-      Location value = locations->InAt(2);
-      DCHECK(value.IsFpuRegister());
-      if (index.IsConstant()) {
-        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
-        __ StoreSToOffset(value.AsFpuRegister<SRegister>(), array, offset);
-      } else {
-        __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
-        __ StoreSToOffset(value.AsFpuRegister<SRegister>(), IP, data_offset);
-      }
-      break;
-    }
-
-    case Primitive::kPrimDouble: {
-      Location value = locations->InAt(2);
-      DCHECK(value.IsFpuRegisterPair());
-      if (index.IsConstant()) {
-        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
-        __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), array, offset);
-      } else {
-        __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
-        __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), IP, data_offset);
-      }
-
-      break;
-    }
-
-    case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unreachable type " << value_type;
-      UNREACHABLE();
-  }
-
-  // Objects are handled in the switch.
-  if (value_type != Primitive::kPrimNot) {
-    codegen_->MaybeRecordImplicitNullCheck(instruction);
-  }
-}
-
-void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
-  Register obj = locations->InAt(0).AsRegister<Register>();
-  Register out = locations->Out().AsRegister<Register>();
-  __ LoadFromOffset(kLoadWord, out, obj, offset);
-  codegen_->MaybeRecordImplicitNullCheck(instruction);
-  // Mask out compression flag from String's array length.
-  if (mirror::kUseStringCompression && instruction->IsStringLength()) {
-    __ Lsr(out, out, 1u);
-  }
-}
-
-void LocationsBuilderARM::VisitIntermediateAddress(HIntermediateAddress* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset()));
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void InstructionCodeGeneratorARM::VisitIntermediateAddress(HIntermediateAddress* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  Location out = locations->Out();
-  Location first = locations->InAt(0);
-  Location second = locations->InAt(1);
-
-  if (second.IsRegister()) {
-    __ add(out.AsRegister<Register>(),
-           first.AsRegister<Register>(),
-           ShifterOperand(second.AsRegister<Register>()));
-  } else {
-    __ AddConstant(out.AsRegister<Register>(),
-                   first.AsRegister<Register>(),
-                   second.GetConstant()->AsIntConstant()->GetValue());
-  }
-}
-
-void LocationsBuilderARM::VisitIntermediateAddressIndex(HIntermediateAddressIndex* instruction) {
-  LOG(FATAL) << "Unreachable " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorARM::VisitIntermediateAddressIndex(
-    HIntermediateAddressIndex* instruction) {
-  LOG(FATAL) << "Unreachable " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) {
-  RegisterSet caller_saves = RegisterSet::Empty();
-  InvokeRuntimeCallingConvention calling_convention;
-  caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
-  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
-
-  HInstruction* index = instruction->InputAt(0);
-  HInstruction* length = instruction->InputAt(1);
-  // If both index and length are constants we can statically check the bounds. But if at least one
-  // of them is not encodable ArmEncodableConstantOrRegister will create
-  // Location::RequiresRegister() which is not desired to happen. Instead we create constant
-  // locations.
-  bool both_const = index->IsConstant() && length->IsConstant();
-  locations->SetInAt(0, both_const
-      ? Location::ConstantLocation(index->AsConstant())
-      : ArmEncodableConstantOrRegister(index, CMP));
-  locations->SetInAt(1, both_const
-      ? Location::ConstantLocation(length->AsConstant())
-      : ArmEncodableConstantOrRegister(length, CMP));
-}
-
-void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  Location index_loc = locations->InAt(0);
-  Location length_loc = locations->InAt(1);
-
-  if (length_loc.IsConstant()) {
-    int32_t length = helpers::Int32ConstantFrom(length_loc);
-    if (index_loc.IsConstant()) {
-      // BCE will remove the bounds check if we are guaranteed to pass.
-      int32_t index = helpers::Int32ConstantFrom(index_loc);
-      if (index < 0 || index >= length) {
-        SlowPathCodeARM* slow_path =
-            new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction);
-        codegen_->AddSlowPath(slow_path);
-        __ b(slow_path->GetEntryLabel());
-      } else {
-        // Some optimization after BCE may have generated this, and we should not
-        // generate a bounds check if it is a valid range.
-      }
-      return;
-    }
-
-    SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction);
-    __ cmp(index_loc.AsRegister<Register>(), ShifterOperand(length));
-    codegen_->AddSlowPath(slow_path);
-    __ b(slow_path->GetEntryLabel(), HS);
-  } else {
-    SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction);
-    if (index_loc.IsConstant()) {
-      int32_t index = helpers::Int32ConstantFrom(index_loc);
-      __ cmp(length_loc.AsRegister<Register>(), ShifterOperand(index));
-    } else {
-      __ cmp(length_loc.AsRegister<Register>(), ShifterOperand(index_loc.AsRegister<Register>()));
-    }
-    codegen_->AddSlowPath(slow_path);
-    __ b(slow_path->GetEntryLabel(), LS);
-  }
-}
-
-void CodeGeneratorARM::MarkGCCard(Register temp,
-                                  Register card,
-                                  Register object,
-                                  Register value,
-                                  bool can_be_null) {
-  Label is_null;
-  if (can_be_null) {
-    __ CompareAndBranchIfZero(value, &is_null);
-  }
-  __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
-  __ Lsr(temp, object, gc::accounting::CardTable::kCardShift);
-  __ strb(card, Address(card, temp));
-  if (can_be_null) {
-    __ Bind(&is_null);
-  }
-}
-
-void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
-  LOG(FATAL) << "Unreachable";
-}
-
-void InstructionCodeGeneratorARM::VisitParallelMove(HParallelMove* instruction) {
-  codegen_->GetMoveResolver()->EmitNativeCode(instruction);
-}
-
-void LocationsBuilderARM::VisitSuspendCheck(HSuspendCheck* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
-  locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
-}
-
-void InstructionCodeGeneratorARM::VisitSuspendCheck(HSuspendCheck* instruction) {
-  HBasicBlock* block = instruction->GetBlock();
-  if (block->GetLoopInformation() != nullptr) {
-    DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
-    // The back edge will generate the suspend check.
-    return;
-  }
-  if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
-    // The goto will generate the suspend check.
-    return;
-  }
-  GenerateSuspendCheck(instruction, nullptr);
-}
-
-void InstructionCodeGeneratorARM::GenerateSuspendCheck(HSuspendCheck* instruction,
-                                                       HBasicBlock* successor) {
-  SuspendCheckSlowPathARM* slow_path =
-      down_cast<SuspendCheckSlowPathARM*>(instruction->GetSlowPath());
-  if (slow_path == nullptr) {
-    slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARM(instruction, successor);
-    instruction->SetSlowPath(slow_path);
-    codegen_->AddSlowPath(slow_path);
-    if (successor != nullptr) {
-      DCHECK(successor->IsLoopHeader());
-      codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction);
-    }
-  } else {
-    DCHECK_EQ(slow_path->GetSuccessor(), successor);
-  }
-
-  __ LoadFromOffset(
-      kLoadUnsignedHalfword, IP, TR, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
-  if (successor == nullptr) {
-    __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel());
-    __ Bind(slow_path->GetReturnLabel());
-  } else {
-    __ CompareAndBranchIfZero(IP, codegen_->GetLabelOf(successor));
-    __ b(slow_path->GetEntryLabel());
-  }
-}
-
-ArmAssembler* ParallelMoveResolverARM::GetAssembler() const {
-  return codegen_->GetAssembler();
-}
-
-void ParallelMoveResolverARM::EmitMove(size_t index) {
-  MoveOperands* move = moves_[index];
-  Location source = move->GetSource();
-  Location destination = move->GetDestination();
-
-  if (source.IsRegister()) {
-    if (destination.IsRegister()) {
-      __ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>());
-    } else if (destination.IsFpuRegister()) {
-      __ vmovsr(destination.AsFpuRegister<SRegister>(), source.AsRegister<Register>());
-    } else {
-      DCHECK(destination.IsStackSlot());
-      __ StoreToOffset(kStoreWord, source.AsRegister<Register>(),
-                       SP, destination.GetStackIndex());
-    }
-  } else if (source.IsStackSlot()) {
-    if (destination.IsRegister()) {
-      __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(),
-                        SP, source.GetStackIndex());
-    } else if (destination.IsFpuRegister()) {
-      __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex());
-    } else {
-      DCHECK(destination.IsStackSlot());
-      __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex());
-      __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
-    }
-  } else if (source.IsFpuRegister()) {
-    if (destination.IsRegister()) {
-      __ vmovrs(destination.AsRegister<Register>(), source.AsFpuRegister<SRegister>());
-    } else if (destination.IsFpuRegister()) {
-      __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>());
-    } else {
-      DCHECK(destination.IsStackSlot());
-      __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex());
-    }
-  } else if (source.IsDoubleStackSlot()) {
-    if (destination.IsDoubleStackSlot()) {
-      __ LoadDFromOffset(DTMP, SP, source.GetStackIndex());
-      __ StoreDToOffset(DTMP, SP, destination.GetStackIndex());
-    } else if (destination.IsRegisterPair()) {
-      DCHECK(ExpectedPairLayout(destination));
-      __ LoadFromOffset(
-          kLoadWordPair, destination.AsRegisterPairLow<Register>(), SP, source.GetStackIndex());
-    } else {
-      DCHECK(destination.IsFpuRegisterPair()) << destination;
-      __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
-                         SP,
-                         source.GetStackIndex());
-    }
-  } else if (source.IsRegisterPair()) {
-    if (destination.IsRegisterPair()) {
-      __ Mov(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>());
-      __ Mov(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>());
-    } else if (destination.IsFpuRegisterPair()) {
-      __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
-                 source.AsRegisterPairLow<Register>(),
-                 source.AsRegisterPairHigh<Register>());
-    } else {
-      DCHECK(destination.IsDoubleStackSlot()) << destination;
-      DCHECK(ExpectedPairLayout(source));
-      __ StoreToOffset(
-          kStoreWordPair, source.AsRegisterPairLow<Register>(), SP, destination.GetStackIndex());
-    }
-  } else if (source.IsFpuRegisterPair()) {
-    if (destination.IsRegisterPair()) {
-      __ vmovrrd(destination.AsRegisterPairLow<Register>(),
-                 destination.AsRegisterPairHigh<Register>(),
-                 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
-    } else if (destination.IsFpuRegisterPair()) {
-      __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
-               FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
-    } else {
-      DCHECK(destination.IsDoubleStackSlot()) << destination;
-      __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()),
-                        SP,
-                        destination.GetStackIndex());
-    }
-  } else {
-    DCHECK(source.IsConstant()) << source;
-    HConstant* constant = source.GetConstant();
-    if (constant->IsIntConstant() || constant->IsNullConstant()) {
-      int32_t value = CodeGenerator::GetInt32ValueOf(constant);
-      if (destination.IsRegister()) {
-        __ LoadImmediate(destination.AsRegister<Register>(), value);
-      } else {
-        DCHECK(destination.IsStackSlot());
-        __ LoadImmediate(IP, value);
-        __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
-      }
-    } else if (constant->IsLongConstant()) {
-      int64_t value = constant->AsLongConstant()->GetValue();
-      if (destination.IsRegisterPair()) {
-        __ LoadImmediate(destination.AsRegisterPairLow<Register>(), Low32Bits(value));
-        __ LoadImmediate(destination.AsRegisterPairHigh<Register>(), High32Bits(value));
-      } else {
-        DCHECK(destination.IsDoubleStackSlot()) << destination;
-        __ LoadImmediate(IP, Low32Bits(value));
-        __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
-        __ LoadImmediate(IP, High32Bits(value));
-        __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize));
-      }
-    } else if (constant->IsDoubleConstant()) {
-      double value = constant->AsDoubleConstant()->GetValue();
-      if (destination.IsFpuRegisterPair()) {
-        __ LoadDImmediate(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), value);
-      } else {
-        DCHECK(destination.IsDoubleStackSlot()) << destination;
-        uint64_t int_value = bit_cast<uint64_t, double>(value);
-        __ LoadImmediate(IP, Low32Bits(int_value));
-        __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
-        __ LoadImmediate(IP, High32Bits(int_value));
-        __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize));
-      }
-    } else {
-      DCHECK(constant->IsFloatConstant()) << constant->DebugName();
-      float value = constant->AsFloatConstant()->GetValue();
-      if (destination.IsFpuRegister()) {
-        __ LoadSImmediate(destination.AsFpuRegister<SRegister>(), value);
-      } else {
-        DCHECK(destination.IsStackSlot());
-        __ LoadImmediate(IP, bit_cast<int32_t, float>(value));
-        __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
-      }
-    }
-  }
-}
-
-void ParallelMoveResolverARM::Exchange(Register reg, int mem) {
-  __ Mov(IP, reg);
-  __ LoadFromOffset(kLoadWord, reg, SP, mem);
-  __ StoreToOffset(kStoreWord, IP, SP, mem);
-}
-
-void ParallelMoveResolverARM::Exchange(int mem1, int mem2) {
-  ScratchRegisterScope ensure_scratch(this, IP, R0, codegen_->GetNumberOfCoreRegisters());
-  int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0;
-  __ LoadFromOffset(kLoadWord, static_cast<Register>(ensure_scratch.GetRegister()),
-                    SP, mem1 + stack_offset);
-  __ LoadFromOffset(kLoadWord, IP, SP, mem2 + stack_offset);
-  __ StoreToOffset(kStoreWord, static_cast<Register>(ensure_scratch.GetRegister()),
-                   SP, mem2 + stack_offset);
-  __ StoreToOffset(kStoreWord, IP, SP, mem1 + stack_offset);
-}
-
-void ParallelMoveResolverARM::EmitSwap(size_t index) {
-  MoveOperands* move = moves_[index];
-  Location source = move->GetSource();
-  Location destination = move->GetDestination();
-
-  if (source.IsRegister() && destination.IsRegister()) {
-    DCHECK_NE(source.AsRegister<Register>(), IP);
-    DCHECK_NE(destination.AsRegister<Register>(), IP);
-    __ Mov(IP, source.AsRegister<Register>());
-    __ Mov(source.AsRegister<Register>(), destination.AsRegister<Register>());
-    __ Mov(destination.AsRegister<Register>(), IP);
-  } else if (source.IsRegister() && destination.IsStackSlot()) {
-    Exchange(source.AsRegister<Register>(), destination.GetStackIndex());
-  } else if (source.IsStackSlot() && destination.IsRegister()) {
-    Exchange(destination.AsRegister<Register>(), source.GetStackIndex());
-  } else if (source.IsStackSlot() && destination.IsStackSlot()) {
-    Exchange(source.GetStackIndex(), destination.GetStackIndex());
-  } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
-    __ vmovrs(IP, source.AsFpuRegister<SRegister>());
-    __ vmovs(source.AsFpuRegister<SRegister>(), destination.AsFpuRegister<SRegister>());
-    __ vmovsr(destination.AsFpuRegister<SRegister>(), IP);
-  } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
-    __ vmovdrr(DTMP, source.AsRegisterPairLow<Register>(), source.AsRegisterPairHigh<Register>());
-    __ Mov(source.AsRegisterPairLow<Register>(), destination.AsRegisterPairLow<Register>());
-    __ Mov(source.AsRegisterPairHigh<Register>(), destination.AsRegisterPairHigh<Register>());
-    __ vmovrrd(destination.AsRegisterPairLow<Register>(),
-               destination.AsRegisterPairHigh<Register>(),
-               DTMP);
-  } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
-    Register low_reg = source.IsRegisterPair()
-        ? source.AsRegisterPairLow<Register>()
-        : destination.AsRegisterPairLow<Register>();
-    int mem = source.IsRegisterPair()
-        ? destination.GetStackIndex()
-        : source.GetStackIndex();
-    DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
-    __ vmovdrr(DTMP, low_reg, static_cast<Register>(low_reg + 1));
-    __ LoadFromOffset(kLoadWordPair, low_reg, SP, mem);
-    __ StoreDToOffset(DTMP, SP, mem);
-  } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
-    DRegister first = FromLowSToD(source.AsFpuRegisterPairLow<SRegister>());
-    DRegister second = FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>());
-    __ vmovd(DTMP, first);
-    __ vmovd(first, second);
-    __ vmovd(second, DTMP);
-  } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
-    DRegister reg = source.IsFpuRegisterPair()
-        ? FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())
-        : FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>());
-    int mem = source.IsFpuRegisterPair()
-        ? destination.GetStackIndex()
-        : source.GetStackIndex();
-    __ vmovd(DTMP, reg);
-    __ LoadDFromOffset(reg, SP, mem);
-    __ StoreDToOffset(DTMP, SP, mem);
-  } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
-    SRegister reg = source.IsFpuRegister() ? source.AsFpuRegister<SRegister>()
-                                           : destination.AsFpuRegister<SRegister>();
-    int mem = source.IsFpuRegister()
-        ? destination.GetStackIndex()
-        : source.GetStackIndex();
-
-    __ vmovrs(IP, reg);
-    __ LoadSFromOffset(reg, SP, mem);
-    __ StoreToOffset(kStoreWord, IP, SP, mem);
-  } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
-    Exchange(source.GetStackIndex(), destination.GetStackIndex());
-    Exchange(source.GetHighStackIndex(kArmWordSize), destination.GetHighStackIndex(kArmWordSize));
-  } else {
-    LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
-  }
-}
-
-void ParallelMoveResolverARM::SpillScratch(int reg) {
-  __ Push(static_cast<Register>(reg));
-}
-
-void ParallelMoveResolverARM::RestoreScratch(int reg) {
-  __ Pop(static_cast<Register>(reg));
-}
-
-HLoadClass::LoadKind CodeGeneratorARM::GetSupportedLoadClassKind(
-    HLoadClass::LoadKind desired_class_load_kind) {
-  switch (desired_class_load_kind) {
-    case HLoadClass::LoadKind::kInvalid:
-      LOG(FATAL) << "UNREACHABLE";
-      UNREACHABLE();
-    case HLoadClass::LoadKind::kReferrersClass:
-      break;
-    case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-    case HLoadClass::LoadKind::kBssEntry:
-      DCHECK(!Runtime::Current()->UseJitCompilation());
-      break;
-    case HLoadClass::LoadKind::kJitTableAddress:
-      DCHECK(Runtime::Current()->UseJitCompilation());
-      break;
-    case HLoadClass::LoadKind::kBootImageAddress:
-    case HLoadClass::LoadKind::kRuntimeCall:
-      break;
-  }
-  return desired_class_load_kind;
-}
-
-void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) {
-  HLoadClass::LoadKind load_kind = cls->GetLoadKind();
-  if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
-    InvokeRuntimeCallingConvention calling_convention;
-    CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(
-        cls,
-        Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
-        Location::RegisterLocation(R0));
-    DCHECK_EQ(calling_convention.GetRegisterAt(0), R0);
-    return;
-  }
-  DCHECK(!cls->NeedsAccessCheck());
-
-  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
-  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
-  if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
-  }
-
-  if (load_kind == HLoadClass::LoadKind::kReferrersClass) {
-    locations->SetInAt(0, Location::RequiresRegister());
-  }
-  locations->SetOut(Location::RequiresRegister());
-  if (load_kind == HLoadClass::LoadKind::kBssEntry) {
-    if (!kUseReadBarrier || kUseBakerReadBarrier) {
-      // Rely on the type resolution or initialization and marking to save everything we need.
-      // Note that IP may be clobbered by saving/restoring the live register (only one thanks
-      // to the custom calling convention) or by marking, so we request a different temp.
-      locations->AddTemp(Location::RequiresRegister());
-      RegisterSet caller_saves = RegisterSet::Empty();
-      InvokeRuntimeCallingConvention calling_convention;
-      caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-      // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
-      // that the the kPrimNot result register is the same as the first argument register.
-      locations->SetCustomSlowPathCallerSaves(caller_saves);
-    } else {
-      // For non-Baker read barrier we have a temp-clobbering call.
-    }
-  }
-  if (kUseBakerReadBarrier && kBakerReadBarrierLinkTimeThunksEnableForGcRoots) {
-    if (load_kind == HLoadClass::LoadKind::kBssEntry ||
-        (load_kind == HLoadClass::LoadKind::kReferrersClass &&
-            !Runtime::Current()->UseJitCompilation())) {
-      locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister));
-    }
-  }
-}
-
-// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
-// move.
-void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
-  HLoadClass::LoadKind load_kind = cls->GetLoadKind();
-  if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
-    codegen_->GenerateLoadClassRuntimeCall(cls);
-    return;
-  }
-  DCHECK(!cls->NeedsAccessCheck());
-
-  LocationSummary* locations = cls->GetLocations();
-  Location out_loc = locations->Out();
-  Register out = out_loc.AsRegister<Register>();
-
-  const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
-      ? kWithoutReadBarrier
-      : kCompilerReadBarrierOption;
-  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_
-      Register current_method = locations->InAt(0).AsRegister<Register>();
-      GenerateGcRootFieldLoad(cls,
-                              out_loc,
-                              current_method,
-                              ArtMethod::DeclaringClassOffset().Int32Value(),
-                              read_barrier_option);
-      break;
-    }
-    case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
-      DCHECK(codegen_->GetCompilerOptions().IsBootImage());
-      DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
-      CodeGeneratorARM::PcRelativePatchInfo* labels =
-          codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
-      __ BindTrackedLabel(&labels->movw_label);
-      __ movw(out, /* placeholder */ 0u);
-      __ BindTrackedLabel(&labels->movt_label);
-      __ movt(out, /* placeholder */ 0u);
-      __ BindTrackedLabel(&labels->add_pc_label);
-      __ add(out, out, ShifterOperand(PC));
-      break;
-    }
-    case HLoadClass::LoadKind::kBootImageAddress: {
-      DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
-      uint32_t address = dchecked_integral_cast<uint32_t>(
-          reinterpret_cast<uintptr_t>(cls->GetClass().Get()));
-      DCHECK_NE(address, 0u);
-      __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
-      break;
-    }
-    case HLoadClass::LoadKind::kBssEntry: {
-      Register temp = (!kUseReadBarrier || kUseBakerReadBarrier)
-          ? locations->GetTemp(0).AsRegister<Register>()
-          : out;
-      CodeGeneratorARM::PcRelativePatchInfo* labels =
-          codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
-      __ BindTrackedLabel(&labels->movw_label);
-      __ movw(temp, /* placeholder */ 0u);
-      __ BindTrackedLabel(&labels->movt_label);
-      __ movt(temp, /* placeholder */ 0u);
-      __ BindTrackedLabel(&labels->add_pc_label);
-      __ add(temp, temp, ShifterOperand(PC));
-      GenerateGcRootFieldLoad(cls, out_loc, temp, /* offset */ 0, read_barrier_option);
-      generate_null_check = true;
-      break;
-    }
-    case HLoadClass::LoadKind::kJitTableAddress: {
-      __ LoadLiteral(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
-                                                               cls->GetTypeIndex(),
-                                                               cls->GetClass()));
-      // /* GcRoot<mirror::Class> */ out = *out
-      GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option);
-      break;
-    }
-    case HLoadClass::LoadKind::kRuntimeCall:
-    case HLoadClass::LoadKind::kInvalid:
-      LOG(FATAL) << "UNREACHABLE";
-      UNREACHABLE();
-  }
-
-  if (generate_null_check || cls->MustGenerateClinitCheck()) {
-    DCHECK(cls->CanCallRuntime());
-    SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM(
-        cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
-    codegen_->AddSlowPath(slow_path);
-    if (generate_null_check) {
-      __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
-    }
-    if (cls->MustGenerateClinitCheck()) {
-      GenerateClassInitializationCheck(slow_path, out);
-    } else {
-      __ Bind(slow_path->GetExitLabel());
-    }
-  }
-}
-
-void LocationsBuilderARM::VisitClinitCheck(HClinitCheck* check) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
-  locations->SetInAt(0, Location::RequiresRegister());
-  if (check->HasUses()) {
-    locations->SetOut(Location::SameAsFirstInput());
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitClinitCheck(HClinitCheck* check) {
-  // We assume the class is not null.
-  SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM(
-      check->GetLoadClass(), check, check->GetDexPc(), true);
-  codegen_->AddSlowPath(slow_path);
-  GenerateClassInitializationCheck(slow_path,
-                                   check->GetLocations()->InAt(0).AsRegister<Register>());
-}
-
-void InstructionCodeGeneratorARM::GenerateClassInitializationCheck(
-    SlowPathCodeARM* slow_path, Register class_reg) {
-  __ LoadFromOffset(kLoadWord, IP, class_reg, mirror::Class::StatusOffset().Int32Value());
-  __ cmp(IP, ShifterOperand(mirror::Class::kStatusInitialized));
-  __ b(slow_path->GetEntryLabel(), LT);
-  // Even if the initialized flag is set, we may be in a situation where caches are not synced
-  // properly. Therefore, we do a memory fence.
-  __ dmb(ISH);
-  __ Bind(slow_path->GetExitLabel());
-}
-
-HLoadString::LoadKind CodeGeneratorARM::GetSupportedLoadStringKind(
-    HLoadString::LoadKind desired_string_load_kind) {
-  switch (desired_string_load_kind) {
-    case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
-    case HLoadString::LoadKind::kBssEntry:
-      DCHECK(!Runtime::Current()->UseJitCompilation());
-      break;
-    case HLoadString::LoadKind::kJitTableAddress:
-      DCHECK(Runtime::Current()->UseJitCompilation());
-      break;
-    case HLoadString::LoadKind::kBootImageAddress:
-    case HLoadString::LoadKind::kRuntimeCall:
-      break;
-  }
-  return desired_string_load_kind;
-}
-
-void LocationsBuilderARM::VisitLoadString(HLoadString* load) {
-  LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
-  HLoadString::LoadKind load_kind = load->GetLoadKind();
-  if (load_kind == HLoadString::LoadKind::kRuntimeCall) {
-    locations->SetOut(Location::RegisterLocation(R0));
-  } else {
-    locations->SetOut(Location::RequiresRegister());
-    if (load_kind == HLoadString::LoadKind::kBssEntry) {
-      if (!kUseReadBarrier || kUseBakerReadBarrier) {
-        // Rely on the pResolveString and marking to save everything we need, including temps.
-        // Note that IP may be clobbered by saving/restoring the live register (only one thanks
-        // to the custom calling convention) or by marking, so we request a different temp.
-        locations->AddTemp(Location::RequiresRegister());
-        RegisterSet caller_saves = RegisterSet::Empty();
-        InvokeRuntimeCallingConvention calling_convention;
-        caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-        // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
-        // that the the kPrimNot result register is the same as the first argument register.
-        locations->SetCustomSlowPathCallerSaves(caller_saves);
-        if (kUseBakerReadBarrier && kBakerReadBarrierLinkTimeThunksEnableForGcRoots) {
-          locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister));
-        }
-      } else {
-        // For non-Baker read barrier we have a temp-clobbering call.
-      }
-    }
-  }
-}
-
-// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
-// move.
-void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
-  LocationSummary* locations = load->GetLocations();
-  Location out_loc = locations->Out();
-  Register out = out_loc.AsRegister<Register>();
-  HLoadString::LoadKind load_kind = load->GetLoadKind();
-
-  switch (load_kind) {
-    case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
-      DCHECK(codegen_->GetCompilerOptions().IsBootImage());
-      CodeGeneratorARM::PcRelativePatchInfo* labels =
-          codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
-      __ BindTrackedLabel(&labels->movw_label);
-      __ movw(out, /* placeholder */ 0u);
-      __ BindTrackedLabel(&labels->movt_label);
-      __ movt(out, /* placeholder */ 0u);
-      __ BindTrackedLabel(&labels->add_pc_label);
-      __ add(out, out, ShifterOperand(PC));
-      return;  // No dex cache slow path.
-    }
-    case HLoadString::LoadKind::kBootImageAddress: {
-      uint32_t address = dchecked_integral_cast<uint32_t>(
-          reinterpret_cast<uintptr_t>(load->GetString().Get()));
-      DCHECK_NE(address, 0u);
-      __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
-      return;  // No dex cache slow path.
-    }
-    case HLoadString::LoadKind::kBssEntry: {
-      DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
-      Register temp = (!kUseReadBarrier || kUseBakerReadBarrier)
-          ? locations->GetTemp(0).AsRegister<Register>()
-          : out;
-      CodeGeneratorARM::PcRelativePatchInfo* labels =
-          codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
-      __ BindTrackedLabel(&labels->movw_label);
-      __ movw(temp, /* placeholder */ 0u);
-      __ BindTrackedLabel(&labels->movt_label);
-      __ movt(temp, /* placeholder */ 0u);
-      __ BindTrackedLabel(&labels->add_pc_label);
-      __ add(temp, temp, ShifterOperand(PC));
-      GenerateGcRootFieldLoad(load, out_loc, temp, /* offset */ 0, kCompilerReadBarrierOption);
-      SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load);
-      codegen_->AddSlowPath(slow_path);
-      __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
-      __ Bind(slow_path->GetExitLabel());
-      return;
-    }
-    case HLoadString::LoadKind::kJitTableAddress: {
-      __ LoadLiteral(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
-                                                                load->GetStringIndex(),
-                                                                load->GetString()));
-      // /* GcRoot<mirror::String> */ out = *out
-      GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
-      return;
-    }
-    default:
-      break;
-  }
-
-  // TODO: Consider re-adding the compiler code to do string dex cache lookup again.
-  DCHECK(load_kind == HLoadString::LoadKind::kRuntimeCall);
-  InvokeRuntimeCallingConvention calling_convention;
-  DCHECK_EQ(calling_convention.GetRegisterAt(0), out);
-  __ LoadImmediate(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
-  codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
-  CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
-}
-
-static int32_t GetExceptionTlsOffset() {
-  return Thread::ExceptionOffset<kArmPointerSize>().Int32Value();
-}
-
-void LocationsBuilderARM::VisitLoadException(HLoadException* load) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
-  locations->SetOut(Location::RequiresRegister());
-}
-
-void InstructionCodeGeneratorARM::VisitLoadException(HLoadException* load) {
-  Register out = load->GetLocations()->Out().AsRegister<Register>();
-  __ LoadFromOffset(kLoadWord, out, TR, GetExceptionTlsOffset());
-}
-
-void LocationsBuilderARM::VisitClearException(HClearException* clear) {
-  new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall);
-}
-
-void InstructionCodeGeneratorARM::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
-  __ LoadImmediate(IP, 0);
-  __ StoreToOffset(kStoreWord, IP, TR, GetExceptionTlsOffset());
-}
-
-void LocationsBuilderARM::VisitThrow(HThrow* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
-  InvokeRuntimeCallingConvention calling_convention;
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-}
-
-void InstructionCodeGeneratorARM::VisitThrow(HThrow* instruction) {
-  codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
-  CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
-}
-
-// Temp is used for read barrier.
-static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
-  if (kEmitCompilerReadBarrier &&
-       (kUseBakerReadBarrier ||
-          type_check_kind == TypeCheckKind::kAbstractClassCheck ||
-          type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
-          type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
-    return 1;
-  }
-  return 0;
-}
-
-// Interface case has 3 temps, one for holding the number of interfaces, one for the current
-// interface pointer, one for loading the current interface.
-// The other checks have one temp for loading the object's class.
-static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
-  if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
-    return 3;
-  }
-  return 1 + NumberOfInstanceOfTemps(type_check_kind);
-}
-
-void LocationsBuilderARM::VisitInstanceOf(HInstanceOf* instruction) {
-  LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
-  TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
-  bool baker_read_barrier_slow_path = false;
-  switch (type_check_kind) {
-    case TypeCheckKind::kExactCheck:
-    case TypeCheckKind::kAbstractClassCheck:
-    case TypeCheckKind::kClassHierarchyCheck:
-    case TypeCheckKind::kArrayObjectCheck:
-      call_kind =
-          kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
-      baker_read_barrier_slow_path = kUseBakerReadBarrier;
-      break;
-    case TypeCheckKind::kArrayCheck:
-    case TypeCheckKind::kUnresolvedCheck:
-    case TypeCheckKind::kInterfaceCheck:
-      call_kind = LocationSummary::kCallOnSlowPath;
-      break;
-  }
-
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
-  if (baker_read_barrier_slow_path) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
-  }
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  // The "out" register is used as a temporary, so it overlaps with the inputs.
-  // Note that TypeCheckSlowPathARM uses this register too.
-  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-  locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
-  if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
-    codegen_->MaybeAddBakerCcEntrypointTempForFields(locations);
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) {
-  TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
-  LocationSummary* locations = instruction->GetLocations();
-  Location obj_loc = locations->InAt(0);
-  Register obj = obj_loc.AsRegister<Register>();
-  Register cls = locations->InAt(1).AsRegister<Register>();
-  Location out_loc = locations->Out();
-  Register out = out_loc.AsRegister<Register>();
-  const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
-  DCHECK_LE(num_temps, 1u);
-  Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
-  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
-  uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
-  uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
-  uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
-  Label done;
-  Label* const final_label = codegen_->GetFinalLabel(instruction, &done);
-  SlowPathCodeARM* slow_path = nullptr;
-
-  // Return 0 if `obj` is null.
-  // avoid null check if we know obj is not null.
-  if (instruction->MustDoNullCheck()) {
-    DCHECK_NE(out, obj);
-    __ LoadImmediate(out, 0);
-    __ CompareAndBranchIfZero(obj, final_label);
-  }
-
-  switch (type_check_kind) {
-    case TypeCheckKind::kExactCheck: {
-      // /* HeapReference<Class> */ out = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        out_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp_loc,
-                                        kCompilerReadBarrierOption);
-      // Classes must be equal for the instanceof to succeed.
-      __ cmp(out, ShifterOperand(cls));
-      // We speculatively set the result to false without changing the condition
-      // flags, which allows us to avoid some branching later.
-      __ mov(out, ShifterOperand(0), AL, kCcKeep);
-
-      // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
-      // we check that the output is in a low register, so that a 16-bit MOV
-      // encoding can be used.
-      if (ArmAssembler::IsLowRegister(out)) {
-        __ it(EQ);
-        __ mov(out, ShifterOperand(1), EQ);
-      } else {
-        __ b(final_label, NE);
-        __ LoadImmediate(out, 1);
-      }
-
-      break;
-    }
-
-    case TypeCheckKind::kAbstractClassCheck: {
-      // /* HeapReference<Class> */ out = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        out_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp_loc,
-                                        kCompilerReadBarrierOption);
-      // If the class is abstract, we eagerly fetch the super class of the
-      // object to avoid doing a comparison we know will fail.
-      Label loop;
-      __ Bind(&loop);
-      // /* HeapReference<Class> */ out = out->super_class_
-      GenerateReferenceLoadOneRegister(instruction,
-                                       out_loc,
-                                       super_offset,
-                                       maybe_temp_loc,
-                                       kCompilerReadBarrierOption);
-      // If `out` is null, we use it for the result, and jump to the final label.
-      __ CompareAndBranchIfZero(out, final_label);
-      __ cmp(out, ShifterOperand(cls));
-      __ b(&loop, NE);
-      __ LoadImmediate(out, 1);
-      break;
-    }
-
-    case TypeCheckKind::kClassHierarchyCheck: {
-      // /* HeapReference<Class> */ out = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        out_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp_loc,
-                                        kCompilerReadBarrierOption);
-      // Walk over the class hierarchy to find a match.
-      Label loop, success;
-      __ Bind(&loop);
-      __ cmp(out, ShifterOperand(cls));
-      __ b(&success, EQ);
-      // /* HeapReference<Class> */ out = out->super_class_
-      GenerateReferenceLoadOneRegister(instruction,
-                                       out_loc,
-                                       super_offset,
-                                       maybe_temp_loc,
-                                       kCompilerReadBarrierOption);
-      // This is essentially a null check, but it sets the condition flags to the
-      // proper value for the code that follows the loop, i.e. not `EQ`.
-      __ cmp(out, ShifterOperand(1));
-      __ b(&loop, HS);
-
-      // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
-      // we check that the output is in a low register, so that a 16-bit MOV
-      // encoding can be used.
-      if (ArmAssembler::IsLowRegister(out)) {
-        // If `out` is null, we use it for the result, and the condition flags
-        // have already been set to `NE`, so the IT block that comes afterwards
-        // (and which handles the successful case) turns into a NOP (instead of
-        // overwriting `out`).
-        __ Bind(&success);
-        // There is only one branch to the `success` label (which is bound to this
-        // IT block), and it has the same condition, `EQ`, so in that case the MOV
-        // is executed.
-        __ it(EQ);
-        __ mov(out, ShifterOperand(1), EQ);
-      } else {
-        // If `out` is null, we use it for the result, and jump to the final label.
-        __ b(final_label);
-        __ Bind(&success);
-        __ LoadImmediate(out, 1);
-      }
-
-      break;
-    }
-
-    case TypeCheckKind::kArrayObjectCheck: {
-      // /* HeapReference<Class> */ out = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        out_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp_loc,
-                                        kCompilerReadBarrierOption);
-      // Do an exact check.
-      Label exact_check;
-      __ cmp(out, ShifterOperand(cls));
-      __ b(&exact_check, EQ);
-      // Otherwise, we need to check that the object's class is a non-primitive array.
-      // /* HeapReference<Class> */ out = out->component_type_
-      GenerateReferenceLoadOneRegister(instruction,
-                                       out_loc,
-                                       component_offset,
-                                       maybe_temp_loc,
-                                       kCompilerReadBarrierOption);
-      // If `out` is null, we use it for the result, and jump to the final label.
-      __ CompareAndBranchIfZero(out, final_label);
-      __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
-      static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-      __ cmp(out, ShifterOperand(0));
-      // We speculatively set the result to false without changing the condition
-      // flags, which allows us to avoid some branching later.
-      __ mov(out, ShifterOperand(0), AL, kCcKeep);
-
-      // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
-      // we check that the output is in a low register, so that a 16-bit MOV
-      // encoding can be used.
-      if (ArmAssembler::IsLowRegister(out)) {
-        __ Bind(&exact_check);
-        __ it(EQ);
-        __ mov(out, ShifterOperand(1), EQ);
-      } else {
-        __ b(final_label, NE);
-        __ Bind(&exact_check);
-        __ LoadImmediate(out, 1);
-      }
-
-      break;
-    }
-
-    case TypeCheckKind::kArrayCheck: {
-      // No read barrier since the slow path will retry upon failure.
-      // /* HeapReference<Class> */ out = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        out_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp_loc,
-                                        kWithoutReadBarrier);
-      __ cmp(out, ShifterOperand(cls));
-      DCHECK(locations->OnlyCallsOnSlowPath());
-      slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction,
-                                                                    /* is_fatal */ false);
-      codegen_->AddSlowPath(slow_path);
-      __ b(slow_path->GetEntryLabel(), NE);
-      __ LoadImmediate(out, 1);
-      break;
-    }
-
-    case TypeCheckKind::kUnresolvedCheck:
-    case TypeCheckKind::kInterfaceCheck: {
-      // Note that we indeed only call on slow path, but we always go
-      // into the slow path for the unresolved and interface check
-      // cases.
-      //
-      // We cannot directly call the InstanceofNonTrivial runtime
-      // entry point without resorting to a type checking slow path
-      // here (i.e. by calling InvokeRuntime directly), as it would
-      // require to assign fixed registers for the inputs of this
-      // HInstanceOf instruction (following the runtime calling
-      // convention), which might be cluttered by the potential first
-      // read barrier emission at the beginning of this method.
-      //
-      // TODO: Introduce a new runtime entry point taking the object
-      // to test (instead of its class) as argument, and let it deal
-      // with the read barrier issues. This will let us refactor this
-      // case of the `switch` code as it was previously (with a direct
-      // call to the runtime not using a type checking slow path).
-      // This should also be beneficial for the other cases above.
-      DCHECK(locations->OnlyCallsOnSlowPath());
-      slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction,
-                                                                    /* is_fatal */ false);
-      codegen_->AddSlowPath(slow_path);
-      __ b(slow_path->GetEntryLabel());
-      break;
-    }
-  }
-
-  if (done.IsLinked()) {
-    __ Bind(&done);
-  }
-
-  if (slow_path != nullptr) {
-    __ Bind(slow_path->GetExitLabel());
-  }
-}
-
-void LocationsBuilderARM::VisitCheckCast(HCheckCast* instruction) {
-  LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
-  bool throws_into_catch = instruction->CanThrowIntoCatchBlock();
-
-  TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
-  switch (type_check_kind) {
-    case TypeCheckKind::kExactCheck:
-    case TypeCheckKind::kAbstractClassCheck:
-    case TypeCheckKind::kClassHierarchyCheck:
-    case TypeCheckKind::kArrayObjectCheck:
-      call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ?
-          LocationSummary::kCallOnSlowPath :
-          LocationSummary::kNoCall;  // In fact, call on a fatal (non-returning) slow path.
-      break;
-    case TypeCheckKind::kArrayCheck:
-    case TypeCheckKind::kUnresolvedCheck:
-    case TypeCheckKind::kInterfaceCheck:
-      call_kind = LocationSummary::kCallOnSlowPath;
-      break;
-  }
-
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
-}
-
-void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) {
-  TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
-  LocationSummary* locations = instruction->GetLocations();
-  Location obj_loc = locations->InAt(0);
-  Register obj = obj_loc.AsRegister<Register>();
-  Register cls = locations->InAt(1).AsRegister<Register>();
-  Location temp_loc = locations->GetTemp(0);
-  Register temp = temp_loc.AsRegister<Register>();
-  const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
-  DCHECK_LE(num_temps, 3u);
-  Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
-  Location maybe_temp3_loc = (num_temps >= 3) ? locations->GetTemp(2) : Location::NoLocation();
-  const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
-  const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
-  const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
-  const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
-  const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
-  const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
-  const uint32_t object_array_data_offset =
-      mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
-
-  // Always false for read barriers since we may need to go to the entrypoint for non-fatal cases
-  // from false negatives. The false negatives may come from avoiding read barriers below. Avoiding
-  // read barriers is done for performance and code size reasons.
-  bool is_type_check_slow_path_fatal = false;
-  if (!kEmitCompilerReadBarrier) {
-    is_type_check_slow_path_fatal =
-        (type_check_kind == TypeCheckKind::kExactCheck ||
-         type_check_kind == TypeCheckKind::kAbstractClassCheck ||
-         type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
-         type_check_kind == TypeCheckKind::kArrayObjectCheck) &&
-        !instruction->CanThrowIntoCatchBlock();
-  }
-  SlowPathCodeARM* type_check_slow_path =
-      new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction,
-                                                        is_type_check_slow_path_fatal);
-  codegen_->AddSlowPath(type_check_slow_path);
-
-  Label done;
-  Label* final_label = codegen_->GetFinalLabel(instruction, &done);
-  // Avoid null check if we know obj is not null.
-  if (instruction->MustDoNullCheck()) {
-    __ CompareAndBranchIfZero(obj, final_label);
-  }
-
-  switch (type_check_kind) {
-    case TypeCheckKind::kExactCheck:
-    case TypeCheckKind::kArrayCheck: {
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        temp_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp2_loc,
-                                        kWithoutReadBarrier);
-
-      __ cmp(temp, ShifterOperand(cls));
-      // Jump to slow path for throwing the exception or doing a
-      // more involved array check.
-      __ b(type_check_slow_path->GetEntryLabel(), NE);
-      break;
-    }
-
-    case TypeCheckKind::kAbstractClassCheck: {
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        temp_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp2_loc,
-                                        kWithoutReadBarrier);
-
-      // If the class is abstract, we eagerly fetch the super class of the
-      // object to avoid doing a comparison we know will fail.
-      Label loop;
-      __ Bind(&loop);
-      // /* HeapReference<Class> */ temp = temp->super_class_
-      GenerateReferenceLoadOneRegister(instruction,
-                                       temp_loc,
-                                       super_offset,
-                                       maybe_temp2_loc,
-                                       kWithoutReadBarrier);
-
-      // If the class reference currently in `temp` is null, jump to the slow path to throw the
-      // exception.
-      __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
-
-      // Otherwise, compare the classes.
-      __ cmp(temp, ShifterOperand(cls));
-      __ b(&loop, NE);
-      break;
-    }
-
-    case TypeCheckKind::kClassHierarchyCheck: {
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        temp_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp2_loc,
-                                        kWithoutReadBarrier);
-
-      // Walk over the class hierarchy to find a match.
-      Label loop;
-      __ Bind(&loop);
-      __ cmp(temp, ShifterOperand(cls));
-      __ b(final_label, EQ);
-
-      // /* HeapReference<Class> */ temp = temp->super_class_
-      GenerateReferenceLoadOneRegister(instruction,
-                                       temp_loc,
-                                       super_offset,
-                                       maybe_temp2_loc,
-                                       kWithoutReadBarrier);
-
-      // If the class reference currently in `temp` is null, jump to the slow path to throw the
-      // exception.
-      __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
-      // Otherwise, jump to the beginning of the loop.
-      __ b(&loop);
-      break;
-    }
-
-    case TypeCheckKind::kArrayObjectCheck: {
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        temp_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp2_loc,
-                                        kWithoutReadBarrier);
-
-      // Do an exact check.
-      __ cmp(temp, ShifterOperand(cls));
-      __ b(final_label, EQ);
-
-      // Otherwise, we need to check that the object's class is a non-primitive array.
-      // /* HeapReference<Class> */ temp = temp->component_type_
-      GenerateReferenceLoadOneRegister(instruction,
-                                       temp_loc,
-                                       component_offset,
-                                       maybe_temp2_loc,
-                                       kWithoutReadBarrier);
-      // If the component type is null, jump to the slow path to throw the exception.
-      __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
-      // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
-      // to further check that this component type is not a primitive type.
-      __ LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
-      static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot");
-      __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
-      break;
-    }
-
-    case TypeCheckKind::kUnresolvedCheck:
-      // We always go into the type check slow path for the unresolved check case.
-      // We cannot directly call the CheckCast runtime entry point
-      // without resorting to a type checking slow path here (i.e. by
-      // calling InvokeRuntime directly), as it would require to
-      // assign fixed registers for the inputs of this HInstanceOf
-      // instruction (following the runtime calling convention), which
-      // might be cluttered by the potential first read barrier
-      // emission at the beginning of this method.
-
-      __ b(type_check_slow_path->GetEntryLabel());
-      break;
-
-    case TypeCheckKind::kInterfaceCheck: {
-      // Avoid read barriers to improve performance of the fast path. We can not get false
-      // positives by doing this.
-      // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        temp_loc,
-                                        obj_loc,
-                                        class_offset,
-                                        maybe_temp2_loc,
-                                        kWithoutReadBarrier);
-
-      // /* HeapReference<Class> */ temp = temp->iftable_
-      GenerateReferenceLoadTwoRegisters(instruction,
-                                        temp_loc,
-                                        temp_loc,
-                                        iftable_offset,
-                                        maybe_temp2_loc,
-                                        kWithoutReadBarrier);
-      // Iftable is never null.
-      __ ldr(maybe_temp2_loc.AsRegister<Register>(), Address(temp, array_length_offset));
-      // Loop through the iftable and check if any class matches.
-      Label start_loop;
-      __ Bind(&start_loop);
-      __ CompareAndBranchIfZero(maybe_temp2_loc.AsRegister<Register>(),
-                                type_check_slow_path->GetEntryLabel());
-      __ ldr(maybe_temp3_loc.AsRegister<Register>(), Address(temp, object_array_data_offset));
-      __ MaybeUnpoisonHeapReference(maybe_temp3_loc.AsRegister<Register>());
-      // Go to next interface.
-      __ add(temp, temp, ShifterOperand(2 * kHeapReferenceSize));
-      __ sub(maybe_temp2_loc.AsRegister<Register>(),
-             maybe_temp2_loc.AsRegister<Register>(),
-             ShifterOperand(2));
-      // Compare the classes and continue the loop if they do not match.
-      __ cmp(cls, ShifterOperand(maybe_temp3_loc.AsRegister<Register>()));
-      __ b(&start_loop, NE);
-      break;
-    }
-  }
-
-  if (done.IsLinked()) {
-    __ Bind(&done);
-  }
-
-  __ Bind(type_check_slow_path->GetExitLabel());
-}
-
-void LocationsBuilderARM::VisitMonitorOperation(HMonitorOperation* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
-  InvokeRuntimeCallingConvention calling_convention;
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-}
-
-void InstructionCodeGeneratorARM::VisitMonitorOperation(HMonitorOperation* instruction) {
-  codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
-                          instruction,
-                          instruction->GetDexPc());
-  if (instruction->IsEnter()) {
-    CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
-  } else {
-    CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
-  }
-}
-
-void LocationsBuilderARM::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction, AND); }
-void LocationsBuilderARM::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction, ORR); }
-void LocationsBuilderARM::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction, EOR); }
-
-void LocationsBuilderARM::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  DCHECK(instruction->GetResultType() == Primitive::kPrimInt
-         || instruction->GetResultType() == Primitive::kPrimLong);
-  // Note: GVN reorders commutative operations to have the constant on the right hand side.
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode));
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void InstructionCodeGeneratorARM::VisitAnd(HAnd* instruction) {
-  HandleBitwiseOperation(instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitOr(HOr* instruction) {
-  HandleBitwiseOperation(instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitXor(HXor* instruction) {
-  HandleBitwiseOperation(instruction);
-}
-
-
-void LocationsBuilderARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  DCHECK(instruction->GetResultType() == Primitive::kPrimInt
-         || instruction->GetResultType() == Primitive::kPrimLong);
-
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void InstructionCodeGeneratorARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  Location first = locations->InAt(0);
-  Location second = locations->InAt(1);
-  Location out = locations->Out();
-
-  if (instruction->GetResultType() == Primitive::kPrimInt) {
-    Register first_reg = first.AsRegister<Register>();
-    ShifterOperand second_reg(second.AsRegister<Register>());
-    Register out_reg = out.AsRegister<Register>();
-
-    switch (instruction->GetOpKind()) {
-      case HInstruction::kAnd:
-        __ bic(out_reg, first_reg, second_reg);
-        break;
-      case HInstruction::kOr:
-        __ orn(out_reg, first_reg, second_reg);
-        break;
-      // There is no EON on arm.
-      case HInstruction::kXor:
-      default:
-        LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
-        UNREACHABLE();
-    }
-    return;
-
-  } else {
-    DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
-    Register first_low = first.AsRegisterPairLow<Register>();
-    Register first_high = first.AsRegisterPairHigh<Register>();
-    ShifterOperand second_low(second.AsRegisterPairLow<Register>());
-    ShifterOperand second_high(second.AsRegisterPairHigh<Register>());
-    Register out_low = out.AsRegisterPairLow<Register>();
-    Register out_high = out.AsRegisterPairHigh<Register>();
-
-    switch (instruction->GetOpKind()) {
-      case HInstruction::kAnd:
-        __ bic(out_low, first_low, second_low);
-        __ bic(out_high, first_high, second_high);
-        break;
-      case HInstruction::kOr:
-        __ orn(out_low, first_low, second_low);
-        __ orn(out_high, first_high, second_high);
-        break;
-      // There is no EON on arm.
-      case HInstruction::kXor:
-      default:
-        LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
-        UNREACHABLE();
-    }
-  }
-}
-
-void LocationsBuilderARM::VisitDataProcWithShifterOp(
-    HDataProcWithShifterOp* instruction) {
-  DCHECK(instruction->GetType() == Primitive::kPrimInt ||
-         instruction->GetType() == Primitive::kPrimLong);
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  const bool overlap = instruction->GetType() == Primitive::kPrimLong &&
-                       HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind());
-
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(),
-                    overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap);
-}
-
-void InstructionCodeGeneratorARM::VisitDataProcWithShifterOp(
-    HDataProcWithShifterOp* instruction) {
-  const LocationSummary* const locations = instruction->GetLocations();
-  const HInstruction::InstructionKind kind = instruction->GetInstrKind();
-  const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
-  const Location left = locations->InAt(0);
-  const Location right = locations->InAt(1);
-  const Location out = locations->Out();
-
-  if (instruction->GetType() == Primitive::kPrimInt) {
-    DCHECK(!HDataProcWithShifterOp::IsExtensionOp(op_kind));
-
-    const Register second = instruction->InputAt(1)->GetType() == Primitive::kPrimLong
-        ? right.AsRegisterPairLow<Register>()
-        : right.AsRegister<Register>();
-
-    GenerateDataProcInstruction(kind,
-                                out.AsRegister<Register>(),
-                                left.AsRegister<Register>(),
-                                ShifterOperand(second,
-                                               ShiftFromOpKind(op_kind),
-                                               instruction->GetShiftAmount()),
-                                codegen_);
-  } else {
-    DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong);
-
-    if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
-      const Register second = right.AsRegister<Register>();
-
-      DCHECK_NE(out.AsRegisterPairLow<Register>(), second);
-      GenerateDataProc(kind,
-                       out,
-                       left,
-                       ShifterOperand(second),
-                       ShifterOperand(second, ASR, 31),
-                       codegen_);
-    } else {
-      GenerateLongDataProc(instruction, codegen_);
-    }
-  }
-}
-
-void InstructionCodeGeneratorARM::GenerateAndConst(Register out, Register first, uint32_t value) {
-  // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
-  if (value == 0xffffffffu) {
-    if (out != first) {
-      __ mov(out, ShifterOperand(first));
-    }
-    return;
-  }
-  if (value == 0u) {
-    __ mov(out, ShifterOperand(0));
-    return;
-  }
-  ShifterOperand so;
-  if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, AND, value, &so)) {
-    __ and_(out, first, so);
-  } else if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, BIC, ~value, &so)) {
-    __ bic(out, first, ShifterOperand(~value));
-  } else {
-    DCHECK(IsPowerOfTwo(value + 1));
-    __ ubfx(out, first, 0, WhichPowerOf2(value + 1));
-  }
-}
-
-void InstructionCodeGeneratorARM::GenerateOrrConst(Register out, Register first, uint32_t value) {
-  // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier).
-  if (value == 0u) {
-    if (out != first) {
-      __ mov(out, ShifterOperand(first));
-    }
-    return;
-  }
-  if (value == 0xffffffffu) {
-    __ mvn(out, ShifterOperand(0));
-    return;
-  }
-  ShifterOperand so;
-  if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORR, value, &so)) {
-    __ orr(out, first, so);
-  } else {
-    DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORN, ~value, &so));
-    __ orn(out, first, ShifterOperand(~value));
-  }
-}
-
-void InstructionCodeGeneratorARM::GenerateEorConst(Register out, Register first, uint32_t value) {
-  // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier).
-  if (value == 0u) {
-    if (out != first) {
-      __ mov(out, ShifterOperand(first));
-    }
-    return;
-  }
-  __ eor(out, first, ShifterOperand(value));
-}
-
-void InstructionCodeGeneratorARM::GenerateAddLongConst(Location out,
-                                                       Location first,
-                                                       uint64_t value) {
-  Register out_low = out.AsRegisterPairLow<Register>();
-  Register out_high = out.AsRegisterPairHigh<Register>();
-  Register first_low = first.AsRegisterPairLow<Register>();
-  Register first_high = first.AsRegisterPairHigh<Register>();
-  uint32_t value_low = Low32Bits(value);
-  uint32_t value_high = High32Bits(value);
-  if (value_low == 0u) {
-    if (out_low != first_low) {
-      __ mov(out_low, ShifterOperand(first_low));
-    }
-    __ AddConstant(out_high, first_high, value_high);
-    return;
-  }
-  __ AddConstantSetFlags(out_low, first_low, value_low);
-  ShifterOperand so;
-  if (__ ShifterOperandCanHold(out_high, first_high, ADC, value_high, kCcDontCare, &so)) {
-    __ adc(out_high, first_high, so);
-  } else if (__ ShifterOperandCanHold(out_low, first_low, SBC, ~value_high, kCcDontCare, &so)) {
-    __ sbc(out_high, first_high, so);
-  } else {
-    LOG(FATAL) << "Unexpected constant " << value_high;
-    UNREACHABLE();
-  }
-}
-
-void InstructionCodeGeneratorARM::HandleBitwiseOperation(HBinaryOperation* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  Location first = locations->InAt(0);
-  Location second = locations->InAt(1);
-  Location out = locations->Out();
-
-  if (second.IsConstant()) {
-    uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
-    uint32_t value_low = Low32Bits(value);
-    if (instruction->GetResultType() == Primitive::kPrimInt) {
-      Register first_reg = first.AsRegister<Register>();
-      Register out_reg = out.AsRegister<Register>();
-      if (instruction->IsAnd()) {
-        GenerateAndConst(out_reg, first_reg, value_low);
-      } else if (instruction->IsOr()) {
-        GenerateOrrConst(out_reg, first_reg, value_low);
-      } else {
-        DCHECK(instruction->IsXor());
-        GenerateEorConst(out_reg, first_reg, value_low);
-      }
-    } else {
-      DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
-      uint32_t value_high = High32Bits(value);
-      Register first_low = first.AsRegisterPairLow<Register>();
-      Register first_high = first.AsRegisterPairHigh<Register>();
-      Register out_low = out.AsRegisterPairLow<Register>();
-      Register out_high = out.AsRegisterPairHigh<Register>();
-      if (instruction->IsAnd()) {
-        GenerateAndConst(out_low, first_low, value_low);
-        GenerateAndConst(out_high, first_high, value_high);
-      } else if (instruction->IsOr()) {
-        GenerateOrrConst(out_low, first_low, value_low);
-        GenerateOrrConst(out_high, first_high, value_high);
-      } else {
-        DCHECK(instruction->IsXor());
-        GenerateEorConst(out_low, first_low, value_low);
-        GenerateEorConst(out_high, first_high, value_high);
-      }
-    }
-    return;
-  }
-
-  if (instruction->GetResultType() == Primitive::kPrimInt) {
-    Register first_reg = first.AsRegister<Register>();
-    ShifterOperand second_reg(second.AsRegister<Register>());
-    Register out_reg = out.AsRegister<Register>();
-    if (instruction->IsAnd()) {
-      __ and_(out_reg, first_reg, second_reg);
-    } else if (instruction->IsOr()) {
-      __ orr(out_reg, first_reg, second_reg);
-    } else {
-      DCHECK(instruction->IsXor());
-      __ eor(out_reg, first_reg, second_reg);
-    }
-  } else {
-    DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
-    Register first_low = first.AsRegisterPairLow<Register>();
-    Register first_high = first.AsRegisterPairHigh<Register>();
-    ShifterOperand second_low(second.AsRegisterPairLow<Register>());
-    ShifterOperand second_high(second.AsRegisterPairHigh<Register>());
-    Register out_low = out.AsRegisterPairLow<Register>();
-    Register out_high = out.AsRegisterPairHigh<Register>();
-    if (instruction->IsAnd()) {
-      __ and_(out_low, first_low, second_low);
-      __ and_(out_high, first_high, second_high);
-    } else if (instruction->IsOr()) {
-      __ orr(out_low, first_low, second_low);
-      __ orr(out_high, first_high, second_high);
-    } else {
-      DCHECK(instruction->IsXor());
-      __ eor(out_low, first_low, second_low);
-      __ eor(out_high, first_high, second_high);
-    }
-  }
-}
-
-void InstructionCodeGeneratorARM::GenerateReferenceLoadOneRegister(
-    HInstruction* instruction,
-    Location out,
-    uint32_t offset,
-    Location maybe_temp,
-    ReadBarrierOption read_barrier_option) {
-  Register out_reg = out.AsRegister<Register>();
-  if (read_barrier_option == kWithReadBarrier) {
-    CHECK(kEmitCompilerReadBarrier);
-    DCHECK(maybe_temp.IsRegister()) << maybe_temp;
-    if (kUseBakerReadBarrier) {
-      // Load with fast path based Baker's read barrier.
-      // /* HeapReference<Object> */ out = *(out + offset)
-      codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false);
-    } else {
-      // Load with slow path based read barrier.
-      // Save the value of `out` into `maybe_temp` before overwriting it
-      // in the following move operation, as we will need it for the
-      // read barrier below.
-      __ Mov(maybe_temp.AsRegister<Register>(), out_reg);
-      // /* HeapReference<Object> */ out = *(out + offset)
-      __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
-      codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset);
-    }
-  } else {
-    // Plain load with no read barrier.
-    // /* HeapReference<Object> */ out = *(out + offset)
-    __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
-    __ MaybeUnpoisonHeapReference(out_reg);
-  }
-}
-
-void InstructionCodeGeneratorARM::GenerateReferenceLoadTwoRegisters(
-    HInstruction* instruction,
-    Location out,
-    Location obj,
-    uint32_t offset,
-    Location maybe_temp,
-    ReadBarrierOption read_barrier_option) {
-  Register out_reg = out.AsRegister<Register>();
-  Register obj_reg = obj.AsRegister<Register>();
-  if (read_barrier_option == kWithReadBarrier) {
-    CHECK(kEmitCompilerReadBarrier);
-    if (kUseBakerReadBarrier) {
-      DCHECK(maybe_temp.IsRegister()) << maybe_temp;
-      // Load with fast path based Baker's read barrier.
-      // /* HeapReference<Object> */ out = *(obj + offset)
-      codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false);
-    } else {
-      // Load with slow path based read barrier.
-      // /* HeapReference<Object> */ out = *(obj + offset)
-      __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
-      codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
-    }
-  } else {
-    // Plain load with no read barrier.
-    // /* HeapReference<Object> */ out = *(obj + offset)
-    __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
-    __ MaybeUnpoisonHeapReference(out_reg);
-  }
-}
-
-void InstructionCodeGeneratorARM::GenerateGcRootFieldLoad(HInstruction* instruction,
-                                                          Location root,
-                                                          Register obj,
-                                                          uint32_t offset,
-                                                          ReadBarrierOption read_barrier_option) {
-  Register root_reg = root.AsRegister<Register>();
-  if (read_barrier_option == kWithReadBarrier) {
-    DCHECK(kEmitCompilerReadBarrier);
-    if (kUseBakerReadBarrier) {
-      // Fast path implementation of art::ReadBarrier::BarrierForRoot when
-      // Baker's read barrier are used.
-      if (kBakerReadBarrierLinkTimeThunksEnableForGcRoots &&
-          !Runtime::Current()->UseJitCompilation()) {
-        // Note that we do not actually check the value of `GetIsGcMarking()`
-        // to decide whether to mark the loaded GC root or not.  Instead, we
-        // load into `temp` (actually kBakerCcEntrypointRegister) the read
-        // barrier mark introspection entrypoint. If `temp` is null, it means
-        // that `GetIsGcMarking()` is false, and vice versa.
-        //
-        // We use link-time generated thunks for the slow path. That thunk
-        // checks the reference and jumps to the entrypoint if needed.
-        //
-        //     temp = Thread::Current()->pReadBarrierMarkIntrospection
-        //     lr = &return_address;
-        //     GcRoot<mirror::Object> root = *(obj+offset);  // Original reference load.
-        //     if (temp != nullptr) {
-        //        goto gc_root_thunk<root_reg>(lr)
-        //     }
-        //   return_address:
-
-        CheckLastTempIsBakerCcEntrypointRegister(instruction);
-        bool narrow = CanEmitNarrowLdr(root_reg, obj, offset);
-        uint32_t custom_data =
-            linker::Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg, narrow);
-        Label* bne_label = codegen_->NewBakerReadBarrierPatch(custom_data);
-
-        // entrypoint_reg =
-        //     Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection.
-        DCHECK_EQ(IP, 12);
-        const int32_t entry_point_offset =
-            CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(IP);
-        __ LoadFromOffset(kLoadWord, kBakerCcEntrypointRegister, TR, entry_point_offset);
-
-        Label return_address;
-        __ AdrCode(LR, &return_address);
-        __ CmpConstant(kBakerCcEntrypointRegister, 0);
-        // Currently the offset is always within range. If that changes,
-        // we shall have to split the load the same way as for fields.
-        DCHECK_LT(offset, kReferenceLoadMinFarOffset);
-        DCHECK(!down_cast<Thumb2Assembler*>(GetAssembler())->IsForced32Bit());
-        ScopedForce32Bit maybe_force_32bit(down_cast<Thumb2Assembler*>(GetAssembler()), !narrow);
-        int old_position = GetAssembler()->GetBuffer()->GetPosition();
-        __ LoadFromOffset(kLoadWord, root_reg, obj, offset);
-        EmitPlaceholderBne(codegen_, bne_label);
-        __ Bind(&return_address);
-        DCHECK_EQ(old_position - GetAssembler()->GetBuffer()->GetPosition(),
-                  narrow ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET
-                         : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET);
-      } else {
-        // Note that we do not actually check the value of
-        // `GetIsGcMarking()` to decide whether to mark the loaded GC
-        // root or not.  Instead, we load into `temp` the read barrier
-        // mark entry point corresponding to register `root`. If `temp`
-        // is null, it means that `GetIsGcMarking()` is false, and vice
-        // versa.
-        //
-        //   temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
-        //   GcRoot<mirror::Object> root = *(obj+offset);  // Original reference load.
-        //   if (temp != nullptr) {  // <=> Thread::Current()->GetIsGcMarking()
-        //     // Slow path.
-        //     root = temp(root);  // root = ReadBarrier::Mark(root);  // Runtime entry point call.
-        //   }
-
-        // Slow path marking the GC root `root`. The entrypoint will already be loaded in `temp`.
-        Location temp = Location::RegisterLocation(LR);
-        SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(
-            instruction, root, /* entrypoint */ temp);
-        codegen_->AddSlowPath(slow_path);
-
-        // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
-        const int32_t entry_point_offset =
-            CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(root.reg());
-        // Loading the entrypoint does not require a load acquire since it is only changed when
-        // threads are suspended or running a checkpoint.
-        __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, entry_point_offset);
-
-        // /* GcRoot<mirror::Object> */ root = *(obj + offset)
-        __ LoadFromOffset(kLoadWord, root_reg, obj, offset);
-        static_assert(
-            sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
-            "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
-            "have different sizes.");
-        static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
-                      "art::mirror::CompressedReference<mirror::Object> and int32_t "
-                      "have different sizes.");
-
-        // The entrypoint is null when the GC is not marking, this prevents one load compared to
-        // checking GetIsGcMarking.
-        __ CompareAndBranchIfNonZero(temp.AsRegister<Register>(), slow_path->GetEntryLabel());
-        __ Bind(slow_path->GetExitLabel());
-      }
-    } else {
-      // GC root loaded through a slow path for read barriers other
-      // than Baker's.
-      // /* GcRoot<mirror::Object>* */ root = obj + offset
-      __ AddConstant(root_reg, obj, offset);
-      // /* mirror::Object* */ root = root->Read()
-      codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
-    }
-  } else {
-    // Plain GC root load with no read barrier.
-    // /* GcRoot<mirror::Object> */ root = *(obj + offset)
-    __ LoadFromOffset(kLoadWord, 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 CodeGeneratorARM::MaybeAddBakerCcEntrypointTempForFields(LocationSummary* locations) {
-  DCHECK(kEmitCompilerReadBarrier);
-  DCHECK(kUseBakerReadBarrier);
-  if (kBakerReadBarrierLinkTimeThunksEnableForFields) {
-    if (!Runtime::Current()->UseJitCompilation()) {
-      locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister));
-    }
-  }
-}
-
-void CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
-                                                             Location ref,
-                                                             Register obj,
-                                                             uint32_t offset,
-                                                             Location temp,
-                                                             bool needs_null_check) {
-  DCHECK(kEmitCompilerReadBarrier);
-  DCHECK(kUseBakerReadBarrier);
-
-  if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
-      !Runtime::Current()->UseJitCompilation()) {
-    // Note that we do not actually check the value of `GetIsGcMarking()`
-    // to decide whether to mark the loaded reference or not.  Instead, we
-    // load into `temp` (actually kBakerCcEntrypointRegister) the read
-    // barrier mark introspection entrypoint. If `temp` is null, it means
-    // that `GetIsGcMarking()` is false, and vice versa.
-    //
-    // We use link-time generated thunks for the slow path. That thunk checks
-    // the holder and jumps to the entrypoint if needed. If the holder is not
-    // gray, it creates a fake dependency and returns to the LDR instruction.
-    //
-    //     temp = Thread::Current()->pReadBarrierMarkIntrospection
-    //     lr = &gray_return_address;
-    //     if (temp != nullptr) {
-    //        goto field_thunk<holder_reg, base_reg>(lr)
-    //     }
-    //   not_gray_return_address:
-    //     // Original reference load. If the offset is too large to fit
-    //     // into LDR, we use an adjusted base register here.
-    //     HeapReference<mirror::Object> reference = *(obj+offset);
-    //   gray_return_address:
-
-    DCHECK_ALIGNED(offset, sizeof(mirror::HeapReference<mirror::Object>));
-    Register ref_reg = ref.AsRegister<Register>();
-    bool narrow = CanEmitNarrowLdr(ref_reg, obj, offset);
-    Register base = obj;
-    if (offset >= kReferenceLoadMinFarOffset) {
-      base = temp.AsRegister<Register>();
-      DCHECK_NE(base, kBakerCcEntrypointRegister);
-      static_assert(IsPowerOfTwo(kReferenceLoadMinFarOffset), "Expecting a power of 2.");
-      __ AddConstant(base, obj, offset & ~(kReferenceLoadMinFarOffset - 1u));
-      offset &= (kReferenceLoadMinFarOffset - 1u);
-      // Use narrow LDR only for small offsets. Generating narrow encoding LDR for the large
-      // offsets with `(offset & (kReferenceLoadMinFarOffset - 1u)) < 32u` would most likely
-      // increase the overall code size when taking the generated thunks into account.
-      DCHECK(!narrow);
-    }
-    CheckLastTempIsBakerCcEntrypointRegister(instruction);
-    uint32_t custom_data =
-        linker::Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData(base, obj, narrow);
-    Label* bne_label = NewBakerReadBarrierPatch(custom_data);
-
-    // entrypoint_reg =
-    //     Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection.
-    DCHECK_EQ(IP, 12);
-    const int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(IP);
-    __ LoadFromOffset(kLoadWord, kBakerCcEntrypointRegister, TR, entry_point_offset);
-
-    Label return_address;
-    __ AdrCode(LR, &return_address);
-    __ CmpConstant(kBakerCcEntrypointRegister, 0);
-    EmitPlaceholderBne(this, bne_label);
-    DCHECK_LT(offset, kReferenceLoadMinFarOffset);
-    DCHECK(!down_cast<Thumb2Assembler*>(GetAssembler())->IsForced32Bit());
-    ScopedForce32Bit maybe_force_32bit(down_cast<Thumb2Assembler*>(GetAssembler()), !narrow);
-    int old_position = GetAssembler()->GetBuffer()->GetPosition();
-    __ LoadFromOffset(kLoadWord, ref_reg, base, offset);
-    if (needs_null_check) {
-      MaybeRecordImplicitNullCheck(instruction);
-    }
-    GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
-    __ Bind(&return_address);
-    DCHECK_EQ(old_position - GetAssembler()->GetBuffer()->GetPosition(),
-              narrow ? BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET
-                     : BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET);
-    return;
-  }
-
-  // /* HeapReference<Object> */ ref = *(obj + offset)
-  Location no_index = Location::NoLocation();
-  ScaleFactor no_scale_factor = TIMES_1;
-  GenerateReferenceLoadWithBakerReadBarrier(
-      instruction, ref, obj, offset, no_index, no_scale_factor, temp, needs_null_check);
-}
-
-void CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
-                                                             Location ref,
-                                                             Register obj,
-                                                             uint32_t data_offset,
-                                                             Location index,
-                                                             Location temp,
-                                                             bool needs_null_check) {
-  DCHECK(kEmitCompilerReadBarrier);
-  DCHECK(kUseBakerReadBarrier);
-
-  static_assert(
-      sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
-      "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
-  ScaleFactor scale_factor = TIMES_4;
-
-  if (kBakerReadBarrierLinkTimeThunksEnableForArrays &&
-      !Runtime::Current()->UseJitCompilation()) {
-    // Note that we do not actually check the value of `GetIsGcMarking()`
-    // to decide whether to mark the loaded reference or not.  Instead, we
-    // load into `temp` (actually kBakerCcEntrypointRegister) the read
-    // barrier mark introspection entrypoint. If `temp` is null, it means
-    // that `GetIsGcMarking()` is false, and vice versa.
-    //
-    // We use link-time generated thunks for the slow path. That thunk checks
-    // the holder and jumps to the entrypoint if needed. If the holder is not
-    // gray, it creates a fake dependency and returns to the LDR instruction.
-    //
-    //     temp = Thread::Current()->pReadBarrierMarkIntrospection
-    //     lr = &gray_return_address;
-    //     if (temp != nullptr) {
-    //        goto field_thunk<holder_reg, base_reg>(lr)
-    //     }
-    //   not_gray_return_address:
-    //     // Original reference load. If the offset is too large to fit
-    //     // into LDR, we use an adjusted base register here.
-    //     HeapReference<mirror::Object> reference = data[index];
-    //   gray_return_address:
-
-    DCHECK(index.IsValid());
-    Register index_reg = index.AsRegister<Register>();
-    Register ref_reg = ref.AsRegister<Register>();
-    Register data_reg = temp.AsRegister<Register>();
-    DCHECK_NE(data_reg, kBakerCcEntrypointRegister);
-
-    CheckLastTempIsBakerCcEntrypointRegister(instruction);
-    uint32_t custom_data =
-        linker::Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(data_reg);
-    Label* bne_label = NewBakerReadBarrierPatch(custom_data);
-
-    // entrypoint_reg =
-    //     Thread::Current()->pReadBarrierMarkReg16, i.e. pReadBarrierMarkIntrospection.
-    DCHECK_EQ(IP, 12);
-    const int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(IP);
-    __ LoadFromOffset(kLoadWord, kBakerCcEntrypointRegister, TR, entry_point_offset);
-    __ AddConstant(data_reg, obj, data_offset);
-
-    Label return_address;
-    __ AdrCode(LR, &return_address);
-    __ CmpConstant(kBakerCcEntrypointRegister, 0);
-    EmitPlaceholderBne(this, bne_label);
-    ScopedForce32Bit maybe_force_32bit(down_cast<Thumb2Assembler*>(GetAssembler()));
-    int old_position = GetAssembler()->GetBuffer()->GetPosition();
-    __ ldr(ref_reg, Address(data_reg, index_reg, LSL, scale_factor));
-    DCHECK(!needs_null_check);  // The thunk cannot handle the null check.
-    GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
-    __ Bind(&return_address);
-    DCHECK_EQ(old_position - GetAssembler()->GetBuffer()->GetPosition(),
-              BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET);
-    return;
-  }
-
-  // /* HeapReference<Object> */ ref =
-  //     *(obj + data_offset + index * sizeof(HeapReference<Object>))
-  GenerateReferenceLoadWithBakerReadBarrier(
-      instruction, ref, obj, data_offset, index, scale_factor, temp, needs_null_check);
-}
-
-void CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
-                                                                 Location ref,
-                                                                 Register obj,
-                                                                 uint32_t offset,
-                                                                 Location index,
-                                                                 ScaleFactor scale_factor,
-                                                                 Location temp,
-                                                                 bool needs_null_check) {
-  DCHECK(kEmitCompilerReadBarrier);
-  DCHECK(kUseBakerReadBarrier);
-
-  // Query `art::Thread::Current()->GetIsGcMarking()` to decide
-  // whether we need to enter the slow path to mark the reference.
-  // Then, in the slow path, check the gray bit in the lock word of
-  // the reference's holder (`obj`) to decide whether to mark `ref` or
-  // not.
-  //
-  // Note that we do not actually check the value of `GetIsGcMarking()`;
-  // instead, we load into `temp2` the read barrier mark entry point
-  // corresponding to register `ref`. If `temp2` is null, it means
-  // that `GetIsGcMarking()` is false, and vice versa.
-  //
-  //   temp2 = Thread::Current()->pReadBarrierMarkReg ## root.reg()
-  //   if (temp2 != nullptr) {  // <=> Thread::Current()->GetIsGcMarking()
-  //     // Slow path.
-  //     uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
-  //     lfence;  // Load fence or artificial data dependency to prevent load-load reordering
-  //     HeapReference<mirror::Object> ref = *src;  // Original reference load.
-  //     bool is_gray = (rb_state == ReadBarrier::GrayState());
-  //     if (is_gray) {
-  //       ref = temp2(ref);  // ref = ReadBarrier::Mark(ref);  // Runtime entry point call.
-  //     }
-  //   } else {
-  //     HeapReference<mirror::Object> ref = *src;  // Original reference load.
-  //   }
-
-  Register temp_reg = temp.AsRegister<Register>();
-
-  // Slow path marking the object `ref` when the GC is marking. The
-  // entrypoint will already be loaded in `temp2`.
-  Location temp2 = Location::RegisterLocation(LR);
-  SlowPathCodeARM* slow_path =
-      new (GetGraph()->GetArena()) LoadReferenceWithBakerReadBarrierSlowPathARM(
-          instruction,
-          ref,
-          obj,
-          offset,
-          index,
-          scale_factor,
-          needs_null_check,
-          temp_reg,
-          /* entrypoint */ temp2);
-  AddSlowPath(slow_path);
-
-  // temp2 = Thread::Current()->pReadBarrierMarkReg ## ref.reg()
-  const int32_t entry_point_offset =
-      CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref.reg());
-  // Loading the entrypoint does not require a load acquire since it is only changed when
-  // threads are suspended or running a checkpoint.
-  __ LoadFromOffset(kLoadWord, temp2.AsRegister<Register>(), TR, entry_point_offset);
-  // The entrypoint is null when the GC is not marking, this prevents one load compared to
-  // checking GetIsGcMarking.
-  __ CompareAndBranchIfNonZero(temp2.AsRegister<Register>(), slow_path->GetEntryLabel());
-  // Fast path: the GC is not marking: just load the reference.
-  GenerateRawReferenceLoad(instruction, ref, obj, offset, index, scale_factor, needs_null_check);
-  __ Bind(slow_path->GetExitLabel());
-}
-
-void CodeGeneratorARM::UpdateReferenceFieldWithBakerReadBarrier(HInstruction* instruction,
-                                                                Location ref,
-                                                                Register obj,
-                                                                Location field_offset,
-                                                                Location temp,
-                                                                bool needs_null_check,
-                                                                Register temp2) {
-  DCHECK(kEmitCompilerReadBarrier);
-  DCHECK(kUseBakerReadBarrier);
-
-  // Query `art::Thread::Current()->GetIsGcMarking()` to decide
-  // whether we need to enter the slow path to update the reference
-  // field within `obj`.  Then, in the slow path, check the gray bit
-  // in the lock word of the reference's holder (`obj`) to decide
-  // whether to mark `ref` and update the field or not.
-  //
-  // Note that we do not actually check the value of `GetIsGcMarking()`;
-  // instead, we load into `temp3` the read barrier mark entry point
-  // corresponding to register `ref`. If `temp3` is null, it means
-  // that `GetIsGcMarking()` is false, and vice versa.
-  //
-  //   temp3 = Thread::Current()->pReadBarrierMarkReg ## root.reg()
-  //   if (temp3 != nullptr) {  // <=> Thread::Current()->GetIsGcMarking()
-  //     // Slow path.
-  //     uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
-  //     lfence;  // Load fence or artificial data dependency to prevent load-load reordering
-  //     HeapReference<mirror::Object> ref = *src;  // Original reference load.
-  //     bool is_gray = (rb_state == ReadBarrier::GrayState());
-  //     if (is_gray) {
-  //       old_ref = ref;
-  //       ref = temp3(ref);  // ref = ReadBarrier::Mark(ref);  // Runtime entry point call.
-  //       compareAndSwapObject(obj, field_offset, old_ref, ref);
-  //     }
-  //   }
-
-  Register temp_reg = temp.AsRegister<Register>();
-
-  // Slow path updating the object reference at address `obj +
-  // field_offset` when the GC is marking. The entrypoint will already
-  // be loaded in `temp3`.
-  Location temp3 = Location::RegisterLocation(LR);
-  SlowPathCodeARM* slow_path =
-      new (GetGraph()->GetArena()) LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM(
-          instruction,
-          ref,
-          obj,
-          /* offset */ 0u,
-          /* index */ field_offset,
-          /* scale_factor */ ScaleFactor::TIMES_1,
-          needs_null_check,
-          temp_reg,
-          temp2,
-          /* entrypoint */ temp3);
-  AddSlowPath(slow_path);
-
-  // temp3 = Thread::Current()->pReadBarrierMarkReg ## ref.reg()
-  const int32_t entry_point_offset =
-      CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref.reg());
-  // Loading the entrypoint does not require a load acquire since it is only changed when
-  // threads are suspended or running a checkpoint.
-  __ LoadFromOffset(kLoadWord, temp3.AsRegister<Register>(), TR, entry_point_offset);
-  // The entrypoint is null when the GC is not marking, this prevents one load compared to
-  // checking GetIsGcMarking.
-  __ CompareAndBranchIfNonZero(temp3.AsRegister<Register>(), slow_path->GetEntryLabel());
-  // Fast path: the GC is not marking: nothing to do (the field is
-  // up-to-date, and we don't need to load the reference).
-  __ Bind(slow_path->GetExitLabel());
-}
-
-void CodeGeneratorARM::GenerateRawReferenceLoad(HInstruction* instruction,
-                                                Location ref,
-                                                Register obj,
-                                                uint32_t offset,
-                                                Location index,
-                                                ScaleFactor scale_factor,
-                                                bool needs_null_check) {
-  Register ref_reg = ref.AsRegister<Register>();
-
-  if (index.IsValid()) {
-    // Load types involving an "index": ArrayGet,
-    // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
-    // intrinsics.
-    // /* HeapReference<mirror::Object> */ ref = *(obj + offset + (index << scale_factor))
-    if (index.IsConstant()) {
-      size_t computed_offset =
-          (index.GetConstant()->AsIntConstant()->GetValue() << scale_factor) + offset;
-      __ LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset);
-    } else {
-      // Handle the special case of the
-      // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
-      // intrinsics, which use a register pair as index ("long
-      // offset"), of which only the low part contains data.
-      Register index_reg = index.IsRegisterPair()
-          ? index.AsRegisterPairLow<Register>()
-          : index.AsRegister<Register>();
-      __ add(IP, obj, ShifterOperand(index_reg, LSL, scale_factor));
-      __ LoadFromOffset(kLoadWord, ref_reg, IP, offset);
-    }
-  } else {
-    // /* HeapReference<mirror::Object> */ ref = *(obj + offset)
-    __ LoadFromOffset(kLoadWord, ref_reg, obj, offset);
-  }
-
-  if (needs_null_check) {
-    MaybeRecordImplicitNullCheck(instruction);
-  }
-
-  // Object* ref = ref_addr->AsMirrorPtr()
-  __ MaybeUnpoisonHeapReference(ref_reg);
-}
-
-void CodeGeneratorARM::GenerateReadBarrierSlow(HInstruction* instruction,
-                                               Location out,
-                                               Location ref,
-                                               Location obj,
-                                               uint32_t offset,
-                                               Location index) {
-  DCHECK(kEmitCompilerReadBarrier);
-
-  // Insert a slow path based read barrier *after* the reference load.
-  //
-  // If heap poisoning is enabled, the unpoisoning of the loaded
-  // reference will be carried out by the runtime within the slow
-  // path.
-  //
-  // Note that `ref` currently does not get unpoisoned (when heap
-  // poisoning is enabled), which is alright as the `ref` argument is
-  // not used by the artReadBarrierSlow entry point.
-  //
-  // TODO: Unpoison `ref` when it is used by artReadBarrierSlow.
-  SlowPathCodeARM* slow_path = new (GetGraph()->GetArena())
-      ReadBarrierForHeapReferenceSlowPathARM(instruction, out, ref, obj, offset, index);
-  AddSlowPath(slow_path);
-
-  __ b(slow_path->GetEntryLabel());
-  __ Bind(slow_path->GetExitLabel());
-}
-
-void CodeGeneratorARM::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
-                                                    Location out,
-                                                    Location ref,
-                                                    Location obj,
-                                                    uint32_t offset,
-                                                    Location index) {
-  if (kEmitCompilerReadBarrier) {
-    // Baker's read barriers shall be handled by the fast path
-    // (CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier).
-    DCHECK(!kUseBakerReadBarrier);
-    // If heap poisoning is enabled, unpoisoning will be taken care of
-    // by the runtime within the slow path.
-    GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
-  } else if (kPoisonHeapReferences) {
-    __ UnpoisonHeapReference(out.AsRegister<Register>());
-  }
-}
-
-void CodeGeneratorARM::GenerateReadBarrierForRootSlow(HInstruction* instruction,
-                                                      Location out,
-                                                      Location root) {
-  DCHECK(kEmitCompilerReadBarrier);
-
-  // Insert a slow path based read barrier *after* the GC root load.
-  //
-  // Note that GC roots are not affected by heap poisoning, so we do
-  // not need to do anything special for this here.
-  SlowPathCodeARM* slow_path =
-      new (GetGraph()->GetArena()) ReadBarrierForRootSlowPathARM(instruction, out, root);
-  AddSlowPath(slow_path);
-
-  __ b(slow_path->GetEntryLabel());
-  __ Bind(slow_path->GetExitLabel());
-}
-
-HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOrDirectDispatch(
-      const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
-  return desired_dispatch_info;
-}
-
-Register CodeGeneratorARM::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke,
-                                                                 Register temp) {
-  DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
-  Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
-  if (!invoke->GetLocations()->Intrinsified()) {
-    return location.AsRegister<Register>();
-  }
-  // For intrinsics we allow any location, so it may be on the stack.
-  if (!location.IsRegister()) {
-    __ LoadFromOffset(kLoadWord, temp, SP, location.GetStackIndex());
-    return temp;
-  }
-  // For register locations, check if the register was saved. If so, get it from the stack.
-  // Note: There is a chance that the register was saved but not overwritten, so we could
-  // save one load. However, since this is just an intrinsic slow path we prefer this
-  // simple and more robust approach rather that trying to determine if that's the case.
-  SlowPathCode* slow_path = GetCurrentSlowPath();
-  DCHECK(slow_path != nullptr);  // For intrinsified invokes the call is emitted on the slow path.
-  if (slow_path->IsCoreRegisterSaved(location.AsRegister<Register>())) {
-    int stack_offset = slow_path->GetStackOffsetOfCoreRegister(location.AsRegister<Register>());
-    __ LoadFromOffset(kLoadWord, temp, SP, stack_offset);
-    return temp;
-  }
-  return location.AsRegister<Register>();
-}
-
-void CodeGeneratorARM::GenerateStaticOrDirectCall(
-    HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) {
-  Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
-  switch (invoke->GetMethodLoadKind()) {
-    case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
-      uint32_t offset =
-          GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
-      // temp = thread->string_init_entrypoint
-      __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, offset);
-      break;
-    }
-    case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
-      callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
-      break;
-    case HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative: {
-      DCHECK(GetCompilerOptions().IsBootImage());
-      Register temp_reg = temp.AsRegister<Register>();
-      PcRelativePatchInfo* labels = NewPcRelativeMethodPatch(invoke->GetTargetMethod());
-      __ BindTrackedLabel(&labels->movw_label);
-      __ movw(temp_reg, /* placeholder */ 0u);
-      __ BindTrackedLabel(&labels->movt_label);
-      __ movt(temp_reg, /* placeholder */ 0u);
-      __ BindTrackedLabel(&labels->add_pc_label);
-      __ add(temp_reg, temp_reg, ShifterOperand(PC));
-      break;
-    }
-    case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
-      __ LoadImmediate(temp.AsRegister<Register>(), invoke->GetMethodAddress());
-      break;
-    case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
-      Register temp_reg = temp.AsRegister<Register>();
-      PcRelativePatchInfo* labels = NewMethodBssEntryPatch(
-          MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()));
-      __ BindTrackedLabel(&labels->movw_label);
-      __ movw(temp_reg, /* placeholder */ 0u);
-      __ BindTrackedLabel(&labels->movt_label);
-      __ movt(temp_reg, /* placeholder */ 0u);
-      __ BindTrackedLabel(&labels->add_pc_label);
-      __ add(temp_reg, temp_reg, ShifterOperand(PC));
-      __ LoadFromOffset(kLoadWord, temp_reg, temp_reg, /* offset */ 0);
-      break;
-    }
-    case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: {
-      GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path);
-      return;  // No code pointer retrieval; the runtime performs the call directly.
-    }
-  }
-
-  switch (invoke->GetCodePtrLocation()) {
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
-      __ bl(GetFrameEntryLabel());
-      break;
-    case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
-      // LR = callee_method->entry_point_from_quick_compiled_code_
-      __ LoadFromOffset(
-          kLoadWord, LR, callee_method.AsRegister<Register>(),
-          ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
-      // LR()
-      __ blx(LR);
-      break;
-  }
-  RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
-
-  DCHECK(!IsLeafMethod());
-}
-
-void CodeGeneratorARM::GenerateVirtualCall(
-    HInvokeVirtual* invoke, Location temp_location, SlowPathCode* slow_path) {
-  Register temp = temp_location.AsRegister<Register>();
-  uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
-      invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
-
-  // Use the calling convention instead of the location of the receiver, as
-  // intrinsics may have put the receiver in a different register. In the intrinsics
-  // slow path, the arguments have been moved to the right place, so here we are
-  // guaranteed that the receiver is the first register of the calling convention.
-  InvokeDexCallingConvention calling_convention;
-  Register receiver = calling_convention.GetRegisterAt(0);
-  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
-  // /* HeapReference<Class> */ temp = receiver->klass_
-  __ LoadFromOffset(kLoadWord, temp, receiver, class_offset);
-  MaybeRecordImplicitNullCheck(invoke);
-  // Instead of simply (possibly) unpoisoning `temp` here, we should
-  // emit a read barrier for the previous class reference load.
-  // However this is not required in practice, as this is an
-  // intermediate/temporary reference and because the current
-  // concurrent copying collector keeps the from-space memory
-  // intact/accessible until the end of the marking phase (the
-  // concurrent copying collector may not in the future).
-  __ MaybeUnpoisonHeapReference(temp);
-  // temp = temp->GetMethodAt(method_offset);
-  uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
-      kArmPointerSize).Int32Value();
-  __ LoadFromOffset(kLoadWord, temp, temp, method_offset);
-  // LR = temp->GetEntryPoint();
-  __ LoadFromOffset(kLoadWord, LR, temp, entry_point);
-  // LR();
-  __ blx(LR);
-  RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
-}
-
-CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeMethodPatch(
-    MethodReference target_method) {
-  return NewPcRelativePatch(*target_method.dex_file,
-                            target_method.dex_method_index,
-                            &pc_relative_method_patches_);
-}
-
-CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewMethodBssEntryPatch(
-    MethodReference target_method) {
-  return NewPcRelativePatch(*target_method.dex_file,
-                            target_method.dex_method_index,
-                            &method_bss_entry_patches_);
-}
-
-CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeTypePatch(
-    const DexFile& dex_file, dex::TypeIndex type_index) {
-  return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_);
-}
-
-CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewTypeBssEntryPatch(
-    const DexFile& dex_file, dex::TypeIndex type_index) {
-  return NewPcRelativePatch(dex_file, type_index.index_, &type_bss_entry_patches_);
-}
-
-CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeStringPatch(
-    const DexFile& dex_file, dex::StringIndex string_index) {
-  return NewPcRelativePatch(dex_file, string_index.index_, &pc_relative_string_patches_);
-}
-
-CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativePatch(
-    const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
-  patches->emplace_back(dex_file, offset_or_index);
-  return &patches->back();
-}
-
-Label* CodeGeneratorARM::NewBakerReadBarrierPatch(uint32_t custom_data) {
-  baker_read_barrier_patches_.emplace_back(custom_data);
-  return &baker_read_barrier_patches_.back().label;
-}
-
-Literal* CodeGeneratorARM::DeduplicateBootImageAddressLiteral(uint32_t address) {
-  return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), &uint32_literals_);
-}
-
-Literal* CodeGeneratorARM::DeduplicateJitStringLiteral(const DexFile& dex_file,
-                                                       dex::StringIndex string_index,
-                                                       Handle<mirror::String> handle) {
-  jit_string_roots_.Overwrite(StringReference(&dex_file, string_index),
-                              reinterpret_cast64<uint64_t>(handle.GetReference()));
-  return jit_string_patches_.GetOrCreate(
-      StringReference(&dex_file, string_index),
-      [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
-}
-
-Literal* CodeGeneratorARM::DeduplicateJitClassLiteral(const DexFile& dex_file,
-                                                      dex::TypeIndex type_index,
-                                                      Handle<mirror::Class> handle) {
-  jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index),
-                             reinterpret_cast64<uint64_t>(handle.GetReference()));
-  return jit_class_patches_.GetOrCreate(
-      TypeReference(&dex_file, type_index),
-      [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
-}
-
-template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
-inline void CodeGeneratorARM::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.add_pc_label.IsBound());
-    uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.Position());
-    // Add MOVW patch.
-    DCHECK(info.movw_label.IsBound());
-    uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.Position());
-    linker_patches->push_back(Factory(movw_offset, &dex_file, add_pc_offset, offset_or_index));
-    // Add MOVT patch.
-    DCHECK(info.movt_label.IsBound());
-    uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.Position());
-    linker_patches->push_back(Factory(movt_offset, &dex_file, add_pc_offset, offset_or_index));
-  }
-}
-
-void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
-  DCHECK(linker_patches->empty());
-  size_t size =
-      /* MOVW+MOVT for each entry */ 2u * pc_relative_method_patches_.size() +
-      /* MOVW+MOVT for each entry */ 2u * method_bss_entry_patches_.size() +
-      /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size() +
-      /* MOVW+MOVT for each entry */ 2u * type_bss_entry_patches_.size() +
-      /* MOVW+MOVT for each entry */ 2u * pc_relative_string_patches_.size() +
-      baker_read_barrier_patches_.size();
-  linker_patches->reserve(size);
-  if (GetCompilerOptions().IsBootImage()) {
-    EmitPcRelativeLinkerPatches<LinkerPatch::RelativeMethodPatch>(pc_relative_method_patches_,
-                                                                  linker_patches);
-    EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_,
-                                                                linker_patches);
-    EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_,
-                                                                  linker_patches);
-  } else {
-    DCHECK(pc_relative_method_patches_.empty());
-    DCHECK(pc_relative_type_patches_.empty());
-    EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_,
-                                                                  linker_patches);
-  }
-  EmitPcRelativeLinkerPatches<LinkerPatch::MethodBssEntryPatch>(method_bss_entry_patches_,
-                                                                linker_patches);
-  EmitPcRelativeLinkerPatches<LinkerPatch::TypeBssEntryPatch>(type_bss_entry_patches_,
-                                                              linker_patches);
-  for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) {
-    linker_patches->push_back(LinkerPatch::BakerReadBarrierBranchPatch(info.label.Position(),
-                                                                       info.custom_data));
-  }
-  DCHECK_EQ(size, linker_patches->size());
-}
-
-Literal* CodeGeneratorARM::DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map) {
-  return map->GetOrCreate(
-      value,
-      [this, value]() { return __ NewLiteral<uint32_t>(value); });
-}
-
-void LocationsBuilderARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall);
-  locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
-                     Location::RequiresRegister());
-  locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
-  locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void InstructionCodeGeneratorARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
-  LocationSummary* locations = instr->GetLocations();
-  Register res = locations->Out().AsRegister<Register>();
-  Register accumulator =
-      locations->InAt(HMultiplyAccumulate::kInputAccumulatorIndex).AsRegister<Register>();
-  Register mul_left =
-      locations->InAt(HMultiplyAccumulate::kInputMulLeftIndex).AsRegister<Register>();
-  Register mul_right =
-      locations->InAt(HMultiplyAccumulate::kInputMulRightIndex).AsRegister<Register>();
-
-  if (instr->GetOpKind() == HInstruction::kAdd) {
-    __ mla(res, mul_left, mul_right, accumulator);
-  } else {
-    __ mls(res, mul_left, mul_right, accumulator);
-  }
-}
-
-void LocationsBuilderARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
-  // Nothing to do, this should be removed during prepare for register allocator.
-  LOG(FATAL) << "Unreachable";
-}
-
-void InstructionCodeGeneratorARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
-  // Nothing to do, this should be removed during prepare for register allocator.
-  LOG(FATAL) << "Unreachable";
-}
-
-// Simple implementation of packed switch - generate cascaded compare/jumps.
-void LocationsBuilderARM::VisitPackedSwitch(HPackedSwitch* switch_instr) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall);
-  locations->SetInAt(0, Location::RequiresRegister());
-  if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold &&
-      codegen_->GetAssembler()->IsThumb()) {
-    locations->AddTemp(Location::RequiresRegister());  // We need a temp for the table base.
-    if (switch_instr->GetStartValue() != 0) {
-      locations->AddTemp(Location::RequiresRegister());  // We need a temp for the bias.
-    }
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitPackedSwitch(HPackedSwitch* switch_instr) {
-  int32_t lower_bound = switch_instr->GetStartValue();
-  uint32_t num_entries = switch_instr->GetNumEntries();
-  LocationSummary* locations = switch_instr->GetLocations();
-  Register value_reg = locations->InAt(0).AsRegister<Register>();
-  HBasicBlock* default_block = switch_instr->GetDefaultBlock();
-
-  if (num_entries <= kPackedSwitchCompareJumpThreshold || !codegen_->GetAssembler()->IsThumb()) {
-    // Create a series of compare/jumps.
-    Register temp_reg = IP;
-    // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store
-    // the immediate, because IP is used as the destination register. For the other
-    // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant,
-    // and they can be encoded in the instruction without making use of IP register.
-    __ AddConstantSetFlags(temp_reg, value_reg, -lower_bound);
-
-    const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
-    // Jump to successors[0] if value == lower_bound.
-    __ b(codegen_->GetLabelOf(successors[0]), EQ);
-    int32_t last_index = 0;
-    for (; num_entries - last_index > 2; last_index += 2) {
-      __ AddConstantSetFlags(temp_reg, temp_reg, -2);
-      // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
-      __ b(codegen_->GetLabelOf(successors[last_index + 1]), LO);
-      // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
-      __ b(codegen_->GetLabelOf(successors[last_index + 2]), EQ);
-    }
-    if (num_entries - last_index == 2) {
-      // The last missing case_value.
-      __ CmpConstant(temp_reg, 1);
-      __ b(codegen_->GetLabelOf(successors[last_index + 1]), EQ);
-    }
-
-    // And the default for any other value.
-    if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
-      __ b(codegen_->GetLabelOf(default_block));
-    }
-  } else {
-    // Create a table lookup.
-    Register temp_reg = locations->GetTemp(0).AsRegister<Register>();
-
-    // Materialize a pointer to the switch table
-    std::vector<Label*> labels(num_entries);
-    const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
-    for (uint32_t i = 0; i < num_entries; i++) {
-      labels[i] = codegen_->GetLabelOf(successors[i]);
-    }
-    JumpTable* table = __ CreateJumpTable(std::move(labels), temp_reg);
-
-    // Remove the bias.
-    Register key_reg;
-    if (lower_bound != 0) {
-      key_reg = locations->GetTemp(1).AsRegister<Register>();
-      __ AddConstant(key_reg, value_reg, -lower_bound);
-    } else {
-      key_reg = value_reg;
-    }
-
-    // Check whether the value is in the table, jump to default block if not.
-    __ CmpConstant(key_reg, num_entries - 1);
-    __ b(codegen_->GetLabelOf(default_block), Condition::HI);
-
-    // Load the displacement from the table.
-    __ ldr(temp_reg, Address(temp_reg, key_reg, Shift::LSL, 2));
-
-    // Dispatch is a direct add to the PC (for Thumb2).
-    __ EmitJumpTableDispatch(table, temp_reg);
-  }
-}
-
-void CodeGeneratorARM::MoveFromReturnRegister(Location trg, Primitive::Type type) {
-  if (!trg.IsValid()) {
-    DCHECK_EQ(type, Primitive::kPrimVoid);
-    return;
-  }
-
-  DCHECK_NE(type, Primitive::kPrimVoid);
-
-  Location return_loc = InvokeDexCallingConventionVisitorARM().GetReturnLocation(type);
-  if (return_loc.Equals(trg)) {
-    return;
-  }
-
-  // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged
-  //       with the last branch.
-  if (type == Primitive::kPrimLong) {
-    HParallelMove parallel_move(GetGraph()->GetArena());
-    parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimInt, nullptr);
-    parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimInt, nullptr);
-    GetMoveResolver()->EmitNativeCode(&parallel_move);
-  } else if (type == Primitive::kPrimDouble) {
-    HParallelMove parallel_move(GetGraph()->GetArena());
-    parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimFloat, nullptr);
-    parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimFloat, nullptr);
-    GetMoveResolver()->EmitNativeCode(&parallel_move);
-  } else {
-    // Let the parallel move resolver take care of all of this.
-    HParallelMove parallel_move(GetGraph()->GetArena());
-    parallel_move.AddMove(return_loc, trg, type, nullptr);
-    GetMoveResolver()->EmitNativeCode(&parallel_move);
-  }
-}
-
-void LocationsBuilderARM::VisitClassTableGet(HClassTableGet* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister());
-}
-
-void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
-    uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
-        instruction->GetIndex(), kArmPointerSize).SizeValue();
-    __ LoadFromOffset(kLoadWord,
-                      locations->Out().AsRegister<Register>(),
-                      locations->InAt(0).AsRegister<Register>(),
-                      method_offset);
-  } else {
-    uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
-        instruction->GetIndex(), kArmPointerSize));
-    __ LoadFromOffset(kLoadWord,
-                      locations->Out().AsRegister<Register>(),
-                      locations->InAt(0).AsRegister<Register>(),
-                      mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
-    __ LoadFromOffset(kLoadWord,
-                      locations->Out().AsRegister<Register>(),
-                      locations->Out().AsRegister<Register>(),
-                      method_offset);
-  }
-}
-
-static void PatchJitRootUse(uint8_t* code,
-                            const uint8_t* roots_data,
-                            Literal* literal,
-                            uint64_t index_in_table) {
-  DCHECK(literal->GetLabel()->IsBound());
-  uint32_t literal_offset = literal->GetLabel()->Position();
-  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 CodeGeneratorARM::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
-  for (const auto& entry : jit_string_patches_) {
-    const StringReference& string_reference = entry.first;
-    Literal* table_entry_literal = entry.second;
-    const auto it = jit_string_roots_.find(string_reference);
-    DCHECK(it != jit_string_roots_.end());
-    uint64_t index_in_table = it->second;
-    PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
-  }
-  for (const auto& entry : jit_class_patches_) {
-    const TypeReference& type_reference = entry.first;
-    Literal* table_entry_literal = entry.second;
-    const auto it = jit_class_roots_.find(type_reference);
-    DCHECK(it != jit_class_roots_.end());
-    uint64_t index_in_table = it->second;
-    PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
-  }
-}
-
-#undef __
-#undef QUICK_ENTRY_POINT
-
-}  // namespace arm
-}  // namespace art
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
deleted file mode 100644
index 9280e63..0000000
--- a/compiler/optimizing/code_generator_arm.h
+++ /dev/null
@@ -1,695 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM_H_
-#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM_H_
-
-#include "base/enums.h"
-#include "code_generator.h"
-#include "dex_file_types.h"
-#include "driver/compiler_options.h"
-#include "nodes.h"
-#include "string_reference.h"
-#include "parallel_move_resolver.h"
-#include "type_reference.h"
-#include "utils/arm/assembler_thumb2.h"
-
-namespace art {
-namespace arm {
-
-class CodeGeneratorARM;
-
-// Use a local definition to prevent copying mistakes.
-static constexpr size_t kArmWordSize = static_cast<size_t>(kArmPointerSize);
-static constexpr size_t kArmBitsPerWord = kArmWordSize * kBitsPerByte;
-
-static constexpr Register kParameterCoreRegisters[] = { R1, R2, R3 };
-static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters);
-static constexpr SRegister kParameterFpuRegisters[] =
-    { S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15 };
-static constexpr size_t kParameterFpuRegistersLength = arraysize(kParameterFpuRegisters);
-
-static constexpr Register kArtMethodRegister = R0;
-
-static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1, R2, R3 };
-static constexpr size_t kRuntimeParameterCoreRegistersLength =
-    arraysize(kRuntimeParameterCoreRegisters);
-static constexpr SRegister kRuntimeParameterFpuRegisters[] = { S0, S1, S2, S3 };
-static constexpr size_t kRuntimeParameterFpuRegistersLength =
-    arraysize(kRuntimeParameterFpuRegisters);
-
-class SlowPathCodeARM : public SlowPathCode {
- public:
-  explicit SlowPathCodeARM(HInstruction* instruction) : SlowPathCode(instruction) {}
-
-  void SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) FINAL;
-  void RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) FINAL;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM);
-};
-
-
-class InvokeRuntimeCallingConvention : public CallingConvention<Register, SRegister> {
- public:
-  InvokeRuntimeCallingConvention()
-      : CallingConvention(kRuntimeParameterCoreRegisters,
-                          kRuntimeParameterCoreRegistersLength,
-                          kRuntimeParameterFpuRegisters,
-                          kRuntimeParameterFpuRegistersLength,
-                          kArmPointerSize) {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
-};
-
-constexpr DRegister FromLowSToD(SRegister reg) {
-  DCHECK_EQ(reg % 2, 0);
-  return static_cast<DRegister>(reg / 2);
-}
-
-
-class InvokeDexCallingConvention : public CallingConvention<Register, SRegister> {
- public:
-  InvokeDexCallingConvention()
-      : CallingConvention(kParameterCoreRegisters,
-                          kParameterCoreRegistersLength,
-                          kParameterFpuRegisters,
-                          kParameterFpuRegistersLength,
-                          kArmPointerSize) {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConvention);
-};
-
-class InvokeDexCallingConventionVisitorARM : public InvokeDexCallingConventionVisitor {
- public:
-  InvokeDexCallingConventionVisitorARM() {}
-  virtual ~InvokeDexCallingConventionVisitorARM() {}
-
-  Location GetNextLocation(Primitive::Type type) OVERRIDE;
-  Location GetReturnLocation(Primitive::Type type) const OVERRIDE;
-  Location GetMethodLocation() const OVERRIDE;
-
- private:
-  InvokeDexCallingConvention calling_convention;
-  uint32_t double_index_ = 0;
-
-  DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitorARM);
-};
-
-class FieldAccessCallingConventionARM : public FieldAccessCallingConvention {
- public:
-  FieldAccessCallingConventionARM() {}
-
-  Location GetObjectLocation() const OVERRIDE {
-    return Location::RegisterLocation(R1);
-  }
-  Location GetFieldIndexLocation() const OVERRIDE {
-    return Location::RegisterLocation(R0);
-  }
-  Location GetReturnLocation(Primitive::Type type) const OVERRIDE {
-    return Primitive::Is64BitType(type)
-        ? Location::RegisterPairLocation(R0, R1)
-        : Location::RegisterLocation(R0);
-  }
-  Location GetSetValueLocation(Primitive::Type type, bool is_instance) const OVERRIDE {
-    return Primitive::Is64BitType(type)
-        ? Location::RegisterPairLocation(R2, R3)
-        : (is_instance
-            ? Location::RegisterLocation(R2)
-            : Location::RegisterLocation(R1));
-  }
-  Location GetFpuLocation(Primitive::Type type) const OVERRIDE {
-    return Primitive::Is64BitType(type)
-        ? Location::FpuRegisterPairLocation(S0, S1)
-        : Location::FpuRegisterLocation(S0);
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConventionARM);
-};
-
-class ParallelMoveResolverARM : public ParallelMoveResolverWithSwap {
- public:
-  ParallelMoveResolverARM(ArenaAllocator* allocator, CodeGeneratorARM* codegen)
-      : ParallelMoveResolverWithSwap(allocator), codegen_(codegen) {}
-
-  void EmitMove(size_t index) OVERRIDE;
-  void EmitSwap(size_t index) OVERRIDE;
-  void SpillScratch(int reg) OVERRIDE;
-  void RestoreScratch(int reg) OVERRIDE;
-
-  ArmAssembler* GetAssembler() const;
-
- private:
-  void Exchange(Register reg, int mem);
-  void Exchange(int mem1, int mem2);
-
-  CodeGeneratorARM* const codegen_;
-
-  DISALLOW_COPY_AND_ASSIGN(ParallelMoveResolverARM);
-};
-
-class LocationsBuilderARM : public HGraphVisitor {
- public:
-  LocationsBuilderARM(HGraph* graph, CodeGeneratorARM* codegen)
-      : HGraphVisitor(graph), codegen_(codegen) {}
-
-#define DECLARE_VISIT_INSTRUCTION(name, super)     \
-  void Visit##name(H##name* instr) OVERRIDE;
-
-  FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
-  FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION)
-  FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION)
-
-#undef DECLARE_VISIT_INSTRUCTION
-
-  void VisitInstruction(HInstruction* instruction) OVERRIDE {
-    LOG(FATAL) << "Unreachable instruction " << instruction->DebugName()
-               << " (id " << instruction->GetId() << ")";
-  }
-
- private:
-  void HandleInvoke(HInvoke* invoke);
-  void HandleBitwiseOperation(HBinaryOperation* operation, Opcode opcode);
-  void HandleCondition(HCondition* condition);
-  void HandleIntegerRotate(LocationSummary* locations);
-  void HandleLongRotate(LocationSummary* locations);
-  void HandleShift(HBinaryOperation* operation);
-  void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
-  void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
-
-  Location ArithmeticZeroOrFpuRegister(HInstruction* input);
-  Location ArmEncodableConstantOrRegister(HInstruction* constant, Opcode opcode);
-  bool CanEncodeConstantAsImmediate(HConstant* input_cst, Opcode opcode);
-  bool CanEncodeConstantAsImmediate(uint32_t value, Opcode opcode, SetCc set_cc = kCcDontCare);
-
-  CodeGeneratorARM* const codegen_;
-  InvokeDexCallingConventionVisitorARM parameter_visitor_;
-
-  DISALLOW_COPY_AND_ASSIGN(LocationsBuilderARM);
-};
-
-class InstructionCodeGeneratorARM : public InstructionCodeGenerator {
- public:
-  InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen);
-
-#define DECLARE_VISIT_INSTRUCTION(name, super)     \
-  void Visit##name(H##name* instr) OVERRIDE;
-
-  FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
-  FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION)
-  FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION)
-
-#undef DECLARE_VISIT_INSTRUCTION
-
-  void VisitInstruction(HInstruction* instruction) OVERRIDE {
-    LOG(FATAL) << "Unreachable instruction " << instruction->DebugName()
-               << " (id " << instruction->GetId() << ")";
-  }
-
-  ArmAssembler* GetAssembler() const { return assembler_; }
-
- private:
-  // Generate code for the given suspend check. If not null, `successor`
-  // is the block to branch to if the suspend check is not needed, and after
-  // the suspend call.
-  void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);
-  void GenerateClassInitializationCheck(SlowPathCodeARM* slow_path, Register class_reg);
-  void GenerateAndConst(Register out, Register first, uint32_t value);
-  void GenerateOrrConst(Register out, Register first, uint32_t value);
-  void GenerateEorConst(Register out, Register first, uint32_t value);
-  void GenerateAddLongConst(Location out, Location first, uint64_t value);
-  void HandleBitwiseOperation(HBinaryOperation* operation);
-  void HandleCondition(HCondition* condition);
-  void HandleIntegerRotate(LocationSummary* locations);
-  void HandleLongRotate(HRor* ror);
-  void HandleShift(HBinaryOperation* operation);
-
-  void GenerateWideAtomicStore(Register addr, uint32_t offset,
-                               Register value_lo, Register value_hi,
-                               Register temp1, Register temp2,
-                               HInstruction* instruction);
-  void GenerateWideAtomicLoad(Register addr, uint32_t offset,
-                              Register out_lo, Register out_hi);
-
-  void HandleFieldSet(HInstruction* instruction,
-                      const FieldInfo& field_info,
-                      bool value_can_be_null);
-  void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
-
-  // Generate a heap reference load using one register `out`:
-  //
-  //   out <- *(out + offset)
-  //
-  // while honoring heap poisoning and/or read barriers (if any).
-  //
-  // Location `maybe_temp` is used when generating a read barrier and
-  // shall be a register in that case; it may be an invalid location
-  // otherwise.
-  void GenerateReferenceLoadOneRegister(HInstruction* instruction,
-                                        Location out,
-                                        uint32_t offset,
-                                        Location maybe_temp,
-                                        ReadBarrierOption read_barrier_option);
-  // Generate a heap reference load using two different registers
-  // `out` and `obj`:
-  //
-  //   out <- *(obj + offset)
-  //
-  // while honoring heap poisoning and/or read barriers (if any).
-  //
-  // Location `maybe_temp` is used when generating a Baker's (fast
-  // path) read barrier and shall be a register in that case; it may
-  // be an invalid location otherwise.
-  void GenerateReferenceLoadTwoRegisters(HInstruction* instruction,
-                                         Location out,
-                                         Location obj,
-                                         uint32_t offset,
-                                         Location maybe_temp,
-                                         ReadBarrierOption read_barrier_option);
-  // Generate a GC root reference load:
-  //
-  //   root <- *(obj + offset)
-  //
-  // while honoring read barriers based on read_barrier_option.
-  void GenerateGcRootFieldLoad(HInstruction* instruction,
-                               Location root,
-                               Register obj,
-                               uint32_t offset,
-                               ReadBarrierOption read_barrier_option);
-  void GenerateTestAndBranch(HInstruction* instruction,
-                             size_t condition_input_index,
-                             Label* true_target,
-                             Label* false_target);
-  void GenerateCompareTestAndBranch(HCondition* condition,
-                                    Label* true_target,
-                                    Label* false_target);
-  void DivRemOneOrMinusOne(HBinaryOperation* instruction);
-  void DivRemByPowerOfTwo(HBinaryOperation* instruction);
-  void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
-  void GenerateDivRemConstantIntegral(HBinaryOperation* instruction);
-  void HandleGoto(HInstruction* got, HBasicBlock* successor);
-
-  ArmAssembler* const assembler_;
-  CodeGeneratorARM* const codegen_;
-
-  DISALLOW_COPY_AND_ASSIGN(InstructionCodeGeneratorARM);
-};
-
-class CodeGeneratorARM : public CodeGenerator {
- public:
-  CodeGeneratorARM(HGraph* graph,
-                   const ArmInstructionSetFeatures& isa_features,
-                   const CompilerOptions& compiler_options,
-                   OptimizingCompilerStats* stats = nullptr);
-  virtual ~CodeGeneratorARM() {}
-
-  void GenerateFrameEntry() OVERRIDE;
-  void GenerateFrameExit() OVERRIDE;
-  void Bind(HBasicBlock* block) OVERRIDE;
-  void MoveConstant(Location destination, int32_t value) OVERRIDE;
-  void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
-  void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
-
-  size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
-  size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
-  size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
-  size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
-
-  size_t GetWordSize() const OVERRIDE {
-    return kArmWordSize;
-  }
-
-  size_t GetFloatingPointSpillSlotSize() const OVERRIDE {
-    // Allocated in S registers, which are word sized.
-    return kArmWordSize;
-  }
-
-  HGraphVisitor* GetLocationBuilder() OVERRIDE {
-    return &location_builder_;
-  }
-
-  HGraphVisitor* GetInstructionVisitor() OVERRIDE {
-    return &instruction_visitor_;
-  }
-
-  ArmAssembler* GetAssembler() OVERRIDE {
-    return &assembler_;
-  }
-
-  const ArmAssembler& GetAssembler() const OVERRIDE {
-    return assembler_;
-  }
-
-  uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE {
-    return GetLabelOf(block)->Position();
-  }
-
-  void SetupBlockedRegisters() const OVERRIDE;
-
-  void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
-  void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
-
-  ParallelMoveResolverARM* GetMoveResolver() OVERRIDE {
-    return &move_resolver_;
-  }
-
-  InstructionSet GetInstructionSet() const OVERRIDE {
-    return InstructionSet::kThumb2;
-  }
-
-  // Helper method to move a 32bits value between two locations.
-  void Move32(Location destination, Location source);
-  // Helper method to move a 64bits value between two locations.
-  void Move64(Location destination, Location source);
-
-  void LoadOrStoreToOffset(Primitive::Type type,
-                           Location loc,
-                           Register base,
-                           int32_t offset,
-                           bool is_load,
-                           Condition cond = AL);
-
-  void LoadFromShiftedRegOffset(Primitive::Type type,
-                                Location out_loc,
-                                Register base,
-                                Register reg_offset,
-                                Condition cond = AL);
-  void StoreToShiftedRegOffset(Primitive::Type type,
-                               Location out_loc,
-                               Register base,
-                               Register reg_offset,
-                               Condition cond = AL);
-
-  // Generate code to invoke a runtime entry point.
-  void InvokeRuntime(QuickEntrypointEnum entrypoint,
-                     HInstruction* instruction,
-                     uint32_t dex_pc,
-                     SlowPathCode* slow_path = nullptr) OVERRIDE;
-
-  // Generate code to invoke a runtime entry point, but do not record
-  // PC-related information in a stack map.
-  void InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
-                                           HInstruction* instruction,
-                                           SlowPathCode* slow_path);
-
-  void GenerateInvokeRuntime(int32_t entry_point_offset);
-
-  // Emit a write barrier.
-  void MarkGCCard(Register temp, Register card, Register object, Register value, bool can_be_null);
-
-  void GenerateMemoryBarrier(MemBarrierKind kind);
-
-  Label* GetLabelOf(HBasicBlock* block) const {
-    return CommonGetLabelOf<Label>(block_labels_, block);
-  }
-
-  Label* GetFinalLabel(HInstruction* instruction, Label* final_label);
-
-  void Initialize() OVERRIDE {
-    block_labels_ = CommonInitializeLabels<Label>();
-  }
-
-  void Finalize(CodeAllocator* allocator) OVERRIDE;
-
-  const ArmInstructionSetFeatures& GetInstructionSetFeatures() const {
-    return isa_features_;
-  }
-
-  bool NeedsTwoRegisters(Primitive::Type type) const OVERRIDE {
-    return type == Primitive::kPrimDouble || type == Primitive::kPrimLong;
-  }
-
-  void ComputeSpillMask() OVERRIDE;
-
-  Label* GetFrameEntryLabel() { return &frame_entry_label_; }
-
-  // Check if the desired_string_load_kind is supported. If it is, return it,
-  // otherwise return a fall-back kind that should be used instead.
-  HLoadString::LoadKind GetSupportedLoadStringKind(
-      HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
-
-  // Check if the desired_class_load_kind is supported. If it is, return it,
-  // otherwise return a fall-back kind that should be used instead.
-  HLoadClass::LoadKind GetSupportedLoadClassKind(
-      HLoadClass::LoadKind desired_class_load_kind) OVERRIDE;
-
-  // Check if the desired_dispatch_info is supported. If it is, return it,
-  // otherwise return a fall-back info that should be used instead.
-  HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
-      const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      HInvokeStaticOrDirect* invoke) OVERRIDE;
-
-  void GenerateStaticOrDirectCall(
-      HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE;
-  void GenerateVirtualCall(
-      HInvokeVirtual* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE;
-
-  void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
-
-  // 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
-  // offset_or_index. The PC-relative address is loaded with three instructions,
-  // MOVW+MOVT to load the offset to base_reg and then ADD base_reg, PC. The offset
-  // is calculated from the ADD's effective PC, i.e. PC+4 on Thumb2. Though we
-  // currently emit these 3 instructions together, instruction scheduling could
-  // split this sequence apart, so we keep separate labels for each of them.
-  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 index.
-    uint32_t offset_or_index;
-    Label movw_label;
-    Label movt_label;
-    Label add_pc_label;
-  };
-
-  PcRelativePatchInfo* NewPcRelativeMethodPatch(MethodReference target_method);
-  PcRelativePatchInfo* NewMethodBssEntryPatch(MethodReference target_method);
-  PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index);
-  PcRelativePatchInfo* NewTypeBssEntryPatch(const DexFile& dex_file, dex::TypeIndex type_index);
-  PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file,
-                                                dex::StringIndex string_index);
-
-  // Add a new baker read barrier patch and return the label to be bound
-  // before the BNE instruction.
-  Label* NewBakerReadBarrierPatch(uint32_t custom_data);
-
-  Literal* DeduplicateBootImageAddressLiteral(uint32_t address);
-  Literal* DeduplicateJitStringLiteral(const DexFile& dex_file,
-                                       dex::StringIndex string_index,
-                                       Handle<mirror::String> handle);
-  Literal* DeduplicateJitClassLiteral(const DexFile& dex_file,
-                                      dex::TypeIndex type_index,
-                                      Handle<mirror::Class> handle);
-
-  void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
-
-  void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE;
-
-  // Maybe add the reserved entrypoint register as a temporary for field load. This temp
-  // is added only for AOT compilation if link-time generated thunks for fields are enabled.
-  void MaybeAddBakerCcEntrypointTempForFields(LocationSummary* locations);
-
-  // Fast path implementation of ReadBarrier::Barrier for a heap
-  // reference field load when Baker's read barriers are used.
-  void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
-                                             Location ref,
-                                             Register obj,
-                                             uint32_t offset,
-                                             Location temp,
-                                             bool needs_null_check);
-  // Fast path implementation of ReadBarrier::Barrier for a heap
-  // reference array load when Baker's read barriers are used.
-  void GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
-                                             Location ref,
-                                             Register obj,
-                                             uint32_t data_offset,
-                                             Location index,
-                                             Location temp,
-                                             bool needs_null_check);
-  // Factored implementation, used by GenerateFieldLoadWithBakerReadBarrier,
-  // GenerateArrayLoadWithBakerReadBarrier and some intrinsics.
-  //
-  // Load the object reference located at the address
-  // `obj + offset + (index << scale_factor)`, held by object `obj`, into
-  // `ref`, and mark it if needed.
-  void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
-                                                 Location ref,
-                                                 Register obj,
-                                                 uint32_t offset,
-                                                 Location index,
-                                                 ScaleFactor scale_factor,
-                                                 Location temp,
-                                                 bool needs_null_check);
-
-  // Generate code checking whether the the reference field at the
-  // address `obj + field_offset`, held by object `obj`, needs to be
-  // marked, and if so, marking it and updating the field within `obj`
-  // with the marked value.
-  //
-  // This routine is used for the implementation of the
-  // UnsafeCASObject intrinsic with Baker read barriers.
-  //
-  // This method has a structure similar to
-  // GenerateReferenceLoadWithBakerReadBarrier, but note that argument
-  // `ref` is only as a temporary here, and thus its value should not
-  // be used afterwards.
-  void UpdateReferenceFieldWithBakerReadBarrier(HInstruction* instruction,
-                                                Location ref,
-                                                Register obj,
-                                                Location field_offset,
-                                                Location temp,
-                                                bool needs_null_check,
-                                                Register temp2);
-
-  // Generate a heap reference load (with no read barrier).
-  void GenerateRawReferenceLoad(HInstruction* instruction,
-                                Location ref,
-                                Register obj,
-                                uint32_t offset,
-                                Location index,
-                                ScaleFactor scale_factor,
-                                bool needs_null_check);
-
-  // Generate a read barrier for a heap reference within `instruction`
-  // using a slow path.
-  //
-  // A read barrier for an object reference read from the heap is
-  // implemented as a call to the artReadBarrierSlow runtime entry
-  // point, which is passed the values in locations `ref`, `obj`, and
-  // `offset`:
-  //
-  //   mirror::Object* artReadBarrierSlow(mirror::Object* ref,
-  //                                      mirror::Object* obj,
-  //                                      uint32_t offset);
-  //
-  // The `out` location contains the value returned by
-  // artReadBarrierSlow.
-  //
-  // When `index` is provided (i.e. for array accesses), the offset
-  // value passed to artReadBarrierSlow is adjusted to take `index`
-  // into account.
-  void GenerateReadBarrierSlow(HInstruction* instruction,
-                               Location out,
-                               Location ref,
-                               Location obj,
-                               uint32_t offset,
-                               Location index = Location::NoLocation());
-
-  // If read barriers are enabled, generate a read barrier for a heap
-  // reference using a slow path. If heap poisoning is enabled, also
-  // unpoison the reference in `out`.
-  void MaybeGenerateReadBarrierSlow(HInstruction* instruction,
-                                    Location out,
-                                    Location ref,
-                                    Location obj,
-                                    uint32_t offset,
-                                    Location index = Location::NoLocation());
-
-  // Generate a read barrier for a GC root within `instruction` using
-  // a slow path.
-  //
-  // A read barrier for an object reference GC root is implemented as
-  // a call to the artReadBarrierForRootSlow runtime entry point,
-  // which is passed the value in location `root`:
-  //
-  //   mirror::Object* artReadBarrierForRootSlow(GcRoot<mirror::Object>* root);
-  //
-  // The `out` location contains the value returned by
-  // artReadBarrierForRootSlow.
-  void GenerateReadBarrierForRootSlow(HInstruction* instruction, Location out, Location root);
-
-  void GenerateNop() OVERRIDE;
-
-  void GenerateImplicitNullCheck(HNullCheck* instruction) OVERRIDE;
-  void GenerateExplicitNullCheck(HNullCheck* instruction) OVERRIDE;
-
-  // `temp` is an extra temporary register that is used for some conditions;
-  // callers may not specify it, in which case the method will use a scratch
-  // register instead.
-  void GenerateConditionWithZero(IfCondition condition,
-                                 Register out,
-                                 Register in,
-                                 Register temp = kNoRegister);
-
- private:
-  Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp);
-
-  using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, Literal*>;
-  using StringToLiteralMap = ArenaSafeMap<StringReference,
-                                          Literal*,
-                                          StringReferenceValueComparator>;
-  using TypeToLiteralMap = ArenaSafeMap<TypeReference,
-                                        Literal*,
-                                        TypeReferenceValueComparator>;
-
-  struct BakerReadBarrierPatchInfo {
-    explicit BakerReadBarrierPatchInfo(uint32_t data) : label(), custom_data(data) { }
-
-    Label label;
-    uint32_t custom_data;
-  };
-
-  Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* 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)>
-  static void EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo>& infos,
-                                          ArenaVector<LinkerPatch>* linker_patches);
-
-  // Labels for each block that will be compiled.
-  Label* block_labels_;  // Indexed by block id.
-  Label frame_entry_label_;
-  LocationsBuilderARM location_builder_;
-  InstructionCodeGeneratorARM instruction_visitor_;
-  ParallelMoveResolverARM move_resolver_;
-  Thumb2Assembler assembler_;
-  const ArmInstructionSetFeatures& isa_features_;
-
-  // Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
-  Uint32ToLiteralMap uint32_literals_;
-  // PC-relative method patch info for kBootImageLinkTimePcRelative.
-  ArenaDeque<PcRelativePatchInfo> pc_relative_method_patches_;
-  // PC-relative method patch info for kBssEntry.
-  ArenaDeque<PcRelativePatchInfo> method_bss_entry_patches_;
-  // PC-relative type patch info for kBootImageLinkTimePcRelative.
-  ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_;
-  // PC-relative type patch info for kBssEntry.
-  ArenaDeque<PcRelativePatchInfo> type_bss_entry_patches_;
-  // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC).
-  ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
-  // Baker read barrier patch info.
-  ArenaDeque<BakerReadBarrierPatchInfo> baker_read_barrier_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(CodeGeneratorARM);
-};
-
-}  // namespace arm
-}  // namespace art
-
-#endif  // ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM_H_
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 2561ed0..7e5b1a0 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -672,7 +672,9 @@
 // `ref`.
 //
 // Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked.
+// barrier marking runtime entry point to be invoked or an empty
+// location; in the latter case, the read barrier marking runtime
+// entry point will be loaded by the slow path code itself.
 class ReadBarrierMarkSlowPathBaseARM64 : public SlowPathCodeARM64 {
  protected:
   ReadBarrierMarkSlowPathBaseARM64(HInstruction* instruction, Location ref, Location entrypoint)
@@ -716,7 +718,7 @@
     } else {
       // Entrypoint is not already loaded, load from the thread.
       int32_t entry_point_offset =
-          CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(ref_.reg());
+          Thread::ReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(ref_.reg());
       // This runtime call does not require a stack map.
       arm64_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
     }
@@ -743,9 +745,10 @@
 // another thread, or if another thread installed another object
 // reference (different from `ref`) in `obj.field`).
 //
-// If `entrypoint` is a valid location it is assumed to already be
-// holding the entrypoint. The case where the entrypoint is passed in
-// is when the decision to mark is based on whether the GC is marking.
+// Argument `entrypoint` must be a register location holding the read
+// barrier marking runtime entry point to be invoked or an empty
+// location; in the latter case, the read barrier marking runtime
+// entry point will be loaded by the slow path code itself.
 class ReadBarrierMarkSlowPathARM64 : public ReadBarrierMarkSlowPathBaseARM64 {
  public:
   ReadBarrierMarkSlowPathARM64(HInstruction* instruction,
@@ -791,7 +794,9 @@
 // reference (different from `ref`) in `obj.field`).
 //
 // Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked.
+// barrier marking runtime entry point to be invoked or an empty
+// location; in the latter case, the read barrier marking runtime
+// entry point will be loaded by the slow path code itself.
 class LoadReferenceWithBakerReadBarrierSlowPathARM64 : public ReadBarrierMarkSlowPathBaseARM64 {
  public:
   LoadReferenceWithBakerReadBarrierSlowPathARM64(HInstruction* instruction,
@@ -803,7 +808,7 @@
                                                  bool needs_null_check,
                                                  bool use_load_acquire,
                                                  Register temp,
-                                                 Location entrypoint)
+                                                 Location entrypoint = Location::NoLocation())
       : ReadBarrierMarkSlowPathBaseARM64(instruction, ref, entrypoint),
         obj_(obj),
         offset_(offset),
@@ -947,20 +952,23 @@
 // another object reference (different from `ref`) in `obj.field`).
 //
 // Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked.
+// barrier marking runtime entry point to be invoked or an empty
+// location; in the latter case, the read barrier marking runtime
+// entry point will be loaded by the slow path code itself.
 class LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM64
     : public ReadBarrierMarkSlowPathBaseARM64 {
  public:
-  LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM64(HInstruction* instruction,
-                                                               Location ref,
-                                                               Register obj,
-                                                               uint32_t offset,
-                                                               Location index,
-                                                               size_t scale_factor,
-                                                               bool needs_null_check,
-                                                               bool use_load_acquire,
-                                                               Register temp,
-                                                               Location entrypoint)
+  LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM64(
+      HInstruction* instruction,
+      Location ref,
+      Register obj,
+      uint32_t offset,
+      Location index,
+      size_t scale_factor,
+      bool needs_null_check,
+      bool use_load_acquire,
+      Register temp,
+      Location entrypoint = Location::NoLocation())
       : ReadBarrierMarkSlowPathBaseARM64(instruction, ref, entrypoint),
         obj_(obj),
         offset_(offset),
@@ -1655,7 +1663,7 @@
   // Blocked core registers:
   //      lr        : Runtime reserved.
   //      tr        : Runtime reserved.
-  //      xSuspend  : Runtime reserved. TODO: Unblock this when the runtime stops using it.
+  //      mr        : Runtime reserved.
   //      ip1       : VIXL core temp.
   //      ip0       : VIXL core temp.
   //
@@ -5921,20 +5929,17 @@
       // Baker's read barrier are used.
       if (kBakerReadBarrierLinkTimeThunksEnableForGcRoots &&
           !Runtime::Current()->UseJitCompilation()) {
-        // Note that we do not actually check the value of `GetIsGcMarking()`
-        // to decide whether to mark the loaded GC root or not.  Instead, we
-        // load into `temp` (actually IP1) the read barrier mark introspection
-        // entrypoint. If `temp` is null, it means that `GetIsGcMarking()` is
-        // false, and vice versa.
+        // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
+        // the Marking Register) to decide whether we need to enter
+        // the slow path to mark the GC root.
         //
         // We use link-time generated thunks for the slow path. That thunk
         // checks the reference and jumps to the entrypoint if needed.
         //
-        //     temp = Thread::Current()->pReadBarrierMarkIntrospection
         //     lr = &return_address;
         //     GcRoot<mirror::Object> root = *(obj+offset);  // Original reference load.
-        //     if (temp != nullptr) {
-        //        goto gc_root_thunk<root_reg>(lr)
+        //     if (mr) {  // Thread::Current()->GetIsGcMarking()
+        //       goto gc_root_thunk<root_reg>(lr)
         //     }
         //   return_address:
 
@@ -5946,11 +5951,6 @@
             linker::Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg.GetCode());
         vixl::aarch64::Label* cbnz_label = codegen_->NewBakerReadBarrierPatch(custom_data);
 
-        // ip1 = Thread::Current()->pReadBarrierMarkReg16, i.e. pReadBarrierMarkIntrospection.
-        DCHECK_EQ(ip0.GetCode(), 16u);
-        const int32_t entry_point_offset =
-            CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(ip0.GetCode());
-        __ Ldr(ip1, MemOperand(tr, entry_point_offset));
         EmissionCheckScope guard(GetVIXLAssembler(), 3 * vixl::aarch64::kInstructionSize);
         vixl::aarch64::Label return_address;
         __ adr(lr, &return_address);
@@ -5961,36 +5961,26 @@
                       "GC root LDR must be 2 instruction (8B) before the return address label.");
         __ ldr(root_reg, MemOperand(obj.X(), offset));
         __ Bind(cbnz_label);
-        __ cbnz(ip1, static_cast<int64_t>(0));  // Placeholder, patched at link-time.
+        __ cbnz(mr, static_cast<int64_t>(0));  // Placeholder, patched at link-time.
         __ Bind(&return_address);
       } else {
-        // Note that we do not actually check the value of
-        // `GetIsGcMarking()` to decide whether to mark the loaded GC
-        // root or not.  Instead, we load into `temp` the read barrier
-        // mark entry point corresponding to register `root`. If `temp`
-        // is null, it means that `GetIsGcMarking()` is false, and vice
-        // versa.
+        // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
+        // the Marking Register) to decide whether we need to enter
+        // the slow path to mark the GC root.
         //
-        //   temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
         //   GcRoot<mirror::Object> root = *(obj+offset);  // Original reference load.
-        //   if (temp != nullptr) {  // <=> Thread::Current()->GetIsGcMarking()
+        //   if (mr) {  // Thread::Current()->GetIsGcMarking()
         //     // Slow path.
-        //     root = temp(root);  // root = ReadBarrier::Mark(root);  // Runtime entry point call.
+        //     entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
+        //     root = entrypoint(root);  // root = ReadBarrier::Mark(root);  // Entry point call.
         //   }
 
-        // Slow path marking the GC root `root`. The entrypoint will already be loaded in `temp`.
-        Register temp = lr;
-        SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM64(
-            instruction, root, /* entrypoint */ LocationFrom(temp));
+        // Slow path marking the GC root `root`. The entrypoint will
+        // be loaded by the slow path code.
+        SlowPathCodeARM64* slow_path =
+            new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM64(instruction, root);
         codegen_->AddSlowPath(slow_path);
 
-        // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
-        const int32_t entry_point_offset =
-            CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(root.reg());
-        // Loading the entrypoint does not require a load acquire since it is only changed when
-        // threads are suspended or running a checkpoint.
-        __ Ldr(temp, MemOperand(tr, entry_point_offset));
-
         // /* GcRoot<mirror::Object> */ root = *(obj + offset)
         if (fixup_label == nullptr) {
           __ Ldr(root_reg, MemOperand(obj, offset));
@@ -6005,9 +5995,7 @@
                       "art::mirror::CompressedReference<mirror::Object> and int32_t "
                       "have different sizes.");
 
-        // The entrypoint is null when the GC is not marking, this prevents one load compared to
-        // checking GetIsGcMarking.
-        __ Cbnz(temp, slow_path->GetEntryLabel());
+        __ Cbnz(mr, slow_path->GetEntryLabel());
         __ Bind(slow_path->GetExitLabel());
       }
     } else {
@@ -6048,20 +6036,19 @@
   if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
       !use_load_acquire &&
       !Runtime::Current()->UseJitCompilation()) {
-    // Note that we do not actually check the value of `GetIsGcMarking()`
-    // to decide whether to mark the loaded reference or not.  Instead, we
-    // load into `temp` (actually IP1) the read barrier mark introspection
-    // entrypoint. If `temp` is null, it means that `GetIsGcMarking()` is
-    // false, and vice versa.
+    // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
+    // Marking Register) to decide whether we need to enter the slow
+    // path to mark the reference. Then, in the slow path, check the
+    // gray bit in the lock word of the reference's holder (`obj`) to
+    // decide whether to mark `ref` or not.
     //
     // We use link-time generated thunks for the slow path. That thunk checks
     // the holder and jumps to the entrypoint if needed. If the holder is not
     // gray, it creates a fake dependency and returns to the LDR instruction.
     //
-    //     temp = Thread::Current()->pReadBarrierMarkIntrospection
     //     lr = &gray_return_address;
-    //     if (temp != nullptr) {
-    //        goto field_thunk<holder_reg, base_reg>(lr)
+    //     if (mr) {  // Thread::Current()->GetIsGcMarking()
+    //       goto field_thunk<holder_reg, base_reg>(lr)
     //     }
     //   not_gray_return_address:
     //     // Original reference load. If the offset is too large to fit
@@ -6087,17 +6074,12 @@
         obj.GetCode());
     vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data);
 
-    // ip1 = Thread::Current()->pReadBarrierMarkReg16, i.e. pReadBarrierMarkIntrospection.
-    DCHECK_EQ(ip0.GetCode(), 16u);
-    const int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(ip0.GetCode());
-    __ Ldr(ip1, MemOperand(tr, entry_point_offset));
     EmissionCheckScope guard(GetVIXLAssembler(),
                              (kPoisonHeapReferences ? 4u : 3u) * vixl::aarch64::kInstructionSize);
     vixl::aarch64::Label return_address;
     __ adr(lr, &return_address);
     __ Bind(cbnz_label);
-    __ cbnz(ip1, static_cast<int64_t>(0));  // Placeholder, patched at link-time.
+    __ cbnz(mr, static_cast<int64_t>(0));  // Placeholder, patched at link-time.
     static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4),
                   "Field LDR must be 1 instruction (4B) before the return address label; "
                   " 2 instructions (8B) for heap poisoning.");
@@ -6143,20 +6125,19 @@
 
   if (kBakerReadBarrierLinkTimeThunksEnableForArrays &&
       !Runtime::Current()->UseJitCompilation()) {
-    // Note that we do not actually check the value of `GetIsGcMarking()`
-    // to decide whether to mark the loaded reference or not.  Instead, we
-    // load into `temp` (actually IP1) the read barrier mark introspection
-    // entrypoint. If `temp` is null, it means that `GetIsGcMarking()` is
-    // false, and vice versa.
+    // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
+    // Marking Register) to decide whether we need to enter the slow
+    // path to mark the reference. Then, in the slow path, check the
+    // gray bit in the lock word of the reference's holder (`obj`) to
+    // decide whether to mark `ref` or not.
     //
     // We use link-time generated thunks for the slow path. That thunk checks
     // the holder and jumps to the entrypoint if needed. If the holder is not
     // gray, it creates a fake dependency and returns to the LDR instruction.
     //
-    //     temp = Thread::Current()->pReadBarrierMarkIntrospection
     //     lr = &gray_return_address;
-    //     if (temp != nullptr) {
-    //        goto field_thunk<holder_reg, base_reg>(lr)
+    //     if (mr) {  // Thread::Current()->GetIsGcMarking()
+    //       goto array_thunk<base_reg>(lr)
     //     }
     //   not_gray_return_address:
     //     // Original reference load. If the offset is too large to fit
@@ -6176,18 +6157,13 @@
         linker::Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(temp.GetCode());
     vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data);
 
-    // ip1 = Thread::Current()->pReadBarrierMarkReg16, i.e. pReadBarrierMarkIntrospection.
-    DCHECK_EQ(ip0.GetCode(), 16u);
-    const int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(ip0.GetCode());
-    __ Ldr(ip1, MemOperand(tr, entry_point_offset));
     __ Add(temp.X(), obj.X(), Operand(data_offset));
     EmissionCheckScope guard(GetVIXLAssembler(),
                              (kPoisonHeapReferences ? 4u : 3u) * vixl::aarch64::kInstructionSize);
     vixl::aarch64::Label return_address;
     __ adr(lr, &return_address);
     __ Bind(cbnz_label);
-    __ cbnz(ip1, static_cast<int64_t>(0));  // Placeholder, patched at link-time.
+    __ cbnz(mr, static_cast<int64_t>(0));  // Placeholder, patched at link-time.
     static_assert(BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4),
                   "Array LDR must be 1 instruction (4B) before the return address label; "
                   " 2 instructions (8B) for heap poisoning.");
@@ -6231,35 +6207,28 @@
   // `instruction->IsArrayGet()` => `!use_load_acquire`.
   DCHECK(!instruction->IsArrayGet() || !use_load_acquire);
 
-  // Query `art::Thread::Current()->GetIsGcMarking()` to decide
-  // whether we need to enter the slow path to mark the reference.
-  // Then, in the slow path, check the gray bit in the lock word of
-  // the reference's holder (`obj`) to decide whether to mark `ref` or
-  // not.
+  // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
+  // Marking Register) to decide whether we need to enter the slow
+  // path to mark the reference. Then, in the slow path, check the
+  // gray bit in the lock word of the reference's holder (`obj`) to
+  // decide whether to mark `ref` or not.
   //
-  // Note that we do not actually check the value of `GetIsGcMarking()`;
-  // instead, we load into `temp2` the read barrier mark entry point
-  // corresponding to register `ref`. If `temp2` is null, it means
-  // that `GetIsGcMarking()` is false, and vice versa.
-  //
-  //   temp2 = Thread::Current()->pReadBarrierMarkReg ## root.reg()
-  //   if (temp2 != nullptr) {  // <=> Thread::Current()->GetIsGcMarking()
+  //   if (mr) {  // Thread::Current()->GetIsGcMarking()
   //     // Slow path.
   //     uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
   //     lfence;  // Load fence or artificial data dependency to prevent load-load reordering
   //     HeapReference<mirror::Object> ref = *src;  // Original reference load.
   //     bool is_gray = (rb_state == ReadBarrier::GrayState());
   //     if (is_gray) {
-  //       ref = temp2(ref);  // ref = ReadBarrier::Mark(ref);  // Runtime entry point call.
+  //       entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
+  //       ref = entrypoint(ref);  // ref = ReadBarrier::Mark(ref);  // Runtime entry point call.
   //     }
   //   } else {
   //     HeapReference<mirror::Object> ref = *src;  // Original reference load.
   //   }
 
   // Slow path marking the object `ref` when the GC is marking. The
-  // entrypoint will already be loaded in `temp2`.
-  Register temp2 = lr;
-  Location temp2_loc = LocationFrom(temp2);
+  // entrypoint will be loaded by the slow path code.
   SlowPathCodeARM64* slow_path =
       new (GetGraph()->GetArena()) LoadReferenceWithBakerReadBarrierSlowPathARM64(
           instruction,
@@ -6270,19 +6239,10 @@
           scale_factor,
           needs_null_check,
           use_load_acquire,
-          temp,
-          /* entrypoint */ temp2_loc);
+          temp);
   AddSlowPath(slow_path);
 
-  // temp2 = Thread::Current()->pReadBarrierMarkReg ## ref.reg()
-  const int32_t entry_point_offset =
-      CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(ref.reg());
-  // Loading the entrypoint does not require a load acquire since it is only changed when
-  // threads are suspended or running a checkpoint.
-  __ Ldr(temp2, MemOperand(tr, entry_point_offset));
-  // The entrypoint is null when the GC is not marking, this prevents one load compared to
-  // checking GetIsGcMarking.
-  __ Cbnz(temp2, slow_path->GetEntryLabel());
+  __ Cbnz(mr, slow_path->GetEntryLabel());
   // Fast path: the GC is not marking: just load the reference.
   GenerateRawReferenceLoad(
       instruction, ref, obj, offset, index, scale_factor, needs_null_check, use_load_acquire);
@@ -6303,19 +6263,14 @@
   // `instruction->IsArrayGet()` => `!use_load_acquire`.
   DCHECK(!instruction->IsArrayGet() || !use_load_acquire);
 
-  // Query `art::Thread::Current()->GetIsGcMarking()` to decide
-  // whether we need to enter the slow path to update the reference
-  // field within `obj`.  Then, in the slow path, check the gray bit
-  // in the lock word of the reference's holder (`obj`) to decide
-  // whether to mark `ref` and update the field or not.
+  // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
+  // Marking Register) to decide whether we need to enter the slow
+  // path to update the reference field within `obj`. Then, in the
+  // slow path, check the gray bit in the lock word of the reference's
+  // holder (`obj`) to decide whether to mark `ref` and update the
+  // field or not.
   //
-  // Note that we do not actually check the value of `GetIsGcMarking()`;
-  // instead, we load into `temp2` the read barrier mark entry point
-  // corresponding to register `ref`. If `temp2` is null, it means
-  // that `GetIsGcMarking()` is false, and vice versa.
-  //
-  //   temp2 = Thread::Current()->pReadBarrierMarkReg ## root.reg()
-  //   if (temp2 != nullptr) {  // <=> Thread::Current()->GetIsGcMarking()
+  //   if (mr) {  // Thread::Current()->GetIsGcMarking()
   //     // Slow path.
   //     uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
   //     lfence;  // Load fence or artificial data dependency to prevent load-load reordering
@@ -6323,15 +6278,14 @@
   //     bool is_gray = (rb_state == ReadBarrier::GrayState());
   //     if (is_gray) {
   //       old_ref = ref;
-  //       ref = temp2(ref);  // ref = ReadBarrier::Mark(ref);  // Runtime entry point call.
+  //       entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
+  //       ref = entrypoint(ref);  // ref = ReadBarrier::Mark(ref);  // Runtime entry point call.
   //       compareAndSwapObject(obj, field_offset, old_ref, ref);
   //     }
   //   }
 
   // Slow path updating the object reference at address `obj + field_offset`
-  // when the GC is marking. The entrypoint will already be loaded in `temp2`.
-  Register temp2 = lr;
-  Location temp2_loc = LocationFrom(temp2);
+  // when the GC is marking. The entrypoint will be loaded by the slow path code.
   SlowPathCodeARM64* slow_path =
       new (GetGraph()->GetArena()) LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM64(
           instruction,
@@ -6342,19 +6296,10 @@
           /* scale_factor */ 0u /* "times 1" */,
           needs_null_check,
           use_load_acquire,
-          temp,
-          /* entrypoint */ temp2_loc);
+          temp);
   AddSlowPath(slow_path);
 
-  // temp2 = Thread::Current()->pReadBarrierMarkReg ## ref.reg()
-  const int32_t entry_point_offset =
-      CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(ref.reg());
-  // Loading the entrypoint does not require a load acquire since it is only changed when
-  // threads are suspended or running a checkpoint.
-  __ Ldr(temp2, MemOperand(tr, entry_point_offset));
-  // The entrypoint is null when the GC is not marking, this prevents one load compared to
-  // checking GetIsGcMarking.
-  __ Cbnz(temp2, slow_path->GetEntryLabel());
+  __ Cbnz(mr, slow_path->GetEntryLabel());
   // Fast path: the GC is not marking: nothing to do (the field is
   // up-to-date, and we don't need to load the reference).
   __ Bind(slow_path->GetExitLabel());
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index d9c49d1..584eead 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -70,21 +70,32 @@
 };
 static constexpr size_t kParameterFPRegistersLength = arraysize(kParameterFPRegisters);
 
-// Thread Register
+// Thread Register.
 const vixl::aarch64::Register tr = vixl::aarch64::x19;
+// Marking Register.
+const vixl::aarch64::Register mr = vixl::aarch64::x20;
 // Method register on invoke.
 static const vixl::aarch64::Register kArtMethodRegister = vixl::aarch64::x0;
 const vixl::aarch64::CPURegList vixl_reserved_core_registers(vixl::aarch64::ip0,
                                                              vixl::aarch64::ip1);
 const vixl::aarch64::CPURegList vixl_reserved_fp_registers(vixl::aarch64::d31);
 
-const vixl::aarch64::CPURegList runtime_reserved_core_registers(tr, vixl::aarch64::lr);
+const vixl::aarch64::CPURegList runtime_reserved_core_registers =
+    vixl::aarch64::CPURegList(
+        tr,
+        // Reserve X20 as Marking Register when emitting Baker read barriers.
+        ((kEmitCompilerReadBarrier && kUseBakerReadBarrier) ? mr : vixl::aarch64::NoCPUReg),
+        vixl::aarch64::lr);
 
-// Callee-saved registers AAPCS64 (without x19 - Thread Register)
-const vixl::aarch64::CPURegList callee_saved_core_registers(vixl::aarch64::CPURegister::kRegister,
-                                                            vixl::aarch64::kXRegSize,
-                                                            vixl::aarch64::x20.GetCode(),
-                                                            vixl::aarch64::x30.GetCode());
+// Callee-save registers AAPCS64, without x19 (Thread Register) (nor
+// x20 (Marking Register) when emitting Baker read barriers).
+const vixl::aarch64::CPURegList callee_saved_core_registers(
+    vixl::aarch64::CPURegister::kRegister,
+    vixl::aarch64::kXRegSize,
+    ((kEmitCompilerReadBarrier && kUseBakerReadBarrier)
+         ? vixl::aarch64::x21.GetCode()
+         : vixl::aarch64::x20.GetCode()),
+     vixl::aarch64::x30.GetCode());
 const vixl::aarch64::CPURegList callee_saved_fp_registers(vixl::aarch64::CPURegister::kFPRegister,
                                                           vixl::aarch64::kDRegSize,
                                                           vixl::aarch64::d8.GetCode(),
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 9a2402b..b9d4700 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -740,7 +740,9 @@
 // `ref`.
 //
 // Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked.
+// barrier marking runtime entry point to be invoked or an empty
+// location; in the latter case, the read barrier marking runtime
+// entry point will be loaded by the slow path code itself.
 class ReadBarrierMarkSlowPathBaseARMVIXL : public SlowPathCodeARMVIXL {
  protected:
   ReadBarrierMarkSlowPathBaseARMVIXL(HInstruction* instruction, Location ref, Location entrypoint)
@@ -786,7 +788,7 @@
     } else {
       // Entrypoint is not already loaded, load from the thread.
       int32_t entry_point_offset =
-          CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref_reg.GetCode());
+          Thread::ReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref_reg.GetCode());
       // This runtime call does not require a stack map.
       arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
     }
@@ -813,9 +815,10 @@
 // another thread, or if another thread installed another object
 // reference (different from `ref`) in `obj.field`).
 //
-// If `entrypoint` is a valid location it is assumed to already be
-// holding the entrypoint. The case where the entrypoint is passed in
-// is when the decision to mark is based on whether the GC is marking.
+// Argument `entrypoint` must be a register location holding the read
+// barrier marking runtime entry point to be invoked or an empty
+// location; in the latter case, the read barrier marking runtime
+// entry point will be loaded by the slow path code itself.
 class ReadBarrierMarkSlowPathARMVIXL : public ReadBarrierMarkSlowPathBaseARMVIXL {
  public:
   ReadBarrierMarkSlowPathARMVIXL(HInstruction* instruction,
@@ -861,7 +864,9 @@
 // reference (different from `ref`) in `obj.field`).
 //
 // Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked.
+// barrier marking runtime entry point to be invoked or an empty
+// location; in the latter case, the read barrier marking runtime
+// entry point will be loaded by the slow path code itself.
 class LoadReferenceWithBakerReadBarrierSlowPathARMVIXL : public ReadBarrierMarkSlowPathBaseARMVIXL {
  public:
   LoadReferenceWithBakerReadBarrierSlowPathARMVIXL(HInstruction* instruction,
@@ -872,7 +877,7 @@
                                                    ScaleFactor scale_factor,
                                                    bool needs_null_check,
                                                    vixl32::Register temp,
-                                                   Location entrypoint)
+                                                   Location entrypoint = Location::NoLocation())
       : ReadBarrierMarkSlowPathBaseARMVIXL(instruction, ref, entrypoint),
         obj_(obj),
         offset_(offset),
@@ -1006,22 +1011,24 @@
 // hold the same to-space reference (unless another thread installed
 // another object reference (different from `ref`) in `obj.field`).
 //
-//
 // Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked.
+// barrier marking runtime entry point to be invoked or an empty
+// location; in the latter case, the read barrier marking runtime
+// entry point will be loaded by the slow path code itself.
 class LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL
     : public ReadBarrierMarkSlowPathBaseARMVIXL {
  public:
-  LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL(HInstruction* instruction,
-                                                                 Location ref,
-                                                                 vixl32::Register obj,
-                                                                 uint32_t offset,
-                                                                 Location index,
-                                                                 ScaleFactor scale_factor,
-                                                                 bool needs_null_check,
-                                                                 vixl32::Register temp1,
-                                                                 vixl32::Register temp2,
-                                                                 Location entrypoint)
+  LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL(
+      HInstruction* instruction,
+      Location ref,
+      vixl32::Register obj,
+      uint32_t offset,
+      Location index,
+      ScaleFactor scale_factor,
+      bool needs_null_check,
+      vixl32::Register temp1,
+      vixl32::Register temp2,
+      Location entrypoint = Location::NoLocation())
       : ReadBarrierMarkSlowPathBaseARMVIXL(instruction, ref, entrypoint),
         obj_(obj),
         offset_(offset),
@@ -1288,8 +1295,8 @@
         DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg.GetCode()));
         if (codegen->IsCoreCalleeSaveRegister(index_reg.GetCode())) {
           // We are about to change the value of `index_reg` (see the
-          // calls to art::arm::Thumb2Assembler::Lsl and
-          // art::arm::Thumb2Assembler::AddConstant below), but it has
+          // calls to art::arm::ArmVIXLMacroAssembler::Lsl and
+          // art::arm::ArmVIXLMacroAssembler::Add below), but it has
           // not been saved by the previous call to
           // art::SlowPathCode::SaveLiveRegisters, as it is a
           // callee-save register --
@@ -2310,7 +2317,8 @@
   }
 }
 
-static void GenerateConditionIntegralOrNonPrimitive(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
+static void GenerateConditionIntegralOrNonPrimitive(HCondition* cond,
+                                                    CodeGeneratorARMVIXL* codegen) {
   const Primitive::Type type = cond->GetLeft()->GetType();
 
   DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
@@ -2576,6 +2584,11 @@
   blocked_core_registers_[LR] = true;
   blocked_core_registers_[PC] = true;
 
+  if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+    // Reserve marking register.
+    blocked_core_registers_[MR] = true;
+  }
+
   // Reserve thread register.
   blocked_core_registers_[TR] = true;
 
@@ -8531,20 +8544,17 @@
       // Baker's read barrier are used.
       if (kBakerReadBarrierLinkTimeThunksEnableForGcRoots &&
           !Runtime::Current()->UseJitCompilation()) {
-        // Note that we do not actually check the value of `GetIsGcMarking()`
-        // to decide whether to mark the loaded GC root or not.  Instead, we
-        // load into `temp` (actually kBakerCcEntrypointRegister) the read
-        // barrier mark introspection entrypoint. If `temp` is null, it means
-        // that `GetIsGcMarking()` is false, and vice versa.
+        // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
+        // the Marking Register) to decide whether we need to enter
+        // the slow path to mark the GC root.
         //
         // We use link-time generated thunks for the slow path. That thunk
         // checks the reference and jumps to the entrypoint if needed.
         //
-        //     temp = Thread::Current()->pReadBarrierMarkIntrospection
         //     lr = &return_address;
         //     GcRoot<mirror::Object> root = *(obj+offset);  // Original reference load.
-        //     if (temp != nullptr) {
-        //        goto gc_root_thunk<root_reg>(lr)
+        //     if (mr) {  // Thread::Current()->GetIsGcMarking()
+        //       goto gc_root_thunk<root_reg>(lr)
         //     }
         //   return_address:
 
@@ -8555,18 +8565,10 @@
             root_reg.GetCode(), narrow);
         vixl32::Label* bne_label = codegen_->NewBakerReadBarrierPatch(custom_data);
 
-        // entrypoint_reg =
-        //     Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection.
-        DCHECK_EQ(ip.GetCode(), 12u);
-        const int32_t entry_point_offset =
-            CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ip.GetCode());
-        __ Ldr(kBakerCcEntrypointRegister, MemOperand(tr, entry_point_offset));
-
-        vixl::EmissionCheckScope guard(GetVIXLAssembler(),
-                                       4 * vixl32::kMaxInstructionSizeInBytes);
+        vixl::EmissionCheckScope guard(GetVIXLAssembler(), 4 * vixl32::kMaxInstructionSizeInBytes);
         vixl32::Label return_address;
         EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
-        __ cmp(kBakerCcEntrypointRegister, Operand(0));
+        __ cmp(mr, Operand(0));
         // Currently the offset is always within range. If that changes,
         // we shall have to split the load the same way as for fields.
         DCHECK_LT(offset, kReferenceLoadMinFarOffset);
@@ -8578,34 +8580,23 @@
                   narrow ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET
                          : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET);
       } else {
-        // Note that we do not actually check the value of
-        // `GetIsGcMarking()` to decide whether to mark the loaded GC
-        // root or not.  Instead, we load into `temp` the read barrier
-        // mark entry point corresponding to register `root`. If `temp`
-        // is null, it means that `GetIsGcMarking()` is false, and vice
-        // versa.
+        // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
+        // the Marking Register) to decide whether we need to enter
+        // the slow path to mark the GC root.
         //
-        //   temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
         //   GcRoot<mirror::Object> root = *(obj+offset);  // Original reference load.
-        //   if (temp != nullptr) {  // <=> Thread::Current()->GetIsGcMarking()
+        //   if (mr) {  // Thread::Current()->GetIsGcMarking()
         //     // Slow path.
-        //     root = temp(root);  // root = ReadBarrier::Mark(root);  // Runtime entry point call.
+        //     entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
+        //     root = entrypoint(root);  // root = ReadBarrier::Mark(root);  // Entry point call.
         //   }
 
-        // Slow path marking the GC root `root`. The entrypoint will already be loaded in `temp`.
-        Location temp = LocationFrom(lr);
+        // Slow path marking the GC root `root`. The entrypoint will
+        // be loaded by the slow path code.
         SlowPathCodeARMVIXL* slow_path =
-            new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARMVIXL(
-                instruction, root, /* entrypoint */ temp);
+            new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARMVIXL(instruction, root);
         codegen_->AddSlowPath(slow_path);
 
-        // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
-        const int32_t entry_point_offset =
-            CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(root.reg());
-        // Loading the entrypoint does not require a load acquire since it is only changed when
-        // threads are suspended or running a checkpoint.
-        GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), tr, entry_point_offset);
-
         // /* GcRoot<mirror::Object> */ root = *(obj + offset)
         GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
         static_assert(
@@ -8616,9 +8607,7 @@
                       "art::mirror::CompressedReference<mirror::Object> and int32_t "
                       "have different sizes.");
 
-        // The entrypoint is null when the GC is not marking, this prevents one load compared to
-        // checking GetIsGcMarking.
-        __ CompareAndBranchIfNonZero(RegisterFrom(temp), slow_path->GetEntryLabel());
+        __ CompareAndBranchIfNonZero(mr, slow_path->GetEntryLabel());
         __ Bind(slow_path->GetExitLabel());
       }
     } else {
@@ -8659,20 +8648,19 @@
 
   if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
       !Runtime::Current()->UseJitCompilation()) {
-    // Note that we do not actually check the value of `GetIsGcMarking()`
-    // to decide whether to mark the loaded reference or not.  Instead, we
-    // load into `temp` (actually kBakerCcEntrypointRegister) the read
-    // barrier mark introspection entrypoint. If `temp` is null, it means
-    // that `GetIsGcMarking()` is false, and vice versa.
+    // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
+    // Marking Register) to decide whether we need to enter the slow
+    // path to mark the reference. Then, in the slow path, check the
+    // gray bit in the lock word of the reference's holder (`obj`) to
+    // decide whether to mark `ref` or not.
     //
     // We use link-time generated thunks for the slow path. That thunk checks
     // the holder and jumps to the entrypoint if needed. If the holder is not
     // gray, it creates a fake dependency and returns to the LDR instruction.
     //
-    //     temp = Thread::Current()->pReadBarrierMarkIntrospection
     //     lr = &gray_return_address;
-    //     if (temp != nullptr) {
-    //        goto field_thunk<holder_reg, base_reg>(lr)
+    //     if (mr) {  // Thread::Current()->GetIsGcMarking()
+    //       goto field_thunk<holder_reg, base_reg>(lr)
     //     }
     //   not_gray_return_address:
     //     // Original reference load. If the offset is too large to fit
@@ -8701,19 +8689,12 @@
         base.GetCode(), obj.GetCode(), narrow);
     vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data);
 
-    // entrypoint_reg =
-    //     Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection.
-    DCHECK_EQ(ip.GetCode(), 12u);
-    const int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ip.GetCode());
-    __ Ldr(kBakerCcEntrypointRegister, MemOperand(tr, entry_point_offset));
-
     vixl::EmissionCheckScope guard(
         GetVIXLAssembler(),
         (kPoisonHeapReferences ? 5u : 4u) * vixl32::kMaxInstructionSizeInBytes);
     vixl32::Label return_address;
     EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
-    __ cmp(kBakerCcEntrypointRegister, Operand(0));
+    __ cmp(mr, Operand(0));
     EmitPlaceholderBne(this, bne_label);
     ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
     __ ldr(EncodingSize(narrow ? Narrow : Wide), ref_reg, MemOperand(base, offset));
@@ -8760,20 +8741,19 @@
 
   if (kBakerReadBarrierLinkTimeThunksEnableForArrays &&
       !Runtime::Current()->UseJitCompilation()) {
-    // Note that we do not actually check the value of `GetIsGcMarking()`
-    // to decide whether to mark the loaded reference or not.  Instead, we
-    // load into `temp` (actually kBakerCcEntrypointRegister) the read
-    // barrier mark introspection entrypoint. If `temp` is null, it means
-    // that `GetIsGcMarking()` is false, and vice versa.
+    // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
+    // Marking Register) to decide whether we need to enter the slow
+    // path to mark the reference. Then, in the slow path, check the
+    // gray bit in the lock word of the reference's holder (`obj`) to
+    // decide whether to mark `ref` or not.
     //
     // We use link-time generated thunks for the slow path. That thunk checks
     // the holder and jumps to the entrypoint if needed. If the holder is not
     // gray, it creates a fake dependency and returns to the LDR instruction.
     //
-    //     temp = Thread::Current()->pReadBarrierMarkIntrospection
     //     lr = &gray_return_address;
-    //     if (temp != nullptr) {
-    //        goto field_thunk<holder_reg, base_reg>(lr)
+    //     if (mr) {  // Thread::Current()->GetIsGcMarking()
+    //       goto array_thunk<base_reg>(lr)
     //     }
     //   not_gray_return_address:
     //     // Original reference load. If the offset is too large to fit
@@ -8793,20 +8773,13 @@
         linker::Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(data_reg.GetCode());
     vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data);
 
-    // entrypoint_reg =
-    //     Thread::Current()->pReadBarrierMarkReg16, i.e. pReadBarrierMarkIntrospection.
-    DCHECK_EQ(ip.GetCode(), 12u);
-    const int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ip.GetCode());
-    __ Ldr(kBakerCcEntrypointRegister, MemOperand(tr, entry_point_offset));
     __ Add(data_reg, obj, Operand(data_offset));
-
     vixl::EmissionCheckScope guard(
         GetVIXLAssembler(),
         (kPoisonHeapReferences ? 5u : 4u) * vixl32::kMaxInstructionSizeInBytes);
     vixl32::Label return_address;
     EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
-    __ cmp(kBakerCcEntrypointRegister, Operand(0));
+    __ cmp(mr, Operand(0));
     EmitPlaceholderBne(this, bne_label);
     ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
     __ ldr(ref_reg, MemOperand(data_reg, index_reg, vixl32::LSL, scale_factor));
@@ -8838,26 +8811,21 @@
   DCHECK(kEmitCompilerReadBarrier);
   DCHECK(kUseBakerReadBarrier);
 
-  // Query `art::Thread::Current()->GetIsGcMarking()` to decide
-  // whether we need to enter the slow path to mark the reference.
-  // Then, in the slow path, check the gray bit in the lock word of
-  // the reference's holder (`obj`) to decide whether to mark `ref` or
-  // not.
+  // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
+  // Marking Register) to decide whether we need to enter the slow
+  // path to mark the reference. Then, in the slow path, check the
+  // gray bit in the lock word of the reference's holder (`obj`) to
+  // decide whether to mark `ref` or not.
   //
-  // Note that we do not actually check the value of `GetIsGcMarking()`;
-  // instead, we load into `temp2` the read barrier mark entry point
-  // corresponding to register `ref`. If `temp2` is null, it means
-  // that `GetIsGcMarking()` is false, and vice versa.
-  //
-  //   temp2 = Thread::Current()->pReadBarrierMarkReg ## root.reg()
-  //   if (temp2 != nullptr) {  // <=> Thread::Current()->GetIsGcMarking()
+  //   if (mr) {  // Thread::Current()->GetIsGcMarking()
   //     // Slow path.
   //     uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
   //     lfence;  // Load fence or artificial data dependency to prevent load-load reordering
   //     HeapReference<mirror::Object> ref = *src;  // Original reference load.
   //     bool is_gray = (rb_state == ReadBarrier::GrayState());
   //     if (is_gray) {
-  //       ref = temp2(ref);  // ref = ReadBarrier::Mark(ref);  // Runtime entry point call.
+  //       entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
+  //       ref = entrypoint(ref);  // ref = ReadBarrier::Mark(ref);  // Runtime entry point call.
   //     }
   //   } else {
   //     HeapReference<mirror::Object> ref = *src;  // Original reference load.
@@ -8866,30 +8834,13 @@
   vixl32::Register temp_reg = RegisterFrom(temp);
 
   // Slow path marking the object `ref` when the GC is marking. The
-  // entrypoint will already be loaded in `temp2`.
-  Location temp2 = LocationFrom(lr);
+  // entrypoint will be loaded by the slow path code.
   SlowPathCodeARMVIXL* slow_path =
       new (GetGraph()->GetArena()) LoadReferenceWithBakerReadBarrierSlowPathARMVIXL(
-          instruction,
-          ref,
-          obj,
-          offset,
-          index,
-          scale_factor,
-          needs_null_check,
-          temp_reg,
-          /* entrypoint */ temp2);
+          instruction, ref, obj, offset, index, scale_factor, needs_null_check, temp_reg);
   AddSlowPath(slow_path);
 
-  // temp2 = Thread::Current()->pReadBarrierMarkReg ## ref.reg()
-  const int32_t entry_point_offset =
-      CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref.reg());
-  // Loading the entrypoint does not require a load acquire since it is only changed when
-  // threads are suspended or running a checkpoint.
-  GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp2), tr, entry_point_offset);
-  // The entrypoint is null when the GC is not marking, this prevents one load compared to
-  // checking GetIsGcMarking.
-  __ CompareAndBranchIfNonZero(RegisterFrom(temp2), slow_path->GetEntryLabel());
+  __ CompareAndBranchIfNonZero(mr, slow_path->GetEntryLabel());
   // Fast path: the GC is not marking: just load the reference.
   GenerateRawReferenceLoad(instruction, ref, obj, offset, index, scale_factor, needs_null_check);
   __ Bind(slow_path->GetExitLabel());
@@ -8905,19 +8856,14 @@
   DCHECK(kEmitCompilerReadBarrier);
   DCHECK(kUseBakerReadBarrier);
 
-  // Query `art::Thread::Current()->GetIsGcMarking()` to decide
-  // whether we need to enter the slow path to update the reference
-  // field within `obj`.  Then, in the slow path, check the gray bit
-  // in the lock word of the reference's holder (`obj`) to decide
-  // whether to mark `ref` and update the field or not.
+  // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
+  // Marking Register) to decide whether we need to enter the slow
+  // path to update the reference field within `obj`. Then, in the
+  // slow path, check the gray bit in the lock word of the reference's
+  // holder (`obj`) to decide whether to mark `ref` and update the
+  // field or not.
   //
-  // Note that we do not actually check the value of `GetIsGcMarking()`;
-  // instead, we load into `temp3` the read barrier mark entry point
-  // corresponding to register `ref`. If `temp3` is null, it means
-  // that `GetIsGcMarking()` is false, and vice versa.
-  //
-  //   temp3 = Thread::Current()->pReadBarrierMarkReg ## root.reg()
-  //   if (temp3 != nullptr) {  // <=> Thread::Current()->GetIsGcMarking()
+  //   if (mr) {  // Thread::Current()->GetIsGcMarking()
   //     // Slow path.
   //     uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
   //     lfence;  // Load fence or artificial data dependency to prevent load-load reordering
@@ -8925,7 +8871,8 @@
   //     bool is_gray = (rb_state == ReadBarrier::GrayState());
   //     if (is_gray) {
   //       old_ref = ref;
-  //       ref = temp3(ref);  // ref = ReadBarrier::Mark(ref);  // Runtime entry point call.
+  //       entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
+  //       ref = entrypoint(ref);  // ref = ReadBarrier::Mark(ref);  // Runtime entry point call.
   //       compareAndSwapObject(obj, field_offset, old_ref, ref);
   //     }
   //   }
@@ -8933,8 +8880,7 @@
   vixl32::Register temp_reg = RegisterFrom(temp);
 
   // Slow path updating the object reference at address `obj + field_offset`
-  // when the GC is marking. The entrypoint will already be loaded in `temp3`.
-  Location temp3 = LocationFrom(lr);
+  // when the GC is marking. The entrypoint will be loaded by the slow path code.
   SlowPathCodeARMVIXL* slow_path =
       new (GetGraph()->GetArena()) LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL(
           instruction,
@@ -8945,19 +8891,10 @@
           /* scale_factor */ ScaleFactor::TIMES_1,
           needs_null_check,
           temp_reg,
-          temp2,
-          /* entrypoint */ temp3);
+          temp2);
   AddSlowPath(slow_path);
 
-  // temp3 = Thread::Current()->pReadBarrierMarkReg ## ref.reg()
-  const int32_t entry_point_offset =
-      CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref.reg());
-  // Loading the entrypoint does not require a load acquire since it is only changed when
-  // threads are suspended or running a checkpoint.
-  GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp3), tr, entry_point_offset);
-  // The entrypoint is null when the GC is not marking, this prevents one load compared to
-  // checking GetIsGcMarking.
-  __ CompareAndBranchIfNonZero(RegisterFrom(temp3), slow_path->GetEntryLabel());
+  __ CompareAndBranchIfNonZero(mr, slow_path->GetEntryLabel());
   // Fast path: the GC is not marking: nothing to do (the field is
   // up-to-date, and we don't need to load the reference).
   __ Bind(slow_path->GetExitLabel());
@@ -9057,7 +8994,7 @@
                                                         Location index) {
   if (kEmitCompilerReadBarrier) {
     // Baker's read barriers shall be handled by the fast path
-    // (CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier).
+    // (CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier).
     DCHECK(!kUseBakerReadBarrier);
     // If heap poisoning is enabled, unpoisoning will be taken care of
     // by the runtime within the slow path.
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index ad3283a..01cf287 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -35,13 +35,6 @@
 #include "aarch32/macro-assembler-aarch32.h"
 #pragma GCC diagnostic pop
 
-// Default to use the VIXL-based backend on ARM.
-#ifdef ART_USE_OLD_ARM_BACKEND
-static constexpr bool kArmUseVIXL32 = false;
-#else
-static constexpr bool kArmUseVIXL32 = true;
-#endif
-
 namespace art {
 namespace arm {
 
@@ -80,12 +73,16 @@
 
 static const vixl::aarch32::Register kCoreAlwaysSpillRegister = vixl::aarch32::r5;
 
-// Callee saves core registers r5, r6, r7, r8, r10, r11, and lr.
+// Callee saves core registers r5, r6, r7, r8 (except when emitting Baker
+// read barriers, where it is used as Marking Register), r10, r11, and lr.
 static const vixl::aarch32::RegisterList kCoreCalleeSaves = vixl::aarch32::RegisterList::Union(
     vixl::aarch32::RegisterList(vixl::aarch32::r5,
                                 vixl::aarch32::r6,
-                                vixl::aarch32::r7,
-                                vixl::aarch32::r8),
+                                vixl::aarch32::r7),
+    // Do not consider r8 as a callee-save register with Baker read barriers.
+    ((kEmitCompilerReadBarrier && kUseBakerReadBarrier)
+         ? vixl::aarch32::RegisterList()
+         : vixl::aarch32::RegisterList(vixl::aarch32::r8)),
     vixl::aarch32::RegisterList(vixl::aarch32::r10,
                                 vixl::aarch32::r11,
                                 vixl::aarch32::lr));
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 4c4d97b..23d188d 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -434,10 +434,13 @@
       : SlowPathCodeMIPS(instruction), successor_(successor) {}
 
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    LocationSummary* locations = instruction_->GetLocations();
     CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
     __ Bind(GetEntryLabel());
+    SaveLiveRegisters(codegen, locations);     // Only saves live vector registers for SIMD.
     mips_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
     CheckEntrypointTypes<kQuickTestSuspend, void, void>();
+    RestoreLiveRegisters(codegen, locations);  // Only restores live vector registers for SIMD.
     if (successor_ == nullptr) {
       __ B(GetReturnLabel());
     } else {
@@ -653,7 +656,7 @@
       __ NopIfNoReordering();
     } else {
       int32_t entry_point_offset =
-          CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(ref_reg - 1);
+          Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(ref_reg - 1);
       // This runtime call does not require a stack map.
       mips_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset,
                                                         instruction_,
@@ -747,7 +750,7 @@
     //   rX <- ReadBarrierMarkRegX(rX)
     //
     int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(ref_reg - 1);
+        Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(ref_reg - 1);
     // This runtime call does not require a stack map.
     mips_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset,
                                                       instruction_,
@@ -1448,6 +1451,11 @@
   __ Bind(GetLabelOf(block));
 }
 
+VectorRegister VectorRegisterFrom(Location location) {
+  DCHECK(location.IsFpuRegister());
+  return static_cast<VectorRegister>(location.AsFpuRegister<FRegister>());
+}
+
 void CodeGeneratorMIPS::MoveLocation(Location destination,
                                      Location source,
                                      Primitive::Type dst_type) {
@@ -1495,12 +1503,19 @@
         __ Mtc1(src_low, dst);
         __ MoveToFpuHigh(src_high, dst);
       } else if (source.IsFpuRegister()) {
-        if (Primitive::Is64BitType(dst_type)) {
-          __ MovD(destination.AsFpuRegister<FRegister>(), source.AsFpuRegister<FRegister>());
+        if (GetGraph()->HasSIMD()) {
+          __ MoveV(VectorRegisterFrom(destination),
+                   VectorRegisterFrom(source));
         } else {
-          DCHECK_EQ(dst_type, Primitive::kPrimFloat);
-          __ MovS(destination.AsFpuRegister<FRegister>(), source.AsFpuRegister<FRegister>());
+          if (Primitive::Is64BitType(dst_type)) {
+            __ MovD(destination.AsFpuRegister<FRegister>(), source.AsFpuRegister<FRegister>());
+          } else {
+            DCHECK_EQ(dst_type, Primitive::kPrimFloat);
+            __ MovS(destination.AsFpuRegister<FRegister>(), source.AsFpuRegister<FRegister>());
+          }
         }
+      } else if (source.IsSIMDStackSlot()) {
+        __ LoadQFromOffset(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
       } else if (source.IsDoubleStackSlot()) {
         DCHECK(Primitive::Is64BitType(dst_type));
         __ LoadDFromOffset(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
@@ -1509,6 +1524,14 @@
         DCHECK(source.IsStackSlot()) << "Cannot move from " << source << " to " << destination;
         __ LoadSFromOffset(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
       }
+    } else if (destination.IsSIMDStackSlot()) {
+      if (source.IsFpuRegister()) {
+        __ StoreQToOffset(source.AsFpuRegister<FRegister>(), SP, destination.GetStackIndex());
+      } else {
+        DCHECK(source.IsSIMDStackSlot());
+        __ LoadQFromOffset(FTMP, SP, source.GetStackIndex());
+        __ StoreQToOffset(FTMP, SP, destination.GetStackIndex());
+      }
     } else if (destination.IsDoubleStackSlot()) {
       int32_t dst_offset = destination.GetStackIndex();
       if (source.IsRegisterPair()) {
@@ -1875,13 +1898,21 @@
 }
 
 size_t CodeGeneratorMIPS::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
-  __ StoreDToOffset(FRegister(reg_id), SP, stack_index);
-  return kMipsDoublewordSize;
+  if (GetGraph()->HasSIMD()) {
+    __ StoreQToOffset(FRegister(reg_id), SP, stack_index);
+  } else {
+    __ StoreDToOffset(FRegister(reg_id), SP, stack_index);
+  }
+  return GetFloatingPointSpillSlotSize();
 }
 
 size_t CodeGeneratorMIPS::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
-  __ LoadDFromOffset(FRegister(reg_id), SP, stack_index);
-  return kMipsDoublewordSize;
+  if (GetGraph()->HasSIMD()) {
+    __ LoadQFromOffset(FRegister(reg_id), SP, stack_index);
+  } else {
+    __ LoadDFromOffset(FRegister(reg_id), SP, stack_index);
+  }
+  return GetFloatingPointSpillSlotSize();
 }
 
 void CodeGeneratorMIPS::DumpCoreRegister(std::ostream& stream, int reg) const {
@@ -6466,7 +6497,7 @@
 
       // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
       const int32_t entry_point_offset =
-          CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(root.reg() - 1);
+          Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(root.reg() - 1);
       // Loading the entrypoint does not require a load acquire since it is only changed when
       // threads are suspended or running a checkpoint.
       __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, entry_point_offset);
@@ -7828,8 +7859,11 @@
 void InstructionCodeGeneratorMIPS::VisitNewArray(HNewArray* instruction) {
   // Note: if heap poisoning is enabled, the entry point takes care
   // of poisoning the reference.
-  codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc());
+  QuickEntrypointEnum entrypoint =
+      CodeGenerator::GetArrayAllocationEntrypoint(instruction->GetLoadClass()->GetClass());
+  codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
   CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
+  DCHECK(!codegen_->IsLeafMethod());
 }
 
 void LocationsBuilderMIPS::VisitNewInstance(HNewInstance* instruction) {
@@ -8216,7 +8250,11 @@
 void LocationsBuilderMIPS::VisitSuspendCheck(HSuspendCheck* instruction) {
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
-  locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
+  // In suspend check slow path, usually there are no caller-save registers at all.
+  // If SIMD instructions are present, however, we force spilling all live SIMD
+  // registers in full width (since the runtime only saves/restores lower part).
+  locations->SetCustomSlowPathCallerSaves(
+      GetGraph()->HasSIMD() ? RegisterSet::AllFpu() : RegisterSet::Empty());
 }
 
 void InstructionCodeGeneratorMIPS::VisitSuspendCheck(HSuspendCheck* instruction) {
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index c259ea3..52ee852 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -61,6 +61,8 @@
 
 class CodeGeneratorMIPS;
 
+VectorRegister VectorRegisterFrom(Location location);
+
 class InvokeDexCallingConvention : public CallingConvention<Register, FRegister> {
  public:
   InvokeDexCallingConvention()
@@ -344,6 +346,10 @@
                                  uint32_t num_entries,
                                  HBasicBlock* switch_block,
                                  HBasicBlock* default_block);
+
+  int32_t VecAddress(LocationSummary* locations,
+                     size_t size,
+                     /* out */ Register* adjusted_base);
   void GenConditionalMoveR2(HSelect* select);
   void GenConditionalMoveR6(HSelect* select);
 
@@ -372,7 +378,11 @@
 
   size_t GetWordSize() const OVERRIDE { return kMipsWordSize; }
 
-  size_t GetFloatingPointSpillSlotSize() const OVERRIDE { return kMipsDoublewordSize; }
+  size_t GetFloatingPointSpillSlotSize() const OVERRIDE {
+    return GetGraph()->HasSIMD()
+        ? 2 * kMipsDoublewordSize   // 16 bytes for each spill.
+        : 1 * kMipsDoublewordSize;  //  8 bytes for each spill.
+  }
 
   uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE {
     return assembler_.GetLabelLocation(GetLabelOf(block));
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 5fb8755..454a2dd 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -606,7 +606,7 @@
       __ Nop();
     } else {
       int32_t entry_point_offset =
-          CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(ref_reg - 1);
+          Thread::ReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(ref_reg - 1);
       // This runtime call does not require a stack map.
       mips64_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset,
                                                           instruction_,
@@ -699,7 +699,7 @@
     //   rX <- ReadBarrierMarkRegX(rX)
     //
     int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(ref_reg - 1);
+        Thread::ReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(ref_reg - 1);
     // This runtime call does not require a stack map.
     mips64_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset,
                                                         instruction_,
@@ -1289,6 +1289,11 @@
                           SP,
                           source.GetStackIndex());
       }
+    } else if (source.IsSIMDStackSlot()) {
+      __ LoadFpuFromOffset(kLoadQuadword,
+                           destination.AsFpuRegister<FpuRegister>(),
+                           SP,
+                           source.GetStackIndex());
     } else if (source.IsConstant()) {
       // Move to GPR/FPR from constant
       GpuRegister gpr = AT;
@@ -1329,12 +1334,17 @@
       }
     } else if (source.IsFpuRegister()) {
       if (destination.IsFpuRegister()) {
-        // Move to FPR from FPR
-        if (dst_type == Primitive::kPrimFloat) {
-          __ MovS(destination.AsFpuRegister<FpuRegister>(), source.AsFpuRegister<FpuRegister>());
+        if (GetGraph()->HasSIMD()) {
+          __ MoveV(VectorRegisterFrom(destination),
+                   VectorRegisterFrom(source));
         } else {
-          DCHECK_EQ(dst_type, Primitive::kPrimDouble);
-          __ MovD(destination.AsFpuRegister<FpuRegister>(), source.AsFpuRegister<FpuRegister>());
+          // Move to FPR from FPR
+          if (dst_type == Primitive::kPrimFloat) {
+            __ MovS(destination.AsFpuRegister<FpuRegister>(), source.AsFpuRegister<FpuRegister>());
+          } else {
+            DCHECK_EQ(dst_type, Primitive::kPrimDouble);
+            __ MovD(destination.AsFpuRegister<FpuRegister>(), source.AsFpuRegister<FpuRegister>());
+          }
         }
       } else {
         DCHECK(destination.IsRegister());
@@ -1345,6 +1355,23 @@
         }
       }
     }
+  } else if (destination.IsSIMDStackSlot()) {
+    if (source.IsFpuRegister()) {
+      __ StoreFpuToOffset(kStoreQuadword,
+                          source.AsFpuRegister<FpuRegister>(),
+                          SP,
+                          destination.GetStackIndex());
+    } else {
+      DCHECK(source.IsSIMDStackSlot());
+      __ LoadFpuFromOffset(kLoadQuadword,
+                           FTMP,
+                           SP,
+                           source.GetStackIndex());
+      __ StoreFpuToOffset(kStoreQuadword,
+                          FTMP,
+                          SP,
+                          destination.GetStackIndex());
+    }
   } else {  // The destination is not a register. It must be a stack slot.
     DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot());
     if (source.IsRegister() || source.IsFpuRegister()) {
@@ -4394,7 +4421,7 @@
 
       // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
       const int32_t entry_point_offset =
-          CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(root.reg() - 1);
+          Thread::ReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(root.reg() - 1);
       // Loading the entrypoint does not require a load acquire since it is only changed when
       // threads are suspended or running a checkpoint.
       __ LoadFromOffset(kLoadDoubleword, temp.AsRegister<GpuRegister>(), TR, entry_point_offset);
@@ -5551,8 +5578,11 @@
 void InstructionCodeGeneratorMIPS64::VisitNewArray(HNewArray* instruction) {
   // Note: if heap poisoning is enabled, the entry point takes care
   // of poisoning the reference.
-  codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc());
+  QuickEntrypointEnum entrypoint =
+      CodeGenerator::GetArrayAllocationEntrypoint(instruction->GetLoadClass()->GetClass());
+  codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
   CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
+  DCHECK(!codegen_->IsLeafMethod());
 }
 
 void LocationsBuilderMIPS64::VisitNewInstance(HNewInstance* instruction) {
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index b620973..c94cc93 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -59,6 +59,8 @@
 
 class CodeGeneratorMIPS64;
 
+VectorRegister VectorRegisterFrom(Location location);
+
 class InvokeDexCallingConvention : public CallingConvention<GpuRegister, FpuRegister> {
  public:
   InvokeDexCallingConvention()
diff --git a/compiler/optimizing/code_generator_vector_arm.cc b/compiler/optimizing/code_generator_vector_arm.cc
deleted file mode 100644
index f8552dc..0000000
--- a/compiler/optimizing/code_generator_vector_arm.cc
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "code_generator_arm.h"
-
-namespace art {
-namespace arm {
-
-// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
-#define __ down_cast<ArmAssembler*>(GetAssembler())->  // NOLINT
-
-void LocationsBuilderARM::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorARM::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecSetScalars(HVecSetScalars* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorARM::VisitVecSetScalars(HVecSetScalars* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecSumReduce(HVecSumReduce* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorARM::VisitVecSumReduce(HVecSumReduce* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-// Helper to set up locations for vector unary operations.
-static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* instruction) {
-  LocationSummary* locations = new (arena) LocationSummary(instruction);
-  switch (instruction->GetPackedType()) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-    case Primitive::kPrimInt:
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      DCHECK(locations);
-      break;
-    default:
-      LOG(FATAL) << "Unsupported SIMD type";
-      UNREACHABLE();
-  }
-}
-
-void LocationsBuilderARM::VisitVecCnv(HVecCnv* instruction) {
-  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecCnv(HVecCnv* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecNeg(HVecNeg* instruction) {
-  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecNeg(HVecNeg* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecAbs(HVecAbs* instruction) {
-  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecAbs(HVecAbs* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecNot(HVecNot* instruction) {
-  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecNot(HVecNot* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-// Helper to set up locations for vector binary operations.
-static void CreateVecBinOpLocations(ArenaAllocator* arena, HVecBinaryOperation* instruction) {
-  LocationSummary* locations = new (arena) LocationSummary(instruction);
-  switch (instruction->GetPackedType()) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-    case Primitive::kPrimInt:
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      DCHECK(locations);
-      break;
-    default:
-      LOG(FATAL) << "Unsupported SIMD type";
-      UNREACHABLE();
-  }
-}
-
-void LocationsBuilderARM::VisitVecAdd(HVecAdd* instruction) {
-  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecAdd(HVecAdd* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
-  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecSub(HVecSub* instruction) {
-  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecSub(HVecSub* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecMul(HVecMul* instruction) {
-  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecMul(HVecMul* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecDiv(HVecDiv* instruction) {
-  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecDiv(HVecDiv* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecMin(HVecMin* instruction) {
-  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecMin(HVecMin* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecMax(HVecMax* instruction) {
-  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecMax(HVecMax* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecAnd(HVecAnd* instruction) {
-  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecAnd(HVecAnd* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecAndNot(HVecAndNot* instruction) {
-  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecAndNot(HVecAndNot* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecOr(HVecOr* instruction) {
-  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecOr(HVecOr* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecXor(HVecXor* instruction) {
-  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecXor(HVecXor* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-// Helper to set up locations for vector shift operations.
-static void CreateVecShiftLocations(ArenaAllocator* arena, HVecBinaryOperation* instruction) {
-  LocationSummary* locations = new (arena) LocationSummary(instruction);
-  switch (instruction->GetPackedType()) {
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-    case Primitive::kPrimInt:
-    case Primitive::kPrimLong:
-      DCHECK(locations);
-      break;
-    default:
-      LOG(FATAL) << "Unsupported SIMD type";
-      UNREACHABLE();
-  }
-}
-
-void LocationsBuilderARM::VisitVecShl(HVecShl* instruction) {
-  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecShl(HVecShl* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecShr(HVecShr* instruction) {
-  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecShr(HVecShr* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecUShr(HVecUShr* instruction) {
-  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
-}
-
-void InstructionCodeGeneratorARM::VisitVecUShr(HVecUShr* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) {
-  LOG(FATAL) << "No SIMD for " << instr->GetId();
-}
-
-void InstructionCodeGeneratorARM::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) {
-  LOG(FATAL) << "No SIMD for " << instr->GetId();
-}
-
-void LocationsBuilderARM::VisitVecLoad(HVecLoad* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorARM::VisitVecLoad(HVecLoad* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM::VisitVecStore(HVecStore* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorARM::VisitVecStore(HVecStore* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-#undef __
-
-}  // namespace arm
-}  // namespace art
diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc
index c4a3225..ea36e90 100644
--- a/compiler/optimizing/code_generator_vector_mips.cc
+++ b/compiler/optimizing/code_generator_vector_mips.cc
@@ -15,6 +15,7 @@
  */
 
 #include "code_generator_mips.h"
+#include "mirror/array-inl.h"
 
 namespace art {
 namespace mips {
@@ -23,11 +24,68 @@
 #define __ down_cast<MipsAssembler*>(GetAssembler())->  // NOLINT
 
 void LocationsBuilderMIPS::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ FillB(dst, locations->InAt(0).AsRegister<Register>());
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ FillH(dst, locations->InAt(0).AsRegister<Register>());
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ FillW(dst, locations->InAt(0).AsRegister<Register>());
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Mtc1(locations->InAt(0).AsRegisterPairLow<Register>(), FTMP);
+      __ MoveToFpuHigh(locations->InAt(0).AsRegisterPairHigh<Register>(), FTMP);
+      __ ReplicateFPToVectorRegister(dst, FTMP, /* is_double */ true);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ ReplicateFPToVectorRegister(dst,
+                                     locations->InAt(0).AsFpuRegister<FRegister>(),
+                                     /* is_double */ false);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ ReplicateFPToVectorRegister(dst,
+                                     locations->InAt(0).AsFpuRegister<FRegister>(),
+                                     /* is_double */ true);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecSetScalars(HVecSetScalars* instruction) {
@@ -51,13 +109,23 @@
   LocationSummary* locations = new (arena) LocationSummary(instruction);
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimBoolean:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(),
+                        instruction->IsVecNot() ? Location::kOutputOverlap
+                                                : Location::kNoOutputOverlap);
+      break;
     case Primitive::kPrimByte:
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble:
-      DCHECK(locations);
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(),
+                        (instruction->IsVecNeg() || instruction->IsVecAbs())
+                            ? Location::kOutputOverlap
+                            : Location::kNoOutputOverlap);
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -70,7 +138,17 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecCnv(HVecCnv* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister src = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  Primitive::Type from = instruction->GetInputType();
+  Primitive::Type to = instruction->GetResultType();
+  if (from == Primitive::kPrimInt && to == Primitive::kPrimFloat) {
+    DCHECK_EQ(4u, instruction->GetVectorLength());
+    __ Ffint_sW(dst, src);
+  } else {
+    LOG(FATAL) << "Unsupported SIMD type";
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecNeg(HVecNeg* instruction) {
@@ -78,7 +156,45 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecNeg(HVecNeg* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister src = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ FillB(dst, ZERO);
+      __ SubvB(dst, dst, src);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ FillH(dst, ZERO);
+      __ SubvH(dst, dst, src);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ FillW(dst, ZERO);
+      __ SubvW(dst, dst, src);
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ FillW(dst, ZERO);
+      __ SubvD(dst, dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ FillW(dst, ZERO);
+      __ FsubW(dst, dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ FillW(dst, ZERO);
+      __ FsubD(dst, dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecAbs(HVecAbs* instruction) {
@@ -86,7 +202,47 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecAbs(HVecAbs* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister src = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ FillB(dst, ZERO);       // all zeroes
+      __ Add_aB(dst, dst, src);  // dst = abs(0) + abs(src)
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ FillH(dst, ZERO);       // all zeroes
+      __ Add_aH(dst, dst, src);  // dst = abs(0) + abs(src)
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ FillW(dst, ZERO);       // all zeroes
+      __ Add_aW(dst, dst, src);  // dst = abs(0) + abs(src)
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ FillW(dst, ZERO);       // all zeroes
+      __ Add_aD(dst, dst, src);  // dst = abs(0) + abs(src)
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ LdiW(dst, -1);          // all ones
+      __ SrliW(dst, dst, 1);
+      __ AndV(dst, dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ LdiD(dst, -1);          // all ones
+      __ SrliD(dst, dst, 1);
+      __ AndV(dst, dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecNot(HVecNot* instruction) {
@@ -94,7 +250,30 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecNot(HVecNot* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister src = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:  // special case boolean-not
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ LdiB(dst, 1);
+      __ XorV(dst, dst, src);
+      break;
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      __ NorV(dst, src, src);  // lanes do not matter
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 // Helper to set up locations for vector binary operations.
@@ -106,9 +285,12 @@
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble:
-      DCHECK(locations);
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -121,7 +303,40 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecAdd(HVecAdd* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister lhs = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister rhs = VectorRegisterFrom(locations->InAt(1));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ AddvB(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ AddvH(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ AddvW(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ AddvD(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ FaddW(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ FaddD(dst, lhs, rhs);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
@@ -129,7 +344,40 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister lhs = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister rhs = VectorRegisterFrom(locations->InAt(1));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      if (instruction->IsUnsigned()) {
+        instruction->IsRounded()
+            ? __ Aver_uB(dst, lhs, rhs)
+            : __ Ave_uB(dst, lhs, rhs);
+      } else {
+        instruction->IsRounded()
+            ? __ Aver_sB(dst, lhs, rhs)
+            : __ Ave_sB(dst, lhs, rhs);
+      }
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      if (instruction->IsUnsigned()) {
+        instruction->IsRounded()
+            ? __ Aver_uH(dst, lhs, rhs)
+            : __ Ave_uH(dst, lhs, rhs);
+      } else {
+        instruction->IsRounded()
+            ? __ Aver_sH(dst, lhs, rhs)
+            : __ Ave_sH(dst, lhs, rhs);
+      }
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecSub(HVecSub* instruction) {
@@ -137,7 +385,40 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecSub(HVecSub* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister lhs = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister rhs = VectorRegisterFrom(locations->InAt(1));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ SubvB(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ SubvH(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ SubvW(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ SubvD(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ FsubW(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ FsubD(dst, lhs, rhs);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecMul(HVecMul* instruction) {
@@ -145,7 +426,40 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecMul(HVecMul* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister lhs = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister rhs = VectorRegisterFrom(locations->InAt(1));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ MulvB(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ MulvH(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ MulvW(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ MulvD(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ FmulW(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ FmulD(dst, lhs, rhs);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecDiv(HVecDiv* instruction) {
@@ -153,7 +467,23 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecDiv(HVecDiv* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister lhs = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister rhs = VectorRegisterFrom(locations->InAt(1));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ FdivW(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ FdivD(dst, lhs, rhs);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecMin(HVecMin* instruction) {
@@ -161,7 +491,60 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecMin(HVecMin* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister lhs = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister rhs = VectorRegisterFrom(locations->InAt(1));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      if (instruction->IsUnsigned()) {
+        __ Min_uB(dst, lhs, rhs);
+      } else {
+        __ Min_sB(dst, lhs, rhs);
+      }
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      if (instruction->IsUnsigned()) {
+        __ Min_uH(dst, lhs, rhs);
+      } else {
+        __ Min_sH(dst, lhs, rhs);
+      }
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      if (instruction->IsUnsigned()) {
+        __ Min_uW(dst, lhs, rhs);
+      } else {
+        __ Min_sW(dst, lhs, rhs);
+      }
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      if (instruction->IsUnsigned()) {
+        __ Min_uD(dst, lhs, rhs);
+      } else {
+        __ Min_sD(dst, lhs, rhs);
+      }
+      break;
+    // When one of arguments is NaN, fmin.df returns other argument, but Java expects a NaN value.
+    // TODO: Fix min(x, NaN) cases for float and double.
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      DCHECK(!instruction->IsUnsigned());
+      __ FminW(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      DCHECK(!instruction->IsUnsigned());
+      __ FminD(dst, lhs, rhs);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecMax(HVecMax* instruction) {
@@ -169,7 +552,60 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecMax(HVecMax* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister lhs = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister rhs = VectorRegisterFrom(locations->InAt(1));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      if (instruction->IsUnsigned()) {
+        __ Max_uB(dst, lhs, rhs);
+      } else {
+        __ Max_sB(dst, lhs, rhs);
+      }
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      if (instruction->IsUnsigned()) {
+        __ Max_uH(dst, lhs, rhs);
+      } else {
+        __ Max_sH(dst, lhs, rhs);
+      }
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      if (instruction->IsUnsigned()) {
+        __ Max_uW(dst, lhs, rhs);
+      } else {
+        __ Max_sW(dst, lhs, rhs);
+      }
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      if (instruction->IsUnsigned()) {
+        __ Max_uD(dst, lhs, rhs);
+      } else {
+        __ Max_sD(dst, lhs, rhs);
+      }
+      break;
+    // When one of arguments is NaN, fmax.df returns other argument, but Java expects a NaN value.
+    // TODO: Fix max(x, NaN) cases for float and double.
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      DCHECK(!instruction->IsUnsigned());
+      __ FmaxW(dst, lhs, rhs);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      DCHECK(!instruction->IsUnsigned());
+      __ FmaxD(dst, lhs, rhs);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecAnd(HVecAnd* instruction) {
@@ -177,7 +613,27 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecAnd(HVecAnd* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister lhs = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister rhs = VectorRegisterFrom(locations->InAt(1));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      __ AndV(dst, lhs, rhs);  // lanes do not matter
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecAndNot(HVecAndNot* instruction) {
@@ -193,7 +649,27 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecOr(HVecOr* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister lhs = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister rhs = VectorRegisterFrom(locations->InAt(1));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      __ OrV(dst, lhs, rhs);  // lanes do not matter
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecXor(HVecXor* instruction) {
@@ -201,7 +677,27 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecXor(HVecXor* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister lhs = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister rhs = VectorRegisterFrom(locations->InAt(1));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      __ XorV(dst, lhs, rhs);  // lanes do not matter
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 // Helper to set up locations for vector shift operations.
@@ -213,7 +709,9 @@
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
     case Primitive::kPrimLong:
-      DCHECK(locations);
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -226,7 +724,32 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecShl(HVecShl* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister lhs = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ SlliB(dst, lhs, value);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ SlliH(dst, lhs, value);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ SlliW(dst, lhs, value);
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ SlliD(dst, lhs, value);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecShr(HVecShr* instruction) {
@@ -234,7 +757,32 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecShr(HVecShr* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister lhs = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ SraiB(dst, lhs, value);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ SraiH(dst, lhs, value);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ SraiW(dst, lhs, value);
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ SraiD(dst, lhs, value);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecUShr(HVecUShr* instruction) {
@@ -242,7 +790,32 @@
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecUShr(HVecUShr* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  VectorRegister lhs = VectorRegisterFrom(locations->InAt(0));
+  VectorRegister dst = VectorRegisterFrom(locations->Out());
+  int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ SrliB(dst, lhs, value);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ SrliH(dst, lhs, value);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ SrliW(dst, lhs, value);
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ SrliD(dst, lhs, value);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) {
@@ -253,20 +826,143 @@
   LOG(FATAL) << "No SIMD for " << instr->GetId();
 }
 
+// Helper to set up locations for vector memory operations.
+static void CreateVecMemLocations(ArenaAllocator* arena,
+                                  HVecMemoryOperation* instruction,
+                                  bool is_load) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+      if (is_load) {
+        locations->SetOut(Location::RequiresFpuRegister());
+      } else {
+        locations->SetInAt(2, Location::RequiresFpuRegister());
+      }
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+// Helper to prepare register and offset for vector memory operations. Returns the offset and sets
+// the output parameter adjusted_base to the original base or to a reserved temporary register (AT).
+int32_t InstructionCodeGeneratorMIPS::VecAddress(LocationSummary* locations,
+                                                 size_t size,
+                                                 /* out */ Register* adjusted_base) {
+  Register base = locations->InAt(0).AsRegister<Register>();
+  Location index = locations->InAt(1);
+  int scale = TIMES_1;
+  switch (size) {
+    case 2: scale = TIMES_2; break;
+    case 4: scale = TIMES_4; break;
+    case 8: scale = TIMES_8; break;
+    default: break;
+  }
+  int32_t offset = mirror::Array::DataOffset(size).Int32Value();
+
+  if (index.IsConstant()) {
+    offset += index.GetConstant()->AsIntConstant()->GetValue() << scale;
+    __ AdjustBaseOffsetAndElementSizeShift(base, offset, scale);
+    *adjusted_base = base;
+  } else {
+    Register index_reg = index.AsRegister<Register>();
+    if (scale != TIMES_1) {
+      __ Lsa(AT, index_reg, base, scale);
+    } else {
+      __ Addu(AT, base, index_reg);
+    }
+    *adjusted_base = AT;
+  }
+  return offset;
+}
+
 void LocationsBuilderMIPS::VisitVecLoad(HVecLoad* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  CreateVecMemLocations(GetGraph()->GetArena(), instruction, /* is_load */ true);
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecLoad(HVecLoad* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  size_t size = Primitive::ComponentSize(instruction->GetPackedType());
+  VectorRegister reg = VectorRegisterFrom(locations->Out());
+  Register base;
+  int32_t offset = VecAddress(locations, size, &base);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ LdB(reg, base, offset);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      // Loading 8-bytes (needed if dealing with compressed strings in StringCharAt) from unaligned
+      // memory address may cause a trap to the kernel if the CPU doesn't directly support unaligned
+      // loads and stores.
+      // TODO: Implement support for StringCharAt.
+      DCHECK(!instruction->IsStringCharAt());
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ LdH(reg, base, offset);
+      break;
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ LdW(reg, base, offset);
+      break;
+    case Primitive::kPrimLong:
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ LdD(reg, base, offset);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 void LocationsBuilderMIPS::VisitVecStore(HVecStore* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  CreateVecMemLocations(GetGraph()->GetArena(), instruction, /* is_load */ false);
 }
 
 void InstructionCodeGeneratorMIPS::VisitVecStore(HVecStore* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+  LocationSummary* locations = instruction->GetLocations();
+  size_t size = Primitive::ComponentSize(instruction->GetPackedType());
+  VectorRegister reg = VectorRegisterFrom(locations->InAt(2));
+  Register base;
+  int32_t offset = VecAddress(locations, size, &base);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ StB(reg, base, offset);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ StH(reg, base, offset);
+      break;
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ StW(reg, base, offset);
+      break;
+    case Primitive::kPrimLong:
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ StD(reg, base, offset);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 #undef __
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 79fccfe..af0e646 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -509,8 +509,7 @@
     //
     //   rX <- ReadBarrierMarkRegX(rX)
     //
-    int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86PointerSize>(ref_reg);
+    int32_t entry_point_offset = Thread::ReadBarrierMarkEntryPointsOffset<kX86PointerSize>(ref_reg);
     // This runtime call does not require a stack map.
     x86_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
     __ jmp(GetExitLabel());
@@ -595,8 +594,7 @@
     //
     //   rX <- ReadBarrierMarkRegX(rX)
     //
-    int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86PointerSize>(ref_reg);
+    int32_t entry_point_offset = Thread::ReadBarrierMarkEntryPointsOffset<kX86PointerSize>(ref_reg);
     // This runtime call does not require a stack map.
     x86_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
 
@@ -7153,7 +7151,7 @@
 
       // Test the entrypoint (`Thread::Current()->pReadBarrierMarkReg ## root.reg()`).
       const int32_t entry_point_offset =
-          CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86PointerSize>(root.reg());
+          Thread::ReadBarrierMarkEntryPointsOffset<kX86PointerSize>(root.reg());
       __ fs()->cmpl(Address::Absolute(entry_point_offset), Immediate(0));
       // The entrypoint is null when the GC is not marking.
       __ j(kNotEqual, slow_path->GetEntryLabel());
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 57319ce..86f6d51 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -524,7 +524,7 @@
     //   rX <- ReadBarrierMarkRegX(rX)
     //
     int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86_64PointerSize>(ref_reg);
+        Thread::ReadBarrierMarkEntryPointsOffset<kX86_64PointerSize>(ref_reg);
     // This runtime call does not require a stack map.
     x86_64_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
     __ jmp(GetExitLabel());
@@ -615,7 +615,7 @@
     //   rX <- ReadBarrierMarkRegX(rX)
     //
     int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86_64PointerSize>(ref_reg);
+        Thread::ReadBarrierMarkEntryPointsOffset<kX86_64PointerSize>(ref_reg);
     // This runtime call does not require a stack map.
     x86_64_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
 
@@ -6540,7 +6540,7 @@
 
       // Test the `Thread::Current()->pReadBarrierMarkReg ## root.reg()` entrypoint.
       const int32_t entry_point_offset =
-          CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86_64PointerSize>(root.reg());
+          Thread::ReadBarrierMarkEntryPointsOffset<kX86_64PointerSize>(root.reg());
       __ gs()->cmpl(Address::Absolute(entry_point_offset, /* no_rip */ true), Immediate(0));
       // The entrypoint is null when the GC is not marking.
       __ j(kNotEqual, slow_path->GetEntryLabel());
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index fe25b76..0a8e97c 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -43,8 +43,7 @@
   ::std::vector<CodegenTargetConfig> v;
   ::std::vector<CodegenTargetConfig> test_config_candidates = {
 #ifdef ART_ENABLE_CODEGEN_arm
-    CodegenTargetConfig(kArm, create_codegen_arm),
-    CodegenTargetConfig(kThumb2, create_codegen_arm),
+    // TODO: Should't this be `kThumb2` instead of `kArm` here?
     CodegenTargetConfig(kArm, create_codegen_arm_vixl32),
 #endif
 #ifdef ART_ENABLE_CODEGEN_arm64
diff --git a/compiler/optimizing/codegen_test_utils.h b/compiler/optimizing/codegen_test_utils.h
index 00a16fe..1b38acd 100644
--- a/compiler/optimizing/codegen_test_utils.h
+++ b/compiler/optimizing/codegen_test_utils.h
@@ -35,7 +35,6 @@
 #include "ssa_liveness_analysis.h"
 
 #ifdef ART_ENABLE_CODEGEN_arm
-#include "code_generator_arm.h"
 #include "code_generator_arm_vixl.h"
 #endif
 
@@ -84,26 +83,6 @@
 // in ART, and callee-save in C. Alternatively, we could use or write
 // the stub that saves and restores all registers, but it is easier
 // to just overwrite the code generator.
-class TestCodeGeneratorARM : public arm::CodeGeneratorARM {
- public:
-  TestCodeGeneratorARM(HGraph* graph,
-                       const ArmInstructionSetFeatures& isa_features,
-                       const CompilerOptions& compiler_options)
-      : arm::CodeGeneratorARM(graph, isa_features, compiler_options) {
-    AddAllocatedRegister(Location::RegisterLocation(arm::R6));
-    AddAllocatedRegister(Location::RegisterLocation(arm::R7));
-  }
-
-  void SetupBlockedRegisters() const OVERRIDE {
-    arm::CodeGeneratorARM::SetupBlockedRegisters();
-    blocked_core_registers_[arm::R4] = true;
-    blocked_core_registers_[arm::R6] = false;
-    blocked_core_registers_[arm::R7] = false;
-  }
-};
-
-// A way to test the VIXL32-based code generator on ARM. This will replace
-// TestCodeGeneratorARM when the VIXL32-based backend replaces the existing one.
 class TestCodeGeneratorARMVIXL : public arm::CodeGeneratorARMVIXL {
  public:
   TestCodeGeneratorARMVIXL(HGraph* graph,
@@ -288,14 +267,6 @@
 }
 
 #ifdef ART_ENABLE_CODEGEN_arm
-CodeGenerator* create_codegen_arm(HGraph* graph, const CompilerOptions& compiler_options) {
-  std::unique_ptr<const ArmInstructionSetFeatures> features_arm(
-      ArmInstructionSetFeatures::FromCppDefines());
-  return new (graph->GetArena()) TestCodeGeneratorARM(graph,
-                                                      *features_arm.get(),
-                                                      compiler_options);
-}
-
 CodeGenerator* create_codegen_arm_vixl32(HGraph* graph, const CompilerOptions& compiler_options) {
   std::unique_ptr<const ArmInstructionSetFeatures> features_arm(
       ArmInstructionSetFeatures::FromCppDefines());
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 142c957..18390cc 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -146,7 +146,10 @@
   //   that this method is actually inlined;
   // - if a method's name contains the substring "$noinline$", do not
   //   inline that method.
-  const bool honor_inlining_directives = IsCompilingWithCoreImage();
+  // We limit this to AOT compilation, as the JIT may or may not inline
+  // depending on the state of classes at runtime.
+  const bool honor_inlining_directives =
+      IsCompilingWithCoreImage() && Runtime::Current()->IsAotCompiler();
 
   // Keep a copy of all blocks when starting the visit.
   ArenaVector<HBasicBlock*> blocks = graph_->GetReversePostOrder();
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index a73b124..839f328 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -22,6 +22,7 @@
 #include "dex_instruction-inl.h"
 #include "driver/compiler_options.h"
 #include "imtable-inl.h"
+#include "quicken_info.h"
 #include "sharpening.h"
 #include "scoped_thread_state_change-inl.h"
 
@@ -312,6 +313,11 @@
 
     DCHECK(!IsBlockPopulated(current_block_));
 
+    uint32_t quicken_index = 0;
+    if (CanDecodeQuickenedInfo()) {
+      quicken_index = block_builder_->GetQuickenIndex(block_dex_pc);
+    }
+
     for (CodeItemIterator it(code_item_, block_dex_pc); !it.Done(); it.Advance()) {
       if (current_block_ == nullptr) {
         // The previous instruction ended this block.
@@ -332,9 +338,13 @@
         AppendInstruction(new (arena_) HNativeDebugInfo(dex_pc));
       }
 
-      if (!ProcessDexInstruction(it.CurrentInstruction(), dex_pc)) {
+      if (!ProcessDexInstruction(it.CurrentInstruction(), dex_pc, quicken_index)) {
         return false;
       }
+
+      if (QuickenInfoTable::NeedsIndexForInstruction(&it.CurrentInstruction())) {
+        ++quicken_index;
+      }
     }
 
     if (current_block_ != nullptr) {
@@ -1261,7 +1271,8 @@
 
 bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instruction,
                                                    uint32_t dex_pc,
-                                                   bool is_put) {
+                                                   bool is_put,
+                                                   size_t quicken_index) {
   uint32_t source_or_dest_reg = instruction.VRegA_22c();
   uint32_t obj_reg = instruction.VRegB_22c();
   uint16_t field_index;
@@ -1269,7 +1280,7 @@
     if (!CanDecodeQuickenedInfo()) {
       return false;
     }
-    field_index = LookupQuickenedInfo(dex_pc);
+    field_index = LookupQuickenedInfo(quicken_index);
   } else {
     field_index = instruction.VRegC_22c();
   }
@@ -1805,40 +1816,17 @@
 }
 
 bool HInstructionBuilder::CanDecodeQuickenedInfo() const {
-  return interpreter_metadata_ != nullptr;
+  return !quicken_info_.IsNull();
 }
 
-uint16_t HInstructionBuilder::LookupQuickenedInfo(uint32_t dex_pc) {
-  DCHECK(interpreter_metadata_ != nullptr);
-
-  // First check if the info has already been decoded from `interpreter_metadata_`.
-  auto it = skipped_interpreter_metadata_.find(dex_pc);
-  if (it != skipped_interpreter_metadata_.end()) {
-    // Remove the entry from the map and return the parsed info.
-    uint16_t value_in_map = it->second;
-    skipped_interpreter_metadata_.erase(it);
-    return value_in_map;
-  }
-
-  // Otherwise start parsing `interpreter_metadata_` until the slot for `dex_pc`
-  // is found. Store skipped values in the `skipped_interpreter_metadata_` map.
-  while (true) {
-    uint32_t dex_pc_in_map = DecodeUnsignedLeb128(&interpreter_metadata_);
-    uint16_t value_in_map = DecodeUnsignedLeb128(&interpreter_metadata_);
-    DCHECK_LE(dex_pc_in_map, dex_pc);
-
-    if (dex_pc_in_map == dex_pc) {
-      return value_in_map;
-    } else {
-      // Overwrite and not Put, as quickened CHECK-CAST has two entries with
-      // the same dex_pc. This is OK, because the compiler does not care about those
-      // entries.
-      skipped_interpreter_metadata_.Overwrite(dex_pc_in_map, value_in_map);
-    }
-  }
+uint16_t HInstructionBuilder::LookupQuickenedInfo(uint32_t quicken_index) {
+  DCHECK(CanDecodeQuickenedInfo());
+  return quicken_info_.GetData(quicken_index);
 }
 
-bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, uint32_t dex_pc) {
+bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction,
+                                                uint32_t dex_pc,
+                                                size_t quicken_index) {
   switch (instruction.Opcode()) {
     case Instruction::CONST_4: {
       int32_t register_index = instruction.VRegA();
@@ -1995,7 +1983,7 @@
         if (!CanDecodeQuickenedInfo()) {
           return false;
         }
-        method_idx = LookupQuickenedInfo(dex_pc);
+        method_idx = LookupQuickenedInfo(quicken_index);
       } else {
         method_idx = instruction.VRegB_35c();
       }
@@ -2020,7 +2008,7 @@
         if (!CanDecodeQuickenedInfo()) {
           return false;
         }
-        method_idx = LookupQuickenedInfo(dex_pc);
+        method_idx = LookupQuickenedInfo(quicken_index);
       } else {
         method_idx = instruction.VRegB_3rc();
       }
@@ -2693,7 +2681,7 @@
     case Instruction::IGET_CHAR_QUICK:
     case Instruction::IGET_SHORT:
     case Instruction::IGET_SHORT_QUICK: {
-      if (!BuildInstanceFieldAccess(instruction, dex_pc, false)) {
+      if (!BuildInstanceFieldAccess(instruction, dex_pc, false, quicken_index)) {
         return false;
       }
       break;
@@ -2713,7 +2701,7 @@
     case Instruction::IPUT_CHAR_QUICK:
     case Instruction::IPUT_SHORT:
     case Instruction::IPUT_SHORT_QUICK: {
-      if (!BuildInstanceFieldAccess(instruction, dex_pc, true)) {
+      if (!BuildInstanceFieldAccess(instruction, dex_pc, true, quicken_index)) {
         return false;
       }
       break;
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index e968760..5a83df3 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -27,6 +27,7 @@
 #include "mirror/dex_cache.h"
 #include "nodes.h"
 #include "optimizing_compiler_stats.h"
+#include "quicken_info.h"
 #include "ssa_builder.h"
 
 namespace art {
@@ -67,9 +68,7 @@
         code_generator_(code_generator),
         dex_compilation_unit_(dex_compilation_unit),
         outer_compilation_unit_(outer_compilation_unit),
-        interpreter_metadata_(interpreter_metadata),
-        skipped_interpreter_metadata_(std::less<uint32_t>(),
-                                      arena_->Adapter(kArenaAllocGraphBuilder)),
+        quicken_info_(interpreter_metadata),
         compilation_stats_(compiler_stats),
         dex_cache_(dex_cache),
         loop_headers_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)) {
@@ -85,11 +84,11 @@
   void PropagateLocalsToCatchBlocks();
   void SetLoopHeaderPhiInputs();
 
-  bool ProcessDexInstruction(const Instruction& instruction, uint32_t dex_pc);
+  bool ProcessDexInstruction(const Instruction& instruction, uint32_t dex_pc, size_t quicken_index);
   void FindNativeDebugInfoLocations(ArenaBitVector* locations);
 
   bool CanDecodeQuickenedInfo() const;
-  uint16_t LookupQuickenedInfo(uint32_t dex_pc);
+  uint16_t LookupQuickenedInfo(uint32_t quicken_index);
 
   HBasicBlock* FindBlockStartingAt(uint32_t dex_pc) const;
 
@@ -159,7 +158,10 @@
   void BuildReturn(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
 
   // Builds an instance field access node and returns whether the instruction is supported.
-  bool BuildInstanceFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put);
+  bool BuildInstanceFieldAccess(const Instruction& instruction,
+                                uint32_t dex_pc,
+                                bool is_put,
+                                size_t quicken_index);
 
   void BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
                                         uint32_t dex_pc,
@@ -349,14 +351,8 @@
   // methods.
   const DexCompilationUnit* const outer_compilation_unit_;
 
-  // Original values kept after instruction quickening. This is a data buffer
-  // of Leb128-encoded (dex_pc, value) pairs sorted by dex_pc.
-  const uint8_t* interpreter_metadata_;
-
-  // InstructionBuilder does not parse instructions in dex_pc order. Quickening
-  // info for out-of-order dex_pcs is stored in a map until the positions
-  // are eventually visited.
-  ArenaSafeMap<uint32_t, uint16_t> skipped_interpreter_metadata_;
+  // Original values kept after instruction quickening.
+  QuickenInfoTable quicken_info_;
 
   OptimizingCompilerStats* compilation_stats_;
   Handle<mirror::DexCache> dex_cache_;
diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc
index 3fc7c50..fe22595 100644
--- a/compiler/optimizing/instruction_simplifier_arm.cc
+++ b/compiler/optimizing/instruction_simplifier_arm.cc
@@ -147,8 +147,8 @@
   Primitive::Type type = instruction->GetType();
 
   // TODO: Implement reading (length + compression) for String compression feature from
-  // negative offset (count_offset - data_offset). Thumb2Assembler does not support T4
-  // encoding of "LDR (immediate)" at the moment.
+  // negative offset (count_offset - data_offset). Thumb2Assembler (now removed) did
+  // not support T4 encoding of "LDR (immediate)", but ArmVIXLMacroAssembler might.
   // Don't move array pointer if it is charAt because we need to take the count first.
   if (mirror::kUseStringCompression && instruction->IsStringCharAt()) {
     return;
diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc
index e5a8499..d1bc4da 100644
--- a/compiler/optimizing/instruction_simplifier_shared.cc
+++ b/compiler/optimizing/instruction_simplifier_shared.cc
@@ -274,8 +274,8 @@
   // `HArm64Load` and `HArm64Store`,`HArmLoad` and `HArmStore`). We defer these changes
   // because these new instructions would not bring any advantages yet.
   // Also see the comments in
-  // `InstructionCodeGeneratorARM::VisitArrayGet()`
-  // `InstructionCodeGeneratorARM::VisitArraySet()`
+  // `InstructionCodeGeneratorARMVIXL::VisitArrayGet()`
+  // `InstructionCodeGeneratorARMVIXL::VisitArraySet()`
   // `InstructionCodeGeneratorARM64::VisitArrayGet()`
   // `InstructionCodeGeneratorARM64::VisitArraySet()`.
   return true;
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
deleted file mode 100644
index ae5f8d1..0000000
--- a/compiler/optimizing/intrinsics_arm.cc
+++ /dev/null
@@ -1,2760 +0,0 @@
-/*
- * Copyright (C) 2015 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 "intrinsics_arm.h"
-
-#include "arch/arm/instruction_set_features_arm.h"
-#include "art_method.h"
-#include "code_generator_arm.h"
-#include "entrypoints/quick/quick_entrypoints.h"
-#include "intrinsics.h"
-#include "intrinsics_utils.h"
-#include "lock_word.h"
-#include "mirror/array-inl.h"
-#include "mirror/object_array-inl.h"
-#include "mirror/reference.h"
-#include "mirror/string.h"
-#include "scoped_thread_state_change-inl.h"
-#include "thread-current-inl.h"
-#include "utils/arm/assembler_arm.h"
-
-namespace art {
-
-namespace arm {
-
-ArmAssembler* IntrinsicCodeGeneratorARM::GetAssembler() {
-  return codegen_->GetAssembler();
-}
-
-ArenaAllocator* IntrinsicCodeGeneratorARM::GetAllocator() {
-  return codegen_->GetGraph()->GetArena();
-}
-
-using IntrinsicSlowPathARM = IntrinsicSlowPath<InvokeDexCallingConventionVisitorARM>;
-
-#define __ assembler->
-
-// Compute base address for the System.arraycopy intrinsic in `base`.
-static void GenSystemArrayCopyBaseAddress(ArmAssembler* assembler,
-                                          Primitive::Type type,
-                                          const Register& array,
-                                          const Location& pos,
-                                          const Register& base) {
-  // This routine is only used by the SystemArrayCopy intrinsic at the
-  // moment. We can allow Primitive::kPrimNot as `type` to implement
-  // the SystemArrayCopyChar intrinsic.
-  DCHECK_EQ(type, Primitive::kPrimNot);
-  const int32_t element_size = Primitive::ComponentSize(type);
-  const uint32_t element_size_shift = Primitive::ComponentSizeShift(type);
-  const uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
-
-  if (pos.IsConstant()) {
-    int32_t constant = pos.GetConstant()->AsIntConstant()->GetValue();
-    __ AddConstant(base, array, element_size * constant + data_offset);
-  } else {
-    __ add(base, array, ShifterOperand(pos.AsRegister<Register>(), LSL, element_size_shift));
-    __ AddConstant(base, data_offset);
-  }
-}
-
-// Compute end address for the System.arraycopy intrinsic in `end`.
-static void GenSystemArrayCopyEndAddress(ArmAssembler* assembler,
-                                         Primitive::Type type,
-                                         const Location& copy_length,
-                                         const Register& base,
-                                         const Register& end) {
-  // This routine is only used by the SystemArrayCopy intrinsic at the
-  // moment. We can allow Primitive::kPrimNot as `type` to implement
-  // the SystemArrayCopyChar intrinsic.
-  DCHECK_EQ(type, Primitive::kPrimNot);
-  const int32_t element_size = Primitive::ComponentSize(type);
-  const uint32_t element_size_shift = Primitive::ComponentSizeShift(type);
-
-  if (copy_length.IsConstant()) {
-    int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue();
-    __ AddConstant(end, base, element_size * constant);
-  } else {
-    __ add(end, base, ShifterOperand(copy_length.AsRegister<Register>(), LSL, element_size_shift));
-  }
-}
-
-#undef __
-
-// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
-#define __ down_cast<ArmAssembler*>(codegen->GetAssembler())->  // NOLINT
-
-// Slow path implementing the SystemArrayCopy intrinsic copy loop with read barriers.
-class ReadBarrierSystemArrayCopySlowPathARM : public SlowPathCode {
- public:
-  explicit ReadBarrierSystemArrayCopySlowPathARM(HInstruction* instruction)
-      : SlowPathCode(instruction) {
-    DCHECK(kEmitCompilerReadBarrier);
-    DCHECK(kUseBakerReadBarrier);
-  }
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    ArmAssembler* assembler = arm_codegen->GetAssembler();
-    LocationSummary* locations = instruction_->GetLocations();
-    DCHECK(locations->CanCall());
-    DCHECK(instruction_->IsInvokeStaticOrDirect())
-        << "Unexpected instruction in read barrier arraycopy slow path: "
-        << instruction_->DebugName();
-    DCHECK(instruction_->GetLocations()->Intrinsified());
-    DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kSystemArrayCopy);
-
-    Primitive::Type type = Primitive::kPrimNot;
-    const int32_t element_size = Primitive::ComponentSize(type);
-
-    Register dest = locations->InAt(2).AsRegister<Register>();
-    Location dest_pos = locations->InAt(3);
-    Register src_curr_addr = locations->GetTemp(0).AsRegister<Register>();
-    Register dst_curr_addr = locations->GetTemp(1).AsRegister<Register>();
-    Register src_stop_addr = locations->GetTemp(2).AsRegister<Register>();
-    Register tmp = locations->GetTemp(3).AsRegister<Register>();
-
-    __ Bind(GetEntryLabel());
-    // Compute the base destination address in `dst_curr_addr`.
-    GenSystemArrayCopyBaseAddress(assembler, type, dest, dest_pos, dst_curr_addr);
-
-    Label loop;
-    __ Bind(&loop);
-    __ ldr(tmp, Address(src_curr_addr, element_size, Address::PostIndex));
-    __ MaybeUnpoisonHeapReference(tmp);
-    // TODO: Inline the mark bit check before calling the runtime?
-    // tmp = ReadBarrier::Mark(tmp);
-    // No need to save live registers; it's taken care of by the
-    // entrypoint. Also, there is no need to update the stack mask,
-    // as this runtime call will not trigger a garbage collection.
-    // (See ReadBarrierMarkSlowPathARM::EmitNativeCode for more
-    // explanations.)
-    DCHECK_NE(tmp, SP);
-    DCHECK_NE(tmp, LR);
-    DCHECK_NE(tmp, PC);
-    // IP is used internally by the ReadBarrierMarkRegX entry point
-    // as a temporary (and not preserved).  It thus cannot be used by
-    // any live register in this slow path.
-    DCHECK_NE(src_curr_addr, IP);
-    DCHECK_NE(dst_curr_addr, IP);
-    DCHECK_NE(src_stop_addr, IP);
-    DCHECK_NE(tmp, IP);
-    DCHECK(0 <= tmp && tmp < kNumberOfCoreRegisters) << tmp;
-    // TODO: Load the entrypoint once before the loop, instead of
-    // loading it at every iteration.
-    int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(tmp);
-    // This runtime call does not require a stack map.
-    arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
-    __ MaybePoisonHeapReference(tmp);
-    __ str(tmp, Address(dst_curr_addr, element_size, Address::PostIndex));
-    __ cmp(src_curr_addr, ShifterOperand(src_stop_addr));
-    __ b(&loop, NE);
-    __ b(GetExitLabel());
-  }
-
-  const char* GetDescription() const OVERRIDE { return "ReadBarrierSystemArrayCopySlowPathARM"; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathARM);
-};
-
-#undef __
-
-IntrinsicLocationsBuilderARM::IntrinsicLocationsBuilderARM(CodeGeneratorARM* codegen)
-    : arena_(codegen->GetGraph()->GetArena()),
-      codegen_(codegen),
-      assembler_(codegen->GetAssembler()),
-      features_(codegen->GetInstructionSetFeatures()) {}
-
-bool IntrinsicLocationsBuilderARM::TryDispatch(HInvoke* invoke) {
-  Dispatch(invoke);
-  LocationSummary* res = invoke->GetLocations();
-  if (res == nullptr) {
-    return false;
-  }
-  return res->Intrinsified();
-}
-
-#define __ assembler->
-
-static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
-  LocationSummary* locations = new (arena) LocationSummary(invoke,
-                                                           LocationSummary::kNoCall,
-                                                           kIntrinsified);
-  locations->SetInAt(0, Location::RequiresFpuRegister());
-  locations->SetOut(Location::RequiresRegister());
-}
-
-static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
-  LocationSummary* locations = new (arena) LocationSummary(invoke,
-                                                           LocationSummary::kNoCall,
-                                                           kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresFpuRegister());
-}
-
-static void MoveFPToInt(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
-  Location input = locations->InAt(0);
-  Location output = locations->Out();
-  if (is64bit) {
-    __ vmovrrd(output.AsRegisterPairLow<Register>(),
-               output.AsRegisterPairHigh<Register>(),
-               FromLowSToD(input.AsFpuRegisterPairLow<SRegister>()));
-  } else {
-    __ vmovrs(output.AsRegister<Register>(), input.AsFpuRegister<SRegister>());
-  }
-}
-
-static void MoveIntToFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
-  Location input = locations->InAt(0);
-  Location output = locations->Out();
-  if (is64bit) {
-    __ vmovdrr(FromLowSToD(output.AsFpuRegisterPairLow<SRegister>()),
-               input.AsRegisterPairLow<Register>(),
-               input.AsRegisterPairHigh<Register>());
-  } else {
-    __ vmovsr(output.AsFpuRegister<SRegister>(), input.AsRegister<Register>());
-  }
-}
-
-void IntrinsicLocationsBuilderARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
-  CreateFPToIntLocations(arena_, invoke);
-}
-void IntrinsicLocationsBuilderARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
-  CreateIntToFPLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
-  MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
-}
-void IntrinsicCodeGeneratorARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
-  MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
-}
-
-void IntrinsicLocationsBuilderARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
-  CreateFPToIntLocations(arena_, invoke);
-}
-void IntrinsicLocationsBuilderARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
-  CreateIntToFPLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
-  MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
-}
-void IntrinsicCodeGeneratorARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
-  MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
-}
-
-static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
-  LocationSummary* locations = new (arena) LocationSummary(invoke,
-                                                           LocationSummary::kNoCall,
-                                                           kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
-  LocationSummary* locations = new (arena) LocationSummary(invoke,
-                                                           LocationSummary::kNoCall,
-                                                           kIntrinsified);
-  locations->SetInAt(0, Location::RequiresFpuRegister());
-  locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
-}
-
-static void GenNumberOfLeadingZeros(HInvoke* invoke,
-                                    Primitive::Type type,
-                                    CodeGeneratorARM* codegen) {
-  ArmAssembler* assembler = codegen->GetAssembler();
-  LocationSummary* locations = invoke->GetLocations();
-  Location in = locations->InAt(0);
-  Register out = locations->Out().AsRegister<Register>();
-
-  DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
-
-  if (type == Primitive::kPrimLong) {
-    Register in_reg_lo = in.AsRegisterPairLow<Register>();
-    Register in_reg_hi = in.AsRegisterPairHigh<Register>();
-    Label end;
-    Label* final_label = codegen->GetFinalLabel(invoke, &end);
-    __ clz(out, in_reg_hi);
-    __ CompareAndBranchIfNonZero(in_reg_hi, final_label);
-    __ clz(out, in_reg_lo);
-    __ AddConstant(out, 32);
-    if (end.IsLinked()) {
-      __ Bind(&end);
-    }
-  } else {
-    __ clz(out, in.AsRegister<Register>());
-  }
-}
-
-void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
-  CreateIntToIntLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
-  GenNumberOfLeadingZeros(invoke, Primitive::kPrimInt, codegen_);
-}
-
-void IntrinsicLocationsBuilderARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                           LocationSummary::kNoCall,
-                                                           kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-}
-
-void IntrinsicCodeGeneratorARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
-  GenNumberOfLeadingZeros(invoke, Primitive::kPrimLong, codegen_);
-}
-
-static void GenNumberOfTrailingZeros(HInvoke* invoke,
-                                     Primitive::Type type,
-                                     CodeGeneratorARM* codegen) {
-  DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
-
-  ArmAssembler* assembler = codegen->GetAssembler();
-  LocationSummary* locations = invoke->GetLocations();
-  Register out = locations->Out().AsRegister<Register>();
-
-  if (type == Primitive::kPrimLong) {
-    Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
-    Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
-    Label end;
-    Label* final_label = codegen->GetFinalLabel(invoke, &end);
-    __ rbit(out, in_reg_lo);
-    __ clz(out, out);
-    __ CompareAndBranchIfNonZero(in_reg_lo, final_label);
-    __ rbit(out, in_reg_hi);
-    __ clz(out, out);
-    __ AddConstant(out, 32);
-    if (end.IsLinked()) {
-      __ Bind(&end);
-    }
-  } else {
-    Register in = locations->InAt(0).AsRegister<Register>();
-    __ rbit(out, in);
-    __ clz(out, out);
-  }
-}
-
-void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kNoCall,
-                                                            kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
-  GenNumberOfTrailingZeros(invoke, Primitive::kPrimInt, codegen_);
-}
-
-void IntrinsicLocationsBuilderARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kNoCall,
-                                                            kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-}
-
-void IntrinsicCodeGeneratorARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
-  GenNumberOfTrailingZeros(invoke, Primitive::kPrimLong, codegen_);
-}
-
-static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
-  Location in = locations->InAt(0);
-  Location out = locations->Out();
-
-  if (is64bit) {
-    __ vabsd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
-             FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
-  } else {
-    __ vabss(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>());
-  }
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathAbsDouble(HInvoke* invoke) {
-  CreateFPToFPLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathAbsDouble(HInvoke* invoke) {
-  MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathAbsFloat(HInvoke* invoke) {
-  CreateFPToFPLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathAbsFloat(HInvoke* invoke) {
-  MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
-}
-
-static void CreateIntToIntPlusTemp(ArenaAllocator* arena, HInvoke* invoke) {
-  LocationSummary* locations = new (arena) LocationSummary(invoke,
-                                                           LocationSummary::kNoCall,
-                                                           kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-
-  locations->AddTemp(Location::RequiresRegister());
-}
-
-static void GenAbsInteger(LocationSummary* locations,
-                          bool is64bit,
-                          ArmAssembler* assembler) {
-  Location in = locations->InAt(0);
-  Location output = locations->Out();
-
-  Register mask = locations->GetTemp(0).AsRegister<Register>();
-
-  if (is64bit) {
-    Register in_reg_lo = in.AsRegisterPairLow<Register>();
-    Register in_reg_hi = in.AsRegisterPairHigh<Register>();
-    Register out_reg_lo = output.AsRegisterPairLow<Register>();
-    Register out_reg_hi = output.AsRegisterPairHigh<Register>();
-
-    DCHECK_NE(out_reg_lo, in_reg_hi) << "Diagonal overlap unexpected.";
-
-    __ Asr(mask, in_reg_hi, 31);
-    __ adds(out_reg_lo, in_reg_lo, ShifterOperand(mask));
-    __ adc(out_reg_hi, in_reg_hi, ShifterOperand(mask));
-    __ eor(out_reg_lo, mask, ShifterOperand(out_reg_lo));
-    __ eor(out_reg_hi, mask, ShifterOperand(out_reg_hi));
-  } else {
-    Register in_reg = in.AsRegister<Register>();
-    Register out_reg = output.AsRegister<Register>();
-
-    __ Asr(mask, in_reg, 31);
-    __ add(out_reg, in_reg, ShifterOperand(mask));
-    __ eor(out_reg, mask, ShifterOperand(out_reg));
-  }
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathAbsInt(HInvoke* invoke) {
-  CreateIntToIntPlusTemp(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathAbsInt(HInvoke* invoke) {
-  GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
-}
-
-
-void IntrinsicLocationsBuilderARM::VisitMathAbsLong(HInvoke* invoke) {
-  CreateIntToIntPlusTemp(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathAbsLong(HInvoke* invoke) {
-  GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
-}
-
-static void GenMinMax(LocationSummary* locations,
-                      bool is_min,
-                      ArmAssembler* assembler) {
-  Register op1 = locations->InAt(0).AsRegister<Register>();
-  Register op2 = locations->InAt(1).AsRegister<Register>();
-  Register out = locations->Out().AsRegister<Register>();
-
-  __ cmp(op1, ShifterOperand(op2));
-
-  __ it((is_min) ? Condition::LT : Condition::GT, kItElse);
-  __ mov(out, ShifterOperand(op1), is_min ? Condition::LT : Condition::GT);
-  __ mov(out, ShifterOperand(op2), is_min ? Condition::GE : Condition::LE);
-}
-
-static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
-  LocationSummary* locations = new (arena) LocationSummary(invoke,
-                                                           LocationSummary::kNoCall,
-                                                           kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathMinIntInt(HInvoke* invoke) {
-  CreateIntIntToIntLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathMinIntInt(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ true, GetAssembler());
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathMaxIntInt(HInvoke* invoke) {
-  CreateIntIntToIntLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathMaxIntInt(HInvoke* invoke) {
-  GenMinMax(invoke->GetLocations(), /* is_min */ false, GetAssembler());
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathSqrt(HInvoke* invoke) {
-  CreateFPToFPLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathSqrt(HInvoke* invoke) {
-  LocationSummary* locations = invoke->GetLocations();
-  ArmAssembler* assembler = GetAssembler();
-  __ vsqrtd(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
-            FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
-}
-
-void IntrinsicLocationsBuilderARM::VisitMemoryPeekByte(HInvoke* invoke) {
-  CreateIntToIntLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMemoryPeekByte(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  // Ignore upper 4B of long address.
-  __ ldrsb(invoke->GetLocations()->Out().AsRegister<Register>(),
-           Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
-}
-
-void IntrinsicLocationsBuilderARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
-  CreateIntToIntLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  // Ignore upper 4B of long address.
-  __ ldr(invoke->GetLocations()->Out().AsRegister<Register>(),
-         Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
-}
-
-void IntrinsicLocationsBuilderARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
-  CreateIntToIntLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  // Ignore upper 4B of long address.
-  Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
-  // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
-  // exception. So we can't use ldrd as addr may be unaligned.
-  Register lo = invoke->GetLocations()->Out().AsRegisterPairLow<Register>();
-  Register hi = invoke->GetLocations()->Out().AsRegisterPairHigh<Register>();
-  if (addr == lo) {
-    __ ldr(hi, Address(addr, 4));
-    __ ldr(lo, Address(addr, 0));
-  } else {
-    __ ldr(lo, Address(addr, 0));
-    __ ldr(hi, Address(addr, 4));
-  }
-}
-
-void IntrinsicLocationsBuilderARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
-  CreateIntToIntLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  // Ignore upper 4B of long address.
-  __ ldrsh(invoke->GetLocations()->Out().AsRegister<Register>(),
-           Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
-}
-
-static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
-  LocationSummary* locations = new (arena) LocationSummary(invoke,
-                                                           LocationSummary::kNoCall,
-                                                           kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-}
-
-void IntrinsicLocationsBuilderARM::VisitMemoryPokeByte(HInvoke* invoke) {
-  CreateIntIntToVoidLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMemoryPokeByte(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  __ strb(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
-          Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
-}
-
-void IntrinsicLocationsBuilderARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
-  CreateIntIntToVoidLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  __ str(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
-         Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
-}
-
-void IntrinsicLocationsBuilderARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
-  CreateIntIntToVoidLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  // Ignore upper 4B of long address.
-  Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
-  // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
-  // exception. So we can't use ldrd as addr may be unaligned.
-  __ str(invoke->GetLocations()->InAt(1).AsRegisterPairLow<Register>(), Address(addr, 0));
-  __ str(invoke->GetLocations()->InAt(1).AsRegisterPairHigh<Register>(), Address(addr, 4));
-}
-
-void IntrinsicLocationsBuilderARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
-  CreateIntIntToVoidLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  __ strh(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
-          Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
-}
-
-void IntrinsicLocationsBuilderARM::VisitThreadCurrentThread(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kNoCall,
-                                                            kIntrinsified);
-  locations->SetOut(Location::RequiresRegister());
-}
-
-void IntrinsicCodeGeneratorARM::VisitThreadCurrentThread(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  __ LoadFromOffset(kLoadWord,
-                    invoke->GetLocations()->Out().AsRegister<Register>(),
-                    TR,
-                    Thread::PeerOffset<kArmPointerSize>().Int32Value());
-}
-
-static void GenUnsafeGet(HInvoke* invoke,
-                         Primitive::Type type,
-                         bool is_volatile,
-                         CodeGeneratorARM* codegen) {
-  LocationSummary* locations = invoke->GetLocations();
-  ArmAssembler* assembler = codegen->GetAssembler();
-  Location base_loc = locations->InAt(1);
-  Register base = base_loc.AsRegister<Register>();             // Object pointer.
-  Location offset_loc = locations->InAt(2);
-  Register offset = offset_loc.AsRegisterPairLow<Register>();  // Long offset, lo part only.
-  Location trg_loc = locations->Out();
-
-  switch (type) {
-    case Primitive::kPrimInt: {
-      Register trg = trg_loc.AsRegister<Register>();
-      __ ldr(trg, Address(base, offset));
-      if (is_volatile) {
-        __ dmb(ISH);
-      }
-      break;
-    }
-
-    case Primitive::kPrimNot: {
-      Register trg = trg_loc.AsRegister<Register>();
-      if (kEmitCompilerReadBarrier) {
-        if (kUseBakerReadBarrier) {
-          Location temp = locations->GetTemp(0);
-          codegen->GenerateReferenceLoadWithBakerReadBarrier(
-              invoke, trg_loc, base, 0U, offset_loc, TIMES_1, temp, /* needs_null_check */ false);
-          if (is_volatile) {
-            __ dmb(ISH);
-          }
-        } else {
-          __ ldr(trg, Address(base, offset));
-          if (is_volatile) {
-            __ dmb(ISH);
-          }
-          codegen->GenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
-        }
-      } else {
-        __ ldr(trg, Address(base, offset));
-        if (is_volatile) {
-          __ dmb(ISH);
-        }
-        __ MaybeUnpoisonHeapReference(trg);
-      }
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      Register trg_lo = trg_loc.AsRegisterPairLow<Register>();
-      __ add(IP, base, ShifterOperand(offset));
-      if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
-        Register trg_hi = trg_loc.AsRegisterPairHigh<Register>();
-        __ ldrexd(trg_lo, trg_hi, IP);
-      } else {
-        __ ldrd(trg_lo, Address(IP));
-      }
-      if (is_volatile) {
-        __ dmb(ISH);
-      }
-      break;
-    }
-
-    default:
-      LOG(FATAL) << "Unexpected type " << type;
-      UNREACHABLE();
-  }
-}
-
-static void CreateIntIntIntToIntLocations(ArenaAllocator* arena,
-                                          HInvoke* invoke,
-                                          Primitive::Type type) {
-  bool can_call = kEmitCompilerReadBarrier &&
-      (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
-       invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
-  LocationSummary* locations = new (arena) LocationSummary(invoke,
-                                                           (can_call
-                                                                ? LocationSummary::kCallOnSlowPath
-                                                                : LocationSummary::kNoCall),
-                                                           kIntrinsified);
-  if (can_call && kUseBakerReadBarrier) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
-  }
-  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
-  locations->SetInAt(1, Location::RequiresRegister());
-  locations->SetInAt(2, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(),
-                    (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
-  if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
-    // We need a temporary register for the read barrier marking slow
-    // path in InstructionCodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier.
-    locations->AddTemp(Location::RequiresRegister());
-  }
-}
-
-void IntrinsicLocationsBuilderARM::VisitUnsafeGet(HInvoke* invoke) {
-  CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
-}
-void IntrinsicLocationsBuilderARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
-  CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
-}
-void IntrinsicLocationsBuilderARM::VisitUnsafeGetLong(HInvoke* invoke) {
-  CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
-}
-void IntrinsicLocationsBuilderARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
-  CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
-}
-void IntrinsicLocationsBuilderARM::VisitUnsafeGetObject(HInvoke* invoke) {
-  CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
-}
-void IntrinsicLocationsBuilderARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
-  CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
-}
-
-void IntrinsicCodeGeneratorARM::VisitUnsafeGet(HInvoke* invoke) {
-  GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_);
-}
-void IntrinsicCodeGeneratorARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
-  GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_);
-}
-void IntrinsicCodeGeneratorARM::VisitUnsafeGetLong(HInvoke* invoke) {
-  GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_);
-}
-void IntrinsicCodeGeneratorARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
-  GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_);
-}
-void IntrinsicCodeGeneratorARM::VisitUnsafeGetObject(HInvoke* invoke) {
-  GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_);
-}
-void IntrinsicCodeGeneratorARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
-  GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_);
-}
-
-static void CreateIntIntIntIntToVoid(ArenaAllocator* arena,
-                                     const ArmInstructionSetFeatures& features,
-                                     Primitive::Type type,
-                                     bool is_volatile,
-                                     HInvoke* invoke) {
-  LocationSummary* locations = new (arena) LocationSummary(invoke,
-                                                           LocationSummary::kNoCall,
-                                                           kIntrinsified);
-  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
-  locations->SetInAt(1, Location::RequiresRegister());
-  locations->SetInAt(2, Location::RequiresRegister());
-  locations->SetInAt(3, Location::RequiresRegister());
-
-  if (type == Primitive::kPrimLong) {
-    // Potentially need temps for ldrexd-strexd loop.
-    if (is_volatile && !features.HasAtomicLdrdAndStrd()) {
-      locations->AddTemp(Location::RequiresRegister());  // Temp_lo.
-      locations->AddTemp(Location::RequiresRegister());  // Temp_hi.
-    }
-  } else if (type == Primitive::kPrimNot) {
-    // Temps for card-marking.
-    locations->AddTemp(Location::RequiresRegister());  // Temp.
-    locations->AddTemp(Location::RequiresRegister());  // Card.
-  }
-}
-
-void IntrinsicLocationsBuilderARM::VisitUnsafePut(HInvoke* invoke) {
-  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ false, invoke);
-}
-void IntrinsicLocationsBuilderARM::VisitUnsafePutOrdered(HInvoke* invoke) {
-  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ false, invoke);
-}
-void IntrinsicLocationsBuilderARM::VisitUnsafePutVolatile(HInvoke* invoke) {
-  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, /* is_volatile */ true, invoke);
-}
-void IntrinsicLocationsBuilderARM::VisitUnsafePutObject(HInvoke* invoke) {
-  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ false, invoke);
-}
-void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
-  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ false, invoke);
-}
-void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
-  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, /* is_volatile */ true, invoke);
-}
-void IntrinsicLocationsBuilderARM::VisitUnsafePutLong(HInvoke* invoke) {
-  CreateIntIntIntIntToVoid(
-      arena_, features_, Primitive::kPrimLong, /* is_volatile */ false, invoke);
-}
-void IntrinsicLocationsBuilderARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
-  CreateIntIntIntIntToVoid(
-      arena_, features_, Primitive::kPrimLong, /* is_volatile */ false, invoke);
-}
-void IntrinsicLocationsBuilderARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
-  CreateIntIntIntIntToVoid(
-      arena_, features_, Primitive::kPrimLong, /* is_volatile */ true, invoke);
-}
-
-static void GenUnsafePut(LocationSummary* locations,
-                         Primitive::Type type,
-                         bool is_volatile,
-                         bool is_ordered,
-                         CodeGeneratorARM* codegen) {
-  ArmAssembler* assembler = codegen->GetAssembler();
-
-  Register base = locations->InAt(1).AsRegister<Register>();           // Object pointer.
-  Register offset = locations->InAt(2).AsRegisterPairLow<Register>();  // Long offset, lo part only.
-  Register value;
-
-  if (is_volatile || is_ordered) {
-    __ dmb(ISH);
-  }
-
-  if (type == Primitive::kPrimLong) {
-    Register value_lo = locations->InAt(3).AsRegisterPairLow<Register>();
-    value = value_lo;
-    if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
-      Register temp_lo = locations->GetTemp(0).AsRegister<Register>();
-      Register temp_hi = locations->GetTemp(1).AsRegister<Register>();
-      Register value_hi = locations->InAt(3).AsRegisterPairHigh<Register>();
-
-      __ add(IP, base, ShifterOperand(offset));
-      Label loop_head;
-      __ Bind(&loop_head);
-      __ ldrexd(temp_lo, temp_hi, IP);
-      __ strexd(temp_lo, value_lo, value_hi, IP);
-      __ cmp(temp_lo, ShifterOperand(0));
-      __ b(&loop_head, NE);
-    } else {
-      __ add(IP, base, ShifterOperand(offset));
-      __ strd(value_lo, Address(IP));
-    }
-  } else {
-    value = locations->InAt(3).AsRegister<Register>();
-    Register source = value;
-    if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
-      Register temp = locations->GetTemp(0).AsRegister<Register>();
-      __ Mov(temp, value);
-      __ PoisonHeapReference(temp);
-      source = temp;
-    }
-    __ str(source, Address(base, offset));
-  }
-
-  if (is_volatile) {
-    __ dmb(ISH);
-  }
-
-  if (type == Primitive::kPrimNot) {
-    Register temp = locations->GetTemp(0).AsRegister<Register>();
-    Register card = locations->GetTemp(1).AsRegister<Register>();
-    bool value_can_be_null = true;  // TODO: Worth finding out this information?
-    codegen->MarkGCCard(temp, card, base, value, value_can_be_null);
-  }
-}
-
-void IntrinsicCodeGeneratorARM::VisitUnsafePut(HInvoke* invoke) {
-  GenUnsafePut(invoke->GetLocations(),
-               Primitive::kPrimInt,
-               /* is_volatile */ false,
-               /* is_ordered */ false,
-               codegen_);
-}
-void IntrinsicCodeGeneratorARM::VisitUnsafePutOrdered(HInvoke* invoke) {
-  GenUnsafePut(invoke->GetLocations(),
-               Primitive::kPrimInt,
-               /* is_volatile */ false,
-               /* is_ordered */ true,
-               codegen_);
-}
-void IntrinsicCodeGeneratorARM::VisitUnsafePutVolatile(HInvoke* invoke) {
-  GenUnsafePut(invoke->GetLocations(),
-               Primitive::kPrimInt,
-               /* is_volatile */ true,
-               /* is_ordered */ false,
-               codegen_);
-}
-void IntrinsicCodeGeneratorARM::VisitUnsafePutObject(HInvoke* invoke) {
-  GenUnsafePut(invoke->GetLocations(),
-               Primitive::kPrimNot,
-               /* is_volatile */ false,
-               /* is_ordered */ false,
-               codegen_);
-}
-void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
-  GenUnsafePut(invoke->GetLocations(),
-               Primitive::kPrimNot,
-               /* is_volatile */ false,
-               /* is_ordered */ true,
-               codegen_);
-}
-void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
-  GenUnsafePut(invoke->GetLocations(),
-               Primitive::kPrimNot,
-               /* is_volatile */ true,
-               /* is_ordered */ false,
-               codegen_);
-}
-void IntrinsicCodeGeneratorARM::VisitUnsafePutLong(HInvoke* invoke) {
-  GenUnsafePut(invoke->GetLocations(),
-               Primitive::kPrimLong,
-               /* is_volatile */ false,
-               /* is_ordered */ false,
-               codegen_);
-}
-void IntrinsicCodeGeneratorARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
-  GenUnsafePut(invoke->GetLocations(),
-               Primitive::kPrimLong,
-               /* is_volatile */ false,
-               /* is_ordered */ true,
-               codegen_);
-}
-void IntrinsicCodeGeneratorARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
-  GenUnsafePut(invoke->GetLocations(),
-               Primitive::kPrimLong,
-               /* is_volatile */ true,
-               /* is_ordered */ false,
-               codegen_);
-}
-
-static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* arena,
-                                                HInvoke* invoke,
-                                                Primitive::Type type) {
-  bool can_call = kEmitCompilerReadBarrier &&
-      kUseBakerReadBarrier &&
-      (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
-  LocationSummary* locations = new (arena) LocationSummary(invoke,
-                                                           (can_call
-                                                                ? LocationSummary::kCallOnSlowPath
-                                                                : LocationSummary::kNoCall),
-                                                           kIntrinsified);
-  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
-  locations->SetInAt(1, Location::RequiresRegister());
-  locations->SetInAt(2, Location::RequiresRegister());
-  locations->SetInAt(3, Location::RequiresRegister());
-  locations->SetInAt(4, Location::RequiresRegister());
-
-  // If heap poisoning is enabled, we don't want the unpoisoning
-  // operations to potentially clobber the output. Likewise when
-  // emitting a (Baker) read barrier, which may call.
-  Location::OutputOverlap overlaps =
-      ((kPoisonHeapReferences && type == Primitive::kPrimNot) || can_call)
-      ? Location::kOutputOverlap
-      : Location::kNoOutputOverlap;
-  locations->SetOut(Location::RequiresRegister(), overlaps);
-
-  // Temporary registers used in CAS. In the object case
-  // (UnsafeCASObject intrinsic), these are also used for
-  // card-marking, and possibly for (Baker) read barrier.
-  locations->AddTemp(Location::RequiresRegister());  // Pointer.
-  locations->AddTemp(Location::RequiresRegister());  // Temp 1.
-}
-
-static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARM* codegen) {
-  DCHECK_NE(type, Primitive::kPrimLong);
-
-  ArmAssembler* assembler = codegen->GetAssembler();
-  LocationSummary* locations = invoke->GetLocations();
-
-  Location out_loc = locations->Out();
-  Register out = out_loc.AsRegister<Register>();                  // Boolean result.
-
-  Register base = locations->InAt(1).AsRegister<Register>();      // Object pointer.
-  Location offset_loc = locations->InAt(2);
-  Register offset = offset_loc.AsRegisterPairLow<Register>();     // Offset (discard high 4B).
-  Register expected = locations->InAt(3).AsRegister<Register>();  // Expected.
-  Register value = locations->InAt(4).AsRegister<Register>();     // Value.
-
-  Location tmp_ptr_loc = locations->GetTemp(0);
-  Register tmp_ptr = tmp_ptr_loc.AsRegister<Register>();          // Pointer to actual memory.
-  Register tmp = locations->GetTemp(1).AsRegister<Register>();    // Value in memory.
-
-  if (type == Primitive::kPrimNot) {
-    // The only read barrier implementation supporting the
-    // UnsafeCASObject intrinsic is the Baker-style read barriers.
-    DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
-
-    // Mark card for object assuming new value is stored. Worst case we will mark an unchanged
-    // object and scan the receiver at the next GC for nothing.
-    bool value_can_be_null = true;  // TODO: Worth finding out this information?
-    codegen->MarkGCCard(tmp_ptr, tmp, base, value, value_can_be_null);
-
-    if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
-      // Need to make sure the reference stored in the field is a to-space
-      // one before attempting the CAS or the CAS could fail incorrectly.
-      codegen->UpdateReferenceFieldWithBakerReadBarrier(
-          invoke,
-          out_loc,  // Unused, used only as a "temporary" within the read barrier.
-          base,
-          /* field_offset */ offset_loc,
-          tmp_ptr_loc,
-          /* needs_null_check */ false,
-          tmp);
-    }
-  }
-
-  // Prevent reordering with prior memory operations.
-  // Emit a DMB ISH instruction instead of an DMB ISHST one, as the
-  // latter allows a preceding load to be delayed past the STXR
-  // instruction below.
-  __ dmb(ISH);
-
-  __ add(tmp_ptr, base, ShifterOperand(offset));
-
-  if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
-    __ PoisonHeapReference(expected);
-    if (value == expected) {
-      // Do not poison `value`, as it is the same register as
-      // `expected`, which has just been poisoned.
-    } else {
-      __ PoisonHeapReference(value);
-    }
-  }
-
-  // do {
-  //   tmp = [r_ptr] - expected;
-  // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
-  // result = tmp != 0;
-
-  Label loop_head;
-  __ Bind(&loop_head);
-
-  __ ldrex(tmp, tmp_ptr);
-
-  __ subs(tmp, tmp, ShifterOperand(expected));
-
-  __ it(EQ, ItState::kItT);
-  __ strex(tmp, value, tmp_ptr, EQ);
-  __ cmp(tmp, ShifterOperand(1), EQ);
-
-  __ b(&loop_head, EQ);
-
-  __ dmb(ISH);
-
-  __ rsbs(out, tmp, ShifterOperand(1));
-  __ it(CC);
-  __ mov(out, ShifterOperand(0), CC);
-
-  if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
-    __ UnpoisonHeapReference(expected);
-    if (value == expected) {
-      // Do not unpoison `value`, as it is the same register as
-      // `expected`, which has just been unpoisoned.
-    } else {
-      __ UnpoisonHeapReference(value);
-    }
-  }
-}
-
-void IntrinsicLocationsBuilderARM::VisitUnsafeCASInt(HInvoke* invoke) {
-  CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimInt);
-}
-void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke) {
-  // The only read barrier implementation supporting the
-  // UnsafeCASObject intrinsic is the Baker-style read barriers.
-  if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
-    return;
-  }
-
-  CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimNot);
-}
-void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) {
-  GenCas(invoke, Primitive::kPrimInt, codegen_);
-}
-void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) {
-  // The only read barrier implementation supporting the
-  // UnsafeCASObject intrinsic is the Baker-style read barriers.
-  DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
-
-  GenCas(invoke, Primitive::kPrimNot, codegen_);
-}
-
-void IntrinsicLocationsBuilderARM::VisitStringCompareTo(HInvoke* invoke) {
-  // The inputs plus one temp.
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            invoke->InputAt(1)->CanBeNull()
-                                                                ? LocationSummary::kCallOnSlowPath
-                                                                : LocationSummary::kNoCall,
-                                                            kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  locations->AddTemp(Location::RequiresRegister());
-  locations->AddTemp(Location::RequiresRegister());
-  locations->AddTemp(Location::RequiresRegister());
-  // Need temporary registers for String compression's feature.
-  if (mirror::kUseStringCompression) {
-    locations->AddTemp(Location::RequiresRegister());
-  }
-  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-}
-
-void IntrinsicCodeGeneratorARM::VisitStringCompareTo(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  LocationSummary* locations = invoke->GetLocations();
-
-  Register str = locations->InAt(0).AsRegister<Register>();
-  Register arg = locations->InAt(1).AsRegister<Register>();
-  Register out = locations->Out().AsRegister<Register>();
-
-  Register temp0 = locations->GetTemp(0).AsRegister<Register>();
-  Register temp1 = locations->GetTemp(1).AsRegister<Register>();
-  Register temp2 = locations->GetTemp(2).AsRegister<Register>();
-  Register temp3;
-  if (mirror::kUseStringCompression) {
-    temp3 = locations->GetTemp(3).AsRegister<Register>();
-  }
-
-  Label loop;
-  Label find_char_diff;
-  Label end;
-  Label different_compression;
-
-  // Get offsets of count and value fields within a string object.
-  const int32_t count_offset = mirror::String::CountOffset().Int32Value();
-  const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
-
-  // Note that the null check must have been done earlier.
-  DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
-
-  // Take slow path and throw if input can be and is null.
-  SlowPathCode* slow_path = nullptr;
-  const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
-  if (can_slow_path) {
-    slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
-    codegen_->AddSlowPath(slow_path);
-    __ CompareAndBranchIfZero(arg, slow_path->GetEntryLabel());
-  }
-
-  // Reference equality check, return 0 if same reference.
-  __ subs(out, str, ShifterOperand(arg));
-  __ b(&end, EQ);
-
-  if (mirror::kUseStringCompression) {
-    // Load `count` fields of this and argument strings.
-    __ ldr(temp3, Address(str, count_offset));
-    __ ldr(temp2, Address(arg, count_offset));
-    // Extract lengths from the `count` fields.
-    __ Lsr(temp0, temp3, 1u);
-    __ Lsr(temp1, temp2, 1u);
-  } else {
-    // Load lengths of this and argument strings.
-    __ ldr(temp0, Address(str, count_offset));
-    __ ldr(temp1, Address(arg, count_offset));
-  }
-  // out = length diff.
-  __ subs(out, temp0, ShifterOperand(temp1));
-  // temp0 = min(len(str), len(arg)).
-  __ it(GT);
-  __ mov(temp0, ShifterOperand(temp1), GT);
-  // Shorter string is empty?
-  __ CompareAndBranchIfZero(temp0, &end);
-
-  if (mirror::kUseStringCompression) {
-    // Check if both strings using same compression style to use this comparison loop.
-    __ eor(temp2, temp2, ShifterOperand(temp3));
-    __ Lsrs(temp2, temp2, 1u);
-    __ b(&different_compression, CS);
-    // For string compression, calculate the number of bytes to compare (not chars).
-    // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
-    __ Lsls(temp3, temp3, 31u);  // Extract purely the compression flag.
-    __ it(NE);
-    __ add(temp0, temp0, ShifterOperand(temp0), NE);
-  }
-
-  // Store offset of string value in preparation for comparison loop.
-  __ mov(temp1, ShifterOperand(value_offset));
-
-  // Assertions that must hold in order to compare multiple characters at a time.
-  CHECK_ALIGNED(value_offset, 8);
-  static_assert(IsAligned<8>(kObjectAlignment),
-                "String data must be 8-byte aligned for unrolled CompareTo loop.");
-
-  const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
-  DCHECK_EQ(char_size, 2u);
-
-  Label find_char_diff_2nd_cmp;
-  // Unrolled loop comparing 4x16-bit chars per iteration (ok because of string data alignment).
-  __ Bind(&loop);
-  __ ldr(IP, Address(str, temp1));
-  __ ldr(temp2, Address(arg, temp1));
-  __ cmp(IP, ShifterOperand(temp2));
-  __ b(&find_char_diff, NE);
-  __ add(temp1, temp1, ShifterOperand(char_size * 2));
-
-  __ ldr(IP, Address(str, temp1));
-  __ ldr(temp2, Address(arg, temp1));
-  __ cmp(IP, ShifterOperand(temp2));
-  __ b(&find_char_diff_2nd_cmp, NE);
-  __ add(temp1, temp1, ShifterOperand(char_size * 2));
-  // With string compression, we have compared 8 bytes, otherwise 4 chars.
-  __ subs(temp0, temp0, ShifterOperand(mirror::kUseStringCompression ? 8 : 4));
-  __ b(&loop, HI);
-  __ b(&end);
-
-  __ Bind(&find_char_diff_2nd_cmp);
-  if (mirror::kUseStringCompression) {
-    __ subs(temp0, temp0, ShifterOperand(4));  // 4 bytes previously compared.
-    __ b(&end, LS);  // 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.
-    __ sub(temp0, temp0, ShifterOperand(2));
-  }
-
-  // Find the single character difference.
-  __ Bind(&find_char_diff);
-  // Get the bit position of the first character that differs.
-  __ eor(temp1, temp2, ShifterOperand(IP));
-  __ rbit(temp1, temp1);
-  __ clz(temp1, temp1);
-
-  // temp0 = number of characters remaining to compare.
-  // (Without string compression, it could be < 1 if a difference is found by the second CMP
-  // in the comparison loop, and after the end of the shorter string data).
-
-  // Without string compression (temp1 >> 4) = character where difference occurs between the last
-  // two words compared, in the interval [0,1].
-  // (0 for low half-word different, 1 for high half-word different).
-  // With string compression, (temp1 << 3) = byte where the difference occurs,
-  // in the interval [0,3].
-
-  // If temp0 <= (temp1 >> (kUseStringCompression ? 3 : 4)), the difference occurs outside
-  // the remaining string data, so just return length diff (out).
-  // The comparison is unsigned for string compression, otherwise signed.
-  __ cmp(temp0, ShifterOperand(temp1, LSR, mirror::kUseStringCompression ? 3 : 4));
-  __ b(&end, mirror::kUseStringCompression ? LS : LE);
-
-  // Extract the characters and calculate the difference.
-  if (mirror::kUseStringCompression) {
-    // For compressed strings we need to clear 0x7 from temp1, for uncompressed we need to clear
-    // 0xf. We also need to prepare the character extraction mask `uncompressed ? 0xffffu : 0xffu`.
-    // The compression flag is now in the highest bit of temp3, so let's play some tricks.
-    __ orr(temp3, temp3, ShifterOperand(0xffu << 23));  // uncompressed ? 0xff800000u : 0x7ff80000u
-    __ bic(temp1, temp1, ShifterOperand(temp3, LSR, 31 - 3));  // &= ~(uncompressed ? 0xfu : 0x7u)
-    __ Asr(temp3, temp3, 7u);                           // uncompressed ? 0xffff0000u : 0xff0000u.
-    __ Lsr(temp2, temp2, temp1);                        // Extract second character.
-    __ Lsr(temp3, temp3, 16u);                          // uncompressed ? 0xffffu : 0xffu
-    __ Lsr(out, IP, temp1);                             // Extract first character.
-    __ and_(temp2, temp2, ShifterOperand(temp3));
-    __ and_(out, out, ShifterOperand(temp3));
-  } else {
-    __ bic(temp1, temp1, ShifterOperand(0xf));
-    __ Lsr(temp2, temp2, temp1);
-    __ Lsr(out, IP, temp1);
-    __ movt(temp2, 0);
-    __ movt(out, 0);
-  }
-
-  __ sub(out, out, ShifterOperand(temp2));
-
-  if (mirror::kUseStringCompression) {
-    __ b(&end);
-    __ Bind(&different_compression);
-
-    // Comparison for different compression style.
-    const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
-    DCHECK_EQ(c_char_size, 1u);
-
-    // We want to free up the temp3, currently holding `str.count`, for comparison.
-    // So, we move it to the bottom bit of the iteration count `temp0` which we tnen
-    // need to treat as unsigned. Start by freeing the bit with an ADD and continue
-    // further down by a LSRS+SBC which will flip the meaning of the flag but allow
-    // `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
-    __ add(temp0, temp0, ShifterOperand(temp0));  // Unlike LSL, this ADD is always 16-bit.
-    // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
-    __ mov(temp1, ShifterOperand(str));
-    __ mov(temp2, ShifterOperand(arg));
-    __ Lsrs(temp3, temp3, 1u);                // Continue the move of the compression flag.
-    __ it(CS, kItThen);                       // Interleave with selection of temp1 and temp2.
-    __ mov(temp1, ShifterOperand(arg), CS);   // Preserves flags.
-    __ mov(temp2, ShifterOperand(str), CS);   // Preserves flags.
-    __ sbc(temp0, temp0, ShifterOperand(0));  // Complete the move of the compression flag.
-
-    // Adjust temp1 and temp2 from string pointers to data pointers.
-    __ add(temp1, temp1, ShifterOperand(value_offset));
-    __ add(temp2, temp2, ShifterOperand(value_offset));
-
-    Label different_compression_loop;
-    Label different_compression_diff;
-
-    // Main loop for different compression.
-    __ Bind(&different_compression_loop);
-    __ ldrb(IP, Address(temp1, c_char_size, Address::PostIndex));
-    __ ldrh(temp3, Address(temp2, char_size, Address::PostIndex));
-    __ cmp(IP, ShifterOperand(temp3));
-    __ b(&different_compression_diff, NE);
-    __ subs(temp0, temp0, ShifterOperand(2));
-    __ b(&different_compression_loop, HI);
-    __ b(&end);
-
-    // Calculate the difference.
-    __ Bind(&different_compression_diff);
-    __ sub(out, IP, ShifterOperand(temp3));
-    // Flip the difference if the `arg` is compressed.
-    // `temp0` contains inverted `str` compression flag, i.e the same as `arg` compression flag.
-    __ Lsrs(temp0, temp0, 1u);
-    static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
-                  "Expecting 0=compressed, 1=uncompressed");
-    __ it(CC);
-    __ rsb(out, out, ShifterOperand(0), CC);
-  }
-
-  __ Bind(&end);
-
-  if (can_slow_path) {
-    __ Bind(slow_path->GetExitLabel());
-  }
-}
-
-void IntrinsicLocationsBuilderARM::VisitStringEquals(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kNoCall,
-                                                            kIntrinsified);
-  InvokeRuntimeCallingConvention calling_convention;
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  // Temporary registers to store lengths of strings and for calculations.
-  // Using instruction cbz requires a low register, so explicitly set a temp to be R0.
-  locations->AddTemp(Location::RegisterLocation(R0));
-  locations->AddTemp(Location::RequiresRegister());
-  locations->AddTemp(Location::RequiresRegister());
-
-  locations->SetOut(Location::RequiresRegister());
-}
-
-void IntrinsicCodeGeneratorARM::VisitStringEquals(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  LocationSummary* locations = invoke->GetLocations();
-
-  Register str = locations->InAt(0).AsRegister<Register>();
-  Register arg = locations->InAt(1).AsRegister<Register>();
-  Register out = locations->Out().AsRegister<Register>();
-
-  Register temp = locations->GetTemp(0).AsRegister<Register>();
-  Register temp1 = locations->GetTemp(1).AsRegister<Register>();
-  Register temp2 = locations->GetTemp(2).AsRegister<Register>();
-
-  Label loop;
-  Label end;
-  Label return_true;
-  Label return_false;
-  Label* final_label = codegen_->GetFinalLabel(invoke, &end);
-
-  // Get offsets of count, value, and class fields within a string object.
-  const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
-  const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
-  const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value();
-
-  // Note that the null check must have been done earlier.
-  DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
-
-  StringEqualsOptimizations optimizations(invoke);
-  if (!optimizations.GetArgumentNotNull()) {
-    // Check if input is null, return false if it is.
-    __ CompareAndBranchIfZero(arg, &return_false);
-  }
-
-  // Reference equality check, return true if same reference.
-  __ cmp(str, ShifterOperand(arg));
-  __ b(&return_true, EQ);
-
-  if (!optimizations.GetArgumentIsString()) {
-    // Instanceof check for the argument by comparing class fields.
-    // All string objects must have the same type since String cannot be subclassed.
-    // Receiver must be a string object, so its class field is equal to all strings' class fields.
-    // If the argument is a string object, its class field must be equal to receiver's class field.
-    __ ldr(temp, Address(str, class_offset));
-    __ ldr(temp1, Address(arg, class_offset));
-    __ cmp(temp, ShifterOperand(temp1));
-    __ b(&return_false, NE);
-  }
-
-  // Load `count` fields of this and argument strings.
-  __ ldr(temp, Address(str, count_offset));
-  __ ldr(temp1, Address(arg, count_offset));
-  // Check if `count` fields are equal, return false if they're not.
-  // Also compares the compression style, if differs return false.
-  __ cmp(temp, ShifterOperand(temp1));
-  __ b(&return_false, NE);
-  // Return true if both strings are empty. Even with string compression `count == 0` means empty.
-  static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
-                "Expecting 0=compressed, 1=uncompressed");
-  __ cbz(temp, &return_true);
-
-  // Assertions that must hold in order to compare strings 4 bytes at a time.
-  DCHECK_ALIGNED(value_offset, 4);
-  static_assert(IsAligned<4>(kObjectAlignment), "String data must be aligned for fast compare.");
-
-  if (mirror::kUseStringCompression) {
-    // 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.
-    __ it(CS);                                      // If uncompressed,
-    __ add(temp, temp, ShifterOperand(temp), CS);   //   double the byte count.
-  }
-
-  // Store offset of string value in preparation for comparison loop.
-  __ LoadImmediate(temp1, value_offset);
-
-  // Loop to compare strings 4 bytes at a time starting at the front of the string.
-  // Ok to do this because strings are zero-padded to kObjectAlignment.
-  __ Bind(&loop);
-  __ ldr(out, Address(str, temp1));
-  __ ldr(temp2, Address(arg, temp1));
-  __ add(temp1, temp1, ShifterOperand(sizeof(uint32_t)));
-  __ cmp(out, ShifterOperand(temp2));
-  __ b(&return_false, NE);
-  // With string compression, we have compared 4 bytes, otherwise 2 chars.
-  __ subs(temp, temp, ShifterOperand(mirror::kUseStringCompression ? 4 : 2));
-  __ b(&loop, HI);
-
-  // Return true and exit the function.
-  // If loop does not result in returning false, we return true.
-  __ Bind(&return_true);
-  __ LoadImmediate(out, 1);
-  __ b(final_label);
-
-  // Return false and exit the function.
-  __ Bind(&return_false);
-  __ LoadImmediate(out, 0);
-
-  if (end.IsLinked()) {
-    __ Bind(&end);
-  }
-}
-
-static void GenerateVisitStringIndexOf(HInvoke* invoke,
-                                       ArmAssembler* assembler,
-                                       CodeGeneratorARM* codegen,
-                                       ArenaAllocator* allocator,
-                                       bool start_at_zero) {
-  LocationSummary* locations = invoke->GetLocations();
-
-  // Note that the null check must have been done earlier.
-  DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
-
-  // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
-  // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
-  SlowPathCode* slow_path = nullptr;
-  HInstruction* code_point = invoke->InputAt(1);
-  if (code_point->IsIntConstant()) {
-    if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) >
-        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.
-      slow_path = new (allocator) IntrinsicSlowPathARM(invoke);
-      codegen->AddSlowPath(slow_path);
-      __ b(slow_path->GetEntryLabel());
-      __ Bind(slow_path->GetExitLabel());
-      return;
-    }
-  } else if (code_point->GetType() != Primitive::kPrimChar) {
-    Register char_reg = locations->InAt(1).AsRegister<Register>();
-    // 0xffff is not modified immediate but 0x10000 is, so use `>= 0x10000` instead of `> 0xffff`.
-    __ cmp(char_reg,
-           ShifterOperand(static_cast<uint32_t>(std::numeric_limits<uint16_t>::max()) + 1));
-    slow_path = new (allocator) IntrinsicSlowPathARM(invoke);
-    codegen->AddSlowPath(slow_path);
-    __ b(slow_path->GetEntryLabel(), HS);
-  }
-
-  if (start_at_zero) {
-    Register tmp_reg = locations->GetTemp(0).AsRegister<Register>();
-    DCHECK_EQ(tmp_reg, R2);
-    // Start-index = 0.
-    __ LoadImmediate(tmp_reg, 0);
-  }
-
-  codegen->InvokeRuntime(kQuickIndexOf, invoke, invoke->GetDexPc(), slow_path);
-  CheckEntrypointTypes<kQuickIndexOf, int32_t, void*, uint32_t, uint32_t>();
-
-  if (slow_path != nullptr) {
-    __ Bind(slow_path->GetExitLabel());
-  }
-}
-
-void IntrinsicLocationsBuilderARM::VisitStringIndexOf(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kCallOnMainAndSlowPath,
-                                                            kIntrinsified);
-  // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
-  // best to align the inputs accordingly.
-  InvokeRuntimeCallingConvention calling_convention;
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
-  locations->SetOut(Location::RegisterLocation(R0));
-
-  // Need to send start-index=0.
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
-}
-
-void IntrinsicCodeGeneratorARM::VisitStringIndexOf(HInvoke* invoke) {
-  GenerateVisitStringIndexOf(
-      invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true);
-}
-
-void IntrinsicLocationsBuilderARM::VisitStringIndexOfAfter(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kCallOnMainAndSlowPath,
-                                                            kIntrinsified);
-  // We have a hand-crafted assembly stub that follows the runtime calling convention. So it's
-  // best to align the inputs accordingly.
-  InvokeRuntimeCallingConvention calling_convention;
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
-  locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
-  locations->SetOut(Location::RegisterLocation(R0));
-}
-
-void IntrinsicCodeGeneratorARM::VisitStringIndexOfAfter(HInvoke* invoke) {
-  GenerateVisitStringIndexOf(
-      invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false);
-}
-
-void IntrinsicLocationsBuilderARM::VisitStringNewStringFromBytes(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kCallOnMainAndSlowPath,
-                                                            kIntrinsified);
-  InvokeRuntimeCallingConvention calling_convention;
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
-  locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
-  locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
-  locations->SetOut(Location::RegisterLocation(R0));
-}
-
-void IntrinsicCodeGeneratorARM::VisitStringNewStringFromBytes(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  LocationSummary* locations = invoke->GetLocations();
-
-  Register byte_array = locations->InAt(0).AsRegister<Register>();
-  __ cmp(byte_array, ShifterOperand(0));
-  SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
-  codegen_->AddSlowPath(slow_path);
-  __ b(slow_path->GetEntryLabel(), EQ);
-
-  codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc(), slow_path);
-  CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
-  __ Bind(slow_path->GetExitLabel());
-}
-
-void IntrinsicLocationsBuilderARM::VisitStringNewStringFromChars(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kCallOnMainOnly,
-                                                            kIntrinsified);
-  InvokeRuntimeCallingConvention calling_convention;
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
-  locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
-  locations->SetOut(Location::RegisterLocation(R0));
-}
-
-void IntrinsicCodeGeneratorARM::VisitStringNewStringFromChars(HInvoke* invoke) {
-  // No need to emit code checking whether `locations->InAt(2)` is a null
-  // pointer, as callers of the native method
-  //
-  //   java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
-  //
-  // all include a null check on `data` before calling that method.
-  codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
-  CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
-}
-
-void IntrinsicLocationsBuilderARM::VisitStringNewStringFromString(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kCallOnMainAndSlowPath,
-                                                            kIntrinsified);
-  InvokeRuntimeCallingConvention calling_convention;
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->SetOut(Location::RegisterLocation(R0));
-}
-
-void IntrinsicCodeGeneratorARM::VisitStringNewStringFromString(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  LocationSummary* locations = invoke->GetLocations();
-
-  Register string_to_copy = locations->InAt(0).AsRegister<Register>();
-  __ cmp(string_to_copy, ShifterOperand(0));
-  SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
-  codegen_->AddSlowPath(slow_path);
-  __ b(slow_path->GetEntryLabel(), EQ);
-
-  codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc(), slow_path);
-  CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
-
-  __ Bind(slow_path->GetExitLabel());
-}
-
-void IntrinsicLocationsBuilderARM::VisitSystemArrayCopy(HInvoke* invoke) {
-  // The only read barrier implementation supporting the
-  // SystemArrayCopy intrinsic is the Baker-style read barriers.
-  if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
-    return;
-  }
-
-  CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
-  LocationSummary* locations = invoke->GetLocations();
-  if (locations == nullptr) {
-    return;
-  }
-
-  HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
-  HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
-  HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
-
-  if (src_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(src_pos->GetValue())) {
-    locations->SetInAt(1, Location::RequiresRegister());
-  }
-  if (dest_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(dest_pos->GetValue())) {
-    locations->SetInAt(3, Location::RequiresRegister());
-  }
-  if (length != nullptr && !assembler_->ShifterOperandCanAlwaysHold(length->GetValue())) {
-    locations->SetInAt(4, Location::RequiresRegister());
-  }
-  if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
-    // Temporary register IP cannot be used in
-    // ReadBarrierSystemArrayCopySlowPathARM (because that register
-    // is clobbered by ReadBarrierMarkRegX entry points). Get an extra
-    // temporary register from the register allocator.
-    locations->AddTemp(Location::RequiresRegister());
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen_);
-    arm_codegen->MaybeAddBakerCcEntrypointTempForFields(locations);
-  }
-}
-
-static void CheckPosition(ArmAssembler* assembler,
-                          Location pos,
-                          Register input,
-                          Location length,
-                          SlowPathCode* slow_path,
-                          Register temp,
-                          bool length_is_input_length = false) {
-  // Where is the length in the Array?
-  const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
-
-  if (pos.IsConstant()) {
-    int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
-    if (pos_const == 0) {
-      if (!length_is_input_length) {
-        // Check that length(input) >= length.
-        __ LoadFromOffset(kLoadWord, temp, input, length_offset);
-        if (length.IsConstant()) {
-          __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
-        } else {
-          __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
-        }
-        __ b(slow_path->GetEntryLabel(), LT);
-      }
-    } else {
-      // Check that length(input) >= pos.
-      __ LoadFromOffset(kLoadWord, temp, input, length_offset);
-      __ subs(temp, temp, ShifterOperand(pos_const));
-      __ b(slow_path->GetEntryLabel(), LT);
-
-      // Check that (length(input) - pos) >= length.
-      if (length.IsConstant()) {
-        __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
-      } else {
-        __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
-      }
-      __ b(slow_path->GetEntryLabel(), LT);
-    }
-  } else if (length_is_input_length) {
-    // The only way the copy can succeed is if pos is zero.
-    Register pos_reg = pos.AsRegister<Register>();
-    __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel());
-  } else {
-    // Check that pos >= 0.
-    Register pos_reg = pos.AsRegister<Register>();
-    __ cmp(pos_reg, ShifterOperand(0));
-    __ b(slow_path->GetEntryLabel(), LT);
-
-    // Check that pos <= length(input).
-    __ LoadFromOffset(kLoadWord, temp, input, length_offset);
-    __ subs(temp, temp, ShifterOperand(pos_reg));
-    __ b(slow_path->GetEntryLabel(), LT);
-
-    // Check that (length(input) - pos) >= length.
-    if (length.IsConstant()) {
-      __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue()));
-    } else {
-      __ cmp(temp, ShifterOperand(length.AsRegister<Register>()));
-    }
-    __ b(slow_path->GetEntryLabel(), LT);
-  }
-}
-
-void IntrinsicCodeGeneratorARM::VisitSystemArrayCopy(HInvoke* invoke) {
-  // The only read barrier implementation supporting the
-  // SystemArrayCopy intrinsic is the Baker-style read barriers.
-  DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
-
-  ArmAssembler* assembler = GetAssembler();
-  LocationSummary* locations = invoke->GetLocations();
-
-  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
-  uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
-  uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
-  uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
-  uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
-
-  Register src = locations->InAt(0).AsRegister<Register>();
-  Location src_pos = locations->InAt(1);
-  Register dest = locations->InAt(2).AsRegister<Register>();
-  Location dest_pos = locations->InAt(3);
-  Location length = locations->InAt(4);
-  Location temp1_loc = locations->GetTemp(0);
-  Register temp1 = temp1_loc.AsRegister<Register>();
-  Location temp2_loc = locations->GetTemp(1);
-  Register temp2 = temp2_loc.AsRegister<Register>();
-  Location temp3_loc = locations->GetTemp(2);
-  Register temp3 = temp3_loc.AsRegister<Register>();
-
-  SlowPathCode* intrinsic_slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
-  codegen_->AddSlowPath(intrinsic_slow_path);
-
-  Label conditions_on_positions_validated;
-  SystemArrayCopyOptimizations optimizations(invoke);
-
-  // If source and destination are the same, we go to slow path if we need to do
-  // forward copying.
-  if (src_pos.IsConstant()) {
-    int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
-    if (dest_pos.IsConstant()) {
-      int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
-      if (optimizations.GetDestinationIsSource()) {
-        // Checked when building locations.
-        DCHECK_GE(src_pos_constant, dest_pos_constant);
-      } else if (src_pos_constant < dest_pos_constant) {
-        __ cmp(src, ShifterOperand(dest));
-        __ b(intrinsic_slow_path->GetEntryLabel(), EQ);
-      }
-
-      // Checked when building locations.
-      DCHECK(!optimizations.GetDestinationIsSource()
-             || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue()));
-    } else {
-      if (!optimizations.GetDestinationIsSource()) {
-        __ cmp(src, ShifterOperand(dest));
-        __ b(&conditions_on_positions_validated, NE);
-      }
-      __ cmp(dest_pos.AsRegister<Register>(), ShifterOperand(src_pos_constant));
-      __ b(intrinsic_slow_path->GetEntryLabel(), GT);
-    }
-  } else {
-    if (!optimizations.GetDestinationIsSource()) {
-      __ cmp(src, ShifterOperand(dest));
-      __ b(&conditions_on_positions_validated, NE);
-    }
-    if (dest_pos.IsConstant()) {
-      int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
-      __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos_constant));
-    } else {
-      __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos.AsRegister<Register>()));
-    }
-    __ b(intrinsic_slow_path->GetEntryLabel(), LT);
-  }
-
-  __ Bind(&conditions_on_positions_validated);
-
-  if (!optimizations.GetSourceIsNotNull()) {
-    // Bail out if the source is null.
-    __ CompareAndBranchIfZero(src, intrinsic_slow_path->GetEntryLabel());
-  }
-
-  if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
-    // Bail out if the destination is null.
-    __ CompareAndBranchIfZero(dest, intrinsic_slow_path->GetEntryLabel());
-  }
-
-  // If the length is negative, bail out.
-  // We have already checked in the LocationsBuilder for the constant case.
-  if (!length.IsConstant() &&
-      !optimizations.GetCountIsSourceLength() &&
-      !optimizations.GetCountIsDestinationLength()) {
-    __ cmp(length.AsRegister<Register>(), ShifterOperand(0));
-    __ b(intrinsic_slow_path->GetEntryLabel(), LT);
-  }
-
-  // Validity checks: source.
-  CheckPosition(assembler,
-                src_pos,
-                src,
-                length,
-                intrinsic_slow_path,
-                temp1,
-                optimizations.GetCountIsSourceLength());
-
-  // Validity checks: dest.
-  CheckPosition(assembler,
-                dest_pos,
-                dest,
-                length,
-                intrinsic_slow_path,
-                temp1,
-                optimizations.GetCountIsDestinationLength());
-
-  if (!optimizations.GetDoesNotNeedTypeCheck()) {
-    // Check whether all elements of the source array are assignable to the component
-    // type of the destination array. We do two checks: the classes are the same,
-    // or the destination is Object[]. If none of these checks succeed, we go to the
-    // slow path.
-
-    if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
-      if (!optimizations.GetSourceIsNonPrimitiveArray()) {
-        // /* HeapReference<Class> */ temp1 = src->klass_
-        codegen_->GenerateFieldLoadWithBakerReadBarrier(
-            invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
-        // Bail out if the source is not a non primitive array.
-        // /* HeapReference<Class> */ temp1 = temp1->component_type_
-        codegen_->GenerateFieldLoadWithBakerReadBarrier(
-            invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
-        __ CompareAndBranchIfZero(temp1, intrinsic_slow_path->GetEntryLabel());
-        // If heap poisoning is enabled, `temp1` has been unpoisoned
-        // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
-        // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
-        __ LoadFromOffset(kLoadUnsignedHalfword, temp1, temp1, primitive_offset);
-        static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-        __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
-      }
-
-      // /* HeapReference<Class> */ temp1 = dest->klass_
-      codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          invoke, temp1_loc, dest, class_offset, temp2_loc, /* needs_null_check */ false);
-
-      if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
-        // Bail out if the destination is not a non primitive array.
-        //
-        // Register `temp1` is not trashed by the read barrier emitted
-        // by GenerateFieldLoadWithBakerReadBarrier below, as that
-        // method produces a call to a ReadBarrierMarkRegX entry point,
-        // which saves all potentially live registers, including
-        // temporaries such a `temp1`.
-        // /* HeapReference<Class> */ temp2 = temp1->component_type_
-        codegen_->GenerateFieldLoadWithBakerReadBarrier(
-            invoke, temp2_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false);
-        __ CompareAndBranchIfZero(temp2, intrinsic_slow_path->GetEntryLabel());
-        // If heap poisoning is enabled, `temp2` has been unpoisoned
-        // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
-        // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
-        __ LoadFromOffset(kLoadUnsignedHalfword, temp2, temp2, primitive_offset);
-        static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-        __ CompareAndBranchIfNonZero(temp2, intrinsic_slow_path->GetEntryLabel());
-      }
-
-      // For the same reason given earlier, `temp1` is not trashed by the
-      // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
-      // /* HeapReference<Class> */ temp2 = src->klass_
-      codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          invoke, temp2_loc, src, class_offset, temp3_loc, /* needs_null_check */ false);
-      // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
-      __ cmp(temp1, ShifterOperand(temp2));
-
-      if (optimizations.GetDestinationIsTypedObjectArray()) {
-        Label do_copy;
-        __ b(&do_copy, EQ);
-        // /* HeapReference<Class> */ temp1 = temp1->component_type_
-        codegen_->GenerateFieldLoadWithBakerReadBarrier(
-            invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
-        // /* HeapReference<Class> */ temp1 = temp1->super_class_
-        // We do not need to emit a read barrier for the following
-        // heap reference load, as `temp1` is only used in a
-        // comparison with null below, and this reference is not
-        // kept afterwards.
-        __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
-        __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
-        __ Bind(&do_copy);
-      } else {
-        __ b(intrinsic_slow_path->GetEntryLabel(), NE);
-      }
-    } else {
-      // Non read barrier code.
-
-      // /* HeapReference<Class> */ temp1 = dest->klass_
-      __ LoadFromOffset(kLoadWord, temp1, dest, class_offset);
-      // /* HeapReference<Class> */ temp2 = src->klass_
-      __ LoadFromOffset(kLoadWord, temp2, src, class_offset);
-      bool did_unpoison = false;
-      if (!optimizations.GetDestinationIsNonPrimitiveArray() ||
-          !optimizations.GetSourceIsNonPrimitiveArray()) {
-        // One or two of the references need to be unpoisoned. Unpoison them
-        // both to make the identity check valid.
-        __ MaybeUnpoisonHeapReference(temp1);
-        __ MaybeUnpoisonHeapReference(temp2);
-        did_unpoison = true;
-      }
-
-      if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
-        // Bail out if the destination is not a non primitive array.
-        // /* HeapReference<Class> */ temp3 = temp1->component_type_
-        __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset);
-        __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
-        __ MaybeUnpoisonHeapReference(temp3);
-        // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
-        __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
-        static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-        __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
-      }
-
-      if (!optimizations.GetSourceIsNonPrimitiveArray()) {
-        // Bail out if the source is not a non primitive array.
-        // /* HeapReference<Class> */ temp3 = temp2->component_type_
-        __ LoadFromOffset(kLoadWord, temp3, temp2, component_offset);
-        __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
-        __ MaybeUnpoisonHeapReference(temp3);
-        // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
-        __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
-        static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-        __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
-      }
-
-      __ cmp(temp1, ShifterOperand(temp2));
-
-      if (optimizations.GetDestinationIsTypedObjectArray()) {
-        Label do_copy;
-        __ b(&do_copy, EQ);
-        if (!did_unpoison) {
-          __ MaybeUnpoisonHeapReference(temp1);
-        }
-        // /* HeapReference<Class> */ temp1 = temp1->component_type_
-        __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
-        __ MaybeUnpoisonHeapReference(temp1);
-        // /* HeapReference<Class> */ temp1 = temp1->super_class_
-        __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
-        // No need to unpoison the result, we're comparing against null.
-        __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
-        __ Bind(&do_copy);
-      } else {
-        __ b(intrinsic_slow_path->GetEntryLabel(), NE);
-      }
-    }
-  } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
-    DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
-    // Bail out if the source is not a non primitive array.
-    if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
-      // /* HeapReference<Class> */ temp1 = src->klass_
-      codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
-      // /* HeapReference<Class> */ temp3 = temp1->component_type_
-      codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          invoke, temp3_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
-      __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
-      // If heap poisoning is enabled, `temp3` has been unpoisoned
-      // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
-    } else {
-      // /* HeapReference<Class> */ temp1 = src->klass_
-      __ LoadFromOffset(kLoadWord, temp1, src, class_offset);
-      __ MaybeUnpoisonHeapReference(temp1);
-      // /* HeapReference<Class> */ temp3 = temp1->component_type_
-      __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset);
-      __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
-      __ MaybeUnpoisonHeapReference(temp3);
-    }
-    // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
-    __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset);
-    static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-    __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
-  }
-
-  if (length.IsConstant() && length.GetConstant()->AsIntConstant()->GetValue() == 0) {
-    // Null constant length: not need to emit the loop code at all.
-  } else {
-    Label done;
-    const Primitive::Type type = Primitive::kPrimNot;
-    const int32_t element_size = Primitive::ComponentSize(type);
-
-    if (length.IsRegister()) {
-      // Don't enter the copy loop if the length is null.
-      __ CompareAndBranchIfZero(length.AsRegister<Register>(), &done);
-    }
-
-    if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
-      // TODO: Also convert this intrinsic to the IsGcMarking strategy?
-
-      // SystemArrayCopy implementation for Baker read barriers (see
-      // also CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier):
-      //
-      //   uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
-      //   lfence;  // Load fence or artificial data dependency to prevent load-load reordering
-      //   bool is_gray = (rb_state == ReadBarrier::GrayState());
-      //   if (is_gray) {
-      //     // Slow-path copy.
-      //     do {
-      //       *dest_ptr++ = MaybePoison(ReadBarrier::Mark(MaybeUnpoison(*src_ptr++)));
-      //     } while (src_ptr != end_ptr)
-      //   } else {
-      //     // Fast-path copy.
-      //     do {
-      //       *dest_ptr++ = *src_ptr++;
-      //     } while (src_ptr != end_ptr)
-      //   }
-
-      // /* int32_t */ monitor = src->monitor_
-      __ LoadFromOffset(kLoadWord, temp2, src, monitor_offset);
-      // /* LockWord */ lock_word = LockWord(monitor)
-      static_assert(sizeof(LockWord) == sizeof(int32_t),
-                    "art::LockWord and int32_t have different sizes.");
-
-      // Introduce a dependency on the lock_word including the rb_state,
-      // which shall prevent load-load reordering without using
-      // a memory barrier (which would be more expensive).
-      // `src` is unchanged by this operation, but its value now depends
-      // on `temp2`.
-      __ add(src, src, ShifterOperand(temp2, LSR, 32));
-
-      // Compute the base source address in `temp1`.
-      // Note that `temp1` (the base source address) is computed from
-      // `src` (and `src_pos`) here, and thus honors the artificial
-      // dependency of `src` on `temp2`.
-      GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
-      // Compute the end source address in `temp3`.
-      GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
-      // The base destination address is computed later, as `temp2` is
-      // used for intermediate computations.
-
-      // Slow path used to copy array when `src` is gray.
-      // Note that the base destination address is computed in `temp2`
-      // by the slow path code.
-      SlowPathCode* read_barrier_slow_path =
-          new (GetAllocator()) ReadBarrierSystemArrayCopySlowPathARM(invoke);
-      codegen_->AddSlowPath(read_barrier_slow_path);
-
-      // Given the numeric representation, it's enough to check the low bit of the
-      // rb_state. We do that by shifting the bit out of the lock word with LSRS
-      // which can be a 16-bit instruction unlike the TST immediate.
-      static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
-      static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
-      __ Lsrs(temp2, temp2, LockWord::kReadBarrierStateShift + 1);
-      // Carry flag is the last bit shifted out by LSRS.
-      __ b(read_barrier_slow_path->GetEntryLabel(), CS);
-
-      // Fast-path copy.
-      // Compute the base destination address in `temp2`.
-      GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
-      // Iterate over the arrays and do a raw copy of the objects. We don't need to
-      // poison/unpoison.
-      Label loop;
-      __ Bind(&loop);
-      __ ldr(IP, Address(temp1, element_size, Address::PostIndex));
-      __ str(IP, Address(temp2, element_size, Address::PostIndex));
-      __ cmp(temp1, ShifterOperand(temp3));
-      __ b(&loop, NE);
-
-      __ Bind(read_barrier_slow_path->GetExitLabel());
-    } else {
-      // Non read barrier code.
-      // Compute the base source address in `temp1`.
-      GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
-      // Compute the base destination address in `temp2`.
-      GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
-      // Compute the end source address in `temp3`.
-      GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
-      // Iterate over the arrays and do a raw copy of the objects. We don't need to
-      // poison/unpoison.
-      Label loop;
-      __ Bind(&loop);
-      __ ldr(IP, Address(temp1, element_size, Address::PostIndex));
-      __ str(IP, Address(temp2, element_size, Address::PostIndex));
-      __ cmp(temp1, ShifterOperand(temp3));
-      __ b(&loop, NE);
-    }
-    __ Bind(&done);
-  }
-
-  // We only need one card marking on the destination array.
-  codegen_->MarkGCCard(temp1, temp2, dest, Register(kNoRegister), /* value_can_be_null */ false);
-
-  __ Bind(intrinsic_slow_path->GetExitLabel());
-}
-
-static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
-  // If the graph is debuggable, all callee-saved floating-point registers are blocked by
-  // the code generator. Furthermore, the register allocator creates fixed live intervals
-  // for all caller-saved registers because we are doing a function call. As a result, if
-  // the input and output locations are unallocated, the register allocator runs out of
-  // registers and fails; however, a debuggable graph is not the common case.
-  if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
-    return;
-  }
-
-  DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
-  DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
-  DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
-
-  LocationSummary* const locations = new (arena) LocationSummary(invoke,
-                                                                 LocationSummary::kCallOnMainOnly,
-                                                                 kIntrinsified);
-  const InvokeRuntimeCallingConvention calling_convention;
-
-  locations->SetInAt(0, Location::RequiresFpuRegister());
-  locations->SetOut(Location::RequiresFpuRegister());
-  // Native code uses the soft float ABI.
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
-}
-
-static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
-  // If the graph is debuggable, all callee-saved floating-point registers are blocked by
-  // the code generator. Furthermore, the register allocator creates fixed live intervals
-  // for all caller-saved registers because we are doing a function call. As a result, if
-  // the input and output locations are unallocated, the register allocator runs out of
-  // registers and fails; however, a debuggable graph is not the common case.
-  if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
-    return;
-  }
-
-  DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
-  DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
-  DCHECK_EQ(invoke->InputAt(1)->GetType(), Primitive::kPrimDouble);
-  DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
-
-  LocationSummary* const locations = new (arena) LocationSummary(invoke,
-                                                                 LocationSummary::kCallOnMainOnly,
-                                                                 kIntrinsified);
-  const InvokeRuntimeCallingConvention calling_convention;
-
-  locations->SetInAt(0, Location::RequiresFpuRegister());
-  locations->SetInAt(1, Location::RequiresFpuRegister());
-  locations->SetOut(Location::RequiresFpuRegister());
-  // Native code uses the soft float ABI.
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
-}
-
-static void GenFPToFPCall(HInvoke* invoke,
-                          ArmAssembler* assembler,
-                          CodeGeneratorARM* codegen,
-                          QuickEntrypointEnum entry) {
-  LocationSummary* const locations = invoke->GetLocations();
-  const InvokeRuntimeCallingConvention calling_convention;
-
-  DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
-  DCHECK(locations->WillCall() && locations->Intrinsified());
-  DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(0)));
-  DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(1)));
-
-  // Native code uses the soft float ABI.
-  __ vmovrrd(calling_convention.GetRegisterAt(0),
-             calling_convention.GetRegisterAt(1),
-             FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
-  codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
-  __ vmovdrr(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
-             calling_convention.GetRegisterAt(0),
-             calling_convention.GetRegisterAt(1));
-}
-
-static void GenFPFPToFPCall(HInvoke* invoke,
-                          ArmAssembler* assembler,
-                          CodeGeneratorARM* codegen,
-                          QuickEntrypointEnum entry) {
-  LocationSummary* const locations = invoke->GetLocations();
-  const InvokeRuntimeCallingConvention calling_convention;
-
-  DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
-  DCHECK(locations->WillCall() && locations->Intrinsified());
-  DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(0)));
-  DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(1)));
-  DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(2)));
-  DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(3)));
-
-  // Native code uses the soft float ABI.
-  __ vmovrrd(calling_convention.GetRegisterAt(0),
-             calling_convention.GetRegisterAt(1),
-             FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
-  __ vmovrrd(calling_convention.GetRegisterAt(2),
-             calling_convention.GetRegisterAt(3),
-             FromLowSToD(locations->InAt(1).AsFpuRegisterPairLow<SRegister>()));
-  codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
-  __ vmovdrr(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
-             calling_convention.GetRegisterAt(0),
-             calling_convention.GetRegisterAt(1));
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathCos(HInvoke* invoke) {
-  CreateFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathCos(HInvoke* invoke) {
-  GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCos);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathSin(HInvoke* invoke) {
-  CreateFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathSin(HInvoke* invoke) {
-  GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSin);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathAcos(HInvoke* invoke) {
-  CreateFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathAcos(HInvoke* invoke) {
-  GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAcos);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathAsin(HInvoke* invoke) {
-  CreateFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathAsin(HInvoke* invoke) {
-  GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAsin);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathAtan(HInvoke* invoke) {
-  CreateFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathAtan(HInvoke* invoke) {
-  GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathCbrt(HInvoke* invoke) {
-  CreateFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathCbrt(HInvoke* invoke) {
-  GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCbrt);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathCosh(HInvoke* invoke) {
-  CreateFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathCosh(HInvoke* invoke) {
-  GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCosh);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathExp(HInvoke* invoke) {
-  CreateFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathExp(HInvoke* invoke) {
-  GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExp);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathExpm1(HInvoke* invoke) {
-  CreateFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathExpm1(HInvoke* invoke) {
-  GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExpm1);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathLog(HInvoke* invoke) {
-  CreateFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathLog(HInvoke* invoke) {
-  GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathLog10(HInvoke* invoke) {
-  CreateFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathLog10(HInvoke* invoke) {
-  GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog10);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathSinh(HInvoke* invoke) {
-  CreateFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathSinh(HInvoke* invoke) {
-  GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSinh);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathTan(HInvoke* invoke) {
-  CreateFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathTan(HInvoke* invoke) {
-  GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTan);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathTanh(HInvoke* invoke) {
-  CreateFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathTanh(HInvoke* invoke) {
-  GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTanh);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathAtan2(HInvoke* invoke) {
-  CreateFPFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathAtan2(HInvoke* invoke) {
-  GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan2);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathHypot(HInvoke* invoke) {
-  CreateFPFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathHypot(HInvoke* invoke) {
-  GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickHypot);
-}
-
-void IntrinsicLocationsBuilderARM::VisitMathNextAfter(HInvoke* invoke) {
-  CreateFPFPToFPCallLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitMathNextAfter(HInvoke* invoke) {
-  GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter);
-}
-
-void IntrinsicLocationsBuilderARM::VisitIntegerReverse(HInvoke* invoke) {
-  CreateIntToIntLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitIntegerReverse(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  LocationSummary* locations = invoke->GetLocations();
-
-  Register out = locations->Out().AsRegister<Register>();
-  Register in  = locations->InAt(0).AsRegister<Register>();
-
-  __ rbit(out, in);
-}
-
-void IntrinsicLocationsBuilderARM::VisitLongReverse(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kNoCall,
-                                                            kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-}
-
-void IntrinsicCodeGeneratorARM::VisitLongReverse(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  LocationSummary* locations = invoke->GetLocations();
-
-  Register in_reg_lo  = locations->InAt(0).AsRegisterPairLow<Register>();
-  Register in_reg_hi  = locations->InAt(0).AsRegisterPairHigh<Register>();
-  Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
-  Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
-
-  __ rbit(out_reg_lo, in_reg_hi);
-  __ rbit(out_reg_hi, in_reg_lo);
-}
-
-void IntrinsicLocationsBuilderARM::VisitIntegerReverseBytes(HInvoke* invoke) {
-  CreateIntToIntLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitIntegerReverseBytes(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  LocationSummary* locations = invoke->GetLocations();
-
-  Register out = locations->Out().AsRegister<Register>();
-  Register in  = locations->InAt(0).AsRegister<Register>();
-
-  __ rev(out, in);
-}
-
-void IntrinsicLocationsBuilderARM::VisitLongReverseBytes(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kNoCall,
-                                                            kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-}
-
-void IntrinsicCodeGeneratorARM::VisitLongReverseBytes(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  LocationSummary* locations = invoke->GetLocations();
-
-  Register in_reg_lo  = locations->InAt(0).AsRegisterPairLow<Register>();
-  Register in_reg_hi  = locations->InAt(0).AsRegisterPairHigh<Register>();
-  Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
-  Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
-
-  __ rev(out_reg_lo, in_reg_hi);
-  __ rev(out_reg_hi, in_reg_lo);
-}
-
-void IntrinsicLocationsBuilderARM::VisitShortReverseBytes(HInvoke* invoke) {
-  CreateIntToIntLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitShortReverseBytes(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  LocationSummary* locations = invoke->GetLocations();
-
-  Register out = locations->Out().AsRegister<Register>();
-  Register in  = locations->InAt(0).AsRegister<Register>();
-
-  __ revsh(out, in);
-}
-
-static void GenBitCount(HInvoke* instr, Primitive::Type type, ArmAssembler* assembler) {
-  DCHECK(Primitive::IsIntOrLongType(type)) << type;
-  DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
-  DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
-
-  bool is_long = type == Primitive::kPrimLong;
-  LocationSummary* locations = instr->GetLocations();
-  Location in = locations->InAt(0);
-  Register src_0 = is_long ? in.AsRegisterPairLow<Register>() : in.AsRegister<Register>();
-  Register src_1 = is_long ? in.AsRegisterPairHigh<Register>() : src_0;
-  SRegister tmp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>();
-  DRegister tmp_d = FromLowSToD(tmp_s);
-  Register  out_r = locations->Out().AsRegister<Register>();
-
-  // Move data from core register(s) to temp D-reg for bit count calculation, then move back.
-  // According to Cortex A57 and A72 optimization guides, compared to transferring to full D-reg,
-  // transferring data from core reg to upper or lower half of vfp D-reg requires extra latency,
-  // That's why for integer bit count, we use 'vmov d0, r0, r0' instead of 'vmov d0[0], r0'.
-  __ vmovdrr(tmp_d, src_1, src_0);                         // Temp DReg |--src_1|--src_0|
-  __ vcntd(tmp_d, tmp_d);                                  // Temp DReg |c|c|c|c|c|c|c|c|
-  __ vpaddld(tmp_d, tmp_d, 8, /* is_unsigned */ true);     // Temp DReg |--c|--c|--c|--c|
-  __ vpaddld(tmp_d, tmp_d, 16, /* is_unsigned */ true);    // Temp DReg |------c|------c|
-  if (is_long) {
-    __ vpaddld(tmp_d, tmp_d, 32, /* is_unsigned */ true);  // Temp DReg |--------------c|
-  }
-  __ vmovrs(out_r, tmp_s);
-}
-
-void IntrinsicLocationsBuilderARM::VisitIntegerBitCount(HInvoke* invoke) {
-  CreateIntToIntLocations(arena_, invoke);
-  invoke->GetLocations()->AddTemp(Location::RequiresFpuRegister());
-}
-
-void IntrinsicCodeGeneratorARM::VisitIntegerBitCount(HInvoke* invoke) {
-  GenBitCount(invoke, Primitive::kPrimInt, GetAssembler());
-}
-
-void IntrinsicLocationsBuilderARM::VisitLongBitCount(HInvoke* invoke) {
-  VisitIntegerBitCount(invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitLongBitCount(HInvoke* invoke) {
-  GenBitCount(invoke, Primitive::kPrimLong, GetAssembler());
-}
-
-void IntrinsicLocationsBuilderARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kNoCall,
-                                                            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());
-
-  // Temporary registers to store lengths of strings and for calculations.
-  locations->AddTemp(Location::RequiresRegister());
-  locations->AddTemp(Location::RequiresRegister());
-  locations->AddTemp(Location::RequiresRegister());
-}
-
-void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
-  ArmAssembler* 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);
-
-  // Location of data in char array buffer.
-  const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
-
-  // Location of char array data in string.
-  const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
-
-  // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
-  // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
-  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 num_chr = locations->GetTemp(0).AsRegister<Register>();
-  Register src_ptr = locations->GetTemp(1).AsRegister<Register>();
-  Register dst_ptr = locations->GetTemp(2).AsRegister<Register>();
-
-  Label done, compressed_string_loop;
-  Label* final_label = codegen_->GetFinalLabel(invoke, &done);
-  // dst to be copied.
-  __ add(dst_ptr, dstObj, ShifterOperand(data_offset));
-  __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1));
-
-  __ subs(num_chr, srcEnd, ShifterOperand(srcBegin));
-  // Early out for valid zero-length retrievals.
-  __ b(final_label, EQ);
-
-  // src range to copy.
-  __ add(src_ptr, srcObj, ShifterOperand(value_offset));
-  Label compressed_string_preloop;
-  if (mirror::kUseStringCompression) {
-    // Location of count in string.
-    const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
-    // String's length.
-    __ ldr(IP, Address(srcObj, count_offset));
-    __ tst(IP, ShifterOperand(1));
-    __ b(&compressed_string_preloop, EQ);
-  }
-  __ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1));
-
-  // Do the copy.
-  Label loop, remainder;
-
-  // Save repairing the value of num_chr on the < 4 character path.
-  __ subs(IP, num_chr, ShifterOperand(4));
-  __ b(&remainder, LT);
-
-  // Keep the result of the earlier subs, we are going to fetch at least 4 characters.
-  __ mov(num_chr, ShifterOperand(IP));
-
-  // Main loop used for longer fetches loads and stores 4x16-bit characters at a time.
-  // (LDRD/STRD fault on unaligned addresses and it's not worth inlining extra code
-  // to rectify these everywhere this intrinsic applies.)
-  __ Bind(&loop);
-  __ ldr(IP, Address(src_ptr, char_size * 2));
-  __ subs(num_chr, num_chr, ShifterOperand(4));
-  __ str(IP, Address(dst_ptr, char_size * 2));
-  __ ldr(IP, Address(src_ptr, char_size * 4, Address::PostIndex));
-  __ str(IP, Address(dst_ptr, char_size * 4, Address::PostIndex));
-  __ b(&loop, GE);
-
-  __ adds(num_chr, num_chr, ShifterOperand(4));
-  __ b(final_label, EQ);
-
-  // Main loop for < 4 character case and remainder handling. Loads and stores one
-  // 16-bit Java character at a time.
-  __ Bind(&remainder);
-  __ ldrh(IP, Address(src_ptr, char_size, Address::PostIndex));
-  __ subs(num_chr, num_chr, ShifterOperand(1));
-  __ strh(IP, Address(dst_ptr, char_size, Address::PostIndex));
-  __ b(&remainder, GT);
-
-  if (mirror::kUseStringCompression) {
-    __ b(final_label);
-
-    const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
-    DCHECK_EQ(c_char_size, 1u);
-    // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
-    __ Bind(&compressed_string_preloop);
-    __ add(src_ptr, src_ptr, ShifterOperand(srcBegin));
-    __ Bind(&compressed_string_loop);
-    __ ldrb(IP, Address(src_ptr, c_char_size, Address::PostIndex));
-    __ strh(IP, Address(dst_ptr, char_size, Address::PostIndex));
-    __ subs(num_chr, num_chr, ShifterOperand(1));
-    __ b(&compressed_string_loop, GT);
-  }
-
-  if (done.IsLinked()) {
-    __ Bind(&done);
-  }
-}
-
-void IntrinsicLocationsBuilderARM::VisitFloatIsInfinite(HInvoke* invoke) {
-  CreateFPToIntLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitFloatIsInfinite(HInvoke* invoke) {
-  ArmAssembler* const assembler = GetAssembler();
-  LocationSummary* const locations = invoke->GetLocations();
-  const Register out = locations->Out().AsRegister<Register>();
-  // Shifting left by 1 bit makes the value encodable as an immediate operand;
-  // we don't care about the sign bit anyway.
-  constexpr uint32_t infinity = kPositiveInfinityFloat << 1U;
-
-  __ vmovrs(out, locations->InAt(0).AsFpuRegister<SRegister>());
-  // We don't care about the sign bit, so shift left.
-  __ Lsl(out, out, 1);
-  __ eor(out, out, ShifterOperand(infinity));
-  codegen_->GenerateConditionWithZero(kCondEQ, out, out);
-}
-
-void IntrinsicLocationsBuilderARM::VisitDoubleIsInfinite(HInvoke* invoke) {
-  CreateFPToIntLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM::VisitDoubleIsInfinite(HInvoke* invoke) {
-  ArmAssembler* const assembler = GetAssembler();
-  LocationSummary* const locations = invoke->GetLocations();
-  const Register out = locations->Out().AsRegister<Register>();
-  // The highest 32 bits of double precision positive infinity separated into
-  // two constants encodable as immediate operands.
-  constexpr uint32_t infinity_high  = 0x7f000000U;
-  constexpr uint32_t infinity_high2 = 0x00f00000U;
-
-  static_assert((infinity_high | infinity_high2) == static_cast<uint32_t>(kPositiveInfinityDouble >> 32U),
-                "The constants do not add up to the high 32 bits of double precision positive infinity.");
-  __ vmovrrd(IP, out, FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
-  __ eor(out, out, ShifterOperand(infinity_high));
-  __ eor(out, out, ShifterOperand(infinity_high2));
-  // We don't care about the sign bit, so shift left.
-  __ orr(out, IP, ShifterOperand(out, LSL, 1));
-  codegen_->GenerateConditionWithZero(kCondEQ, out, out);
-}
-
-void IntrinsicLocationsBuilderARM::VisitIntegerValueOf(HInvoke* invoke) {
-  InvokeRuntimeCallingConvention calling_convention;
-  IntrinsicVisitor::ComputeIntegerValueOfLocations(
-      invoke,
-      codegen_,
-      Location::RegisterLocation(R0),
-      Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-}
-
-void IntrinsicCodeGeneratorARM::VisitIntegerValueOf(HInvoke* invoke) {
-  IntrinsicVisitor::IntegerValueOfInfo info = IntrinsicVisitor::ComputeIntegerValueOfInfo();
-  LocationSummary* locations = invoke->GetLocations();
-  ArmAssembler* const assembler = GetAssembler();
-
-  Register out = locations->Out().AsRegister<Register>();
-  InvokeRuntimeCallingConvention calling_convention;
-  Register argument = calling_convention.GetRegisterAt(0);
-  if (invoke->InputAt(0)->IsConstant()) {
-    int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
-    if (value >= info.low && value <= info.high) {
-      // Just embed the j.l.Integer in the code.
-      ScopedObjectAccess soa(Thread::Current());
-      mirror::Object* boxed = info.cache->Get(value + (-info.low));
-      DCHECK(boxed != nullptr && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boxed));
-      uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(boxed));
-      __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
-    } else {
-      // Allocate and initialize a new j.l.Integer.
-      // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
-      // JIT object table.
-      uint32_t address =
-          dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
-      __ LoadLiteral(argument, codegen_->DeduplicateBootImageAddressLiteral(address));
-      codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
-      CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
-      __ LoadImmediate(IP, value);
-      __ StoreToOffset(kStoreWord, IP, out, info.value_offset);
-      // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
-      // one.
-      codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
-    }
-  } else {
-    Register in = locations->InAt(0).AsRegister<Register>();
-    // Check bounds of our cache.
-    __ AddConstant(out, in, -info.low);
-    __ CmpConstant(out, info.high - info.low + 1);
-    Label allocate, done;
-    __ b(&allocate, HS);
-    // If the value is within the bounds, load the j.l.Integer directly from the array.
-    uint32_t data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
-    uint32_t address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.cache));
-    __ LoadLiteral(IP, codegen_->DeduplicateBootImageAddressLiteral(data_offset + address));
-    codegen_->LoadFromShiftedRegOffset(Primitive::kPrimNot, locations->Out(), IP, out);
-    __ MaybeUnpoisonHeapReference(out);
-    __ b(&done);
-    __ Bind(&allocate);
-    // Otherwise allocate and initialize a new j.l.Integer.
-    address = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(info.integer));
-    __ LoadLiteral(argument, codegen_->DeduplicateBootImageAddressLiteral(address));
-    codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
-    CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
-    __ StoreToOffset(kStoreWord, in, out, info.value_offset);
-    // `value` is a final field :-( Ideally, we'd merge this memory barrier with the allocation
-    // one.
-    codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
-    __ Bind(&done);
-  }
-}
-
-void IntrinsicLocationsBuilderARM::VisitThreadInterrupted(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kNoCall,
-                                                            kIntrinsified);
-  locations->SetOut(Location::RequiresRegister());
-}
-
-void IntrinsicCodeGeneratorARM::VisitThreadInterrupted(HInvoke* invoke) {
-  ArmAssembler* assembler = GetAssembler();
-  Register out = invoke->GetLocations()->Out().AsRegister<Register>();
-  int32_t offset = Thread::InterruptedOffset<kArmPointerSize>().Int32Value();
-  __ LoadFromOffset(kLoadWord, out, TR, offset);
-  Label done;
-  Label* const final_label = codegen_->GetFinalLabel(invoke, &done);
-  __ CompareAndBranchIfZero(out, final_label);
-  __ dmb(ISH);
-  __ LoadImmediate(IP, 0);
-  __ StoreToOffset(kStoreWord, IP, TR, offset);
-  __ dmb(ISH);
-  if (done.IsLinked()) {
-    __ Bind(&done);
-  }
-}
-
-UNIMPLEMENTED_INTRINSIC(ARM, MathMinDoubleDouble)
-UNIMPLEMENTED_INTRINSIC(ARM, MathMinFloatFloat)
-UNIMPLEMENTED_INTRINSIC(ARM, MathMaxDoubleDouble)
-UNIMPLEMENTED_INTRINSIC(ARM, MathMaxFloatFloat)
-UNIMPLEMENTED_INTRINSIC(ARM, MathMinLongLong)
-UNIMPLEMENTED_INTRINSIC(ARM, MathMaxLongLong)
-UNIMPLEMENTED_INTRINSIC(ARM, MathCeil)          // Could be done by changing rounding mode, maybe?
-UNIMPLEMENTED_INTRINSIC(ARM, MathFloor)         // Could be done by changing rounding mode, maybe?
-UNIMPLEMENTED_INTRINSIC(ARM, MathRint)
-UNIMPLEMENTED_INTRINSIC(ARM, MathRoundDouble)   // Could be done by changing rounding mode, maybe?
-UNIMPLEMENTED_INTRINSIC(ARM, MathRoundFloat)    // Could be done by changing rounding mode, maybe?
-UNIMPLEMENTED_INTRINSIC(ARM, UnsafeCASLong)     // High register pressure.
-UNIMPLEMENTED_INTRINSIC(ARM, SystemArrayCopyChar)
-UNIMPLEMENTED_INTRINSIC(ARM, ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(ARM, IntegerHighestOneBit)
-UNIMPLEMENTED_INTRINSIC(ARM, LongHighestOneBit)
-UNIMPLEMENTED_INTRINSIC(ARM, IntegerLowestOneBit)
-UNIMPLEMENTED_INTRINSIC(ARM, LongLowestOneBit)
-
-UNIMPLEMENTED_INTRINSIC(ARM, StringStringIndexOf);
-UNIMPLEMENTED_INTRINSIC(ARM, StringStringIndexOfAfter);
-UNIMPLEMENTED_INTRINSIC(ARM, StringBufferAppend);
-UNIMPLEMENTED_INTRINSIC(ARM, StringBufferLength);
-UNIMPLEMENTED_INTRINSIC(ARM, StringBufferToString);
-UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderAppend);
-UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderLength);
-UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderToString);
-
-// 1.8.
-UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddInt)
-UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddLong)
-UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetInt)
-UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetLong)
-UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetObject)
-
-UNREACHABLE_INTRINSICS(ARM)
-
-#undef __
-
-}  // namespace arm
-}  // namespace art
diff --git a/compiler/optimizing/intrinsics_arm.h b/compiler/optimizing/intrinsics_arm.h
deleted file mode 100644
index 2840863..0000000
--- a/compiler/optimizing/intrinsics_arm.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2015 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_INTRINSICS_ARM_H_
-#define ART_COMPILER_OPTIMIZING_INTRINSICS_ARM_H_
-
-#include "intrinsics.h"
-
-namespace art {
-
-class ArenaAllocator;
-class ArmInstructionSetFeatures;
-class HInvokeStaticOrDirect;
-class HInvokeVirtual;
-
-namespace arm {
-
-class ArmAssembler;
-class CodeGeneratorARM;
-
-class IntrinsicLocationsBuilderARM FINAL : public IntrinsicVisitor {
- public:
-  explicit IntrinsicLocationsBuilderARM(CodeGeneratorARM* codegen);
-
-  // Define visitor methods.
-
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
-  void Visit ## Name(HInvoke* invoke) OVERRIDE;
-#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
-#undef INTRINSICS_LIST
-#undef OPTIMIZING_INTRINSICS
-
-  // Check whether an invoke is an intrinsic, and if so, create a location summary. Returns whether
-  // a corresponding LocationSummary with the intrinsified_ flag set was generated and attached to
-  // the invoke.
-  bool TryDispatch(HInvoke* invoke);
-
- private:
-  ArenaAllocator* arena_;
-  CodeGenerator* codegen_;
-  ArmAssembler* assembler_;
-
-  const ArmInstructionSetFeatures& features_;
-
-  DISALLOW_COPY_AND_ASSIGN(IntrinsicLocationsBuilderARM);
-};
-
-class IntrinsicCodeGeneratorARM FINAL : public IntrinsicVisitor {
- public:
-  explicit IntrinsicCodeGeneratorARM(CodeGeneratorARM* codegen) : codegen_(codegen) {}
-
-  // Define visitor methods.
-
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
-  void Visit ## Name(HInvoke* invoke) OVERRIDE;
-#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
-#undef INTRINSICS_LIST
-#undef OPTIMIZING_INTRINSICS
-
- private:
-  ArmAssembler* GetAssembler();
-
-  ArenaAllocator* GetAllocator();
-
-  CodeGeneratorARM* codegen_;
-
-  DISALLOW_COPY_AND_ASSIGN(IntrinsicCodeGeneratorARM);
-};
-
-}  // namespace arm
-}  // namespace art
-
-#endif  // ART_COMPILER_OPTIMIZING_INTRINSICS_ARM_H_
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 37d7981..5691dd0 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -205,7 +205,7 @@
     // TODO: Load the entrypoint once before the loop, instead of
     // loading it at every iteration.
     int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(tmp_.reg());
+        Thread::ReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(tmp_.reg());
     // This runtime call does not require a stack map.
     codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
     codegen->GetAssembler()->MaybePoisonHeapReference(tmp_reg);
@@ -2738,7 +2738,7 @@
         // TODO: Also convert this intrinsic to the IsGcMarking strategy?
 
         // SystemArrayCopy implementation for Baker read barriers (see
-        // also CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier):
+        // also CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier):
         //
         //   uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
         //   lfence;  // Load fence or artificial data dependency to prevent load-load reordering
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 3c9b613..8b4044d 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -226,7 +226,7 @@
     // TODO: Load the entrypoint once before the loop, instead of
     // loading it at every iteration.
     int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(tmp.GetCode());
+        Thread::ReadBarrierMarkEntryPointsOffset<kArmPointerSize>(tmp.GetCode());
     // This runtime call does not require a stack map.
     arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
     assembler->MaybePoisonHeapReference(tmp);
@@ -1058,7 +1058,7 @@
                     (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
   if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
     // We need a temporary register for the read barrier marking slow
-    // path in InstructionCodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier.
+    // path in CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier.
     locations->AddTemp(Location::RequiresRegister());
   }
 }
@@ -2377,7 +2377,7 @@
       // TODO: Also convert this intrinsic to the IsGcMarking strategy?
 
       // SystemArrayCopy implementation for Baker read barriers (see
-      // also CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier):
+      // also CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier):
       //
       //   uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
       //   lfence;  // Load fence or artificial data dependency to prevent load-load reordering
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 6b4851d..a18b0cc 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -143,8 +143,7 @@
     // explanations.)
     DCHECK_NE(temp2, ESP);
     DCHECK(0 <= temp2 && temp2 < kNumberOfCpuRegisters) << temp2;
-    int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86PointerSize>(temp2);
+    int32_t entry_point_offset = Thread::ReadBarrierMarkEntryPointsOffset<kX86PointerSize>(temp2);
     // This runtime call does not require a stack map.
     x86_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
     __ MaybePoisonHeapReference(temp2);
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index ef98b7b..5abdb1d 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -105,8 +105,7 @@
     // No need to save live registers; it's taken care of by the
     // entrypoint. Also, there is no need to update the stack mask,
     // as this runtime call will not trigger a garbage collection.
-    int32_t entry_point_offset =
-        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86_64PointerSize>(TMP);
+    int32_t entry_point_offset = Thread::ReadBarrierMarkEntryPointsOffset<kX86_64PointerSize>(TMP);
     // This runtime call does not require a stack map.
     x86_64_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
     __ MaybePoisonHeapReference(CpuRegister(TMP));
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 83f31c7..422e58d 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -1171,7 +1171,32 @@
       }
       return false;
     case kMips:
-      // TODO: implement MIPS SIMD.
+      if (features->AsMipsInstructionSetFeatures()->HasMsa()) {
+        switch (type) {
+          case Primitive::kPrimBoolean:
+          case Primitive::kPrimByte:
+            *restrictions |= kNoDiv;
+            return TrySetVectorLength(16);
+          case Primitive::kPrimChar:
+          case Primitive::kPrimShort:
+            *restrictions |= kNoDiv | kNoStringCharAt;
+            return TrySetVectorLength(8);
+          case Primitive::kPrimInt:
+            *restrictions |= kNoDiv;
+            return TrySetVectorLength(4);
+          case Primitive::kPrimLong:
+            *restrictions |= kNoDiv;
+            return TrySetVectorLength(2);
+          case Primitive::kPrimFloat:
+            *restrictions |= kNoMinMax;  // min/max(x, NaN)
+            return TrySetVectorLength(4);
+          case Primitive::kPrimDouble:
+            *restrictions |= kNoMinMax;  // min/max(x, NaN)
+            return TrySetVectorLength(2);
+          default:
+            break;
+        }  // switch type
+      }
       return false;
     case kMips64:
       if (features->AsMips64InstructionSetFeatures()->HasMsa()) {
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index 5dbe29b..6261171 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -46,6 +46,10 @@
     return "ALIGN(" + std::to_string(base_) + "," + std::to_string(offset_) + ")";
   }
 
+  bool operator==(const Alignment& other) const {
+    return base_ == other.base_ && offset_ == other.offset_;
+  }
+
  private:
   size_t base_;
   size_t offset_;
@@ -96,6 +100,19 @@
     return GetPackedField<TypeField>();
   }
 
+  // Assumes vector nodes cannot be moved by default. Each concrete implementation
+  // that can be moved should override this method and return true.
+  bool CanBeMoved() const OVERRIDE { return false; }
+
+  // Tests if all data of a vector node (vector length and packed type) is equal.
+  // Each concrete implementation that adds more fields should test equality of
+  // those fields in its own method *and* call all super methods.
+  bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+    DCHECK(other->IsVecOperation());
+    const HVecOperation* o = other->AsVecOperation();
+    return GetVectorLength() == o->GetVectorLength() && GetPackedType() == o->GetPackedType();
+  }
+
   DECLARE_ABSTRACT_INSTRUCTION(VecOperation);
 
  protected:
@@ -189,6 +206,12 @@
   HInstruction* GetArray() const { return InputAt(0); }
   HInstruction* GetIndex() const { return InputAt(1); }
 
+  bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+    DCHECK(other->IsVecMemoryOperation());
+    const HVecMemoryOperation* o = other->AsVecMemoryOperation();
+    return HVecOperation::InstructionDataEquals(o) && GetAlignment() == o->GetAlignment();
+  }
+
   DECLARE_ABSTRACT_INSTRUCTION(VecMemoryOperation);
 
  private:
@@ -231,7 +254,13 @@
       : HVecUnaryOperation(arena, scalar, packed_type, vector_length, dex_pc) {
     DCHECK(!scalar->IsVecOperation());
   }
+
+  // A replicate needs to stay in place, since SIMD registers are not
+  // kept alive across vector loop boundaries (yet).
+  bool CanBeMoved() const OVERRIDE { return false; }
+
   DECLARE_INSTRUCTION(VecReplicateScalar);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecReplicateScalar);
 };
@@ -251,7 +280,10 @@
   // TODO: probably integral promotion
   Primitive::Type GetType() const OVERRIDE { return GetPackedType(); }
 
+  bool CanBeMoved() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(VecSumReduce);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecSumReduce);
 };
@@ -273,6 +305,8 @@
   Primitive::Type GetInputType() const { return InputAt(0)->AsVecOperation()->GetPackedType(); }
   Primitive::Type GetResultType() const { return GetPackedType(); }
 
+  bool CanBeMoved() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(VecCnv);
 
  private:
@@ -291,7 +325,11 @@
       : HVecUnaryOperation(arena, input, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(input, packed_type));
   }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(VecNeg);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecNeg);
 };
@@ -308,7 +346,11 @@
       : HVecUnaryOperation(arena, input, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(input, packed_type));
   }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(VecAbs);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecAbs);
 };
@@ -326,7 +368,11 @@
       : HVecUnaryOperation(arena, input, packed_type, vector_length, dex_pc) {
     DCHECK(input->IsVecOperation());
   }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(VecNot);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecNot);
 };
@@ -349,7 +395,11 @@
     DCHECK(HasConsistentPackedTypes(left, packed_type));
     DCHECK(HasConsistentPackedTypes(right, packed_type));
   }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(VecAdd);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecAdd);
 };
@@ -378,6 +428,16 @@
   bool IsUnsigned() const { return GetPackedFlag<kFieldHAddIsUnsigned>(); }
   bool IsRounded() const { return GetPackedFlag<kFieldHAddIsRounded>(); }
 
+  bool CanBeMoved() const OVERRIDE { return true; }
+
+  bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+    DCHECK(other->IsVecHalvingAdd());
+    const HVecHalvingAdd* o = other->AsVecHalvingAdd();
+    return HVecOperation::InstructionDataEquals(o) &&
+        IsUnsigned() == o->IsUnsigned() &&
+        IsRounded() == o->IsRounded();
+  }
+
   DECLARE_INSTRUCTION(VecHalvingAdd);
 
  private:
@@ -404,7 +464,11 @@
     DCHECK(HasConsistentPackedTypes(left, packed_type));
     DCHECK(HasConsistentPackedTypes(right, packed_type));
   }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(VecSub);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecSub);
 };
@@ -423,7 +487,11 @@
     DCHECK(HasConsistentPackedTypes(left, packed_type));
     DCHECK(HasConsistentPackedTypes(right, packed_type));
   }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(VecMul);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecMul);
 };
@@ -442,7 +510,11 @@
     DCHECK(HasConsistentPackedTypes(left, packed_type));
     DCHECK(HasConsistentPackedTypes(right, packed_type));
   }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(VecDiv);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecDiv);
 };
@@ -466,6 +538,14 @@
 
   bool IsUnsigned() const { return GetPackedFlag<kFieldMinOpIsUnsigned>(); }
 
+  bool CanBeMoved() const OVERRIDE { return true; }
+
+  bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+    DCHECK(other->IsVecMin());
+    const HVecMin* o = other->AsVecMin();
+    return HVecOperation::InstructionDataEquals(o) && IsUnsigned() == o->IsUnsigned();
+  }
+
   DECLARE_INSTRUCTION(VecMin);
 
  private:
@@ -496,6 +576,14 @@
 
   bool IsUnsigned() const { return GetPackedFlag<kFieldMaxOpIsUnsigned>(); }
 
+  bool CanBeMoved() const OVERRIDE { return true; }
+
+  bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+    DCHECK(other->IsVecMax());
+    const HVecMax* o = other->AsVecMax();
+    return HVecOperation::InstructionDataEquals(o) && IsUnsigned() == o->IsUnsigned();
+  }
+
   DECLARE_INSTRUCTION(VecMax);
 
  private:
@@ -520,7 +608,11 @@
       : HVecBinaryOperation(arena, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(left->IsVecOperation() && right->IsVecOperation());
   }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(VecAnd);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecAnd);
 };
@@ -538,7 +630,11 @@
          : HVecBinaryOperation(arena, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(left->IsVecOperation() && right->IsVecOperation());
   }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(VecAndNot);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecAndNot);
 };
@@ -556,7 +652,11 @@
       : HVecBinaryOperation(arena, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(left->IsVecOperation() && right->IsVecOperation());
   }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(VecOr);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecOr);
 };
@@ -574,7 +674,11 @@
       : HVecBinaryOperation(arena, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(left->IsVecOperation() && right->IsVecOperation());
   }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(VecXor);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecXor);
 };
@@ -592,7 +696,11 @@
       : HVecBinaryOperation(arena, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(left, packed_type));
   }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(VecShl);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecShl);
 };
@@ -610,7 +718,11 @@
       : HVecBinaryOperation(arena, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(left, packed_type));
   }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(VecShr);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecShr);
 };
@@ -628,7 +740,11 @@
       : HVecBinaryOperation(arena, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(left, packed_type));
   }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(VecUShr);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecUShr);
 };
@@ -656,7 +772,13 @@
       SetRawInputAt(0, scalars[i]);
     }
   }
+
+  // Setting scalars needs to stay in place, since SIMD registers are not
+  // kept alive across vector loop boundaries (yet).
+  bool CanBeMoved() const OVERRIDE { return false; }
+
   DECLARE_INSTRUCTION(VecSetScalars);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecSetScalars);
 };
@@ -697,7 +819,9 @@
   bool CanBeMoved() const OVERRIDE { return true; }
 
   bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
-    return op_kind_ == other->AsVecMultiplyAccumulate()->op_kind_;
+    DCHECK(other->IsVecMultiplyAccumulate());
+    const HVecMultiplyAccumulate* o = other->AsVecMultiplyAccumulate();
+    return HVecOperation::InstructionDataEquals(o) && GetOpKind() == o->GetOpKind();
   }
 
   InstructionKind GetOpKind() const { return op_kind_; }
@@ -732,10 +856,19 @@
     SetRawInputAt(1, index);
     SetPackedFlag<kFieldIsStringCharAt>(is_string_char_at);
   }
-  DECLARE_INSTRUCTION(VecLoad);
 
   bool IsStringCharAt() const { return GetPackedFlag<kFieldIsStringCharAt>(); }
 
+  bool CanBeMoved() const OVERRIDE { return true; }
+
+  bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+    DCHECK(other->IsVecLoad());
+    const HVecLoad* o = other->AsVecLoad();
+    return HVecMemoryOperation::InstructionDataEquals(o) && IsStringCharAt() == o->IsStringCharAt();
+  }
+
+  DECLARE_INSTRUCTION(VecLoad);
+
  private:
   // Additional packed bits.
   static constexpr size_t kFieldIsStringCharAt = HVecOperation::kNumberOfVectorOpPackedBits;
@@ -767,7 +900,12 @@
     SetRawInputAt(1, index);
     SetRawInputAt(2, value);
   }
+
+  // A store needs to stay in place.
+  bool CanBeMoved() const OVERRIDE { return false; }
+
   DECLARE_INSTRUCTION(VecStore);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HVecStore);
 };
diff --git a/compiler/optimizing/nodes_vector_test.cc b/compiler/optimizing/nodes_vector_test.cc
new file mode 100644
index 0000000..0238ea4
--- /dev/null
+++ b/compiler/optimizing/nodes_vector_test.cc
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/arena_allocator.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+
+namespace art {
+
+/**
+ * Fixture class for testing vector nodes.
+ */
+class NodesVectorTest : public CommonCompilerTest {
+ public:
+  NodesVectorTest()
+      : pool_(),
+        allocator_(&pool_),
+        graph_(CreateGraph(&allocator_)) {
+    BuildGraph();
+  }
+
+  ~NodesVectorTest() { }
+
+  void BuildGraph() {
+    graph_->SetNumberOfVRegs(1);
+    entry_block_ = new (&allocator_) HBasicBlock(graph_);
+    exit_block_ = new (&allocator_) HBasicBlock(graph_);
+    graph_->AddBlock(entry_block_);
+    graph_->AddBlock(exit_block_);
+    graph_->SetEntryBlock(entry_block_);
+    graph_->SetExitBlock(exit_block_);
+    parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(),
+                                                   dex::TypeIndex(0),
+                                                   0,
+                                                   Primitive::kPrimInt);
+    entry_block_->AddInstruction(parameter_);
+  }
+
+  // General building fields.
+  ArenaPool pool_;
+  ArenaAllocator allocator_;
+  HGraph* graph_;
+
+  HBasicBlock* entry_block_;
+  HBasicBlock* exit_block_;
+
+  HInstruction* parameter_;
+};
+
+//
+// The actual vector nodes tests.
+//
+
+TEST(NodesVector, Alignment) {
+  EXPECT_TRUE(Alignment(1, 0).IsAlignedAt(1));
+  EXPECT_FALSE(Alignment(1, 0).IsAlignedAt(2));
+
+  EXPECT_TRUE(Alignment(2, 0).IsAlignedAt(1));
+  EXPECT_TRUE(Alignment(2, 1).IsAlignedAt(1));
+  EXPECT_TRUE(Alignment(2, 0).IsAlignedAt(2));
+  EXPECT_FALSE(Alignment(2, 1).IsAlignedAt(2));
+  EXPECT_FALSE(Alignment(2, 0).IsAlignedAt(4));
+  EXPECT_FALSE(Alignment(2, 1).IsAlignedAt(4));
+
+  EXPECT_TRUE(Alignment(4, 0).IsAlignedAt(1));
+  EXPECT_TRUE(Alignment(4, 2).IsAlignedAt(1));
+  EXPECT_TRUE(Alignment(4, 0).IsAlignedAt(2));
+  EXPECT_TRUE(Alignment(4, 2).IsAlignedAt(2));
+  EXPECT_TRUE(Alignment(4, 0).IsAlignedAt(4));
+  EXPECT_FALSE(Alignment(4, 2).IsAlignedAt(4));
+  EXPECT_FALSE(Alignment(4, 0).IsAlignedAt(8));
+  EXPECT_FALSE(Alignment(4, 2).IsAlignedAt(8));
+
+  EXPECT_TRUE(Alignment(16, 0).IsAlignedAt(1));
+  EXPECT_TRUE(Alignment(16, 0).IsAlignedAt(2));
+  EXPECT_TRUE(Alignment(16, 0).IsAlignedAt(4));
+  EXPECT_TRUE(Alignment(16, 8).IsAlignedAt(8));
+  EXPECT_TRUE(Alignment(16, 0).IsAlignedAt(16));
+  EXPECT_FALSE(Alignment(16, 1).IsAlignedAt(16));
+  EXPECT_FALSE(Alignment(16, 7).IsAlignedAt(16));
+  EXPECT_FALSE(Alignment(16, 0).IsAlignedAt(32));
+}
+
+TEST(NodesVector, AlignmentEQ) {
+  EXPECT_TRUE(Alignment(2, 0) == Alignment(2, 0));
+  EXPECT_TRUE(Alignment(2, 1) == Alignment(2, 1));
+  EXPECT_TRUE(Alignment(4, 0) == Alignment(4, 0));
+  EXPECT_TRUE(Alignment(4, 2) == Alignment(4, 2));
+
+  EXPECT_FALSE(Alignment(4, 0) == Alignment(2, 0));
+  EXPECT_FALSE(Alignment(4, 0) == Alignment(4, 1));
+  EXPECT_FALSE(Alignment(4, 0) == Alignment(8, 0));
+}
+
+TEST(NodesVector, AlignmentString) {
+  EXPECT_STREQ("ALIGN(1,0)", Alignment(1, 0).ToString().c_str());
+
+  EXPECT_STREQ("ALIGN(2,0)", Alignment(2, 0).ToString().c_str());
+  EXPECT_STREQ("ALIGN(2,1)", Alignment(2, 1).ToString().c_str());
+
+  EXPECT_STREQ("ALIGN(16,0)", Alignment(16, 0).ToString().c_str());
+  EXPECT_STREQ("ALIGN(16,1)", Alignment(16, 1).ToString().c_str());
+  EXPECT_STREQ("ALIGN(16,8)", Alignment(16, 8).ToString().c_str());
+  EXPECT_STREQ("ALIGN(16,9)", Alignment(16, 9).ToString().c_str());
+}
+
+TEST_F(NodesVectorTest, VectorOperationProperties) {
+  HVecOperation* v0 = new (&allocator_)
+      HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+  HVecOperation* v1 = new (&allocator_)
+      HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+  HVecOperation* v2 = new (&allocator_)
+      HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 2);
+  HVecOperation* v3 = new (&allocator_)
+      HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimShort, 4);
+  HVecOperation* v4 = new (&allocator_)
+      HVecStore(&allocator_, parameter_, parameter_, v0, Primitive::kPrimInt, 4);
+
+  EXPECT_TRUE(v0->Equals(v0));
+  EXPECT_TRUE(v1->Equals(v1));
+  EXPECT_TRUE(v2->Equals(v2));
+  EXPECT_TRUE(v3->Equals(v3));
+  EXPECT_TRUE(v4->Equals(v4));
+
+  EXPECT_TRUE(v0->Equals(v1));
+  EXPECT_FALSE(v0->Equals(v2));  // different vector lengths
+  EXPECT_FALSE(v0->Equals(v3));  // different packed types
+  EXPECT_FALSE(v0->Equals(v4));  // different kinds
+
+  EXPECT_TRUE(v1->Equals(v0));  // switch operands
+  EXPECT_FALSE(v4->Equals(v0));
+
+  EXPECT_EQ(4u, v0->GetVectorLength());
+  EXPECT_EQ(4u, v1->GetVectorLength());
+  EXPECT_EQ(2u, v2->GetVectorLength());
+  EXPECT_EQ(4u, v3->GetVectorLength());
+  EXPECT_EQ(4u, v4->GetVectorLength());
+
+  EXPECT_EQ(Primitive::kPrimDouble, v0->GetType());
+  EXPECT_EQ(Primitive::kPrimDouble, v1->GetType());
+  EXPECT_EQ(Primitive::kPrimDouble, v2->GetType());
+  EXPECT_EQ(Primitive::kPrimDouble, v3->GetType());
+  EXPECT_EQ(Primitive::kPrimDouble, v4->GetType());
+
+  EXPECT_EQ(Primitive::kPrimInt, v0->GetPackedType());
+  EXPECT_EQ(Primitive::kPrimInt, v1->GetPackedType());
+  EXPECT_EQ(Primitive::kPrimInt, v2->GetPackedType());
+  EXPECT_EQ(Primitive::kPrimShort, v3->GetPackedType());
+  EXPECT_EQ(Primitive::kPrimInt, v4->GetPackedType());
+
+  EXPECT_EQ(16u, v0->GetVectorNumberOfBytes());
+  EXPECT_EQ(16u, v1->GetVectorNumberOfBytes());
+  EXPECT_EQ(8u, v2->GetVectorNumberOfBytes());
+  EXPECT_EQ(8u, v3->GetVectorNumberOfBytes());
+  EXPECT_EQ(16u, v4->GetVectorNumberOfBytes());
+
+  EXPECT_FALSE(v0->CanBeMoved());
+  EXPECT_FALSE(v1->CanBeMoved());
+  EXPECT_FALSE(v2->CanBeMoved());
+  EXPECT_FALSE(v3->CanBeMoved());
+  EXPECT_FALSE(v4->CanBeMoved());
+}
+
+TEST_F(NodesVectorTest, VectorAlignmentAndStringCharAtMatterOnLoad) {
+  HVecLoad* v0 = new (&allocator_)
+      HVecLoad(&allocator_, parameter_, parameter_, Primitive::kPrimInt, 4, /*is_string_char_at*/ false);
+  HVecLoad* v1 = new (&allocator_)
+      HVecLoad(&allocator_, parameter_, parameter_, Primitive::kPrimInt, 4, /*is_string_char_at*/ false);
+  HVecLoad* v2 = new (&allocator_)
+      HVecLoad(&allocator_, parameter_, parameter_, Primitive::kPrimInt, 4, /*is_string_char_at*/ true);
+
+  EXPECT_TRUE(v0->CanBeMoved());
+  EXPECT_TRUE(v1->CanBeMoved());
+  EXPECT_TRUE(v2->CanBeMoved());
+
+  EXPECT_FALSE(v0->IsStringCharAt());
+  EXPECT_FALSE(v1->IsStringCharAt());
+  EXPECT_TRUE(v2->IsStringCharAt());
+
+  EXPECT_TRUE(v0->Equals(v0));
+  EXPECT_TRUE(v1->Equals(v1));
+  EXPECT_TRUE(v2->Equals(v2));
+
+  EXPECT_TRUE(v0->Equals(v1));
+  EXPECT_FALSE(v0->Equals(v2));
+
+  EXPECT_TRUE(v0->GetAlignment() == Alignment(4, 0));
+  EXPECT_TRUE(v1->GetAlignment() == Alignment(4, 0));
+  EXPECT_TRUE(v2->GetAlignment() == Alignment(4, 0));
+
+  v1->SetAlignment(Alignment(8, 0));
+
+  EXPECT_TRUE(v1->GetAlignment() == Alignment(8, 0));
+
+  EXPECT_FALSE(v0->Equals(v1));  // no longer equal
+}
+
+TEST_F(NodesVectorTest, VectorSignMattersOnMin) {
+  HVecOperation* v0 = new (&allocator_)
+      HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+
+  HVecMin* v1 = new (&allocator_)
+      HVecMin(&allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ true);
+  HVecMin* v2 = new (&allocator_)
+      HVecMin(&allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ false);
+  HVecMin* v3 = new (&allocator_)
+      HVecMin(&allocator_, v0, v0, Primitive::kPrimInt, 2, /*is_unsigned*/ true);
+
+  EXPECT_FALSE(v0->CanBeMoved());
+  EXPECT_TRUE(v1->CanBeMoved());
+  EXPECT_TRUE(v2->CanBeMoved());
+  EXPECT_TRUE(v3->CanBeMoved());
+
+  EXPECT_TRUE(v1->IsUnsigned());
+  EXPECT_FALSE(v2->IsUnsigned());
+  EXPECT_TRUE(v3->IsUnsigned());
+
+  EXPECT_TRUE(v1->Equals(v1));
+  EXPECT_TRUE(v2->Equals(v2));
+  EXPECT_TRUE(v3->Equals(v3));
+
+  EXPECT_FALSE(v1->Equals(v2));  // different signs
+  EXPECT_FALSE(v1->Equals(v3));  // different vector lengths
+}
+
+TEST_F(NodesVectorTest, VectorSignMattersOnMax) {
+  HVecOperation* v0 = new (&allocator_)
+      HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+
+  HVecMax* v1 = new (&allocator_)
+      HVecMax(&allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ true);
+  HVecMax* v2 = new (&allocator_)
+      HVecMax(&allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ false);
+  HVecMax* v3 = new (&allocator_)
+      HVecMax(&allocator_, v0, v0, Primitive::kPrimInt, 2, /*is_unsigned*/ true);
+
+  EXPECT_FALSE(v0->CanBeMoved());
+  EXPECT_TRUE(v1->CanBeMoved());
+  EXPECT_TRUE(v2->CanBeMoved());
+  EXPECT_TRUE(v3->CanBeMoved());
+
+  EXPECT_TRUE(v1->IsUnsigned());
+  EXPECT_FALSE(v2->IsUnsigned());
+  EXPECT_TRUE(v3->IsUnsigned());
+
+  EXPECT_TRUE(v1->Equals(v1));
+  EXPECT_TRUE(v2->Equals(v2));
+  EXPECT_TRUE(v3->Equals(v3));
+
+  EXPECT_FALSE(v1->Equals(v2));  // different signs
+  EXPECT_FALSE(v1->Equals(v3));  // different vector lengths
+}
+
+TEST_F(NodesVectorTest, VectorAttributesMatterOnHalvingAdd) {
+  HVecOperation* v0 = new (&allocator_)
+      HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+
+  HVecHalvingAdd* v1 = new (&allocator_) HVecHalvingAdd(
+      &allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ true, /*is_rounded*/ true);
+  HVecHalvingAdd* v2 = new (&allocator_) HVecHalvingAdd(
+      &allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ true, /*is_rounded*/ false);
+  HVecHalvingAdd* v3 = new (&allocator_) HVecHalvingAdd(
+      &allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ false, /*is_rounded*/ true);
+  HVecHalvingAdd* v4 = new (&allocator_) HVecHalvingAdd(
+      &allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ false, /*is_rounded*/ false);
+  HVecHalvingAdd* v5 = new (&allocator_) HVecHalvingAdd(
+      &allocator_, v0, v0, Primitive::kPrimInt, 2, /*is_unsigned*/ true, /*is_rounded*/ true);
+
+  EXPECT_FALSE(v0->CanBeMoved());
+  EXPECT_TRUE(v1->CanBeMoved());
+  EXPECT_TRUE(v2->CanBeMoved());
+  EXPECT_TRUE(v3->CanBeMoved());
+  EXPECT_TRUE(v4->CanBeMoved());
+  EXPECT_TRUE(v5->CanBeMoved());
+
+  EXPECT_TRUE(v1->Equals(v1));
+  EXPECT_TRUE(v2->Equals(v2));
+  EXPECT_TRUE(v3->Equals(v3));
+  EXPECT_TRUE(v4->Equals(v4));
+  EXPECT_TRUE(v5->Equals(v5));
+
+  EXPECT_TRUE(v1->IsUnsigned() && v1->IsRounded());
+  EXPECT_TRUE(v2->IsUnsigned() && !v2->IsRounded());
+  EXPECT_TRUE(!v3->IsUnsigned() && v3->IsRounded());
+  EXPECT_TRUE(!v4->IsUnsigned() && !v4->IsRounded());
+  EXPECT_TRUE(v5->IsUnsigned() && v5->IsRounded());
+
+  EXPECT_FALSE(v1->Equals(v2));  // different attributes
+  EXPECT_FALSE(v1->Equals(v3));  // different attributes
+  EXPECT_FALSE(v1->Equals(v4));  // different attributes
+  EXPECT_FALSE(v1->Equals(v5));  // different vector lengths
+}
+
+TEST_F(NodesVectorTest, VectorOperationMattersOnMultiplyAccumulate) {
+  HVecOperation* v0 = new (&allocator_)
+      HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+
+  HVecMultiplyAccumulate* v1 = new (&allocator_)
+      HVecMultiplyAccumulate(&allocator_, HInstruction::kAdd, v0, v0, v0, Primitive::kPrimInt, 4);
+  HVecMultiplyAccumulate* v2 = new (&allocator_)
+      HVecMultiplyAccumulate(&allocator_, HInstruction::kSub, v0, v0, v0, Primitive::kPrimInt, 4);
+  HVecMultiplyAccumulate* v3 = new (&allocator_)
+      HVecMultiplyAccumulate(&allocator_, HInstruction::kAdd, v0, v0, v0, Primitive::kPrimInt, 2);
+
+  EXPECT_FALSE(v0->CanBeMoved());
+  EXPECT_TRUE(v1->CanBeMoved());
+  EXPECT_TRUE(v2->CanBeMoved());
+  EXPECT_TRUE(v3->CanBeMoved());
+
+  EXPECT_EQ(HInstruction::kAdd, v1->GetOpKind());
+  EXPECT_EQ(HInstruction::kSub, v2->GetOpKind());
+  EXPECT_EQ(HInstruction::kAdd, v3->GetOpKind());
+
+  EXPECT_TRUE(v1->Equals(v1));
+  EXPECT_TRUE(v2->Equals(v2));
+  EXPECT_TRUE(v3->Equals(v3));
+
+  EXPECT_FALSE(v1->Equals(v2));  // different operators
+  EXPECT_FALSE(v1->Equals(v3));  // different vector lengths
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc
index 490e50c..6cb27b3 100644
--- a/compiler/optimizing/optimizing_cfi_test.cc
+++ b/compiler/optimizing/optimizing_cfi_test.cc
@@ -24,21 +24,15 @@
 #include "optimizing/code_generator.h"
 #include "optimizing/optimizing_unit_test.h"
 #include "utils/assembler.h"
-#ifdef ART_USE_OLD_ARM_BACKEND
-#include "utils/arm/assembler_thumb2.h"
-#else
 #include "utils/arm/assembler_arm_vixl.h"
-#endif
 #include "utils/mips/assembler_mips.h"
 #include "utils/mips64/assembler_mips64.h"
 
 #include "optimizing/optimizing_cfi_test_expected.inc"
 
-#ifndef ART_USE_OLD_ARM_BACKEND
 namespace vixl32 = vixl::aarch32;
 
 using vixl32::r0;
-#endif
 
 namespace art {
 
@@ -171,18 +165,31 @@
 #ifdef ART_ENABLE_CODEGEN_arm
 TEST_ISA(kThumb2)
 #endif
+
 #ifdef ART_ENABLE_CODEGEN_arm64
+// Run the tests for ARM64 only with Baker read barriers, as the
+// expected generated code saves and restore X21 and X22 (instead of
+// X20 and X21), as X20 is used as Marking Register in the Baker read
+// barrier configuration, and as such is removed from the set of
+// callee-save registers in the ARM64 code generator of the Optimizing
+// compiler.
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
 TEST_ISA(kArm64)
 #endif
+#endif
+
 #ifdef ART_ENABLE_CODEGEN_x86
 TEST_ISA(kX86)
 #endif
+
 #ifdef ART_ENABLE_CODEGEN_x86_64
 TEST_ISA(kX86_64)
 #endif
+
 #ifdef ART_ENABLE_CODEGEN_mips
 TEST_ISA(kMips)
 #endif
+
 #ifdef ART_ENABLE_CODEGEN_mips64
 TEST_ISA(kMips64)
 #endif
@@ -196,15 +203,6 @@
       expected_cfi_kThumb2_adjust,
       expected_cfi_kThumb2_adjust + arraysize(expected_cfi_kThumb2_adjust));
   SetUpFrame(kThumb2);
-#ifdef ART_USE_OLD_ARM_BACKEND
-#define __ down_cast<arm::Thumb2Assembler*>(GetCodeGenerator()->GetAssembler())->
-  Label target;
-  __ CompareAndBranchIfZero(arm::R0, &target);
-  // Push the target out of range of CBZ.
-  for (size_t i = 0; i != 65; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-#else
 #define __ down_cast<arm::ArmVIXLAssembler*>(GetCodeGenerator() \
     ->GetAssembler())->GetVIXLAssembler()->
   vixl32::Label target;
@@ -213,7 +211,6 @@
   for (size_t i = 0; i != 65; ++i) {
     __ Ldr(r0, vixl32::MemOperand(r0));
   }
-#endif
   __ Bind(&target);
 #undef __
   Finish();
diff --git a/compiler/optimizing/optimizing_cfi_test_expected.inc b/compiler/optimizing/optimizing_cfi_test_expected.inc
index 60af2b4..77a63ac 100644
--- a/compiler/optimizing/optimizing_cfi_test_expected.inc
+++ b/compiler/optimizing/optimizing_cfi_test_expected.inc
@@ -31,21 +31,21 @@
 // 0x00000010: .cfi_def_cfa_offset: 64
 
 static constexpr uint8_t expected_asm_kArm64[] = {
-    0xFF, 0x03, 0x01, 0xD1, 0xF4, 0x17, 0x00, 0xF9, 0xF5, 0x7B, 0x03, 0xA9,
-    0xE8, 0xA7, 0x01, 0x6D, 0xE8, 0xA7, 0x41, 0x6D, 0xF4, 0x17, 0x40, 0xF9,
-    0xF5, 0x7B, 0x43, 0xA9, 0xFF, 0x03, 0x01, 0x91, 0xC0, 0x03, 0x5F, 0xD6,
+    0xFF, 0x03, 0x01, 0xD1, 0xF5, 0x17, 0x00, 0xF9, 0xF6, 0x7B, 0x03, 0xA9,
+    0xE8, 0xA7, 0x01, 0x6D, 0xE8, 0xA7, 0x41, 0x6D, 0xF5, 0x17, 0x40, 0xF9,
+    0xF6, 0x7B, 0x43, 0xA9, 0xFF, 0x03, 0x01, 0x91, 0xC0, 0x03, 0x5F, 0xD6,
 };
 static constexpr uint8_t expected_cfi_kArm64[] = {
-    0x44, 0x0E, 0x40, 0x44, 0x94, 0x06, 0x44, 0x95, 0x04, 0x9E, 0x02, 0x44,
+    0x44, 0x0E, 0x40, 0x44, 0x95, 0x06, 0x44, 0x96, 0x04, 0x9E, 0x02, 0x44,
     0x05, 0x48, 0x0A, 0x05, 0x49, 0x08, 0x0A, 0x44, 0x06, 0x48, 0x06, 0x49,
-    0x44, 0xD4, 0x44, 0xD5, 0xDE, 0x44, 0x0E, 0x00, 0x44, 0x0B, 0x0E, 0x40,
+    0x44, 0xD5, 0x44, 0xD6, 0xDE, 0x44, 0x0E, 0x00, 0x44, 0x0B, 0x0E, 0x40,
 };
 // 0x00000000: sub sp, sp, #0x40 (64)
 // 0x00000004: .cfi_def_cfa_offset: 64
-// 0x00000004: str x20, [sp, #40]
-// 0x00000008: .cfi_offset: r20 at cfa-24
-// 0x00000008: stp x21, lr, [sp, #48]
-// 0x0000000c: .cfi_offset: r21 at cfa-16
+// 0x00000004: str x21, [sp, #40]
+// 0x00000008: .cfi_offset: r21 at cfa-24
+// 0x00000008: stp x22, lr, [sp, #48]
+// 0x0000000c: .cfi_offset: r22 at cfa-16
 // 0x0000000c: .cfi_offset: r30 at cfa-8
 // 0x0000000c: stp d8, d9, [sp, #24]
 // 0x00000010: .cfi_offset_extended: r72 at cfa-40
@@ -54,10 +54,10 @@
 // 0x00000010: ldp d8, d9, [sp, #24]
 // 0x00000014: .cfi_restore_extended: r72
 // 0x00000014: .cfi_restore_extended: r73
-// 0x00000014: ldr x20, [sp, #40]
-// 0x00000018: .cfi_restore: r20
-// 0x00000018: ldp x21, lr, [sp, #48]
-// 0x0000001c: .cfi_restore: r21
+// 0x00000014: ldr x21, [sp, #40]
+// 0x00000018: .cfi_restore: r21
+// 0x00000018: ldp x22, lr, [sp, #48]
+// 0x0000001c: .cfi_restore: r22
 // 0x0000001c: .cfi_restore: r30
 // 0x0000001c: add sp, sp, #0x40 (64)
 // 0x00000020: .cfi_def_cfa_offset: 0
@@ -215,16 +215,11 @@
 // 0x00000034: .cfi_def_cfa_offset: 64
 
 static constexpr uint8_t expected_asm_kThumb2_adjust[] = {
-#ifdef ART_USE_OLD_ARM_BACKEND
-    0x60, 0xB5, 0x2D, 0xED, 0x02, 0x8A, 0x8B, 0xB0, 0x00, 0x28,
-    0x40, 0xD0, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68,
-#else
     // VIXL emits an extra 2 bytes here for a 32-bit beq as there is no
     // optimistic 16-bit emit and subsequent fixup for out of reach targets
     // as with the old assembler.
     0x60, 0xB5, 0x2D, 0xED, 0x02, 0x8A, 0x8B, 0xB0, 0x00, 0x28, 0x00, 0xF0,
     0x41, 0x80, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68,
-#endif
     0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68,
     0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68,
     0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68,
@@ -239,11 +234,7 @@
 };
 static constexpr uint8_t expected_cfi_kThumb2_adjust[] = {
     0x42, 0x0E, 0x0C, 0x85, 0x03, 0x86, 0x02, 0x8E, 0x01, 0x44, 0x0E, 0x14,
-#ifdef ART_USE_OLD_ARM_BACKEND
-    0x05, 0x50, 0x05, 0x05, 0x51, 0x04, 0x42, 0x0E, 0x40, 0x02, 0x86, 0x0A,
-#else
     0x05, 0x50, 0x05, 0x05, 0x51, 0x04, 0x42, 0x0E, 0x40, 0x02, 0x88, 0x0A,
-#endif
     0x42, 0x0E, 0x14, 0x44, 0x0E, 0x0C, 0x06, 0x50, 0x06, 0x51, 0x42, 0x0B,
     0x0E, 0x40,
 };
diff --git a/compiler/optimizing/scheduler_arm.h b/compiler/optimizing/scheduler_arm.h
index 897e97d..a9f2295 100644
--- a/compiler/optimizing/scheduler_arm.h
+++ b/compiler/optimizing/scheduler_arm.h
@@ -17,20 +17,13 @@
 #ifndef ART_COMPILER_OPTIMIZING_SCHEDULER_ARM_H_
 #define ART_COMPILER_OPTIMIZING_SCHEDULER_ARM_H_
 
-#ifdef ART_USE_OLD_ARM_BACKEND
-#include "code_generator_arm.h"
-#else
 #include "code_generator_arm_vixl.h"
-#endif
 #include "scheduler.h"
 
 namespace art {
 namespace arm {
-#ifdef ART_USE_OLD_ARM_BACKEND
-typedef CodeGeneratorARM CodeGeneratorARMType;
-#else
+// TODO: Replace CodeGeneratorARMType with CodeGeneratorARMVIXL everywhere?
 typedef CodeGeneratorARMVIXL CodeGeneratorARMType;
-#endif
 
 // AArch32 instruction latencies.
 // We currently assume that all ARM CPUs share the same instruction latency list.
diff --git a/compiler/optimizing/scheduler_test.cc b/compiler/optimizing/scheduler_test.cc
index cc7222d..10c3cd7 100644
--- a/compiler/optimizing/scheduler_test.cc
+++ b/compiler/optimizing/scheduler_test.cc
@@ -41,8 +41,8 @@
   ::std::vector<CodegenTargetConfig> v;
   ::std::vector<CodegenTargetConfig> test_config_candidates = {
 #ifdef ART_ENABLE_CODEGEN_arm
-    CodegenTargetConfig(kArm, create_codegen_arm),
-    CodegenTargetConfig(kThumb2, create_codegen_arm),
+    // TODO: Should't this be `kThumb2` instead of `kArm` here?
+    CodegenTargetConfig(kArm, create_codegen_arm_vixl32),
 #endif
 #ifdef ART_ENABLE_CODEGEN_arm64
     CodegenTargetConfig(kArm64, create_codegen_arm64),
diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc
deleted file mode 100644
index d5cd59d..0000000
--- a/compiler/utils/arm/assembler_arm.cc
+++ /dev/null
@@ -1,453 +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 "assembler_arm.h"
-
-#include <algorithm>
-
-#include "base/bit_utils.h"
-#include "base/logging.h"
-#include "entrypoints/quick/quick_entrypoints.h"
-#include "offsets.h"
-#include "thread.h"
-
-namespace art {
-namespace arm {
-
-const char* kRegisterNames[] = {
-  "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10",
-  "fp", "ip", "sp", "lr", "pc"
-};
-
-const char* kConditionNames[] = {
-  "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC", "HI", "LS", "GE", "LT", "GT",
-  "LE", "AL",
-};
-
-std::ostream& operator<<(std::ostream& os, const Register& rhs) {
-  if (rhs >= R0 && rhs <= PC) {
-    os << kRegisterNames[rhs];
-  } else {
-    os << "Register[" << static_cast<int>(rhs) << "]";
-  }
-  return os;
-}
-
-
-std::ostream& operator<<(std::ostream& os, const SRegister& rhs) {
-  if (rhs >= S0 && rhs < kNumberOfSRegisters) {
-    os << "s" << static_cast<int>(rhs);
-  } else {
-    os << "SRegister[" << static_cast<int>(rhs) << "]";
-  }
-  return os;
-}
-
-
-std::ostream& operator<<(std::ostream& os, const DRegister& rhs) {
-  if (rhs >= D0 && rhs < kNumberOfDRegisters) {
-    os << "d" << static_cast<int>(rhs);
-  } else {
-    os << "DRegister[" << static_cast<int>(rhs) << "]";
-  }
-  return os;
-}
-
-std::ostream& operator<<(std::ostream& os, const Condition& rhs) {
-  if (rhs >= EQ && rhs <= AL) {
-    os << kConditionNames[rhs];
-  } else {
-    os << "Condition[" << static_cast<int>(rhs) << "]";
-  }
-  return os;
-}
-
-ShifterOperand::ShifterOperand(uint32_t immed)
-    : type_(kImmediate), rm_(kNoRegister), rs_(kNoRegister),
-      is_rotate_(false), is_shift_(false), shift_(kNoShift), rotate_(0), immed_(immed) {
-  CHECK(immed < (1u << 12) || ArmAssembler::ModifiedImmediate(immed) != kInvalidModifiedImmediate);
-}
-
-
-uint32_t ShifterOperand::encodingArm() const {
-  CHECK(is_valid());
-  switch (type_) {
-    case kImmediate:
-      if (is_rotate_) {
-        return (rotate_ << kRotateShift) | (immed_ << kImmed8Shift);
-      } else {
-        return immed_;
-      }
-    case kRegister:
-      if (is_shift_) {
-        uint32_t shift_type;
-        switch (shift_) {
-          case arm::Shift::ROR:
-            shift_type = static_cast<uint32_t>(shift_);
-            CHECK_NE(immed_, 0U);
-            break;
-          case arm::Shift::RRX:
-            shift_type = static_cast<uint32_t>(arm::Shift::ROR);  // Same encoding as ROR.
-            CHECK_EQ(immed_, 0U);
-            break;
-          default:
-            shift_type = static_cast<uint32_t>(shift_);
-        }
-        // Shifted immediate or register.
-        if (rs_ == kNoRegister) {
-          // Immediate shift.
-          return immed_ << kShiftImmShift |
-                          shift_type << kShiftShift |
-                          static_cast<uint32_t>(rm_);
-        } else {
-          // Register shift.
-          return static_cast<uint32_t>(rs_) << kShiftRegisterShift |
-              shift_type << kShiftShift | (1 << 4) |
-              static_cast<uint32_t>(rm_);
-        }
-      } else {
-        // Simple register
-        return static_cast<uint32_t>(rm_);
-      }
-    default:
-      // Can't get here.
-      LOG(FATAL) << "Invalid shifter operand for ARM";
-      return 0;
-  }
-}
-
-uint32_t ShifterOperand::encodingThumb() const {
-  switch (type_) {
-    case kImmediate:
-      return immed_;
-    case kRegister:
-      if (is_shift_) {
-        // Shifted immediate or register.
-        if (rs_ == kNoRegister) {
-          // Immediate shift.
-          if (shift_ == RRX) {
-            DCHECK_EQ(immed_, 0u);
-            // RRX is encoded as an ROR with imm 0.
-            return ROR << 4 | static_cast<uint32_t>(rm_);
-          } else {
-            DCHECK((1 <= immed_ && immed_ <= 31) ||
-                   (immed_ == 0u && shift_ == LSL) ||
-                   (immed_ == 32u && (shift_ == ASR || shift_ == LSR)));
-            uint32_t imm3 = (immed_ >> 2) & 7 /* 0b111*/;
-            uint32_t imm2 = immed_ & 3U /* 0b11 */;
-
-            return imm3 << 12 | imm2 << 6 | shift_ << 4 |
-                static_cast<uint32_t>(rm_);
-          }
-        } else {
-          LOG(FATAL) << "No register-shifted register instruction available in thumb";
-          return 0;
-        }
-      } else {
-        // Simple register
-        return static_cast<uint32_t>(rm_);
-      }
-    default:
-      // Can't get here.
-      LOG(FATAL) << "Invalid shifter operand for thumb";
-      UNREACHABLE();
-  }
-}
-
-uint32_t Address::encodingArm() const {
-  CHECK(IsAbsoluteUint<12>(offset_));
-  uint32_t encoding;
-  if (is_immed_offset_) {
-    if (offset_ < 0) {
-      encoding = (am_ ^ (1 << kUShift)) | -offset_;  // Flip U to adjust sign.
-    } else {
-      encoding =  am_ | offset_;
-    }
-  } else {
-    uint32_t shift = shift_;
-    if (shift == RRX) {
-      CHECK_EQ(offset_, 0);
-      shift = ROR;
-    }
-    encoding = am_ | static_cast<uint32_t>(rm_) | shift << 5 | offset_ << 7 | B25;
-  }
-  encoding |= static_cast<uint32_t>(rn_) << kRnShift;
-  return encoding;
-}
-
-
-uint32_t Address::encodingThumb(bool is_32bit) const {
-  uint32_t encoding = 0;
-  if (is_immed_offset_) {
-    encoding = static_cast<uint32_t>(rn_) << 16;
-    // Check for the T3/T4 encoding.
-    // PUW must Offset for T3
-    // Convert ARM PU0W to PUW
-    // The Mode is in ARM encoding format which is:
-    // |P|U|0|W|
-    // we need this in thumb2 mode:
-    // |P|U|W|
-
-    uint32_t am = am_;
-    int32_t offset = offset_;
-    if (offset < 0) {
-      am ^= 1 << kUShift;
-      offset = -offset;
-    }
-    if (offset_ < 0 || (offset >= 0 && offset < 256 &&
-        am_ != Mode::Offset)) {
-      // T4 encoding.
-      uint32_t PUW = am >> 21;   // Move down to bottom of word.
-      PUW = (PUW >> 1) | (PUW & 1);   // Bits 3, 2 and 0.
-      // If P is 0 then W must be 1 (Different from ARM).
-      if ((PUW & 4U /* 0b100 */) == 0) {
-        PUW |= 1U /* 0b1 */;
-      }
-      encoding |= B11 | PUW << 8 | offset;
-    } else {
-      // T3 encoding (also sets op1 to 0b01).
-      encoding |= B23 | offset_;
-    }
-  } else {
-    // Register offset, possibly shifted.
-    // Need to choose between encoding T1 (16 bit) or T2.
-    // Only Offset mode is supported.  Shift must be LSL and the count
-    // is only 2 bits.
-    CHECK_EQ(shift_, LSL);
-    CHECK_LE(offset_, 4);
-    CHECK_EQ(am_, Offset);
-    bool is_t2 = is_32bit;
-    if (ArmAssembler::IsHighRegister(rn_) || ArmAssembler::IsHighRegister(rm_)) {
-      is_t2 = true;
-    } else if (offset_ != 0) {
-      is_t2 = true;
-    }
-    if (is_t2) {
-      encoding = static_cast<uint32_t>(rn_) << 16 | static_cast<uint32_t>(rm_) |
-          offset_ << 4;
-    } else {
-      encoding = static_cast<uint32_t>(rn_) << 3 | static_cast<uint32_t>(rm_) << 6;
-    }
-  }
-  return encoding;
-}
-
-// This is very like the ARM encoding except the offset is 10 bits.
-uint32_t Address::encodingThumbLdrdStrd() const {
-  DCHECK(IsImmediate());
-  uint32_t encoding;
-  uint32_t am = am_;
-  // If P is 0 then W must be 1 (Different from ARM).
-  uint32_t PU1W = am_ >> 21;   // Move down to bottom of word.
-  if ((PU1W & 8U /* 0b1000 */) == 0) {
-    am |= 1 << 21;      // Set W bit.
-  }
-  if (offset_ < 0) {
-    int32_t off = -offset_;
-    CHECK_LT(off, 1024);
-    CHECK_ALIGNED(off, 4);
-    encoding = (am ^ (1 << kUShift)) | off >> 2;  // Flip U to adjust sign.
-  } else {
-    CHECK_LT(offset_, 1024);
-    CHECK_ALIGNED(offset_, 4);
-    encoding =  am | offset_ >> 2;
-  }
-  encoding |= static_cast<uint32_t>(rn_) << 16;
-  return encoding;
-}
-
-// Encoding for ARM addressing mode 3.
-uint32_t Address::encoding3() const {
-  const uint32_t offset_mask = (1 << 12) - 1;
-  uint32_t encoding = encodingArm();
-  uint32_t offset = encoding & offset_mask;
-  CHECK_LT(offset, 256u);
-  return (encoding & ~offset_mask) | ((offset & 0xf0) << 4) | (offset & 0xf);
-}
-
-// Encoding for vfp load/store addressing.
-uint32_t Address::vencoding() const {
-  CHECK(IsAbsoluteUint<10>(offset_));  // In the range -1020 to +1020.
-  CHECK_ALIGNED(offset_, 2);  // Multiple of 4.
-
-  const uint32_t offset_mask = (1 << 12) - 1;
-  uint32_t encoding = encodingArm();
-  uint32_t offset = encoding & offset_mask;
-  CHECK((am_ == Offset) || (am_ == NegOffset));
-  uint32_t vencoding_value = (encoding & (0xf << kRnShift)) | (offset >> 2);
-  if (am_ == Offset) {
-    vencoding_value |= 1 << 23;
-  }
-  return vencoding_value;
-}
-
-
-bool Address::CanHoldLoadOffsetArm(LoadOperandType type, int offset) {
-  switch (type) {
-    case kLoadSignedByte:
-    case kLoadSignedHalfword:
-    case kLoadUnsignedHalfword:
-    case kLoadWordPair:
-      return IsAbsoluteUint<8>(offset);  // Addressing mode 3.
-    case kLoadUnsignedByte:
-    case kLoadWord:
-      return IsAbsoluteUint<12>(offset);  // Addressing mode 2.
-    case kLoadSWord:
-    case kLoadDWord:
-      return IsAbsoluteUint<10>(offset);  // VFP addressing mode.
-    default:
-      LOG(FATAL) << "UNREACHABLE";
-      UNREACHABLE();
-  }
-}
-
-
-bool Address::CanHoldStoreOffsetArm(StoreOperandType type, int offset) {
-  switch (type) {
-    case kStoreHalfword:
-    case kStoreWordPair:
-      return IsAbsoluteUint<8>(offset);  // Addressing mode 3.
-    case kStoreByte:
-    case kStoreWord:
-      return IsAbsoluteUint<12>(offset);  // Addressing mode 2.
-    case kStoreSWord:
-    case kStoreDWord:
-      return IsAbsoluteUint<10>(offset);  // VFP addressing mode.
-    default:
-      LOG(FATAL) << "UNREACHABLE";
-      UNREACHABLE();
-  }
-}
-
-bool Address::CanHoldLoadOffsetThumb(LoadOperandType type, int offset) {
-  switch (type) {
-    case kLoadSignedByte:
-    case kLoadSignedHalfword:
-    case kLoadUnsignedHalfword:
-    case kLoadUnsignedByte:
-    case kLoadWord:
-      return IsAbsoluteUint<12>(offset);
-    case kLoadSWord:
-    case kLoadDWord:
-      return IsAbsoluteUint<10>(offset) && (offset & 3) == 0;  // VFP addressing mode.
-    case kLoadWordPair:
-      return IsAbsoluteUint<10>(offset) && (offset & 3) == 0;
-    default:
-      LOG(FATAL) << "UNREACHABLE";
-      UNREACHABLE();
-  }
-}
-
-
-bool Address::CanHoldStoreOffsetThumb(StoreOperandType type, int offset) {
-  switch (type) {
-    case kStoreHalfword:
-    case kStoreByte:
-    case kStoreWord:
-      return IsAbsoluteUint<12>(offset);
-    case kStoreSWord:
-    case kStoreDWord:
-      return IsAbsoluteUint<10>(offset) && (offset & 3) == 0;  // VFP addressing mode.
-    case kStoreWordPair:
-      return IsAbsoluteUint<10>(offset) && (offset & 3) == 0;
-    default:
-      LOG(FATAL) << "UNREACHABLE";
-      UNREACHABLE();
-  }
-}
-
-void ArmAssembler::Pad(uint32_t bytes) {
-  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
-  for (uint32_t i = 0; i < bytes; ++i) {
-    buffer_.Emit<uint8_t>(0);
-  }
-}
-
-static int LeadingZeros(uint32_t val) {
-  uint32_t alt;
-  int32_t n;
-  int32_t count;
-
-  count = 16;
-  n = 32;
-  do {
-    alt = val >> count;
-    if (alt != 0) {
-      n = n - count;
-      val = alt;
-    }
-    count >>= 1;
-  } while (count);
-  return n - val;
-}
-
-
-uint32_t ArmAssembler::ModifiedImmediate(uint32_t value) {
-  int32_t z_leading;
-  int32_t z_trailing;
-  uint32_t b0 = value & 0xff;
-
-  /* Note: case of value==0 must use 0:000:0:0000000 encoding */
-  if (value <= 0xFF)
-    return b0;  // 0:000:a:bcdefgh.
-  if (value == ((b0 << 16) | b0))
-    return (0x1 << 12) | b0; /* 0:001:a:bcdefgh */
-  if (value == ((b0 << 24) | (b0 << 16) | (b0 << 8) | b0))
-    return (0x3 << 12) | b0; /* 0:011:a:bcdefgh */
-  b0 = (value >> 8) & 0xff;
-  if (value == ((b0 << 24) | (b0 << 8)))
-    return (0x2 << 12) | b0; /* 0:010:a:bcdefgh */
-  /* Can we do it with rotation? */
-  z_leading = LeadingZeros(value);
-  z_trailing = 32 - LeadingZeros(~value & (value - 1));
-  /* A run of eight or fewer active bits? */
-  if ((z_leading + z_trailing) < 24)
-    return kInvalidModifiedImmediate;  /* No - bail */
-  /* left-justify the constant, discarding msb (known to be 1) */
-  value <<= z_leading + 1;
-  /* Create bcdefgh */
-  value >>= 25;
-
-  /* Put it all together */
-  uint32_t v = 8 + z_leading;
-
-  uint32_t i = (v & 16U /* 0b10000 */) >> 4;
-  uint32_t imm3 = (v >> 1) & 7U /* 0b111 */;
-  uint32_t a = v & 1;
-  return value | i << 26 | imm3 << 12 | a << 7;
-}
-
-void ArmAssembler::FinalizeTrackedLabels() {
-  if (!tracked_labels_.empty()) {
-    // This array should be sorted, as assembly is generated in linearized order. It isn't
-    // technically required, but GetAdjustedPosition() used in AdjustLabelPosition() can take
-    // advantage of it. So ensure that it's actually the case.
-    DCHECK(std::is_sorted(
-        tracked_labels_.begin(),
-        tracked_labels_.end(),
-        [](const Label* lhs, const Label* rhs) { return lhs->Position() < rhs->Position(); }));
-
-    Label* last_label = nullptr;  // Track duplicates, we must not adjust twice.
-    for (Label* label : tracked_labels_) {
-      DCHECK_NE(label, last_label);
-      AdjustLabelPosition(label);
-      last_label = label;
-    }
-  }
-}
-
-}  // namespace arm
-}  // namespace art
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
deleted file mode 100644
index bb23a29..0000000
--- a/compiler/utils/arm/assembler_arm.h
+++ /dev/null
@@ -1,942 +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_COMPILER_UTILS_ARM_ASSEMBLER_ARM_H_
-#define ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_H_
-
-#include <type_traits>
-#include <vector>
-
-#include "base/arena_allocator.h"
-#include "base/arena_containers.h"
-#include "base/bit_utils.h"
-#include "base/enums.h"
-#include "base/logging.h"
-#include "base/stl_util_identity.h"
-#include "base/value_object.h"
-#include "constants_arm.h"
-#include "utils/arm/assembler_arm_shared.h"
-#include "utils/arm/managed_register_arm.h"
-#include "utils/assembler.h"
-#include "utils/jni_macro_assembler.h"
-#include "offsets.h"
-
-namespace art {
-namespace arm {
-
-class Thumb2Assembler;
-
-// 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_;
-  }
-
-  Label* GetLabel() {
-    return &label_;
-  }
-
-  const Label* GetLabel() const {
-    return &label_;
-  }
-
- private:
-  Label label_;
-  const uint32_t size_;
-  uint8_t data_[kMaxSize];
-
-  DISALLOW_COPY_AND_ASSIGN(Literal);
-};
-
-// Jump table: table of labels emitted after the literals. Similar to literals.
-class JumpTable {
- public:
-  explicit JumpTable(std::vector<Label*>&& labels)
-      : label_(), anchor_label_(), labels_(std::move(labels)) {
-  }
-
-  uint32_t GetSize() const {
-    return static_cast<uint32_t>(labels_.size()) * sizeof(uint32_t);
-  }
-
-  const std::vector<Label*>& GetData() const {
-    return labels_;
-  }
-
-  Label* GetLabel() {
-    return &label_;
-  }
-
-  const Label* GetLabel() const {
-    return &label_;
-  }
-
-  Label* GetAnchorLabel() {
-    return &anchor_label_;
-  }
-
-  const Label* GetAnchorLabel() const {
-    return &anchor_label_;
-  }
-
- private:
-  Label label_;
-  Label anchor_label_;
-  std::vector<Label*> labels_;
-
-  DISALLOW_COPY_AND_ASSIGN(JumpTable);
-};
-
-class ShifterOperand {
- public:
-  ShifterOperand() : type_(kUnknown), rm_(kNoRegister), rs_(kNoRegister),
-      is_rotate_(false), is_shift_(false), shift_(kNoShift), rotate_(0), immed_(0) {
-  }
-
-  explicit ShifterOperand(uint32_t immed);
-
-  // Data-processing operands - Register
-  explicit ShifterOperand(Register rm) : type_(kRegister), rm_(rm), rs_(kNoRegister),
-      is_rotate_(false), is_shift_(false), shift_(kNoShift), rotate_(0), immed_(0) {
-  }
-
-  ShifterOperand(uint32_t rotate, uint32_t immed8) : type_(kImmediate), rm_(kNoRegister),
-      rs_(kNoRegister),
-      is_rotate_(true), is_shift_(false), shift_(kNoShift), rotate_(rotate), immed_(immed8) {
-  }
-
-  ShifterOperand(Register rm, Shift shift, uint32_t shift_imm = 0) : type_(kRegister), rm_(rm),
-      rs_(kNoRegister),
-      is_rotate_(false), is_shift_(true), shift_(shift), rotate_(0), immed_(shift_imm) {
-  }
-
-  // Data-processing operands - Logical shift/rotate by register
-  ShifterOperand(Register rm, Shift shift, Register rs)  : type_(kRegister), rm_(rm),
-      rs_(rs),
-      is_rotate_(false), is_shift_(true), shift_(shift), rotate_(0), immed_(0) {
-  }
-
-  bool is_valid() const { return (type_ == kImmediate) || (type_ == kRegister); }
-
-  uint32_t type() const {
-    CHECK(is_valid());
-    return type_;
-  }
-
-  uint32_t encodingArm() const;
-  uint32_t encodingThumb() const;
-
-  bool IsEmpty() const {
-    return type_ == kUnknown;
-  }
-
-  bool IsImmediate() const {
-    return type_ == kImmediate;
-  }
-
-  bool IsRegister() const {
-    return type_ == kRegister;
-  }
-
-  bool IsShift() const {
-    return is_shift_;
-  }
-
-  uint32_t GetImmediate() const {
-    return immed_;
-  }
-
-  Shift GetShift() const {
-    return shift_;
-  }
-
-  Register GetRegister() const {
-    return rm_;
-  }
-
-  Register GetSecondRegister() const {
-    return rs_;
-  }
-
-  enum Type {
-    kUnknown = -1,
-    kRegister,
-    kImmediate
-  };
-
- private:
-  Type type_;
-  Register rm_;
-  Register rs_;
-  bool is_rotate_;
-  bool is_shift_;
-  Shift shift_;
-  uint32_t rotate_;
-  uint32_t immed_;
-
-  friend class Thumb2Assembler;
-
-#ifdef SOURCE_ASSEMBLER_SUPPORT
-  friend class BinaryAssembler;
-#endif
-};
-
-// Load/store multiple addressing mode.
-enum BlockAddressMode {
-  // bit encoding P U W
-  DA           = (0|0|0) << 21,  // decrement after
-  IA           = (0|4|0) << 21,  // increment after
-  DB           = (8|0|0) << 21,  // decrement before
-  IB           = (8|4|0) << 21,  // increment before
-  DA_W         = (0|0|1) << 21,  // decrement after with writeback to base
-  IA_W         = (0|4|1) << 21,  // increment after with writeback to base
-  DB_W         = (8|0|1) << 21,  // decrement before with writeback to base
-  IB_W         = (8|4|1) << 21   // increment before with writeback to base
-};
-inline std::ostream& operator<<(std::ostream& os, const BlockAddressMode& rhs) {
-  os << static_cast<int>(rhs);
-  return os;
-}
-
-class Address : public ValueObject {
- public:
-  // Memory operand addressing mode (in ARM encoding form.  For others we need
-  // to adjust)
-  enum Mode {
-    // bit encoding P U W
-    Offset       = (8|4|0) << 21,  // offset (w/o writeback to base)
-    PreIndex     = (8|4|1) << 21,  // pre-indexed addressing with writeback
-    PostIndex    = (0|4|0) << 21,  // post-indexed addressing with writeback
-    NegOffset    = (8|0|0) << 21,  // negative offset (w/o writeback to base)
-    NegPreIndex  = (8|0|1) << 21,  // negative pre-indexed with writeback
-    NegPostIndex = (0|0|0) << 21   // negative post-indexed with writeback
-  };
-
-  explicit Address(Register rn, int32_t offset = 0, Mode am = Offset) : rn_(rn), rm_(R0),
-      offset_(offset),
-      am_(am), is_immed_offset_(true), shift_(LSL) {
-  }
-
-  Address(Register rn, Register rm, Mode am = Offset) : rn_(rn), rm_(rm), offset_(0),
-      am_(am), is_immed_offset_(false), shift_(LSL) {
-    CHECK_NE(rm, PC);
-  }
-
-  Address(Register rn, Register rm, Shift shift, uint32_t count, Mode am = Offset) :
-                       rn_(rn), rm_(rm), offset_(count),
-                       am_(am), is_immed_offset_(false), shift_(shift) {
-    CHECK_NE(rm, PC);
-  }
-
-  static bool CanHoldLoadOffsetArm(LoadOperandType type, int offset);
-  static bool CanHoldStoreOffsetArm(StoreOperandType type, int offset);
-
-  static bool CanHoldLoadOffsetThumb(LoadOperandType type, int offset);
-  static bool CanHoldStoreOffsetThumb(StoreOperandType type, int offset);
-
-  uint32_t encodingArm() const;
-  uint32_t encodingThumb(bool is_32bit) const;
-
-  uint32_t encoding3() const;
-  uint32_t vencoding() const;
-
-  uint32_t encodingThumbLdrdStrd() const;
-
-  Register GetRegister() const {
-    return rn_;
-  }
-
-  Register GetRegisterOffset() const {
-    return rm_;
-  }
-
-  int32_t GetOffset() const {
-    return offset_;
-  }
-
-  Mode GetMode() const {
-    return am_;
-  }
-
-  bool IsImmediate() const {
-    return is_immed_offset_;
-  }
-
-  Shift GetShift() const {
-    return shift_;
-  }
-
-  int32_t GetShiftCount() const {
-    CHECK(!is_immed_offset_);
-    return offset_;
-  }
-
- private:
-  const Register rn_;
-  const Register rm_;
-  const int32_t offset_;      // Used as shift amount for register offset.
-  const Mode am_;
-  const bool is_immed_offset_;
-  const Shift shift_;
-};
-inline std::ostream& operator<<(std::ostream& os, const Address::Mode& rhs) {
-  os << static_cast<int>(rhs);
-  return os;
-}
-
-// Instruction encoding bits.
-enum {
-  H   = 1 << 5,   // halfword (or byte)
-  L   = 1 << 20,  // load (or store)
-  S   = 1 << 20,  // set condition code (or leave unchanged)
-  W   = 1 << 21,  // writeback base register (or leave unchanged)
-  A   = 1 << 21,  // accumulate in multiply instruction (or not)
-  B   = 1 << 22,  // unsigned byte (or word)
-  N   = 1 << 22,  // long (or short)
-  U   = 1 << 23,  // positive (or negative) offset/index
-  P   = 1 << 24,  // offset/pre-indexed addressing (or post-indexed addressing)
-  I   = 1 << 25,  // immediate shifter operand (or not)
-
-  B0 = 1,
-  B1 = 1 << 1,
-  B2 = 1 << 2,
-  B3 = 1 << 3,
-  B4 = 1 << 4,
-  B5 = 1 << 5,
-  B6 = 1 << 6,
-  B7 = 1 << 7,
-  B8 = 1 << 8,
-  B9 = 1 << 9,
-  B10 = 1 << 10,
-  B11 = 1 << 11,
-  B12 = 1 << 12,
-  B13 = 1 << 13,
-  B14 = 1 << 14,
-  B15 = 1 << 15,
-  B16 = 1 << 16,
-  B17 = 1 << 17,
-  B18 = 1 << 18,
-  B19 = 1 << 19,
-  B20 = 1 << 20,
-  B21 = 1 << 21,
-  B22 = 1 << 22,
-  B23 = 1 << 23,
-  B24 = 1 << 24,
-  B25 = 1 << 25,
-  B26 = 1 << 26,
-  B27 = 1 << 27,
-  B28 = 1 << 28,
-  B29 = 1 << 29,
-  B30 = 1 << 30,
-  B31 = 1 << 31,
-
-  // Instruction bit masks.
-  RdMask = 15 << 12,  // in str instruction
-  CondMask = 15 << 28,
-  CoprocessorMask = 15 << 8,
-  OpCodeMask = 15 << 21,  // in data-processing instructions
-  Imm24Mask = (1 << 24) - 1,
-  Off12Mask = (1 << 12) - 1,
-
-  // ldrex/strex register field encodings.
-  kLdExRnShift = 16,
-  kLdExRtShift = 12,
-  kStrExRnShift = 16,
-  kStrExRdShift = 12,
-  kStrExRtShift = 0,
-};
-
-// IfThen state for IT instructions.
-enum ItState {
-  kItOmitted,
-  kItThen,
-  kItT = kItThen,
-  kItElse,
-  kItE = kItElse
-};
-
-constexpr uint32_t kNoItCondition = 3;
-constexpr uint32_t kInvalidModifiedImmediate = -1;
-
-extern const char* kRegisterNames[];
-extern const char* kConditionNames[];
-
-// This is an abstract ARM assembler.  Subclasses provide assemblers for the individual
-// instruction sets (ARM32, Thumb2, etc.)
-//
-class ArmAssembler : public Assembler {
- public:
-  virtual ~ArmAssembler() {}
-
-  // Is this assembler for the thumb instruction set?
-  virtual bool IsThumb() const = 0;
-
-  // Data-processing instructions.
-  virtual void and_(Register rd, Register rn, const ShifterOperand& so,
-                    Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  virtual void ands(Register rd, Register rn, const ShifterOperand& so, Condition cond = AL) {
-    and_(rd, rn, so, cond, kCcSet);
-  }
-
-  virtual void eor(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  virtual void eors(Register rd, Register rn, const ShifterOperand& so, Condition cond = AL) {
-    eor(rd, rn, so, cond, kCcSet);
-  }
-
-  virtual void sub(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  virtual void subs(Register rd, Register rn, const ShifterOperand& so, Condition cond = AL) {
-    sub(rd, rn, so, cond, kCcSet);
-  }
-
-  virtual void rsb(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  virtual void rsbs(Register rd, Register rn, const ShifterOperand& so, Condition cond = AL) {
-    rsb(rd, rn, so, cond, kCcSet);
-  }
-
-  virtual void add(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  virtual void adds(Register rd, Register rn, const ShifterOperand& so, Condition cond = AL) {
-    add(rd, rn, so, cond, kCcSet);
-  }
-
-  virtual void adc(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  virtual void adcs(Register rd, Register rn, const ShifterOperand& so, Condition cond = AL) {
-    adc(rd, rn, so, cond, kCcSet);
-  }
-
-  virtual void sbc(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  virtual void sbcs(Register rd, Register rn, const ShifterOperand& so, Condition cond = AL) {
-    sbc(rd, rn, so, cond, kCcSet);
-  }
-
-  virtual void rsc(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  virtual void rscs(Register rd, Register rn, const ShifterOperand& so, Condition cond = AL) {
-    rsc(rd, rn, so, cond, kCcSet);
-  }
-
-  virtual void tst(Register rn, const ShifterOperand& so, Condition cond = AL) = 0;
-
-  virtual void teq(Register rn, const ShifterOperand& so, Condition cond = AL) = 0;
-
-  virtual void cmp(Register rn, const ShifterOperand& so, Condition cond = AL) = 0;
-
-  // Note: CMN updates flags based on addition of its operands. Do not confuse
-  // the "N" suffix with bitwise inversion performed by MVN.
-  virtual void cmn(Register rn, const ShifterOperand& so, Condition cond = AL) = 0;
-
-  virtual void orr(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  virtual void orrs(Register rd, Register rn, const ShifterOperand& so, Condition cond = AL) {
-    orr(rd, rn, so, cond, kCcSet);
-  }
-
-  virtual void orn(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  virtual void orns(Register rd, Register rn, const ShifterOperand& so, Condition cond = AL) {
-    orn(rd, rn, so, cond, kCcSet);
-  }
-
-  virtual void mov(Register rd, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  virtual void movs(Register rd, const ShifterOperand& so, Condition cond = AL) {
-    mov(rd, so, cond, kCcSet);
-  }
-
-  virtual void bic(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  virtual void bics(Register rd, Register rn, const ShifterOperand& so, Condition cond = AL) {
-    bic(rd, rn, so, cond, kCcSet);
-  }
-
-  virtual void mvn(Register rd, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  virtual void mvns(Register rd, const ShifterOperand& so, Condition cond = AL) {
-    mvn(rd, so, cond, kCcSet);
-  }
-
-  // Miscellaneous data-processing instructions.
-  virtual void clz(Register rd, Register rm, Condition cond = AL) = 0;
-  virtual void movw(Register rd, uint16_t imm16, Condition cond = AL) = 0;
-  virtual void movt(Register rd, uint16_t imm16, Condition cond = AL) = 0;
-  virtual void rbit(Register rd, Register rm, Condition cond = AL) = 0;
-  virtual void rev(Register rd, Register rm, Condition cond = AL) = 0;
-  virtual void rev16(Register rd, Register rm, Condition cond = AL) = 0;
-  virtual void revsh(Register rd, Register rm, Condition cond = AL) = 0;
-
-  // Multiply instructions.
-  virtual void mul(Register rd, Register rn, Register rm, Condition cond = AL) = 0;
-  virtual void mla(Register rd, Register rn, Register rm, Register ra,
-                   Condition cond = AL) = 0;
-  virtual void mls(Register rd, Register rn, Register rm, Register ra,
-                   Condition cond = AL) = 0;
-  virtual void smull(Register rd_lo, Register rd_hi, Register rn, Register rm,
-                     Condition cond = AL) = 0;
-  virtual void umull(Register rd_lo, Register rd_hi, Register rn, Register rm,
-                     Condition cond = AL) = 0;
-
-  virtual void sdiv(Register rd, Register rn, Register rm, Condition cond = AL) = 0;
-  virtual void udiv(Register rd, Register rn, Register rm, Condition cond = AL) = 0;
-
-  // Bit field extract instructions.
-  virtual void sbfx(Register rd, Register rn, uint32_t lsb, uint32_t width,
-                    Condition cond = AL) = 0;
-  virtual void ubfx(Register rd, Register rn, uint32_t lsb, uint32_t width,
-                    Condition cond = AL) = 0;
-
-  // Load/store instructions.
-  virtual void ldr(Register rd, const Address& ad, Condition cond = AL) = 0;
-  virtual void str(Register rd, const Address& ad, Condition cond = AL) = 0;
-
-  virtual void ldrb(Register rd, const Address& ad, Condition cond = AL) = 0;
-  virtual void strb(Register rd, const Address& ad, Condition cond = AL) = 0;
-
-  virtual void ldrh(Register rd, const Address& ad, Condition cond = AL) = 0;
-  virtual void strh(Register rd, const Address& ad, Condition cond = AL) = 0;
-
-  virtual void ldrsb(Register rd, const Address& ad, Condition cond = AL) = 0;
-  virtual void ldrsh(Register rd, const Address& ad, Condition cond = AL) = 0;
-
-  virtual void ldrd(Register rd, const Address& ad, Condition cond = AL) = 0;
-  virtual void strd(Register rd, const Address& ad, Condition cond = AL) = 0;
-
-  virtual void ldm(BlockAddressMode am, Register base,
-                   RegList regs, Condition cond = AL) = 0;
-  virtual void stm(BlockAddressMode am, Register base,
-                   RegList regs, Condition cond = AL) = 0;
-
-  virtual void ldrex(Register rd, Register rn, Condition cond = AL) = 0;
-  virtual void strex(Register rd, Register rt, Register rn, Condition cond = AL) = 0;
-  virtual void ldrexd(Register rt, Register rt2, Register rn, Condition cond = AL) = 0;
-  virtual void strexd(Register rd, Register rt, Register rt2, Register rn, Condition cond = AL) = 0;
-
-  // Miscellaneous instructions.
-  virtual void clrex(Condition cond = AL) = 0;
-  virtual void nop(Condition cond = AL) = 0;
-
-  // Note that gdb sets breakpoints using the undefined instruction 0xe7f001f0.
-  virtual void bkpt(uint16_t imm16) = 0;
-  virtual void svc(uint32_t imm24) = 0;
-
-  virtual void it(Condition firstcond ATTRIBUTE_UNUSED,
-                  ItState i1 ATTRIBUTE_UNUSED = kItOmitted,
-                  ItState i2 ATTRIBUTE_UNUSED = kItOmitted,
-                  ItState i3 ATTRIBUTE_UNUSED = kItOmitted) {
-    // Ignored if not supported.
-  }
-
-  virtual void cbz(Register rn, Label* target) = 0;
-  virtual void cbnz(Register rn, Label* target) = 0;
-
-  // Floating point instructions (VFPv3-D16 and VFPv3-D32 profiles).
-  virtual void vmovsr(SRegister sn, Register rt, Condition cond = AL) = 0;
-  virtual void vmovrs(Register rt, SRegister sn, Condition cond = AL) = 0;
-  virtual void vmovsrr(SRegister sm, Register rt, Register rt2, Condition cond = AL) = 0;
-  virtual void vmovrrs(Register rt, Register rt2, SRegister sm, Condition cond = AL) = 0;
-  virtual void vmovdrr(DRegister dm, Register rt, Register rt2, Condition cond = AL) = 0;
-  virtual void vmovrrd(Register rt, Register rt2, DRegister dm, Condition cond = AL) = 0;
-  virtual void vmovs(SRegister sd, SRegister sm, Condition cond = AL) = 0;
-  virtual void vmovd(DRegister dd, DRegister dm, Condition cond = AL) = 0;
-
-  // Returns false if the immediate cannot be encoded.
-  virtual bool vmovs(SRegister sd, float s_imm, Condition cond = AL) = 0;
-  virtual bool vmovd(DRegister dd, double d_imm, Condition cond = AL) = 0;
-
-  virtual void vldrs(SRegister sd, const Address& ad, Condition cond = AL) = 0;
-  virtual void vstrs(SRegister sd, const Address& ad, Condition cond = AL) = 0;
-  virtual void vldrd(DRegister dd, const Address& ad, Condition cond = AL) = 0;
-  virtual void vstrd(DRegister dd, const Address& ad, Condition cond = AL) = 0;
-
-  virtual void vadds(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL) = 0;
-  virtual void vaddd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL) = 0;
-  virtual void vsubs(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL) = 0;
-  virtual void vsubd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL) = 0;
-  virtual void vmuls(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL) = 0;
-  virtual void vmuld(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL) = 0;
-  virtual void vmlas(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL) = 0;
-  virtual void vmlad(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL) = 0;
-  virtual void vmlss(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL) = 0;
-  virtual void vmlsd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL) = 0;
-  virtual void vdivs(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL) = 0;
-  virtual void vdivd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL) = 0;
-
-  virtual void vabss(SRegister sd, SRegister sm, Condition cond = AL) = 0;
-  virtual void vabsd(DRegister dd, DRegister dm, Condition cond = AL) = 0;
-  virtual void vnegs(SRegister sd, SRegister sm, Condition cond = AL) = 0;
-  virtual void vnegd(DRegister dd, DRegister dm, Condition cond = AL) = 0;
-  virtual void vsqrts(SRegister sd, SRegister sm, Condition cond = AL) = 0;
-  virtual void vsqrtd(DRegister dd, DRegister dm, Condition cond = AL) = 0;
-
-  virtual void vcvtsd(SRegister sd, DRegister dm, Condition cond = AL) = 0;
-  virtual void vcvtds(DRegister dd, SRegister sm, Condition cond = AL) = 0;
-  virtual void vcvtis(SRegister sd, SRegister sm, Condition cond = AL) = 0;
-  virtual void vcvtid(SRegister sd, DRegister dm, Condition cond = AL) = 0;
-  virtual void vcvtsi(SRegister sd, SRegister sm, Condition cond = AL) = 0;
-  virtual void vcvtdi(DRegister dd, SRegister sm, Condition cond = AL) = 0;
-  virtual void vcvtus(SRegister sd, SRegister sm, Condition cond = AL) = 0;
-  virtual void vcvtud(SRegister sd, DRegister dm, Condition cond = AL) = 0;
-  virtual void vcvtsu(SRegister sd, SRegister sm, Condition cond = AL) = 0;
-  virtual void vcvtdu(DRegister dd, SRegister sm, Condition cond = AL) = 0;
-
-  virtual void vcmps(SRegister sd, SRegister sm, Condition cond = AL) = 0;
-  virtual void vcmpd(DRegister dd, DRegister dm, Condition cond = AL) = 0;
-  virtual void vcmpsz(SRegister sd, Condition cond = AL) = 0;
-  virtual void vcmpdz(DRegister dd, Condition cond = AL) = 0;
-  virtual void vmstat(Condition cond = AL) = 0;  // VMRS APSR_nzcv, FPSCR
-
-  virtual void vcntd(DRegister dd, DRegister dm) = 0;
-  virtual void vpaddld(DRegister dd, DRegister dm, int32_t size, bool is_unsigned) = 0;
-
-  virtual void vpushs(SRegister reg, int nregs, Condition cond = AL) = 0;
-  virtual void vpushd(DRegister reg, int nregs, Condition cond = AL) = 0;
-  virtual void vpops(SRegister reg, int nregs, Condition cond = AL) = 0;
-  virtual void vpopd(DRegister reg, int nregs, Condition cond = AL) = 0;
-  virtual void vldmiad(Register base_reg, DRegister reg, int nregs, Condition cond = AL) = 0;
-  virtual void vstmiad(Register base_reg, DRegister reg, int nregs, Condition cond = AL) = 0;
-
-  // Branch instructions.
-  virtual void b(Label* label, Condition cond = AL) = 0;
-  virtual void bl(Label* label, Condition cond = AL) = 0;
-  virtual void blx(Register rm, Condition cond = AL) = 0;
-  virtual void bx(Register rm, Condition cond = AL) = 0;
-
-  // ADR instruction loading register for branching to the label.
-  virtual void AdrCode(Register rt, Label* label) = 0;
-
-  // Memory barriers.
-  virtual void dmb(DmbOptions flavor) = 0;
-
-  void Pad(uint32_t bytes);
-
-  // Adjust label position.
-  void AdjustLabelPosition(Label* label) {
-    DCHECK(label->IsBound());
-    uint32_t old_position = static_cast<uint32_t>(label->Position());
-    uint32_t new_position = GetAdjustedPosition(old_position);
-    label->Reinitialize();
-    DCHECK_GE(static_cast<int>(new_position), 0);
-    label->BindTo(static_cast<int>(new_position));
-  }
-
-  // Get the final position of a label after local fixup based on the old position
-  // recorded before FinalizeCode().
-  virtual uint32_t GetAdjustedPosition(uint32_t old_position) = 0;
-
-  // Macros.
-  // Most of these are pure virtual as they need to be implemented per instruction set.
-
-  // 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));
-  }
-
-  // Create a new literal with the given data.
-  virtual Literal* NewLiteral(size_t size, const uint8_t* data) = 0;
-
-  // Load literal.
-  virtual void LoadLiteral(Register rt, Literal* literal) = 0;
-  virtual void LoadLiteral(Register rt, Register rt2, Literal* literal) = 0;
-  virtual void LoadLiteral(SRegister sd, Literal* literal) = 0;
-  virtual void LoadLiteral(DRegister dd, Literal* literal) = 0;
-
-  // Add signed constant value to rd. May clobber IP.
-  virtual void AddConstant(Register rd, Register rn, int32_t value,
-                           Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-  void AddConstantSetFlags(Register rd, Register rn, int32_t value, Condition cond = AL) {
-    AddConstant(rd, rn, value, cond, kCcSet);
-  }
-  void AddConstant(Register rd, int32_t value, Condition cond = AL, SetCc set_cc = kCcDontCare) {
-    AddConstant(rd, rd, value, cond, set_cc);
-  }
-
-  virtual void CmpConstant(Register rn, int32_t value, Condition cond = AL) = 0;
-
-  // Load and Store. May clobber IP.
-  virtual void LoadImmediate(Register rd, int32_t value, Condition cond = AL) = 0;
-  void LoadSImmediate(SRegister sd, float value, Condition cond = AL) {
-    if (!vmovs(sd, value, cond)) {
-      int32_t int_value = bit_cast<int32_t, float>(value);
-      if (int_value == bit_cast<int32_t, float>(0.0f)) {
-        // 0.0 is quite common, so we special case it by loading
-        // 2.0 in `sd` and then substracting it.
-        bool success = vmovs(sd, 2.0, cond);
-        CHECK(success);
-        vsubs(sd, sd, sd, cond);
-      } else {
-        LoadImmediate(IP, int_value, cond);
-        vmovsr(sd, IP, cond);
-      }
-    }
-  }
-
-  virtual void LoadDImmediate(DRegister dd, double value, Condition cond = AL) = 0;
-
-  virtual void MarkExceptionHandler(Label* label) = 0;
-  virtual void LoadFromOffset(LoadOperandType type,
-                              Register reg,
-                              Register base,
-                              int32_t offset,
-                              Condition cond = AL) = 0;
-  virtual void StoreToOffset(StoreOperandType type,
-                             Register reg,
-                             Register base,
-                             int32_t offset,
-                             Condition cond = AL) = 0;
-  virtual void LoadSFromOffset(SRegister reg,
-                               Register base,
-                               int32_t offset,
-                               Condition cond = AL) = 0;
-  virtual void StoreSToOffset(SRegister reg,
-                              Register base,
-                              int32_t offset,
-                              Condition cond = AL) = 0;
-  virtual void LoadDFromOffset(DRegister reg,
-                               Register base,
-                               int32_t offset,
-                               Condition cond = AL) = 0;
-  virtual void StoreDToOffset(DRegister reg,
-                              Register base,
-                              int32_t offset,
-                              Condition cond = AL) = 0;
-
-  virtual void Push(Register rd, Condition cond = AL) = 0;
-  virtual void Pop(Register rd, Condition cond = AL) = 0;
-
-  virtual void PushList(RegList regs, Condition cond = AL) = 0;
-  virtual void PopList(RegList regs, Condition cond = AL) = 0;
-
-  virtual void StoreList(RegList regs, size_t stack_offset) = 0;
-  virtual void LoadList(RegList regs, size_t stack_offset) = 0;
-
-  virtual void Mov(Register rd, Register rm, Condition cond = AL) = 0;
-
-  // Convenience shift instructions. Use mov instruction with shifter operand
-  // for variants setting the status flags or using a register shift count.
-  virtual void Lsl(Register rd, Register rm, uint32_t shift_imm,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  void Lsls(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL) {
-    Lsl(rd, rm, shift_imm, cond, kCcSet);
-  }
-
-  virtual void Lsr(Register rd, Register rm, uint32_t shift_imm,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  void Lsrs(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL) {
-    Lsr(rd, rm, shift_imm, cond, kCcSet);
-  }
-
-  virtual void Asr(Register rd, Register rm, uint32_t shift_imm,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  void Asrs(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL) {
-    Asr(rd, rm, shift_imm, cond, kCcSet);
-  }
-
-  virtual void Ror(Register rd, Register rm, uint32_t shift_imm,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  void Rors(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL) {
-    Ror(rd, rm, shift_imm, cond, kCcSet);
-  }
-
-  virtual void Rrx(Register rd, Register rm,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  void Rrxs(Register rd, Register rm, Condition cond = AL) {
-    Rrx(rd, rm, cond, kCcSet);
-  }
-
-  virtual void Lsl(Register rd, Register rm, Register rn,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  void Lsls(Register rd, Register rm, Register rn, Condition cond = AL) {
-    Lsl(rd, rm, rn, cond, kCcSet);
-  }
-
-  virtual void Lsr(Register rd, Register rm, Register rn,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  void Lsrs(Register rd, Register rm, Register rn, Condition cond = AL) {
-    Lsr(rd, rm, rn, cond, kCcSet);
-  }
-
-  virtual void Asr(Register rd, Register rm, Register rn,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  void Asrs(Register rd, Register rm, Register rn, Condition cond = AL) {
-    Asr(rd, rm, rn, cond, kCcSet);
-  }
-
-  virtual void Ror(Register rd, Register rm, Register rn,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
-
-  void Rors(Register rd, Register rm, Register rn, Condition cond = AL) {
-    Ror(rd, rm, rn, cond, kCcSet);
-  }
-
-  // Returns whether the `immediate` can fit in a `ShifterOperand`. If yes,
-  // `shifter_op` contains the operand.
-  virtual bool ShifterOperandCanHold(Register rd,
-                                     Register rn,
-                                     Opcode opcode,
-                                     uint32_t immediate,
-                                     SetCc set_cc,
-                                     ShifterOperand* shifter_op) = 0;
-  bool ShifterOperandCanHold(Register rd,
-                             Register rn,
-                             Opcode opcode,
-                             uint32_t immediate,
-                             ShifterOperand* shifter_op) {
-    return ShifterOperandCanHold(rd, rn, opcode, immediate, kCcDontCare, shifter_op);
-  }
-
-  virtual bool ShifterOperandCanAlwaysHold(uint32_t immediate) = 0;
-
-  static bool IsInstructionForExceptionHandling(uintptr_t pc);
-
-  virtual void CompareAndBranchIfZero(Register r, Label* label) = 0;
-  virtual void CompareAndBranchIfNonZero(Register r, Label* label) = 0;
-
-  static uint32_t ModifiedImmediate(uint32_t value);
-
-  static bool IsLowRegister(Register r) {
-    return r < R8;
-  }
-
-  static bool IsHighRegister(Register r) {
-     return r >= R8;
-  }
-
-  //
-  // Heap poisoning.
-  //
-
-  // Poison a heap reference contained in `reg`.
-  void PoisonHeapReference(Register reg) {
-    // reg = -reg.
-    rsb(reg, reg, ShifterOperand(0));
-  }
-  // Unpoison a heap reference contained in `reg`.
-  void UnpoisonHeapReference(Register reg) {
-    // reg = -reg.
-    rsb(reg, reg, ShifterOperand(0));
-  }
-  // Poison a heap reference contained in `reg` if heap poisoning is enabled.
-  void MaybePoisonHeapReference(Register reg) {
-    if (kPoisonHeapReferences) {
-      PoisonHeapReference(reg);
-    }
-  }
-  // Unpoison a heap reference contained in `reg` if heap poisoning is enabled.
-  void MaybeUnpoisonHeapReference(Register reg) {
-    if (kPoisonHeapReferences) {
-      UnpoisonHeapReference(reg);
-    }
-  }
-
-  void Jump(Label* label) OVERRIDE {
-    b(label);
-  }
-
-  // Jump table support. This is split into three functions:
-  //
-  // * CreateJumpTable creates the internal metadata to track the jump targets, and emits code to
-  // load the base address of the jump table.
-  //
-  // * EmitJumpTableDispatch emits the code to actually jump, assuming that the right table value
-  // has been loaded into a register already.
-  //
-  // * FinalizeTables emits the jump table into the literal pool. This can only be called after the
-  // labels for the jump targets have been finalized.
-
-  // Create a jump table for the given labels that will be emitted when finalizing. Create a load
-  // sequence (or placeholder) that stores the base address into the given register. When the table
-  // is emitted, offsets will be relative to the location EmitJumpTableDispatch was called on (the
-  // anchor).
-  virtual JumpTable* CreateJumpTable(std::vector<Label*>&& labels, Register base_reg) = 0;
-
-  // Emit the jump-table jump, assuming that the right value was loaded into displacement_reg.
-  virtual void EmitJumpTableDispatch(JumpTable* jump_table, Register displacement_reg) = 0;
-
-  // Bind a Label that needs to be updated by the assembler in FinalizeCode() if its position
-  // changes due to branch/literal fixup.
-  void BindTrackedLabel(Label* label) {
-    Bind(label);
-    tracked_labels_.push_back(label);
-  }
-
- protected:
-  explicit ArmAssembler(ArenaAllocator* arena)
-      : Assembler(arena), tracked_labels_(arena->Adapter(kArenaAllocAssembler)) {}
-
-  // Returns whether or not the given register is used for passing parameters.
-  static int RegisterCompare(const Register* reg1, const Register* reg2) {
-    return *reg1 - *reg2;
-  }
-
-  void FinalizeTrackedLabels();
-
-  // Tracked labels. Use a vector, as we need to sort before adjusting.
-  ArenaVector<Label*> tracked_labels_;
-};
-
-}  // namespace arm
-}  // namespace art
-
-#endif  // ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_H_
diff --git a/compiler/utils/arm/assembler_arm_vixl.cc b/compiler/utils/arm/assembler_arm_vixl.cc
index eb3f870..af3b447 100644
--- a/compiler/utils/arm/assembler_arm_vixl.cc
+++ b/compiler/utils/arm/assembler_arm_vixl.cc
@@ -37,7 +37,10 @@
 #define ___   vixl_masm_.
 #endif
 
+// Thread register definition.
 extern const vixl32::Register tr(TR);
+// Marking register definition.
+extern const vixl32::Register mr(MR);
 
 void ArmVIXLAssembler::FinalizeCode() {
   vixl_masm_.FinalizeCode();
diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h
index e81e7675..66b22ea 100644
--- a/compiler/utils/arm/assembler_arm_vixl.h
+++ b/compiler/utils/arm/assembler_arm_vixl.h
@@ -241,6 +241,8 @@
 
 // Thread register declaration.
 extern const vixl32::Register tr;
+// Marking register declaration.
+extern const vixl32::Register mr;
 
 }  // namespace arm
 }  // namespace art
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
deleted file mode 100644
index abc36c6..0000000
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ /dev/null
@@ -1,4076 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <type_traits>
-
-#include "assembler_thumb2.h"
-
-#include "base/bit_utils.h"
-#include "base/logging.h"
-#include "entrypoints/quick/quick_entrypoints.h"
-#include "offsets.h"
-#include "thread.h"
-
-namespace art {
-namespace arm {
-
-template <typename Function>
-void Thumb2Assembler::Fixup::ForExpandableDependencies(Thumb2Assembler* assembler, Function fn) {
-  static_assert(
-      std::is_same<typename std::result_of<Function(FixupId, FixupId)>::type, void>::value,
-      "Incorrect signature for argument `fn`: expected (FixupId, FixupId) -> void");
-  Fixup* fixups = assembler->fixups_.data();
-  for (FixupId fixup_id = 0u, end_id = assembler->fixups_.size(); fixup_id != end_id; ++fixup_id) {
-    uint32_t target = fixups[fixup_id].target_;
-    if (target > fixups[fixup_id].location_) {
-      for (FixupId id = fixup_id + 1u; id != end_id && fixups[id].location_ < target; ++id) {
-        if (fixups[id].CanExpand()) {
-          fn(id, fixup_id);
-        }
-      }
-    } else {
-      for (FixupId id = fixup_id; id != 0u && fixups[id - 1u].location_ >= target; --id) {
-        if (fixups[id - 1u].CanExpand()) {
-          fn(id - 1u, fixup_id);
-        }
-      }
-    }
-  }
-}
-
-void Thumb2Assembler::Fixup::PrepareDependents(Thumb2Assembler* assembler) {
-  // For each Fixup, it's easy to find the Fixups that it depends on as they are either
-  // the following or the preceding Fixups until we find the target. However, for fixup
-  // adjustment we need the reverse lookup, i.e. what Fixups depend on a given Fixup.
-  // This function creates a compact representation of this relationship, where we have
-  // all the dependents in a single array and Fixups reference their ranges by start
-  // index and count. (Instead of having a per-fixup vector.)
-
-  // Count the number of dependents of each Fixup.
-  Fixup* fixups = assembler->fixups_.data();
-  ForExpandableDependencies(
-      assembler,
-      [fixups](FixupId dependency, FixupId dependent ATTRIBUTE_UNUSED) {
-        fixups[dependency].dependents_count_ += 1u;
-      });
-  // Assign index ranges in fixup_dependents_ to individual fixups. Record the end of the
-  // range in dependents_start_, we shall later decrement it as we fill in fixup_dependents_.
-  uint32_t number_of_dependents = 0u;
-  for (FixupId fixup_id = 0u, end_id = assembler->fixups_.size(); fixup_id != end_id; ++fixup_id) {
-    number_of_dependents += fixups[fixup_id].dependents_count_;
-    fixups[fixup_id].dependents_start_ = number_of_dependents;
-  }
-  if (number_of_dependents == 0u) {
-    return;
-  }
-  // Create and fill in the fixup_dependents_.
-  assembler->fixup_dependents_.resize(number_of_dependents);
-  FixupId* dependents = assembler->fixup_dependents_.data();
-  ForExpandableDependencies(
-      assembler,
-      [fixups, dependents](FixupId dependency, FixupId dependent) {
-        fixups[dependency].dependents_start_ -= 1u;
-        dependents[fixups[dependency].dependents_start_] = dependent;
-      });
-}
-
-void Thumb2Assembler::BindLabel(Label* label, uint32_t bound_pc) {
-  CHECK(!label->IsBound());
-
-  while (label->IsLinked()) {
-    FixupId fixup_id = label->Position();                     // The id for linked Fixup.
-    Fixup* fixup = GetFixup(fixup_id);                        // Get the Fixup at this id.
-    fixup->Resolve(bound_pc);                                 // Fixup can be resolved now.
-    uint32_t fixup_location = fixup->GetLocation();
-    uint16_t next = buffer_.Load<uint16_t>(fixup_location);   // Get next in chain.
-    buffer_.Store<int16_t>(fixup_location, 0);
-    label->position_ = next;                                  // Move to next.
-  }
-  label->BindTo(bound_pc);
-}
-
-uint32_t Thumb2Assembler::BindLiterals() {
-  // We don't add the padding here, that's done only after adjusting the Fixup sizes.
-  uint32_t code_size = buffer_.Size();
-  for (Literal& lit : literals_) {
-    Label* label = lit.GetLabel();
-    BindLabel(label, code_size);
-    code_size += lit.GetSize();
-  }
-  return code_size;
-}
-
-void Thumb2Assembler::BindJumpTables(uint32_t code_size) {
-  for (JumpTable& table : jump_tables_) {
-    Label* label = table.GetLabel();
-    BindLabel(label, code_size);
-    code_size += table.GetSize();
-  }
-}
-
-void Thumb2Assembler::AdjustFixupIfNeeded(Fixup* fixup, uint32_t* current_code_size,
-                                          std::deque<FixupId>* fixups_to_recalculate) {
-  uint32_t adjustment = fixup->AdjustSizeIfNeeded(*current_code_size);
-  if (adjustment != 0u) {
-    DCHECK(fixup->CanExpand());
-    *current_code_size += adjustment;
-    for (FixupId dependent_id : fixup->Dependents(*this)) {
-      Fixup* dependent = GetFixup(dependent_id);
-      dependent->IncreaseAdjustment(adjustment);
-      if (buffer_.Load<int16_t>(dependent->GetLocation()) == 0) {
-        buffer_.Store<int16_t>(dependent->GetLocation(), 1);
-        fixups_to_recalculate->push_back(dependent_id);
-      }
-    }
-  }
-}
-
-uint32_t Thumb2Assembler::AdjustFixups() {
-  Fixup::PrepareDependents(this);
-  uint32_t current_code_size = buffer_.Size();
-  std::deque<FixupId> fixups_to_recalculate;
-  if (kIsDebugBuild) {
-    // We will use the placeholders in the buffer_ to mark whether the fixup has
-    // been added to the fixups_to_recalculate. Make sure we start with zeros.
-    for (Fixup& fixup : fixups_) {
-      CHECK_EQ(buffer_.Load<int16_t>(fixup.GetLocation()), 0);
-    }
-  }
-  for (Fixup& fixup : fixups_) {
-    AdjustFixupIfNeeded(&fixup, &current_code_size, &fixups_to_recalculate);
-  }
-  while (!fixups_to_recalculate.empty()) {
-    do {
-      // Pop the fixup.
-      FixupId fixup_id = fixups_to_recalculate.front();
-      fixups_to_recalculate.pop_front();
-      Fixup* fixup = GetFixup(fixup_id);
-      DCHECK_NE(buffer_.Load<int16_t>(fixup->GetLocation()), 0);
-      buffer_.Store<int16_t>(fixup->GetLocation(), 0);
-      // See if it needs adjustment.
-      AdjustFixupIfNeeded(fixup, &current_code_size, &fixups_to_recalculate);
-    } while (!fixups_to_recalculate.empty());
-
-    if ((current_code_size & 2) != 0 && (!literals_.empty() || !jump_tables_.empty())) {
-      // If we need to add padding before literals, this may just push some out of range,
-      // so recalculate all load literals. This makes up for the fact that we don't mark
-      // load literal as a dependency of all previous Fixups even though it actually is.
-      for (Fixup& fixup : fixups_) {
-        if (fixup.IsLoadLiteral()) {
-          AdjustFixupIfNeeded(&fixup, &current_code_size, &fixups_to_recalculate);
-        }
-      }
-    }
-  }
-  if (kIsDebugBuild) {
-    // Check that no fixup is marked as being in fixups_to_recalculate anymore.
-    for (Fixup& fixup : fixups_) {
-      CHECK_EQ(buffer_.Load<int16_t>(fixup.GetLocation()), 0);
-    }
-  }
-
-  // Adjust literal pool labels for padding.
-  DCHECK_ALIGNED(current_code_size, 2);
-  uint32_t literals_adjustment = current_code_size + (current_code_size & 2) - buffer_.Size();
-  if (literals_adjustment != 0u) {
-    for (Literal& literal : literals_) {
-      Label* label = literal.GetLabel();
-      DCHECK(label->IsBound());
-      int old_position = label->Position();
-      label->Reinitialize();
-      label->BindTo(old_position + literals_adjustment);
-    }
-    for (JumpTable& table : jump_tables_) {
-      Label* label = table.GetLabel();
-      DCHECK(label->IsBound());
-      int old_position = label->Position();
-      label->Reinitialize();
-      label->BindTo(old_position + literals_adjustment);
-    }
-  }
-
-  return current_code_size;
-}
-
-void Thumb2Assembler::EmitFixups(uint32_t adjusted_code_size) {
-  // Move non-fixup code to its final place and emit fixups.
-  // Process fixups in reverse order so that we don't repeatedly move the same data.
-  size_t src_end = buffer_.Size();
-  size_t dest_end = adjusted_code_size;
-  buffer_.Resize(dest_end);
-  DCHECK_GE(dest_end, src_end);
-  for (auto i = fixups_.rbegin(), end = fixups_.rend(); i != end; ++i) {
-    Fixup* fixup = &*i;
-    size_t old_fixup_location = fixup->GetLocation();
-    if (fixup->GetOriginalSize() == fixup->GetSize()) {
-      // The size of this Fixup didn't change. To avoid moving the data
-      // in small chunks, emit the code to its original position.
-      fixup->Finalize(dest_end - src_end);
-      fixup->Emit(old_fixup_location, &buffer_, adjusted_code_size);
-    } else {
-      // Move the data between the end of the fixup and src_end to its final location.
-      size_t src_begin = old_fixup_location + fixup->GetOriginalSizeInBytes();
-      size_t data_size = src_end - src_begin;
-      size_t dest_begin  = dest_end - data_size;
-      buffer_.Move(dest_begin, src_begin, data_size);
-      src_end = old_fixup_location;
-      dest_end = dest_begin - fixup->GetSizeInBytes();
-      // Finalize the Fixup and emit the data to the new location.
-      fixup->Finalize(dest_end - src_end);
-      fixup->Emit(fixup->GetLocation(), &buffer_, adjusted_code_size);
-    }
-  }
-  CHECK_EQ(src_end, dest_end);
-}
-
-void Thumb2Assembler::EmitLiterals() {
-  if (!literals_.empty()) {
-    // Load literal instructions (LDR, LDRD, VLDR) require 4-byte alignment.
-    // We don't support byte and half-word literals.
-    uint32_t code_size = buffer_.Size();
-    DCHECK_ALIGNED(code_size, 2);
-    if ((code_size & 2u) != 0u) {
-      Emit16(0);
-    }
-    for (Literal& literal : literals_) {
-      AssemblerBuffer::EnsureCapacity ensured(&buffer_);
-      DCHECK_EQ(static_cast<size_t>(literal.GetLabel()->Position()), buffer_.Size());
-      DCHECK(literal.GetSize() == 4u || literal.GetSize() == 8u);
-      for (size_t i = 0, size = literal.GetSize(); i != size; ++i) {
-        buffer_.Emit<uint8_t>(literal.GetData()[i]);
-      }
-    }
-  }
-}
-
-void Thumb2Assembler::EmitJumpTables() {
-  if (!jump_tables_.empty()) {
-    // Jump tables require 4 byte alignment. (We don't support byte and half-word jump tables.)
-    uint32_t code_size = buffer_.Size();
-    DCHECK_ALIGNED(code_size, 2);
-    if ((code_size & 2u) != 0u) {
-      Emit16(0);
-    }
-    for (JumpTable& table : jump_tables_) {
-      // 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
-
-      DCHECK_EQ(static_cast<size_t>(table.GetLabel()->Position()), buffer_.Size());
-      int32_t anchor_position = table.GetAnchorLabel()->Position() + 4;
-
-      for (Label* target : table.GetData()) {
-        // Ensure that the label was tracked, so that it will have the right position.
-        DCHECK(std::find(tracked_labels_.begin(), tracked_labels_.end(), target) !=
-                   tracked_labels_.end());
-
-        int32_t offset = target->Position() - anchor_position;
-        buffer_.Emit<int32_t>(offset);
-      }
-
-#ifndef NDEBUG
-      buffer_.has_ensured_capacity_ = false;
-#endif
-      size_t new_size = buffer_.Size();
-      DCHECK_LE(new_size - orig_size, table.GetSize());
-    }
-  }
-}
-
-void Thumb2Assembler::PatchCFI() {
-  if (cfi().NumberOfDelayedAdvancePCs() == 0u) {
-    return;
-  }
-
-  typedef DebugFrameOpCodeWriterForAssembler::DelayedAdvancePC DelayedAdvancePC;
-  const auto data = cfi().ReleaseStreamAndPrepareForDelayedAdvancePC();
-  const std::vector<uint8_t>& old_stream = data.first;
-  const std::vector<DelayedAdvancePC>& advances = data.second;
-
-  // Refill our data buffer with patched opcodes.
-  cfi().ReserveCFIStream(old_stream.size() + advances.size() + 16);
-  size_t stream_pos = 0;
-  for (const DelayedAdvancePC& advance : advances) {
-    DCHECK_GE(advance.stream_pos, stream_pos);
-    // Copy old data up to the point where advance was issued.
-    cfi().AppendRawData(old_stream, stream_pos, advance.stream_pos);
-    stream_pos = advance.stream_pos;
-    // Insert the advance command with its final offset.
-    size_t final_pc = GetAdjustedPosition(advance.pc);
-    cfi().AdvancePC(final_pc);
-  }
-  // Copy the final segment if any.
-  cfi().AppendRawData(old_stream, stream_pos, old_stream.size());
-}
-
-inline int16_t Thumb2Assembler::BEncoding16(int32_t offset, Condition cond) {
-  DCHECK_ALIGNED(offset, 2);
-  int16_t encoding = static_cast<int16_t>(B15 | B14);
-  if (cond != AL) {
-    DCHECK(IsInt<9>(offset));
-    encoding |= B12 |  (static_cast<int32_t>(cond) << 8) | ((offset >> 1) & 0xff);
-  } else {
-    DCHECK(IsInt<12>(offset));
-    encoding |= B13 | ((offset >> 1) & 0x7ff);
-  }
-  return encoding;
-}
-
-inline int32_t Thumb2Assembler::BEncoding32(int32_t offset, Condition cond) {
-  DCHECK_ALIGNED(offset, 2);
-  int32_t s = (offset >> 31) & 1;   // Sign bit.
-  int32_t encoding = B31 | B30 | B29 | B28 | B15 |
-      (s << 26) |                   // Sign bit goes to bit 26.
-      ((offset >> 1) & 0x7ff);      // imm11 goes to bits 0-10.
-  if (cond != AL) {
-    DCHECK(IsInt<21>(offset));
-    // Encode cond, move imm6 from bits 12-17 to bits 16-21 and move J1 and J2.
-    encoding |= (static_cast<int32_t>(cond) << 22) | ((offset & 0x3f000) << (16 - 12)) |
-        ((offset & (1 << 19)) >> (19 - 13)) |   // Extract J1 from bit 19 to bit 13.
-        ((offset & (1 << 18)) >> (18 - 11));    // Extract J2 from bit 18 to bit 11.
-  } else {
-    DCHECK(IsInt<25>(offset));
-    int32_t j1 = ((offset >> 23) ^ s ^ 1) & 1;  // Calculate J1 from I1 extracted from bit 23.
-    int32_t j2 = ((offset >> 22)^ s ^ 1) & 1;   // Calculate J2 from I2 extracted from bit 22.
-    // Move imm10 from bits 12-21 to bits 16-25 and add J1 and J2.
-    encoding |= B12 | ((offset & 0x3ff000) << (16 - 12)) |
-        (j1 << 13) | (j2 << 11);
-  }
-  return encoding;
-}
-
-inline int16_t Thumb2Assembler::CbxzEncoding16(Register rn, int32_t offset, Condition cond) {
-  DCHECK(!IsHighRegister(rn));
-  DCHECK_ALIGNED(offset, 2);
-  DCHECK(IsUint<7>(offset));
-  DCHECK(cond == EQ || cond == NE);
-  return B15 | B13 | B12 | B8 | (cond == NE ? B11 : 0) | static_cast<int32_t>(rn) |
-      ((offset & 0x3e) << (3 - 1)) |    // Move imm5 from bits 1-5 to bits 3-7.
-      ((offset & 0x40) << (9 - 6));     // Move i from bit 6 to bit 11
-}
-
-inline int16_t Thumb2Assembler::CmpRnImm8Encoding16(Register rn, int32_t value) {
-  DCHECK(!IsHighRegister(rn));
-  DCHECK(IsUint<8>(value));
-  return B13 | B11 | (rn << 8) | value;
-}
-
-inline int16_t Thumb2Assembler::AddRdnRmEncoding16(Register rdn, Register rm) {
-  // The high bit of rn is moved across 4-bit rm.
-  return B14 | B10 | (static_cast<int32_t>(rm) << 3) |
-      (static_cast<int32_t>(rdn) & 7) | ((static_cast<int32_t>(rdn) & 8) << 4);
-}
-
-inline int32_t Thumb2Assembler::MovwEncoding32(Register rd, int32_t value) {
-  DCHECK(IsUint<16>(value));
-  return B31 | B30 | B29 | B28 | B25 | B22 |
-      (static_cast<int32_t>(rd) << 8) |
-      ((value & 0xf000) << (16 - 12)) |   // Move imm4 from bits 12-15 to bits 16-19.
-      ((value & 0x0800) << (26 - 11)) |   // Move i from bit 11 to bit 26.
-      ((value & 0x0700) << (12 - 8)) |    // Move imm3 from bits 8-10 to bits 12-14.
-      (value & 0xff);                     // Keep imm8 in bits 0-7.
-}
-
-inline int32_t Thumb2Assembler::MovtEncoding32(Register rd, int32_t value) {
-  DCHECK_EQ(value & 0xffff, 0);
-  int32_t movw_encoding = MovwEncoding32(rd, (value >> 16) & 0xffff);
-  return movw_encoding | B25 | B23;
-}
-
-inline int32_t Thumb2Assembler::MovModImmEncoding32(Register rd, int32_t value) {
-  uint32_t mod_imm = ModifiedImmediate(value);
-  DCHECK_NE(mod_imm, kInvalidModifiedImmediate);
-  return B31 | B30 | B29 | B28 | B22 | B19 | B18 | B17 | B16 |
-      (static_cast<int32_t>(rd) << 8) | static_cast<int32_t>(mod_imm);
-}
-
-inline int16_t Thumb2Assembler::LdrLitEncoding16(Register rt, int32_t offset) {
-  DCHECK(!IsHighRegister(rt));
-  DCHECK_ALIGNED(offset, 4);
-  DCHECK(IsUint<10>(offset));
-  return B14 | B11 | (static_cast<int32_t>(rt) << 8) | (offset >> 2);
-}
-
-inline int32_t Thumb2Assembler::LdrLitEncoding32(Register rt, int32_t offset) {
-  // NOTE: We don't support negative offset, i.e. U=0 (B23).
-  return LdrRtRnImm12Encoding(rt, PC, offset);
-}
-
-inline int32_t Thumb2Assembler::LdrdEncoding32(Register rt, Register rt2, Register rn, int32_t offset) {
-  DCHECK_ALIGNED(offset, 4);
-  CHECK(IsUint<10>(offset));
-  return B31 | B30 | B29 | B27 |
-      B24 /* P = 1 */ | B23 /* U = 1 */ | B22 | 0 /* W = 0 */ | B20 |
-      (static_cast<int32_t>(rn) << 16) | (static_cast<int32_t>(rt) << 12) |
-      (static_cast<int32_t>(rt2) << 8) | (offset >> 2);
-}
-
-inline int32_t Thumb2Assembler::VldrsEncoding32(SRegister sd, Register rn, int32_t offset) {
-  DCHECK_ALIGNED(offset, 4);
-  CHECK(IsUint<10>(offset));
-  return B31 | B30 | B29 | B27 | B26 | B24 |
-      B23 /* U = 1 */ | B20 | B11 | B9 |
-      (static_cast<int32_t>(rn) << 16) |
-      ((static_cast<int32_t>(sd) & 0x01) << (22 - 0)) |   // Move D from bit 0 to bit 22.
-      ((static_cast<int32_t>(sd) & 0x1e) << (12 - 1)) |   // Move Vd from bits 1-4 to bits 12-15.
-      (offset >> 2);
-}
-
-inline int32_t Thumb2Assembler::VldrdEncoding32(DRegister dd, Register rn, int32_t offset) {
-  DCHECK_ALIGNED(offset, 4);
-  CHECK(IsUint<10>(offset));
-  return B31 | B30 | B29 | B27 | B26 | B24 |
-      B23 /* U = 1 */ | B20 | B11 | B9 | B8 |
-      (rn << 16) |
-      ((static_cast<int32_t>(dd) & 0x10) << (22 - 4)) |   // Move D from bit 4 to bit 22.
-      ((static_cast<int32_t>(dd) & 0x0f) << (12 - 0)) |   // Move Vd from bits 0-3 to bits 12-15.
-      (offset >> 2);
-}
-
-inline int16_t Thumb2Assembler::LdrRtRnImm5Encoding16(Register rt, Register rn, int32_t offset) {
-  DCHECK(!IsHighRegister(rt));
-  DCHECK(!IsHighRegister(rn));
-  DCHECK_ALIGNED(offset, 4);
-  DCHECK(IsUint<7>(offset));
-  return B14 | B13 | B11 |
-      (static_cast<int32_t>(rn) << 3) | static_cast<int32_t>(rt) |
-      (offset << (6 - 2));                // Move imm5 from bits 2-6 to bits 6-10.
-}
-
-int32_t Thumb2Assembler::Fixup::LoadWideOrFpEncoding(Register rbase, int32_t offset) const {
-  switch (type_) {
-    case kLoadLiteralWide:
-      return LdrdEncoding32(rn_, rt2_, rbase, offset);
-    case kLoadFPLiteralSingle:
-      return VldrsEncoding32(sd_, rbase, offset);
-    case kLoadFPLiteralDouble:
-      return VldrdEncoding32(dd_, rbase, offset);
-    default:
-      LOG(FATAL) << "Unexpected type: " << static_cast<int>(type_);
-      UNREACHABLE();
-  }
-}
-
-inline int32_t Thumb2Assembler::LdrRtRnImm12Encoding(Register rt, Register rn, int32_t offset) {
-  DCHECK(IsUint<12>(offset));
-  return B31 | B30 | B29 | B28 | B27 | B23 | B22 | B20 | (rn << 16) | (rt << 12) | offset;
-}
-
-inline int16_t Thumb2Assembler::AdrEncoding16(Register rd, int32_t offset) {
-  DCHECK(IsUint<10>(offset));
-  DCHECK(IsAligned<4>(offset));
-  DCHECK(!IsHighRegister(rd));
-  return B15 | B13 | (rd << 8) | (offset >> 2);
-}
-
-inline int32_t Thumb2Assembler::AdrEncoding32(Register rd, int32_t offset) {
-  DCHECK(IsUint<12>(offset));
-  // Bit     26: offset[11]
-  // Bits 14-12: offset[10-8]
-  // Bits   7-0: offset[7-0]
-  int32_t immediate_mask =
-      ((offset & (1 << 11)) << (26 - 11)) |
-      ((offset & (7 << 8)) << (12 - 8)) |
-      (offset & 0xFF);
-  return B31 | B30 | B29 | B28 | B25 | B19 | B18 | B17 | B16 | (rd << 8) | immediate_mask;
-}
-
-void Thumb2Assembler::FinalizeCode() {
-  ArmAssembler::FinalizeCode();
-  uint32_t size_after_literals = BindLiterals();
-  BindJumpTables(size_after_literals);
-  uint32_t adjusted_code_size = AdjustFixups();
-  EmitFixups(adjusted_code_size);
-  EmitLiterals();
-  FinalizeTrackedLabels();
-  EmitJumpTables();
-  PatchCFI();
-}
-
-bool Thumb2Assembler::ShifterOperandCanAlwaysHold(uint32_t immediate) {
-  return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate;
-}
-
-bool Thumb2Assembler::ShifterOperandCanHold(Register rd ATTRIBUTE_UNUSED,
-                                            Register rn ATTRIBUTE_UNUSED,
-                                            Opcode opcode,
-                                            uint32_t immediate,
-                                            SetCc set_cc,
-                                            ShifterOperand* shifter_op) {
-  shifter_op->type_ = ShifterOperand::kImmediate;
-  shifter_op->immed_ = immediate;
-  shifter_op->is_shift_ = false;
-  shifter_op->is_rotate_ = false;
-  switch (opcode) {
-    case ADD:
-    case SUB:
-      // Less than (or equal to) 12 bits can be done if we don't need to set condition codes.
-      if (immediate < (1 << 12) && set_cc != kCcSet) {
-        return true;
-      }
-      return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate;
-
-    case MOV:
-      // TODO: Support less than or equal to 12bits.
-      return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate;
-
-    case MVN:
-    default:
-      return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate;
-  }
-}
-
-void Thumb2Assembler::and_(Register rd, Register rn, const ShifterOperand& so,
-                           Condition cond, SetCc set_cc) {
-  EmitDataProcessing(cond, AND, set_cc, rn, rd, so);
-}
-
-
-void Thumb2Assembler::eor(Register rd, Register rn, const ShifterOperand& so,
-                          Condition cond, SetCc set_cc) {
-  EmitDataProcessing(cond, EOR, set_cc, rn, rd, so);
-}
-
-
-void Thumb2Assembler::sub(Register rd, Register rn, const ShifterOperand& so,
-                          Condition cond, SetCc set_cc) {
-  EmitDataProcessing(cond, SUB, set_cc, rn, rd, so);
-}
-
-
-void Thumb2Assembler::rsb(Register rd, Register rn, const ShifterOperand& so,
-                          Condition cond, SetCc set_cc) {
-  EmitDataProcessing(cond, RSB, set_cc, rn, rd, so);
-}
-
-
-void Thumb2Assembler::add(Register rd, Register rn, const ShifterOperand& so,
-                          Condition cond, SetCc set_cc) {
-  EmitDataProcessing(cond, ADD, set_cc, rn, rd, so);
-}
-
-
-void Thumb2Assembler::adc(Register rd, Register rn, const ShifterOperand& so,
-                          Condition cond, SetCc set_cc) {
-  EmitDataProcessing(cond, ADC, set_cc, rn, rd, so);
-}
-
-
-void Thumb2Assembler::sbc(Register rd, Register rn, const ShifterOperand& so,
-                          Condition cond, SetCc set_cc) {
-  EmitDataProcessing(cond, SBC, set_cc, rn, rd, so);
-}
-
-
-void Thumb2Assembler::rsc(Register rd, Register rn, const ShifterOperand& so,
-                          Condition cond, SetCc set_cc) {
-  EmitDataProcessing(cond, RSC, set_cc, rn, rd, so);
-}
-
-
-void Thumb2Assembler::tst(Register rn, const ShifterOperand& so, Condition cond) {
-  CHECK_NE(rn, PC);  // Reserve tst pc instruction for exception handler marker.
-  EmitDataProcessing(cond, TST, kCcSet, rn, R0, so);
-}
-
-
-void Thumb2Assembler::teq(Register rn, const ShifterOperand& so, Condition cond) {
-  CHECK_NE(rn, PC);  // Reserve teq pc instruction for exception handler marker.
-  EmitDataProcessing(cond, TEQ, kCcSet, rn, R0, so);
-}
-
-
-void Thumb2Assembler::cmp(Register rn, const ShifterOperand& so, Condition cond) {
-  EmitDataProcessing(cond, CMP, kCcSet, rn, R0, so);
-}
-
-
-void Thumb2Assembler::cmn(Register rn, const ShifterOperand& so, Condition cond) {
-  EmitDataProcessing(cond, CMN, kCcSet, rn, R0, so);
-}
-
-
-void Thumb2Assembler::orr(Register rd, Register rn, const ShifterOperand& so,
-                          Condition cond, SetCc set_cc) {
-  EmitDataProcessing(cond, ORR, set_cc, rn, rd, so);
-}
-
-
-void Thumb2Assembler::orn(Register rd, Register rn, const ShifterOperand& so,
-                          Condition cond, SetCc set_cc) {
-  EmitDataProcessing(cond, ORN, set_cc, rn, rd, so);
-}
-
-
-void Thumb2Assembler::mov(Register rd, const ShifterOperand& so,
-                          Condition cond, SetCc set_cc) {
-  EmitDataProcessing(cond, MOV, set_cc, R0, rd, so);
-}
-
-
-void Thumb2Assembler::bic(Register rd, Register rn, const ShifterOperand& so,
-                          Condition cond, SetCc set_cc) {
-  EmitDataProcessing(cond, BIC, set_cc, rn, rd, so);
-}
-
-
-void Thumb2Assembler::mvn(Register rd, const ShifterOperand& so,
-                          Condition cond, SetCc set_cc) {
-  EmitDataProcessing(cond, MVN, set_cc, R0, rd, so);
-}
-
-
-void Thumb2Assembler::mul(Register rd, Register rn, Register rm, Condition cond) {
-  CheckCondition(cond);
-
-  if (rd == rm && !IsHighRegister(rd) && !IsHighRegister(rn) && !force_32bit_) {
-    // 16 bit.
-    int16_t encoding = B14 | B9 | B8 | B6 |
-        rn << 3 | rd;
-    Emit16(encoding);
-  } else {
-    // 32 bit.
-    uint32_t op1 = 0U /* 0b000 */;
-    uint32_t op2 = 0U /* 0b00 */;
-    int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 |
-        op1 << 20 |
-        B15 | B14 | B13 | B12 |
-        op2 << 4 |
-        static_cast<uint32_t>(rd) << 8 |
-        static_cast<uint32_t>(rn) << 16 |
-        static_cast<uint32_t>(rm);
-
-    Emit32(encoding);
-  }
-}
-
-
-void Thumb2Assembler::mla(Register rd, Register rn, Register rm, Register ra,
-                          Condition cond) {
-  CheckCondition(cond);
-
-  uint32_t op1 = 0U /* 0b000 */;
-  uint32_t op2 = 0U /* 0b00 */;
-  int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 |
-      op1 << 20 |
-      op2 << 4 |
-      static_cast<uint32_t>(rd) << 8 |
-      static_cast<uint32_t>(ra) << 12 |
-      static_cast<uint32_t>(rn) << 16 |
-      static_cast<uint32_t>(rm);
-
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::mls(Register rd, Register rn, Register rm, Register ra,
-                          Condition cond) {
-  CheckCondition(cond);
-
-  uint32_t op1 = 0U /* 0b000 */;
-  uint32_t op2 = 01 /* 0b01 */;
-  int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 |
-      op1 << 20 |
-      op2 << 4 |
-      static_cast<uint32_t>(rd) << 8 |
-      static_cast<uint32_t>(ra) << 12 |
-      static_cast<uint32_t>(rn) << 16 |
-      static_cast<uint32_t>(rm);
-
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::smull(Register rd_lo, Register rd_hi, Register rn,
-                            Register rm, Condition cond) {
-  CheckCondition(cond);
-
-  uint32_t op1 = 0U /* 0b000; */;
-  uint32_t op2 = 0U /* 0b0000 */;
-  int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | B23 |
-      op1 << 20 |
-      op2 << 4 |
-      static_cast<uint32_t>(rd_lo) << 12 |
-      static_cast<uint32_t>(rd_hi) << 8 |
-      static_cast<uint32_t>(rn) << 16 |
-      static_cast<uint32_t>(rm);
-
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::umull(Register rd_lo, Register rd_hi, Register rn,
-                            Register rm, Condition cond) {
-  CheckCondition(cond);
-
-  uint32_t op1 = 2U /* 0b010; */;
-  uint32_t op2 = 0U /* 0b0000 */;
-  int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | B23 |
-      op1 << 20 |
-      op2 << 4 |
-      static_cast<uint32_t>(rd_lo) << 12 |
-      static_cast<uint32_t>(rd_hi) << 8 |
-      static_cast<uint32_t>(rn) << 16 |
-      static_cast<uint32_t>(rm);
-
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::sdiv(Register rd, Register rn, Register rm, Condition cond) {
-  CheckCondition(cond);
-
-  uint32_t op1 = 1U  /* 0b001 */;
-  uint32_t op2 = 15U /* 0b1111 */;
-  int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | B23 | B20 |
-      op1 << 20 |
-      op2 << 4 |
-      0xf << 12 |
-      static_cast<uint32_t>(rd) << 8 |
-      static_cast<uint32_t>(rn) << 16 |
-      static_cast<uint32_t>(rm);
-
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::udiv(Register rd, Register rn, Register rm, Condition cond) {
-  CheckCondition(cond);
-
-  uint32_t op1 = 1U  /* 0b001 */;
-  uint32_t op2 = 15U /* 0b1111 */;
-  int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | B23 | B21 | B20 |
-      op1 << 20 |
-      op2 << 4 |
-      0xf << 12 |
-      static_cast<uint32_t>(rd) << 8 |
-      static_cast<uint32_t>(rn) << 16 |
-      static_cast<uint32_t>(rm);
-
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::sbfx(Register rd, Register rn, uint32_t lsb, uint32_t width, Condition cond) {
-  CheckCondition(cond);
-  CHECK_LE(lsb, 31U);
-  CHECK(1U <= width && width <= 32U) << width;
-  uint32_t widthminus1 = width - 1;
-  uint32_t imm2 = lsb & (B1 | B0);  // Bits 0-1 of `lsb`.
-  uint32_t imm3 = (lsb & (B4 | B3 | B2)) >> 2;  // Bits 2-4 of `lsb`.
-
-  uint32_t op = 20U /* 0b10100 */;
-  int32_t encoding = B31 | B30 | B29 | B28 | B25 |
-      op << 20 |
-      static_cast<uint32_t>(rn) << 16 |
-      imm3 << 12 |
-      static_cast<uint32_t>(rd) << 8 |
-      imm2 << 6 |
-      widthminus1;
-
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::ubfx(Register rd, Register rn, uint32_t lsb, uint32_t width, Condition cond) {
-  CheckCondition(cond);
-  CHECK_LE(lsb, 31U);
-  CHECK(1U <= width && width <= 32U) << width;
-  uint32_t widthminus1 = width - 1;
-  uint32_t imm2 = lsb & (B1 | B0);  // Bits 0-1 of `lsb`.
-  uint32_t imm3 = (lsb & (B4 | B3 | B2)) >> 2;  // Bits 2-4 of `lsb`.
-
-  uint32_t op = 28U /* 0b11100 */;
-  int32_t encoding = B31 | B30 | B29 | B28 | B25 |
-      op << 20 |
-      static_cast<uint32_t>(rn) << 16 |
-      imm3 << 12 |
-      static_cast<uint32_t>(rd) << 8 |
-      imm2 << 6 |
-      widthminus1;
-
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::ldr(Register rd, const Address& ad, Condition cond) {
-  EmitLoadStore(cond, true, false, false, false, rd, ad);
-}
-
-
-void Thumb2Assembler::str(Register rd, const Address& ad, Condition cond) {
-  EmitLoadStore(cond, false, false, false, false, rd, ad);
-}
-
-
-void Thumb2Assembler::ldrb(Register rd, const Address& ad, Condition cond) {
-  EmitLoadStore(cond, true, true, false, false, rd, ad);
-}
-
-
-void Thumb2Assembler::strb(Register rd, const Address& ad, Condition cond) {
-  EmitLoadStore(cond, false, true, false, false, rd, ad);
-}
-
-
-void Thumb2Assembler::ldrh(Register rd, const Address& ad, Condition cond) {
-  EmitLoadStore(cond, true, false, true, false, rd, ad);
-}
-
-
-void Thumb2Assembler::strh(Register rd, const Address& ad, Condition cond) {
-  EmitLoadStore(cond, false, false, true, false, rd, ad);
-}
-
-
-void Thumb2Assembler::ldrsb(Register rd, const Address& ad, Condition cond) {
-  EmitLoadStore(cond, true, true, false, true, rd, ad);
-}
-
-
-void Thumb2Assembler::ldrsh(Register rd, const Address& ad, Condition cond) {
-  EmitLoadStore(cond, true, false, true, true, rd, ad);
-}
-
-
-void Thumb2Assembler::ldrd(Register rd, const Address& ad, Condition cond) {
-  ldrd(rd, Register(rd + 1), ad, cond);
-}
-
-
-void Thumb2Assembler::ldrd(Register rd, Register rd2, const Address& ad, Condition cond) {
-  CheckCondition(cond);
-  // Encoding T1.
-  // This is different from other loads.  The encoding is like ARM.
-  int32_t encoding = B31 | B30 | B29 | B27 | B22 | B20 |
-      static_cast<int32_t>(rd) << 12 |
-      static_cast<int32_t>(rd2) << 8 |
-      ad.encodingThumbLdrdStrd();
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::strd(Register rd, const Address& ad, Condition cond) {
-  strd(rd, Register(rd + 1), ad, cond);
-}
-
-
-void Thumb2Assembler::strd(Register rd, Register rd2, const Address& ad, Condition cond) {
-  CheckCondition(cond);
-  // Encoding T1.
-  // This is different from other loads.  The encoding is like ARM.
-  int32_t encoding = B31 | B30 | B29 | B27 | B22 |
-      static_cast<int32_t>(rd) << 12 |
-      static_cast<int32_t>(rd2) << 8 |
-      ad.encodingThumbLdrdStrd();
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::ldm(BlockAddressMode am,
-                          Register base,
-                          RegList regs,
-                          Condition cond) {
-  CHECK_NE(regs, 0u);  // Do not use ldm if there's nothing to load.
-  if (IsPowerOfTwo(regs)) {
-    // Thumb doesn't support one reg in the list.
-    // Find the register number.
-    int reg = CTZ(static_cast<uint32_t>(regs));
-    CHECK_LT(reg, 16);
-    CHECK(am == DB_W);      // Only writeback is supported.
-    ldr(static_cast<Register>(reg), Address(base, kRegisterSize, Address::PostIndex), cond);
-  } else {
-    EmitMultiMemOp(cond, am, true, base, regs);
-  }
-}
-
-
-void Thumb2Assembler::stm(BlockAddressMode am,
-                          Register base,
-                          RegList regs,
-                          Condition cond) {
-  CHECK_NE(regs, 0u);  // Do not use stm if there's nothing to store.
-  if (IsPowerOfTwo(regs)) {
-    // Thumb doesn't support one reg in the list.
-    // Find the register number.
-    int reg = CTZ(static_cast<uint32_t>(regs));
-    CHECK_LT(reg, 16);
-    CHECK(am == IA || am == IA_W);
-    Address::Mode strmode = am == IA ? Address::PreIndex : Address::Offset;
-    str(static_cast<Register>(reg), Address(base, -kRegisterSize, strmode), cond);
-  } else {
-    EmitMultiMemOp(cond, am, false, base, regs);
-  }
-}
-
-
-bool Thumb2Assembler::vmovs(SRegister sd, float s_imm, Condition cond) {
-  uint32_t imm32 = bit_cast<uint32_t, float>(s_imm);
-  if (((imm32 & ((1 << 19) - 1)) == 0) &&
-      ((((imm32 >> 25) & ((1 << 6) - 1)) == (1 << 5)) ||
-       (((imm32 >> 25) & ((1 << 6) - 1)) == ((1 << 5) -1)))) {
-    uint8_t imm8 = ((imm32 >> 31) << 7) | (((imm32 >> 29) & 1) << 6) |
-        ((imm32 >> 19) & ((1 << 6) -1));
-    EmitVFPsss(cond, B23 | B21 | B20 | ((imm8 >> 4)*B16) | (imm8 & 0xf),
-               sd, S0, S0);
-    return true;
-  }
-  return false;
-}
-
-
-bool Thumb2Assembler::vmovd(DRegister dd, double d_imm, Condition cond) {
-  uint64_t imm64 = bit_cast<uint64_t, double>(d_imm);
-  if (((imm64 & ((1LL << 48) - 1)) == 0) &&
-      ((((imm64 >> 54) & ((1 << 9) - 1)) == (1 << 8)) ||
-       (((imm64 >> 54) & ((1 << 9) - 1)) == ((1 << 8) -1)))) {
-    uint8_t imm8 = ((imm64 >> 63) << 7) | (((imm64 >> 61) & 1) << 6) |
-        ((imm64 >> 48) & ((1 << 6) -1));
-    EmitVFPddd(cond, B23 | B21 | B20 | ((imm8 >> 4)*B16) | B8 | (imm8 & 0xf),
-               dd, D0, D0);
-    return true;
-  }
-  return false;
-}
-
-
-void Thumb2Assembler::vmovs(SRegister sd, SRegister sm, Condition cond) {
-  EmitVFPsss(cond, B23 | B21 | B20 | B6, sd, S0, sm);
-}
-
-
-void Thumb2Assembler::vmovd(DRegister dd, DRegister dm, Condition cond) {
-  EmitVFPddd(cond, B23 | B21 | B20 | B6, dd, D0, dm);
-}
-
-
-void Thumb2Assembler::vadds(SRegister sd, SRegister sn, SRegister sm,
-                            Condition cond) {
-  EmitVFPsss(cond, B21 | B20, sd, sn, sm);
-}
-
-
-void Thumb2Assembler::vaddd(DRegister dd, DRegister dn, DRegister dm,
-                            Condition cond) {
-  EmitVFPddd(cond, B21 | B20, dd, dn, dm);
-}
-
-
-void Thumb2Assembler::vsubs(SRegister sd, SRegister sn, SRegister sm,
-                            Condition cond) {
-  EmitVFPsss(cond, B21 | B20 | B6, sd, sn, sm);
-}
-
-
-void Thumb2Assembler::vsubd(DRegister dd, DRegister dn, DRegister dm,
-                            Condition cond) {
-  EmitVFPddd(cond, B21 | B20 | B6, dd, dn, dm);
-}
-
-
-void Thumb2Assembler::vmuls(SRegister sd, SRegister sn, SRegister sm,
-                            Condition cond) {
-  EmitVFPsss(cond, B21, sd, sn, sm);
-}
-
-
-void Thumb2Assembler::vmuld(DRegister dd, DRegister dn, DRegister dm,
-                            Condition cond) {
-  EmitVFPddd(cond, B21, dd, dn, dm);
-}
-
-
-void Thumb2Assembler::vmlas(SRegister sd, SRegister sn, SRegister sm,
-                            Condition cond) {
-  EmitVFPsss(cond, 0, sd, sn, sm);
-}
-
-
-void Thumb2Assembler::vmlad(DRegister dd, DRegister dn, DRegister dm,
-                            Condition cond) {
-  EmitVFPddd(cond, 0, dd, dn, dm);
-}
-
-
-void Thumb2Assembler::vmlss(SRegister sd, SRegister sn, SRegister sm,
-                            Condition cond) {
-  EmitVFPsss(cond, B6, sd, sn, sm);
-}
-
-
-void Thumb2Assembler::vmlsd(DRegister dd, DRegister dn, DRegister dm,
-                            Condition cond) {
-  EmitVFPddd(cond, B6, dd, dn, dm);
-}
-
-
-void Thumb2Assembler::vdivs(SRegister sd, SRegister sn, SRegister sm,
-                            Condition cond) {
-  EmitVFPsss(cond, B23, sd, sn, sm);
-}
-
-
-void Thumb2Assembler::vdivd(DRegister dd, DRegister dn, DRegister dm,
-                            Condition cond) {
-  EmitVFPddd(cond, B23, dd, dn, dm);
-}
-
-
-void Thumb2Assembler::vabss(SRegister sd, SRegister sm, Condition cond) {
-  EmitVFPsss(cond, B23 | B21 | B20 | B7 | B6, sd, S0, sm);
-}
-
-
-void Thumb2Assembler::vabsd(DRegister dd, DRegister dm, Condition cond) {
-  EmitVFPddd(cond, B23 | B21 | B20 | B7 | B6, dd, D0, dm);
-}
-
-
-void Thumb2Assembler::vnegs(SRegister sd, SRegister sm, Condition cond) {
-  EmitVFPsss(cond, B23 | B21 | B20 | B16 | B6, sd, S0, sm);
-}
-
-
-void Thumb2Assembler::vnegd(DRegister dd, DRegister dm, Condition cond) {
-  EmitVFPddd(cond, B23 | B21 | B20 | B16 | B6, dd, D0, dm);
-}
-
-
-void Thumb2Assembler::vsqrts(SRegister sd, SRegister sm, Condition cond) {
-  EmitVFPsss(cond, B23 | B21 | B20 | B16 | B7 | B6, sd, S0, sm);
-}
-
-void Thumb2Assembler::vsqrtd(DRegister dd, DRegister dm, Condition cond) {
-  EmitVFPddd(cond, B23 | B21 | B20 | B16 | B7 | B6, dd, D0, dm);
-}
-
-
-void Thumb2Assembler::vcvtsd(SRegister sd, DRegister dm, Condition cond) {
-  EmitVFPsd(cond, B23 | B21 | B20 | B18 | B17 | B16 | B8 | B7 | B6, sd, dm);
-}
-
-
-void Thumb2Assembler::vcvtds(DRegister dd, SRegister sm, Condition cond) {
-  EmitVFPds(cond, B23 | B21 | B20 | B18 | B17 | B16 | B7 | B6, dd, sm);
-}
-
-
-void Thumb2Assembler::vcvtis(SRegister sd, SRegister sm, Condition cond) {
-  EmitVFPsss(cond, B23 | B21 | B20 | B19 | B18 | B16 | B7 | B6, sd, S0, sm);
-}
-
-
-void Thumb2Assembler::vcvtid(SRegister sd, DRegister dm, Condition cond) {
-  EmitVFPsd(cond, B23 | B21 | B20 | B19 | B18 | B16 | B8 | B7 | B6, sd, dm);
-}
-
-
-void Thumb2Assembler::vcvtsi(SRegister sd, SRegister sm, Condition cond) {
-  EmitVFPsss(cond, B23 | B21 | B20 | B19 | B7 | B6, sd, S0, sm);
-}
-
-
-void Thumb2Assembler::vcvtdi(DRegister dd, SRegister sm, Condition cond) {
-  EmitVFPds(cond, B23 | B21 | B20 | B19 | B8 | B7 | B6, dd, sm);
-}
-
-
-void Thumb2Assembler::vcvtus(SRegister sd, SRegister sm, Condition cond) {
-  EmitVFPsss(cond, B23 | B21 | B20 | B19 | B18 | B7 | B6, sd, S0, sm);
-}
-
-
-void Thumb2Assembler::vcvtud(SRegister sd, DRegister dm, Condition cond) {
-  EmitVFPsd(cond, B23 | B21 | B20 | B19 | B18 | B8 | B7 | B6, sd, dm);
-}
-
-
-void Thumb2Assembler::vcvtsu(SRegister sd, SRegister sm, Condition cond) {
-  EmitVFPsss(cond, B23 | B21 | B20 | B19 | B6, sd, S0, sm);
-}
-
-
-void Thumb2Assembler::vcvtdu(DRegister dd, SRegister sm, Condition cond) {
-  EmitVFPds(cond, B23 | B21 | B20 | B19 | B8 | B6, dd, sm);
-}
-
-
-void Thumb2Assembler::vcmps(SRegister sd, SRegister sm, Condition cond) {
-  EmitVFPsss(cond, B23 | B21 | B20 | B18 | B6, sd, S0, sm);
-}
-
-
-void Thumb2Assembler::vcmpd(DRegister dd, DRegister dm, Condition cond) {
-  EmitVFPddd(cond, B23 | B21 | B20 | B18 | B6, dd, D0, dm);
-}
-
-
-void Thumb2Assembler::vcmpsz(SRegister sd, Condition cond) {
-  EmitVFPsss(cond, B23 | B21 | B20 | B18 | B16 | B6, sd, S0, S0);
-}
-
-
-void Thumb2Assembler::vcmpdz(DRegister dd, Condition cond) {
-  EmitVFPddd(cond, B23 | B21 | B20 | B18 | B16 | B6, dd, D0, D0);
-}
-
-void Thumb2Assembler::b(Label* label, Condition cond) {
-  DCHECK_EQ(next_condition_, AL);
-  EmitBranch(cond, label, false, false);
-}
-
-
-void Thumb2Assembler::bl(Label* label, Condition cond) {
-  CheckCondition(cond);
-  EmitBranch(cond, label, true, false);
-}
-
-
-void Thumb2Assembler::blx(Label* label) {
-  EmitBranch(AL, label, true, true);
-}
-
-
-void Thumb2Assembler::MarkExceptionHandler(Label* label) {
-  EmitDataProcessing(AL, TST, kCcSet, PC, R0, ShifterOperand(0));
-  Label l;
-  b(&l);
-  EmitBranch(AL, label, false, false);
-  Bind(&l);
-}
-
-
-void Thumb2Assembler::Emit32(int32_t value) {
-  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
-  buffer_.Emit<int16_t>(value >> 16);
-  buffer_.Emit<int16_t>(value & 0xffff);
-}
-
-
-void Thumb2Assembler::Emit16(int16_t value) {
-  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
-  buffer_.Emit<int16_t>(value);
-}
-
-
-bool Thumb2Assembler::Is32BitDataProcessing(Condition cond,
-                                            Opcode opcode,
-                                            SetCc set_cc,
-                                            Register rn,
-                                            Register rd,
-                                            const ShifterOperand& so) {
-  if (force_32bit_) {
-    return true;
-  }
-
-  // Check special case for SP relative ADD and SUB immediate.
-  if ((opcode == ADD || opcode == SUB) && rn == SP && so.IsImmediate() && set_cc != kCcSet) {
-    // If the immediate is in range, use 16 bit.
-    if (rd == SP) {
-      if (so.GetImmediate() < (1 << 9)) {    // 9 bit immediate.
-        return false;
-      }
-    } else if (!IsHighRegister(rd) && opcode == ADD) {
-      if (so.GetImmediate() < (1 << 10)) {    // 10 bit immediate.
-        return false;
-      }
-    }
-  }
-
-  bool can_contain_high_register =
-      (opcode == CMP) ||
-      (opcode == MOV && set_cc != kCcSet) ||
-      ((opcode == ADD) && (rn == rd) && set_cc != kCcSet);
-
-  if (IsHighRegister(rd) || IsHighRegister(rn)) {
-    if (!can_contain_high_register) {
-      return true;
-    }
-
-    // There are high register instructions available for this opcode.
-    // However, there is no actual shift available, neither for ADD nor for MOV (ASR/LSR/LSL/ROR).
-    if (so.IsShift() && (so.GetShift() == RRX || so.GetImmediate() != 0u)) {
-      return true;
-    }
-
-    // The ADD and MOV instructions that work with high registers don't have 16-bit
-    // immediate variants.
-    if (so.IsImmediate()) {
-      return true;
-    }
-  }
-
-  if (so.IsRegister() && IsHighRegister(so.GetRegister()) && !can_contain_high_register) {
-    return true;
-  }
-
-  bool rn_is_valid = true;
-
-  // Check for single operand instructions and ADD/SUB.
-  switch (opcode) {
-    case CMP:
-    case MOV:
-    case TST:
-    case MVN:
-      rn_is_valid = false;      // There is no Rn for these instructions.
-      break;
-    case TEQ:
-    case ORN:
-      return true;
-    case ADD:
-    case SUB:
-      break;
-    default:
-      if (so.IsRegister() && rd != rn) {
-        return true;
-      }
-  }
-
-  if (so.IsImmediate()) {
-    if (opcode == RSB) {
-      DCHECK(rn_is_valid);
-      if (so.GetImmediate() != 0u) {
-        return true;
-      }
-    } else if (rn_is_valid && rn != rd) {
-      // The only thumb1 instructions with a register and an immediate are ADD and SUB
-      // with a 3-bit immediate, and RSB with zero immediate.
-      if (opcode == ADD || opcode == SUB) {
-        if ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet) {
-          return true;  // Cannot match "setflags".
-        }
-        if (!IsUint<3>(so.GetImmediate()) && !IsUint<3>(-so.GetImmediate())) {
-          return true;
-        }
-      } else {
-        return true;
-      }
-    } else {
-      // ADD, SUB, CMP and MOV may be thumb1 only if the immediate is 8 bits.
-      if (!(opcode == ADD || opcode == SUB || opcode == MOV || opcode == CMP)) {
-        return true;
-      } else if (opcode != CMP && ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet)) {
-        return true;  // Cannot match "setflags" for ADD, SUB or MOV.
-      } else {
-        // For ADD and SUB allow also negative 8-bit immediate as we will emit the oposite opcode.
-        if (!IsUint<8>(so.GetImmediate()) &&
-            (opcode == MOV || opcode == CMP || !IsUint<8>(-so.GetImmediate()))) {
-          return true;
-        }
-      }
-    }
-  } else {
-    DCHECK(so.IsRegister());
-    if (so.IsShift()) {
-      // Shift operand - check if it is a MOV convertible to a 16-bit shift instruction.
-      if (opcode != MOV) {
-        return true;
-      }
-      // Check for MOV with an ROR/RRX. There is no 16-bit ROR immediate and no 16-bit RRX.
-      if (so.GetShift() == ROR || so.GetShift() == RRX) {
-        return true;
-      }
-      // 16-bit shifts set condition codes if and only if outside IT block,
-      // i.e. if and only if cond == AL.
-      if ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet) {
-        return true;
-      }
-    } else {
-      // Register operand without shift.
-      switch (opcode) {
-        case ADD:
-          // The 16-bit ADD that cannot contain high registers can set condition codes
-          // if and only if outside IT block, i.e. if and only if cond == AL.
-          if (!can_contain_high_register &&
-              ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet)) {
-            return true;
-          }
-          break;
-        case AND:
-        case BIC:
-        case EOR:
-        case ORR:
-        case MVN:
-        case ADC:
-        case SUB:
-        case SBC:
-          // These 16-bit opcodes set condition codes if and only if outside IT block,
-          // i.e. if and only if cond == AL.
-          if ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet) {
-            return true;
-          }
-          break;
-        case RSB:
-        case RSC:
-          // No 16-bit RSB/RSC Rd, Rm, Rn. It would be equivalent to SUB/SBC Rd, Rn, Rm.
-          return true;
-        case CMP:
-        default:
-          break;
-      }
-    }
-  }
-
-  // The instruction can be encoded in 16 bits.
-  return false;
-}
-
-
-void Thumb2Assembler::Emit32BitDataProcessing(Condition cond ATTRIBUTE_UNUSED,
-                                              Opcode opcode,
-                                              SetCc set_cc,
-                                              Register rn,
-                                              Register rd,
-                                              const ShifterOperand& so) {
-  uint8_t thumb_opcode = 255U /* 0b11111111 */;
-  switch (opcode) {
-    case AND: thumb_opcode =  0U /* 0b0000 */; break;
-    case EOR: thumb_opcode =  4U /* 0b0100 */; break;
-    case SUB: thumb_opcode = 13U /* 0b1101 */; break;
-    case RSB: thumb_opcode = 14U /* 0b1110 */; break;
-    case ADD: thumb_opcode =  8U /* 0b1000 */; break;
-    case ADC: thumb_opcode = 10U /* 0b1010 */; break;
-    case SBC: thumb_opcode = 11U /* 0b1011 */; break;
-    case RSC: break;
-    case TST: thumb_opcode =  0U /* 0b0000 */; DCHECK(set_cc == kCcSet); rd = PC; break;
-    case TEQ: thumb_opcode =  4U /* 0b0100 */; DCHECK(set_cc == kCcSet); rd = PC; break;
-    case CMP: thumb_opcode = 13U /* 0b1101 */; DCHECK(set_cc == kCcSet); rd = PC; break;
-    case CMN: thumb_opcode =  8U /* 0b1000 */; DCHECK(set_cc == kCcSet); rd = PC; break;
-    case ORR: thumb_opcode =  2U /* 0b0010 */; break;
-    case MOV: thumb_opcode =  2U /* 0b0010 */; rn = PC; break;
-    case BIC: thumb_opcode =  1U /* 0b0001 */; break;
-    case MVN: thumb_opcode =  3U /* 0b0011 */; rn = PC; break;
-    case ORN: thumb_opcode =  3U /* 0b0011 */; break;
-    default:
-      break;
-  }
-
-  if (thumb_opcode == 255U /* 0b11111111 */) {
-    LOG(FATAL) << "Invalid thumb2 opcode " << opcode;
-    UNREACHABLE();
-  }
-
-  int32_t encoding = 0;
-  if (so.IsImmediate()) {
-    // Check special cases.
-    if ((opcode == SUB || opcode == ADD) && (so.GetImmediate() < (1u << 12)) &&
-        /* Prefer T3 encoding to T4. */ !ShifterOperandCanAlwaysHold(so.GetImmediate())) {
-      if (set_cc != kCcSet) {
-        if (opcode == SUB) {
-          thumb_opcode = 5U;
-        } else if (opcode == ADD) {
-          thumb_opcode = 0U;
-        }
-      }
-      uint32_t imm = so.GetImmediate();
-
-      uint32_t i = (imm >> 11) & 1;
-      uint32_t imm3 = (imm >> 8) & 7U /* 0b111 */;
-      uint32_t imm8 = imm & 0xff;
-
-      encoding = B31 | B30 | B29 | B28 |
-          (set_cc == kCcSet ? B20 : B25) |
-          thumb_opcode << 21 |
-          rn << 16 |
-          rd << 8 |
-          i << 26 |
-          imm3 << 12 |
-          imm8;
-    } else {
-      // Modified immediate.
-      uint32_t imm = ModifiedImmediate(so.encodingThumb());
-      if (imm == kInvalidModifiedImmediate) {
-        LOG(FATAL) << "Immediate value cannot fit in thumb2 modified immediate";
-        UNREACHABLE();
-      }
-      encoding = B31 | B30 | B29 | B28 |
-          thumb_opcode << 21 |
-          (set_cc == kCcSet ? B20 : 0) |
-          rn << 16 |
-          rd << 8 |
-          imm;
-    }
-  } else if (so.IsRegister()) {
-    // Register (possibly shifted)
-    encoding = B31 | B30 | B29 | B27 | B25 |
-        thumb_opcode << 21 |
-        (set_cc == kCcSet ? B20 : 0) |
-        rn << 16 |
-        rd << 8 |
-        so.encodingThumb();
-  }
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::Emit16BitDataProcessing(Condition cond,
-                                              Opcode opcode,
-                                              SetCc set_cc,
-                                              Register rn,
-                                              Register rd,
-                                              const ShifterOperand& so) {
-  if (opcode == ADD || opcode == SUB) {
-    Emit16BitAddSub(cond, opcode, set_cc, rn, rd, so);
-    return;
-  }
-  uint8_t thumb_opcode = 255U /* 0b11111111 */;
-  // Thumb1.
-  uint8_t dp_opcode = 1U /* 0b01 */;
-  uint8_t opcode_shift = 6;
-  uint8_t rd_shift = 0;
-  uint8_t rn_shift = 3;
-  uint8_t immediate_shift = 0;
-  bool use_immediate = false;
-  uint8_t immediate = 0;
-
-  if (opcode == MOV && so.IsRegister() && so.IsShift()) {
-    // Convert shifted mov operand2 into 16 bit opcodes.
-    dp_opcode = 0;
-    opcode_shift = 11;
-
-    use_immediate = true;
-    immediate = so.GetImmediate();
-    immediate_shift = 6;
-
-    rn = so.GetRegister();
-
-    switch (so.GetShift()) {
-    case LSL:
-      DCHECK_LE(immediate, 31u);
-      thumb_opcode = 0U /* 0b00 */;
-      break;
-    case LSR:
-      DCHECK(1 <= immediate && immediate <= 32);
-      immediate &= 31;  // 32 is encoded as 0.
-      thumb_opcode = 1U /* 0b01 */;
-      break;
-    case ASR:
-      DCHECK(1 <= immediate && immediate <= 32);
-      immediate &= 31;  // 32 is encoded as 0.
-      thumb_opcode = 2U /* 0b10 */;
-      break;
-    case ROR:  // No 16-bit ROR immediate.
-    case RRX:  // No 16-bit RRX.
-    default:
-      LOG(FATAL) << "Unexpected shift: " << so.GetShift();
-      UNREACHABLE();
-    }
-  } else {
-    if (so.IsImmediate()) {
-      use_immediate = true;
-      immediate = so.GetImmediate();
-    } else {
-      CHECK(!(so.IsRegister() && so.IsShift() && so.GetSecondRegister() != kNoRegister))
-          << "No register-shifted register instruction available in thumb";
-      // Adjust rn and rd: only two registers will be emitted.
-      switch (opcode) {
-        case AND:
-        case ORR:
-        case EOR:
-        case RSB:
-        case ADC:
-        case SBC:
-        case BIC: {
-          // Sets condition codes if and only if outside IT block,
-          // check that it complies with set_cc.
-          DCHECK((cond == AL) ? set_cc != kCcKeep : set_cc != kCcSet);
-          if (rn == rd) {
-            rn = so.GetRegister();
-          } else {
-            CHECK_EQ(rd, so.GetRegister());
-          }
-          break;
-        }
-        case CMP:
-        case CMN: {
-          CHECK_EQ(rd, 0);
-          rd = rn;
-          rn = so.GetRegister();
-          break;
-        }
-        case MVN: {
-          // Sets condition codes if and only if outside IT block,
-          // check that it complies with set_cc.
-          DCHECK((cond == AL) ? set_cc != kCcKeep : set_cc != kCcSet);
-          CHECK_EQ(rn, 0);
-          rn = so.GetRegister();
-          break;
-        }
-        case TST:
-        case TEQ: {
-          DCHECK(set_cc == kCcSet);
-          CHECK_EQ(rn, 0);
-          rn = so.GetRegister();
-          break;
-        }
-        default:
-          break;
-      }
-    }
-
-    switch (opcode) {
-      case AND: thumb_opcode = 0U /* 0b0000 */; break;
-      case ORR: thumb_opcode = 12U /* 0b1100 */; break;
-      case EOR: thumb_opcode = 1U /* 0b0001 */; break;
-      case RSB: thumb_opcode = 9U /* 0b1001 */; break;
-      case ADC: thumb_opcode = 5U /* 0b0101 */; break;
-      case SBC: thumb_opcode = 6U /* 0b0110 */; break;
-      case BIC: thumb_opcode = 14U /* 0b1110 */; break;
-      case TST: thumb_opcode = 8U /* 0b1000 */; CHECK(!use_immediate); break;
-      case MVN: thumb_opcode = 15U /* 0b1111 */; CHECK(!use_immediate); break;
-      case CMP: {
-        DCHECK(set_cc == kCcSet);
-        if (use_immediate) {
-          // T2 encoding.
-          dp_opcode = 0;
-          opcode_shift = 11;
-          thumb_opcode = 5U /* 0b101 */;
-          rd_shift = 8;
-          rn_shift = 8;
-        } else if (IsHighRegister(rd) || IsHighRegister(rn)) {
-          // Special cmp for high registers.
-          dp_opcode = 1U /* 0b01 */;
-          opcode_shift = 7;
-          // Put the top bit of rd into the bottom bit of the opcode.
-          thumb_opcode = 10U /* 0b0001010 */ | static_cast<uint32_t>(rd) >> 3;
-          rd = static_cast<Register>(static_cast<uint32_t>(rd) & 7U /* 0b111 */);
-        } else {
-          thumb_opcode = 10U /* 0b1010 */;
-        }
-
-        break;
-      }
-      case CMN: {
-        CHECK(!use_immediate);
-        thumb_opcode = 11U /* 0b1011 */;
-        break;
-      }
-      case MOV:
-        dp_opcode = 0;
-        if (use_immediate) {
-          // T2 encoding.
-          opcode_shift = 11;
-          thumb_opcode = 4U /* 0b100 */;
-          rd_shift = 8;
-          rn_shift = 8;
-        } else {
-          rn = so.GetRegister();
-          if (set_cc != kCcSet) {
-            // Special mov for high registers.
-            dp_opcode = 1U /* 0b01 */;
-            opcode_shift = 7;
-            // Put the top bit of rd into the bottom bit of the opcode.
-            thumb_opcode = 12U /* 0b0001100 */ | static_cast<uint32_t>(rd) >> 3;
-            rd = static_cast<Register>(static_cast<uint32_t>(rd) & 7U /* 0b111 */);
-          } else {
-            DCHECK(!IsHighRegister(rn));
-            DCHECK(!IsHighRegister(rd));
-            thumb_opcode = 0;
-          }
-        }
-        break;
-
-      case TEQ:
-      case RSC:
-      default:
-        LOG(FATAL) << "Invalid thumb1 opcode " << opcode;
-        break;
-    }
-  }
-
-  if (thumb_opcode == 255U /* 0b11111111 */) {
-    LOG(FATAL) << "Invalid thumb1 opcode " << opcode;
-    UNREACHABLE();
-  }
-
-  int16_t encoding = dp_opcode << 14 |
-      (thumb_opcode << opcode_shift) |
-      rd << rd_shift |
-      rn << rn_shift |
-      (use_immediate ? (immediate << immediate_shift) : 0);
-
-  Emit16(encoding);
-}
-
-
-// ADD and SUB are complex enough to warrant their own emitter.
-void Thumb2Assembler::Emit16BitAddSub(Condition cond,
-                                      Opcode opcode,
-                                      SetCc set_cc,
-                                      Register rn,
-                                      Register rd,
-                                      const ShifterOperand& so) {
-  uint8_t dp_opcode = 0;
-  uint8_t opcode_shift = 6;
-  uint8_t rd_shift = 0;
-  uint8_t rn_shift = 3;
-  uint8_t immediate_shift = 0;
-  bool use_immediate = false;
-  uint32_t immediate = 0;  // Should be at most 10 bits but keep the full immediate for CHECKs.
-  uint8_t thumb_opcode;
-
-  if (so.IsImmediate()) {
-    use_immediate = true;
-    immediate = so.GetImmediate();
-    if (!IsUint<10>(immediate)) {
-      // Flip ADD/SUB.
-      opcode = (opcode == ADD) ? SUB : ADD;
-      immediate = -immediate;
-      DCHECK(IsUint<10>(immediate));  // More stringent checks below.
-    }
-  }
-
-  switch (opcode) {
-    case ADD:
-      if (so.IsRegister()) {
-        Register rm = so.GetRegister();
-        if (rn == rd && set_cc != kCcSet) {
-          // Can use T2 encoding (allows 4 bit registers)
-          dp_opcode = 1U /* 0b01 */;
-          opcode_shift = 10;
-          thumb_opcode = 1U /* 0b0001 */;
-          // Make Rn also contain the top bit of rd.
-          rn = static_cast<Register>(static_cast<uint32_t>(rm) |
-                                     (static_cast<uint32_t>(rd) & 8U /* 0b1000 */) << 1);
-          rd = static_cast<Register>(static_cast<uint32_t>(rd) & 7U /* 0b111 */);
-        } else {
-          // T1.
-          DCHECK(!IsHighRegister(rd));
-          DCHECK(!IsHighRegister(rn));
-          DCHECK(!IsHighRegister(rm));
-          // Sets condition codes if and only if outside IT block,
-          // check that it complies with set_cc.
-          DCHECK((cond == AL) ? set_cc != kCcKeep : set_cc != kCcSet);
-          opcode_shift = 9;
-          thumb_opcode = 12U /* 0b01100 */;
-          immediate = static_cast<uint32_t>(so.GetRegister());
-          use_immediate = true;
-          immediate_shift = 6;
-        }
-      } else {
-        // Immediate.
-        if (rd == SP && rn == SP) {
-          // ADD sp, sp, #imm
-          dp_opcode = 2U /* 0b10 */;
-          thumb_opcode = 3U /* 0b11 */;
-          opcode_shift = 12;
-          CHECK(IsUint<9>(immediate));
-          CHECK_ALIGNED(immediate, 4);
-
-          // Remove rd and rn from instruction by orring it with immed and clearing bits.
-          rn = R0;
-          rd = R0;
-          rd_shift = 0;
-          rn_shift = 0;
-          immediate >>= 2;
-        } else if (rd != SP && rn == SP) {
-          // ADD rd, SP, #imm
-          dp_opcode = 2U /* 0b10 */;
-          thumb_opcode = 5U /* 0b101 */;
-          opcode_shift = 11;
-          CHECK(IsUint<10>(immediate));
-          CHECK_ALIGNED(immediate, 4);
-
-          // Remove rn from instruction.
-          rn = R0;
-          rn_shift = 0;
-          rd_shift = 8;
-          immediate >>= 2;
-        } else if (rn != rd) {
-          // Must use T1.
-          CHECK(IsUint<3>(immediate));
-          opcode_shift = 9;
-          thumb_opcode = 14U /* 0b01110 */;
-          immediate_shift = 6;
-        } else {
-          // T2 encoding.
-          CHECK(IsUint<8>(immediate));
-          opcode_shift = 11;
-          thumb_opcode = 6U /* 0b110 */;
-          rd_shift = 8;
-          rn_shift = 8;
-        }
-      }
-      break;
-
-    case SUB:
-      if (so.IsRegister()) {
-        // T1.
-        Register rm = so.GetRegister();
-        DCHECK(!IsHighRegister(rd));
-        DCHECK(!IsHighRegister(rn));
-        DCHECK(!IsHighRegister(rm));
-        // Sets condition codes if and only if outside IT block,
-        // check that it complies with set_cc.
-        DCHECK((cond == AL) ? set_cc != kCcKeep : set_cc != kCcSet);
-        opcode_shift = 9;
-        thumb_opcode = 13U /* 0b01101 */;
-        immediate = static_cast<uint32_t>(rm);
-        use_immediate = true;
-        immediate_shift = 6;
-      } else {
-        if (rd == SP && rn == SP) {
-          // SUB sp, sp, #imm
-          dp_opcode = 2U /* 0b10 */;
-          thumb_opcode = 0x61 /* 0b1100001 */;
-          opcode_shift = 7;
-          CHECK(IsUint<9>(immediate));
-          CHECK_ALIGNED(immediate, 4);
-
-          // Remove rd and rn from instruction by orring it with immed and clearing bits.
-          rn = R0;
-          rd = R0;
-          rd_shift = 0;
-          rn_shift = 0;
-          immediate >>= 2;
-        } else if (rn != rd) {
-          // Must use T1.
-          CHECK(IsUint<3>(immediate));
-          opcode_shift = 9;
-          thumb_opcode = 15U /* 0b01111 */;
-          immediate_shift = 6;
-        } else {
-          // T2 encoding.
-          CHECK(IsUint<8>(immediate));
-          opcode_shift = 11;
-          thumb_opcode = 7U /* 0b111 */;
-          rd_shift = 8;
-          rn_shift = 8;
-        }
-      }
-      break;
-    default:
-      LOG(FATAL) << "This opcode is not an ADD or SUB: " << opcode;
-      UNREACHABLE();
-  }
-
-  int16_t encoding = dp_opcode << 14 |
-      (thumb_opcode << opcode_shift) |
-      rd << rd_shift |
-      rn << rn_shift |
-      (use_immediate ? (immediate << immediate_shift) : 0);
-
-  Emit16(encoding);
-}
-
-
-void Thumb2Assembler::EmitDataProcessing(Condition cond,
-                                         Opcode opcode,
-                                         SetCc set_cc,
-                                         Register rn,
-                                         Register rd,
-                                         const ShifterOperand& so) {
-  CHECK_NE(rd, kNoRegister);
-  CheckCondition(cond);
-
-  if (Is32BitDataProcessing(cond, opcode, set_cc, rn, rd, so)) {
-    Emit32BitDataProcessing(cond, opcode, set_cc, rn, rd, so);
-  } else {
-    Emit16BitDataProcessing(cond, opcode, set_cc, rn, rd, so);
-  }
-}
-
-void Thumb2Assembler::EmitShift(Register rd,
-                                Register rm,
-                                Shift shift,
-                                uint8_t amount,
-                                Condition cond,
-                                SetCc set_cc) {
-  CHECK_LT(amount, (1 << 5));
-  if ((IsHighRegister(rd) || IsHighRegister(rm) || shift == ROR || shift == RRX) ||
-      ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet)) {
-    uint16_t opcode = 0;
-    switch (shift) {
-      case LSL: opcode = 0U /* 0b00 */; break;
-      case LSR: opcode = 1U /* 0b01 */; break;
-      case ASR: opcode = 2U /* 0b10 */; break;
-      case ROR: opcode = 3U /* 0b11 */; break;
-      case RRX: opcode = 3U /* 0b11 */; amount = 0; break;
-      default:
-        LOG(FATAL) << "Unsupported thumb2 shift opcode";
-        UNREACHABLE();
-    }
-    // 32 bit.
-    int32_t encoding = B31 | B30 | B29 | B27 | B25 | B22 |
-        0xf << 16 | (set_cc == kCcSet ? B20 : 0);
-    uint32_t imm3 = amount >> 2;
-    uint32_t imm2 = amount & 3U /* 0b11 */;
-    encoding |= imm3 << 12 | imm2 << 6 | static_cast<int16_t>(rm) |
-        static_cast<int16_t>(rd) << 8 | opcode << 4;
-    Emit32(encoding);
-  } else {
-    // 16 bit shift
-    uint16_t opcode = 0;
-    switch (shift) {
-      case LSL: opcode = 0U /* 0b00 */; break;
-      case LSR: opcode = 1U /* 0b01 */; break;
-      case ASR: opcode = 2U /* 0b10 */; break;
-      default:
-        LOG(FATAL) << "Unsupported thumb2 shift opcode";
-        UNREACHABLE();
-    }
-    int16_t encoding = opcode << 11 | amount << 6 | static_cast<int16_t>(rm) << 3 |
-        static_cast<int16_t>(rd);
-    Emit16(encoding);
-  }
-}
-
-void Thumb2Assembler::EmitShift(Register rd,
-                                Register rn,
-                                Shift shift,
-                                Register rm,
-                                Condition cond,
-                                SetCc set_cc) {
-  CHECK_NE(shift, RRX);
-  bool must_be_32bit = false;
-  if (IsHighRegister(rd) || IsHighRegister(rm) || IsHighRegister(rn) || rd != rn ||
-      ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet)) {
-    must_be_32bit = true;
-  }
-
-  if (must_be_32bit) {
-    uint16_t opcode = 0;
-     switch (shift) {
-       case LSL: opcode = 0U /* 0b00 */; break;
-       case LSR: opcode = 1U /* 0b01 */; break;
-       case ASR: opcode = 2U /* 0b10 */; break;
-       case ROR: opcode = 3U /* 0b11 */; break;
-       default:
-         LOG(FATAL) << "Unsupported thumb2 shift opcode";
-         UNREACHABLE();
-     }
-     // 32 bit.
-     int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 |
-         0xf << 12 | (set_cc == kCcSet ? B20 : 0);
-     encoding |= static_cast<int16_t>(rn) << 16 | static_cast<int16_t>(rm) |
-         static_cast<int16_t>(rd) << 8 | opcode << 21;
-     Emit32(encoding);
-  } else {
-    uint16_t opcode = 0;
-    switch (shift) {
-      case LSL: opcode = 2U /* 0b0010 */; break;
-      case LSR: opcode = 3U /* 0b0011 */; break;
-      case ASR: opcode = 4U /* 0b0100 */; break;
-      case ROR: opcode = 7U /* 0b0111 */; break;
-      default:
-        LOG(FATAL) << "Unsupported thumb2 shift opcode";
-        UNREACHABLE();
-    }
-    int16_t encoding = B14 | opcode << 6 | static_cast<int16_t>(rm) << 3 |
-        static_cast<int16_t>(rd);
-    Emit16(encoding);
-  }
-}
-
-inline size_t Thumb2Assembler::Fixup::SizeInBytes(Size size) {
-  switch (size) {
-    case kBranch16Bit:
-      return 2u;
-    case kBranch32Bit:
-      return 4u;
-
-    case kCbxz16Bit:
-      return 2u;
-    case kCbxz32Bit:
-      return 4u;
-    case kCbxz48Bit:
-      return 6u;
-
-    case kCodeAddr4KiB:
-      return 4u;
-
-    case kLiteral1KiB:
-      return 2u;
-    case kLiteral4KiB:
-      return 4u;
-    case kLiteral64KiB:
-      return 8u;
-    case kLiteral1MiB:
-      return 10u;
-    case kLiteralFar:
-      return 14u;
-
-    case kLiteralAddr1KiB:
-      return 2u;
-    case kLiteralAddr4KiB:
-      return 4u;
-    case kLiteralAddr64KiB:
-      return 6u;
-    case kLiteralAddrFar:
-      return 10u;
-
-    case kLongOrFPLiteral1KiB:
-      return 4u;
-    case kLongOrFPLiteral64KiB:
-      return 10u;
-    case kLongOrFPLiteralFar:
-      return 14u;
-  }
-  LOG(FATAL) << "Unexpected size: " << static_cast<int>(size);
-  UNREACHABLE();
-}
-
-inline uint32_t Thumb2Assembler::Fixup::GetOriginalSizeInBytes() const {
-  return SizeInBytes(original_size_);
-}
-
-inline uint32_t Thumb2Assembler::Fixup::GetSizeInBytes() const {
-  return SizeInBytes(size_);
-}
-
-inline size_t Thumb2Assembler::Fixup::LiteralPoolPaddingSize(uint32_t current_code_size) {
-  // The code size must be a multiple of 2.
-  DCHECK_ALIGNED(current_code_size, 2);
-  // If it isn't a multiple of 4, we need to add a 2-byte padding before the literal pool.
-  return current_code_size & 2;
-}
-
-inline int32_t Thumb2Assembler::Fixup::GetOffset(uint32_t current_code_size) const {
-  static constexpr int32_t int32_min = std::numeric_limits<int32_t>::min();
-  static constexpr int32_t int32_max = std::numeric_limits<int32_t>::max();
-  DCHECK_LE(target_, static_cast<uint32_t>(int32_max));
-  DCHECK_LE(location_, static_cast<uint32_t>(int32_max));
-  DCHECK_LE(adjustment_, static_cast<uint32_t>(int32_max));
-  int32_t diff = static_cast<int32_t>(target_) - static_cast<int32_t>(location_);
-  if (target_ > location_) {
-    DCHECK_LE(adjustment_, static_cast<uint32_t>(int32_max - diff));
-    diff += static_cast<int32_t>(adjustment_);
-  } else {
-    DCHECK_LE(int32_min + static_cast<int32_t>(adjustment_), diff);
-    diff -= static_cast<int32_t>(adjustment_);
-  }
-  // The default PC adjustment for Thumb2 is 4 bytes.
-  DCHECK_GE(diff, int32_min + 4);
-  diff -= 4;
-  // Add additional adjustment for instructions preceding the PC usage, padding
-  // before the literal pool and rounding down the PC for literal loads.
-  switch (GetSize()) {
-    case kBranch16Bit:
-    case kBranch32Bit:
-      break;
-
-    case kCbxz16Bit:
-      break;
-    case kCbxz32Bit:
-    case kCbxz48Bit:
-      DCHECK_GE(diff, int32_min + 2);
-      diff -= 2;        // Extra CMP Rn, #0, 16-bit.
-      break;
-
-    case kCodeAddr4KiB:
-      // The ADR instruction rounds down the PC+4 to a multiple of 4, so if the PC
-      // isn't a multiple of 2, we need to adjust.
-      DCHECK_ALIGNED(diff, 2);
-      diff += location_ & 2;
-      // Add the Thumb mode bit.
-      diff += 1;
-      break;
-
-    case kLiteral1KiB:
-    case kLiteral4KiB:
-    case kLongOrFPLiteral1KiB:
-    case kLiteralAddr1KiB:
-    case kLiteralAddr4KiB:
-      DCHECK(diff >= 0 || (GetSize() == kLiteral1KiB && diff == -2));
-      diff += LiteralPoolPaddingSize(current_code_size);
-      // Load literal instructions round down the PC+4 to a multiple of 4, so if the PC
-      // isn't a multiple of 2, we need to adjust. Since we already adjusted for the target
-      // being aligned, current PC alignment can be inferred from diff.
-      DCHECK_ALIGNED(diff, 2);
-      diff = diff + (diff & 2);
-      DCHECK_GE(diff, 0);
-      break;
-    case kLiteral64KiB:
-    case kLiteral1MiB:
-    case kLongOrFPLiteral64KiB:
-    case kLiteralAddr64KiB:
-      DCHECK_GE(diff, 4);  // The target must be at least 4 bytes after the ADD rX, PC.
-      diff -= 4;        // One extra 32-bit MOV.
-      diff += LiteralPoolPaddingSize(current_code_size);
-      break;
-    case kLiteralFar:
-    case kLongOrFPLiteralFar:
-    case kLiteralAddrFar:
-      DCHECK_GE(diff, 8);  // The target must be at least 4 bytes after the ADD rX, PC.
-      diff -= 8;        // Extra MOVW+MOVT; both 32-bit.
-      diff += LiteralPoolPaddingSize(current_code_size);
-      break;
-  }
-  return diff;
-}
-
-inline size_t Thumb2Assembler::Fixup::IncreaseSize(Size new_size) {
-  DCHECK_NE(target_, kUnresolved);
-  Size old_size = size_;
-  size_ = new_size;
-  DCHECK_GT(SizeInBytes(new_size), SizeInBytes(old_size));
-  size_t adjustment = SizeInBytes(new_size) - SizeInBytes(old_size);
-  if (target_ > location_) {
-    adjustment_ += adjustment;
-  }
-  return adjustment;
-}
-
-bool Thumb2Assembler::Fixup::IsCandidateForEmitEarly() const {
-  DCHECK(size_ == original_size_);
-  if (target_ == kUnresolved) {
-    return false;
-  }
-  // GetOffset() does not depend on current_code_size for branches, only for literals.
-  constexpr uint32_t current_code_size = 0u;
-  switch (GetSize()) {
-    case kBranch16Bit:
-      return IsInt(cond_ != AL ? 9 : 12, GetOffset(current_code_size));
-    case kBranch32Bit:
-      // We don't support conditional branches beyond +-1MiB
-      // or unconditional branches beyond +-16MiB.
-      return true;
-
-    case kCbxz16Bit:
-      return IsUint<7>(GetOffset(current_code_size));
-    case kCbxz32Bit:
-      return IsInt<9>(GetOffset(current_code_size));
-    case kCbxz48Bit:
-      // We don't support conditional branches beyond +-1MiB.
-      return true;
-
-    case kCodeAddr4KiB:
-      // ADR uses the aligned PC and as such the offset cannot be calculated early.
-      return false;
-
-    case kLiteral1KiB:
-    case kLiteral4KiB:
-    case kLiteral64KiB:
-    case kLiteral1MiB:
-    case kLiteralFar:
-    case kLiteralAddr1KiB:
-    case kLiteralAddr4KiB:
-    case kLiteralAddr64KiB:
-    case kLiteralAddrFar:
-    case kLongOrFPLiteral1KiB:
-    case kLongOrFPLiteral64KiB:
-    case kLongOrFPLiteralFar:
-      return false;
-  }
-}
-
-uint32_t Thumb2Assembler::Fixup::AdjustSizeIfNeeded(uint32_t current_code_size) {
-  uint32_t old_code_size = current_code_size;
-  switch (GetSize()) {
-    case kBranch16Bit:
-      if (IsInt(cond_ != AL ? 9 : 12, GetOffset(current_code_size))) {
-        break;
-      }
-      current_code_size += IncreaseSize(kBranch32Bit);
-      FALLTHROUGH_INTENDED;
-    case kBranch32Bit:
-      // We don't support conditional branches beyond +-1MiB
-      // or unconditional branches beyond +-16MiB.
-      break;
-
-    case kCbxz16Bit:
-      if (IsUint<7>(GetOffset(current_code_size))) {
-        break;
-      }
-      current_code_size += IncreaseSize(kCbxz32Bit);
-      FALLTHROUGH_INTENDED;
-    case kCbxz32Bit:
-      if (IsInt<9>(GetOffset(current_code_size))) {
-        break;
-      }
-      current_code_size += IncreaseSize(kCbxz48Bit);
-      FALLTHROUGH_INTENDED;
-    case kCbxz48Bit:
-      // We don't support conditional branches beyond +-1MiB.
-      break;
-
-    case kCodeAddr4KiB:
-      // We don't support Code address ADR beyond +4KiB.
-      break;
-
-    case kLiteral1KiB:
-      DCHECK(!IsHighRegister(rn_));
-      if (IsUint<10>(GetOffset(current_code_size))) {
-        break;
-      }
-      current_code_size += IncreaseSize(kLiteral4KiB);
-      FALLTHROUGH_INTENDED;
-    case kLiteral4KiB:
-      if (IsUint<12>(GetOffset(current_code_size))) {
-        break;
-      }
-      current_code_size += IncreaseSize(kLiteral64KiB);
-      FALLTHROUGH_INTENDED;
-    case kLiteral64KiB:
-      // Can't handle high register which we can encounter by fall-through from kLiteral4KiB.
-      if (!IsHighRegister(rn_) && IsUint<16>(GetOffset(current_code_size))) {
-        break;
-      }
-      current_code_size += IncreaseSize(kLiteral1MiB);
-      FALLTHROUGH_INTENDED;
-    case kLiteral1MiB:
-      if (IsUint<20>(GetOffset(current_code_size))) {
-        break;
-      }
-      current_code_size += IncreaseSize(kLiteralFar);
-      FALLTHROUGH_INTENDED;
-    case kLiteralFar:
-      // This encoding can reach any target.
-      break;
-
-    case kLiteralAddr1KiB:
-      DCHECK(!IsHighRegister(rn_));
-      if (IsUint<10>(GetOffset(current_code_size))) {
-        break;
-      }
-      current_code_size += IncreaseSize(kLiteralAddr4KiB);
-      FALLTHROUGH_INTENDED;
-    case kLiteralAddr4KiB:
-      if (IsUint<12>(GetOffset(current_code_size))) {
-        break;
-      }
-      current_code_size += IncreaseSize(kLiteralAddr64KiB);
-      FALLTHROUGH_INTENDED;
-    case kLiteralAddr64KiB:
-      if (IsUint<16>(GetOffset(current_code_size))) {
-        break;
-      }
-      current_code_size += IncreaseSize(kLiteralAddrFar);
-      FALLTHROUGH_INTENDED;
-    case kLiteralAddrFar:
-      // This encoding can reach any target.
-      break;
-
-    case kLongOrFPLiteral1KiB:
-      if (IsUint<10>(GetOffset(current_code_size))) {
-        break;
-      }
-      current_code_size += IncreaseSize(kLongOrFPLiteral64KiB);
-      FALLTHROUGH_INTENDED;
-    case kLongOrFPLiteral64KiB:
-      if (IsUint<16>(GetOffset(current_code_size))) {
-        break;
-      }
-      current_code_size += IncreaseSize(kLongOrFPLiteralFar);
-      FALLTHROUGH_INTENDED;
-    case kLongOrFPLiteralFar:
-      // This encoding can reach any target.
-      break;
-  }
-  return current_code_size - old_code_size;
-}
-
-void Thumb2Assembler::Fixup::Emit(uint32_t emit_location,
-                                  AssemblerBuffer* buffer,
-                                  uint32_t code_size) const {
-  switch (GetSize()) {
-    case kBranch16Bit: {
-      DCHECK(type_ == kUnconditional || type_ == kConditional);
-      DCHECK_EQ(type_ == kConditional, cond_ != AL);
-      int16_t encoding = BEncoding16(GetOffset(code_size), cond_);
-      buffer->Store<int16_t>(emit_location, encoding);
-      break;
-    }
-    case kBranch32Bit: {
-      DCHECK(type_ == kConditional || type_ == kUnconditional ||
-             type_ == kUnconditionalLink || type_ == kUnconditionalLinkX);
-      DCHECK_EQ(type_ == kConditional, cond_ != AL);
-      int32_t encoding = BEncoding32(GetOffset(code_size), cond_);
-      if (type_ == kUnconditionalLink) {
-        DCHECK_NE(encoding & B12, 0);
-        encoding |= B14;
-      } else if (type_ == kUnconditionalLinkX) {
-        DCHECK_NE(encoding & B12, 0);
-        encoding ^= B14 | B12;
-      }
-      buffer->Store<int16_t>(emit_location, encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(encoding & 0xffff));
-      break;
-    }
-
-    case kCbxz16Bit: {
-      DCHECK(type_ == kCompareAndBranchXZero);
-      int16_t encoding = CbxzEncoding16(rn_, GetOffset(code_size), cond_);
-      buffer->Store<int16_t>(emit_location, encoding);
-      break;
-    }
-    case kCbxz32Bit: {
-      DCHECK(type_ == kCompareAndBranchXZero);
-      DCHECK(cond_ == EQ || cond_ == NE);
-      int16_t cmp_encoding = CmpRnImm8Encoding16(rn_, 0);
-      int16_t b_encoding = BEncoding16(GetOffset(code_size), cond_);
-      buffer->Store<int16_t>(emit_location, cmp_encoding);
-      buffer->Store<int16_t>(emit_location + 2, b_encoding);
-      break;
-    }
-    case kCbxz48Bit: {
-      DCHECK(type_ == kCompareAndBranchXZero);
-      DCHECK(cond_ == EQ || cond_ == NE);
-      int16_t cmp_encoding = CmpRnImm8Encoding16(rn_, 0);
-      int32_t b_encoding = BEncoding32(GetOffset(code_size), cond_);
-      buffer->Store<int16_t>(emit_location, cmp_encoding);
-      buffer->Store<int16_t>(emit_location + 2u, b_encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 4u, static_cast<int16_t>(b_encoding & 0xffff));
-      break;
-    }
-
-    case kCodeAddr4KiB: {
-      DCHECK(type_ == kLoadCodeAddr);
-      int32_t encoding = AdrEncoding32(rn_, GetOffset(code_size));
-      buffer->Store<int16_t>(emit_location, encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(encoding & 0xffff));
-      break;
-    }
-
-    case kLiteral1KiB: {
-      DCHECK(type_ == kLoadLiteralNarrow);
-      int16_t encoding = LdrLitEncoding16(rn_, GetOffset(code_size));
-      buffer->Store<int16_t>(emit_location, encoding);
-      break;
-    }
-    case kLiteral4KiB: {
-      DCHECK(type_ == kLoadLiteralNarrow);
-      // GetOffset() uses PC+4 but load literal uses AlignDown(PC+4, 4). Adjust offset accordingly.
-      int32_t encoding = LdrLitEncoding32(rn_, GetOffset(code_size));
-      buffer->Store<int16_t>(emit_location, encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(encoding & 0xffff));
-      break;
-    }
-    case kLiteral64KiB: {
-      DCHECK(type_ == kLoadLiteralNarrow);
-      int32_t mov_encoding = MovwEncoding32(rn_, GetOffset(code_size));
-      int16_t add_pc_encoding = AddRdnRmEncoding16(rn_, PC);
-      int16_t ldr_encoding = LdrRtRnImm5Encoding16(rn_, rn_, 0);
-      buffer->Store<int16_t>(location_, mov_encoding >> 16);
-      buffer->Store<int16_t>(location_ + 2u, static_cast<int16_t>(mov_encoding & 0xffff));
-      buffer->Store<int16_t>(location_ + 4u, add_pc_encoding);
-      buffer->Store<int16_t>(location_ + 6u, ldr_encoding);
-      break;
-    }
-    case kLiteral1MiB: {
-      DCHECK(type_ == kLoadLiteralNarrow);
-      int32_t offset = GetOffset(code_size);
-      int32_t mov_encoding = MovModImmEncoding32(rn_, offset & ~0xfff);
-      int16_t add_pc_encoding = AddRdnRmEncoding16(rn_, PC);
-      int32_t ldr_encoding = LdrRtRnImm12Encoding(rn_, rn_, offset & 0xfff);
-      buffer->Store<int16_t>(emit_location, mov_encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(mov_encoding & 0xffff));
-      buffer->Store<int16_t>(emit_location + 4u, add_pc_encoding);
-      buffer->Store<int16_t>(emit_location + 6u, ldr_encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 8u, static_cast<int16_t>(ldr_encoding & 0xffff));
-      break;
-    }
-    case kLiteralFar: {
-      DCHECK(type_ == kLoadLiteralNarrow);
-      int32_t offset = GetOffset(code_size);
-      int32_t movw_encoding = MovwEncoding32(rn_, offset & 0xffff);
-      int32_t movt_encoding = MovtEncoding32(rn_, offset & ~0xffff);
-      int16_t add_pc_encoding = AddRdnRmEncoding16(rn_, PC);
-      int32_t ldr_encoding = LdrRtRnImm12Encoding(rn_, rn_, 0);
-      buffer->Store<int16_t>(emit_location, movw_encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(movw_encoding & 0xffff));
-      buffer->Store<int16_t>(emit_location + 4u, movt_encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 6u, static_cast<int16_t>(movt_encoding & 0xffff));
-      buffer->Store<int16_t>(emit_location + 8u, add_pc_encoding);
-      buffer->Store<int16_t>(emit_location + 10u, ldr_encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 12u, static_cast<int16_t>(ldr_encoding & 0xffff));
-      break;
-    }
-
-    case kLiteralAddr1KiB: {
-      DCHECK(type_ == kLoadLiteralAddr);
-      int16_t encoding = AdrEncoding16(rn_, GetOffset(code_size));
-      buffer->Store<int16_t>(emit_location, encoding);
-      break;
-    }
-    case kLiteralAddr4KiB: {
-      DCHECK(type_ == kLoadLiteralAddr);
-      int32_t encoding = AdrEncoding32(rn_, GetOffset(code_size));
-      buffer->Store<int16_t>(emit_location, encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(encoding & 0xffff));
-      break;
-    }
-    case kLiteralAddr64KiB: {
-      DCHECK(type_ == kLoadLiteralAddr);
-      int32_t mov_encoding = MovwEncoding32(rn_, GetOffset(code_size));
-      int16_t add_pc_encoding = AddRdnRmEncoding16(rn_, PC);
-      buffer->Store<int16_t>(emit_location, mov_encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(mov_encoding & 0xffff));
-      buffer->Store<int16_t>(emit_location + 4u, add_pc_encoding);
-      break;
-    }
-    case kLiteralAddrFar: {
-      DCHECK(type_ == kLoadLiteralAddr);
-      int32_t offset = GetOffset(code_size);
-      int32_t movw_encoding = MovwEncoding32(rn_, offset & 0xffff);
-      int32_t movt_encoding = MovtEncoding32(rn_, offset & ~0xffff);
-      int16_t add_pc_encoding = AddRdnRmEncoding16(rn_, PC);
-      buffer->Store<int16_t>(emit_location, movw_encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(movw_encoding & 0xffff));
-      buffer->Store<int16_t>(emit_location + 4u, movt_encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 6u, static_cast<int16_t>(movt_encoding & 0xffff));
-      buffer->Store<int16_t>(emit_location + 8u, add_pc_encoding);
-      break;
-    }
-
-    case kLongOrFPLiteral1KiB: {
-      int32_t encoding = LoadWideOrFpEncoding(PC, GetOffset(code_size));  // DCHECKs type_.
-      buffer->Store<int16_t>(emit_location, encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(encoding & 0xffff));
-      break;
-    }
-    case kLongOrFPLiteral64KiB: {
-      int32_t mov_encoding = MovwEncoding32(IP, GetOffset(code_size));
-      int16_t add_pc_encoding = AddRdnRmEncoding16(IP, PC);
-      int32_t ldr_encoding = LoadWideOrFpEncoding(IP, 0u);    // DCHECKs type_.
-      buffer->Store<int16_t>(emit_location, mov_encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(mov_encoding & 0xffff));
-      buffer->Store<int16_t>(emit_location + 4u, add_pc_encoding);
-      buffer->Store<int16_t>(emit_location + 6u, ldr_encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 8u, static_cast<int16_t>(ldr_encoding & 0xffff));
-      break;
-    }
-    case kLongOrFPLiteralFar: {
-      int32_t offset = GetOffset(code_size);
-      int32_t movw_encoding = MovwEncoding32(IP, offset & 0xffff);
-      int32_t movt_encoding = MovtEncoding32(IP, offset & ~0xffff);
-      int16_t add_pc_encoding = AddRdnRmEncoding16(IP, PC);
-      int32_t ldr_encoding = LoadWideOrFpEncoding(IP, 0);                 // DCHECKs type_.
-      buffer->Store<int16_t>(emit_location, movw_encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(movw_encoding & 0xffff));
-      buffer->Store<int16_t>(emit_location + 4u, movt_encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 6u, static_cast<int16_t>(movt_encoding & 0xffff));
-      buffer->Store<int16_t>(emit_location + 8u, add_pc_encoding);
-      buffer->Store<int16_t>(emit_location + 10u, ldr_encoding >> 16);
-      buffer->Store<int16_t>(emit_location + 12u, static_cast<int16_t>(ldr_encoding & 0xffff));
-      break;
-    }
-  }
-}
-
-uint16_t Thumb2Assembler::EmitCompareAndBranch(Register rn, uint16_t prev, bool n) {
-  CHECK(IsLowRegister(rn));
-  uint32_t location = buffer_.Size();
-
-  // This is always unresolved as it must be a forward branch.
-  Emit16(prev);      // Previous link.
-  return AddFixup(Fixup::CompareAndBranch(location, rn, n ? NE : EQ));
-}
-
-
-// NOTE: this only support immediate offsets, not [rx,ry].
-// TODO: support [rx,ry] instructions.
-void Thumb2Assembler::EmitLoadStore(Condition cond,
-                                    bool load,
-                                    bool byte,
-                                    bool half,
-                                    bool is_signed,
-                                    Register rd,
-                                    const Address& ad) {
-  CHECK_NE(rd, kNoRegister);
-  CheckCondition(cond);
-  bool must_be_32bit = force_32bit_;
-  if (IsHighRegister(rd)) {
-    must_be_32bit = true;
-  }
-
-  Register rn = ad.GetRegister();
-  if (IsHighRegister(rn) && (byte || half || (rn != SP && rn != PC))) {
-    must_be_32bit = true;
-  }
-
-  if (is_signed || ad.GetOffset() < 0 || ad.GetMode() != Address::Offset) {
-    must_be_32bit = true;
-  }
-
-  if (ad.IsImmediate()) {
-    // Immediate offset
-    int32_t offset = ad.GetOffset();
-
-    if (byte) {
-      // 5 bit offset, no shift.
-      if ((offset & ~0x1f) != 0) {
-        must_be_32bit = true;
-      }
-    } else if (half) {
-      // 5 bit offset, shifted by 1.
-      if ((offset & ~(0x1f << 1)) != 0) {
-        must_be_32bit = true;
-      }
-    } else if (rn == SP || rn == PC) {
-      // The 16 bit SP/PC relative instruction can only have an (imm8 << 2) offset.
-      if ((offset & ~(0xff << 2)) != 0) {
-        must_be_32bit = true;
-      }
-    } else {
-      // 5 bit offset, shifted by 2.
-      if ((offset & ~(0x1f << 2)) != 0) {
-        must_be_32bit = true;
-      }
-    }
-
-    if (must_be_32bit) {
-      int32_t encoding = B31 | B30 | B29 | B28 | B27 |
-          (load ? B20 : 0) |
-          (is_signed ? B24 : 0) |
-          static_cast<uint32_t>(rd) << 12 |
-          ad.encodingThumb(true) |
-          (byte ? 0 : half ? B21 : B22);
-      Emit32(encoding);
-    } else {
-      // 16 bit thumb1.
-      uint8_t opA = 0;
-      bool sp_or_pc_relative = false;
-
-      if (byte) {
-        opA = 7U /* 0b0111 */;
-      } else if (half) {
-        opA = 8U /* 0b1000 */;
-      } else {
-        if (rn == SP) {
-          opA = 9U /* 0b1001 */;
-          sp_or_pc_relative = true;
-        } else if (rn == PC) {
-          opA = 4U;
-          sp_or_pc_relative = true;
-        } else {
-          opA = 6U /* 0b0110 */;
-        }
-      }
-      int16_t encoding = opA << 12 |
-          (load ? B11 : 0);
-
-      CHECK_GE(offset, 0);
-      if (sp_or_pc_relative) {
-        // SP relative, 10 bit offset.
-        CHECK_LT(offset, (1 << 10));
-        CHECK_ALIGNED(offset, 4);
-        encoding |= rd << 8 | offset >> 2;
-      } else {
-        // No SP relative.  The offset is shifted right depending on
-        // the size of the load/store.
-        encoding |= static_cast<uint32_t>(rd);
-
-        if (byte) {
-          // 5 bit offset, no shift.
-          CHECK_LT(offset, (1 << 5));
-        } else if (half) {
-          // 6 bit offset, shifted by 1.
-          CHECK_LT(offset, (1 << 6));
-          CHECK_ALIGNED(offset, 2);
-          offset >>= 1;
-        } else {
-          // 7 bit offset, shifted by 2.
-          CHECK_LT(offset, (1 << 7));
-          CHECK_ALIGNED(offset, 4);
-          offset >>= 2;
-        }
-        encoding |= rn << 3 | offset  << 6;
-      }
-
-      Emit16(encoding);
-    }
-  } else {
-    // Register shift.
-    CHECK_NE(ad.GetRegister(), PC);
-    if (ad.GetShiftCount() != 0) {
-      // If there is a shift count this must be 32 bit.
-      must_be_32bit = true;
-    } else if (IsHighRegister(ad.GetRegisterOffset())) {
-      must_be_32bit = true;
-    }
-
-    if (must_be_32bit) {
-      int32_t encoding = 0x1f << 27 | (load ? B20 : 0) | static_cast<uint32_t>(rd) << 12 |
-          ad.encodingThumb(true);
-      if (half) {
-        encoding |= B21;
-      } else if (!byte) {
-        encoding |= B22;
-      }
-      if (load && is_signed && (byte || half)) {
-        encoding |= B24;
-      }
-      Emit32(encoding);
-    } else {
-      // 16 bit register offset.
-      int32_t encoding = B14 | B12 | (load ? B11 : 0) | static_cast<uint32_t>(rd) |
-          ad.encodingThumb(false);
-      if (byte) {
-        encoding |= B10;
-      } else if (half) {
-        encoding |= B9;
-      }
-      Emit16(encoding);
-    }
-  }
-}
-
-
-void Thumb2Assembler::EmitMultiMemOp(Condition cond,
-                                     BlockAddressMode bam,
-                                     bool load,
-                                     Register base,
-                                     RegList regs) {
-  CHECK_NE(base, kNoRegister);
-  CheckCondition(cond);
-  bool must_be_32bit = force_32bit_;
-
-  if (!must_be_32bit && base == SP && bam == (load ? IA_W : DB_W) &&
-      (regs & 0xff00 & ~(1 << (load ? PC : LR))) == 0) {
-    // Use 16-bit PUSH/POP.
-    int16_t encoding = B15 | B13 | B12 | (load ? B11 : 0) | B10 |
-        ((regs & (1 << (load ? PC : LR))) != 0 ? B8 : 0) | (regs & 0x00ff);
-    Emit16(encoding);
-    return;
-  }
-
-  if ((regs & 0xff00) != 0) {
-    must_be_32bit = true;
-  }
-
-  bool w_bit = bam == IA_W || bam == DB_W || bam == DA_W || bam == IB_W;
-  // 16 bit always uses writeback.
-  if (!w_bit) {
-    must_be_32bit = true;
-  }
-
-  if (must_be_32bit) {
-    uint32_t op = 0;
-    switch (bam) {
-      case IA:
-      case IA_W:
-        op = 1U /* 0b01 */;
-        break;
-      case DB:
-      case DB_W:
-        op = 2U /* 0b10 */;
-        break;
-      case DA:
-      case IB:
-      case DA_W:
-      case IB_W:
-        LOG(FATAL) << "LDM/STM mode not supported on thumb: " << bam;
-        UNREACHABLE();
-    }
-    if (load) {
-      // Cannot have SP in the list.
-      CHECK_EQ((regs & (1 << SP)), 0);
-    } else {
-      // Cannot have PC or SP in the list.
-      CHECK_EQ((regs & (1 << PC | 1 << SP)), 0);
-    }
-    int32_t encoding = B31 | B30 | B29 | B27 |
-                    (op << 23) |
-                    (load ? B20 : 0) |
-                    base << 16 |
-                    regs |
-                    (w_bit << 21);
-    Emit32(encoding);
-  } else {
-    int16_t encoding = B15 | B14 |
-                    (load ? B11 : 0) |
-                    base << 8 |
-                    regs;
-    Emit16(encoding);
-  }
-}
-
-void Thumb2Assembler::EmitBranch(Condition cond, Label* label, bool link, bool x) {
-  bool use32bit = IsForced32Bit() || !CanRelocateBranches();
-  uint32_t pc = buffer_.Size();
-  Fixup::Type branch_type;
-  if (cond == AL) {
-    if (link) {
-      use32bit = true;
-      if (x) {
-        branch_type = Fixup::kUnconditionalLinkX;      // BLX.
-      } else {
-        branch_type = Fixup::kUnconditionalLink;       // BX.
-      }
-    } else {
-      branch_type = Fixup::kUnconditional;             // B.
-      // The T2 encoding offset is `SignExtend(imm11:'0', 32)` and there is a PC adjustment of 4.
-      static constexpr size_t kMaxT2BackwardDistance = (1u << 11) - 4u;
-      if (!use32bit && label->IsBound() && pc - label->Position() > kMaxT2BackwardDistance) {
-        use32bit = true;
-      }
-    }
-  } else {
-    branch_type = Fixup::kConditional;                 // B<cond>.
-    // The T1 encoding offset is `SignExtend(imm8:'0', 32)` and there is a PC adjustment of 4.
-    static constexpr size_t kMaxT1BackwardDistance = (1u << 8) - 4u;
-    if (!use32bit && label->IsBound() && pc - label->Position() > kMaxT1BackwardDistance) {
-      use32bit = true;
-    }
-  }
-
-  Fixup::Size size = use32bit ? Fixup::kBranch32Bit : Fixup::kBranch16Bit;
-  FixupId branch_id = AddFixup(Fixup::Branch(pc, branch_type, size, cond));
-
-  if (label->IsBound()) {
-    // The branch is to a bound label which means that it's a backwards branch.
-    GetFixup(branch_id)->Resolve(label->Position());
-    Emit16(0);
-  } else {
-    // Branch target is an unbound label. Add it to a singly-linked list maintained within
-    // the code with the label serving as the head.
-    Emit16(static_cast<uint16_t>(label->position_));
-    label->LinkTo(branch_id);
-  }
-
-  if (use32bit) {
-    Emit16(0);
-  }
-  DCHECK_EQ(buffer_.Size() - pc, GetFixup(branch_id)->GetSizeInBytes());
-}
-
-
-void Thumb2Assembler::Emit32Miscellaneous(uint8_t op1,
-                                          uint8_t op2,
-                                          uint32_t rest_encoding) {
-  int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B23 |
-      op1 << 20 |
-      0xf << 12 |
-      B7 |
-      op2 << 4 |
-      rest_encoding;
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::Emit16Miscellaneous(uint32_t rest_encoding) {
-  int16_t encoding = B15 | B13 | B12 |
-      rest_encoding;
-  Emit16(encoding);
-}
-
-void Thumb2Assembler::clz(Register rd, Register rm, Condition cond) {
-  CHECK_NE(rd, kNoRegister);
-  CHECK_NE(rm, kNoRegister);
-  CheckCondition(cond);
-  CHECK_NE(rd, PC);
-  CHECK_NE(rm, PC);
-  int32_t encoding =
-      static_cast<uint32_t>(rm) << 16 |
-      static_cast<uint32_t>(rd) << 8 |
-      static_cast<uint32_t>(rm);
-  Emit32Miscellaneous(0b11, 0b00, encoding);
-}
-
-
-void Thumb2Assembler::movw(Register rd, uint16_t imm16, Condition cond) {
-  CheckCondition(cond);
-  // Always 32 bits, encoding T3. (Other encondings are called MOV, not MOVW.)
-  uint32_t imm4 = (imm16 >> 12) & 15U /* 0b1111 */;
-  uint32_t i = (imm16 >> 11) & 1U /* 0b1 */;
-  uint32_t imm3 = (imm16 >> 8) & 7U /* 0b111 */;
-  uint32_t imm8 = imm16 & 0xff;
-  int32_t encoding = B31 | B30 | B29 | B28 |
-                  B25 | B22 |
-                  static_cast<uint32_t>(rd) << 8 |
-                  i << 26 |
-                  imm4 << 16 |
-                  imm3 << 12 |
-                  imm8;
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::movt(Register rd, uint16_t imm16, Condition cond) {
-  CheckCondition(cond);
-  // Always 32 bits.
-  uint32_t imm4 = (imm16 >> 12) & 15U /* 0b1111 */;
-  uint32_t i = (imm16 >> 11) & 1U /* 0b1 */;
-  uint32_t imm3 = (imm16 >> 8) & 7U /* 0b111 */;
-  uint32_t imm8 = imm16 & 0xff;
-  int32_t encoding = B31 | B30 | B29 | B28 |
-                  B25 | B23 | B22 |
-                  static_cast<uint32_t>(rd) << 8 |
-                  i << 26 |
-                  imm4 << 16 |
-                  imm3 << 12 |
-                  imm8;
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::rbit(Register rd, Register rm, Condition cond) {
-  CHECK_NE(rd, kNoRegister);
-  CHECK_NE(rm, kNoRegister);
-  CheckCondition(cond);
-  CHECK_NE(rd, PC);
-  CHECK_NE(rm, PC);
-  CHECK_NE(rd, SP);
-  CHECK_NE(rm, SP);
-  int32_t encoding =
-      static_cast<uint32_t>(rm) << 16 |
-      static_cast<uint32_t>(rd) << 8 |
-      static_cast<uint32_t>(rm);
-
-  Emit32Miscellaneous(0b01, 0b10, encoding);
-}
-
-
-void Thumb2Assembler::EmitReverseBytes(Register rd, Register rm,
-                                       uint32_t op) {
-  CHECK_NE(rd, kNoRegister);
-  CHECK_NE(rm, kNoRegister);
-  CHECK_NE(rd, PC);
-  CHECK_NE(rm, PC);
-  CHECK_NE(rd, SP);
-  CHECK_NE(rm, SP);
-
-  if (!IsHighRegister(rd) && !IsHighRegister(rm) && !force_32bit_) {
-    uint16_t t1_op = B11 | B9 | (op << 6);
-    int16_t encoding = t1_op |
-        static_cast<uint16_t>(rm) << 3 |
-        static_cast<uint16_t>(rd);
-    Emit16Miscellaneous(encoding);
-  } else {
-    int32_t encoding =
-        static_cast<uint32_t>(rm) << 16 |
-        static_cast<uint32_t>(rd) << 8 |
-        static_cast<uint32_t>(rm);
-    Emit32Miscellaneous(0b01, op, encoding);
-  }
-}
-
-
-void Thumb2Assembler::rev(Register rd, Register rm, Condition cond) {
-  CheckCondition(cond);
-  EmitReverseBytes(rd, rm, 0b00);
-}
-
-
-void Thumb2Assembler::rev16(Register rd, Register rm, Condition cond) {
-  CheckCondition(cond);
-  EmitReverseBytes(rd, rm, 0b01);
-}
-
-
-void Thumb2Assembler::revsh(Register rd, Register rm, Condition cond) {
-  CheckCondition(cond);
-  EmitReverseBytes(rd, rm, 0b11);
-}
-
-
-void Thumb2Assembler::ldrex(Register rt, Register rn, uint16_t imm, Condition cond) {
-  CHECK_NE(rn, kNoRegister);
-  CHECK_NE(rt, kNoRegister);
-  CheckCondition(cond);
-  CHECK_LT(imm, (1u << 10));
-
-  int32_t encoding = B31 | B30 | B29 | B27 | B22 | B20 |
-      static_cast<uint32_t>(rn) << 16 |
-      static_cast<uint32_t>(rt) << 12 |
-      0xf << 8 |
-      imm >> 2;
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::ldrex(Register rt, Register rn, Condition cond) {
-  ldrex(rt, rn, 0, cond);
-}
-
-
-void Thumb2Assembler::strex(Register rd,
-                            Register rt,
-                            Register rn,
-                            uint16_t imm,
-                            Condition cond) {
-  CHECK_NE(rn, kNoRegister);
-  CHECK_NE(rd, kNoRegister);
-  CHECK_NE(rt, kNoRegister);
-  CheckCondition(cond);
-  CHECK_LT(imm, (1u << 10));
-
-  int32_t encoding = B31 | B30 | B29 | B27 | B22 |
-      static_cast<uint32_t>(rn) << 16 |
-      static_cast<uint32_t>(rt) << 12 |
-      static_cast<uint32_t>(rd) << 8 |
-      imm >> 2;
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::ldrexd(Register rt, Register rt2, Register rn, Condition cond) {
-  CHECK_NE(rn, kNoRegister);
-  CHECK_NE(rt, kNoRegister);
-  CHECK_NE(rt2, kNoRegister);
-  CHECK_NE(rt, rt2);
-  CheckCondition(cond);
-
-  int32_t encoding = B31 | B30 | B29 | B27 | B23 | B22 | B20 |
-      static_cast<uint32_t>(rn) << 16 |
-      static_cast<uint32_t>(rt) << 12 |
-      static_cast<uint32_t>(rt2) << 8 |
-      B6 | B5 | B4 | B3 | B2 | B1 | B0;
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::strex(Register rd,
-                            Register rt,
-                            Register rn,
-                            Condition cond) {
-  strex(rd, rt, rn, 0, cond);
-}
-
-
-void Thumb2Assembler::strexd(Register rd, Register rt, Register rt2, Register rn, Condition cond) {
-  CHECK_NE(rd, kNoRegister);
-  CHECK_NE(rn, kNoRegister);
-  CHECK_NE(rt, kNoRegister);
-  CHECK_NE(rt2, kNoRegister);
-  CHECK_NE(rt, rt2);
-  CHECK_NE(rd, rt);
-  CHECK_NE(rd, rt2);
-  CheckCondition(cond);
-
-  int32_t encoding = B31 | B30 | B29 | B27 | B23 | B22 |
-      static_cast<uint32_t>(rn) << 16 |
-      static_cast<uint32_t>(rt) << 12 |
-      static_cast<uint32_t>(rt2) << 8 |
-      B6 | B5 | B4 |
-      static_cast<uint32_t>(rd);
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::clrex(Condition cond) {
-  CheckCondition(cond);
-  int32_t encoding = B31 | B30 | B29 | B28 | B25 | B24 | B23 |
-      B21 | B20 |
-      0xf << 16 |
-      B15 |
-      0xf << 8 |
-      B5 |
-      0xf;
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::nop(Condition cond) {
-  CheckCondition(cond);
-  uint16_t encoding = B15 | B13 | B12 |
-      B11 | B10 | B9 | B8;
-  Emit16(static_cast<int16_t>(encoding));
-}
-
-
-void Thumb2Assembler::vmovsr(SRegister sn, Register rt, Condition cond) {
-  CHECK_NE(sn, kNoSRegister);
-  CHECK_NE(rt, kNoRegister);
-  CHECK_NE(rt, SP);
-  CHECK_NE(rt, PC);
-  CheckCondition(cond);
-  int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
-                     B27 | B26 | B25 |
-                     ((static_cast<int32_t>(sn) >> 1)*B16) |
-                     (static_cast<int32_t>(rt)*B12) | B11 | B9 |
-                     ((static_cast<int32_t>(sn) & 1)*B7) | B4;
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::vmovrs(Register rt, SRegister sn, Condition cond) {
-  CHECK_NE(sn, kNoSRegister);
-  CHECK_NE(rt, kNoRegister);
-  CHECK_NE(rt, SP);
-  CHECK_NE(rt, PC);
-  CheckCondition(cond);
-  int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
-                     B27 | B26 | B25 | B20 |
-                     ((static_cast<int32_t>(sn) >> 1)*B16) |
-                     (static_cast<int32_t>(rt)*B12) | B11 | B9 |
-                     ((static_cast<int32_t>(sn) & 1)*B7) | B4;
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::vmovsrr(SRegister sm, Register rt, Register rt2,
-                              Condition cond) {
-  CHECK_NE(sm, kNoSRegister);
-  CHECK_NE(sm, S31);
-  CHECK_NE(rt, kNoRegister);
-  CHECK_NE(rt, SP);
-  CHECK_NE(rt, PC);
-  CHECK_NE(rt2, kNoRegister);
-  CHECK_NE(rt2, SP);
-  CHECK_NE(rt2, PC);
-  CheckCondition(cond);
-  int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
-                     B27 | B26 | B22 |
-                     (static_cast<int32_t>(rt2)*B16) |
-                     (static_cast<int32_t>(rt)*B12) | B11 | B9 |
-                     ((static_cast<int32_t>(sm) & 1)*B5) | B4 |
-                     (static_cast<int32_t>(sm) >> 1);
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::vmovrrs(Register rt, Register rt2, SRegister sm,
-                              Condition cond) {
-  CHECK_NE(sm, kNoSRegister);
-  CHECK_NE(sm, S31);
-  CHECK_NE(rt, kNoRegister);
-  CHECK_NE(rt, SP);
-  CHECK_NE(rt, PC);
-  CHECK_NE(rt2, kNoRegister);
-  CHECK_NE(rt2, SP);
-  CHECK_NE(rt2, PC);
-  CHECK_NE(rt, rt2);
-  CheckCondition(cond);
-  int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
-                     B27 | B26 | B22 | B20 |
-                     (static_cast<int32_t>(rt2)*B16) |
-                     (static_cast<int32_t>(rt)*B12) | B11 | B9 |
-                     ((static_cast<int32_t>(sm) & 1)*B5) | B4 |
-                     (static_cast<int32_t>(sm) >> 1);
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::vmovdrr(DRegister dm, Register rt, Register rt2,
-                              Condition cond) {
-  CHECK_NE(dm, kNoDRegister);
-  CHECK_NE(rt, kNoRegister);
-  CHECK_NE(rt, SP);
-  CHECK_NE(rt, PC);
-  CHECK_NE(rt2, kNoRegister);
-  CHECK_NE(rt2, SP);
-  CHECK_NE(rt2, PC);
-  CheckCondition(cond);
-  int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
-                     B27 | B26 | B22 |
-                     (static_cast<int32_t>(rt2)*B16) |
-                     (static_cast<int32_t>(rt)*B12) | B11 | B9 | B8 |
-                     ((static_cast<int32_t>(dm) >> 4)*B5) | B4 |
-                     (static_cast<int32_t>(dm) & 0xf);
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::vmovrrd(Register rt, Register rt2, DRegister dm,
-                              Condition cond) {
-  CHECK_NE(dm, kNoDRegister);
-  CHECK_NE(rt, kNoRegister);
-  CHECK_NE(rt, SP);
-  CHECK_NE(rt, PC);
-  CHECK_NE(rt2, kNoRegister);
-  CHECK_NE(rt2, SP);
-  CHECK_NE(rt2, PC);
-  CHECK_NE(rt, rt2);
-  CheckCondition(cond);
-  int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
-                     B27 | B26 | B22 | B20 |
-                     (static_cast<int32_t>(rt2)*B16) |
-                     (static_cast<int32_t>(rt)*B12) | B11 | B9 | B8 |
-                     ((static_cast<int32_t>(dm) >> 4)*B5) | B4 |
-                     (static_cast<int32_t>(dm) & 0xf);
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::vldrs(SRegister sd, const Address& ad, Condition cond) {
-  const Address& addr = static_cast<const Address&>(ad);
-  CHECK_NE(sd, kNoSRegister);
-  CheckCondition(cond);
-  int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
-                     B27 | B26 | B24 | B20 |
-                     ((static_cast<int32_t>(sd) & 1)*B22) |
-                     ((static_cast<int32_t>(sd) >> 1)*B12) |
-                     B11 | B9 | addr.vencoding();
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::vstrs(SRegister sd, const Address& ad, Condition cond) {
-  const Address& addr = static_cast<const Address&>(ad);
-  CHECK_NE(static_cast<Register>(addr.encodingArm() & (0xf << kRnShift)), PC);
-  CHECK_NE(sd, kNoSRegister);
-  CheckCondition(cond);
-  int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
-                     B27 | B26 | B24 |
-                     ((static_cast<int32_t>(sd) & 1)*B22) |
-                     ((static_cast<int32_t>(sd) >> 1)*B12) |
-                     B11 | B9 | addr.vencoding();
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::vldrd(DRegister dd, const Address& ad, Condition cond) {
-  const Address& addr = static_cast<const Address&>(ad);
-  CHECK_NE(dd, kNoDRegister);
-  CheckCondition(cond);
-  int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
-                     B27 | B26 | B24 | B20 |
-                     ((static_cast<int32_t>(dd) >> 4)*B22) |
-                     ((static_cast<int32_t>(dd) & 0xf)*B12) |
-                     B11 | B9 | B8 | addr.vencoding();
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::vstrd(DRegister dd, const Address& ad, Condition cond) {
-  const Address& addr = static_cast<const Address&>(ad);
-  CHECK_NE(static_cast<Register>(addr.encodingArm() & (0xf << kRnShift)), PC);
-  CHECK_NE(dd, kNoDRegister);
-  CheckCondition(cond);
-  int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
-                     B27 | B26 | B24 |
-                     ((static_cast<int32_t>(dd) >> 4)*B22) |
-                     ((static_cast<int32_t>(dd) & 0xf)*B12) |
-                     B11 | B9 | B8 | addr.vencoding();
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::vpushs(SRegister reg, int nregs, Condition cond) {
-  EmitVPushPop(static_cast<uint32_t>(reg), nregs, true, false, cond);
-}
-
-
-void Thumb2Assembler::vpushd(DRegister reg, int nregs, Condition cond) {
-  EmitVPushPop(static_cast<uint32_t>(reg), nregs, true, true, cond);
-}
-
-
-void Thumb2Assembler::vpops(SRegister reg, int nregs, Condition cond) {
-  EmitVPushPop(static_cast<uint32_t>(reg), nregs, false, false, cond);
-}
-
-
-void Thumb2Assembler::vpopd(DRegister reg, int nregs, Condition cond) {
-  EmitVPushPop(static_cast<uint32_t>(reg), nregs, false, true, cond);
-}
-
-
-void Thumb2Assembler::vldmiad(Register base_reg, DRegister reg, int nregs, Condition cond) {
-  int32_t rest = B23;
-  EmitVLdmOrStm(rest,
-                static_cast<uint32_t>(reg),
-                nregs,
-                base_reg,
-                /*is_load*/ true,
-                /*dbl*/ true,
-                cond);
-}
-
-
-void Thumb2Assembler::vstmiad(Register base_reg, DRegister reg, int nregs, Condition cond) {
-  int32_t rest = B23;
-  EmitVLdmOrStm(rest,
-                static_cast<uint32_t>(reg),
-                nregs,
-                base_reg,
-                /*is_load*/ false,
-                /*dbl*/ true,
-                cond);
-}
-
-
-void Thumb2Assembler::EmitVPushPop(uint32_t reg, int nregs, bool push, bool dbl, Condition cond) {
-  int32_t rest = B21 | (push ? B24 : B23);
-  EmitVLdmOrStm(rest, reg, nregs, SP, /*is_load*/ !push, dbl, cond);
-}
-
-
-void Thumb2Assembler::EmitVLdmOrStm(int32_t rest,
-                                    uint32_t reg,
-                                    int nregs,
-                                    Register rn,
-                                    bool is_load,
-                                    bool dbl,
-                                    Condition cond) {
-  CheckCondition(cond);
-
-  DCHECK_GT(nregs, 0);
-  DCHECK_LE(reg + nregs, 32u);
-  DCHECK(!dbl || (nregs <= 16));
-
-  uint32_t D;
-  uint32_t Vd;
-  if (dbl) {
-    // Encoded as D:Vd.
-    D = (reg >> 4) & 1;
-    Vd = reg & 15U /* 0b1111 */;
-  } else {
-    // Encoded as Vd:D.
-    D = reg & 1;
-    Vd = (reg >> 1) & 15U /* 0b1111 */;
-  }
-
-  int32_t encoding = rest |
-                     14U /* 0b1110 */ << 28 |
-                     B27 | B26 | B11 | B9 |
-                     (is_load ? B20 : 0) |
-                     static_cast<int16_t>(rn) << 16 |
-                     D << 22 |
-                     Vd << 12 |
-                     (dbl ? B8 : 0) |
-                     nregs << (dbl ? 1 : 0);
-
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::EmitVFPsss(Condition cond, int32_t opcode,
-                                 SRegister sd, SRegister sn, SRegister sm) {
-  CHECK_NE(sd, kNoSRegister);
-  CHECK_NE(sn, kNoSRegister);
-  CHECK_NE(sm, kNoSRegister);
-  CheckCondition(cond);
-  int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
-                     B27 | B26 | B25 | B11 | B9 | opcode |
-                     ((static_cast<int32_t>(sd) & 1)*B22) |
-                     ((static_cast<int32_t>(sn) >> 1)*B16) |
-                     ((static_cast<int32_t>(sd) >> 1)*B12) |
-                     ((static_cast<int32_t>(sn) & 1)*B7) |
-                     ((static_cast<int32_t>(sm) & 1)*B5) |
-                     (static_cast<int32_t>(sm) >> 1);
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::EmitVFPddd(Condition cond, int32_t opcode,
-                                 DRegister dd, DRegister dn, DRegister dm) {
-  CHECK_NE(dd, kNoDRegister);
-  CHECK_NE(dn, kNoDRegister);
-  CHECK_NE(dm, kNoDRegister);
-  CheckCondition(cond);
-  int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
-                     B27 | B26 | B25 | B11 | B9 | B8 | opcode |
-                     ((static_cast<int32_t>(dd) >> 4)*B22) |
-                     ((static_cast<int32_t>(dn) & 0xf)*B16) |
-                     ((static_cast<int32_t>(dd) & 0xf)*B12) |
-                     ((static_cast<int32_t>(dn) >> 4)*B7) |
-                     ((static_cast<int32_t>(dm) >> 4)*B5) |
-                     (static_cast<int32_t>(dm) & 0xf);
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::EmitVFPsd(Condition cond, int32_t opcode,
-                                SRegister sd, DRegister dm) {
-  CHECK_NE(sd, kNoSRegister);
-  CHECK_NE(dm, kNoDRegister);
-  CheckCondition(cond);
-  int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
-                     B27 | B26 | B25 | B11 | B9 | opcode |
-                     ((static_cast<int32_t>(sd) & 1)*B22) |
-                     ((static_cast<int32_t>(sd) >> 1)*B12) |
-                     ((static_cast<int32_t>(dm) >> 4)*B5) |
-                     (static_cast<int32_t>(dm) & 0xf);
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::EmitVFPds(Condition cond, int32_t opcode,
-                                DRegister dd, SRegister sm) {
-  CHECK_NE(dd, kNoDRegister);
-  CHECK_NE(sm, kNoSRegister);
-  CheckCondition(cond);
-  int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
-                     B27 | B26 | B25 | B11 | B9 | opcode |
-                     ((static_cast<int32_t>(dd) >> 4)*B22) |
-                     ((static_cast<int32_t>(dd) & 0xf)*B12) |
-                     ((static_cast<int32_t>(sm) & 1)*B5) |
-                     (static_cast<int32_t>(sm) >> 1);
-  Emit32(encoding);
-}
-
-
-void Thumb2Assembler::vmstat(Condition cond) {  // VMRS APSR_nzcv, FPSCR.
-  CHECK_NE(cond, kNoCondition);
-  CheckCondition(cond);
-  int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
-      B27 | B26 | B25 | B23 | B22 | B21 | B20 | B16 |
-      (static_cast<int32_t>(PC)*B12) |
-      B11 | B9 | B4;
-  Emit32(encoding);
-}
-
-void Thumb2Assembler::vcntd(DRegister dd, DRegister dm) {
-  uint32_t encoding = (B31 | B30 | B29 | B28 | B27 | B26 | B25 | B24 | B23 | B21 | B20) |
-    ((static_cast<int32_t>(dd) >> 4) * B22) |
-    ((static_cast<uint32_t>(dd) & 0xf) * B12) |
-    (B10 | B8) |
-    ((static_cast<int32_t>(dm) >> 4) * B5) |
-    (static_cast<uint32_t>(dm) & 0xf);
-
-  Emit32(encoding);
-}
-
-void Thumb2Assembler::vpaddld(DRegister dd, DRegister dm, int32_t size, bool is_unsigned) {
-  CHECK(size == 8 || size == 16 || size == 32) << size;
-  uint32_t encoding = (B31 | B30 | B29 | B28 | B27 | B26 | B25 | B24 | B23 | B21 | B20) |
-    ((static_cast<uint32_t>(size >> 4) & 0x3) * B18) |
-    ((static_cast<int32_t>(dd) >> 4) * B22) |
-    ((static_cast<uint32_t>(dd) & 0xf) * B12) |
-    (B9) |
-    (is_unsigned ? B7 : 0) |
-    ((static_cast<int32_t>(dm) >> 4) * B5) |
-    (static_cast<uint32_t>(dm) & 0xf);
-
-  Emit32(encoding);
-}
-
-void Thumb2Assembler::svc(uint32_t imm8) {
-  CHECK(IsUint<8>(imm8)) << imm8;
-  int16_t encoding = B15 | B14 | B12 |
-       B11 | B10 | B9 | B8 |
-       imm8;
-  Emit16(encoding);
-}
-
-
-void Thumb2Assembler::bkpt(uint16_t imm8) {
-  CHECK(IsUint<8>(imm8)) << imm8;
-  int16_t encoding = B15 | B13 | B12 |
-      B11 | B10 | B9 |
-      imm8;
-  Emit16(encoding);
-}
-
-// Convert the given IT state to a mask bit given bit 0 of the first
-// condition and a shift position.
-static uint8_t ToItMask(ItState s, uint8_t firstcond0, uint8_t shift) {
-  switch (s) {
-  case kItOmitted: return 1 << shift;
-  case kItThen: return firstcond0 << shift;
-  case kItElse: return !firstcond0 << shift;
-  }
-  return 0;
-}
-
-
-// Set the IT condition in the given position for the given state.  This is used
-// to check that conditional instructions match the preceding IT statement.
-void Thumb2Assembler::SetItCondition(ItState s, Condition cond, uint8_t index) {
-  switch (s) {
-  case kItOmitted: it_conditions_[index] = AL; break;
-  case kItThen: it_conditions_[index] = cond; break;
-  case kItElse:
-    it_conditions_[index] = static_cast<Condition>(static_cast<uint8_t>(cond) ^ 1);
-    break;
-  }
-}
-
-
-void Thumb2Assembler::it(Condition firstcond, ItState i1, ItState i2, ItState i3) {
-  CheckCondition(AL);       // Not allowed in IT block.
-  uint8_t firstcond0 = static_cast<uint8_t>(firstcond) & 1;
-
-  // All conditions to AL.
-  for (uint8_t i = 0; i < 4; ++i) {
-    it_conditions_[i] = AL;
-  }
-
-  SetItCondition(kItThen, firstcond, 0);
-  uint8_t mask = ToItMask(i1, firstcond0, 3);
-  SetItCondition(i1, firstcond, 1);
-
-  if (i1 != kItOmitted) {
-    mask |= ToItMask(i2, firstcond0, 2);
-    SetItCondition(i2, firstcond, 2);
-    if (i2 != kItOmitted) {
-      mask |= ToItMask(i3, firstcond0, 1);
-      SetItCondition(i3, firstcond, 3);
-      if (i3 != kItOmitted) {
-        mask |= 1U /* 0b0001 */;
-      }
-    }
-  }
-
-  // Start at first condition.
-  it_cond_index_ = 0;
-  next_condition_ = it_conditions_[0];
-  uint16_t encoding = B15 | B13 | B12 |
-        B11 | B10 | B9 | B8 |
-        firstcond << 4 |
-        mask;
-  Emit16(encoding);
-}
-
-
-void Thumb2Assembler::cbz(Register rn, Label* label) {
-  CheckCondition(AL);
-  if (label->IsBound()) {
-    LOG(FATAL) << "cbz can only be used to branch forwards";
-    UNREACHABLE();
-  } else if (IsHighRegister(rn)) {
-    LOG(FATAL) << "cbz can only be used with low registers";
-    UNREACHABLE();
-  } else {
-    uint16_t branchid = EmitCompareAndBranch(rn, static_cast<uint16_t>(label->position_), false);
-    label->LinkTo(branchid);
-  }
-}
-
-
-void Thumb2Assembler::cbnz(Register rn, Label* label) {
-  CheckCondition(AL);
-  if (label->IsBound()) {
-    LOG(FATAL) << "cbnz can only be used to branch forwards";
-    UNREACHABLE();
-  } else if (IsHighRegister(rn)) {
-    LOG(FATAL) << "cbnz can only be used with low registers";
-    UNREACHABLE();
-  } else {
-    uint16_t branchid = EmitCompareAndBranch(rn, static_cast<uint16_t>(label->position_), true);
-    label->LinkTo(branchid);
-  }
-}
-
-
-void Thumb2Assembler::blx(Register rm, Condition cond) {
-  CHECK_NE(rm, kNoRegister);
-  CheckCondition(cond);
-  int16_t encoding = B14 | B10 | B9 | B8 | B7 | static_cast<int16_t>(rm) << 3;
-  Emit16(encoding);
-}
-
-
-void Thumb2Assembler::bx(Register rm, Condition cond) {
-  CHECK_NE(rm, kNoRegister);
-  CheckCondition(cond);
-  int16_t encoding = B14 | B10 | B9 | B8 | static_cast<int16_t>(rm) << 3;
-  Emit16(encoding);
-}
-
-
-void Thumb2Assembler::AdrCode(Register rt, Label* label) {
-  uint32_t pc = buffer_.Size();
-  FixupId branch_id = AddFixup(Fixup::LoadCodeAddress(pc, rt));
-  CHECK(!label->IsBound());
-  // ADR target must be an unbound label. Add it to a singly-linked list maintained within
-  // the code with the label serving as the head.
-  Emit16(static_cast<uint16_t>(label->position_));
-  label->LinkTo(branch_id);
-  Emit16(0);
-  DCHECK_EQ(buffer_.Size() - pc, GetFixup(branch_id)->GetSizeInBytes());
-}
-
-
-void Thumb2Assembler::Push(Register rd, Condition cond) {
-  str(rd, Address(SP, -kRegisterSize, Address::PreIndex), cond);
-}
-
-
-void Thumb2Assembler::Pop(Register rd, Condition cond) {
-  ldr(rd, Address(SP, kRegisterSize, Address::PostIndex), cond);
-}
-
-
-void Thumb2Assembler::PushList(RegList regs, Condition cond) {
-  stm(DB_W, SP, regs, cond);
-}
-
-
-void Thumb2Assembler::PopList(RegList regs, Condition cond) {
-  ldm(IA_W, SP, regs, cond);
-}
-
-void Thumb2Assembler::StoreList(RegList regs, size_t stack_offset) {
-  DCHECK_NE(regs, 0u);
-  DCHECK_EQ(regs & (1u << IP), 0u);
-  if (IsPowerOfTwo(regs)) {
-    Register reg = static_cast<Register>(CTZ(static_cast<uint32_t>(regs)));
-    str(reg, Address(SP, stack_offset));
-  } else {
-    add(IP, SP, ShifterOperand(stack_offset));
-    stm(IA, IP, regs);
-  }
-}
-
-void Thumb2Assembler::LoadList(RegList regs, size_t stack_offset) {
-  DCHECK_NE(regs, 0u);
-  DCHECK_EQ(regs & (1u << IP), 0u);
-  if (IsPowerOfTwo(regs)) {
-    Register reg = static_cast<Register>(CTZ(static_cast<uint32_t>(regs)));
-    ldr(reg, Address(SP, stack_offset));
-  } else {
-    Register lowest_reg = static_cast<Register>(CTZ(static_cast<uint32_t>(regs)));
-    add(lowest_reg, SP, ShifterOperand(stack_offset));
-    ldm(IA, lowest_reg, regs);
-  }
-}
-
-void Thumb2Assembler::Mov(Register rd, Register rm, Condition cond) {
-  if (cond != AL || rd != rm) {
-    mov(rd, ShifterOperand(rm), cond);
-  }
-}
-
-
-void Thumb2Assembler::Bind(Label* label) {
-  BindLabel(label, buffer_.Size());
-
-  // Try to emit some Fixups now to reduce the memory needed during the branch fixup later.
-  while (!fixups_.empty() && fixups_.back().IsCandidateForEmitEarly()) {
-    const Fixup& last_fixup = fixups_.back();
-    // Fixups are ordered by location, so the candidate can surely be emitted if it is
-    // a forward branch. If it's a backward branch, it may go over any number of other
-    // fixups. We could check for any number of emit early candidates but we want this
-    // heuristics to be quick, so check just one.
-    uint32_t target = last_fixup.GetTarget();
-    if (target < last_fixup.GetLocation() &&
-        fixups_.size() >= 2u &&
-        fixups_[fixups_.size() - 2u].GetLocation() >= target) {
-      const Fixup& prev_fixup = fixups_[fixups_.size() - 2u];
-      if (!prev_fixup.IsCandidateForEmitEarly()) {
-        break;
-      }
-      uint32_t min_target = std::min(target, prev_fixup.GetTarget());
-      if (fixups_.size() >= 3u && fixups_[fixups_.size() - 3u].GetLocation() >= min_target) {
-        break;
-      }
-    }
-    last_fixup.Emit(last_fixup.GetLocation(), &buffer_, buffer_.Size());
-    fixups_.pop_back();
-  }
-}
-
-
-void Thumb2Assembler::Lsl(Register rd, Register rm, uint32_t shift_imm,
-                          Condition cond, SetCc set_cc) {
-  CHECK_LE(shift_imm, 31u);
-  CheckCondition(cond);
-  EmitShift(rd, rm, LSL, shift_imm, cond, set_cc);
-}
-
-
-void Thumb2Assembler::Lsr(Register rd, Register rm, uint32_t shift_imm,
-                          Condition cond, SetCc set_cc) {
-  CHECK(1u <= shift_imm && shift_imm <= 32u);
-  if (shift_imm == 32) shift_imm = 0;  // Comply to UAL syntax.
-  CheckCondition(cond);
-  EmitShift(rd, rm, LSR, shift_imm, cond, set_cc);
-}
-
-
-void Thumb2Assembler::Asr(Register rd, Register rm, uint32_t shift_imm,
-                          Condition cond, SetCc set_cc) {
-  CHECK(1u <= shift_imm && shift_imm <= 32u);
-  if (shift_imm == 32) shift_imm = 0;  // Comply to UAL syntax.
-  CheckCondition(cond);
-  EmitShift(rd, rm, ASR, shift_imm, cond, set_cc);
-}
-
-
-void Thumb2Assembler::Ror(Register rd, Register rm, uint32_t shift_imm,
-                          Condition cond, SetCc set_cc) {
-  CHECK(1u <= shift_imm && shift_imm <= 31u);
-  CheckCondition(cond);
-  EmitShift(rd, rm, ROR, shift_imm, cond, set_cc);
-}
-
-
-void Thumb2Assembler::Rrx(Register rd, Register rm, Condition cond, SetCc set_cc) {
-  CheckCondition(cond);
-  EmitShift(rd, rm, RRX, 0, cond, set_cc);
-}
-
-
-void Thumb2Assembler::Lsl(Register rd, Register rm, Register rn,
-                          Condition cond, SetCc set_cc) {
-  CheckCondition(cond);
-  EmitShift(rd, rm, LSL, rn, cond, set_cc);
-}
-
-
-void Thumb2Assembler::Lsr(Register rd, Register rm, Register rn,
-                          Condition cond, SetCc set_cc) {
-  CheckCondition(cond);
-  EmitShift(rd, rm, LSR, rn, cond, set_cc);
-}
-
-
-void Thumb2Assembler::Asr(Register rd, Register rm, Register rn,
-                          Condition cond, SetCc set_cc) {
-  CheckCondition(cond);
-  EmitShift(rd, rm, ASR, rn, cond, set_cc);
-}
-
-
-void Thumb2Assembler::Ror(Register rd, Register rm, Register rn,
-                          Condition cond, SetCc set_cc) {
-  CheckCondition(cond);
-  EmitShift(rd, rm, ROR, rn, cond, set_cc);
-}
-
-
-int32_t Thumb2Assembler::EncodeBranchOffset(int32_t offset, int32_t inst) {
-  // The offset is off by 4 due to the way the ARM CPUs read PC.
-  offset -= 4;
-  offset >>= 1;
-
-  uint32_t value = 0;
-  // There are two different encodings depending on the value of bit 12.  In one case
-  // intermediate values are calculated using the sign bit.
-  if ((inst & B12) == B12) {
-    // 25 bits of offset.
-    uint32_t signbit = (offset >> 31) & 0x1;
-    uint32_t i1 = (offset >> 22) & 0x1;
-    uint32_t i2 = (offset >> 21) & 0x1;
-    uint32_t imm10 = (offset >> 11) & 0x03ff;
-    uint32_t imm11 = offset & 0x07ff;
-    uint32_t j1 = (i1 ^ signbit) ? 0 : 1;
-    uint32_t j2 = (i2 ^ signbit) ? 0 : 1;
-    value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) |
-                      imm11;
-    // Remove the offset from the current encoding.
-    inst &= ~(0x3ff << 16 | 0x7ff);
-  } else {
-    uint32_t signbit = (offset >> 31) & 0x1;
-    uint32_t imm6 = (offset >> 11) & 0x03f;
-    uint32_t imm11 = offset & 0x07ff;
-    uint32_t j1 = (offset >> 19) & 1;
-    uint32_t j2 = (offset >> 17) & 1;
-    value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm6 << 16) |
-        imm11;
-    // Remove the offset from the current encoding.
-    inst &= ~(0x3f << 16 | 0x7ff);
-  }
-  // Mask out offset bits in current instruction.
-  inst &= ~(B26 | B13 | B11);
-  inst |= value;
-  return inst;
-}
-
-
-int Thumb2Assembler::DecodeBranchOffset(int32_t instr) {
-  int32_t imm32;
-  if ((instr & B12) == B12) {
-    uint32_t S = (instr >> 26) & 1;
-    uint32_t J2 = (instr >> 11) & 1;
-    uint32_t J1 = (instr >> 13) & 1;
-    uint32_t imm10 = (instr >> 16) & 0x3FF;
-    uint32_t imm11 = instr & 0x7FF;
-
-    uint32_t I1 = ~(J1 ^ S) & 1;
-    uint32_t I2 = ~(J2 ^ S) & 1;
-    imm32 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1);
-    imm32 = (imm32 << 8) >> 8;  // sign extend 24 bit immediate.
-  } else {
-    uint32_t S = (instr >> 26) & 1;
-    uint32_t J2 = (instr >> 11) & 1;
-    uint32_t J1 = (instr >> 13) & 1;
-    uint32_t imm6 = (instr >> 16) & 0x3F;
-    uint32_t imm11 = instr & 0x7FF;
-
-    imm32 = (S << 20) | (J2 << 19) | (J1 << 18) | (imm6 << 12) | (imm11 << 1);
-    imm32 = (imm32 << 11) >> 11;  // sign extend 21 bit immediate.
-  }
-  imm32 += 4;
-  return imm32;
-}
-
-uint32_t Thumb2Assembler::GetAdjustedPosition(uint32_t old_position) {
-  // We can reconstruct the adjustment by going through all the fixups from the beginning
-  // up to the old_position. Since we expect AdjustedPosition() to be called in a loop
-  // with increasing old_position, we can use the data from last AdjustedPosition() to
-  // continue where we left off and the whole loop should be O(m+n) where m is the number
-  // of positions to adjust and n is the number of fixups.
-  if (old_position < last_old_position_) {
-    last_position_adjustment_ = 0u;
-    last_old_position_ = 0u;
-    last_fixup_id_ = 0u;
-  }
-  while (last_fixup_id_ != fixups_.size()) {
-    Fixup* fixup = GetFixup(last_fixup_id_);
-    if (fixup->GetLocation() >= old_position + last_position_adjustment_) {
-      break;
-    }
-    if (fixup->GetSize() != fixup->GetOriginalSize()) {
-      last_position_adjustment_ += fixup->GetSizeInBytes() - fixup->GetOriginalSizeInBytes();
-    }
-     ++last_fixup_id_;
-  }
-  last_old_position_ = old_position;
-  return old_position + last_position_adjustment_;
-}
-
-Literal* Thumb2Assembler::NewLiteral(size_t size, const uint8_t* data)  {
-  DCHECK(size == 4u || size == 8u) << size;
-  literals_.emplace_back(size, data);
-  return &literals_.back();
-}
-
-void Thumb2Assembler::LoadLiteral(Register rt, Literal* literal)  {
-  DCHECK_EQ(literal->GetSize(), 4u);
-  DCHECK(!literal->GetLabel()->IsBound());
-  bool use32bit = IsForced32Bit() || IsHighRegister(rt);
-  uint32_t location = buffer_.Size();
-  Fixup::Size size = use32bit ? Fixup::kLiteral4KiB : Fixup::kLiteral1KiB;
-  FixupId fixup_id = AddFixup(Fixup::LoadNarrowLiteral(location, rt, size));
-  Emit16(static_cast<uint16_t>(literal->GetLabel()->position_));
-  literal->GetLabel()->LinkTo(fixup_id);
-  if (use32bit) {
-    Emit16(0);
-  }
-  DCHECK_EQ(location + GetFixup(fixup_id)->GetSizeInBytes(), buffer_.Size());
-}
-
-void Thumb2Assembler::LoadLiteral(Register rt, Register rt2, Literal* literal)  {
-  DCHECK_EQ(literal->GetSize(), 8u);
-  DCHECK(!literal->GetLabel()->IsBound());
-  uint32_t location = buffer_.Size();
-  FixupId fixup_id =
-      AddFixup(Fixup::LoadWideLiteral(location, rt, rt2, Fixup::kLongOrFPLiteral1KiB));
-  Emit16(static_cast<uint16_t>(literal->GetLabel()->position_));
-  literal->GetLabel()->LinkTo(fixup_id);
-  Emit16(0);
-  DCHECK_EQ(location + GetFixup(fixup_id)->GetSizeInBytes(), buffer_.Size());
-}
-
-void Thumb2Assembler::LoadLiteral(SRegister sd, Literal* literal)  {
-  DCHECK_EQ(literal->GetSize(), 4u);
-  DCHECK(!literal->GetLabel()->IsBound());
-  uint32_t location = buffer_.Size();
-  FixupId fixup_id = AddFixup(Fixup::LoadSingleLiteral(location, sd, Fixup::kLongOrFPLiteral1KiB));
-  Emit16(static_cast<uint16_t>(literal->GetLabel()->position_));
-  literal->GetLabel()->LinkTo(fixup_id);
-  Emit16(0);
-  DCHECK_EQ(location + GetFixup(fixup_id)->GetSizeInBytes(), buffer_.Size());
-}
-
-void Thumb2Assembler::LoadLiteral(DRegister dd, Literal* literal) {
-  DCHECK_EQ(literal->GetSize(), 8u);
-  DCHECK(!literal->GetLabel()->IsBound());
-  uint32_t location = buffer_.Size();
-  FixupId fixup_id = AddFixup(Fixup::LoadDoubleLiteral(location, dd, Fixup::kLongOrFPLiteral1KiB));
-  Emit16(static_cast<uint16_t>(literal->GetLabel()->position_));
-  literal->GetLabel()->LinkTo(fixup_id);
-  Emit16(0);
-  DCHECK_EQ(location + GetFixup(fixup_id)->GetSizeInBytes(), buffer_.Size());
-}
-
-
-void Thumb2Assembler::AddConstant(Register rd, Register rn, int32_t value,
-                                  Condition cond, SetCc set_cc) {
-  if (value == 0 && set_cc != kCcSet) {
-    if (rd != rn) {
-      mov(rd, ShifterOperand(rn), cond);
-    }
-    return;
-  }
-  // We prefer to select the shorter code sequence rather than selecting add for
-  // positive values and sub for negatives ones, which would slightly improve
-  // the readability of generated code for some constants.
-  ShifterOperand shifter_op;
-  if (ShifterOperandCanHold(rd, rn, ADD, value, set_cc, &shifter_op)) {
-    add(rd, rn, shifter_op, cond, set_cc);
-  } else if (ShifterOperandCanHold(rd, rn, SUB, -value, set_cc, &shifter_op)) {
-    sub(rd, rn, shifter_op, cond, set_cc);
-  } else {
-    CHECK(rn != IP);
-    // If rd != rn, use rd as temp. This alows 16-bit ADD/SUB in more situations than using IP.
-    Register temp = (rd != rn) ? rd : IP;
-    if (ShifterOperandCanHold(temp, kNoRegister, MVN, ~value, kCcKeep, &shifter_op)) {
-      mvn(temp, shifter_op, cond, kCcKeep);
-      add(rd, rn, ShifterOperand(temp), cond, set_cc);
-    } else if (ShifterOperandCanHold(temp, kNoRegister, MVN, ~(-value), kCcKeep, &shifter_op)) {
-      mvn(temp, shifter_op, cond, kCcKeep);
-      sub(rd, rn, ShifterOperand(temp), cond, set_cc);
-    } else if (High16Bits(-value) == 0) {
-      movw(temp, Low16Bits(-value), cond);
-      sub(rd, rn, ShifterOperand(temp), cond, set_cc);
-    } else {
-      movw(temp, Low16Bits(value), cond);
-      uint16_t value_high = High16Bits(value);
-      if (value_high != 0) {
-        movt(temp, value_high, cond);
-      }
-      add(rd, rn, ShifterOperand(temp), cond, set_cc);
-    }
-  }
-}
-
-void Thumb2Assembler::CmpConstant(Register rn, int32_t value, Condition cond) {
-  // We prefer to select the shorter code sequence rather than using plain cmp and cmn
-  // which would slightly improve the readability of generated code for some constants.
-  ShifterOperand shifter_op;
-  if (ShifterOperandCanHold(kNoRegister, rn, CMP, value, kCcSet, &shifter_op)) {
-    cmp(rn, shifter_op, cond);
-  } else if (ShifterOperandCanHold(kNoRegister, rn, CMN, -value, kCcSet, &shifter_op)) {
-    cmn(rn, shifter_op, cond);
-  } else {
-    CHECK(rn != IP);
-    if (ShifterOperandCanHold(IP, kNoRegister, MVN, ~value, kCcKeep, &shifter_op)) {
-      mvn(IP, shifter_op, cond, kCcKeep);
-      cmp(rn, ShifterOperand(IP), cond);
-    } else if (ShifterOperandCanHold(IP, kNoRegister, MVN, ~(-value), kCcKeep, &shifter_op)) {
-      mvn(IP, shifter_op, cond, kCcKeep);
-      cmn(rn, ShifterOperand(IP), cond);
-    } else if (High16Bits(-value) == 0) {
-      movw(IP, Low16Bits(-value), cond);
-      cmn(rn, ShifterOperand(IP), cond);
-    } else {
-      movw(IP, Low16Bits(value), cond);
-      uint16_t value_high = High16Bits(value);
-      if (value_high != 0) {
-        movt(IP, value_high, cond);
-      }
-      cmp(rn, ShifterOperand(IP), cond);
-    }
-  }
-}
-
-void Thumb2Assembler::LoadImmediate(Register rd, int32_t value, Condition cond) {
-  ShifterOperand shifter_op;
-  if (ShifterOperandCanHold(rd, R0, MOV, value, &shifter_op)) {
-    mov(rd, shifter_op, cond);
-  } else if (ShifterOperandCanHold(rd, R0, MVN, ~value, &shifter_op)) {
-    mvn(rd, shifter_op, cond);
-  } else {
-    movw(rd, Low16Bits(value), cond);
-    uint16_t value_high = High16Bits(value);
-    if (value_high != 0) {
-      movt(rd, value_high, cond);
-    }
-  }
-}
-
-void Thumb2Assembler::LoadDImmediate(DRegister dd, double value, Condition cond) {
-  if (!vmovd(dd, value, cond)) {
-    uint64_t int_value = bit_cast<uint64_t, double>(value);
-    if (int_value == bit_cast<uint64_t, double>(0.0)) {
-      // 0.0 is quite common, so we special case it by loading
-      // 2.0 in `dd` and then subtracting it.
-      bool success = vmovd(dd, 2.0, cond);
-      CHECK(success);
-      vsubd(dd, dd, dd, cond);
-    } else {
-      Literal* literal = literal64_dedupe_map_.GetOrCreate(
-          int_value,
-          [this, int_value]() { return NewLiteral<uint64_t>(int_value); });
-      LoadLiteral(dd, literal);
-    }
-  }
-}
-
-int32_t Thumb2Assembler::GetAllowedLoadOffsetBits(LoadOperandType type) {
-  switch (type) {
-    case kLoadSignedByte:
-    case kLoadSignedHalfword:
-    case kLoadUnsignedHalfword:
-    case kLoadUnsignedByte:
-    case kLoadWord:
-      // We can encode imm12 offset.
-      return 0xfffu;
-    case kLoadSWord:
-    case kLoadDWord:
-    case kLoadWordPair:
-      // We can encode imm8:'00' offset.
-      return 0xff << 2;
-    default:
-      LOG(FATAL) << "UNREACHABLE";
-      UNREACHABLE();
-  }
-}
-
-int32_t Thumb2Assembler::GetAllowedStoreOffsetBits(StoreOperandType type) {
-  switch (type) {
-    case kStoreHalfword:
-    case kStoreByte:
-    case kStoreWord:
-      // We can encode imm12 offset.
-      return 0xfff;
-    case kStoreSWord:
-    case kStoreDWord:
-    case kStoreWordPair:
-      // We can encode imm8:'00' offset.
-      return 0xff << 2;
-    default:
-      LOG(FATAL) << "UNREACHABLE";
-      UNREACHABLE();
-  }
-}
-
-bool Thumb2Assembler::CanSplitLoadStoreOffset(int32_t allowed_offset_bits,
-                                              int32_t offset,
-                                              /*out*/ int32_t* add_to_base,
-                                              /*out*/ int32_t* offset_for_load_store) {
-  int32_t other_bits = offset & ~allowed_offset_bits;
-  if (ShifterOperandCanAlwaysHold(other_bits) || ShifterOperandCanAlwaysHold(-other_bits)) {
-    *add_to_base = offset & ~allowed_offset_bits;
-    *offset_for_load_store = offset & allowed_offset_bits;
-    return true;
-  }
-  return false;
-}
-
-int32_t Thumb2Assembler::AdjustLoadStoreOffset(int32_t allowed_offset_bits,
-                                               Register temp,
-                                               Register base,
-                                               int32_t offset,
-                                               Condition cond) {
-  DCHECK_NE(offset & ~allowed_offset_bits, 0);
-  int32_t add_to_base, offset_for_load;
-  if (CanSplitLoadStoreOffset(allowed_offset_bits, offset, &add_to_base, &offset_for_load)) {
-    AddConstant(temp, base, add_to_base, cond, kCcKeep);
-    return offset_for_load;
-  } else {
-    LoadImmediate(temp, offset, cond);
-    add(temp, temp, ShifterOperand(base), cond, kCcKeep);
-    return 0;
-  }
-}
-
-// Implementation note: this method must emit at most one instruction when
-// Address::CanHoldLoadOffsetThumb.
-void Thumb2Assembler::LoadFromOffset(LoadOperandType type,
-                                     Register reg,
-                                     Register base,
-                                     int32_t offset,
-                                     Condition cond) {
-  if (!Address::CanHoldLoadOffsetThumb(type, offset)) {
-    CHECK_NE(base, IP);
-    // Inlined AdjustLoadStoreOffset() allows us to pull a few more tricks.
-    int32_t allowed_offset_bits = GetAllowedLoadOffsetBits(type);
-    DCHECK_NE(offset & ~allowed_offset_bits, 0);
-    int32_t add_to_base, offset_for_load;
-    if (CanSplitLoadStoreOffset(allowed_offset_bits, offset, &add_to_base, &offset_for_load)) {
-      // Use reg for the adjusted base. If it's low reg, we may end up using 16-bit load.
-      AddConstant(reg, base, add_to_base, cond, kCcKeep);
-      base = reg;
-      offset = offset_for_load;
-    } else {
-      Register temp = (reg == base) ? IP : reg;
-      LoadImmediate(temp, offset, cond);
-      // TODO: Implement indexed load (not available for LDRD) and use it here to avoid the ADD.
-      // Use reg for the adjusted base. If it's low reg, we may end up using 16-bit load.
-      add(reg, reg, ShifterOperand((reg == base) ? IP : base), cond, kCcKeep);
-      base = reg;
-      offset = 0;
-    }
-  }
-  DCHECK(Address::CanHoldLoadOffsetThumb(type, offset));
-  switch (type) {
-    case kLoadSignedByte:
-      ldrsb(reg, Address(base, offset), cond);
-      break;
-    case kLoadUnsignedByte:
-      ldrb(reg, Address(base, offset), cond);
-      break;
-    case kLoadSignedHalfword:
-      ldrsh(reg, Address(base, offset), cond);
-      break;
-    case kLoadUnsignedHalfword:
-      ldrh(reg, Address(base, offset), cond);
-      break;
-    case kLoadWord:
-      ldr(reg, Address(base, offset), cond);
-      break;
-    case kLoadWordPair:
-      ldrd(reg, Address(base, offset), cond);
-      break;
-    default:
-      LOG(FATAL) << "UNREACHABLE";
-      UNREACHABLE();
-  }
-}
-
-// Implementation note: this method must emit at most one instruction when
-// Address::CanHoldLoadOffsetThumb, as expected by JIT::GuardedLoadFromOffset.
-void Thumb2Assembler::LoadSFromOffset(SRegister reg,
-                                      Register base,
-                                      int32_t offset,
-                                      Condition cond) {
-  if (!Address::CanHoldLoadOffsetThumb(kLoadSWord, offset)) {
-    CHECK_NE(base, IP);
-    offset = AdjustLoadStoreOffset(GetAllowedLoadOffsetBits(kLoadSWord), IP, base, offset, cond);
-    base = IP;
-  }
-  DCHECK(Address::CanHoldLoadOffsetThumb(kLoadSWord, offset));
-  vldrs(reg, Address(base, offset), cond);
-}
-
-
-// Implementation note: this method must emit at most one instruction when
-// Address::CanHoldLoadOffsetThumb, as expected by JIT::GuardedLoadFromOffset.
-void Thumb2Assembler::LoadDFromOffset(DRegister reg,
-                                      Register base,
-                                      int32_t offset,
-                                      Condition cond) {
-  if (!Address::CanHoldLoadOffsetThumb(kLoadDWord, offset)) {
-    CHECK_NE(base, IP);
-    offset = AdjustLoadStoreOffset(GetAllowedLoadOffsetBits(kLoadDWord), IP, base, offset, cond);
-    base = IP;
-  }
-  DCHECK(Address::CanHoldLoadOffsetThumb(kLoadDWord, offset));
-  vldrd(reg, Address(base, offset), cond);
-}
-
-
-// Implementation note: this method must emit at most one instruction when
-// Address::CanHoldStoreOffsetThumb.
-void Thumb2Assembler::StoreToOffset(StoreOperandType type,
-                                    Register reg,
-                                    Register base,
-                                    int32_t offset,
-                                    Condition cond) {
-  Register tmp_reg = kNoRegister;
-  if (!Address::CanHoldStoreOffsetThumb(type, offset)) {
-    CHECK_NE(base, IP);
-    if ((reg != IP) &&
-        ((type != kStoreWordPair) || (reg + 1 != IP))) {
-      tmp_reg = IP;
-    } else {
-      // Be careful not to use IP twice (for `reg` (or `reg` + 1 in
-      // the case of a word-pair store) and `base`) to build the
-      // Address object used by the store instruction(s) below.
-      // Instead, save R5 on the stack (or R6 if R5 is already used by
-      // `base`), use it as secondary temporary register, and restore
-      // it after the store instruction has been emitted.
-      tmp_reg = (base != R5) ? R5 : R6;
-      Push(tmp_reg);
-      if (base == SP) {
-        offset += kRegisterSize;
-      }
-    }
-    // TODO: Implement indexed store (not available for STRD), inline AdjustLoadStoreOffset()
-    // and in the "unsplittable" path get rid of the "add" by using the store indexed instead.
-    offset = AdjustLoadStoreOffset(GetAllowedStoreOffsetBits(type), tmp_reg, base, offset, cond);
-    base = tmp_reg;
-  }
-  DCHECK(Address::CanHoldStoreOffsetThumb(type, offset));
-  switch (type) {
-    case kStoreByte:
-      strb(reg, Address(base, offset), cond);
-      break;
-    case kStoreHalfword:
-      strh(reg, Address(base, offset), cond);
-      break;
-    case kStoreWord:
-      str(reg, Address(base, offset), cond);
-      break;
-    case kStoreWordPair:
-      strd(reg, Address(base, offset), cond);
-      break;
-    default:
-      LOG(FATAL) << "UNREACHABLE";
-      UNREACHABLE();
-  }
-  if ((tmp_reg != kNoRegister) && (tmp_reg != IP)) {
-    CHECK((tmp_reg == R5) || (tmp_reg == R6));
-    Pop(tmp_reg);
-  }
-}
-
-
-// Implementation note: this method must emit at most one instruction when
-// Address::CanHoldStoreOffsetThumb, as expected by JIT::GuardedStoreToOffset.
-void Thumb2Assembler::StoreSToOffset(SRegister reg,
-                                     Register base,
-                                     int32_t offset,
-                                     Condition cond) {
-  if (!Address::CanHoldStoreOffsetThumb(kStoreSWord, offset)) {
-    CHECK_NE(base, IP);
-    offset = AdjustLoadStoreOffset(GetAllowedStoreOffsetBits(kStoreSWord), IP, base, offset, cond);
-    base = IP;
-  }
-  DCHECK(Address::CanHoldStoreOffsetThumb(kStoreSWord, offset));
-  vstrs(reg, Address(base, offset), cond);
-}
-
-
-// Implementation note: this method must emit at most one instruction when
-// Address::CanHoldStoreOffsetThumb, as expected by JIT::GuardedStoreSToOffset.
-void Thumb2Assembler::StoreDToOffset(DRegister reg,
-                                     Register base,
-                                     int32_t offset,
-                                     Condition cond) {
-  if (!Address::CanHoldStoreOffsetThumb(kStoreDWord, offset)) {
-    CHECK_NE(base, IP);
-    offset = AdjustLoadStoreOffset(GetAllowedStoreOffsetBits(kStoreDWord), IP, base, offset, cond);
-    base = IP;
-  }
-  DCHECK(Address::CanHoldStoreOffsetThumb(kStoreDWord, offset));
-  vstrd(reg, Address(base, offset), cond);
-}
-
-
-void Thumb2Assembler::dmb(DmbOptions flavor) {
-  int32_t encoding = 0xf3bf8f50;  // dmb in T1 encoding.
-  Emit32(encoding | flavor);
-}
-
-
-void Thumb2Assembler::CompareAndBranchIfZero(Register r, Label* label) {
-  if (CanRelocateBranches() && IsLowRegister(r) && !label->IsBound()) {
-    cbz(r, label);
-  } else {
-    cmp(r, ShifterOperand(0));
-    b(label, EQ);
-  }
-}
-
-
-void Thumb2Assembler::CompareAndBranchIfNonZero(Register r, Label* label) {
-  if (CanRelocateBranches() && IsLowRegister(r) && !label->IsBound()) {
-    cbnz(r, label);
-  } else {
-    cmp(r, ShifterOperand(0));
-    b(label, NE);
-  }
-}
-
-JumpTable* Thumb2Assembler::CreateJumpTable(std::vector<Label*>&& labels, Register base_reg) {
-  jump_tables_.emplace_back(std::move(labels));
-  JumpTable* table = &jump_tables_.back();
-  DCHECK(!table->GetLabel()->IsBound());
-
-  bool use32bit = IsForced32Bit() || IsHighRegister(base_reg);
-  uint32_t location = buffer_.Size();
-  Fixup::Size size = use32bit ? Fixup::kLiteralAddr4KiB : Fixup::kLiteralAddr1KiB;
-  FixupId fixup_id = AddFixup(Fixup::LoadLiteralAddress(location, base_reg, size));
-  Emit16(static_cast<uint16_t>(table->GetLabel()->position_));
-  table->GetLabel()->LinkTo(fixup_id);
-  if (use32bit) {
-    Emit16(0);
-  }
-  DCHECK_EQ(location + GetFixup(fixup_id)->GetSizeInBytes(), buffer_.Size());
-
-  return table;
-}
-
-void Thumb2Assembler::EmitJumpTableDispatch(JumpTable* jump_table, Register displacement_reg) {
-  CHECK(!IsForced32Bit()) << "Forced 32-bit dispatch not implemented yet";
-  // 32-bit ADD doesn't support PC as an input, so we need a two-instruction sequence:
-  //   SUB ip, ip, #0
-  //   ADD pc, ip, reg
-  // TODO: Implement.
-
-  // The anchor's position needs to be fixed up before we can compute offsets - so make it a tracked
-  // label.
-  BindTrackedLabel(jump_table->GetAnchorLabel());
-
-  add(PC, PC, ShifterOperand(displacement_reg));
-}
-
-}  // namespace arm
-}  // namespace art
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
deleted file mode 100644
index 2ff9018..0000000
--- a/compiler/utils/arm/assembler_thumb2.h
+++ /dev/null
@@ -1,948 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_UTILS_ARM_ASSEMBLER_THUMB2_H_
-#define ART_COMPILER_UTILS_ARM_ASSEMBLER_THUMB2_H_
-
-#include <deque>
-#include <utility>
-#include <vector>
-
-#include "base/arena_containers.h"
-#include "base/array_ref.h"
-#include "base/logging.h"
-#include "constants_arm.h"
-#include "utils/arm/managed_register_arm.h"
-#include "utils/arm/assembler_arm.h"
-#include "offsets.h"
-
-namespace art {
-namespace arm {
-
-class Thumb2Assembler FINAL : public ArmAssembler {
- public:
-  explicit Thumb2Assembler(ArenaAllocator* arena, bool can_relocate_branches = true)
-      : ArmAssembler(arena),
-        can_relocate_branches_(can_relocate_branches),
-        force_32bit_(false),
-        it_cond_index_(kNoItCondition),
-        next_condition_(AL),
-        fixups_(arena->Adapter(kArenaAllocAssembler)),
-        fixup_dependents_(arena->Adapter(kArenaAllocAssembler)),
-        literals_(arena->Adapter(kArenaAllocAssembler)),
-        literal64_dedupe_map_(std::less<uint64_t>(), arena->Adapter(kArenaAllocAssembler)),
-        jump_tables_(arena->Adapter(kArenaAllocAssembler)),
-        last_position_adjustment_(0u),
-        last_old_position_(0u),
-        last_fixup_id_(0u) {
-    cfi().DelayEmittingAdvancePCs();
-  }
-
-  virtual ~Thumb2Assembler() {
-  }
-
-  bool IsThumb() const OVERRIDE {
-    return true;
-  }
-
-  bool IsForced32Bit() const {
-    return force_32bit_;
-  }
-
-  bool CanRelocateBranches() const {
-    return can_relocate_branches_;
-  }
-
-  void FinalizeCode() OVERRIDE;
-
-  // Data-processing instructions.
-  virtual void and_(Register rd, Register rn, const ShifterOperand& so,
-                    Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-
-  virtual void eor(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-
-  virtual void sub(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-
-  virtual void rsb(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-
-  virtual void add(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-
-  virtual void adc(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-
-  virtual void sbc(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-
-  virtual void rsc(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-
-  void tst(Register rn, const ShifterOperand& so, Condition cond = AL) OVERRIDE;
-
-  void teq(Register rn, const ShifterOperand& so, Condition cond = AL) OVERRIDE;
-
-  void cmp(Register rn, const ShifterOperand& so, Condition cond = AL) OVERRIDE;
-
-  void cmn(Register rn, const ShifterOperand& so, Condition cond = AL) OVERRIDE;
-
-  virtual void orr(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-
-  virtual void orn(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-
-  virtual void mov(Register rd, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-
-  virtual void bic(Register rd, Register rn, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-
-  virtual void mvn(Register rd, const ShifterOperand& so,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-
-  // Miscellaneous data-processing instructions.
-  void clz(Register rd, Register rm, Condition cond = AL) OVERRIDE;
-  void movw(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
-  void movt(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
-  void rbit(Register rd, Register rm, Condition cond = AL) OVERRIDE;
-  void rev(Register rd, Register rm, Condition cond = AL) OVERRIDE;
-  void rev16(Register rd, Register rm, Condition cond = AL) OVERRIDE;
-  void revsh(Register rd, Register rm, Condition cond = AL) OVERRIDE;
-
-  // Multiply instructions.
-  void mul(Register rd, Register rn, Register rm, Condition cond = AL) OVERRIDE;
-  void mla(Register rd, Register rn, Register rm, Register ra,
-           Condition cond = AL) OVERRIDE;
-  void mls(Register rd, Register rn, Register rm, Register ra,
-           Condition cond = AL) OVERRIDE;
-  void smull(Register rd_lo, Register rd_hi, Register rn, Register rm,
-             Condition cond = AL) OVERRIDE;
-  void umull(Register rd_lo, Register rd_hi, Register rn, Register rm,
-             Condition cond = AL) OVERRIDE;
-
-  void sdiv(Register rd, Register rn, Register rm, Condition cond = AL) OVERRIDE;
-  void udiv(Register rd, Register rn, Register rm, Condition cond = AL) OVERRIDE;
-
-  // Bit field extract instructions.
-  void sbfx(Register rd, Register rn, uint32_t lsb, uint32_t width, Condition cond = AL) OVERRIDE;
-  void ubfx(Register rd, Register rn, uint32_t lsb, uint32_t width, Condition cond = AL) OVERRIDE;
-
-  // Load/store instructions.
-  void ldr(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
-  void str(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
-
-  void ldrb(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
-  void strb(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
-
-  void ldrh(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
-  void strh(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
-
-  void ldrsb(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
-  void ldrsh(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
-
-  // Load/store register dual instructions using registers `rd` and `rd` + 1.
-  void ldrd(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
-  void strd(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
-
-  // Load/store register dual instructions using registers `rd` and `rd2`.
-  // Note that contrary to the ARM A1 encoding, the Thumb-2 T1 encoding
-  // does not require `rd` to be even, nor `rd2' to be equal to `rd` + 1.
-  void ldrd(Register rd, Register rd2, const Address& ad, Condition cond);
-  void strd(Register rd, Register rd2, const Address& ad, Condition cond);
-
-
-  void ldm(BlockAddressMode am, Register base,
-           RegList regs, Condition cond = AL) OVERRIDE;
-  void stm(BlockAddressMode am, Register base,
-           RegList regs, Condition cond = AL) OVERRIDE;
-
-  void ldrex(Register rd, Register rn, Condition cond = AL) OVERRIDE;
-  void strex(Register rd, Register rt, Register rn, Condition cond = AL) OVERRIDE;
-
-  void ldrex(Register rd, Register rn, uint16_t imm, Condition cond = AL);
-  void strex(Register rd, Register rt, Register rn, uint16_t imm, Condition cond = AL);
-
-  void ldrexd(Register rt, Register rt2, Register rn, Condition cond = AL) OVERRIDE;
-  void strexd(Register rd, Register rt, Register rt2, Register rn, Condition cond = AL) OVERRIDE;
-
-  // Miscellaneous instructions.
-  void clrex(Condition cond = AL) OVERRIDE;
-  void nop(Condition cond = AL) OVERRIDE;
-
-  void bkpt(uint16_t imm16) OVERRIDE;
-  void svc(uint32_t imm24) OVERRIDE;
-
-  // If-then
-  void it(Condition firstcond, ItState i1 = kItOmitted,
-        ItState i2 = kItOmitted, ItState i3 = kItOmitted) OVERRIDE;
-
-  void cbz(Register rn, Label* target) OVERRIDE;
-  void cbnz(Register rn, Label* target) OVERRIDE;
-
-  // Floating point instructions (VFPv3-D16 and VFPv3-D32 profiles).
-  void vmovsr(SRegister sn, Register rt, Condition cond = AL) OVERRIDE;
-  void vmovrs(Register rt, SRegister sn, Condition cond = AL) OVERRIDE;
-  void vmovsrr(SRegister sm, Register rt, Register rt2, Condition cond = AL) OVERRIDE;
-  void vmovrrs(Register rt, Register rt2, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vmovdrr(DRegister dm, Register rt, Register rt2, Condition cond = AL) OVERRIDE;
-  void vmovrrd(Register rt, Register rt2, DRegister dm, Condition cond = AL) OVERRIDE;
-  void vmovs(SRegister sd, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vmovd(DRegister dd, DRegister dm, Condition cond = AL) OVERRIDE;
-
-  // Returns false if the immediate cannot be encoded.
-  bool vmovs(SRegister sd, float s_imm, Condition cond = AL) OVERRIDE;
-  bool vmovd(DRegister dd, double d_imm, Condition cond = AL) OVERRIDE;
-
-  void vldrs(SRegister sd, const Address& ad, Condition cond = AL) OVERRIDE;
-  void vstrs(SRegister sd, const Address& ad, Condition cond = AL) OVERRIDE;
-  void vldrd(DRegister dd, const Address& ad, Condition cond = AL) OVERRIDE;
-  void vstrd(DRegister dd, const Address& ad, Condition cond = AL) OVERRIDE;
-
-  void vadds(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vaddd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL) OVERRIDE;
-  void vsubs(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vsubd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL) OVERRIDE;
-  void vmuls(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vmuld(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL) OVERRIDE;
-  void vmlas(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vmlad(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL) OVERRIDE;
-  void vmlss(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vmlsd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL) OVERRIDE;
-  void vdivs(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vdivd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL) OVERRIDE;
-
-  void vabss(SRegister sd, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vabsd(DRegister dd, DRegister dm, Condition cond = AL) OVERRIDE;
-  void vnegs(SRegister sd, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vnegd(DRegister dd, DRegister dm, Condition cond = AL) OVERRIDE;
-  void vsqrts(SRegister sd, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vsqrtd(DRegister dd, DRegister dm, Condition cond = AL) OVERRIDE;
-
-  void vcvtsd(SRegister sd, DRegister dm, Condition cond = AL) OVERRIDE;
-  void vcvtds(DRegister dd, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vcvtis(SRegister sd, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vcvtid(SRegister sd, DRegister dm, Condition cond = AL) OVERRIDE;
-  void vcvtsi(SRegister sd, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vcvtdi(DRegister dd, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vcvtus(SRegister sd, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vcvtud(SRegister sd, DRegister dm, Condition cond = AL) OVERRIDE;
-  void vcvtsu(SRegister sd, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vcvtdu(DRegister dd, SRegister sm, Condition cond = AL) OVERRIDE;
-
-  void vcmps(SRegister sd, SRegister sm, Condition cond = AL) OVERRIDE;
-  void vcmpd(DRegister dd, DRegister dm, Condition cond = AL) OVERRIDE;
-  void vcmpsz(SRegister sd, Condition cond = AL) OVERRIDE;
-  void vcmpdz(DRegister dd, Condition cond = AL) OVERRIDE;
-  void vmstat(Condition cond = AL) OVERRIDE;  // VMRS APSR_nzcv, FPSCR
-
-  void vcntd(DRegister dd, DRegister dm) OVERRIDE;
-  void vpaddld(DRegister dd, DRegister dm, int32_t size, bool is_unsigned) OVERRIDE;
-
-  void vpushs(SRegister reg, int nregs, Condition cond = AL) OVERRIDE;
-  void vpushd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
-  void vpops(SRegister reg, int nregs, Condition cond = AL) OVERRIDE;
-  void vpopd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
-  void vldmiad(Register base_reg, DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
-  void vstmiad(Register base_reg, DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
-
-  // Branch instructions.
-  void b(Label* label, Condition cond = AL);
-  void bl(Label* label, Condition cond = AL);
-  void blx(Label* label);
-  void blx(Register rm, Condition cond = AL) OVERRIDE;
-  void bx(Register rm, Condition cond = AL) OVERRIDE;
-
-  // ADR instruction loading register for branching to the label, including the Thumb mode bit.
-  void AdrCode(Register rt, Label* label) OVERRIDE;
-
-  virtual void Lsl(Register rd, Register rm, uint32_t shift_imm,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-  virtual void Lsr(Register rd, Register rm, uint32_t shift_imm,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-  virtual void Asr(Register rd, Register rm, uint32_t shift_imm,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-  virtual void Ror(Register rd, Register rm, uint32_t shift_imm,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-  virtual void Rrx(Register rd, Register rm,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-
-  virtual void Lsl(Register rd, Register rm, Register rn,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-  virtual void Lsr(Register rd, Register rm, Register rn,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-  virtual void Asr(Register rd, Register rm, Register rn,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-  virtual void Ror(Register rd, Register rm, Register rn,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-
-  void Push(Register rd, Condition cond = AL) OVERRIDE;
-  void Pop(Register rd, Condition cond = AL) OVERRIDE;
-
-  void PushList(RegList regs, Condition cond = AL) OVERRIDE;
-  void PopList(RegList regs, Condition cond = AL) OVERRIDE;
-  void StoreList(RegList regs, size_t stack_offset) OVERRIDE;
-  void LoadList(RegList regs, size_t stack_offset) OVERRIDE;
-
-  void Mov(Register rd, Register rm, Condition cond = AL) OVERRIDE;
-
-  void CompareAndBranchIfZero(Register r, Label* label) OVERRIDE;
-  void CompareAndBranchIfNonZero(Register r, Label* label) OVERRIDE;
-
-  // Memory barriers.
-  void dmb(DmbOptions flavor) OVERRIDE;
-
-  // 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) OVERRIDE;
-
-  using ArmAssembler::NewLiteral;  // Make the helper template visible.
-
-  Literal* NewLiteral(size_t size, const uint8_t* data) OVERRIDE;
-  void LoadLiteral(Register rt, Literal* literal) OVERRIDE;
-  void LoadLiteral(Register rt, Register rt2, Literal* literal) OVERRIDE;
-  void LoadLiteral(SRegister sd, Literal* literal) OVERRIDE;
-  void LoadLiteral(DRegister dd, Literal* literal) OVERRIDE;
-
-  // Add signed constant value to rd. May clobber IP.
-  void AddConstant(Register rd, Register rn, int32_t value,
-                   Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
-
-  void CmpConstant(Register rn, int32_t value, Condition cond = AL) OVERRIDE;
-
-  // Load and Store. May clobber IP.
-  void LoadImmediate(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
-  void LoadDImmediate(DRegister dd, double value, Condition cond = AL) OVERRIDE;
-  void MarkExceptionHandler(Label* label) OVERRIDE;
-  void LoadFromOffset(LoadOperandType type,
-                      Register reg,
-                      Register base,
-                      int32_t offset,
-                      Condition cond = AL) OVERRIDE;
-  void StoreToOffset(StoreOperandType type,
-                     Register reg,
-                     Register base,
-                     int32_t offset,
-                     Condition cond = AL) OVERRIDE;
-  void LoadSFromOffset(SRegister reg,
-                       Register base,
-                       int32_t offset,
-                       Condition cond = AL) OVERRIDE;
-  void StoreSToOffset(SRegister reg,
-                      Register base,
-                      int32_t offset,
-                      Condition cond = AL) OVERRIDE;
-  void LoadDFromOffset(DRegister reg,
-                       Register base,
-                       int32_t offset,
-                       Condition cond = AL) OVERRIDE;
-  void StoreDToOffset(DRegister reg,
-                      Register base,
-                      int32_t offset,
-                      Condition cond = AL) OVERRIDE;
-
-  bool ShifterOperandCanHold(Register rd,
-                             Register rn,
-                             Opcode opcode,
-                             uint32_t immediate,
-                             SetCc set_cc,
-                             ShifterOperand* shifter_op) OVERRIDE;
-  using ArmAssembler::ShifterOperandCanHold;  // Don't hide the non-virtual override.
-
-  bool ShifterOperandCanAlwaysHold(uint32_t immediate) OVERRIDE;
-
-
-  static bool IsInstructionForExceptionHandling(uintptr_t pc);
-
-  // Emit data (e.g. encoded instruction or immediate) to the.
-  // instruction stream.
-  void Emit32(int32_t value);     // Emit a 32 bit instruction in thumb format.
-  void Emit16(int16_t value);     // Emit a 16 bit instruction in little endian format.
-  void Bind(Label* label) OVERRIDE;
-
-  // Force the assembler to generate 32 bit instructions.
-  void Force32Bit() {
-    force_32bit_ = true;
-  }
-
-  void Allow16Bit() {
-    force_32bit_ = false;
-  }
-
-  // Emit an ADR (or a sequence of instructions) to load the jump table address into base_reg. This
-  // will generate a fixup.
-  JumpTable* CreateJumpTable(std::vector<Label*>&& labels, Register base_reg) OVERRIDE;
-  // Emit an ADD PC, X to dispatch a jump-table jump. This will generate a fixup.
-  void EmitJumpTableDispatch(JumpTable* jump_table, Register displacement_reg) OVERRIDE;
-
- private:
-  typedef uint16_t FixupId;
-
-  // Fixup: branches and literal pool references.
-  //
-  // The thumb2 architecture allows branches to be either 16 or 32 bit instructions. This
-  // depends on both the type of branch and the offset to which it is branching. The 16-bit
-  // cbz and cbnz instructions may also need to be replaced with a separate 16-bit compare
-  // instruction and a 16- or 32-bit branch instruction. Load from a literal pool can also be
-  // 16-bit or 32-bit instruction and, if the method is large, we may need to use a sequence
-  // of instructions to make up for the limited range of load literal instructions (up to
-  // 4KiB for the 32-bit variant). When generating code for these insns we don't know the
-  // size before hand, so we assume it is the smallest available size and determine the final
-  // code offsets and sizes and emit code in FinalizeCode().
-  //
-  // To handle this, we keep a record of every branch and literal pool load in the program.
-  // The actual instruction encoding for these is delayed until we know the final size of
-  // every instruction. When we bind a label to a branch we don't know the final location yet
-  // as some preceding instructions may need to be expanded, so we record a non-final offset.
-  // In FinalizeCode(), we expand the sizes of branches and literal loads that are out of
-  // range. With each expansion, we need to update dependent Fixups, i.e. insntructios with
-  // target on the other side of the expanded insn, as their offsets change and this may
-  // trigger further expansion.
-  //
-  // All Fixups have a 'fixup id' which is a 16 bit unsigned number used to identify the
-  // Fixup. For each unresolved label we keep a singly-linked list of all Fixups pointing
-  // to it, using the fixup ids as links. The first link is stored in the label's position
-  // (the label is linked but not bound), the following links are stored in the code buffer,
-  // in the placeholder where we will eventually emit the actual code.
-
-  class Fixup {
-   public:
-    // Branch type.
-    enum Type : uint8_t {
-      kConditional,               // B<cond>.
-      kUnconditional,             // B.
-      kUnconditionalLink,         // BL.
-      kUnconditionalLinkX,        // BLX.
-      kCompareAndBranchXZero,     // cbz/cbnz.
-      kLoadCodeAddr,              // Get address of a code label, used for Baker read barriers.
-      kLoadLiteralNarrow,         // Load narrrow integer literal.
-      kLoadLiteralWide,           // Load wide integer literal.
-      kLoadLiteralAddr,           // Load address of literal (used for jump table).
-      kLoadFPLiteralSingle,       // Load FP literal single.
-      kLoadFPLiteralDouble,       // Load FP literal double.
-    };
-
-    // Calculated size of branch instruction based on type and offset.
-    enum Size : uint8_t {
-      // Branch variants.
-      kBranch16Bit,
-      kBranch32Bit,
-      // NOTE: We don't support branches which would require multiple instructions, i.e.
-      // conditinoal branches beyond +-1MiB and unconditional branches beyond +-16MiB.
-
-      // CBZ/CBNZ variants.
-      kCbxz16Bit,   // CBZ/CBNZ rX, label; X < 8; 7-bit positive offset.
-      kCbxz32Bit,   // CMP rX, #0 + Bcc label; X < 8; 16-bit Bcc; +-8-bit offset.
-      kCbxz48Bit,   // CMP rX, #0 + Bcc label; X < 8; 32-bit Bcc; up to +-1MiB offset.
-
-      // ADR variants.
-      kCodeAddr4KiB,  // ADR rX, <label>; label must be after the ADR but within 4KiB range.
-                      // Multi-instruction expansion is not supported.
-
-      // Load integer literal variants.
-      // LDR rX, label; X < 8; 16-bit variant up to 1KiB offset; 2 bytes.
-      kLiteral1KiB,
-      // LDR rX, label; 32-bit variant up to 4KiB offset; 4 bytes.
-      kLiteral4KiB,
-      // MOV rX, imm16 + ADD rX, pc + LDR rX, [rX]; X < 8; up to 64KiB offset; 8 bytes.
-      kLiteral64KiB,
-      // MOV rX, modimm + ADD rX, pc + LDR rX, [rX, #imm12]; up to 1MiB offset; 10 bytes.
-      kLiteral1MiB,
-      // NOTE: We don't provide the 12-byte version of kLiteralFar below where the LDR is 16-bit.
-      // MOV rX, imm16 + MOVT rX, imm16 + ADD rX, pc + LDR rX, [rX]; any offset; 14 bytes.
-      kLiteralFar,
-
-      // Load literal base addr.
-      // ADR rX, label; X < 8; 8 bit immediate, shifted to 10 bit. 2 bytes.
-      kLiteralAddr1KiB,
-      // ADR rX, label; 4KiB offset. 4 bytes.
-      kLiteralAddr4KiB,
-      // MOV rX, imm16 + ADD rX, pc; 64KiB offset. 6 bytes.
-      kLiteralAddr64KiB,
-      // MOV rX, imm16 + MOVT rX, imm16 + ADD rX, pc; any offset; 10 bytes.
-      kLiteralAddrFar,
-
-      // Load long or FP literal variants.
-      // VLDR s/dX, label; 32-bit insn, up to 1KiB offset; 4 bytes.
-      kLongOrFPLiteral1KiB,
-      // MOV ip, imm16 + ADD ip, pc + VLDR s/dX, [IP, #0]; up to 64KiB offset; 10 bytes.
-      kLongOrFPLiteral64KiB,
-      // MOV ip, imm16 + MOVT ip, imm16 + ADD ip, pc + VLDR s/dX, [IP]; any offset; 14 bytes.
-      kLongOrFPLiteralFar,
-    };
-
-    // Unresolved branch possibly with a condition.
-    static Fixup Branch(uint32_t location, Type type, Size size = kBranch16Bit,
-                        Condition cond = AL) {
-      DCHECK(type == kConditional || type == kUnconditional ||
-             type == kUnconditionalLink || type == kUnconditionalLinkX);
-      DCHECK(size == kBranch16Bit || size == kBranch32Bit);
-      DCHECK(size == kBranch32Bit || (type == kConditional || type == kUnconditional));
-      return Fixup(kNoRegister, kNoRegister, kNoSRegister, kNoDRegister,
-                   cond, type, size, location);
-    }
-
-    // Unresolved compare-and-branch instruction with a register and condition (EQ or NE).
-    static Fixup CompareAndBranch(uint32_t location, Register rn, Condition cond) {
-      DCHECK(cond == EQ || cond == NE);
-      return Fixup(rn, kNoRegister, kNoSRegister, kNoDRegister,
-                   cond, kCompareAndBranchXZero, kCbxz16Bit, location);
-    }
-
-    // Code address.
-    static Fixup LoadCodeAddress(uint32_t location, Register rt) {
-      return Fixup(rt, kNoRegister, kNoSRegister, kNoDRegister,
-                   AL, kLoadCodeAddr, kCodeAddr4KiB, location);
-    }
-
-    // Load narrow literal.
-    static Fixup LoadNarrowLiteral(uint32_t location, Register rt, Size size) {
-      DCHECK(size == kLiteral1KiB || size == kLiteral4KiB || size == kLiteral64KiB ||
-             size == kLiteral1MiB || size == kLiteralFar);
-      DCHECK(!IsHighRegister(rt) || (size != kLiteral1KiB && size != kLiteral64KiB));
-      return Fixup(rt, kNoRegister, kNoSRegister, kNoDRegister,
-                   AL, kLoadLiteralNarrow, size, location);
-    }
-
-    // Load wide literal.
-    static Fixup LoadWideLiteral(uint32_t location, Register rt, Register rt2,
-                                 Size size = kLongOrFPLiteral1KiB) {
-      DCHECK(size == kLongOrFPLiteral1KiB || size == kLongOrFPLiteral64KiB ||
-             size == kLongOrFPLiteralFar);
-      DCHECK(!IsHighRegister(rt) || (size != kLiteral1KiB && size != kLiteral64KiB));
-      return Fixup(rt, rt2, kNoSRegister, kNoDRegister,
-                   AL, kLoadLiteralWide, size, location);
-    }
-
-    // Load FP single literal.
-    static Fixup LoadSingleLiteral(uint32_t location, SRegister sd,
-                                   Size size = kLongOrFPLiteral1KiB) {
-      DCHECK(size == kLongOrFPLiteral1KiB || size == kLongOrFPLiteral64KiB ||
-             size == kLongOrFPLiteralFar);
-      return Fixup(kNoRegister, kNoRegister, sd, kNoDRegister,
-                   AL, kLoadFPLiteralSingle, size, location);
-    }
-
-    // Load FP double literal.
-    static Fixup LoadDoubleLiteral(uint32_t location, DRegister dd,
-                                   Size size = kLongOrFPLiteral1KiB) {
-      DCHECK(size == kLongOrFPLiteral1KiB || size == kLongOrFPLiteral64KiB ||
-             size == kLongOrFPLiteralFar);
-      return Fixup(kNoRegister, kNoRegister, kNoSRegister, dd,
-                   AL, kLoadFPLiteralDouble, size, location);
-    }
-
-    static Fixup LoadLiteralAddress(uint32_t location, Register rt, Size size) {
-      DCHECK(size == kLiteralAddr1KiB || size == kLiteralAddr4KiB || size == kLiteralAddr64KiB ||
-             size == kLiteralAddrFar);
-      DCHECK(!IsHighRegister(rt) || size != kLiteralAddr1KiB);
-      return Fixup(rt, kNoRegister, kNoSRegister, kNoDRegister,
-                   AL, kLoadLiteralAddr, size, location);
-    }
-
-    Type GetType() const {
-      return type_;
-    }
-
-    bool IsLoadLiteral() const {
-      return GetType() >= kLoadLiteralNarrow;
-    }
-
-    // Returns whether the Fixup can expand from the original size.
-    bool CanExpand() const {
-      switch (GetOriginalSize()) {
-        case kBranch32Bit:
-        case kCbxz48Bit:
-        case kCodeAddr4KiB:
-        case kLiteralFar:
-        case kLiteralAddrFar:
-        case kLongOrFPLiteralFar:
-          return false;
-        default:
-          return true;
-      }
-    }
-
-    Size GetOriginalSize() const {
-      return original_size_;
-    }
-
-    Size GetSize() const {
-      return size_;
-    }
-
-    uint32_t GetOriginalSizeInBytes() const;
-
-    uint32_t GetSizeInBytes() const;
-
-    uint32_t GetLocation() const {
-      return location_;
-    }
-
-    uint32_t GetTarget() const {
-      return target_;
-    }
-
-    uint32_t GetAdjustment() const {
-      return adjustment_;
-    }
-
-    // Prepare the assembler->fixup_dependents_ and each Fixup's dependents_start_/count_.
-    static void PrepareDependents(Thumb2Assembler* assembler);
-
-    ArrayRef<const FixupId> Dependents(const Thumb2Assembler& assembler) const {
-      return ArrayRef<const FixupId>(assembler.fixup_dependents_).SubArray(dependents_start_,
-                                                                           dependents_count_);
-    }
-
-    // Resolve a branch when the target is known.
-    void Resolve(uint32_t target) {
-      DCHECK_EQ(target_, kUnresolved);
-      DCHECK_NE(target, kUnresolved);
-      target_ = target;
-    }
-
-    // Branches with bound targets that are in range can be emitted early.
-    // However, the caller still needs to check if the branch doesn't go over
-    // another Fixup that's not ready to be emitted.
-    bool IsCandidateForEmitEarly() const;
-
-    // Check if the current size is OK for current location_, target_ and adjustment_.
-    // If not, increase the size. Return the size increase, 0 if unchanged.
-    // If the target if after this Fixup, also add the difference to adjustment_,
-    // so that we don't need to consider forward Fixups as their own dependencies.
-    uint32_t AdjustSizeIfNeeded(uint32_t current_code_size);
-
-    // Increase adjustments. This is called for dependents of a Fixup when its size changes.
-    void IncreaseAdjustment(uint32_t increase) {
-      adjustment_ += increase;
-    }
-
-    // Finalize the branch with an adjustment to the location. Both location and target are updated.
-    void Finalize(uint32_t location_adjustment) {
-      DCHECK_NE(target_, kUnresolved);
-      location_ += location_adjustment;
-      target_ += location_adjustment;
-    }
-
-    // Emit the branch instruction into the assembler buffer.  This does the
-    // encoding into the thumb instruction.
-    void Emit(uint32_t emit_location, AssemblerBuffer* buffer, uint32_t code_size) const;
-
-   private:
-    Fixup(Register rn, Register rt2, SRegister sd, DRegister dd,
-          Condition cond, Type type, Size size, uint32_t location)
-        : rn_(rn),
-          rt2_(rt2),
-          sd_(sd),
-          dd_(dd),
-          cond_(cond),
-          type_(type),
-          original_size_(size), size_(size),
-          location_(location),
-          target_(kUnresolved),
-          adjustment_(0u),
-          dependents_count_(0u),
-          dependents_start_(0u) {
-    }
-
-    static size_t SizeInBytes(Size size);
-
-    // The size of padding added before the literal pool.
-    static size_t LiteralPoolPaddingSize(uint32_t current_code_size);
-
-    // Returns the offset from the PC-using insn to the target.
-    int32_t GetOffset(uint32_t current_code_size) const;
-
-    size_t IncreaseSize(Size new_size);
-
-    int32_t LoadWideOrFpEncoding(Register rbase, int32_t offset) const;
-
-    template <typename Function>
-    static void ForExpandableDependencies(Thumb2Assembler* assembler, Function fn);
-
-    static constexpr uint32_t kUnresolved = 0xffffffff;     // Value for target_ for unresolved.
-
-    const Register rn_;   // Rn for cbnz/cbz, Rt for literal loads.
-    Register rt2_;        // For kLoadLiteralWide.
-    SRegister sd_;        // For kLoadFPLiteralSingle.
-    DRegister dd_;        // For kLoadFPLiteralDouble.
-    const Condition cond_;
-    const Type type_;
-    Size original_size_;
-    Size size_;
-    uint32_t location_;     // Offset into assembler buffer in bytes.
-    uint32_t target_;       // Offset into assembler buffer in bytes.
-    uint32_t adjustment_;   // The number of extra bytes inserted between location_ and target_.
-    // Fixups that require adjustment when current size changes are stored in a single
-    // array in the assembler and we store only the start index and count here.
-    uint32_t dependents_count_;
-    uint32_t dependents_start_;
-  };
-
-  // Emit a single 32 or 16 bit data processing instruction.
-  void EmitDataProcessing(Condition cond,
-                          Opcode opcode,
-                          SetCc set_cc,
-                          Register rn,
-                          Register rd,
-                          const ShifterOperand& so);
-
-  // Emit a single 32 bit miscellaneous instruction.
-  void Emit32Miscellaneous(uint8_t op1,
-                           uint8_t op2,
-                           uint32_t rest_encoding);
-
-  // Emit reverse byte instructions: rev, rev16, revsh.
-  void EmitReverseBytes(Register rd, Register rm, uint32_t op);
-
-  // Emit a single 16 bit miscellaneous instruction.
-  void Emit16Miscellaneous(uint32_t rest_encoding);
-
-  // Must the instruction be 32 bits or can it possibly be encoded
-  // in 16 bits?
-  bool Is32BitDataProcessing(Condition cond,
-                             Opcode opcode,
-                             SetCc set_cc,
-                             Register rn,
-                             Register rd,
-                             const ShifterOperand& so);
-
-  // Emit a 32 bit data processing instruction.
-  void Emit32BitDataProcessing(Condition cond,
-                               Opcode opcode,
-                               SetCc set_cc,
-                               Register rn,
-                               Register rd,
-                               const ShifterOperand& so);
-
-  // Emit a 16 bit data processing instruction.
-  void Emit16BitDataProcessing(Condition cond,
-                               Opcode opcode,
-                               SetCc set_cc,
-                               Register rn,
-                               Register rd,
-                               const ShifterOperand& so);
-
-  void Emit16BitAddSub(Condition cond,
-                       Opcode opcode,
-                       SetCc set_cc,
-                       Register rn,
-                       Register rd,
-                       const ShifterOperand& so);
-
-  uint16_t EmitCompareAndBranch(Register rn, uint16_t prev, bool n);
-
-  void EmitLoadStore(Condition cond,
-                     bool load,
-                     bool byte,
-                     bool half,
-                     bool is_signed,
-                     Register rd,
-                     const Address& ad);
-
-  void EmitMemOpAddressMode3(Condition cond,
-                             int32_t mode,
-                             Register rd,
-                             const Address& ad);
-
-  void EmitMultiMemOp(Condition cond,
-                      BlockAddressMode am,
-                      bool load,
-                      Register base,
-                      RegList regs);
-
-  void EmitMulOp(Condition cond,
-                 int32_t opcode,
-                 Register rd,
-                 Register rn,
-                 Register rm,
-                 Register rs);
-
-  void EmitVFPsss(Condition cond,
-                  int32_t opcode,
-                  SRegister sd,
-                  SRegister sn,
-                  SRegister sm);
-
-  void EmitVLdmOrStm(int32_t rest,
-                     uint32_t reg,
-                     int nregs,
-                     Register rn,
-                     bool is_load,
-                     bool dbl,
-                     Condition cond);
-
-  void EmitVFPddd(Condition cond,
-                  int32_t opcode,
-                  DRegister dd,
-                  DRegister dn,
-                  DRegister dm);
-
-  void EmitVFPsd(Condition cond,
-                 int32_t opcode,
-                 SRegister sd,
-                 DRegister dm);
-
-  void EmitVFPds(Condition cond,
-                 int32_t opcode,
-                 DRegister dd,
-                 SRegister sm);
-
-  void EmitVPushPop(uint32_t reg, int nregs, bool push, bool dbl, Condition cond);
-
-  void EmitBranch(Condition cond, Label* label, bool link, bool x);
-  static int32_t EncodeBranchOffset(int32_t offset, int32_t inst);
-  static int DecodeBranchOffset(int32_t inst);
-  void EmitShift(Register rd, Register rm, Shift shift, uint8_t amount,
-                 Condition cond = AL, SetCc set_cc = kCcDontCare);
-  void EmitShift(Register rd, Register rn, Shift shift, Register rm,
-                 Condition cond = AL, SetCc set_cc = kCcDontCare);
-
-  static int32_t GetAllowedLoadOffsetBits(LoadOperandType type);
-  static int32_t GetAllowedStoreOffsetBits(StoreOperandType type);
-  bool CanSplitLoadStoreOffset(int32_t allowed_offset_bits,
-                               int32_t offset,
-                               /*out*/ int32_t* add_to_base,
-                               /*out*/ int32_t* offset_for_load_store);
-  int32_t AdjustLoadStoreOffset(int32_t allowed_offset_bits,
-                                Register temp,
-                                Register base,
-                                int32_t offset,
-                                Condition cond);
-
-  // Whether the assembler can relocate branches. If false, unresolved branches will be
-  // emitted on 32bits.
-  bool can_relocate_branches_;
-
-  // Force the assembler to use 32 bit thumb2 instructions.
-  bool force_32bit_;
-
-  // IfThen conditions.  Used to check that conditional instructions match the preceding IT.
-  Condition it_conditions_[4];
-  uint8_t it_cond_index_;
-  Condition next_condition_;
-
-  void SetItCondition(ItState s, Condition cond, uint8_t index);
-
-  void CheckCondition(Condition cond) {
-    CHECK_EQ(cond, next_condition_);
-
-    // Move to the next condition if there is one.
-    if (it_cond_index_ < 3) {
-      ++it_cond_index_;
-      next_condition_ = it_conditions_[it_cond_index_];
-    } else {
-      next_condition_ = AL;
-    }
-  }
-
-  void CheckConditionLastIt(Condition cond) {
-    if (it_cond_index_ < 3) {
-      // Check that the next condition is AL.  This means that the
-      // current condition is the last in the IT block.
-      CHECK_EQ(it_conditions_[it_cond_index_ + 1], AL);
-    }
-    CheckCondition(cond);
-  }
-
-  FixupId AddFixup(Fixup fixup) {
-    FixupId fixup_id = static_cast<FixupId>(fixups_.size());
-    fixups_.push_back(fixup);
-    // For iterating using FixupId, we need the next id to be representable.
-    DCHECK_EQ(static_cast<size_t>(static_cast<FixupId>(fixups_.size())), fixups_.size());
-    return fixup_id;
-  }
-
-  Fixup* GetFixup(FixupId fixup_id) {
-    DCHECK_LT(fixup_id, fixups_.size());
-    return &fixups_[fixup_id];
-  }
-
-  void BindLabel(Label* label, uint32_t bound_pc);
-  uint32_t BindLiterals();
-  void BindJumpTables(uint32_t code_size);
-  void AdjustFixupIfNeeded(Fixup* fixup, uint32_t* current_code_size,
-                           std::deque<FixupId>* fixups_to_recalculate);
-  uint32_t AdjustFixups();
-  void EmitFixups(uint32_t adjusted_code_size);
-  void EmitLiterals();
-  void EmitJumpTables();
-  void PatchCFI();
-
-  static int16_t BEncoding16(int32_t offset, Condition cond);
-  static int32_t BEncoding32(int32_t offset, Condition cond);
-  static int16_t CbxzEncoding16(Register rn, int32_t offset, Condition cond);
-  static int16_t CmpRnImm8Encoding16(Register rn, int32_t value);
-  static int16_t AddRdnRmEncoding16(Register rdn, Register rm);
-  static int32_t MovwEncoding32(Register rd, int32_t value);
-  static int32_t MovtEncoding32(Register rd, int32_t value);
-  static int32_t MovModImmEncoding32(Register rd, int32_t value);
-  static int16_t LdrLitEncoding16(Register rt, int32_t offset);
-  static int32_t LdrLitEncoding32(Register rt, int32_t offset);
-  static int32_t LdrdEncoding32(Register rt, Register rt2, Register rn, int32_t offset);
-  static int32_t VldrsEncoding32(SRegister sd, Register rn, int32_t offset);
-  static int32_t VldrdEncoding32(DRegister dd, Register rn, int32_t offset);
-  static int16_t LdrRtRnImm5Encoding16(Register rt, Register rn, int32_t offset);
-  static int32_t LdrRtRnImm12Encoding(Register rt, Register rn, int32_t offset);
-  static int16_t AdrEncoding16(Register rd, int32_t offset);
-  static int32_t AdrEncoding32(Register rd, int32_t offset);
-
-  ArenaVector<Fixup> fixups_;
-  ArenaVector<FixupId> fixup_dependents_;
-
-  // Use std::deque<> for literal labels to allow insertions at the end
-  // without invalidating pointers and references to existing elements.
-  ArenaDeque<Literal> literals_;
-
-  // Deduplication map for 64-bit literals, used for LoadDImmediate().
-  ArenaSafeMap<uint64_t, Literal*> literal64_dedupe_map_;
-
-  // Jump table list.
-  ArenaDeque<JumpTable> jump_tables_;
-
-  // Data for AdjustedPosition(), see the description there.
-  uint32_t last_position_adjustment_;
-  uint32_t last_old_position_;
-  FixupId last_fixup_id_;
-};
-
-class ScopedForce32Bit {
- public:
-  explicit ScopedForce32Bit(Thumb2Assembler* assembler, bool force = true)
-      : assembler_(assembler), old_force_32bit_(assembler->IsForced32Bit()) {
-    if (force) {
-      assembler->Force32Bit();
-    }
-  }
-
-  ~ScopedForce32Bit() {
-    if (!old_force_32bit_) {
-      assembler_->Allow16Bit();
-    }
-  }
-
- private:
-  Thumb2Assembler* const assembler_;
-  const bool old_force_32bit_;
-};
-
-}  // namespace arm
-}  // namespace art
-
-#endif  // ART_COMPILER_UTILS_ARM_ASSEMBLER_THUMB2_H_
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
deleted file mode 100644
index 0147a76..0000000
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ /dev/null
@@ -1,1666 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "assembler_thumb2.h"
-
-#include "android-base/stringprintf.h"
-
-#include "base/stl_util.h"
-#include "utils/assembler_test.h"
-
-namespace art {
-
-using android::base::StringPrintf;
-
-class AssemblerThumb2Test : public AssemblerTest<arm::Thumb2Assembler,
-                                                 arm::Register, arm::SRegister,
-                                                 uint32_t> {
- protected:
-  std::string GetArchitectureString() OVERRIDE {
-    return "arm";
-  }
-
-  std::string GetAssemblerParameters() OVERRIDE {
-    return " -march=armv7-a -mcpu=cortex-a15 -mfpu=neon -mthumb";
-  }
-
-  const char* GetAssemblyHeader() OVERRIDE {
-    return kThumb2AssemblyHeader;
-  }
-
-  std::string GetDisassembleParameters() OVERRIDE {
-    return " -D -bbinary -marm --disassembler-options=force-thumb --no-show-raw-insn";
-  }
-
-  void SetUpHelpers() OVERRIDE {
-    if (registers_.size() == 0) {
-      registers_.insert(end(registers_),
-                        {  // NOLINT(whitespace/braces)
-                          new arm::Register(arm::R0),
-                          new arm::Register(arm::R1),
-                          new arm::Register(arm::R2),
-                          new arm::Register(arm::R3),
-                          new arm::Register(arm::R4),
-                          new arm::Register(arm::R5),
-                          new arm::Register(arm::R6),
-                          new arm::Register(arm::R7),
-                          new arm::Register(arm::R8),
-                          new arm::Register(arm::R9),
-                          new arm::Register(arm::R10),
-                          new arm::Register(arm::R11),
-                          new arm::Register(arm::R12),
-                          new arm::Register(arm::R13),
-                          new arm::Register(arm::R14),
-                          new arm::Register(arm::R15)
-                        });
-    }
-  }
-
-  void TearDown() OVERRIDE {
-    AssemblerTest::TearDown();
-    STLDeleteElements(&registers_);
-  }
-
-  std::vector<arm::Register*> GetRegisters() OVERRIDE {
-    return registers_;
-  }
-
-  uint32_t CreateImmediate(int64_t imm_value) OVERRIDE {
-    return imm_value;
-  }
-
-  std::string RepeatInsn(size_t count, const std::string& insn) {
-    std::string result;
-    for (; count != 0u; --count) {
-      result += insn;
-    }
-    return result;
-  }
-
- private:
-  std::vector<arm::Register*> registers_;
-
-  static constexpr const char* kThumb2AssemblyHeader = ".syntax unified\n.thumb\n";
-};
-
-TEST_F(AssemblerThumb2Test, Toolchain) {
-  EXPECT_TRUE(CheckTools());
-}
-
-#define __ GetAssembler()->
-
-TEST_F(AssemblerThumb2Test, Sbfx) {
-  __ sbfx(arm::R0, arm::R1, 0, 1);
-  __ sbfx(arm::R0, arm::R1, 0, 8);
-  __ sbfx(arm::R0, arm::R1, 0, 16);
-  __ sbfx(arm::R0, arm::R1, 0, 32);
-
-  __ sbfx(arm::R0, arm::R1, 8, 1);
-  __ sbfx(arm::R0, arm::R1, 8, 8);
-  __ sbfx(arm::R0, arm::R1, 8, 16);
-  __ sbfx(arm::R0, arm::R1, 8, 24);
-
-  __ sbfx(arm::R0, arm::R1, 16, 1);
-  __ sbfx(arm::R0, arm::R1, 16, 8);
-  __ sbfx(arm::R0, arm::R1, 16, 16);
-
-  __ sbfx(arm::R0, arm::R1, 31, 1);
-
-  const char* expected =
-      "sbfx r0, r1, #0, #1\n"
-      "sbfx r0, r1, #0, #8\n"
-      "sbfx r0, r1, #0, #16\n"
-      "sbfx r0, r1, #0, #32\n"
-
-      "sbfx r0, r1, #8, #1\n"
-      "sbfx r0, r1, #8, #8\n"
-      "sbfx r0, r1, #8, #16\n"
-      "sbfx r0, r1, #8, #24\n"
-
-      "sbfx r0, r1, #16, #1\n"
-      "sbfx r0, r1, #16, #8\n"
-      "sbfx r0, r1, #16, #16\n"
-
-      "sbfx r0, r1, #31, #1\n";
-  DriverStr(expected, "sbfx");
-}
-
-TEST_F(AssemblerThumb2Test, Ubfx) {
-  __ ubfx(arm::R0, arm::R1, 0, 1);
-  __ ubfx(arm::R0, arm::R1, 0, 8);
-  __ ubfx(arm::R0, arm::R1, 0, 16);
-  __ ubfx(arm::R0, arm::R1, 0, 32);
-
-  __ ubfx(arm::R0, arm::R1, 8, 1);
-  __ ubfx(arm::R0, arm::R1, 8, 8);
-  __ ubfx(arm::R0, arm::R1, 8, 16);
-  __ ubfx(arm::R0, arm::R1, 8, 24);
-
-  __ ubfx(arm::R0, arm::R1, 16, 1);
-  __ ubfx(arm::R0, arm::R1, 16, 8);
-  __ ubfx(arm::R0, arm::R1, 16, 16);
-
-  __ ubfx(arm::R0, arm::R1, 31, 1);
-
-  const char* expected =
-      "ubfx r0, r1, #0, #1\n"
-      "ubfx r0, r1, #0, #8\n"
-      "ubfx r0, r1, #0, #16\n"
-      "ubfx r0, r1, #0, #32\n"
-
-      "ubfx r0, r1, #8, #1\n"
-      "ubfx r0, r1, #8, #8\n"
-      "ubfx r0, r1, #8, #16\n"
-      "ubfx r0, r1, #8, #24\n"
-
-      "ubfx r0, r1, #16, #1\n"
-      "ubfx r0, r1, #16, #8\n"
-      "ubfx r0, r1, #16, #16\n"
-
-      "ubfx r0, r1, #31, #1\n";
-  DriverStr(expected, "ubfx");
-}
-
-TEST_F(AssemblerThumb2Test, Vmstat) {
-  __ vmstat();
-
-  const char* expected = "vmrs APSR_nzcv, FPSCR\n";
-
-  DriverStr(expected, "vmrs");
-}
-
-TEST_F(AssemblerThumb2Test, ldrexd) {
-  __ ldrexd(arm::R0, arm::R1, arm::R0);
-  __ ldrexd(arm::R0, arm::R1, arm::R1);
-  __ ldrexd(arm::R0, arm::R1, arm::R2);
-  __ ldrexd(arm::R5, arm::R3, arm::R7);
-
-  const char* expected =
-      "ldrexd r0, r1, [r0]\n"
-      "ldrexd r0, r1, [r1]\n"
-      "ldrexd r0, r1, [r2]\n"
-      "ldrexd r5, r3, [r7]\n";
-  DriverStr(expected, "ldrexd");
-}
-
-TEST_F(AssemblerThumb2Test, strexd) {
-  __ strexd(arm::R9, arm::R0, arm::R1, arm::R0);
-  __ strexd(arm::R9, arm::R0, arm::R1, arm::R1);
-  __ strexd(arm::R9, arm::R0, arm::R1, arm::R2);
-  __ strexd(arm::R9, arm::R5, arm::R3, arm::R7);
-
-  const char* expected =
-      "strexd r9, r0, r1, [r0]\n"
-      "strexd r9, r0, r1, [r1]\n"
-      "strexd r9, r0, r1, [r2]\n"
-      "strexd r9, r5, r3, [r7]\n";
-  DriverStr(expected, "strexd");
-}
-
-TEST_F(AssemblerThumb2Test, clrex) {
-  __ clrex();
-
-  const char* expected = "clrex\n";
-  DriverStr(expected, "clrex");
-}
-
-TEST_F(AssemblerThumb2Test, LdrdStrd) {
-  __ ldrd(arm::R0, arm::Address(arm::R2, 8));
-  __ ldrd(arm::R0, arm::Address(arm::R12));
-  __ strd(arm::R0, arm::Address(arm::R2, 8));
-
-  const char* expected =
-      "ldrd r0, r1, [r2, #8]\n"
-      "ldrd r0, r1, [r12]\n"
-      "strd r0, r1, [r2, #8]\n";
-  DriverStr(expected, "ldrdstrd");
-}
-
-TEST_F(AssemblerThumb2Test, eor) {
-  __ eor(arm::R1, arm::R1, arm::ShifterOperand(arm::R0));
-  __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R1));
-  __ eor(arm::R1, arm::R8, arm::ShifterOperand(arm::R0));
-  __ eor(arm::R8, arm::R1, arm::ShifterOperand(arm::R0));
-  __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R8));
-
-  const char* expected =
-      "eors r1, r0\n"
-      "eor r1, r0, r1\n"
-      "eor r1, r8, r0\n"
-      "eor r8, r1, r0\n"
-      "eor r1, r0, r8\n";
-  DriverStr(expected, "abs");
-}
-
-TEST_F(AssemblerThumb2Test, sub) {
-  __ subs(arm::R1, arm::R0, arm::ShifterOperand(42));
-  __ sub(arm::R1, arm::R0, arm::ShifterOperand(42));
-  __ subs(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
-  __ sub(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
-
-  const char* expected =
-      "subs r1, r0, #42\n"
-      "sub.w r1, r0, #42\n"
-      "subs r1, r0, r2, asr #31\n"
-      "sub r1, r0, r2, asr #31\n";
-  DriverStr(expected, "sub");
-}
-
-TEST_F(AssemblerThumb2Test, add) {
-  __ adds(arm::R1, arm::R0, arm::ShifterOperand(42));
-  __ add(arm::R1, arm::R0, arm::ShifterOperand(42));
-  __ adds(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
-  __ add(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
-
-  const char* expected =
-      "adds r1, r0, #42\n"
-      "add.w r1, r0, #42\n"
-      "adds r1, r0, r2, asr #31\n"
-      "add r1, r0, r2, asr #31\n";
-  DriverStr(expected, "add");
-}
-
-TEST_F(AssemblerThumb2Test, umull) {
-  __ umull(arm::R0, arm::R1, arm::R2, arm::R3);
-
-  const char* expected =
-      "umull r0, r1, r2, r3\n";
-  DriverStr(expected, "umull");
-}
-
-TEST_F(AssemblerThumb2Test, smull) {
-  __ smull(arm::R0, arm::R1, arm::R2, arm::R3);
-
-  const char* expected =
-      "smull r0, r1, r2, r3\n";
-  DriverStr(expected, "smull");
-}
-
-TEST_F(AssemblerThumb2Test, LoadByteFromThumbOffset) {
-  arm::LoadOperandType type = arm::kLoadUnsignedByte;
-
-  __ LoadFromOffset(type, arm::R0, arm::R7, 0);
-  __ LoadFromOffset(type, arm::R1, arm::R7, 31);
-  __ LoadFromOffset(type, arm::R2, arm::R7, 32);
-  __ LoadFromOffset(type, arm::R3, arm::R7, 4095);
-  __ LoadFromOffset(type, arm::R4, arm::SP, 0);
-
-  const char* expected =
-      "ldrb r0, [r7, #0]\n"
-      "ldrb r1, [r7, #31]\n"
-      "ldrb.w r2, [r7, #32]\n"
-      "ldrb.w r3, [r7, #4095]\n"
-      "ldrb.w r4, [sp, #0]\n";
-  DriverStr(expected, "LoadByteFromThumbOffset");
-}
-
-TEST_F(AssemblerThumb2Test, StoreByteToThumbOffset) {
-  arm::StoreOperandType type = arm::kStoreByte;
-
-  __ StoreToOffset(type, arm::R0, arm::R7, 0);
-  __ StoreToOffset(type, arm::R1, arm::R7, 31);
-  __ StoreToOffset(type, arm::R2, arm::R7, 32);
-  __ StoreToOffset(type, arm::R3, arm::R7, 4095);
-  __ StoreToOffset(type, arm::R4, arm::SP, 0);
-
-  const char* expected =
-      "strb r0, [r7, #0]\n"
-      "strb r1, [r7, #31]\n"
-      "strb.w r2, [r7, #32]\n"
-      "strb.w r3, [r7, #4095]\n"
-      "strb.w r4, [sp, #0]\n";
-  DriverStr(expected, "StoreByteToThumbOffset");
-}
-
-TEST_F(AssemblerThumb2Test, LoadHalfFromThumbOffset) {
-  arm::LoadOperandType type = arm::kLoadUnsignedHalfword;
-
-  __ LoadFromOffset(type, arm::R0, arm::R7, 0);
-  __ LoadFromOffset(type, arm::R1, arm::R7, 62);
-  __ LoadFromOffset(type, arm::R2, arm::R7, 64);
-  __ LoadFromOffset(type, arm::R3, arm::R7, 4094);
-  __ LoadFromOffset(type, arm::R4, arm::SP, 0);
-  __ LoadFromOffset(type, arm::R5, arm::R7, 1);  // Unaligned
-
-  const char* expected =
-      "ldrh r0, [r7, #0]\n"
-      "ldrh r1, [r7, #62]\n"
-      "ldrh.w r2, [r7, #64]\n"
-      "ldrh.w r3, [r7, #4094]\n"
-      "ldrh.w r4, [sp, #0]\n"
-      "ldrh.w r5, [r7, #1]\n";
-  DriverStr(expected, "LoadHalfFromThumbOffset");
-}
-
-TEST_F(AssemblerThumb2Test, StoreHalfToThumbOffset) {
-  arm::StoreOperandType type = arm::kStoreHalfword;
-
-  __ StoreToOffset(type, arm::R0, arm::R7, 0);
-  __ StoreToOffset(type, arm::R1, arm::R7, 62);
-  __ StoreToOffset(type, arm::R2, arm::R7, 64);
-  __ StoreToOffset(type, arm::R3, arm::R7, 4094);
-  __ StoreToOffset(type, arm::R4, arm::SP, 0);
-  __ StoreToOffset(type, arm::R5, arm::R7, 1);  // Unaligned
-
-  const char* expected =
-      "strh r0, [r7, #0]\n"
-      "strh r1, [r7, #62]\n"
-      "strh.w r2, [r7, #64]\n"
-      "strh.w r3, [r7, #4094]\n"
-      "strh.w r4, [sp, #0]\n"
-      "strh.w r5, [r7, #1]\n";
-  DriverStr(expected, "StoreHalfToThumbOffset");
-}
-
-TEST_F(AssemblerThumb2Test, LoadWordFromSpPlusOffset) {
-  arm::LoadOperandType type = arm::kLoadWord;
-
-  __ LoadFromOffset(type, arm::R0, arm::SP, 0);
-  __ LoadFromOffset(type, arm::R1, arm::SP, 124);
-  __ LoadFromOffset(type, arm::R2, arm::SP, 128);
-  __ LoadFromOffset(type, arm::R3, arm::SP, 1020);
-  __ LoadFromOffset(type, arm::R4, arm::SP, 1024);
-  __ LoadFromOffset(type, arm::R5, arm::SP, 4092);
-  __ LoadFromOffset(type, arm::R6, arm::SP, 1);  // Unaligned
-
-  const char* expected =
-      "ldr r0, [sp, #0]\n"
-      "ldr r1, [sp, #124]\n"
-      "ldr r2, [sp, #128]\n"
-      "ldr r3, [sp, #1020]\n"
-      "ldr.w r4, [sp, #1024]\n"
-      "ldr.w r5, [sp, #4092]\n"
-      "ldr.w r6, [sp, #1]\n";
-  DriverStr(expected, "LoadWordFromSpPlusOffset");
-}
-
-TEST_F(AssemblerThumb2Test, StoreWordToSpPlusOffset) {
-  arm::StoreOperandType type = arm::kStoreWord;
-
-  __ StoreToOffset(type, arm::R0, arm::SP, 0);
-  __ StoreToOffset(type, arm::R1, arm::SP, 124);
-  __ StoreToOffset(type, arm::R2, arm::SP, 128);
-  __ StoreToOffset(type, arm::R3, arm::SP, 1020);
-  __ StoreToOffset(type, arm::R4, arm::SP, 1024);
-  __ StoreToOffset(type, arm::R5, arm::SP, 4092);
-  __ StoreToOffset(type, arm::R6, arm::SP, 1);  // Unaligned
-
-  const char* expected =
-      "str r0, [sp, #0]\n"
-      "str r1, [sp, #124]\n"
-      "str r2, [sp, #128]\n"
-      "str r3, [sp, #1020]\n"
-      "str.w r4, [sp, #1024]\n"
-      "str.w r5, [sp, #4092]\n"
-      "str.w r6, [sp, #1]\n";
-  DriverStr(expected, "StoreWordToSpPlusOffset");
-}
-
-TEST_F(AssemblerThumb2Test, LoadWordFromPcPlusOffset) {
-  arm::LoadOperandType type = arm::kLoadWord;
-
-  __ LoadFromOffset(type, arm::R0, arm::PC, 0);
-  __ LoadFromOffset(type, arm::R1, arm::PC, 124);
-  __ LoadFromOffset(type, arm::R2, arm::PC, 128);
-  __ LoadFromOffset(type, arm::R3, arm::PC, 1020);
-  __ LoadFromOffset(type, arm::R4, arm::PC, 1024);
-  __ LoadFromOffset(type, arm::R5, arm::PC, 4092);
-  __ LoadFromOffset(type, arm::R6, arm::PC, 1);  // Unaligned
-
-  const char* expected =
-      "ldr r0, [pc, #0]\n"
-      "ldr r1, [pc, #124]\n"
-      "ldr r2, [pc, #128]\n"
-      "ldr r3, [pc, #1020]\n"
-      "ldr.w r4, [pc, #1024]\n"
-      "ldr.w r5, [pc, #4092]\n"
-      "ldr.w r6, [pc, #1]\n";
-  DriverStr(expected, "LoadWordFromPcPlusOffset");
-}
-
-TEST_F(AssemblerThumb2Test, StoreWordToThumbOffset) {
-  arm::StoreOperandType type = arm::kStoreWord;
-  int32_t offset = 4092;
-  ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
-
-  __ StoreToOffset(type, arm::R0, arm::SP, offset);
-  __ StoreToOffset(type, arm::IP, arm::SP, offset);
-  __ StoreToOffset(type, arm::IP, arm::R5, offset);
-
-  const char* expected =
-      "str r0, [sp, #4092]\n"
-      "str ip, [sp, #4092]\n"
-      "str ip, [r5, #4092]\n";
-  DriverStr(expected, "StoreWordToThumbOffset");
-}
-
-TEST_F(AssemblerThumb2Test, StoreWordToNonThumbOffset) {
-  arm::StoreOperandType type = arm::kStoreWord;
-  int32_t offset = 4096;
-  ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
-
-  __ StoreToOffset(type, arm::R0, arm::SP, offset);
-  __ StoreToOffset(type, arm::IP, arm::SP, offset);
-  __ StoreToOffset(type, arm::IP, arm::R5, offset);
-
-  const char* expected =
-      "add.w ip, sp, #4096\n"   // AddConstant(ip, sp, 4096)
-      "str r0, [ip, #0]\n"
-
-      "str r5, [sp, #-4]!\n"    // Push(r5)
-      "add.w r5, sp, #4096\n"   // AddConstant(r5, 4100 & ~0xfff)
-      "str ip, [r5, #4]\n"      // StoreToOffset(type, ip, r5, 4100 & 0xfff)
-      "ldr r5, [sp], #4\n"      // Pop(r5)
-
-      "str r6, [sp, #-4]!\n"    // Push(r6)
-      "add.w r6, r5, #4096\n"   // AddConstant(r6, r5, 4096 & ~0xfff)
-      "str ip, [r6, #0]\n"      // StoreToOffset(type, ip, r6, 4096 & 0xfff)
-      "ldr r6, [sp], #4\n";     // Pop(r6)
-  DriverStr(expected, "StoreWordToNonThumbOffset");
-}
-
-TEST_F(AssemblerThumb2Test, StoreWordPairToThumbOffset) {
-  arm::StoreOperandType type = arm::kStoreWordPair;
-  int32_t offset = 1020;
-  ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
-
-  __ StoreToOffset(type, arm::R0, arm::SP, offset);
-  // We cannot use IP (i.e. R12) as first source register, as it would
-  // force us to use SP (i.e. R13) as second source register, which
-  // would have an "unpredictable" effect according to the ARMv7
-  // specification (the T1 encoding describes the result as
-  // UNPREDICTABLE when of the source registers is R13).
-  //
-  // So we use (R11, IP) (e.g. (R11, R12)) as source registers in the
-  // following instructions.
-  __ StoreToOffset(type, arm::R11, arm::SP, offset);
-  __ StoreToOffset(type, arm::R11, arm::R5, offset);
-
-  const char* expected =
-      "strd r0, r1, [sp, #1020]\n"
-      "strd r11, ip, [sp, #1020]\n"
-      "strd r11, ip, [r5, #1020]\n";
-  DriverStr(expected, "StoreWordPairToThumbOffset");
-}
-
-TEST_F(AssemblerThumb2Test, StoreWordPairToNonThumbOffset) {
-  arm::StoreOperandType type = arm::kStoreWordPair;
-  int32_t offset = 1024;
-  ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
-
-  __ StoreToOffset(type, arm::R0, arm::SP, offset);
-  // Same comment as in AssemblerThumb2Test.StoreWordPairToThumbOffset
-  // regarding the use of (R11, IP) (e.g. (R11, R12)) as source
-  // registers in the following instructions.
-  __ StoreToOffset(type, arm::R11, arm::SP, offset);
-  __ StoreToOffset(type, arm::R11, arm::R5, offset);
-
-  const char* expected =
-      "add.w ip, sp, #1024\n"     // AddConstant(ip, sp, 1024)
-      "strd r0, r1, [ip, #0]\n"
-
-      "str r5, [sp, #-4]!\n"      // Push(r5)
-      "add.w r5, sp, #1024\n"     // AddConstant(r5, sp, (1024 + kRegisterSize) & ~0x3fc)
-      "strd r11, ip, [r5, #4]\n"  // StoreToOffset(type, r11, sp, (1024 + kRegisterSize) & 0x3fc)
-      "ldr r5, [sp], #4\n"        // Pop(r5)
-
-      "str r6, [sp, #-4]!\n"      // Push(r6)
-      "add.w r6, r5, #1024\n"     // AddConstant(r6, r5, 1024 & ~0x3fc)
-      "strd r11, ip, [r6, #0]\n"  // StoreToOffset(type, r11, r6, 1024 & 0x3fc)
-      "ldr r6, [sp], #4\n";       // Pop(r6)
-  DriverStr(expected, "StoreWordPairToNonThumbOffset");
-}
-
-TEST_F(AssemblerThumb2Test, DistantBackBranch) {
-  Label start, end;
-  __ Bind(&start);
-  constexpr size_t kLdrR0R0Count1 = 256;
-  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ b(&end, arm::EQ);
-  __ b(&start, arm::LT);
-  constexpr size_t kLdrR0R0Count2 = 256;
-  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ Bind(&end);
-
-  std::string expected =
-      "0:\n" +
-      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
-      "beq 1f\n"
-      "blt 0b\n" +
-      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
-      "1:\n";
-  DriverStr(expected, "DistantBackBranch");
-}
-
-TEST_F(AssemblerThumb2Test, TwoCbzMaxOffset) {
-  Label label0, label1, label2;
-  __ cbz(arm::R0, &label1);
-  constexpr size_t kLdrR0R0Count1 = 63;
-  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ Bind(&label0);
-  __ cbz(arm::R0, &label2);
-  __ Bind(&label1);
-  constexpr size_t kLdrR0R0Count2 = 64;
-  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ Bind(&label2);
-
-  std::string expected =
-      "cbz r0, 1f\n" +            // cbz r0, label1
-      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
-      "0:\n"
-      "cbz r0, 2f\n"              // cbz r0, label2
-      "1:\n" +
-      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
-      "2:\n";
-  DriverStr(expected, "TwoCbzMaxOffset");
-
-  EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u,
-            __ GetAdjustedPosition(label0.Position()));
-  EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 0u,
-            __ GetAdjustedPosition(label1.Position()));
-  EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 0u,
-            __ GetAdjustedPosition(label2.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, TwoCbzBeyondMaxOffset) {
-  Label label0, label1, label2;
-  __ cbz(arm::R0, &label1);
-  constexpr size_t kLdrR0R0Count1 = 63;
-  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ Bind(&label0);
-  __ cbz(arm::R0, &label2);
-  __ Bind(&label1);
-  constexpr size_t kLdrR0R0Count2 = 65;
-  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ Bind(&label2);
-
-  std::string expected =
-      "cmp r0, #0\n"              // cbz r0, label1
-      "beq.n 1f\n" +
-      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
-      "0:\n"
-      "cmp r0, #0\n"              // cbz r0, label2
-      "beq.n 2f\n"
-      "1:\n" +
-      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
-      "2:\n";
-  DriverStr(expected, "TwoCbzBeyondMaxOffset");
-
-  EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
-            __ GetAdjustedPosition(label0.Position()));
-  EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 4u,
-            __ GetAdjustedPosition(label1.Position()));
-  EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 4u,
-            __ GetAdjustedPosition(label2.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, TwoCbzSecondAtMaxB16Offset) {
-  Label label0, label1, label2;
-  __ cbz(arm::R0, &label1);
-  constexpr size_t kLdrR0R0Count1 = 62;
-  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ Bind(&label0);
-  __ cbz(arm::R0, &label2);
-  __ Bind(&label1);
-  constexpr size_t kLdrR0R0Count2 = 128;
-  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ Bind(&label2);
-
-  std::string expected =
-      "cbz r0, 1f\n" +            // cbz r0, label1
-      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
-      "0:\n"
-      "cmp r0, #0\n"              // cbz r0, label2
-      "beq.n 2f\n"
-      "1:\n" +
-      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
-      "2:\n";
-  DriverStr(expected, "TwoCbzSecondAtMaxB16Offset");
-
-  EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u,
-            __ GetAdjustedPosition(label0.Position()));
-  EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u,
-            __ GetAdjustedPosition(label1.Position()));
-  EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u,
-            __ GetAdjustedPosition(label2.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, TwoCbzSecondBeyondMaxB16Offset) {
-  Label label0, label1, label2;
-  __ cbz(arm::R0, &label1);
-  constexpr size_t kLdrR0R0Count1 = 62;
-  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ Bind(&label0);
-  __ cbz(arm::R0, &label2);
-  __ Bind(&label1);
-  constexpr size_t kLdrR0R0Count2 = 129;
-  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ Bind(&label2);
-
-  std::string expected =
-      "cmp r0, #0\n"              // cbz r0, label1
-      "beq.n 1f\n" +
-      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
-      "0:\n"
-      "cmp r0, #0\n"              // cbz r0, label2
-      "beq.w 2f\n"
-      "1:\n" +
-      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
-      "2:\n";
-  DriverStr(expected, "TwoCbzSecondBeyondMaxB16Offset");
-
-  EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
-            __ GetAdjustedPosition(label0.Position()));
-  EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u,
-            __ GetAdjustedPosition(label1.Position()));
-  EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u,
-            __ GetAdjustedPosition(label2.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, TwoCbzFirstAtMaxB16Offset) {
-  Label label0, label1, label2;
-  __ cbz(arm::R0, &label1);
-  constexpr size_t kLdrR0R0Count1 = 127;
-  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ Bind(&label0);
-  __ cbz(arm::R0, &label2);
-  __ Bind(&label1);
-  constexpr size_t kLdrR0R0Count2 = 64;
-  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ Bind(&label2);
-
-  std::string expected =
-      "cmp r0, #0\n"              // cbz r0, label1
-      "beq.n 1f\n" +
-      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
-      "0:\n"
-      "cbz r0, 2f\n"              // cbz r0, label2
-      "1:\n" +
-      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
-      "2:\n";
-  DriverStr(expected, "TwoCbzFirstAtMaxB16Offset");
-
-  EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
-            __ GetAdjustedPosition(label0.Position()));
-  EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u,
-            __ GetAdjustedPosition(label1.Position()));
-  EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u,
-            __ GetAdjustedPosition(label2.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, TwoCbzFirstBeyondMaxB16Offset) {
-  Label label0, label1, label2;
-  __ cbz(arm::R0, &label1);
-  constexpr size_t kLdrR0R0Count1 = 127;
-  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ Bind(&label0);
-  __ cbz(arm::R0, &label2);
-  __ Bind(&label1);
-  constexpr size_t kLdrR0R0Count2 = 65;
-  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ Bind(&label2);
-
-  std::string expected =
-      "cmp r0, #0\n"              // cbz r0, label1
-      "beq.w 1f\n" +
-      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
-      "0:\n"
-      "cmp r0, #0\n"              // cbz r0, label2
-      "beq.n 2f\n"
-      "1:\n" +
-      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
-      "2:\n";
-  DriverStr(expected, "TwoCbzFirstBeyondMaxB16Offset");
-
-  EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 4u,
-            __ GetAdjustedPosition(label0.Position()));
-  EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u,
-            __ GetAdjustedPosition(label1.Position()));
-  EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u,
-            __ GetAdjustedPosition(label2.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, LoadLiteralMax1KiB) {
-  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
-  __ LoadLiteral(arm::R0, literal);
-  Label label;
-  __ Bind(&label);
-  constexpr size_t kLdrR0R0Count = 511;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  std::string expected =
-      "1:\n"
-      "ldr.n r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2, 0\n"
-      "2:\n"
-      ".word 0x12345678\n";
-  DriverStr(expected, "LoadLiteralMax1KiB");
-
-  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u,
-            __ GetAdjustedPosition(label.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiB) {
-  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
-  __ LoadLiteral(arm::R0, literal);
-  Label label;
-  __ Bind(&label);
-  constexpr size_t kLdrR0R0Count = 512;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  std::string expected =
-      "1:\n"
-      "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2, 0\n"
-      "2:\n"
-      ".word 0x12345678\n";
-  DriverStr(expected, "LoadLiteralBeyondMax1KiB");
-
-  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u,
-            __ GetAdjustedPosition(label.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, LoadLiteralMax4KiB) {
-  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
-  __ LoadLiteral(arm::R1, literal);
-  Label label;
-  __ Bind(&label);
-  constexpr size_t kLdrR0R0Count = 2046;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  std::string expected =
-      "1:\n"
-      "ldr.w r1, [pc, #((2f - 1b - 2) & ~2)]\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2, 0\n"
-      "2:\n"
-      ".word 0x12345678\n";
-  DriverStr(expected, "LoadLiteralMax4KiB");
-
-  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u,
-            __ GetAdjustedPosition(label.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax4KiB) {
-  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
-  __ LoadLiteral(arm::R1, literal);
-  Label label;
-  __ Bind(&label);
-  constexpr size_t kLdrR0R0Count = 2047;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  std::string expected =
-      "movw r1, #4096\n"  // "as" does not consider (2f - 1f - 4) a constant expression for movw.
-      "1:\n"
-      "add r1, pc\n"
-      "ldr r1, [r1, #0]\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2, 0\n"
-      "2:\n"
-      ".word 0x12345678\n";
-  DriverStr(expected, "LoadLiteralBeyondMax4KiB");
-
-  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
-            __ GetAdjustedPosition(label.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, LoadLiteralMax64KiB) {
-  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
-  __ LoadLiteral(arm::R1, literal);
-  Label label;
-  __ Bind(&label);
-  constexpr size_t kLdrR0R0Count = (1u << 15) - 2u;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  std::string expected =
-      "movw r1, #0xfffc\n"  // "as" does not consider (2f - 1f - 4) a constant expression for movw.
-      "1:\n"
-      "add r1, pc\n"
-      "ldr r1, [r1, #0]\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2, 0\n"
-      "2:\n"
-      ".word 0x12345678\n";
-  DriverStr(expected, "LoadLiteralMax64KiB");
-
-  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
-            __ GetAdjustedPosition(label.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax64KiB) {
-  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
-  __ LoadLiteral(arm::R1, literal);
-  Label label;
-  __ Bind(&label);
-  constexpr size_t kLdrR0R0Count = (1u << 15) - 1u;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  std::string expected =
-      "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n"
-      "1:\n"
-      "add r1, pc\n"
-      "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2, 0\n"
-      "2:\n"
-      ".word 0x12345678\n";
-  DriverStr(expected, "LoadLiteralBeyondMax64KiB");
-
-  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u,
-            __ GetAdjustedPosition(label.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, LoadLiteralMax1MiB) {
-  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
-  __ LoadLiteral(arm::R1, literal);
-  Label label;
-  __ Bind(&label);
-  constexpr size_t kLdrR0R0Count = (1u << 19) - 3u;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  std::string expected =
-      "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n"
-      "1:\n"
-      "add r1, pc\n"
-      "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2, 0\n"
-      "2:\n"
-      ".word 0x12345678\n";
-  DriverStr(expected, "LoadLiteralMax1MiB");
-
-  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u,
-            __ GetAdjustedPosition(label.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1MiB) {
-  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
-  __ LoadLiteral(arm::R1, literal);
-  Label label;
-  __ Bind(&label);
-  constexpr size_t kLdrR0R0Count = (1u << 19) - 2u;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  std::string expected =
-      // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
-      "movw r1, #(0x100000 & 0xffff)\n"
-      // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
-      "movt r1, #(0x100000 >> 16)\n"
-      "1:\n"
-      "add r1, pc\n"
-      "ldr.w r1, [r1, #0]\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2, 0\n"
-      "2:\n"
-      ".word 0x12345678\n";
-  DriverStr(expected, "LoadLiteralBeyondMax1MiB");
-
-  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u,
-            __ GetAdjustedPosition(label.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, LoadLiteralFar) {
-  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
-  __ LoadLiteral(arm::R1, literal);
-  Label label;
-  __ Bind(&label);
-  constexpr size_t kLdrR0R0Count = (1u << 19) - 2u + 0x1234;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  std::string expected =
-      // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
-      "movw r1, #((0x100000 + 2 * 0x1234) & 0xffff)\n"
-      // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
-      "movt r1, #((0x100000 + 2 * 0x1234) >> 16)\n"
-      "1:\n"
-      "add r1, pc\n"
-      "ldr.w r1, [r1, #0]\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2, 0\n"
-      "2:\n"
-      ".word 0x12345678\n";
-  DriverStr(expected, "LoadLiteralFar");
-
-  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u,
-            __ GetAdjustedPosition(label.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, LoadLiteralWideMax1KiB) {
-  arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
-  __ LoadLiteral(arm::R1, arm::R3, literal);
-  Label label;
-  __ Bind(&label);
-  constexpr size_t kLdrR0R0Count = 510;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  std::string expected =
-      "1:\n"
-      "ldrd r1, r3, [pc, #((2f - 1b - 2) & ~2)]\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2, 0\n"
-      "2:\n"
-      ".word 0x87654321\n"
-      ".word 0x12345678\n";
-  DriverStr(expected, "LoadLiteralWideMax1KiB");
-
-  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u,
-            __ GetAdjustedPosition(label.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, LoadLiteralWideBeyondMax1KiB) {
-  arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
-  __ LoadLiteral(arm::R1, arm::R3, literal);
-  Label label;
-  __ Bind(&label);
-  constexpr size_t kLdrR0R0Count = 511;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  std::string expected =
-      // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
-      "movw ip, #(0x408 - 0x4 - 4)\n"
-      "1:\n"
-      "add ip, pc\n"
-      "ldrd r1, r3, [ip, #0]\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2, 0\n"
-      "2:\n"
-      ".word 0x87654321\n"
-      ".word 0x12345678\n";
-  DriverStr(expected, "LoadLiteralWideBeyondMax1KiB");
-
-  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
-            __ GetAdjustedPosition(label.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, LoadLiteralSingleMax64KiB) {
-  // The literal size must match but the type doesn't, so use an int32_t rather than float.
-  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
-  __ LoadLiteral(arm::S3, literal);
-  Label label;
-  __ Bind(&label);
-  constexpr size_t kLdrR0R0Count = (1 << 15) - 3u;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  std::string expected =
-      // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
-      "movw ip, #(0x10004 - 0x4 - 4)\n"
-      "1:\n"
-      "add ip, pc\n"
-      "vldr s3, [ip, #0]\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2, 0\n"
-      "2:\n"
-      ".word 0x12345678\n";
-  DriverStr(expected, "LoadLiteralSingleMax64KiB");
-
-  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
-            __ GetAdjustedPosition(label.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, LoadLiteralSingleMax64KiB_UnalignedPC) {
-  // The literal size must match but the type doesn't, so use an int32_t rather than float.
-  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
-  __ ldr(arm::R0, arm::Address(arm::R0));
-  __ LoadLiteral(arm::S3, literal);
-  Label label;
-  __ Bind(&label);
-  constexpr size_t kLdrR0R0Count = (1 << 15) - 4u;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  std::string expected =
-      "ldr r0, [r0]\n"
-      // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
-      "movw ip, #(0x10004 - 0x6 - 4)\n"
-      "1:\n"
-      "add ip, pc\n"
-      "vldr s3, [ip, #0]\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2, 0\n"
-      "2:\n"
-      ".word 0x12345678\n";
-  DriverStr(expected, "LoadLiteralSingleMax64KiB_UnalignedPC");
-
-  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
-            __ GetAdjustedPosition(label.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, LoadLiteralDoubleBeyondMax64KiB) {
-  // The literal size must match but the type doesn't, so use an int64_t rather than double.
-  arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
-  __ LoadLiteral(arm::D3, literal);
-  Label label;
-  __ Bind(&label);
-  constexpr size_t kLdrR0R0Count = (1 << 15) - 2u;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  std::string expected =
-      // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
-      "movw ip, #((0x1000c - 0x8 - 4) & 0xffff)\n"
-      // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
-      "movt ip, #((0x1000c - 0x8 - 4) >> 16)\n"
-      "1:\n"
-      "add ip, pc\n"
-      "vldr d3, [ip, #0]\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2, 0\n"
-      "2:\n"
-      ".word 0x87654321\n"
-      ".word 0x12345678\n";
-  DriverStr(expected, "LoadLiteralDoubleBeyondMax64KiB");
-
-  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u,
-            __ GetAdjustedPosition(label.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, LoadLiteralDoubleFar) {
-  // The literal size must match but the type doesn't, so use an int64_t rather than double.
-  arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
-  __ LoadLiteral(arm::D3, literal);
-  Label label;
-  __ Bind(&label);
-  constexpr size_t kLdrR0R0Count = (1 << 15) - 2u + 0x1234;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  std::string expected =
-      // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
-      "movw ip, #((0x1000c + 2 * 0x1234 - 0x8 - 4) & 0xffff)\n"
-      // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
-      "movt ip, #((0x1000c + 2 * 0x1234 - 0x8 - 4) >> 16)\n"
-      "1:\n"
-      "add ip, pc\n"
-      "vldr d3, [ip, #0]\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2, 0\n"
-      "2:\n"
-      ".word 0x87654321\n"
-      ".word 0x12345678\n";
-  DriverStr(expected, "LoadLiteralDoubleFar");
-
-  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u,
-            __ GetAdjustedPosition(label.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiBDueToAlignmentOnSecondPass) {
-  // First part: as TwoCbzBeyondMaxOffset but add one 16-bit instruction to the end,
-  // so that the size is not Aligned<4>(.). On the first pass, the assembler resizes
-  // the second CBZ because it's out of range, then it will resize the first CBZ
-  // which has been pushed out of range. Thus, after the first pass, the code size
-  // will appear Aligned<4>(.) but the final size will not be.
-  Label label0, label1, label2;
-  __ cbz(arm::R0, &label1);
-  constexpr size_t kLdrR0R0Count1 = 63;
-  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ Bind(&label0);
-  __ cbz(arm::R0, &label2);
-  __ Bind(&label1);
-  constexpr size_t kLdrR0R0Count2 = 65;
-  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ Bind(&label2);
-  __ ldr(arm::R0, arm::Address(arm::R0));
-
-  std::string expected_part1 =
-      "cmp r0, #0\n"              // cbz r0, label1
-      "beq.n 1f\n" +
-      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
-      "0:\n"
-      "cmp r0, #0\n"              // cbz r0, label2
-      "beq.n 2f\n"
-      "1:\n" +
-      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
-      "2:\n"                      // Here the offset is Aligned<4>(.).
-      "ldr r0, [r0]\n";           // Make the first part
-
-  // Second part: as LoadLiteralMax1KiB with the caveat that the offset of the load
-  // literal will not be Aligned<4>(.) but it will appear to be when we process the
-  // instruction during the first pass, so the literal will need a padding and it
-  // will push the literal out of range, so we shall end up with "ldr.w".
-  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
-  __ LoadLiteral(arm::R0, literal);
-  Label label;
-  __ Bind(&label);
-  constexpr size_t kLdrR0R0Count = 511;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  std::string expected =
-      expected_part1 +
-      "1:\n"
-      "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2, 0\n"
-      "2:\n"
-      ".word 0x12345678\n";
-  DriverStr(expected, "LoadLiteralMax1KiB");
-
-  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
-            __ GetAdjustedPosition(label.Position()));
-}
-
-TEST_F(AssemblerThumb2Test, BindTrackedLabel) {
-  Label non_tracked, tracked, branch_target;
-
-  // A few dummy loads on entry.
-  constexpr size_t kLdrR0R0Count = 5;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  // A branch that will need to be fixed up.
-  __ cbz(arm::R0, &branch_target);
-
-  // Some more dummy loads.
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  // Now insert tracked and untracked label.
-  __ Bind(&non_tracked);
-  __ BindTrackedLabel(&tracked);
-
-  // A lot of dummy loads, to ensure the branch needs resizing.
-  constexpr size_t kLdrR0R0CountLong = 60;
-  for (size_t i = 0; i != kLdrR0R0CountLong; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  // Bind the branch target.
-  __ Bind(&branch_target);
-
-  // One more load.
-  __ ldr(arm::R0, arm::Address(arm::R0));
-
-  std::string expected =
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      "cmp r0, #0\n"                                                       // cbz r0, 1f
-      "beq.n 1f\n" +
-      RepeatInsn(kLdrR0R0Count + kLdrR0R0CountLong, "ldr r0, [r0]\n") +
-      "1:\n"
-      "ldr r0, [r0]\n";
-  DriverStr(expected, "BindTrackedLabel");
-
-  // Expectation is that the tracked label should have moved.
-  EXPECT_LT(non_tracked.Position(), tracked.Position());
-}
-
-TEST_F(AssemblerThumb2Test, JumpTable) {
-  // The jump table. Use three labels.
-  Label label1, label2, label3;
-  std::vector<Label*> labels({ &label1, &label2, &label3 });
-
-  // A few dummy loads on entry, interspersed with 2 labels.
-  constexpr size_t kLdrR0R0Count = 5;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ BindTrackedLabel(&label1);
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ BindTrackedLabel(&label2);
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  // Create the jump table, emit the base load.
-  arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
-
-  // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
-  // it's being used.
-  __ ldr(arm::R0, arm::Address(arm::R0));
-
-  // Emit the jump
-  __ EmitJumpTableDispatch(jump_table, arm::R1);
-
-  // Some more dummy instructions.
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ BindTrackedLabel(&label3);
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {          // Note: odd so there's no alignment
-    __ ldr(arm::R0, arm::Address(arm::R0));              //       necessary, as gcc as emits nops,
-  }                                                      //       whereas we emit 0 != nop.
-
-  static_assert((kLdrR0R0Count + 3) * 2 < 1 * KB, "Too much offset");
-
-  std::string expected =
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".L1:\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".L2:\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      "adr r1, .Ljump_table\n"
-      "ldr r0, [r0]\n"
-      ".Lbase:\n"
-      "add pc, r1\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".L3:\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".align 2\n"
-      ".Ljump_table:\n"
-      ".4byte (.L1 - .Lbase - 4)\n"
-      ".4byte (.L2 - .Lbase - 4)\n"
-      ".4byte (.L3 - .Lbase - 4)\n";
-  DriverStr(expected, "JumpTable");
-}
-
-// Test for >1K fixup.
-TEST_F(AssemblerThumb2Test, JumpTable4K) {
-  // The jump table. Use three labels.
-  Label label1, label2, label3;
-  std::vector<Label*> labels({ &label1, &label2, &label3 });
-
-  // A few dummy loads on entry, interspersed with 2 labels.
-  constexpr size_t kLdrR0R0Count = 5;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ BindTrackedLabel(&label1);
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ BindTrackedLabel(&label2);
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  // Create the jump table, emit the base load.
-  arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
-
-  // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
-  // it's being used.
-  __ ldr(arm::R0, arm::Address(arm::R0));
-
-  // Emit the jump
-  __ EmitJumpTableDispatch(jump_table, arm::R1);
-
-  // Some more dummy instructions.
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ BindTrackedLabel(&label3);
-  constexpr size_t kLdrR0R0Count2 = 600;               // Note: even so there's no alignment
-  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {       //       necessary, as gcc as emits nops,
-    __ ldr(arm::R0, arm::Address(arm::R0));            //       whereas we emit 0 != nop.
-  }
-
-  static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 1 * KB, "Not enough offset");
-  static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 4 * KB, "Too much offset");
-
-  std::string expected =
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".L1:\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".L2:\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      "adr r1, .Ljump_table\n"
-      "ldr r0, [r0]\n"
-      ".Lbase:\n"
-      "add pc, r1\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".L3:\n" +
-      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
-      ".align 2\n"
-      ".Ljump_table:\n"
-      ".4byte (.L1 - .Lbase - 4)\n"
-      ".4byte (.L2 - .Lbase - 4)\n"
-      ".4byte (.L3 - .Lbase - 4)\n";
-  DriverStr(expected, "JumpTable4K");
-}
-
-// Test for >4K fixup.
-TEST_F(AssemblerThumb2Test, JumpTable64K) {
-  // The jump table. Use three labels.
-  Label label1, label2, label3;
-  std::vector<Label*> labels({ &label1, &label2, &label3 });
-
-  // A few dummy loads on entry, interspersed with 2 labels.
-  constexpr size_t kLdrR0R0Count = 5;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ BindTrackedLabel(&label1);
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ BindTrackedLabel(&label2);
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  // Create the jump table, emit the base load.
-  arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
-
-  // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
-  // it's being used.
-  __ ldr(arm::R0, arm::Address(arm::R0));
-
-  // Emit the jump
-  __ EmitJumpTableDispatch(jump_table, arm::R1);
-
-  // Some more dummy instructions.
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ BindTrackedLabel(&label3);
-  constexpr size_t kLdrR0R0Count2 = 2601;              // Note: odd so there's no alignment
-  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {       //       necessary, as gcc as emits nops,
-    __ ldr(arm::R0, arm::Address(arm::R0));            //       whereas we emit 0 != nop.
-  }
-
-  static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 4 * KB, "Not enough offset");
-  static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 64 * KB, "Too much offset");
-
-  std::string expected =
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".L1:\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".L2:\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself.
-      // (Note: have to use constants, as labels aren't accepted.
-      "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
-          ") * 2 - 4) & 0xFFFF)\n"
-      "add r1, pc\n"
-      "ldr r0, [r0]\n"
-      ".Lbase:\n"
-      "add pc, r1\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".L3:\n" +
-      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
-      ".align 2\n"
-      ".Ljump_table:\n"
-      ".4byte (.L1 - .Lbase - 4)\n"
-      ".4byte (.L2 - .Lbase - 4)\n"
-      ".4byte (.L3 - .Lbase - 4)\n";
-  DriverStr(expected, "JumpTable64K");
-}
-
-// Test for >64K fixup.
-TEST_F(AssemblerThumb2Test, JumpTableFar) {
-  // The jump table. Use three labels.
-  Label label1, label2, label3;
-  std::vector<Label*> labels({ &label1, &label2, &label3 });
-
-  // A few dummy loads on entry, interspersed with 2 labels.
-  constexpr size_t kLdrR0R0Count = 5;
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ BindTrackedLabel(&label1);
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ BindTrackedLabel(&label2);
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-
-  // Create the jump table, emit the base load.
-  arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
-
-  // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
-  // it's being used.
-  __ ldr(arm::R0, arm::Address(arm::R0));
-
-  // Emit the jump
-  __ EmitJumpTableDispatch(jump_table, arm::R1);
-
-  // Some more dummy instructions.
-  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
-    __ ldr(arm::R0, arm::Address(arm::R0));
-  }
-  __ BindTrackedLabel(&label3);
-  constexpr size_t kLdrR0R0Count2 = 70001;             // Note: odd so there's no alignment
-  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {       //       necessary, as gcc as emits nops,
-    __ ldr(arm::R0, arm::Address(arm::R0));            //       whereas we emit 0 != nop.
-  }
-
-  static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 64 * KB, "Not enough offset");
-
-  std::string expected =
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".L1:\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".L2:\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself.
-      // (Note: have to use constants, as labels aren't accepted.
-      "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
-          ") * 2 - 4) & 0xFFFF)\n"
-      "movt r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
-          ") * 2 - 4) >> 16)\n"
-      ".Lhelp:"
-      "add r1, pc\n"
-      "ldr r0, [r0]\n"
-      ".Lbase:\n"
-      "add pc, r1\n" +
-      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
-      ".L3:\n" +
-      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
-      ".align 2\n"
-      ".Ljump_table:\n"
-      ".4byte (.L1 - .Lbase - 4)\n"
-      ".4byte (.L2 - .Lbase - 4)\n"
-      ".4byte (.L3 - .Lbase - 4)\n";
-  DriverStr(expected, "JumpTableFar");
-}
-
-TEST_F(AssemblerThumb2Test, Clz) {
-  __ clz(arm::R0, arm::R1);
-
-  const char* expected = "clz r0, r1\n";
-
-  DriverStr(expected, "clz");
-}
-
-TEST_F(AssemblerThumb2Test, rbit) {
-  __ rbit(arm::R1, arm::R0);
-
-  const char* expected = "rbit r1, r0\n";
-
-  DriverStr(expected, "rbit");
-}
-
-TEST_F(AssemblerThumb2Test, rev) {
-  __ rev(arm::R1, arm::R0);
-
-  const char* expected = "rev r1, r0\n";
-
-  DriverStr(expected, "rev");
-}
-
-TEST_F(AssemblerThumb2Test, rev16) {
-  __ rev16(arm::R1, arm::R0);
-
-  const char* expected = "rev16 r1, r0\n";
-
-  DriverStr(expected, "rev16");
-}
-
-TEST_F(AssemblerThumb2Test, revsh) {
-  __ revsh(arm::R1, arm::R0);
-
-  const char* expected = "revsh r1, r0\n";
-
-  DriverStr(expected, "revsh");
-}
-
-TEST_F(AssemblerThumb2Test, vcnt) {
-  // Different D register numbers are used here, to test register encoding.
-  // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
-  // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
-  // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
-  __ vcntd(arm::D0, arm::D1);
-  __ vcntd(arm::D19, arm::D20);
-  __ vcntd(arm::D0, arm::D9);
-  __ vcntd(arm::D16, arm::D20);
-
-  std::string expected =
-      "vcnt.8 d0, d1\n"
-      "vcnt.8 d19, d20\n"
-      "vcnt.8 d0, d9\n"
-      "vcnt.8 d16, d20\n";
-
-  DriverStr(expected, "vcnt");
-}
-
-TEST_F(AssemblerThumb2Test, vpaddl) {
-  // Different D register numbers are used here, to test register encoding.
-  // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
-  // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
-  // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
-  // Different data types (signed and unsigned) are also tested.
-  __ vpaddld(arm::D0, arm::D0, 8, true);
-  __ vpaddld(arm::D20, arm::D20, 8, false);
-  __ vpaddld(arm::D0, arm::D20, 16, false);
-  __ vpaddld(arm::D20, arm::D0, 32, true);
-
-  std::string expected =
-      "vpaddl.u8 d0, d0\n"
-      "vpaddl.s8 d20, d20\n"
-      "vpaddl.s16 d0, d20\n"
-      "vpaddl.u32 d20, d0\n";
-
-  DriverStr(expected, "vpaddl");
-}
-
-TEST_F(AssemblerThumb2Test, LoadFromShiftedRegOffset) {
-  arm::Address mem_address(arm::R0, arm::R1, arm::Shift::LSL, 2);
-
-  __ ldrsb(arm::R2, mem_address);
-  __ ldrb(arm::R2, mem_address);
-  __ ldrsh(arm::R2, mem_address);
-  __ ldrh(arm::R2, mem_address);
-  __ ldr(arm::R2, mem_address);
-
-  std::string expected =
-      "ldrsb r2, [r0, r1, LSL #2]\n"
-      "ldrb r2, [r0, r1, LSL #2]\n"
-      "ldrsh r2, [r0, r1, LSL #2]\n"
-      "ldrh r2, [r0, r1, LSL #2]\n"
-      "ldr r2, [r0, r1, LSL #2]\n";
-
-  DriverStr(expected, "LoadFromShiftedRegOffset");
-}
-
-TEST_F(AssemblerThumb2Test, VStmLdmPushPop) {
-  // Different D register numbers are used here, to test register encoding.
-  // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
-  // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
-  // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
-  // Different data types (signed and unsigned) are also tested.
-  __ vstmiad(arm::R0, arm::D0, 4);
-  __ vldmiad(arm::R1, arm::D9, 5);
-  __ vpopd(arm::D0, 4);
-  __ vpushd(arm::D9, 5);
-  __ vpops(arm::S0, 4);
-  __ vpushs(arm::S9, 5);
-  __ vpushs(arm::S16, 5);
-  __ vpushd(arm::D0, 16);
-  __ vpushd(arm::D1, 15);
-  __ vpushd(arm::D8, 16);
-  __ vpushd(arm::D31, 1);
-  __ vpushs(arm::S0, 32);
-  __ vpushs(arm::S1, 31);
-  __ vpushs(arm::S16, 16);
-  __ vpushs(arm::S31, 1);
-
-  std::string expected =
-      "vstmia r0, {d0 - d3}\n"
-      "vldmia r1, {d9 - d13}\n"
-      "vpop {d0 - d3}\n"
-      "vpush {d9 - d13}\n"
-      "vpop {s0 - s3}\n"
-      "vpush {s9 - s13}\n"
-      "vpush {s16 - s20}\n"
-      "vpush {d0 - d15}\n"
-      "vpush {d1 - d15}\n"
-      "vpush {d8 - d23}\n"
-      "vpush {d31}\n"
-      "vpush {s0 - s31}\n"
-      "vpush {s1 - s31}\n"
-      "vpush {s16 - s31}\n"
-      "vpush {s31}\n";
-
-  DriverStr(expected, "VStmLdmPushPop");
-}
-
-}  // namespace art
diff --git a/compiler/utils/arm/constants_arm.cc b/compiler/utils/arm/constants_arm.cc
new file mode 100644
index 0000000..b02b343
--- /dev/null
+++ b/compiler/utils/arm/constants_arm.cc
@@ -0,0 +1,32 @@
+/*
+ * 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 "constants_arm.h"
+
+namespace art {
+namespace arm {
+
+std::ostream& operator<<(std::ostream& os, const DRegister& rhs) {
+  if (rhs >= D0 && rhs < kNumberOfDRegisters) {
+    os << "d" << static_cast<int>(rhs);
+  } else {
+    os << "DRegister[" << static_cast<int>(rhs) << "]";
+  }
+  return os;
+}
+
+}  // namespace arm
+}  // namespace art
diff --git a/compiler/utils/arm/constants_arm.h b/compiler/utils/arm/constants_arm.h
index 2060064..5b87e3e 100644
--- a/compiler/utils/arm/constants_arm.h
+++ b/compiler/utils/arm/constants_arm.h
@@ -97,37 +97,6 @@
 };
 std::ostream& operator<<(std::ostream& os, const DRegister& rhs);
 
-
-// Values for the condition field as defined in Table A8-1 "Condition
-// codes" (refer to Section A8.3 "Conditional execution").
-enum Condition {  // private marker to avoid generate-operator-out.py from processing.
-  kNoCondition = -1,
-  //           Meaning (integer)                      | Meaning (floating-point)
-  //           ---------------------------------------+-----------------------------------------
-  EQ = 0,   // Equal                                  | Equal
-  NE = 1,   // Not equal                              | Not equal, or unordered
-  CS = 2,   // Carry set                              | Greater than, equal, or unordered
-  CC = 3,   // Carry clear                            | Less than
-  MI = 4,   // Minus, negative                        | Less than
-  PL = 5,   // Plus, positive or zero                 | Greater than, equal, or unordered
-  VS = 6,   // Overflow                               | Unordered (i.e. at least one NaN operand)
-  VC = 7,   // No overflow                            | Not unordered
-  HI = 8,   // Unsigned higher                        | Greater than, or unordered
-  LS = 9,   // Unsigned lower or same                 | Less than or equal
-  GE = 10,  // Signed greater than or equal           | Greater than or equal
-  LT = 11,  // Signed less than                       | Less than, or unordered
-  GT = 12,  // Signed greater than                    | Greater than
-  LE = 13,  // Signed less than or equal              | Less than, equal, or unordered
-  AL = 14,  // Always (unconditional)                 | Always (unconditional)
-  kSpecialCondition = 15,  // Special condition (refer to Section A8.3 "Conditional execution").
-  kMaxCondition = 16,
-
-  HS = CS,  // HS (unsigned higher or same) is a synonym for CS.
-  LO = CC   // LO (unsigned lower) is a synonym for CC.
-};
-std::ostream& operator<<(std::ostream& os, const Condition& rhs);
-
-
 // Opcodes for Data-processing instructions (instructions with a type 0 and 1)
 // as defined in section A3.4
 enum Opcode {
@@ -151,70 +120,6 @@
   ORN = 16,  // Logical OR NOT.
   kMaxOperand = 17
 };
-std::ostream& operator<<(std::ostream& os, const Opcode& rhs);
-
-// Shifter types for Data-processing operands as defined in section A5.1.2.
-enum Shift {
-  kNoShift = -1,
-  LSL = 0,  // Logical shift left
-  LSR = 1,  // Logical shift right
-  ASR = 2,  // Arithmetic shift right
-  ROR = 3,  // Rotate right
-  RRX = 4,  // Rotate right with extend.
-  kMaxShift
-};
-std::ostream& operator<<(std::ostream& os, const Shift& rhs);
-
-// Constants used for the decoding or encoding of the individual fields of
-// instructions. Based on the "Figure 3-1 ARM instruction set summary".
-enum InstructionFields {  // private marker to avoid generate-operator-out.py from processing.
-  kConditionShift = 28,
-  kConditionBits = 4,
-  kTypeShift = 25,
-  kTypeBits = 3,
-  kLinkShift = 24,
-  kLinkBits = 1,
-  kUShift = 23,
-  kUBits = 1,
-  kOpcodeShift = 21,
-  kOpcodeBits = 4,
-  kSShift = 20,
-  kSBits = 1,
-  kRnShift = 16,
-  kRnBits = 4,
-  kRdShift = 12,
-  kRdBits = 4,
-  kRsShift = 8,
-  kRsBits = 4,
-  kRmShift = 0,
-  kRmBits = 4,
-
-  // Immediate instruction fields encoding.
-  kRotateShift = 8,
-  kRotateBits = 4,
-  kImmed8Shift = 0,
-  kImmed8Bits = 8,
-
-  // Shift instruction register fields encodings.
-  kShiftImmShift = 7,
-  kShiftRegisterShift = 8,
-  kShiftImmBits = 5,
-  kShiftShift = 5,
-  kShiftBits = 2,
-
-  // Load/store instruction offset field encoding.
-  kOffset12Shift = 0,
-  kOffset12Bits = 12,
-  kOffset12Mask = 0x00000fff,
-
-  // Mul instruction register fields encodings.
-  kMulRdShift = 16,
-  kMulRdBits = 4,
-  kMulRnShift = 12,
-  kMulRnBits = 4,
-
-  kBranchOffsetMask = 0x00ffffff
-};
 
 // Size (in bytes) of registers.
 const int kRegisterSize = 4;
@@ -222,231 +127,6 @@
 // List of registers used in load/store multiple.
 typedef uint16_t RegList;
 
-// The class Instr enables access to individual fields defined in the ARM
-// architecture instruction set encoding as described in figure A3-1.
-//
-// Example: Test whether the instruction at ptr does set the condition code
-// bits.
-//
-// bool InstructionSetsConditionCodes(uint8_t* ptr) {
-//   Instr* instr = Instr::At(ptr);
-//   int type = instr->TypeField();
-//   return ((type == 0) || (type == 1)) && instr->HasS();
-// }
-//
-class Instr {
- public:
-  enum {
-    kInstrSize = 4,
-    kInstrSizeLog2 = 2,
-    kPCReadOffset = 8
-  };
-
-  bool IsBreakPoint() {
-    return IsBkpt();
-  }
-
-  // Get the raw instruction bits.
-  int32_t InstructionBits() const {
-    return *reinterpret_cast<const int32_t*>(this);
-  }
-
-  // Set the raw instruction bits to value.
-  void SetInstructionBits(int32_t value) {
-    *reinterpret_cast<int32_t*>(this) = value;
-  }
-
-  // Read one particular bit out of the instruction bits.
-  int Bit(int nr) const {
-    return (InstructionBits() >> nr) & 1;
-  }
-
-  // Read a bit field out of the instruction bits.
-  int Bits(int shift, int count) const {
-    return (InstructionBits() >> shift) & ((1 << count) - 1);
-  }
-
-
-  // Accessors for the different named fields used in the ARM encoding.
-  // The naming of these accessor corresponds to figure A3-1.
-  // Generally applicable fields
-  Condition ConditionField() const {
-    return static_cast<Condition>(Bits(kConditionShift, kConditionBits));
-  }
-  int TypeField() const { return Bits(kTypeShift, kTypeBits); }
-
-  Register RnField() const { return static_cast<Register>(
-                                        Bits(kRnShift, kRnBits)); }
-  Register RdField() const { return static_cast<Register>(
-                                        Bits(kRdShift, kRdBits)); }
-
-  // Fields used in Data processing instructions
-  Opcode OpcodeField() const {
-    return static_cast<Opcode>(Bits(kOpcodeShift, kOpcodeBits));
-  }
-  int SField() const { return Bits(kSShift, kSBits); }
-  // with register
-  Register RmField() const {
-    return static_cast<Register>(Bits(kRmShift, kRmBits));
-  }
-  Shift ShiftField() const { return static_cast<Shift>(
-                                        Bits(kShiftShift, kShiftBits)); }
-  int RegShiftField() const { return Bit(4); }
-  Register RsField() const {
-    return static_cast<Register>(Bits(kRsShift, kRsBits));
-  }
-  int ShiftAmountField() const { return Bits(kShiftImmShift,
-                                                    kShiftImmBits); }
-  // with immediate
-  int RotateField() const { return Bits(kRotateShift, kRotateBits); }
-  int Immed8Field() const { return Bits(kImmed8Shift, kImmed8Bits); }
-
-  // Fields used in Load/Store instructions
-  int PUField() const { return Bits(23, 2); }
-  int  BField() const { return Bit(22); }
-  int  WField() const { return Bit(21); }
-  int  LField() const { return Bit(20); }
-  // with register uses same fields as Data processing instructions above
-  // with immediate
-  int Offset12Field() const { return Bits(kOffset12Shift,
-                                                 kOffset12Bits); }
-  // multiple
-  int RlistField() const { return Bits(0, 16); }
-  // extra loads and stores
-  int SignField() const { return Bit(6); }
-  int HField() const { return Bit(5); }
-  int ImmedHField() const { return Bits(8, 4); }
-  int ImmedLField() const { return Bits(0, 4); }
-
-  // Fields used in Branch instructions
-  int LinkField() const { return Bits(kLinkShift, kLinkBits); }
-  int SImmed24Field() const { return ((InstructionBits() << 8) >> 8); }
-
-  // Fields used in Supervisor Call instructions
-  uint32_t SvcField() const { return Bits(0, 24); }
-
-  // Field used in Breakpoint instruction
-  uint16_t BkptField() const {
-    return ((Bits(8, 12) << 4) | Bits(0, 4));
-  }
-
-  // Field used in 16-bit immediate move instructions
-  uint16_t MovwField() const {
-    return ((Bits(16, 4) << 12) | Bits(0, 12));
-  }
-
-  // Field used in VFP float immediate move instruction
-  float ImmFloatField() const {
-    uint32_t imm32 = (Bit(19) << 31) | (((1 << 5) - Bit(18)) << 25) |
-                     (Bits(16, 2) << 23) | (Bits(0, 4) << 19);
-    return bit_cast<float, uint32_t>(imm32);
-  }
-
-  // Field used in VFP double immediate move instruction
-  double ImmDoubleField() const {
-    uint64_t imm64 = (Bit(19)*(1LL << 63)) | (((1LL << 8) - Bit(18)) << 54) |
-                     (Bits(16, 2)*(1LL << 52)) | (Bits(0, 4)*(1LL << 48));
-    return bit_cast<double, uint64_t>(imm64);
-  }
-
-  // Test for data processing instructions of type 0 or 1.
-  // See "ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition",
-  // section A5.1 "ARM instruction set encoding".
-  bool IsDataProcessing() const {
-    CHECK_NE(ConditionField(), kSpecialCondition);
-    CHECK_EQ(Bits(26, 2), 0);  // Type 0 or 1.
-    return ((Bits(20, 5) & 0x19) != 0x10) &&
-      ((Bit(25) == 1) ||  // Data processing immediate.
-       (Bit(4) == 0) ||  // Data processing register.
-       (Bit(7) == 0));  // Data processing register-shifted register.
-  }
-
-  // Tests for special encodings of type 0 instructions (extra loads and stores,
-  // as well as multiplications, synchronization primitives, and miscellaneous).
-  // Can only be called for a type 0 or 1 instruction.
-  bool IsMiscellaneous() const {
-    CHECK_EQ(Bits(26, 2), 0);  // Type 0 or 1.
-    return ((Bit(25) == 0) && ((Bits(20, 5) & 0x19) == 0x10) && (Bit(7) == 0));
-  }
-  bool IsMultiplyOrSyncPrimitive() const {
-    CHECK_EQ(Bits(26, 2), 0);  // Type 0 or 1.
-    return ((Bit(25) == 0) && (Bits(4, 4) == 9));
-  }
-
-  // Test for Supervisor Call instruction.
-  bool IsSvc() const {
-    return ((InstructionBits() & 0xff000000) == 0xef000000);
-  }
-
-  // Test for Breakpoint instruction.
-  bool IsBkpt() const {
-    return ((InstructionBits() & 0xfff000f0) == 0xe1200070);
-  }
-
-  // VFP register fields.
-  SRegister SnField() const {
-    return static_cast<SRegister>((Bits(kRnShift, kRnBits) << 1) + Bit(7));
-  }
-  SRegister SdField() const {
-    return static_cast<SRegister>((Bits(kRdShift, kRdBits) << 1) + Bit(22));
-  }
-  SRegister SmField() const {
-    return static_cast<SRegister>((Bits(kRmShift, kRmBits) << 1) + Bit(5));
-  }
-  DRegister DnField() const {
-    return static_cast<DRegister>(Bits(kRnShift, kRnBits) + (Bit(7) << 4));
-  }
-  DRegister DdField() const {
-    return static_cast<DRegister>(Bits(kRdShift, kRdBits) + (Bit(22) << 4));
-  }
-  DRegister DmField() const {
-    return static_cast<DRegister>(Bits(kRmShift, kRmBits) + (Bit(5) << 4));
-  }
-
-  // Test for VFP data processing or single transfer instructions of type 7.
-  bool IsVFPDataProcessingOrSingleTransfer() const {
-    CHECK_NE(ConditionField(), kSpecialCondition);
-    CHECK_EQ(TypeField(), 7);
-    return ((Bit(24) == 0) && (Bits(9, 3) == 5));
-    // Bit(4) == 0: Data Processing
-    // Bit(4) == 1: 8, 16, or 32-bit Transfer between ARM Core and VFP
-  }
-
-  // Test for VFP 64-bit transfer instructions of type 6.
-  bool IsVFPDoubleTransfer() const {
-    CHECK_NE(ConditionField(), kSpecialCondition);
-    CHECK_EQ(TypeField(), 6);
-    return ((Bits(21, 4) == 2) && (Bits(9, 3) == 5) &&
-            ((Bits(4, 4) & 0xd) == 1));
-  }
-
-  // Test for VFP load and store instructions of type 6.
-  bool IsVFPLoadStore() const {
-    CHECK_NE(ConditionField(), kSpecialCondition);
-    CHECK_EQ(TypeField(), 6);
-    return ((Bits(20, 5) & 0x12) == 0x10) && (Bits(9, 3) == 5);
-  }
-
-  // Special accessors that test for existence of a value.
-  bool HasS() const { return SField() == 1; }
-  bool HasB() const { return BField() == 1; }
-  bool HasW() const { return WField() == 1; }
-  bool HasL() const { return LField() == 1; }
-  bool HasSign() const { return SignField() == 1; }
-  bool HasH() const { return HField() == 1; }
-  bool HasLink() const { return LinkField() == 1; }
-
-  // Instructions are read out of a code stream. The only way to get a
-  // reference to an instruction is to convert a pointer. There is no way
-  // to allocate or create instances of class Instr.
-  // Use the At(pc) function to create references to Instr.
-  static Instr* At(uintptr_t pc) { return reinterpret_cast<Instr*>(pc); }
-  Instr* Next() { return this + kInstrSize; }
-
- private:
-  // We need to prevent the creation of instances of class Instr.
-  DISALLOW_IMPLICIT_CONSTRUCTORS(Instr);
-};
 
 }  // namespace arm
 }  // namespace art
diff --git a/compiler/utils/arm/jni_macro_assembler_arm.cc b/compiler/utils/arm/jni_macro_assembler_arm.cc
deleted file mode 100644
index 3f425df..0000000
--- a/compiler/utils/arm/jni_macro_assembler_arm.cc
+++ /dev/null
@@ -1,659 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "jni_macro_assembler_arm.h"
-
-#include <algorithm>
-
-#include "assembler_thumb2.h"
-#include "base/arena_allocator.h"
-#include "base/bit_utils.h"
-#include "base/logging.h"
-#include "entrypoints/quick/quick_entrypoints.h"
-#include "offsets.h"
-#include "thread.h"
-
-namespace art {
-namespace arm {
-
-constexpr size_t kFramePointerSize = static_cast<size_t>(kArmPointerSize);
-
-// Slowpath entered when Thread::Current()->_exception is non-null
-class ArmExceptionSlowPath FINAL : public SlowPath {
- public:
-  ArmExceptionSlowPath(ArmManagedRegister scratch, size_t stack_adjust)
-      : scratch_(scratch), stack_adjust_(stack_adjust) {
-  }
-  void Emit(Assembler *sp_asm) OVERRIDE;
- private:
-  const ArmManagedRegister scratch_;
-  const size_t stack_adjust_;
-};
-
-ArmJNIMacroAssembler::ArmJNIMacroAssembler(ArenaAllocator* arena, InstructionSet isa) {
-  switch (isa) {
-    case kArm:
-    case kThumb2:
-      asm_.reset(new (arena) Thumb2Assembler(arena));
-      break;
-
-    default:
-      LOG(FATAL) << isa;
-      UNREACHABLE();
-  }
-}
-
-ArmJNIMacroAssembler::~ArmJNIMacroAssembler() {
-}
-
-size_t ArmJNIMacroAssembler::CodeSize() const {
-  return asm_->CodeSize();
-}
-
-DebugFrameOpCodeWriterForAssembler& ArmJNIMacroAssembler::cfi() {
-  return asm_->cfi();
-}
-
-void ArmJNIMacroAssembler::FinalizeCode() {
-  asm_->FinalizeCode();
-}
-
-void ArmJNIMacroAssembler::FinalizeInstructions(const MemoryRegion& region) {
-  asm_->FinalizeInstructions(region);
-}
-
-static dwarf::Reg DWARFReg(Register reg) {
-  return dwarf::Reg::ArmCore(static_cast<int>(reg));
-}
-
-static dwarf::Reg DWARFReg(SRegister reg) {
-  return dwarf::Reg::ArmFp(static_cast<int>(reg));
-}
-
-#define __ asm_->
-
-void ArmJNIMacroAssembler::BuildFrame(size_t frame_size,
-                                      ManagedRegister method_reg,
-                                      ArrayRef<const ManagedRegister> callee_save_regs,
-                                      const ManagedRegisterEntrySpills& entry_spills) {
-  CHECK_EQ(CodeSize(), 0U);  // Nothing emitted yet
-  CHECK_ALIGNED(frame_size, kStackAlignment);
-  CHECK_EQ(R0, method_reg.AsArm().AsCoreRegister());
-
-  // Push callee saves and link register.
-  RegList core_spill_mask = 1 << LR;
-  uint32_t fp_spill_mask = 0;
-  for (const ManagedRegister& reg : callee_save_regs) {
-    if (reg.AsArm().IsCoreRegister()) {
-      core_spill_mask |= 1 << reg.AsArm().AsCoreRegister();
-    } else {
-      fp_spill_mask |= 1 << reg.AsArm().AsSRegister();
-    }
-  }
-  __ PushList(core_spill_mask);
-  cfi().AdjustCFAOffset(POPCOUNT(core_spill_mask) * kFramePointerSize);
-  cfi().RelOffsetForMany(DWARFReg(Register(0)), 0, core_spill_mask, kFramePointerSize);
-  if (fp_spill_mask != 0) {
-    __ vpushs(SRegister(CTZ(fp_spill_mask)), POPCOUNT(fp_spill_mask));
-    cfi().AdjustCFAOffset(POPCOUNT(fp_spill_mask) * kFramePointerSize);
-    cfi().RelOffsetForMany(DWARFReg(SRegister(0)), 0, fp_spill_mask, kFramePointerSize);
-  }
-
-  // Increase frame to required size.
-  int pushed_values = POPCOUNT(core_spill_mask) + POPCOUNT(fp_spill_mask);
-  CHECK_GT(frame_size, pushed_values * kFramePointerSize);  // Must at least have space for Method*.
-  IncreaseFrameSize(frame_size - pushed_values * kFramePointerSize);  // handles CFI as well.
-
-  // Write out Method*.
-  __ StoreToOffset(kStoreWord, R0, SP, 0);
-
-  // Write out entry spills.
-  int32_t offset = frame_size + kFramePointerSize;
-  for (size_t i = 0; i < entry_spills.size(); ++i) {
-    ArmManagedRegister reg = entry_spills.at(i).AsArm();
-    if (reg.IsNoRegister()) {
-      // only increment stack offset.
-      ManagedRegisterSpill spill = entry_spills.at(i);
-      offset += spill.getSize();
-    } else if (reg.IsCoreRegister()) {
-      __ StoreToOffset(kStoreWord, reg.AsCoreRegister(), SP, offset);
-      offset += 4;
-    } else if (reg.IsSRegister()) {
-      __ StoreSToOffset(reg.AsSRegister(), SP, offset);
-      offset += 4;
-    } else if (reg.IsDRegister()) {
-      __ StoreDToOffset(reg.AsDRegister(), SP, offset);
-      offset += 8;
-    }
-  }
-}
-
-void ArmJNIMacroAssembler::RemoveFrame(size_t frame_size,
-                                       ArrayRef<const ManagedRegister> callee_save_regs) {
-  CHECK_ALIGNED(frame_size, kStackAlignment);
-  cfi().RememberState();
-
-  // Compute callee saves to pop and PC.
-  RegList core_spill_mask = 1 << PC;
-  uint32_t fp_spill_mask = 0;
-  for (const ManagedRegister& reg : callee_save_regs) {
-    if (reg.AsArm().IsCoreRegister()) {
-      core_spill_mask |= 1 << reg.AsArm().AsCoreRegister();
-    } else {
-      fp_spill_mask |= 1 << reg.AsArm().AsSRegister();
-    }
-  }
-
-  // Decrease frame to start of callee saves.
-  int pop_values = POPCOUNT(core_spill_mask) + POPCOUNT(fp_spill_mask);
-  CHECK_GT(frame_size, pop_values * kFramePointerSize);
-  DecreaseFrameSize(frame_size - (pop_values * kFramePointerSize));  // handles CFI as well.
-
-  if (fp_spill_mask != 0) {
-    __ vpops(SRegister(CTZ(fp_spill_mask)), POPCOUNT(fp_spill_mask));
-    cfi().AdjustCFAOffset(-kFramePointerSize * POPCOUNT(fp_spill_mask));
-    cfi().RestoreMany(DWARFReg(SRegister(0)), fp_spill_mask);
-  }
-
-  // Pop callee saves and PC.
-  __ PopList(core_spill_mask);
-
-  // The CFI should be restored for any code that follows the exit block.
-  cfi().RestoreState();
-  cfi().DefCFAOffset(frame_size);
-}
-
-void ArmJNIMacroAssembler::IncreaseFrameSize(size_t adjust) {
-  __ AddConstant(SP, -adjust);
-  cfi().AdjustCFAOffset(adjust);
-}
-
-static void DecreaseFrameSizeImpl(ArmAssembler* assembler, size_t adjust) {
-  assembler->AddConstant(SP, adjust);
-  assembler->cfi().AdjustCFAOffset(-adjust);
-}
-
-void ArmJNIMacroAssembler::DecreaseFrameSize(size_t adjust) {
-  DecreaseFrameSizeImpl(asm_.get(), adjust);
-}
-
-void ArmJNIMacroAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) {
-  ArmManagedRegister src = msrc.AsArm();
-  if (src.IsNoRegister()) {
-    CHECK_EQ(0u, size);
-  } else if (src.IsCoreRegister()) {
-    CHECK_EQ(4u, size);
-    __ StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
-  } else if (src.IsRegisterPair()) {
-    CHECK_EQ(8u, size);
-    __ StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value());
-    __ StoreToOffset(kStoreWord, src.AsRegisterPairHigh(), SP, dest.Int32Value() + 4);
-  } else if (src.IsSRegister()) {
-    __ StoreSToOffset(src.AsSRegister(), SP, dest.Int32Value());
-  } else {
-    CHECK(src.IsDRegister()) << src;
-    __ StoreDToOffset(src.AsDRegister(), SP, dest.Int32Value());
-  }
-}
-
-void ArmJNIMacroAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
-  ArmManagedRegister src = msrc.AsArm();
-  CHECK(src.IsCoreRegister()) << src;
-  __ StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
-}
-
-void ArmJNIMacroAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
-  ArmManagedRegister src = msrc.AsArm();
-  CHECK(src.IsCoreRegister()) << src;
-  __ StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
-}
-
-void ArmJNIMacroAssembler::StoreSpanning(FrameOffset dest,
-                                         ManagedRegister msrc,
-                                         FrameOffset in_off,
-                                         ManagedRegister mscratch) {
-  ArmManagedRegister src = msrc.AsArm();
-  ArmManagedRegister scratch = mscratch.AsArm();
-  __ StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
-  __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, in_off.Int32Value());
-  __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + sizeof(uint32_t));
-}
-
-void ArmJNIMacroAssembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) {
-  ArmManagedRegister scratch = mscratch.AsArm();
-  __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
-  __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
-}
-
-void ArmJNIMacroAssembler::LoadRef(ManagedRegister mdest,
-                                   ManagedRegister mbase,
-                                   MemberOffset offs,
-                                   bool unpoison_reference) {
-  ArmManagedRegister base = mbase.AsArm();
-  ArmManagedRegister dst = mdest.AsArm();
-  CHECK(base.IsCoreRegister()) << base;
-  CHECK(dst.IsCoreRegister()) << dst;
-  __ LoadFromOffset(kLoadWord,
-                    dst.AsCoreRegister(),
-                    base.AsCoreRegister(),
-                    offs.Int32Value());
-  if (unpoison_reference) {
-    __ MaybeUnpoisonHeapReference(dst.AsCoreRegister());
-  }
-}
-
-void ArmJNIMacroAssembler::LoadRef(ManagedRegister mdest, FrameOffset  src) {
-  ArmManagedRegister dst = mdest.AsArm();
-  CHECK(dst.IsCoreRegister()) << dst;
-  __ LoadFromOffset(kLoadWord, dst.AsCoreRegister(), SP, src.Int32Value());
-}
-
-void ArmJNIMacroAssembler::LoadRawPtr(ManagedRegister mdest,
-                                      ManagedRegister mbase,
-                                      Offset offs) {
-  ArmManagedRegister base = mbase.AsArm();
-  ArmManagedRegister dst = mdest.AsArm();
-  CHECK(base.IsCoreRegister()) << base;
-  CHECK(dst.IsCoreRegister()) << dst;
-  __ LoadFromOffset(kLoadWord,
-                    dst.AsCoreRegister(),
-                    base.AsCoreRegister(),
-                    offs.Int32Value());
-}
-
-void ArmJNIMacroAssembler::StoreImmediateToFrame(FrameOffset dest,
-                                                 uint32_t imm,
-                                                 ManagedRegister mscratch) {
-  ArmManagedRegister scratch = mscratch.AsArm();
-  CHECK(scratch.IsCoreRegister()) << scratch;
-  __ LoadImmediate(scratch.AsCoreRegister(), imm);
-  __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
-}
-
-static void EmitLoad(ArmAssembler* assembler,
-                     ManagedRegister m_dst,
-                     Register src_register,
-                     int32_t src_offset,
-                     size_t size) {
-  ArmManagedRegister dst = m_dst.AsArm();
-  if (dst.IsNoRegister()) {
-    CHECK_EQ(0u, size) << dst;
-  } else if (dst.IsCoreRegister()) {
-    CHECK_EQ(4u, size) << dst;
-    assembler->LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset);
-  } else if (dst.IsRegisterPair()) {
-    CHECK_EQ(8u, size) << dst;
-    assembler->LoadFromOffset(kLoadWord, dst.AsRegisterPairLow(), src_register, src_offset);
-    assembler->LoadFromOffset(kLoadWord, dst.AsRegisterPairHigh(), src_register, src_offset + 4);
-  } else if (dst.IsSRegister()) {
-    assembler->LoadSFromOffset(dst.AsSRegister(), src_register, src_offset);
-  } else {
-    CHECK(dst.IsDRegister()) << dst;
-    assembler->LoadDFromOffset(dst.AsDRegister(), src_register, src_offset);
-  }
-}
-
-void ArmJNIMacroAssembler::Load(ManagedRegister m_dst, FrameOffset src, size_t size) {
-  EmitLoad(asm_.get(), m_dst, SP, src.Int32Value(), size);
-}
-
-void ArmJNIMacroAssembler::LoadFromThread(ManagedRegister m_dst, ThreadOffset32 src, size_t size) {
-  EmitLoad(asm_.get(), m_dst, TR, src.Int32Value(), size);
-}
-
-void ArmJNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister m_dst, ThreadOffset32 offs) {
-  ArmManagedRegister dst = m_dst.AsArm();
-  CHECK(dst.IsCoreRegister()) << dst;
-  __ LoadFromOffset(kLoadWord, dst.AsCoreRegister(), TR, offs.Int32Value());
-}
-
-void ArmJNIMacroAssembler::CopyRawPtrFromThread(FrameOffset fr_offs,
-                                                ThreadOffset32 thr_offs,
-                                                ManagedRegister mscratch) {
-  ArmManagedRegister scratch = mscratch.AsArm();
-  CHECK(scratch.IsCoreRegister()) << scratch;
-  __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), TR, thr_offs.Int32Value());
-  __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
-}
-
-void ArmJNIMacroAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs,
-                                              FrameOffset fr_offs,
-                                              ManagedRegister mscratch) {
-  ArmManagedRegister scratch = mscratch.AsArm();
-  CHECK(scratch.IsCoreRegister()) << scratch;
-  __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
-  __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), TR, thr_offs.Int32Value());
-}
-
-void ArmJNIMacroAssembler::StoreStackOffsetToThread(ThreadOffset32 thr_offs,
-                                                    FrameOffset fr_offs,
-                                                    ManagedRegister mscratch) {
-  ArmManagedRegister scratch = mscratch.AsArm();
-  CHECK(scratch.IsCoreRegister()) << scratch;
-  __ AddConstant(scratch.AsCoreRegister(), SP, fr_offs.Int32Value(), AL);
-  __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), TR, thr_offs.Int32Value());
-}
-
-void ArmJNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
-  __ StoreToOffset(kStoreWord, SP, TR, thr_offs.Int32Value());
-}
-
-void ArmJNIMacroAssembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
-  UNIMPLEMENTED(FATAL) << "no sign extension necessary for arm";
-}
-
-void ArmJNIMacroAssembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
-  UNIMPLEMENTED(FATAL) << "no zero extension necessary for arm";
-}
-
-void ArmJNIMacroAssembler::Move(ManagedRegister m_dst, ManagedRegister m_src, size_t /*size*/) {
-  ArmManagedRegister dst = m_dst.AsArm();
-  ArmManagedRegister src = m_src.AsArm();
-  if (!dst.Equals(src)) {
-    if (dst.IsCoreRegister()) {
-      CHECK(src.IsCoreRegister()) << src;
-      __ mov(dst.AsCoreRegister(), ShifterOperand(src.AsCoreRegister()));
-    } else if (dst.IsDRegister()) {
-      if (src.IsDRegister()) {
-        __ vmovd(dst.AsDRegister(), src.AsDRegister());
-      } else {
-        // VMOV Dn, Rlo, Rhi (Dn = {Rlo, Rhi})
-        CHECK(src.IsRegisterPair()) << src;
-        __ vmovdrr(dst.AsDRegister(), src.AsRegisterPairLow(), src.AsRegisterPairHigh());
-      }
-    } else if (dst.IsSRegister()) {
-      if (src.IsSRegister()) {
-        __ vmovs(dst.AsSRegister(), src.AsSRegister());
-      } else {
-        // VMOV Sn, Rn  (Sn = Rn)
-        CHECK(src.IsCoreRegister()) << src;
-        __ vmovsr(dst.AsSRegister(), src.AsCoreRegister());
-      }
-    } else {
-      CHECK(dst.IsRegisterPair()) << dst;
-      CHECK(src.IsRegisterPair()) << src;
-      // Ensure that the first move doesn't clobber the input of the second.
-      if (src.AsRegisterPairHigh() != dst.AsRegisterPairLow()) {
-        __ mov(dst.AsRegisterPairLow(), ShifterOperand(src.AsRegisterPairLow()));
-        __ mov(dst.AsRegisterPairHigh(), ShifterOperand(src.AsRegisterPairHigh()));
-      } else {
-        __ mov(dst.AsRegisterPairHigh(), ShifterOperand(src.AsRegisterPairHigh()));
-        __ mov(dst.AsRegisterPairLow(), ShifterOperand(src.AsRegisterPairLow()));
-      }
-    }
-  }
-}
-
-void ArmJNIMacroAssembler::Copy(FrameOffset dest,
-                                FrameOffset src,
-                                ManagedRegister mscratch,
-                                size_t size) {
-  ArmManagedRegister scratch = mscratch.AsArm();
-  CHECK(scratch.IsCoreRegister()) << scratch;
-  CHECK(size == 4 || size == 8) << size;
-  if (size == 4) {
-    __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
-    __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
-  } else if (size == 8) {
-    __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
-    __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
-    __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + 4);
-    __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + 4);
-  }
-}
-
-void ArmJNIMacroAssembler::Copy(FrameOffset dest,
-                                ManagedRegister src_base,
-                                Offset src_offset,
-                                ManagedRegister mscratch,
-                                size_t size) {
-  Register scratch = mscratch.AsArm().AsCoreRegister();
-  CHECK_EQ(size, 4u);
-  __ LoadFromOffset(kLoadWord, scratch, src_base.AsArm().AsCoreRegister(), src_offset.Int32Value());
-  __ StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
-}
-
-void ArmJNIMacroAssembler::Copy(ManagedRegister dest_base,
-                                Offset dest_offset,
-                                FrameOffset src,
-                                ManagedRegister mscratch,
-                                size_t size) {
-  Register scratch = mscratch.AsArm().AsCoreRegister();
-  CHECK_EQ(size, 4u);
-  __ LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
-  __ StoreToOffset(kStoreWord,
-                   scratch,
-                   dest_base.AsArm().AsCoreRegister(),
-                   dest_offset.Int32Value());
-}
-
-void ArmJNIMacroAssembler::Copy(FrameOffset /*dst*/,
-                                FrameOffset /*src_base*/,
-                                Offset /*src_offset*/,
-                                ManagedRegister /*mscratch*/,
-                                size_t /*size*/) {
-  UNIMPLEMENTED(FATAL);
-}
-
-void ArmJNIMacroAssembler::Copy(ManagedRegister dest,
-                                Offset dest_offset,
-                                ManagedRegister src,
-                                Offset src_offset,
-                                ManagedRegister mscratch,
-                                size_t size) {
-  CHECK_EQ(size, 4u);
-  Register scratch = mscratch.AsArm().AsCoreRegister();
-  __ LoadFromOffset(kLoadWord, scratch, src.AsArm().AsCoreRegister(), src_offset.Int32Value());
-  __ StoreToOffset(kStoreWord, scratch, dest.AsArm().AsCoreRegister(), dest_offset.Int32Value());
-}
-
-void ArmJNIMacroAssembler::Copy(FrameOffset /*dst*/,
-                                Offset /*dest_offset*/,
-                                FrameOffset /*src*/,
-                                Offset /*src_offset*/,
-                                ManagedRegister /*scratch*/,
-                                size_t /*size*/) {
-  UNIMPLEMENTED(FATAL);
-}
-
-void ArmJNIMacroAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
-                                                  FrameOffset handle_scope_offset,
-                                                  ManagedRegister min_reg,
-                                                  bool null_allowed) {
-  ArmManagedRegister out_reg = mout_reg.AsArm();
-  ArmManagedRegister in_reg = min_reg.AsArm();
-  CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
-  CHECK(out_reg.IsCoreRegister()) << out_reg;
-  if (null_allowed) {
-    // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
-    // the address in the handle scope holding the reference.
-    // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset)
-    if (in_reg.IsNoRegister()) {
-      __ LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
-      in_reg = out_reg;
-    }
-    __ cmp(in_reg.AsCoreRegister(), ShifterOperand(0));
-    if (!out_reg.Equals(in_reg)) {
-      __ it(EQ, kItElse);
-      __ LoadImmediate(out_reg.AsCoreRegister(), 0, EQ);
-    } else {
-      __ it(NE);
-    }
-    __ AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), NE);
-  } else {
-    __ AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), AL);
-  }
-}
-
-void ArmJNIMacroAssembler::CreateHandleScopeEntry(FrameOffset out_off,
-                                                  FrameOffset handle_scope_offset,
-                                                  ManagedRegister mscratch,
-                                                  bool null_allowed) {
-  ArmManagedRegister scratch = mscratch.AsArm();
-  CHECK(scratch.IsCoreRegister()) << scratch;
-  if (null_allowed) {
-    __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
-    // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
-    // the address in the handle scope holding the reference.
-    // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset)
-    __ cmp(scratch.AsCoreRegister(), ShifterOperand(0));
-    __ it(NE);
-    __ AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), NE);
-  } else {
-    __ AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), AL);
-  }
-  __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value());
-}
-
-void ArmJNIMacroAssembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
-                                                        ManagedRegister min_reg) {
-  ArmManagedRegister out_reg = mout_reg.AsArm();
-  ArmManagedRegister in_reg = min_reg.AsArm();
-  CHECK(out_reg.IsCoreRegister()) << out_reg;
-  CHECK(in_reg.IsCoreRegister()) << in_reg;
-  Label null_arg;
-  if (!out_reg.Equals(in_reg)) {
-    __ LoadImmediate(out_reg.AsCoreRegister(), 0, EQ);     // TODO: why EQ?
-  }
-  __ cmp(in_reg.AsCoreRegister(), ShifterOperand(0));
-  __ it(NE);
-  __ LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(), in_reg.AsCoreRegister(), 0, NE);
-}
-
-void ArmJNIMacroAssembler::VerifyObject(ManagedRegister /*src*/, bool /*could_be_null*/) {
-  // TODO: not validating references.
-}
-
-void ArmJNIMacroAssembler::VerifyObject(FrameOffset /*src*/, bool /*could_be_null*/) {
-  // TODO: not validating references.
-}
-
-void ArmJNIMacroAssembler::Call(ManagedRegister mbase,
-                                Offset offset,
-                                ManagedRegister mscratch) {
-  ArmManagedRegister base = mbase.AsArm();
-  ArmManagedRegister scratch = mscratch.AsArm();
-  CHECK(base.IsCoreRegister()) << base;
-  CHECK(scratch.IsCoreRegister()) << scratch;
-  __ LoadFromOffset(kLoadWord,
-                    scratch.AsCoreRegister(),
-                    base.AsCoreRegister(),
-                    offset.Int32Value());
-  __ blx(scratch.AsCoreRegister());
-  // TODO: place reference map on call.
-}
-
-void ArmJNIMacroAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
-  ArmManagedRegister scratch = mscratch.AsArm();
-  CHECK(scratch.IsCoreRegister()) << scratch;
-  // Call *(*(SP + base) + offset)
-  __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, base.Int32Value());
-  __ LoadFromOffset(kLoadWord,
-                    scratch.AsCoreRegister(),
-                    scratch.AsCoreRegister(),
-                    offset.Int32Value());
-  __ blx(scratch.AsCoreRegister());
-  // TODO: place reference map on call
-}
-
-void ArmJNIMacroAssembler::CallFromThread(ThreadOffset32 offset ATTRIBUTE_UNUSED,
-                                          ManagedRegister scratch ATTRIBUTE_UNUSED) {
-  UNIMPLEMENTED(FATAL);
-}
-
-void ArmJNIMacroAssembler::GetCurrentThread(ManagedRegister tr) {
-  __ mov(tr.AsArm().AsCoreRegister(), ShifterOperand(TR));
-}
-
-void ArmJNIMacroAssembler::GetCurrentThread(FrameOffset offset, ManagedRegister /*scratch*/) {
-  __ StoreToOffset(kStoreWord, TR, SP, offset.Int32Value(), AL);
-}
-
-void ArmJNIMacroAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
-  ArmManagedRegister scratch = mscratch.AsArm();
-  ArmExceptionSlowPath* slow = new (__ GetArena()) ArmExceptionSlowPath(scratch, stack_adjust);
-  __ GetBuffer()->EnqueueSlowPath(slow);
-  __ LoadFromOffset(kLoadWord,
-                    scratch.AsCoreRegister(),
-                    TR,
-                    Thread::ExceptionOffset<kArmPointerSize>().Int32Value());
-  __ cmp(scratch.AsCoreRegister(), ShifterOperand(0));
-  __ b(slow->Entry(), NE);
-}
-
-std::unique_ptr<JNIMacroLabel> ArmJNIMacroAssembler::CreateLabel() {
-  return std::unique_ptr<JNIMacroLabel>(new ArmJNIMacroLabel());
-}
-
-void ArmJNIMacroAssembler::Jump(JNIMacroLabel* label) {
-  CHECK(label != nullptr);
-  __ b(ArmJNIMacroLabel::Cast(label)->AsArm());
-}
-
-void ArmJNIMacroAssembler::Jump(JNIMacroLabel* label,
-                                JNIMacroUnaryCondition condition,
-                                ManagedRegister test) {
-  CHECK(label != nullptr);
-
-  arm::Condition arm_cond;
-  switch (condition) {
-    case JNIMacroUnaryCondition::kZero:
-      arm_cond = EQ;
-      break;
-    case JNIMacroUnaryCondition::kNotZero:
-      arm_cond = NE;
-      break;
-    default:
-      LOG(FATAL) << "Not implemented condition: " << static_cast<int>(condition);
-      UNREACHABLE();
-  }
-  __ cmp(test.AsArm().AsCoreRegister(), ShifterOperand(0));
-  __ b(ArmJNIMacroLabel::Cast(label)->AsArm(), arm_cond);
-}
-
-void ArmJNIMacroAssembler::Bind(JNIMacroLabel* label) {
-  CHECK(label != nullptr);
-  __ Bind(ArmJNIMacroLabel::Cast(label)->AsArm());
-}
-
-#undef __
-
-void ArmExceptionSlowPath::Emit(Assembler* sasm) {
-  ArmAssembler* sp_asm = down_cast<ArmAssembler*>(sasm);
-#define __ sp_asm->
-  __ Bind(&entry_);
-  if (stack_adjust_ != 0) {  // Fix up the frame.
-    DecreaseFrameSizeImpl(sp_asm, stack_adjust_);
-  }
-  // Pass exception object as argument.
-  // Don't care about preserving R0 as this call won't return.
-  __ mov(R0, ShifterOperand(scratch_.AsCoreRegister()));
-  // Set up call to Thread::Current()->pDeliverException.
-  __ LoadFromOffset(kLoadWord,
-                    R12,
-                    TR,
-                    QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, pDeliverException).Int32Value());
-  __ blx(R12);
-#undef __
-}
-
-void ArmJNIMacroAssembler::MemoryBarrier(ManagedRegister mscratch) {
-  CHECK_EQ(mscratch.AsArm().AsCoreRegister(), R12);
-  asm_->dmb(SY);
-}
-
-}  // namespace arm
-}  // namespace art
diff --git a/compiler/utils/arm/jni_macro_assembler_arm.h b/compiler/utils/arm/jni_macro_assembler_arm.h
deleted file mode 100644
index 809ac8b..0000000
--- a/compiler/utils/arm/jni_macro_assembler_arm.h
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_UTILS_ARM_JNI_MACRO_ASSEMBLER_ARM_H_
-#define ART_COMPILER_UTILS_ARM_JNI_MACRO_ASSEMBLER_ARM_H_
-
-#include <memory>
-#include <type_traits>
-#include <vector>
-
-#include "arch/instruction_set.h"
-#include "base/enums.h"
-#include "base/macros.h"
-#include "utils/jni_macro_assembler.h"
-#include "utils/label.h"
-#include "offsets.h"
-
-namespace art {
-namespace arm {
-
-class ArmAssembler;
-
-class ArmJNIMacroAssembler : public JNIMacroAssembler<PointerSize::k32> {
- public:
-  ArmJNIMacroAssembler(ArenaAllocator* arena, InstructionSet isa);
-  virtual ~ArmJNIMacroAssembler();
-
-  size_t CodeSize() const OVERRIDE;
-  DebugFrameOpCodeWriterForAssembler& cfi() OVERRIDE;
-  void FinalizeCode() OVERRIDE;
-  void FinalizeInstructions(const MemoryRegion& region) OVERRIDE;
-
-  //
-  // Overridden common assembler high-level functionality
-  //
-
-  // Emit code that will create an activation on the stack
-  void BuildFrame(size_t frame_size,
-                  ManagedRegister method_reg,
-                  ArrayRef<const ManagedRegister> callee_save_regs,
-                  const ManagedRegisterEntrySpills& entry_spills) OVERRIDE;
-
-  // Emit code that will remove an activation from the stack
-  void RemoveFrame(size_t frame_size, ArrayRef<const ManagedRegister> callee_save_regs)
-    OVERRIDE;
-
-  void IncreaseFrameSize(size_t adjust) OVERRIDE;
-  void DecreaseFrameSize(size_t adjust) OVERRIDE;
-
-  // Store routines
-  void Store(FrameOffset offs, ManagedRegister src, size_t size) OVERRIDE;
-  void StoreRef(FrameOffset dest, ManagedRegister src) OVERRIDE;
-  void StoreRawPtr(FrameOffset dest, ManagedRegister src) OVERRIDE;
-
-  void StoreImmediateToFrame(FrameOffset dest, uint32_t imm, ManagedRegister scratch) OVERRIDE;
-
-  void StoreStackOffsetToThread(ThreadOffset32 thr_offs,
-                                FrameOffset fr_offs,
-                                ManagedRegister scratch) OVERRIDE;
-
-  void StoreStackPointerToThread(ThreadOffset32 thr_offs) OVERRIDE;
-
-  void StoreSpanning(FrameOffset dest, ManagedRegister src, FrameOffset in_off,
-                     ManagedRegister scratch) OVERRIDE;
-
-  // Load routines
-  void Load(ManagedRegister dest, FrameOffset src, size_t size) OVERRIDE;
-
-  void LoadFromThread(ManagedRegister dest, ThreadOffset32 src, size_t size) OVERRIDE;
-
-  void LoadRef(ManagedRegister dest, FrameOffset src) OVERRIDE;
-
-  void LoadRef(ManagedRegister dest, ManagedRegister base, MemberOffset offs,
-               bool unpoison_reference) OVERRIDE;
-
-  void LoadRawPtr(ManagedRegister dest, ManagedRegister base, Offset offs) OVERRIDE;
-
-  void LoadRawPtrFromThread(ManagedRegister dest, ThreadOffset32 offs) OVERRIDE;
-
-  // Copying routines
-  void Move(ManagedRegister dest, ManagedRegister src, size_t size) OVERRIDE;
-
-  void CopyRawPtrFromThread(FrameOffset fr_offs,
-                            ThreadOffset32 thr_offs,
-                            ManagedRegister scratch) OVERRIDE;
-
-  void CopyRawPtrToThread(ThreadOffset32 thr_offs, FrameOffset fr_offs, ManagedRegister scratch)
-      OVERRIDE;
-
-  void CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister scratch) OVERRIDE;
-
-  void Copy(FrameOffset dest, FrameOffset src, ManagedRegister scratch, size_t size) OVERRIDE;
-
-  void Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset, ManagedRegister scratch,
-            size_t size) OVERRIDE;
-
-  void Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src, ManagedRegister scratch,
-            size_t size) OVERRIDE;
-
-  void Copy(FrameOffset dest, FrameOffset src_base, Offset src_offset, ManagedRegister scratch,
-            size_t size) OVERRIDE;
-
-  void Copy(ManagedRegister dest, Offset dest_offset, ManagedRegister src, Offset src_offset,
-            ManagedRegister scratch, size_t size) OVERRIDE;
-
-  void Copy(FrameOffset dest, Offset dest_offset, FrameOffset src, Offset src_offset,
-            ManagedRegister scratch, size_t size) OVERRIDE;
-
-  // Sign extension
-  void SignExtend(ManagedRegister mreg, size_t size) OVERRIDE;
-
-  // Zero extension
-  void ZeroExtend(ManagedRegister mreg, size_t size) OVERRIDE;
-
-  // Exploit fast access in managed code to Thread::Current()
-  void GetCurrentThread(ManagedRegister tr) OVERRIDE;
-  void GetCurrentThread(FrameOffset dest_offset, ManagedRegister scratch) OVERRIDE;
-
-  // Set up out_reg to hold a Object** into the handle scope, or to be null if the
-  // value is null and null_allowed. in_reg holds a possibly stale reference
-  // that can be used to avoid loading the handle scope entry to see if the value is
-  // null.
-  void CreateHandleScopeEntry(ManagedRegister out_reg, FrameOffset handlescope_offset,
-                              ManagedRegister in_reg, bool null_allowed) OVERRIDE;
-
-  // Set up out_off to hold a Object** into the handle scope, or to be null if the
-  // value is null and null_allowed.
-  void CreateHandleScopeEntry(FrameOffset out_off, FrameOffset handlescope_offset,
-                              ManagedRegister scratch, bool null_allowed) OVERRIDE;
-
-  // src holds a handle scope entry (Object**) load this into dst
-  void LoadReferenceFromHandleScope(ManagedRegister dst, ManagedRegister src) OVERRIDE;
-
-  // Heap::VerifyObject on src. In some cases (such as a reference to this) we
-  // know that src may not be null.
-  void VerifyObject(ManagedRegister src, bool could_be_null) OVERRIDE;
-  void VerifyObject(FrameOffset src, bool could_be_null) OVERRIDE;
-
-  // Call to address held at [base+offset]
-  void Call(ManagedRegister base, Offset offset, ManagedRegister scratch) OVERRIDE;
-  void Call(FrameOffset base, Offset offset, ManagedRegister scratch) OVERRIDE;
-  void CallFromThread(ThreadOffset32 offset, ManagedRegister scratch) OVERRIDE;
-
-  // Generate code to check if Thread::Current()->exception_ is non-null
-  // and branch to a ExceptionSlowPath if it is.
-  void ExceptionPoll(ManagedRegister scratch, size_t stack_adjust) OVERRIDE;
-
-  void MemoryBarrier(ManagedRegister scratch) OVERRIDE;
-
-  // Create a new label that can be used with Jump/Bind calls.
-  std::unique_ptr<JNIMacroLabel> CreateLabel() OVERRIDE;
-  // Emit an unconditional jump to the label.
-  void Jump(JNIMacroLabel* label) OVERRIDE;
-  // Emit a conditional jump to the label by applying a unary condition test to the register.
-  void Jump(JNIMacroLabel* label, JNIMacroUnaryCondition cond, ManagedRegister test) OVERRIDE;
-  // Code at this offset will serve as the target for the Jump call.
-  void Bind(JNIMacroLabel* label) OVERRIDE;
-
- private:
-  std::unique_ptr<ArmAssembler> asm_;
-};
-
-class ArmJNIMacroLabel FINAL : public JNIMacroLabelCommon<ArmJNIMacroLabel, art::Label, kArm> {
- public:
-  art::Label* AsArm() {
-    return AsPlatformLabel();
-  }
-};
-
-}  // namespace arm
-}  // namespace art
-
-#endif  // ART_COMPILER_UTILS_ARM_JNI_MACRO_ASSEMBLER_ARM_H_
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index d07c047..bebe64c 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -120,8 +120,8 @@
   CHECK_ALIGNED(frame_size, kStackAlignment);
   cfi().RememberState();
 
-  // Compute callee saves to pop and PC.
-  RegList core_spill_mask = 1 << PC;
+  // Compute callee saves to pop and LR.
+  RegList core_spill_mask = 1 << LR;
   uint32_t fp_spill_mask = 0;
   for (const ManagedRegister& reg : callee_save_regs) {
     if (reg.AsArm().IsCoreRegister()) {
@@ -136,6 +136,7 @@
   CHECK_GT(frame_size, pop_values * kFramePointerSize);
   DecreaseFrameSize(frame_size - (pop_values * kFramePointerSize));  // handles CFI as well.
 
+  // Pop FP callee saves.
   if (fp_spill_mask != 0) {
     uint32_t first = CTZ(fp_spill_mask);
     // Check that list is contiguous.
@@ -146,9 +147,18 @@
     cfi().RestoreMany(DWARFReg(s0), fp_spill_mask);
   }
 
-  // Pop callee saves and PC.
+  // Pop core callee saves and LR.
   ___ Pop(RegisterList(core_spill_mask));
 
+  if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+    // Refresh Mark Register.
+    // TODO: Refresh MR only if suspend is taken.
+    ___ Ldr(mr, MemOperand(tr, Thread::IsGcMarkingOffset<kArmPointerSize>().Int32Value()));
+  }
+
+  // Return to LR.
+  ___ Bx(vixl32::lr);
+
   // The CFI should be restored for any code that follows the exit block.
   cfi().RestoreState();
   cfi().DefCFAOffset(frame_size);
diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.cc b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
index 9cd6884..bab84be 100644
--- a/compiler/utils/arm64/jni_macro_assembler_arm64.cc
+++ b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
@@ -772,10 +772,17 @@
   asm_.UnspillRegisters(core_reg_list, frame_size - core_reg_size);
   asm_.UnspillRegisters(fp_reg_list, frame_size - core_reg_size - fp_reg_size);
 
+  if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+    // Refresh Mark Register.
+    // TODO: Refresh MR only if suspend is taken.
+    ___ Ldr(reg_w(MR),
+            MemOperand(reg_x(TR), Thread::IsGcMarkingOffset<kArm64PointerSize>().Int32Value()));
+  }
+
   // Decrease frame size to start of callee saved regs.
   DecreaseFrameSize(frame_size);
 
-  // Pop callee saved and return to LR.
+  // Return to LR.
   ___ Ret();
 
   // The CFI should be restored for any code that follows the exit block.
diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc
index 57f3b15..25eca23 100644
--- a/compiler/utils/assembler.cc
+++ b/compiler/utils/assembler.cc
@@ -19,24 +19,6 @@
 #include <algorithm>
 #include <vector>
 
-#ifdef ART_ENABLE_CODEGEN_arm
-#include "arm/assembler_thumb2.h"
-#endif
-#ifdef ART_ENABLE_CODEGEN_arm64
-#include "arm64/assembler_arm64.h"
-#endif
-#ifdef ART_ENABLE_CODEGEN_mips
-#include "mips/assembler_mips.h"
-#endif
-#ifdef ART_ENABLE_CODEGEN_mips64
-#include "mips64/assembler_mips64.h"
-#endif
-#ifdef ART_ENABLE_CODEGEN_x86
-#include "x86/assembler_x86.h"
-#endif
-#ifdef ART_ENABLE_CODEGEN_x86_64
-#include "x86_64/assembler_x86_64.h"
-#endif
 #include "base/casts.h"
 #include "globals.h"
 #include "memory_region.h"
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 4e9b619..741beab 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -22,7 +22,6 @@
 #include <sys/types.h>
 
 #include "gtest/gtest.h"
-#include "utils/arm/assembler_thumb2.h"
 
 #include "jni/quick/calling_convention.h"
 #include "utils/arm/jni_macro_assembler_arm_vixl.h"
@@ -176,1451 +175,18 @@
 #endif  // ART_TARGET_ANDROID
 }
 
-#define __ assembler->
-
-void EmitAndCheck(arm::Thumb2Assembler* assembler, const char* testname,
-                  const char* const* results) {
-  __ FinalizeCode();
-  size_t cs = __ CodeSize();
-  std::vector<uint8_t> managed_code(cs);
-  MemoryRegion code(&managed_code[0], managed_code.size());
-  __ FinalizeInstructions(code);
-
-  DumpAndCheck(managed_code, testname, results);
-}
-
-void EmitAndCheck(arm::Thumb2Assembler* assembler, const char* testname) {
-  InitResults();
-  std::map<std::string, const char* const*>::iterator results = test_results.find(testname);
-  ASSERT_NE(results, test_results.end());
-
-  EmitAndCheck(assembler, testname, results->second);
-}
-
-#undef __
-
-class Thumb2AssemblerTest : public ::testing::Test {
- public:
-  Thumb2AssemblerTest() : pool(), arena(&pool), assembler(&arena) { }
-
-  ArenaPool pool;
-  ArenaAllocator arena;
-  arm::Thumb2Assembler assembler;
-};
-
-#define __ assembler.
-
-TEST_F(Thumb2AssemblerTest, SimpleMov) {
-  __ movs(R0, ShifterOperand(R1));
-  __ mov(R0, ShifterOperand(R1));
-  __ mov(R8, ShifterOperand(R9));
-
-  __ mov(R0, ShifterOperand(1));
-  __ mov(R8, ShifterOperand(9));
-
-  EmitAndCheck(&assembler, "SimpleMov");
-}
-
-TEST_F(Thumb2AssemblerTest, SimpleMov32) {
-  __ Force32Bit();
-
-  __ mov(R0, ShifterOperand(R1));
-  __ mov(R8, ShifterOperand(R9));
-
-  EmitAndCheck(&assembler, "SimpleMov32");
-}
-
-TEST_F(Thumb2AssemblerTest, SimpleMovAdd) {
-  __ mov(R0, ShifterOperand(R1));
-  __ adds(R0, R1, ShifterOperand(R2));
-  __ add(R0, R1, ShifterOperand(0));
-
-  EmitAndCheck(&assembler, "SimpleMovAdd");
-}
-
-TEST_F(Thumb2AssemblerTest, DataProcessingRegister) {
-  // 32 bit variants using low registers.
-  __ mvn(R0, ShifterOperand(R1), AL, kCcKeep);
-  __ add(R0, R1, ShifterOperand(R2), AL, kCcKeep);
-  __ sub(R0, R1, ShifterOperand(R2), AL, kCcKeep);
-  __ and_(R0, R1, ShifterOperand(R2), AL, kCcKeep);
-  __ orr(R0, R1, ShifterOperand(R2), AL, kCcKeep);
-  __ orn(R0, R1, ShifterOperand(R2), AL, kCcKeep);
-  __ eor(R0, R1, ShifterOperand(R2), AL, kCcKeep);
-  __ bic(R0, R1, ShifterOperand(R2), AL, kCcKeep);
-  __ adc(R0, R1, ShifterOperand(R2), AL, kCcKeep);
-  __ sbc(R0, R1, ShifterOperand(R2), AL, kCcKeep);
-  __ rsb(R0, R1, ShifterOperand(R2), AL, kCcKeep);
-  __ teq(R0, ShifterOperand(R1));
-
-  // 16 bit variants using low registers.
-  __ movs(R0, ShifterOperand(R1));
-  __ mov(R0, ShifterOperand(R1), AL, kCcKeep);
-  __ mvns(R0, ShifterOperand(R1));
-  __ add(R0, R0, ShifterOperand(R1), AL, kCcKeep);
-  __ adds(R0, R1, ShifterOperand(R2));
-  __ subs(R0, R1, ShifterOperand(R2));
-  __ adcs(R0, R0, ShifterOperand(R1));
-  __ sbcs(R0, R0, ShifterOperand(R1));
-  __ ands(R0, R0, ShifterOperand(R1));
-  __ orrs(R0, R0, ShifterOperand(R1));
-  __ eors(R0, R0, ShifterOperand(R1));
-  __ bics(R0, R0, ShifterOperand(R1));
-  __ tst(R0, ShifterOperand(R1));
-  __ cmp(R0, ShifterOperand(R1));
-  __ cmn(R0, ShifterOperand(R1));
-
-  // 16-bit variants using high registers.
-  __ mov(R1, ShifterOperand(R8), AL, kCcKeep);
-  __ mov(R9, ShifterOperand(R0), AL, kCcKeep);
-  __ mov(R8, ShifterOperand(R9), AL, kCcKeep);
-  __ add(R1, R1, ShifterOperand(R8), AL, kCcKeep);
-  __ add(R9, R9, ShifterOperand(R0), AL, kCcKeep);
-  __ add(R8, R8, ShifterOperand(R9), AL, kCcKeep);
-  __ cmp(R0, ShifterOperand(R9));
-  __ cmp(R8, ShifterOperand(R1));
-  __ cmp(R9, ShifterOperand(R8));
-
-  // The 16-bit RSBS Rd, Rn, #0, also known as NEGS Rd, Rn is specified using
-  // an immediate (0) but emitted without any, so we test it here.
-  __ rsbs(R0, R1, ShifterOperand(0));
-  __ rsbs(R0, R0, ShifterOperand(0));  // Check Rd == Rn code path.
-
-  // 32 bit variants using high registers that would be 16-bit if using low registers.
-  __ movs(R0, ShifterOperand(R8));
-  __ mvns(R0, ShifterOperand(R8));
-  __ add(R0, R1, ShifterOperand(R8), AL, kCcKeep);
-  __ adds(R0, R1, ShifterOperand(R8));
-  __ subs(R0, R1, ShifterOperand(R8));
-  __ adcs(R0, R0, ShifterOperand(R8));
-  __ sbcs(R0, R0, ShifterOperand(R8));
-  __ ands(R0, R0, ShifterOperand(R8));
-  __ orrs(R0, R0, ShifterOperand(R8));
-  __ eors(R0, R0, ShifterOperand(R8));
-  __ bics(R0, R0, ShifterOperand(R8));
-  __ tst(R0, ShifterOperand(R8));
-  __ cmn(R0, ShifterOperand(R8));
-  __ rsbs(R0, R8, ShifterOperand(0));  // Check that this is not emitted as 16-bit.
-  __ rsbs(R8, R8, ShifterOperand(0));  // Check that this is not emitted as 16-bit (Rd == Rn).
-
-  // 32-bit variants of instructions that would be 16-bit outside IT block.
-  __ it(arm::EQ);
-  __ mvns(R0, ShifterOperand(R1), arm::EQ);
-  __ it(arm::EQ);
-  __ adds(R0, R1, ShifterOperand(R2), arm::EQ);
-  __ it(arm::EQ);
-  __ subs(R0, R1, ShifterOperand(R2), arm::EQ);
-  __ it(arm::EQ);
-  __ adcs(R0, R0, ShifterOperand(R1), arm::EQ);
-  __ it(arm::EQ);
-  __ sbcs(R0, R0, ShifterOperand(R1), arm::EQ);
-  __ it(arm::EQ);
-  __ ands(R0, R0, ShifterOperand(R1), arm::EQ);
-  __ it(arm::EQ);
-  __ orrs(R0, R0, ShifterOperand(R1), arm::EQ);
-  __ it(arm::EQ);
-  __ eors(R0, R0, ShifterOperand(R1), arm::EQ);
-  __ it(arm::EQ);
-  __ bics(R0, R0, ShifterOperand(R1), arm::EQ);
-
-  // 16-bit variants of instructions that would be 32-bit outside IT block.
-  __ it(arm::EQ);
-  __ mvn(R0, ShifterOperand(R1), arm::EQ, kCcKeep);
-  __ it(arm::EQ);
-  __ add(R0, R1, ShifterOperand(R2), arm::EQ, kCcKeep);
-  __ it(arm::EQ);
-  __ sub(R0, R1, ShifterOperand(R2), arm::EQ, kCcKeep);
-  __ it(arm::EQ);
-  __ adc(R0, R0, ShifterOperand(R1), arm::EQ, kCcKeep);
-  __ it(arm::EQ);
-  __ sbc(R0, R0, ShifterOperand(R1), arm::EQ, kCcKeep);
-  __ it(arm::EQ);
-  __ and_(R0, R0, ShifterOperand(R1), arm::EQ, kCcKeep);
-  __ it(arm::EQ);
-  __ orr(R0, R0, ShifterOperand(R1), arm::EQ, kCcKeep);
-  __ it(arm::EQ);
-  __ eor(R0, R0, ShifterOperand(R1), arm::EQ, kCcKeep);
-  __ it(arm::EQ);
-  __ bic(R0, R0, ShifterOperand(R1), arm::EQ, kCcKeep);
-
-  // 16 bit variants selected for the default kCcDontCare.
-  __ mov(R0, ShifterOperand(R1));
-  __ mvn(R0, ShifterOperand(R1));
-  __ add(R0, R0, ShifterOperand(R1));
-  __ add(R0, R1, ShifterOperand(R2));
-  __ sub(R0, R1, ShifterOperand(R2));
-  __ adc(R0, R0, ShifterOperand(R1));
-  __ sbc(R0, R0, ShifterOperand(R1));
-  __ and_(R0, R0, ShifterOperand(R1));
-  __ orr(R0, R0, ShifterOperand(R1));
-  __ eor(R0, R0, ShifterOperand(R1));
-  __ bic(R0, R0, ShifterOperand(R1));
-  __ mov(R1, ShifterOperand(R8));
-  __ mov(R9, ShifterOperand(R0));
-  __ mov(R8, ShifterOperand(R9));
-  __ add(R1, R1, ShifterOperand(R8));
-  __ add(R9, R9, ShifterOperand(R0));
-  __ add(R8, R8, ShifterOperand(R9));
-  __ rsb(R0, R1, ShifterOperand(0));
-  __ rsb(R0, R0, ShifterOperand(0));
-
-  // And an arbitrary 32-bit instruction using IP.
-  __ add(R12, R1, ShifterOperand(R0), AL, kCcKeep);
-
-  EmitAndCheck(&assembler, "DataProcessingRegister");
-}
-
-TEST_F(Thumb2AssemblerTest, DataProcessingImmediate) {
-  __ mov(R0, ShifterOperand(0x55));
-  __ mvn(R0, ShifterOperand(0x55));
-  __ add(R0, R1, ShifterOperand(0x55));
-  __ sub(R0, R1, ShifterOperand(0x55));
-  __ and_(R0, R1, ShifterOperand(0x55));
-  __ orr(R0, R1, ShifterOperand(0x55));
-  __ orn(R0, R1, ShifterOperand(0x55));
-  __ eor(R0, R1, ShifterOperand(0x55));
-  __ bic(R0, R1, ShifterOperand(0x55));
-  __ adc(R0, R1, ShifterOperand(0x55));
-  __ sbc(R0, R1, ShifterOperand(0x55));
-  __ rsb(R0, R1, ShifterOperand(0x55));
-
-  __ tst(R0, ShifterOperand(0x55));
-  __ teq(R0, ShifterOperand(0x55));
-  __ cmp(R0, ShifterOperand(0x55));
-  __ cmn(R0, ShifterOperand(0x55));
-
-  __ add(R0, R1, ShifterOperand(5));
-  __ sub(R0, R1, ShifterOperand(5));
-
-  __ movs(R0, ShifterOperand(0x55));
-  __ mvns(R0, ShifterOperand(0x55));
-
-  __ adds(R0, R1, ShifterOperand(5));
-  __ subs(R0, R1, ShifterOperand(5));
-
-  EmitAndCheck(&assembler, "DataProcessingImmediate");
-}
-
-TEST_F(Thumb2AssemblerTest, DataProcessingModifiedImmediate) {
-  __ mov(R0, ShifterOperand(0x550055));
-  __ mvn(R0, ShifterOperand(0x550055));
-  __ add(R0, R1, ShifterOperand(0x550055));
-  __ sub(R0, R1, ShifterOperand(0x550055));
-  __ and_(R0, R1, ShifterOperand(0x550055));
-  __ orr(R0, R1, ShifterOperand(0x550055));
-  __ orn(R0, R1, ShifterOperand(0x550055));
-  __ eor(R0, R1, ShifterOperand(0x550055));
-  __ bic(R0, R1, ShifterOperand(0x550055));
-  __ adc(R0, R1, ShifterOperand(0x550055));
-  __ sbc(R0, R1, ShifterOperand(0x550055));
-  __ rsb(R0, R1, ShifterOperand(0x550055));
-
-  __ tst(R0, ShifterOperand(0x550055));
-  __ teq(R0, ShifterOperand(0x550055));
-  __ cmp(R0, ShifterOperand(0x550055));
-  __ cmn(R0, ShifterOperand(0x550055));
-
-  EmitAndCheck(&assembler, "DataProcessingModifiedImmediate");
-}
-
-
-TEST_F(Thumb2AssemblerTest, DataProcessingModifiedImmediates) {
-  __ mov(R0, ShifterOperand(0x550055));
-  __ mov(R0, ShifterOperand(0x55005500));
-  __ mov(R0, ShifterOperand(0x55555555));
-  __ mov(R0, ShifterOperand(0xd5000000));       // rotated to first position
-  __ mov(R0, ShifterOperand(0x6a000000));       // rotated to second position
-  __ mov(R0, ShifterOperand(0x350));            // rotated to 2nd last position
-  __ mov(R0, ShifterOperand(0x1a8));            // rotated to last position
-
-  EmitAndCheck(&assembler, "DataProcessingModifiedImmediates");
-}
-
-TEST_F(Thumb2AssemblerTest, DataProcessingShiftedRegister) {
-  // 16-bit variants.
-  __ movs(R3, ShifterOperand(R4, LSL, 4));
-  __ movs(R3, ShifterOperand(R4, LSR, 5));
-  __ movs(R3, ShifterOperand(R4, ASR, 6));
-
-  // 32-bit ROR because ROR immediate doesn't have the same 16-bit version as other shifts.
-  __ movs(R3, ShifterOperand(R4, ROR, 7));
-
-  // 32-bit RRX because RRX has no 16-bit version.
-  __ movs(R3, ShifterOperand(R4, RRX));
-
-  // 32 bit variants (not setting condition codes).
-  __ mov(R3, ShifterOperand(R4, LSL, 4), AL, kCcKeep);
-  __ mov(R3, ShifterOperand(R4, LSR, 5), AL, kCcKeep);
-  __ mov(R3, ShifterOperand(R4, ASR, 6), AL, kCcKeep);
-  __ mov(R3, ShifterOperand(R4, ROR, 7), AL, kCcKeep);
-  __ mov(R3, ShifterOperand(R4, RRX), AL, kCcKeep);
-
-  // 32 bit variants (high registers).
-  __ movs(R8, ShifterOperand(R4, LSL, 4));
-  __ movs(R8, ShifterOperand(R4, LSR, 5));
-  __ movs(R8, ShifterOperand(R4, ASR, 6));
-  __ movs(R8, ShifterOperand(R4, ROR, 7));
-  __ movs(R8, ShifterOperand(R4, RRX));
-
-  EmitAndCheck(&assembler, "DataProcessingShiftedRegister");
-}
-
-TEST_F(Thumb2AssemblerTest, ShiftImmediate) {
-  // Note: This test produces the same results as DataProcessingShiftedRegister
-  // but it does so using shift functions instead of mov().
-
-  // 16-bit variants.
-  __ Lsl(R3, R4, 4);
-  __ Lsr(R3, R4, 5);
-  __ Asr(R3, R4, 6);
-
-  // 32-bit ROR because ROR immediate doesn't have the same 16-bit version as other shifts.
-  __ Ror(R3, R4, 7);
-
-  // 32-bit RRX because RRX has no 16-bit version.
-  __ Rrx(R3, R4);
-
-  // 32 bit variants (not setting condition codes).
-  __ Lsl(R3, R4, 4, AL, kCcKeep);
-  __ Lsr(R3, R4, 5, AL, kCcKeep);
-  __ Asr(R3, R4, 6, AL, kCcKeep);
-  __ Ror(R3, R4, 7, AL, kCcKeep);
-  __ Rrx(R3, R4, AL, kCcKeep);
-
-  // 32 bit variants (high registers).
-  __ Lsls(R8, R4, 4);
-  __ Lsrs(R8, R4, 5);
-  __ Asrs(R8, R4, 6);
-  __ Rors(R8, R4, 7);
-  __ Rrxs(R8, R4);
-
-  EmitAndCheck(&assembler, "ShiftImmediate");
-}
-
-TEST_F(Thumb2AssemblerTest, BasicLoad) {
-  __ ldr(R3, Address(R4, 24));
-  __ ldrb(R3, Address(R4, 24));
-  __ ldrh(R3, Address(R4, 24));
-  __ ldrsb(R3, Address(R4, 24));
-  __ ldrsh(R3, Address(R4, 24));
-
-  __ ldr(R3, Address(SP, 24));
-
-  // 32 bit variants
-  __ ldr(R8, Address(R4, 24));
-  __ ldrb(R8, Address(R4, 24));
-  __ ldrh(R8, Address(R4, 24));
-  __ ldrsb(R8, Address(R4, 24));
-  __ ldrsh(R8, Address(R4, 24));
-
-  EmitAndCheck(&assembler, "BasicLoad");
-}
-
-
-TEST_F(Thumb2AssemblerTest, BasicStore) {
-  __ str(R3, Address(R4, 24));
-  __ strb(R3, Address(R4, 24));
-  __ strh(R3, Address(R4, 24));
-
-  __ str(R3, Address(SP, 24));
-
-  // 32 bit variants.
-  __ str(R8, Address(R4, 24));
-  __ strb(R8, Address(R4, 24));
-  __ strh(R8, Address(R4, 24));
-
-  EmitAndCheck(&assembler, "BasicStore");
-}
-
-TEST_F(Thumb2AssemblerTest, ComplexLoad) {
-  __ ldr(R3, Address(R4, 24, Address::Mode::Offset));
-  __ ldr(R3, Address(R4, 24, Address::Mode::PreIndex));
-  __ ldr(R3, Address(R4, 24, Address::Mode::PostIndex));
-  __ ldr(R3, Address(R4, 24, Address::Mode::NegOffset));
-  __ ldr(R3, Address(R4, 24, Address::Mode::NegPreIndex));
-  __ ldr(R3, Address(R4, 24, Address::Mode::NegPostIndex));
-
-  __ ldrb(R3, Address(R4, 24, Address::Mode::Offset));
-  __ ldrb(R3, Address(R4, 24, Address::Mode::PreIndex));
-  __ ldrb(R3, Address(R4, 24, Address::Mode::PostIndex));
-  __ ldrb(R3, Address(R4, 24, Address::Mode::NegOffset));
-  __ ldrb(R3, Address(R4, 24, Address::Mode::NegPreIndex));
-  __ ldrb(R3, Address(R4, 24, Address::Mode::NegPostIndex));
-
-  __ ldrh(R3, Address(R4, 24, Address::Mode::Offset));
-  __ ldrh(R3, Address(R4, 24, Address::Mode::PreIndex));
-  __ ldrh(R3, Address(R4, 24, Address::Mode::PostIndex));
-  __ ldrh(R3, Address(R4, 24, Address::Mode::NegOffset));
-  __ ldrh(R3, Address(R4, 24, Address::Mode::NegPreIndex));
-  __ ldrh(R3, Address(R4, 24, Address::Mode::NegPostIndex));
-
-  __ ldrsb(R3, Address(R4, 24, Address::Mode::Offset));
-  __ ldrsb(R3, Address(R4, 24, Address::Mode::PreIndex));
-  __ ldrsb(R3, Address(R4, 24, Address::Mode::PostIndex));
-  __ ldrsb(R3, Address(R4, 24, Address::Mode::NegOffset));
-  __ ldrsb(R3, Address(R4, 24, Address::Mode::NegPreIndex));
-  __ ldrsb(R3, Address(R4, 24, Address::Mode::NegPostIndex));
-
-  __ ldrsh(R3, Address(R4, 24, Address::Mode::Offset));
-  __ ldrsh(R3, Address(R4, 24, Address::Mode::PreIndex));
-  __ ldrsh(R3, Address(R4, 24, Address::Mode::PostIndex));
-  __ ldrsh(R3, Address(R4, 24, Address::Mode::NegOffset));
-  __ ldrsh(R3, Address(R4, 24, Address::Mode::NegPreIndex));
-  __ ldrsh(R3, Address(R4, 24, Address::Mode::NegPostIndex));
-
-  EmitAndCheck(&assembler, "ComplexLoad");
-}
-
-
-TEST_F(Thumb2AssemblerTest, ComplexStore) {
-  __ str(R3, Address(R4, 24, Address::Mode::Offset));
-  __ str(R3, Address(R4, 24, Address::Mode::PreIndex));
-  __ str(R3, Address(R4, 24, Address::Mode::PostIndex));
-  __ str(R3, Address(R4, 24, Address::Mode::NegOffset));
-  __ str(R3, Address(R4, 24, Address::Mode::NegPreIndex));
-  __ str(R3, Address(R4, 24, Address::Mode::NegPostIndex));
-
-  __ strb(R3, Address(R4, 24, Address::Mode::Offset));
-  __ strb(R3, Address(R4, 24, Address::Mode::PreIndex));
-  __ strb(R3, Address(R4, 24, Address::Mode::PostIndex));
-  __ strb(R3, Address(R4, 24, Address::Mode::NegOffset));
-  __ strb(R3, Address(R4, 24, Address::Mode::NegPreIndex));
-  __ strb(R3, Address(R4, 24, Address::Mode::NegPostIndex));
-
-  __ strh(R3, Address(R4, 24, Address::Mode::Offset));
-  __ strh(R3, Address(R4, 24, Address::Mode::PreIndex));
-  __ strh(R3, Address(R4, 24, Address::Mode::PostIndex));
-  __ strh(R3, Address(R4, 24, Address::Mode::NegOffset));
-  __ strh(R3, Address(R4, 24, Address::Mode::NegPreIndex));
-  __ strh(R3, Address(R4, 24, Address::Mode::NegPostIndex));
-
-  EmitAndCheck(&assembler, "ComplexStore");
-}
-
-TEST_F(Thumb2AssemblerTest, NegativeLoadStore) {
-  __ ldr(R3, Address(R4, -24, Address::Mode::Offset));
-  __ ldr(R3, Address(R4, -24, Address::Mode::PreIndex));
-  __ ldr(R3, Address(R4, -24, Address::Mode::PostIndex));
-  __ ldr(R3, Address(R4, -24, Address::Mode::NegOffset));
-  __ ldr(R3, Address(R4, -24, Address::Mode::NegPreIndex));
-  __ ldr(R3, Address(R4, -24, Address::Mode::NegPostIndex));
-
-  __ ldrb(R3, Address(R4, -24, Address::Mode::Offset));
-  __ ldrb(R3, Address(R4, -24, Address::Mode::PreIndex));
-  __ ldrb(R3, Address(R4, -24, Address::Mode::PostIndex));
-  __ ldrb(R3, Address(R4, -24, Address::Mode::NegOffset));
-  __ ldrb(R3, Address(R4, -24, Address::Mode::NegPreIndex));
-  __ ldrb(R3, Address(R4, -24, Address::Mode::NegPostIndex));
-
-  __ ldrh(R3, Address(R4, -24, Address::Mode::Offset));
-  __ ldrh(R3, Address(R4, -24, Address::Mode::PreIndex));
-  __ ldrh(R3, Address(R4, -24, Address::Mode::PostIndex));
-  __ ldrh(R3, Address(R4, -24, Address::Mode::NegOffset));
-  __ ldrh(R3, Address(R4, -24, Address::Mode::NegPreIndex));
-  __ ldrh(R3, Address(R4, -24, Address::Mode::NegPostIndex));
-
-  __ ldrsb(R3, Address(R4, -24, Address::Mode::Offset));
-  __ ldrsb(R3, Address(R4, -24, Address::Mode::PreIndex));
-  __ ldrsb(R3, Address(R4, -24, Address::Mode::PostIndex));
-  __ ldrsb(R3, Address(R4, -24, Address::Mode::NegOffset));
-  __ ldrsb(R3, Address(R4, -24, Address::Mode::NegPreIndex));
-  __ ldrsb(R3, Address(R4, -24, Address::Mode::NegPostIndex));
-
-  __ ldrsh(R3, Address(R4, -24, Address::Mode::Offset));
-  __ ldrsh(R3, Address(R4, -24, Address::Mode::PreIndex));
-  __ ldrsh(R3, Address(R4, -24, Address::Mode::PostIndex));
-  __ ldrsh(R3, Address(R4, -24, Address::Mode::NegOffset));
-  __ ldrsh(R3, Address(R4, -24, Address::Mode::NegPreIndex));
-  __ ldrsh(R3, Address(R4, -24, Address::Mode::NegPostIndex));
-
-  __ str(R3, Address(R4, -24, Address::Mode::Offset));
-  __ str(R3, Address(R4, -24, Address::Mode::PreIndex));
-  __ str(R3, Address(R4, -24, Address::Mode::PostIndex));
-  __ str(R3, Address(R4, -24, Address::Mode::NegOffset));
-  __ str(R3, Address(R4, -24, Address::Mode::NegPreIndex));
-  __ str(R3, Address(R4, -24, Address::Mode::NegPostIndex));
-
-  __ strb(R3, Address(R4, -24, Address::Mode::Offset));
-  __ strb(R3, Address(R4, -24, Address::Mode::PreIndex));
-  __ strb(R3, Address(R4, -24, Address::Mode::PostIndex));
-  __ strb(R3, Address(R4, -24, Address::Mode::NegOffset));
-  __ strb(R3, Address(R4, -24, Address::Mode::NegPreIndex));
-  __ strb(R3, Address(R4, -24, Address::Mode::NegPostIndex));
-
-  __ strh(R3, Address(R4, -24, Address::Mode::Offset));
-  __ strh(R3, Address(R4, -24, Address::Mode::PreIndex));
-  __ strh(R3, Address(R4, -24, Address::Mode::PostIndex));
-  __ strh(R3, Address(R4, -24, Address::Mode::NegOffset));
-  __ strh(R3, Address(R4, -24, Address::Mode::NegPreIndex));
-  __ strh(R3, Address(R4, -24, Address::Mode::NegPostIndex));
-
-  EmitAndCheck(&assembler, "NegativeLoadStore");
-}
-
-TEST_F(Thumb2AssemblerTest, SimpleLoadStoreDual) {
-  __ strd(R2, Address(R0, 24, Address::Mode::Offset));
-  __ ldrd(R2, Address(R0, 24, Address::Mode::Offset));
-
-  EmitAndCheck(&assembler, "SimpleLoadStoreDual");
-}
-
-TEST_F(Thumb2AssemblerTest, ComplexLoadStoreDual) {
-  __ strd(R2, Address(R0, 24, Address::Mode::Offset));
-  __ strd(R2, Address(R0, 24, Address::Mode::PreIndex));
-  __ strd(R2, Address(R0, 24, Address::Mode::PostIndex));
-  __ strd(R2, Address(R0, 24, Address::Mode::NegOffset));
-  __ strd(R2, Address(R0, 24, Address::Mode::NegPreIndex));
-  __ strd(R2, Address(R0, 24, Address::Mode::NegPostIndex));
-
-  __ ldrd(R2, Address(R0, 24, Address::Mode::Offset));
-  __ ldrd(R2, Address(R0, 24, Address::Mode::PreIndex));
-  __ ldrd(R2, Address(R0, 24, Address::Mode::PostIndex));
-  __ ldrd(R2, Address(R0, 24, Address::Mode::NegOffset));
-  __ ldrd(R2, Address(R0, 24, Address::Mode::NegPreIndex));
-  __ ldrd(R2, Address(R0, 24, Address::Mode::NegPostIndex));
-
-  EmitAndCheck(&assembler, "ComplexLoadStoreDual");
-}
-
-TEST_F(Thumb2AssemblerTest, NegativeLoadStoreDual) {
-  __ strd(R2, Address(R0, -24, Address::Mode::Offset));
-  __ strd(R2, Address(R0, -24, Address::Mode::PreIndex));
-  __ strd(R2, Address(R0, -24, Address::Mode::PostIndex));
-  __ strd(R2, Address(R0, -24, Address::Mode::NegOffset));
-  __ strd(R2, Address(R0, -24, Address::Mode::NegPreIndex));
-  __ strd(R2, Address(R0, -24, Address::Mode::NegPostIndex));
-
-  __ ldrd(R2, Address(R0, -24, Address::Mode::Offset));
-  __ ldrd(R2, Address(R0, -24, Address::Mode::PreIndex));
-  __ ldrd(R2, Address(R0, -24, Address::Mode::PostIndex));
-  __ ldrd(R2, Address(R0, -24, Address::Mode::NegOffset));
-  __ ldrd(R2, Address(R0, -24, Address::Mode::NegPreIndex));
-  __ ldrd(R2, Address(R0, -24, Address::Mode::NegPostIndex));
-
-  EmitAndCheck(&assembler, "NegativeLoadStoreDual");
-}
-
-TEST_F(Thumb2AssemblerTest, SimpleBranch) {
-  Label l1;
-  __ mov(R0, ShifterOperand(2));
-  __ Bind(&l1);
-  __ mov(R1, ShifterOperand(1));
-  __ b(&l1);
-  Label l2;
-  __ b(&l2);
-  __ mov(R1, ShifterOperand(2));
-  __ Bind(&l2);
-  __ mov(R0, ShifterOperand(3));
-
-  Label l3;
-  __ mov(R0, ShifterOperand(2));
-  __ Bind(&l3);
-  __ mov(R1, ShifterOperand(1));
-  __ b(&l3, EQ);
-
-  Label l4;
-  __ b(&l4, EQ);
-  __ mov(R1, ShifterOperand(2));
-  __ Bind(&l4);
-  __ mov(R0, ShifterOperand(3));
-
-  // 2 linked labels.
-  Label l5;
-  __ b(&l5);
-  __ mov(R1, ShifterOperand(4));
-  __ b(&l5);
-  __ mov(R1, ShifterOperand(5));
-  __ Bind(&l5);
-  __ mov(R0, ShifterOperand(6));
-
-  EmitAndCheck(&assembler, "SimpleBranch");
-}
-
-TEST_F(Thumb2AssemblerTest, LongBranch) {
-  __ Force32Bit();
-  // 32 bit branches.
-  Label l1;
-  __ mov(R0, ShifterOperand(2));
-  __ Bind(&l1);
-  __ mov(R1, ShifterOperand(1));
-  __ b(&l1);
-
-  Label l2;
-  __ b(&l2);
-  __ mov(R1, ShifterOperand(2));
-  __ Bind(&l2);
-  __ mov(R0, ShifterOperand(3));
-
-  Label l3;
-  __ mov(R0, ShifterOperand(2));
-  __ Bind(&l3);
-  __ mov(R1, ShifterOperand(1));
-  __ b(&l3, EQ);
-
-  Label l4;
-  __ b(&l4, EQ);
-  __ mov(R1, ShifterOperand(2));
-  __ Bind(&l4);
-  __ mov(R0, ShifterOperand(3));
-
-  // 2 linked labels.
-  Label l5;
-  __ b(&l5);
-  __ mov(R1, ShifterOperand(4));
-  __ b(&l5);
-  __ mov(R1, ShifterOperand(5));
-  __ Bind(&l5);
-  __ mov(R0, ShifterOperand(6));
-
-  EmitAndCheck(&assembler, "LongBranch");
-}
-
-TEST_F(Thumb2AssemblerTest, LoadMultiple) {
-  // 16 bit.
-  __ ldm(DB_W, R4, (1 << R0 | 1 << R3));
-
-  // 32 bit.
-  __ ldm(DB_W, R4, (1 << LR | 1 << R11));
-  __ ldm(DB, R4, (1 << LR | 1 << R11));
-
-  // Single reg is converted to ldr
-  __ ldm(DB_W, R4, (1 << R5));
-
-  EmitAndCheck(&assembler, "LoadMultiple");
-}
-
-TEST_F(Thumb2AssemblerTest, StoreMultiple) {
-  // 16 bit.
-  __ stm(IA_W, R4, (1 << R0 | 1 << R3));
-
-  // 32 bit.
-  __ stm(IA_W, R4, (1 << LR | 1 << R11));
-  __ stm(IA, R4, (1 << LR | 1 << R11));
-
-  // Single reg is converted to str
-  __ stm(IA_W, R4, (1 << R5));
-  __ stm(IA, R4, (1 << R5));
-
-  EmitAndCheck(&assembler, "StoreMultiple");
-}
-
-TEST_F(Thumb2AssemblerTest, MovWMovT) {
-  // Always 32 bit.
-  __ movw(R4, 0);
-  __ movw(R4, 0x34);
-  __ movw(R9, 0x34);
-  __ movw(R3, 0x1234);
-  __ movw(R9, 0xffff);
-
-  // Always 32 bit.
-  __ movt(R0, 0);
-  __ movt(R0, 0x1234);
-  __ movt(R1, 0xffff);
-
-  EmitAndCheck(&assembler, "MovWMovT");
-}
-
-TEST_F(Thumb2AssemblerTest, SpecialAddSub) {
-  __ add(R2, SP, ShifterOperand(0x50));   // 16 bit.
-  __ add(SP, SP, ShifterOperand(0x50));   // 16 bit.
-  __ add(R8, SP, ShifterOperand(0x50));   // 32 bit.
-
-  __ add(R2, SP, ShifterOperand(0xf00));  // 32 bit due to imm size.
-  __ add(SP, SP, ShifterOperand(0xf00));  // 32 bit due to imm size.
-  __ add(SP, SP, ShifterOperand(0xffc));  // 32 bit due to imm size; encoding T4.
-
-  __ sub(SP, SP, ShifterOperand(0x50));   // 16 bit
-  __ sub(R0, SP, ShifterOperand(0x50));   // 32 bit
-  __ sub(R8, SP, ShifterOperand(0x50));   // 32 bit.
-
-  __ sub(SP, SP, ShifterOperand(0xf00));  // 32 bit due to imm size
-  __ sub(SP, SP, ShifterOperand(0xffc));  // 32 bit due to imm size; encoding T4.
-
-  EmitAndCheck(&assembler, "SpecialAddSub");
-}
-
-TEST_F(Thumb2AssemblerTest, LoadFromOffset) {
-  __ LoadFromOffset(kLoadWord, R2, R4, 12);
-  __ LoadFromOffset(kLoadWord, R2, R4, 0xfff);
-  __ LoadFromOffset(kLoadWord, R2, R4, 0x1000);
-  __ LoadFromOffset(kLoadWord, R2, R4, 0x1000a4);
-  __ LoadFromOffset(kLoadWord, R2, R4, 0x101000);
-  __ LoadFromOffset(kLoadWord, R4, R4, 0x101000);
-  __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 12);
-  __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 0xfff);
-  __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 0x1000);
-  __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 0x1000a4);
-  __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 0x101000);
-  __ LoadFromOffset(kLoadUnsignedHalfword, R4, R4, 0x101000);
-  __ LoadFromOffset(kLoadWordPair, R2, R4, 12);
-  __ LoadFromOffset(kLoadWordPair, R2, R4, 0x3fc);
-  __ LoadFromOffset(kLoadWordPair, R2, R4, 0x400);
-  __ LoadFromOffset(kLoadWordPair, R2, R4, 0x400a4);
-  __ LoadFromOffset(kLoadWordPair, R2, R4, 0x40400);
-  __ LoadFromOffset(kLoadWordPair, R4, R4, 0x40400);
-
-  __ LoadFromOffset(kLoadWord, R0, R12, 12);  // 32-bit because of R12.
-  __ LoadFromOffset(kLoadWord, R2, R4, 0xa4 - 0x100000);
-
-  __ LoadFromOffset(kLoadSignedByte, R2, R4, 12);
-  __ LoadFromOffset(kLoadUnsignedByte, R2, R4, 12);
-  __ LoadFromOffset(kLoadSignedHalfword, R2, R4, 12);
-
-  EmitAndCheck(&assembler, "LoadFromOffset");
-}
-
-TEST_F(Thumb2AssemblerTest, StoreToOffset) {
-  __ StoreToOffset(kStoreWord, R2, R4, 12);
-  __ StoreToOffset(kStoreWord, R2, R4, 0xfff);
-  __ StoreToOffset(kStoreWord, R2, R4, 0x1000);
-  __ StoreToOffset(kStoreWord, R2, R4, 0x1000a4);
-  __ StoreToOffset(kStoreWord, R2, R4, 0x101000);
-  __ StoreToOffset(kStoreWord, R4, R4, 0x101000);
-  __ StoreToOffset(kStoreHalfword, R2, R4, 12);
-  __ StoreToOffset(kStoreHalfword, R2, R4, 0xfff);
-  __ StoreToOffset(kStoreHalfword, R2, R4, 0x1000);
-  __ StoreToOffset(kStoreHalfword, R2, R4, 0x1000a4);
-  __ StoreToOffset(kStoreHalfword, R2, R4, 0x101000);
-  __ StoreToOffset(kStoreHalfword, R4, R4, 0x101000);
-  __ StoreToOffset(kStoreWordPair, R2, R4, 12);
-  __ StoreToOffset(kStoreWordPair, R2, R4, 0x3fc);
-  __ StoreToOffset(kStoreWordPair, R2, R4, 0x400);
-  __ StoreToOffset(kStoreWordPair, R2, R4, 0x400a4);
-  __ StoreToOffset(kStoreWordPair, R2, R4, 0x40400);
-  __ StoreToOffset(kStoreWordPair, R4, R4, 0x40400);
-
-  __ StoreToOffset(kStoreWord, R0, R12, 12);  // 32-bit because of R12.
-  __ StoreToOffset(kStoreWord, R2, R4, 0xa4 - 0x100000);
-
-  __ StoreToOffset(kStoreByte, R2, R4, 12);
-
-  EmitAndCheck(&assembler, "StoreToOffset");
-}
-
-TEST_F(Thumb2AssemblerTest, IfThen) {
-  __ it(EQ);
-  __ mov(R1, ShifterOperand(1), EQ);
-
-  __ it(EQ, kItThen);
-  __ mov(R1, ShifterOperand(1), EQ);
-  __ mov(R2, ShifterOperand(2), EQ);
-
-  __ it(EQ, kItElse);
-  __ mov(R1, ShifterOperand(1), EQ);
-  __ mov(R2, ShifterOperand(2), NE);
-
-  __ it(EQ, kItThen, kItElse);
-  __ mov(R1, ShifterOperand(1), EQ);
-  __ mov(R2, ShifterOperand(2), EQ);
-  __ mov(R3, ShifterOperand(3), NE);
-
-  __ it(EQ, kItElse, kItElse);
-  __ mov(R1, ShifterOperand(1), EQ);
-  __ mov(R2, ShifterOperand(2), NE);
-  __ mov(R3, ShifterOperand(3), NE);
-
-  __ it(EQ, kItThen, kItThen, kItElse);
-  __ mov(R1, ShifterOperand(1), EQ);
-  __ mov(R2, ShifterOperand(2), EQ);
-  __ mov(R3, ShifterOperand(3), EQ);
-  __ mov(R4, ShifterOperand(4), NE);
-
-  EmitAndCheck(&assembler, "IfThen");
-}
-
-TEST_F(Thumb2AssemblerTest, CbzCbnz) {
-  Label l1;
-  __ cbz(R2, &l1);
-  __ mov(R1, ShifterOperand(3));
-  __ mov(R2, ShifterOperand(3));
-  __ Bind(&l1);
-  __ mov(R2, ShifterOperand(4));
-
-  Label l2;
-  __ cbnz(R2, &l2);
-  __ mov(R8, ShifterOperand(3));
-  __ mov(R2, ShifterOperand(3));
-  __ Bind(&l2);
-  __ mov(R2, ShifterOperand(4));
-
-  EmitAndCheck(&assembler, "CbzCbnz");
-}
-
-TEST_F(Thumb2AssemblerTest, Multiply) {
-  __ mul(R0, R1, R0);
-  __ mul(R0, R1, R2);
-  __ mul(R8, R9, R8);
-  __ mul(R8, R9, R10);
-
-  __ mla(R0, R1, R2, R3);
-  __ mla(R8, R9, R8, R9);
-
-  __ mls(R0, R1, R2, R3);
-  __ mls(R8, R9, R8, R9);
-
-  __ umull(R0, R1, R2, R3);
-  __ umull(R8, R9, R10, R11);
-
-  EmitAndCheck(&assembler, "Multiply");
-}
-
-TEST_F(Thumb2AssemblerTest, Divide) {
-  __ sdiv(R0, R1, R2);
-  __ sdiv(R8, R9, R10);
-
-  __ udiv(R0, R1, R2);
-  __ udiv(R8, R9, R10);
-
-  EmitAndCheck(&assembler, "Divide");
-}
-
-TEST_F(Thumb2AssemblerTest, VMov) {
-  __ vmovs(S1, 1.0);
-  __ vmovd(D1, 1.0);
-
-  __ vmovs(S1, S2);
-  __ vmovd(D1, D2);
-
-  EmitAndCheck(&assembler, "VMov");
-}
-
-
-TEST_F(Thumb2AssemblerTest, BasicFloatingPoint) {
-  __ vadds(S0, S1, S2);
-  __ vsubs(S0, S1, S2);
-  __ vmuls(S0, S1, S2);
-  __ vmlas(S0, S1, S2);
-  __ vmlss(S0, S1, S2);
-  __ vdivs(S0, S1, S2);
-  __ vabss(S0, S1);
-  __ vnegs(S0, S1);
-  __ vsqrts(S0, S1);
-
-  __ vaddd(D0, D1, D2);
-  __ vsubd(D0, D1, D2);
-  __ vmuld(D0, D1, D2);
-  __ vmlad(D0, D1, D2);
-  __ vmlsd(D0, D1, D2);
-  __ vdivd(D0, D1, D2);
-  __ vabsd(D0, D1);
-  __ vnegd(D0, D1);
-  __ vsqrtd(D0, D1);
-
-  EmitAndCheck(&assembler, "BasicFloatingPoint");
-}
-
-TEST_F(Thumb2AssemblerTest, FloatingPointConversions) {
-  __ vcvtsd(S2, D2);
-  __ vcvtds(D2, S2);
-
-  __ vcvtis(S1, S2);
-  __ vcvtsi(S1, S2);
-
-  __ vcvtid(S1, D2);
-  __ vcvtdi(D1, S2);
-
-  __ vcvtus(S1, S2);
-  __ vcvtsu(S1, S2);
-
-  __ vcvtud(S1, D2);
-  __ vcvtdu(D1, S2);
-
-  EmitAndCheck(&assembler, "FloatingPointConversions");
-}
-
-TEST_F(Thumb2AssemblerTest, FloatingPointComparisons) {
-  __ vcmps(S0, S1);
-  __ vcmpd(D0, D1);
-
-  __ vcmpsz(S2);
-  __ vcmpdz(D2);
-
-  EmitAndCheck(&assembler, "FloatingPointComparisons");
-}
-
-TEST_F(Thumb2AssemblerTest, Calls) {
-  __ blx(LR);
-  __ bx(LR);
-
-  EmitAndCheck(&assembler, "Calls");
-}
-
-TEST_F(Thumb2AssemblerTest, Breakpoint) {
-  __ bkpt(0);
-
-  EmitAndCheck(&assembler, "Breakpoint");
-}
-
-TEST_F(Thumb2AssemblerTest, StrR1) {
-  __ str(R1, Address(SP, 68));
-  __ str(R1, Address(SP, 1068));
-
-  EmitAndCheck(&assembler, "StrR1");
-}
-
-TEST_F(Thumb2AssemblerTest, VPushPop) {
-  __ vpushs(S2, 4);
-  __ vpushd(D2, 4);
-
-  __ vpops(S2, 4);
-  __ vpopd(D2, 4);
-
-  EmitAndCheck(&assembler, "VPushPop");
-}
-
-TEST_F(Thumb2AssemblerTest, Max16BitBranch) {
-  Label l1;
-  __ b(&l1);
-  for (int i = 0 ; i < (1 << 11) ; i += 2) {
-    __ mov(R3, ShifterOperand(i & 0xff));
-  }
-  __ Bind(&l1);
-  __ mov(R1, ShifterOperand(R2));
-
-  EmitAndCheck(&assembler, "Max16BitBranch");
-}
-
-TEST_F(Thumb2AssemblerTest, Branch32) {
-  Label l1;
-  __ b(&l1);
-  for (int i = 0 ; i < (1 << 11) + 2 ; i += 2) {
-    __ mov(R3, ShifterOperand(i & 0xff));
-  }
-  __ Bind(&l1);
-  __ mov(R1, ShifterOperand(R2));
-
-  EmitAndCheck(&assembler, "Branch32");
-}
-
-TEST_F(Thumb2AssemblerTest, CompareAndBranchMax) {
-  Label l1;
-  __ cbz(R4, &l1);
-  for (int i = 0 ; i < (1 << 7) ; i += 2) {
-    __ mov(R3, ShifterOperand(i & 0xff));
-  }
-  __ Bind(&l1);
-  __ mov(R1, ShifterOperand(R2));
-
-  EmitAndCheck(&assembler, "CompareAndBranchMax");
-}
-
-TEST_F(Thumb2AssemblerTest, CompareAndBranchRelocation16) {
-  Label l1;
-  __ cbz(R4, &l1);
-  for (int i = 0 ; i < (1 << 7) + 2 ; i += 2) {
-    __ mov(R3, ShifterOperand(i & 0xff));
-  }
-  __ Bind(&l1);
-  __ mov(R1, ShifterOperand(R2));
-
-  EmitAndCheck(&assembler, "CompareAndBranchRelocation16");
-}
-
-TEST_F(Thumb2AssemblerTest, CompareAndBranchRelocation32) {
-  Label l1;
-  __ cbz(R4, &l1);
-  for (int i = 0 ; i < (1 << 11) + 2 ; i += 2) {
-    __ mov(R3, ShifterOperand(i & 0xff));
-  }
-  __ Bind(&l1);
-  __ mov(R1, ShifterOperand(R2));
-
-  EmitAndCheck(&assembler, "CompareAndBranchRelocation32");
-}
-
-TEST_F(Thumb2AssemblerTest, MixedBranch32) {
-  Label l1;
-  Label l2;
-  __ b(&l1);      // Forwards.
-  __ Bind(&l2);
-
-  // Space to force relocation.
-  for (int i = 0 ; i < (1 << 11) + 2 ; i += 2) {
-    __ mov(R3, ShifterOperand(i & 0xff));
-  }
-  __ b(&l2);      // Backwards.
-  __ Bind(&l1);
-  __ mov(R1, ShifterOperand(R2));
-
-  EmitAndCheck(&assembler, "MixedBranch32");
-}
-
-TEST_F(Thumb2AssemblerTest, Shifts) {
-  // 16 bit selected for CcDontCare.
-  __ Lsl(R0, R1, 5);
-  __ Lsr(R0, R1, 5);
-  __ Asr(R0, R1, 5);
-
-  __ Lsl(R0, R0, R1);
-  __ Lsr(R0, R0, R1);
-  __ Asr(R0, R0, R1);
-  __ Ror(R0, R0, R1);
-
-  // 16 bit with kCcSet.
-  __ Lsls(R0, R1, 5);
-  __ Lsrs(R0, R1, 5);
-  __ Asrs(R0, R1, 5);
-
-  __ Lsls(R0, R0, R1);
-  __ Lsrs(R0, R0, R1);
-  __ Asrs(R0, R0, R1);
-  __ Rors(R0, R0, R1);
-
-  // 32-bit with kCcKeep.
-  __ Lsl(R0, R1, 5, AL, kCcKeep);
-  __ Lsr(R0, R1, 5, AL, kCcKeep);
-  __ Asr(R0, R1, 5, AL, kCcKeep);
-
-  __ Lsl(R0, R0, R1, AL, kCcKeep);
-  __ Lsr(R0, R0, R1, AL, kCcKeep);
-  __ Asr(R0, R0, R1, AL, kCcKeep);
-  __ Ror(R0, R0, R1, AL, kCcKeep);
-
-  // 32-bit because ROR immediate doesn't have a 16-bit version like the other shifts.
-  __ Ror(R0, R1, 5);
-  __ Rors(R0, R1, 5);
-  __ Ror(R0, R1, 5, AL, kCcKeep);
-
-  // 32 bit due to high registers.
-  __ Lsl(R8, R1, 5);
-  __ Lsr(R0, R8, 5);
-  __ Asr(R8, R1, 5);
-  __ Ror(R0, R8, 5);
-
-  // 32 bit due to different Rd and Rn.
-  __ Lsl(R0, R1, R2);
-  __ Lsr(R0, R1, R2);
-  __ Asr(R0, R1, R2);
-  __ Ror(R0, R1, R2);
-
-  // 32 bit due to use of high registers.
-  __ Lsl(R8, R1, R2);
-  __ Lsr(R0, R8, R2);
-  __ Asr(R0, R1, R8);
-
-  // S bit (all 32 bit)
-
-  // 32 bit due to high registers.
-  __ Lsls(R8, R1, 5);
-  __ Lsrs(R0, R8, 5);
-  __ Asrs(R8, R1, 5);
-  __ Rors(R0, R8, 5);
-
-  // 32 bit due to different Rd and Rn.
-  __ Lsls(R0, R1, R2);
-  __ Lsrs(R0, R1, R2);
-  __ Asrs(R0, R1, R2);
-  __ Rors(R0, R1, R2);
-
-  // 32 bit due to use of high registers.
-  __ Lsls(R8, R1, R2);
-  __ Lsrs(R0, R8, R2);
-  __ Asrs(R0, R1, R8);
-
-  EmitAndCheck(&assembler, "Shifts");
-}
-
-TEST_F(Thumb2AssemblerTest, LoadStoreRegOffset) {
-  // 16 bit.
-  __ ldr(R0, Address(R1, R2));
-  __ str(R0, Address(R1, R2));
-
-  // 32 bit due to shift.
-  __ ldr(R0, Address(R1, R2, LSL, 1));
-  __ str(R0, Address(R1, R2, LSL, 1));
-
-  __ ldr(R0, Address(R1, R2, LSL, 3));
-  __ str(R0, Address(R1, R2, LSL, 3));
-
-  // 32 bit due to high register use.
-  __ ldr(R8, Address(R1, R2));
-  __ str(R8, Address(R1, R2));
-
-  __ ldr(R1, Address(R8, R2));
-  __ str(R2, Address(R8, R2));
-
-  __ ldr(R0, Address(R1, R8));
-  __ str(R0, Address(R1, R8));
-
-  EmitAndCheck(&assembler, "LoadStoreRegOffset");
-}
-
-TEST_F(Thumb2AssemblerTest, LoadStoreLimits) {
-  __ ldr(R0, Address(R4, 124));     // 16 bit.
-  __ ldr(R0, Address(R4, 128));     // 32 bit.
-
-  __ ldrb(R0, Address(R4, 31));     // 16 bit.
-  __ ldrb(R0, Address(R4, 32));     // 32 bit.
-
-  __ ldrh(R0, Address(R4, 62));     // 16 bit.
-  __ ldrh(R0, Address(R4, 64));     // 32 bit.
-
-  __ ldrsb(R0, Address(R4, 31));     // 32 bit.
-  __ ldrsb(R0, Address(R4, 32));     // 32 bit.
-
-  __ ldrsh(R0, Address(R4, 62));     // 32 bit.
-  __ ldrsh(R0, Address(R4, 64));     // 32 bit.
-
-  __ str(R0, Address(R4, 124));     // 16 bit.
-  __ str(R0, Address(R4, 128));     // 32 bit.
-
-  __ strb(R0, Address(R4, 31));     // 16 bit.
-  __ strb(R0, Address(R4, 32));     // 32 bit.
-
-  __ strh(R0, Address(R4, 62));     // 16 bit.
-  __ strh(R0, Address(R4, 64));     // 32 bit.
-
-  EmitAndCheck(&assembler, "LoadStoreLimits");
-}
-
-TEST_F(Thumb2AssemblerTest, CompareAndBranch) {
-  Label label;
-  __ CompareAndBranchIfZero(arm::R0, &label);
-  __ CompareAndBranchIfZero(arm::R11, &label);
-  __ CompareAndBranchIfNonZero(arm::R0, &label);
-  __ CompareAndBranchIfNonZero(arm::R11, &label);
-  __ Bind(&label);
-
-  EmitAndCheck(&assembler, "CompareAndBranch");
-}
-
-TEST_F(Thumb2AssemblerTest, AddConstant) {
-  // Low registers, Rd != Rn.
-  __ AddConstant(R0, R1, 0);                          // MOV.
-  __ AddConstant(R0, R1, 1);                          // 16-bit ADDS, encoding T1.
-  __ AddConstant(R0, R1, 7);                          // 16-bit ADDS, encoding T1.
-  __ AddConstant(R0, R1, 8);                          // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R1, 255);                        // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R1, 256);                        // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R1, 257);                        // 32-bit ADD, encoding T4.
-  __ AddConstant(R0, R1, 0xfff);                      // 32-bit ADD, encoding T4.
-  __ AddConstant(R0, R1, 0x1000);                     // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R1, 0x1001);                     // MVN+SUB.
-  __ AddConstant(R0, R1, 0x1002);                     // MOVW+ADD.
-  __ AddConstant(R0, R1, 0xffff);                     // MOVW+ADD.
-  __ AddConstant(R0, R1, 0x10000);                    // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R1, 0x10001);                    // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R1, 0x10002);                    // MVN+SUB.
-  __ AddConstant(R0, R1, 0x10003);                    // MOVW+MOVT+ADD.
-  __ AddConstant(R0, R1, -1);                         // 16-bit SUBS.
-  __ AddConstant(R0, R1, -7);                         // 16-bit SUBS.
-  __ AddConstant(R0, R1, -8);                         // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R1, -255);                       // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R1, -256);                       // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R1, -257);                       // 32-bit SUB, encoding T4.
-  __ AddConstant(R0, R1, -0xfff);                     // 32-bit SUB, encoding T4.
-  __ AddConstant(R0, R1, -0x1000);                    // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R1, -0x1001);                    // MVN+ADD.
-  __ AddConstant(R0, R1, -0x1002);                    // MOVW+SUB.
-  __ AddConstant(R0, R1, -0xffff);                    // MOVW+SUB.
-  __ AddConstant(R0, R1, -0x10000);                   // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R1, -0x10001);                   // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R1, -0x10002);                   // MVN+ADD.
-  __ AddConstant(R0, R1, -0x10003);                   // MOVW+MOVT+ADD.
-
-  // Low registers, Rd == Rn.
-  __ AddConstant(R0, R0, 0);                          // Nothing.
-  __ AddConstant(R1, R1, 1);                          // 16-bit ADDS, encoding T2,
-  __ AddConstant(R0, R0, 7);                          // 16-bit ADDS, encoding T2.
-  __ AddConstant(R1, R1, 8);                          // 16-bit ADDS, encoding T2.
-  __ AddConstant(R0, R0, 255);                        // 16-bit ADDS, encoding T2.
-  __ AddConstant(R1, R1, 256);                        // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R0, 257);                        // 32-bit ADD, encoding T4.
-  __ AddConstant(R1, R1, 0xfff);                      // 32-bit ADD, encoding T4.
-  __ AddConstant(R0, R0, 0x1000);                     // 32-bit ADD, encoding T3.
-  __ AddConstant(R1, R1, 0x1001);                     // MVN+SUB.
-  __ AddConstant(R0, R0, 0x1002);                     // MOVW+ADD.
-  __ AddConstant(R1, R1, 0xffff);                     // MOVW+ADD.
-  __ AddConstant(R0, R0, 0x10000);                    // 32-bit ADD, encoding T3.
-  __ AddConstant(R1, R1, 0x10001);                    // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R0, 0x10002);                    // MVN+SUB.
-  __ AddConstant(R1, R1, 0x10003);                    // MOVW+MOVT+ADD.
-  __ AddConstant(R0, R0, -1);                         // 16-bit SUBS, encoding T2.
-  __ AddConstant(R1, R1, -7);                         // 16-bit SUBS, encoding T2.
-  __ AddConstant(R0, R0, -8);                         // 16-bit SUBS, encoding T2.
-  __ AddConstant(R1, R1, -255);                       // 16-bit SUBS, encoding T2.
-  __ AddConstant(R0, R0, -256);                       // 32-bit SUB, encoding T3.
-  __ AddConstant(R1, R1, -257);                       // 32-bit SUB, encoding T4.
-  __ AddConstant(R0, R0, -0xfff);                     // 32-bit SUB, encoding T4.
-  __ AddConstant(R1, R1, -0x1000);                    // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R0, -0x1001);                    // MVN+ADD.
-  __ AddConstant(R1, R1, -0x1002);                    // MOVW+SUB.
-  __ AddConstant(R0, R0, -0xffff);                    // MOVW+SUB.
-  __ AddConstant(R1, R1, -0x10000);                   // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R0, -0x10001);                   // 32-bit SUB, encoding T3.
-  __ AddConstant(R1, R1, -0x10002);                   // MVN+ADD.
-  __ AddConstant(R0, R0, -0x10003);                   // MOVW+MOVT+ADD.
-
-  // High registers.
-  __ AddConstant(R8, R8, 0);                          // Nothing.
-  __ AddConstant(R8, R1, 1);                          // 32-bit ADD, encoding T3,
-  __ AddConstant(R0, R8, 7);                          // 32-bit ADD, encoding T3.
-  __ AddConstant(R8, R8, 8);                          // 32-bit ADD, encoding T3.
-  __ AddConstant(R8, R1, 255);                        // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R8, 256);                        // 32-bit ADD, encoding T3.
-  __ AddConstant(R8, R8, 257);                        // 32-bit ADD, encoding T4.
-  __ AddConstant(R8, R1, 0xfff);                      // 32-bit ADD, encoding T4.
-  __ AddConstant(R0, R8, 0x1000);                     // 32-bit ADD, encoding T3.
-  __ AddConstant(R8, R8, 0x1001);                     // MVN+SUB.
-  __ AddConstant(R0, R1, 0x1002);                     // MOVW+ADD.
-  __ AddConstant(R0, R8, 0xffff);                     // MOVW+ADD.
-  __ AddConstant(R8, R8, 0x10000);                    // 32-bit ADD, encoding T3.
-  __ AddConstant(R8, R1, 0x10001);                    // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R8, 0x10002);                    // MVN+SUB.
-  __ AddConstant(R0, R8, 0x10003);                    // MOVW+MOVT+ADD.
-  __ AddConstant(R8, R8, -1);                         // 32-bit ADD, encoding T3.
-  __ AddConstant(R8, R1, -7);                         // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R8, -8);                         // 32-bit SUB, encoding T3.
-  __ AddConstant(R8, R8, -255);                       // 32-bit SUB, encoding T3.
-  __ AddConstant(R8, R1, -256);                       // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R8, -257);                       // 32-bit SUB, encoding T4.
-  __ AddConstant(R8, R8, -0xfff);                     // 32-bit SUB, encoding T4.
-  __ AddConstant(R8, R1, -0x1000);                    // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R8, -0x1001);                    // MVN+ADD.
-  __ AddConstant(R0, R1, -0x1002);                    // MOVW+SUB.
-  __ AddConstant(R8, R1, -0xffff);                    // MOVW+SUB.
-  __ AddConstant(R0, R8, -0x10000);                   // 32-bit SUB, encoding T3.
-  __ AddConstant(R8, R8, -0x10001);                   // 32-bit SUB, encoding T3.
-  __ AddConstant(R8, R1, -0x10002);                   // MVN+SUB.
-  __ AddConstant(R0, R8, -0x10003);                   // MOVW+MOVT+ADD.
-
-  // Low registers, Rd != Rn, kCcKeep.
-  __ AddConstant(R0, R1, 0, AL, kCcKeep);             // MOV.
-  __ AddConstant(R0, R1, 1, AL, kCcKeep);             // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R1, 7, AL, kCcKeep);             // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R1, 8, AL, kCcKeep);             // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R1, 255, AL, kCcKeep);           // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R1, 256, AL, kCcKeep);           // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R1, 257, AL, kCcKeep);           // 32-bit ADD, encoding T4.
-  __ AddConstant(R0, R1, 0xfff, AL, kCcKeep);         // 32-bit ADD, encoding T4.
-  __ AddConstant(R0, R1, 0x1000, AL, kCcKeep);        // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R1, 0x1001, AL, kCcKeep);        // MVN+SUB.
-  __ AddConstant(R0, R1, 0x1002, AL, kCcKeep);        // MOVW+ADD.
-  __ AddConstant(R0, R1, 0xffff, AL, kCcKeep);        // MOVW+ADD.
-  __ AddConstant(R0, R1, 0x10000, AL, kCcKeep);       // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R1, 0x10001, AL, kCcKeep);       // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R1, 0x10002, AL, kCcKeep);       // MVN+SUB.
-  __ AddConstant(R0, R1, 0x10003, AL, kCcKeep);       // MOVW+MOVT+ADD.
-  __ AddConstant(R0, R1, -1, AL, kCcKeep);            // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R1, -7, AL, kCcKeep);            // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R1, -8, AL, kCcKeep);            // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R1, -255, AL, kCcKeep);          // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R1, -256, AL, kCcKeep);          // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R1, -257, AL, kCcKeep);          // 32-bit SUB, encoding T4.
-  __ AddConstant(R0, R1, -0xfff, AL, kCcKeep);        // 32-bit SUB, encoding T4.
-  __ AddConstant(R0, R1, -0x1000, AL, kCcKeep);       // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R1, -0x1001, AL, kCcKeep);       // MVN+ADD.
-  __ AddConstant(R0, R1, -0x1002, AL, kCcKeep);       // MOVW+SUB.
-  __ AddConstant(R0, R1, -0xffff, AL, kCcKeep);       // MOVW+SUB.
-  __ AddConstant(R0, R1, -0x10000, AL, kCcKeep);      // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R1, -0x10001, AL, kCcKeep);      // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R1, -0x10002, AL, kCcKeep);      // MVN+ADD.
-  __ AddConstant(R0, R1, -0x10003, AL, kCcKeep);      // MOVW+MOVT+ADD.
-
-  // Low registers, Rd == Rn, kCcKeep.
-  __ AddConstant(R0, R0, 0, AL, kCcKeep);             // Nothing.
-  __ AddConstant(R1, R1, 1, AL, kCcKeep);             // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R0, 7, AL, kCcKeep);             // 32-bit ADD, encoding T3.
-  __ AddConstant(R1, R1, 8, AL, kCcKeep);             // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R0, 255, AL, kCcKeep);           // 32-bit ADD, encoding T3.
-  __ AddConstant(R1, R1, 256, AL, kCcKeep);           // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R0, 257, AL, kCcKeep);           // 32-bit ADD, encoding T4.
-  __ AddConstant(R1, R1, 0xfff, AL, kCcKeep);         // 32-bit ADD, encoding T4.
-  __ AddConstant(R0, R0, 0x1000, AL, kCcKeep);        // 32-bit ADD, encoding T3.
-  __ AddConstant(R1, R1, 0x1001, AL, kCcKeep);        // MVN+SUB.
-  __ AddConstant(R0, R0, 0x1002, AL, kCcKeep);        // MOVW+ADD.
-  __ AddConstant(R1, R1, 0xffff, AL, kCcKeep);        // MOVW+ADD.
-  __ AddConstant(R0, R0, 0x10000, AL, kCcKeep);       // 32-bit ADD, encoding T3.
-  __ AddConstant(R1, R1, 0x10001, AL, kCcKeep);       // 32-bit ADD, encoding T3.
-  __ AddConstant(R0, R0, 0x10002, AL, kCcKeep);       // MVN+SUB.
-  __ AddConstant(R1, R1, 0x10003, AL, kCcKeep);       // MOVW+MOVT+ADD.
-  __ AddConstant(R0, R0, -1, AL, kCcKeep);            // 32-bit ADD, encoding T3.
-  __ AddConstant(R1, R1, -7, AL, kCcKeep);            // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R0, -8, AL, kCcKeep);            // 32-bit SUB, encoding T3.
-  __ AddConstant(R1, R1, -255, AL, kCcKeep);          // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R0, -256, AL, kCcKeep);          // 32-bit SUB, encoding T3.
-  __ AddConstant(R1, R1, -257, AL, kCcKeep);          // 32-bit SUB, encoding T4.
-  __ AddConstant(R0, R0, -0xfff, AL, kCcKeep);        // 32-bit SUB, encoding T4.
-  __ AddConstant(R1, R1, -0x1000, AL, kCcKeep);       // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R0, -0x1001, AL, kCcKeep);       // MVN+ADD.
-  __ AddConstant(R1, R1, -0x1002, AL, kCcKeep);       // MOVW+SUB.
-  __ AddConstant(R0, R0, -0xffff, AL, kCcKeep);       // MOVW+SUB.
-  __ AddConstant(R1, R1, -0x10000, AL, kCcKeep);      // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R0, -0x10001, AL, kCcKeep);      // 32-bit SUB, encoding T3.
-  __ AddConstant(R1, R1, -0x10002, AL, kCcKeep);      // MVN+ADD.
-  __ AddConstant(R0, R0, -0x10003, AL, kCcKeep);      // MOVW+MOVT+ADD.
-
-  // Low registers, Rd != Rn, kCcSet.
-  __ AddConstant(R0, R1, 0, AL, kCcSet);              // 16-bit ADDS.
-  __ AddConstant(R0, R1, 1, AL, kCcSet);              // 16-bit ADDS.
-  __ AddConstant(R0, R1, 7, AL, kCcSet);              // 16-bit ADDS.
-  __ AddConstant(R0, R1, 8, AL, kCcSet);              // 32-bit ADDS, encoding T3.
-  __ AddConstant(R0, R1, 255, AL, kCcSet);            // 32-bit ADDS, encoding T3.
-  __ AddConstant(R0, R1, 256, AL, kCcSet);            // 32-bit ADDS, encoding T3.
-  __ AddConstant(R0, R1, 257, AL, kCcSet);            // MVN+SUBS.
-  __ AddConstant(R0, R1, 0xfff, AL, kCcSet);          // MOVW+ADDS.
-  __ AddConstant(R0, R1, 0x1000, AL, kCcSet);         // 32-bit ADDS, encoding T3.
-  __ AddConstant(R0, R1, 0x1001, AL, kCcSet);         // MVN+SUBS.
-  __ AddConstant(R0, R1, 0x1002, AL, kCcSet);         // MOVW+ADDS.
-  __ AddConstant(R0, R1, 0xffff, AL, kCcSet);         // MOVW+ADDS.
-  __ AddConstant(R0, R1, 0x10000, AL, kCcSet);        // 32-bit ADDS, encoding T3.
-  __ AddConstant(R0, R1, 0x10001, AL, kCcSet);        // 32-bit ADDS, encoding T3.
-  __ AddConstant(R0, R1, 0x10002, AL, kCcSet);        // MVN+SUBS.
-  __ AddConstant(R0, R1, 0x10003, AL, kCcSet);        // MOVW+MOVT+ADDS.
-  __ AddConstant(R0, R1, -1, AL, kCcSet);             // 16-bit SUBS.
-  __ AddConstant(R0, R1, -7, AL, kCcSet);             // 16-bit SUBS.
-  __ AddConstant(R0, R1, -8, AL, kCcSet);             // 32-bit SUBS, encoding T3.
-  __ AddConstant(R0, R1, -255, AL, kCcSet);           // 32-bit SUBS, encoding T3.
-  __ AddConstant(R0, R1, -256, AL, kCcSet);           // 32-bit SUBS, encoding T3.
-  __ AddConstant(R0, R1, -257, AL, kCcSet);           // MVN+ADDS.
-  __ AddConstant(R0, R1, -0xfff, AL, kCcSet);         // MOVW+SUBS.
-  __ AddConstant(R0, R1, -0x1000, AL, kCcSet);        // 32-bit SUBS, encoding T3.
-  __ AddConstant(R0, R1, -0x1001, AL, kCcSet);        // MVN+ADDS.
-  __ AddConstant(R0, R1, -0x1002, AL, kCcSet);        // MOVW+SUBS.
-  __ AddConstant(R0, R1, -0xffff, AL, kCcSet);        // MOVW+SUBS.
-  __ AddConstant(R0, R1, -0x10000, AL, kCcSet);       // 32-bit SUBS, encoding T3.
-  __ AddConstant(R0, R1, -0x10001, AL, kCcSet);       // 32-bit SUBS, encoding T3.
-  __ AddConstant(R0, R1, -0x10002, AL, kCcSet);       // MVN+ADDS.
-  __ AddConstant(R0, R1, -0x10003, AL, kCcSet);       // MOVW+MOVT+ADDS.
-
-  // Low registers, Rd == Rn, kCcSet.
-  __ AddConstant(R0, R0, 0, AL, kCcSet);              // 16-bit ADDS, encoding T2.
-  __ AddConstant(R1, R1, 1, AL, kCcSet);              // 16-bit ADDS, encoding T2.
-  __ AddConstant(R0, R0, 7, AL, kCcSet);              // 16-bit ADDS, encoding T2.
-  __ AddConstant(R1, R1, 8, AL, kCcSet);              // 16-bit ADDS, encoding T2.
-  __ AddConstant(R0, R0, 255, AL, kCcSet);            // 16-bit ADDS, encoding T2.
-  __ AddConstant(R1, R1, 256, AL, kCcSet);            // 32-bit ADDS, encoding T3.
-  __ AddConstant(R0, R0, 257, AL, kCcSet);            // MVN+SUBS.
-  __ AddConstant(R1, R1, 0xfff, AL, kCcSet);          // MOVW+ADDS.
-  __ AddConstant(R0, R0, 0x1000, AL, kCcSet);         // 32-bit ADDS, encoding T3.
-  __ AddConstant(R1, R1, 0x1001, AL, kCcSet);         // MVN+SUBS.
-  __ AddConstant(R0, R0, 0x1002, AL, kCcSet);         // MOVW+ADDS.
-  __ AddConstant(R1, R1, 0xffff, AL, kCcSet);         // MOVW+ADDS.
-  __ AddConstant(R0, R0, 0x10000, AL, kCcSet);        // 32-bit ADDS, encoding T3.
-  __ AddConstant(R1, R1, 0x10001, AL, kCcSet);        // 32-bit ADDS, encoding T3.
-  __ AddConstant(R0, R0, 0x10002, AL, kCcSet);        // MVN+SUBS.
-  __ AddConstant(R1, R1, 0x10003, AL, kCcSet);        // MOVW+MOVT+ADDS.
-  __ AddConstant(R0, R0, -1, AL, kCcSet);             // 16-bit SUBS, encoding T2.
-  __ AddConstant(R1, R1, -7, AL, kCcSet);             // 16-bit SUBS, encoding T2.
-  __ AddConstant(R0, R0, -8, AL, kCcSet);             // 16-bit SUBS, encoding T2.
-  __ AddConstant(R1, R1, -255, AL, kCcSet);           // 16-bit SUBS, encoding T2.
-  __ AddConstant(R0, R0, -256, AL, kCcSet);           // 32-bit SUB, encoding T3.
-  __ AddConstant(R1, R1, -257, AL, kCcSet);           // MNV+ADDS.
-  __ AddConstant(R0, R0, -0xfff, AL, kCcSet);         // MOVW+SUBS.
-  __ AddConstant(R1, R1, -0x1000, AL, kCcSet);        // 32-bit SUB, encoding T3.
-  __ AddConstant(R0, R0, -0x1001, AL, kCcSet);        // MVN+ADDS.
-  __ AddConstant(R1, R1, -0x1002, AL, kCcSet);        // MOVW+SUBS.
-  __ AddConstant(R0, R0, -0xffff, AL, kCcSet);        // MOVW+SUBS.
-  __ AddConstant(R1, R1, -0x10000, AL, kCcSet);       // 32-bit SUBS, encoding T3.
-  __ AddConstant(R0, R0, -0x10001, AL, kCcSet);       // 32-bit SUBS, encoding T3.
-  __ AddConstant(R1, R1, -0x10002, AL, kCcSet);       // MVN+ADDS.
-  __ AddConstant(R0, R0, -0x10003, AL, kCcSet);       // MOVW+MOVT+ADDS.
-
-  __ it(EQ);
-  __ AddConstant(R0, R1, 1, EQ, kCcSet);              // 32-bit ADDS, encoding T3.
-  __ it(NE);
-  __ AddConstant(R0, R1, 1, NE, kCcKeep);             // 16-bit ADDS, encoding T1.
-  __ it(GE);
-  __ AddConstant(R0, R0, 1, GE, kCcSet);              // 32-bit ADDS, encoding T3.
-  __ it(LE);
-  __ AddConstant(R0, R0, 1, LE, kCcKeep);             // 16-bit ADDS, encoding T2.
-
-  EmitAndCheck(&assembler, "AddConstant");
-}
-
-TEST_F(Thumb2AssemblerTest, CmpConstant) {
-  __ CmpConstant(R0, 0);                              // 16-bit CMP.
-  __ CmpConstant(R1, 1);                              // 16-bit CMP.
-  __ CmpConstant(R0, 7);                              // 16-bit CMP.
-  __ CmpConstant(R1, 8);                              // 16-bit CMP.
-  __ CmpConstant(R0, 255);                            // 16-bit CMP.
-  __ CmpConstant(R1, 256);                            // 32-bit CMP.
-  __ CmpConstant(R0, 257);                            // MNV+CMN.
-  __ CmpConstant(R1, 0xfff);                          // MOVW+CMP.
-  __ CmpConstant(R0, 0x1000);                         // 32-bit CMP.
-  __ CmpConstant(R1, 0x1001);                         // MNV+CMN.
-  __ CmpConstant(R0, 0x1002);                         // MOVW+CMP.
-  __ CmpConstant(R1, 0xffff);                         // MOVW+CMP.
-  __ CmpConstant(R0, 0x10000);                        // 32-bit CMP.
-  __ CmpConstant(R1, 0x10001);                        // 32-bit CMP.
-  __ CmpConstant(R0, 0x10002);                        // MVN+CMN.
-  __ CmpConstant(R1, 0x10003);                        // MOVW+MOVT+CMP.
-  __ CmpConstant(R0, -1);                             // 32-bit CMP.
-  __ CmpConstant(R1, -7);                             // CMN.
-  __ CmpConstant(R0, -8);                             // CMN.
-  __ CmpConstant(R1, -255);                           // CMN.
-  __ CmpConstant(R0, -256);                           // CMN.
-  __ CmpConstant(R1, -257);                           // MNV+CMP.
-  __ CmpConstant(R0, -0xfff);                         // MOVW+CMN.
-  __ CmpConstant(R1, -0x1000);                        // CMN.
-  __ CmpConstant(R0, -0x1001);                        // MNV+CMP.
-  __ CmpConstant(R1, -0x1002);                        // MOVW+CMN.
-  __ CmpConstant(R0, -0xffff);                        // MOVW+CMN.
-  __ CmpConstant(R1, -0x10000);                       // CMN.
-  __ CmpConstant(R0, -0x10001);                       // CMN.
-  __ CmpConstant(R1, -0x10002);                       // MVN+CMP.
-  __ CmpConstant(R0, -0x10003);                       // MOVW+MOVT+CMP.
-
-  __ CmpConstant(R8, 0);                              // 32-bit CMP.
-  __ CmpConstant(R9, 1);                              // 32-bit CMP.
-  __ CmpConstant(R8, 7);                              // 32-bit CMP.
-  __ CmpConstant(R9, 8);                              // 32-bit CMP.
-  __ CmpConstant(R8, 255);                            // 32-bit CMP.
-  __ CmpConstant(R9, 256);                            // 32-bit CMP.
-  __ CmpConstant(R8, 257);                            // MNV+CMN
-  __ CmpConstant(R9, 0xfff);                          // MOVW+CMP.
-  __ CmpConstant(R8, 0x1000);                         // 32-bit CMP.
-  __ CmpConstant(R9, 0x1001);                         // MVN+CMN.
-  __ CmpConstant(R8, 0x1002);                         // MOVW+CMP.
-  __ CmpConstant(R9, 0xffff);                         // MOVW+CMP.
-  __ CmpConstant(R8, 0x10000);                        // 32-bit CMP.
-  __ CmpConstant(R9, 0x10001);                        // 32-bit CMP.
-  __ CmpConstant(R8, 0x10002);                        // MVN+CMN.
-  __ CmpConstant(R9, 0x10003);                        // MOVW+MOVT+CMP.
-  __ CmpConstant(R8, -1);                             // 32-bit CMP
-  __ CmpConstant(R9, -7);                             // CMN.
-  __ CmpConstant(R8, -8);                             // CMN.
-  __ CmpConstant(R9, -255);                           // CMN.
-  __ CmpConstant(R8, -256);                           // CMN.
-  __ CmpConstant(R9, -257);                           // MNV+CMP.
-  __ CmpConstant(R8, -0xfff);                         // MOVW+CMN.
-  __ CmpConstant(R9, -0x1000);                        // CMN.
-  __ CmpConstant(R8, -0x1001);                        // MVN+CMP.
-  __ CmpConstant(R9, -0x1002);                        // MOVW+CMN.
-  __ CmpConstant(R8, -0xffff);                        // MOVW+CMN.
-  __ CmpConstant(R9, -0x10000);                       // CMN.
-  __ CmpConstant(R8, -0x10001);                       // CMN.
-  __ CmpConstant(R9, -0x10002);                       // MVN+CMP.
-  __ CmpConstant(R8, -0x10003);                       // MOVW+MOVT+CMP.
-
-  EmitAndCheck(&assembler, "CmpConstant");
-}
-
-#define ENABLE_VIXL_TEST
-
-#ifdef ENABLE_VIXL_TEST
-
-#define ARM_VIXL
-
-#ifdef ARM_VIXL
-typedef arm::ArmVIXLJNIMacroAssembler JniAssemblerType;
-#else
-typedef arm::Thumb2Assembler AssemblerType;
-#endif
-
 class ArmVIXLAssemblerTest : public ::testing::Test {
  public:
   ArmVIXLAssemblerTest() : pool(), arena(&pool), assembler(&arena) { }
 
   ArenaPool pool;
   ArenaAllocator arena;
-  JniAssemblerType assembler;
+  ArmVIXLJNIMacroAssembler assembler;
 };
 
-#undef __
 #define __ assembler->
 
-void EmitAndCheck(JniAssemblerType* assembler, const char* testname,
+void EmitAndCheck(ArmVIXLJNIMacroAssembler* assembler, const char* testname,
                   const char* const* results) {
   __ FinalizeCode();
   size_t cs = __ CodeSize();
@@ -1631,7 +197,7 @@
   DumpAndCheck(managed_code, testname, results);
 }
 
-void EmitAndCheck(JniAssemblerType* assembler, const char* testname) {
+void EmitAndCheck(ArmVIXLJNIMacroAssembler* assembler, const char* testname) {
   InitResults();
   std::map<std::string, const char* const*>::iterator results = test_results.find(testname);
   ASSERT_NE(results, test_results.end());
@@ -1640,9 +206,14 @@
 }
 
 #undef __
+
 #define __ assembler.
 
 TEST_F(ArmVIXLAssemblerTest, VixlJniHelpers) {
+  // Run the test only with Baker read barriers, as the expected
+  // generated code contains a Marking Register refresh instruction.
+  TEST_DISABLED_WITHOUT_BAKER_READ_BARRIERS();
+
   const bool is_static = true;
   const bool is_synchronized = false;
   const bool is_critical_native = false;
@@ -1729,14 +300,15 @@
   EmitAndCheck(&assembler, "VixlJniHelpers");
 }
 
-#ifdef ARM_VIXL
+#undef __
+
+// TODO: Avoid these macros.
 #define R0 vixl::aarch32::r0
 #define R2 vixl::aarch32::r2
 #define R4 vixl::aarch32::r4
 #define R12 vixl::aarch32::r12
-#undef __
+
 #define __ assembler.asm_.
-#endif
 
 TEST_F(ArmVIXLAssemblerTest, VixlLoadFromOffset) {
   __ LoadFromOffset(kLoadWord, R2, R4, 12);
@@ -1803,6 +375,5 @@
 }
 
 #undef __
-#endif  // ENABLE_VIXL_TEST
 }  // namespace arm
 }  // namespace art
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index eaaf815..0a09435 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -1,5462 +1,3 @@
-const char* const SimpleMovResults[] = {
-  "   0:	0008      	movs	r0, r1\n",
-  "   2:	4608      	mov	r0, r1\n",
-  "   4:	46c8      	mov	r8, r9\n",
-  "   6:	2001      	movs	r0, #1\n",
-  "   8:	f04f 0809 	mov.w	r8, #9\n",
-  nullptr
-};
-const char* const SimpleMov32Results[] = {
-  "   0:	ea4f 0001 	mov.w	r0, r1\n",
-  "   4:	ea4f 0809 	mov.w	r8, r9\n",
-  nullptr
-};
-const char* const SimpleMovAddResults[] = {
-  "   0:	4608      	mov	r0, r1\n",
-  "   2:	1888      	adds	r0, r1, r2\n",
-  "   4:	1c08      	adds	r0, r1, #0\n",
-  nullptr
-};
-const char* const DataProcessingRegisterResults[] = {
-  "   0:	ea6f 0001 	mvn.w	r0, r1\n",
-  "   4:	eb01 0002 	add.w	r0, r1, r2\n",
-  "   8:	eba1 0002 	sub.w	r0, r1, r2\n",
-  "   c:	ea01 0002 	and.w	r0, r1, r2\n",
-  "  10:	ea41 0002 	orr.w	r0, r1, r2\n",
-  "  14:	ea61 0002 	orn	r0, r1, r2\n",
-  "  18:	ea81 0002 	eor.w	r0, r1, r2\n",
-  "  1c:	ea21 0002 	bic.w	r0, r1, r2\n",
-  "  20:	eb41 0002 	adc.w	r0, r1, r2\n",
-  "  24:	eb61 0002 	sbc.w	r0, r1, r2\n",
-  "  28:	ebc1 0002 	rsb	r0, r1, r2\n",
-  "  2c:	ea90 0f01 	teq	r0, r1\n",
-  "  30:	0008      	movs	r0, r1\n",
-  "  32:	4608      	mov	r0, r1\n",
-  "  34:	43c8      	mvns	r0, r1\n",
-  "  36:	4408      	add	r0, r1\n",
-  "  38:	1888      	adds	r0, r1, r2\n",
-  "  3a:	1a88      	subs	r0, r1, r2\n",
-  "  3c:	4148      	adcs	r0, r1\n",
-  "  3e:	4188      	sbcs	r0, r1\n",
-  "  40:	4008      	ands	r0, r1\n",
-  "  42:	4308      	orrs	r0, r1\n",
-  "  44:	4048      	eors	r0, r1\n",
-  "  46:	4388      	bics	r0, r1\n",
-  "  48:	4208      	tst	r0, r1\n",
-  "  4a:	4288      	cmp	r0, r1\n",
-  "  4c:	42c8      	cmn	r0, r1\n",
-  "  4e:	4641		mov	r1, r8\n",
-  "  50:	4681		mov	r9, r0\n",
-  "  52:	46c8		mov	r8, r9\n",
-  "  54:	4441		add	r1, r8\n",
-  "  56:	4481		add	r9, r0\n",
-  "  58:	44c8		add	r8, r9\n",
-  "  5a:	4548		cmp	r0, r9\n",
-  "  5c:	4588		cmp	r8, r1\n",
-  "  5e:	45c1		cmp	r9, r8\n",
-  "  60:	4248   	   	negs	r0, r1\n",
-  "  62:	4240   	   	negs	r0, r0\n",
-  "  64:	ea5f 0008  	movs.w	r0, r8\n",
-  "  68:	ea7f 0008  	mvns.w	r0, r8\n",
-  "  6c:	eb01 0008 	add.w	r0, r1, r8\n",
-  "  70:	eb11 0008 	adds.w	r0, r1, r8\n",
-  "  74:	ebb1 0008 	subs.w	r0, r1, r8\n",
-  "  78:	eb50 0008 	adcs.w	r0, r0, r8\n",
-  "  7c:	eb70 0008 	sbcs.w	r0, r0, r8\n",
-  "  80:	ea10 0008 	ands.w	r0, r0, r8\n",
-  "  84:	ea50 0008 	orrs.w	r0, r0, r8\n",
-  "  88:	ea90 0008 	eors.w	r0, r0, r8\n",
-  "  8c:	ea30 0008 	bics.w	r0, r0, r8\n",
-  "  90:	ea10 0f08 	tst.w	r0, r8\n",
-  "  94:	eb10 0f08 	cmn.w	r0, r8\n",
-  "  98:	f1d8 0000 	rsbs	r0, r8, #0\n",
-  "  9c:	f1d8 0800 	rsbs	r8, r8, #0\n",
-  "  a0:	bf08       	it	eq\n",
-  "  a2:	ea7f 0001  	mvnseq.w	r0, r1\n",
-  "  a6:	bf08       	it	eq\n",
-  "  a8:	eb11 0002 	addseq.w	r0, r1, r2\n",
-  "  ac:	bf08       	it	eq\n",
-  "  ae:	ebb1 0002 	subseq.w	r0, r1, r2\n",
-  "  b2:	bf08       	it	eq\n",
-  "  b4:	eb50 0001 	adcseq.w	r0, r0, r1\n",
-  "  b8:	bf08       	it	eq\n",
-  "  ba:	eb70 0001 	sbcseq.w	r0, r0, r1\n",
-  "  be:	bf08       	it	eq\n",
-  "  c0:	ea10 0001 	andseq.w	r0, r0, r1\n",
-  "  c4:	bf08       	it	eq\n",
-  "  c6:	ea50 0001 	orrseq.w	r0, r0, r1\n",
-  "  ca:	bf08       	it	eq\n",
-  "  cc:	ea90 0001 	eorseq.w	r0, r0, r1\n",
-  "  d0:	bf08       	it	eq\n",
-  "  d2:	ea30 0001 	bicseq.w	r0, r0, r1\n",
-  "  d6:	bf08       	it	eq\n",
-  "  d8:	43c8      	mvneq	r0, r1\n",
-  "  da:	bf08       	it	eq\n",
-  "  dc:	1888      	addeq	r0, r1, r2\n",
-  "  de:	bf08       	it	eq\n",
-  "  e0:	1a88      	subeq	r0, r1, r2\n",
-  "  e2:	bf08       	it	eq\n",
-  "  e4:	4148      	adceq	r0, r1\n",
-  "  e6:	bf08       	it	eq\n",
-  "  e8:	4188      	sbceq	r0, r1\n",
-  "  ea:	bf08       	it	eq\n",
-  "  ec:	4008      	andeq	r0, r1\n",
-  "  ee:	bf08       	it	eq\n",
-  "  f0:	4308      	orreq	r0, r1\n",
-  "  f2:	bf08       	it	eq\n",
-  "  f4:	4048      	eoreq	r0, r1\n",
-  "  f6:	bf08       	it	eq\n",
-  "  f8:	4388      	biceq	r0, r1\n",
-  "  fa:	4608      	mov	r0, r1\n",
-  "  fc:	43c8      	mvns	r0, r1\n",
-  "  fe:	4408      	add	r0, r1\n",
-  " 100:	1888      	adds	r0, r1, r2\n",
-  " 102:	1a88      	subs	r0, r1, r2\n",
-  " 104:	4148      	adcs	r0, r1\n",
-  " 106:	4188      	sbcs	r0, r1\n",
-  " 108:	4008      	ands	r0, r1\n",
-  " 10a:	4308      	orrs	r0, r1\n",
-  " 10c:	4048      	eors	r0, r1\n",
-  " 10e:	4388      	bics	r0, r1\n",
-  " 110:	4641		mov	r1, r8\n",
-  " 112:	4681		mov	r9, r0\n",
-  " 114:	46c8		mov	r8, r9\n",
-  " 116:	4441		add	r1, r8\n",
-  " 118:	4481		add	r9, r0\n",
-  " 11a:	44c8		add	r8, r9\n",
-  " 11c:	4248   	   	negs	r0, r1\n",
-  " 11e:	4240   	   	negs	r0, r0\n",
-  " 120:	eb01 0c00 	add.w	ip, r1, r0\n",
-  nullptr
-};
-const char* const DataProcessingImmediateResults[] = {
-  "   0:	2055      	movs	r0, #85	; 0x55\n",
-  "   2:	f06f 0055 	mvn.w	r0, #85	; 0x55\n",
-  "   6:	f101 0055 	add.w	r0, r1, #85	; 0x55\n",
-  "   a:	f1a1 0055 	sub.w	r0, r1, #85	; 0x55\n",
-  "   e:	f001 0055 	and.w	r0, r1, #85	; 0x55\n",
-  "  12:	f041 0055 	orr.w	r0, r1, #85	; 0x55\n",
-  "  16:	f061 0055 	orn	r0, r1, #85	; 0x55\n",
-  "  1a:	f081 0055 	eor.w	r0, r1, #85	; 0x55\n",
-  "  1e:	f021 0055 	bic.w	r0, r1, #85	; 0x55\n",
-  "  22:	f141 0055 	adc.w	r0, r1, #85	; 0x55\n",
-  "  26:	f161 0055 	sbc.w	r0, r1, #85	; 0x55\n",
-  "  2a:	f1c1 0055 	rsb	r0, r1, #85	; 0x55\n",
-  "  2e:	f010 0f55 	tst.w	r0, #85	; 0x55\n",
-  "  32:	f090 0f55 	teq	r0, #85	; 0x55\n",
-  "  36:	2855      	cmp	r0, #85	; 0x55\n",
-  "  38:	f110 0f55 	cmn.w	r0, #85	; 0x55\n",
-  "  3c:	1d48      	adds	r0, r1, #5\n",
-  "  3e:	1f48      	subs	r0, r1, #5\n",
-  "  40:	2055      	movs	r0, #85	; 0x55\n",
-  "  42:	f07f 0055 	mvns.w	r0, #85	; 0x55\n",
-  "  46:	1d48      	adds	r0, r1, #5\n",
-  "  48:	1f48      	subs	r0, r1, #5\n",
-  nullptr
-};
-const char* const DataProcessingModifiedImmediateResults[] = {
-  "   0:	f04f 1055 	mov.w	r0, #5570645	; 0x550055\n",
-  "   4:	f06f 1055 	mvn.w	r0, #5570645	; 0x550055\n",
-  "   8:	f101 1055 	add.w	r0, r1, #5570645	; 0x550055\n",
-  "   c:	f1a1 1055 	sub.w	r0, r1, #5570645	; 0x550055\n",
-  "  10:	f001 1055 	and.w	r0, r1, #5570645	; 0x550055\n",
-  "  14:	f041 1055 	orr.w	r0, r1, #5570645	; 0x550055\n",
-  "  18:	f061 1055 	orn	r0, r1, #5570645	; 0x550055\n",
-  "  1c:	f081 1055 	eor.w	r0, r1, #5570645	; 0x550055\n",
-  "  20:	f021 1055 	bic.w	r0, r1, #5570645	; 0x550055\n",
-  "  24:	f141 1055 	adc.w	r0, r1, #5570645	; 0x550055\n",
-  "  28:	f161 1055 	sbc.w	r0, r1, #5570645	; 0x550055\n",
-  "  2c:	f1c1 1055 	rsb	r0, r1, #5570645	; 0x550055\n",
-  "  30:	f010 1f55 	tst.w	r0, #5570645	; 0x550055\n",
-  "  34:	f090 1f55 	teq	r0, #5570645	; 0x550055\n",
-  "  38:	f1b0 1f55 	cmp.w	r0, #5570645	; 0x550055\n",
-  "  3c:	f110 1f55 	cmn.w	r0, #5570645	; 0x550055\n",
-  nullptr
-};
-const char* const DataProcessingModifiedImmediatesResults[] = {
-  "   0:	f04f 1055 	mov.w	r0, #5570645	; 0x550055\n",
-  "   4:	f04f 2055 	mov.w	r0, #1426085120	; 0x55005500\n",
-  "   8:	f04f 3055 	mov.w	r0, #1431655765	; 0x55555555\n",
-  "   c:	f04f 4055 	mov.w	r0, #3573547008	; 0xd5000000\n",
-  "  10:	f04f 40d4 	mov.w	r0, #1778384896	; 0x6a000000\n",
-  "  14:	f44f 7054 	mov.w	r0, #848	; 0x350\n",
-  "  18:	f44f 70d4 	mov.w	r0, #424	; 0x1a8\n",
-  nullptr
-};
-const char* const DataProcessingShiftedRegisterResults[] = {
-  "   0:	0123      	lsls	r3, r4, #4\n",
-  "   2:	0963      	lsrs	r3, r4, #5\n",
-  "   4:	11a3      	asrs	r3, r4, #6\n",
-  "   6:	ea5f 13f4 	movs.w	r3, r4, ror #7\n",
-  "   a:	ea5f 0334 	movs.w	r3, r4, rrx\n",
-  "   e:	ea4f 1304 	mov.w	r3, r4, lsl #4\n",
-  "  12:	ea4f 1354 	mov.w	r3, r4, lsr #5\n",
-  "  16:	ea4f 13a4 	mov.w	r3, r4, asr #6\n",
-  "  1a:	ea4f 13f4 	mov.w	r3, r4, ror #7\n",
-  "  1e:	ea4f 0334 	mov.w	r3, r4, rrx\n",
-  "  22:	ea5f 1804 	movs.w	r8, r4, lsl #4\n",
-  "  26:	ea5f 1854 	movs.w	r8, r4, lsr #5\n",
-  "  2a:	ea5f 18a4 	movs.w	r8, r4, asr #6\n",
-  "  2e:	ea5f 18f4 	movs.w	r8, r4, ror #7\n",
-  "  32:	ea5f 0834 	movs.w	r8, r4, rrx\n",
-  nullptr
-};
-const char* const ShiftImmediateResults[] = {
-  "   0:  0123        lsls  r3, r4, #4\n",
-  "   2:  0963        lsrs  r3, r4, #5\n",
-  "   4:  11a3        asrs  r3, r4, #6\n",
-  "   6:  ea4f 13f4   mov.w  r3, r4, ror #7\n",
-  "   a:  ea4f 0334   mov.w  r3, r4, rrx\n",
-  "   e:  ea4f 1304   mov.w r3, r4, lsl #4\n",
-  "  12:  ea4f 1354   mov.w r3, r4, lsr #5\n",
-  "  16:  ea4f 13a4   mov.w r3, r4, asr #6\n",
-  "  1a:  ea4f 13f4   mov.w r3, r4, ror #7\n",
-  "  1e:  ea4f 0334   mov.w r3, r4, rrx\n",
-  "  22:  ea5f 1804   movs.w  r8, r4, lsl #4\n",
-  "  26:  ea5f 1854   movs.w  r8, r4, lsr #5\n",
-  "  2a:  ea5f 18a4   movs.w  r8, r4, asr #6\n",
-  "  2e:  ea5f 18f4   movs.w  r8, r4, ror #7\n",
-  "  32:  ea5f 0834   movs.w  r8, r4, rrx\n",
-  nullptr
-};
-const char* const BasicLoadResults[] = {
-  "   0:	69a3      	ldr	r3, [r4, #24]\n",
-  "   2:	7e23      	ldrb	r3, [r4, #24]\n",
-  "   4:	8b23      	ldrh	r3, [r4, #24]\n",
-  "   6:	f994 3018 	ldrsb.w	r3, [r4, #24]\n",
-  "   a:	f9b4 3018 	ldrsh.w	r3, [r4, #24]\n",
-  "   e:	9b06      	ldr	r3, [sp, #24]\n",
-  "  10:	f8d4 8018 	ldr.w	r8, [r4, #24]\n",
-  "  14:	f894 8018 	ldrb.w	r8, [r4, #24]\n",
-  "  18:	f8b4 8018 	ldrh.w	r8, [r4, #24]\n",
-  "  1c:	f994 8018 	ldrsb.w	r8, [r4, #24]\n",
-  "  20:	f9b4 8018 	ldrsh.w	r8, [r4, #24]\n",
-  nullptr
-};
-const char* const BasicStoreResults[] = {
-  "   0:	61a3      	str	r3, [r4, #24]\n",
-  "   2:	7623      	strb	r3, [r4, #24]\n",
-  "   4:	8323      	strh	r3, [r4, #24]\n",
-  "   6:	9306      	str	r3, [sp, #24]\n",
-  "   8:	f8c4 8018 	str.w	r8, [r4, #24]\n",
-  "   c:	f884 8018 	strb.w	r8, [r4, #24]\n",
-  "  10:	f8a4 8018 	strh.w	r8, [r4, #24]\n",
-  nullptr
-};
-const char* const ComplexLoadResults[] = {
-  "   0:	69a3      	ldr	r3, [r4, #24]\n",
-  "   2:	f854 3f18 	ldr.w	r3, [r4, #24]!\n",
-  "   6:	f854 3b18 	ldr.w	r3, [r4], #24\n",
-  "   a:	f854 3c18 	ldr.w	r3, [r4, #-24]\n",
-  "   e:	f854 3d18 	ldr.w	r3, [r4, #-24]!\n",
-  "  12:	f854 3918 	ldr.w	r3, [r4], #-24\n",
-  "  16:	7e23      	ldrb	r3, [r4, #24]\n",
-  "  18:	f814 3f18 	ldrb.w	r3, [r4, #24]!\n",
-  "  1c:	f814 3b18 	ldrb.w	r3, [r4], #24\n",
-  "  20:	f814 3c18 	ldrb.w	r3, [r4, #-24]\n",
-  "  24:	f814 3d18 	ldrb.w	r3, [r4, #-24]!\n",
-  "  28:	f814 3918 	ldrb.w	r3, [r4], #-24\n",
-  "  2c:	8b23      	ldrh	r3, [r4, #24]\n",
-  "  2e:	f834 3f18 	ldrh.w	r3, [r4, #24]!\n",
-  "  32:	f834 3b18 	ldrh.w	r3, [r4], #24\n",
-  "  36:	f834 3c18 	ldrh.w	r3, [r4, #-24]\n",
-  "  3a:	f834 3d18 	ldrh.w	r3, [r4, #-24]!\n",
-  "  3e:	f834 3918 	ldrh.w	r3, [r4], #-24\n",
-  "  42:	f994 3018 	ldrsb.w	r3, [r4, #24]\n",
-  "  46:	f914 3f18 	ldrsb.w	r3, [r4, #24]!\n",
-  "  4a:	f914 3b18 	ldrsb.w	r3, [r4], #24\n",
-  "  4e:	f914 3c18 	ldrsb.w	r3, [r4, #-24]\n",
-  "  52:	f914 3d18 	ldrsb.w	r3, [r4, #-24]!\n",
-  "  56:	f914 3918 	ldrsb.w	r3, [r4], #-24\n",
-  "  5a:	f9b4 3018 	ldrsh.w	r3, [r4, #24]\n",
-  "  5e:	f934 3f18 	ldrsh.w	r3, [r4, #24]!\n",
-  "  62:	f934 3b18 	ldrsh.w	r3, [r4], #24\n",
-  "  66:	f934 3c18 	ldrsh.w	r3, [r4, #-24]\n",
-  "  6a:	f934 3d18 	ldrsh.w	r3, [r4, #-24]!\n",
-  "  6e:	f934 3918 	ldrsh.w	r3, [r4], #-24\n",
-  nullptr
-};
-const char* const ComplexStoreResults[] = {
-  "   0:	61a3      	str	r3, [r4, #24]\n",
-  "   2:	f844 3f18 	str.w	r3, [r4, #24]!\n",
-  "   6:	f844 3b18 	str.w	r3, [r4], #24\n",
-  "   a:	f844 3c18 	str.w	r3, [r4, #-24]\n",
-  "   e:	f844 3d18 	str.w	r3, [r4, #-24]!\n",
-  "  12:	f844 3918 	str.w	r3, [r4], #-24\n",
-  "  16:	7623      	strb	r3, [r4, #24]\n",
-  "  18:	f804 3f18 	strb.w	r3, [r4, #24]!\n",
-  "  1c:	f804 3b18 	strb.w	r3, [r4], #24\n",
-  "  20:	f804 3c18 	strb.w	r3, [r4, #-24]\n",
-  "  24:	f804 3d18 	strb.w	r3, [r4, #-24]!\n",
-  "  28:	f804 3918 	strb.w	r3, [r4], #-24\n",
-  "  2c:	8323      	strh	r3, [r4, #24]\n",
-  "  2e:	f824 3f18 	strh.w	r3, [r4, #24]!\n",
-  "  32:	f824 3b18 	strh.w	r3, [r4], #24\n",
-  "  36:	f824 3c18 	strh.w	r3, [r4, #-24]\n",
-  "  3a:	f824 3d18 	strh.w	r3, [r4, #-24]!\n",
-  "  3e:	f824 3918 	strh.w	r3, [r4], #-24\n",
-  nullptr
-};
-const char* const NegativeLoadStoreResults[] = {
-  "   0:	f854 3c18 	ldr.w	r3, [r4, #-24]\n",
-  "   4:	f854 3d18 	ldr.w	r3, [r4, #-24]!\n",
-  "   8:	f854 3918 	ldr.w	r3, [r4], #-24\n",
-  "   c:	f854 3e18 	ldrt	r3, [r4, #24]\n",
-  "  10:	f854 3f18 	ldr.w	r3, [r4, #24]!\n",
-  "  14:	f854 3b18 	ldr.w	r3, [r4], #24\n",
-  "  18:	f814 3c18 	ldrb.w	r3, [r4, #-24]\n",
-  "  1c:	f814 3d18 	ldrb.w	r3, [r4, #-24]!\n",
-  "  20:	f814 3918 	ldrb.w	r3, [r4], #-24\n",
-  "  24:	f814 3e18 	ldrbt	r3, [r4, #24]\n",
-  "  28:	f814 3f18 	ldrb.w	r3, [r4, #24]!\n",
-  "  2c:	f814 3b18 	ldrb.w	r3, [r4], #24\n",
-  "  30:	f834 3c18 	ldrh.w	r3, [r4, #-24]\n",
-  "  34:	f834 3d18 	ldrh.w	r3, [r4, #-24]!\n",
-  "  38:	f834 3918 	ldrh.w	r3, [r4], #-24\n",
-  "  3c:	f834 3e18 	ldrht	r3, [r4, #24]\n",
-  "  40:	f834 3f18 	ldrh.w	r3, [r4, #24]!\n",
-  "  44:	f834 3b18 	ldrh.w	r3, [r4], #24\n",
-  "  48:	f914 3c18 	ldrsb.w	r3, [r4, #-24]\n",
-  "  4c:	f914 3d18 	ldrsb.w	r3, [r4, #-24]!\n",
-  "  50:	f914 3918 	ldrsb.w	r3, [r4], #-24\n",
-  "  54:	f914 3e18 	ldrsbt	r3, [r4, #24]\n",
-  "  58:	f914 3f18 	ldrsb.w	r3, [r4, #24]!\n",
-  "  5c:	f914 3b18 	ldrsb.w	r3, [r4], #24\n",
-  "  60:	f934 3c18 	ldrsh.w	r3, [r4, #-24]\n",
-  "  64:	f934 3d18 	ldrsh.w	r3, [r4, #-24]!\n",
-  "  68:	f934 3918 	ldrsh.w	r3, [r4], #-24\n",
-  "  6c:	f934 3e18 	ldrsht	r3, [r4, #24]\n",
-  "  70:	f934 3f18 	ldrsh.w	r3, [r4, #24]!\n",
-  "  74:	f934 3b18 	ldrsh.w	r3, [r4], #24\n",
-  "  78:	f844 3c18 	str.w	r3, [r4, #-24]\n",
-  "  7c:	f844 3d18 	str.w	r3, [r4, #-24]!\n",
-  "  80:	f844 3918 	str.w	r3, [r4], #-24\n",
-  "  84:	f844 3e18 	strt	r3, [r4, #24]\n",
-  "  88:	f844 3f18 	str.w	r3, [r4, #24]!\n",
-  "  8c:	f844 3b18 	str.w	r3, [r4], #24\n",
-  "  90:	f804 3c18 	strb.w	r3, [r4, #-24]\n",
-  "  94:	f804 3d18 	strb.w	r3, [r4, #-24]!\n",
-  "  98:	f804 3918 	strb.w	r3, [r4], #-24\n",
-  "  9c:	f804 3e18 	strbt	r3, [r4, #24]\n",
-  "  a0:	f804 3f18 	strb.w	r3, [r4, #24]!\n",
-  "  a4:	f804 3b18 	strb.w	r3, [r4], #24\n",
-  "  a8:	f824 3c18 	strh.w	r3, [r4, #-24]\n",
-  "  ac:	f824 3d18 	strh.w	r3, [r4, #-24]!\n",
-  "  b0:	f824 3918 	strh.w	r3, [r4], #-24\n",
-  "  b4:	f824 3e18 	strht	r3, [r4, #24]\n",
-  "  b8:	f824 3f18 	strh.w	r3, [r4, #24]!\n",
-  "  bc:	f824 3b18 	strh.w	r3, [r4], #24\n",
-  nullptr
-};
-const char* const SimpleLoadStoreDualResults[] = {
-  "   0:	e9c0 2306 	strd	r2, r3, [r0, #24]\n",
-  "   4:	e9d0 2306 	ldrd	r2, r3, [r0, #24]\n",
-  nullptr
-};
-const char* const ComplexLoadStoreDualResults[] = {
-  "   0:	e9c0 2306 	strd	r2, r3, [r0, #24]\n",
-  "   4:	e9e0 2306 	strd	r2, r3, [r0, #24]!\n",
-  "   8:	e8e0 2306 	strd	r2, r3, [r0], #24\n",
-  "   c:	e940 2306 	strd	r2, r3, [r0, #-24]\n",
-  "  10:	e960 2306 	strd	r2, r3, [r0, #-24]!\n",
-  "  14:	e860 2306 	strd	r2, r3, [r0], #-24\n",
-  "  18:	e9d0 2306 	ldrd	r2, r3, [r0, #24]\n",
-  "  1c:	e9f0 2306 	ldrd	r2, r3, [r0, #24]!\n",
-  "  20:	e8f0 2306 	ldrd	r2, r3, [r0], #24\n",
-  "  24:	e950 2306 	ldrd	r2, r3, [r0, #-24]\n",
-  "  28:	e970 2306 	ldrd	r2, r3, [r0, #-24]!\n",
-  "  2c:	e870 2306 	ldrd	r2, r3, [r0], #-24\n",
-  nullptr
-};
-const char* const NegativeLoadStoreDualResults[] = {
-  "   0:	e940 2306 	strd	r2, r3, [r0, #-24]\n",
-  "   4:	e960 2306 	strd	r2, r3, [r0, #-24]!\n",
-  "   8:	e860 2306 	strd	r2, r3, [r0], #-24\n",
-  "   c:	e9c0 2306 	strd	r2, r3, [r0, #24]\n",
-  "  10:	e9e0 2306 	strd	r2, r3, [r0, #24]!\n",
-  "  14:	e8e0 2306 	strd	r2, r3, [r0], #24\n",
-  "  18:	e950 2306 	ldrd	r2, r3, [r0, #-24]\n",
-  "  1c:	e970 2306 	ldrd	r2, r3, [r0, #-24]!\n",
-  "  20:	e870 2306 	ldrd	r2, r3, [r0], #-24\n",
-  "  24:	e9d0 2306 	ldrd	r2, r3, [r0, #24]\n",
-  "  28:	e9f0 2306 	ldrd	r2, r3, [r0, #24]!\n",
-  "  2c:	e8f0 2306 	ldrd	r2, r3, [r0], #24\n",
-  nullptr
-};
-const char* const SimpleBranchResults[] = {
-  "   0:	2002      	movs	r0, #2\n",
-  "   2:	2101      	movs	r1, #1\n",
-  "   4:	e7fd      	b.n	2 <SimpleBranch+0x2>\n",
-  "   6:	e000      	b.n	a <SimpleBranch+0xa>\n",
-  "   8:	2102      	movs	r1, #2\n",
-  "   a:	2003      	movs	r0, #3\n",
-  "   c:	2002      	movs	r0, #2\n",
-  "   e:	2101      	movs	r1, #1\n",
-  "  10:	d0fd      	beq.n	e <SimpleBranch+0xe>\n",
-  "  12:	d000      	beq.n	16 <SimpleBranch+0x16>\n",
-  "  14:	2102      	movs	r1, #2\n",
-  "  16:	2003      	movs	r0, #3\n",
-  "  18:	e002      	b.n	20 <SimpleBranch+0x20>\n",
-  "  1a:	2104      	movs	r1, #4\n",
-  "  1c:	e000      	b.n	20 <SimpleBranch+0x20>\n",
-  "  1e:	2105      	movs	r1, #5\n",
-  "  20:	2006      	movs	r0, #6\n",
-  nullptr
-};
-const char* const LongBranchResults[] = {
-  "   0:	f04f 0002 	mov.w	r0, #2\n",
-  "   4:	f04f 0101 	mov.w	r1, #1\n",
-  "   8:	f7ff bffc 	b.w	4 <LongBranch+0x4>\n",
-  "   c:	f000 b802 	b.w	14 <LongBranch+0x14>\n",
-  "  10:	f04f 0102 	mov.w	r1, #2\n",
-  "  14:	f04f 0003 	mov.w	r0, #3\n",
-  "  18:	f04f 0002 	mov.w	r0, #2\n",
-  "  1c:	f04f 0101 	mov.w	r1, #1\n",
-  "  20:	f43f affc 	beq.w	1c <LongBranch+0x1c>\n",
-  "  24:	f000 8002 	beq.w	2c <LongBranch+0x2c>\n",
-  "  28:	f04f 0102 	mov.w	r1, #2\n",
-  "  2c:	f04f 0003 	mov.w	r0, #3\n",
-  "  30:	f000 b806 	b.w	40 <LongBranch+0x40>\n",
-  "  34:	f04f 0104 	mov.w	r1, #4\n",
-  "  38:	f000 b802 	b.w	40 <LongBranch+0x40>\n",
-  "  3c:	f04f 0105 	mov.w	r1, #5\n",
-  "  40:	f04f 0006 	mov.w	r0, #6\n",
-  nullptr
-};
-const char* const LoadMultipleResults[] = {
-  "   0:	cc09      	ldmia	r4!, {r0, r3}\n",
-  "   2:	e934 4800 	ldmdb	r4!, {fp, lr}\n",
-  "   6:	e914 4800 	ldmdb	r4, {fp, lr}\n",
-  "   a:	f854 5b04 	ldr.w	r5, [r4], #4\n",
-  nullptr
-};
-const char* const StoreMultipleResults[] = {
-  "   0:	c409      	stmia	r4!, {r0, r3}\n",
-  "   2:	e8a4 4800 	stmia.w	r4!, {fp, lr}\n",
-  "   6:	e884 4800 	stmia.w	r4, {fp, lr}\n",
-  "   a:	f844 5c04 	str.w	r5, [r4, #-4]\n",
-  "   e:	f844 5d04 	str.w	r5, [r4, #-4]!\n",
-  nullptr
-};
-const char* const MovWMovTResults[] = {
-  "   0:	f240 0400 	movw  r4, #0\n",
-  "   4:	f240 0434 	movw  r4, #52 ; 0x34\n",
-  "   8:	f240 0934 	movw	r9, #52	; 0x34\n",
-  "   c:	f241 2334 	movw	r3, #4660	; 0x1234\n",
-  "  10:	f64f 79ff 	movw	r9, #65535	; 0xffff\n",
-  "  14:	f2c0 0000 	movt	r0, #0\n",
-  "  18:	f2c1 2034 	movt	r0, #4660	; 0x1234\n",
-  "  1c:	f6cf 71ff 	movt	r1, #65535	; 0xffff\n",
-  nullptr
-};
-const char* const SpecialAddSubResults[] = {
-  "   0:	aa14      	add	r2, sp, #80	; 0x50\n",
-  "   2:	b014      	add	sp, #80		; 0x50\n",
-  "   4:	f10d 0850 	add.w	r8, sp, #80	; 0x50\n",
-  "   8:	f50d 6270 	add.w	r2, sp, #3840	; 0xf00\n",
-  "   c:	f50d 6d70 	add.w	sp, sp, #3840	; 0xf00\n",
-  "  10:	f60d 7dfc 	addw	sp, sp, #4092	; 0xffc\n",
-  "  14:	b094      	sub	sp, #80		; 0x50\n",
-  "  16:	f1ad 0050 	sub.w	r0, sp, #80	; 0x50\n",
-  "  1a:	f1ad 0850 	sub.w	r8, sp, #80	; 0x50\n",
-  "  1e:	f5ad 6d70 	sub.w	sp, sp, #3840	; 0xf00\n",
-  "  22:	f6ad 7dfc 	subw	sp, sp, #4092	; 0xffc\n",
-  nullptr
-};
-const char* const LoadFromOffsetResults[] = {
-  "   0:	68e2      	ldr	r2, [r4, #12]\n",
-  "   2:	f8d4 2fff 	ldr.w	r2, [r4, #4095]	; 0xfff\n",
-  "   6:	f504 5280 	add.w	r2, r4, #4096	; 0x1000\n",
-  "   a:	6812      	ldr	r2, [r2, #0]\n",
-  "   c:	f504 1280 	add.w	r2, r4, #1048576	; 0x100000\n",
-  "  10:	f8d2 20a4 	ldr.w	r2, [r2, #164]	; 0xa4\n",
-  "  14:	f241 0200 	movw	r2, #4096	; 0x1000\n",
-  "  18:	f2c0 0210 	movt	r2, #16\n",
-  "  1c:	4422      	add	r2, r4\n",
-  "  1e:	6812      	ldr	r2, [r2, #0]\n",
-  "  20:	f241 0c00 	movw	ip, #4096	; 0x1000\n",
-  "  24:	f2c0 0c10 	movt	ip, #16\n",
-  "  28:	4464      	add	r4, ip\n",
-  "  2a:	6824      	ldr	r4, [r4, #0]\n",
-  "  2c:	89a2      	ldrh	r2, [r4, #12]\n",
-  "  2e:	f8b4 2fff 	ldrh.w	r2, [r4, #4095]	; 0xfff\n",
-  "  32:	f504 5280 	add.w	r2, r4, #4096	; 0x1000\n",
-  "  36:	8812      	ldrh	r2, [r2, #0]\n",
-  "  38:	f504 1280 	add.w	r2, r4, #1048576	; 0x100000\n",
-  "  3c:	f8b2 20a4 	ldrh.w	r2, [r2, #164]	; 0xa4\n",
-  "  40:	f241 0200 	movw	r2, #4096	; 0x1000\n",
-  "  44:	f2c0 0210 	movt	r2, #16\n",
-  "  48:	4422      	add	r2, r4\n",
-  "  4a:	8812      	ldrh	r2, [r2, #0]\n",
-  "  4c:	f241 0c00 	movw	ip, #4096	; 0x1000\n",
-  "  50:	f2c0 0c10 	movt	ip, #16\n",
-  "  54:	4464      	add	r4, ip\n",
-  "  56:	8824      	ldrh	r4, [r4, #0]\n",
-  "  58:	e9d4 2303 	ldrd	r2, r3, [r4, #12]\n",
-  "  5c:	e9d4 23ff 	ldrd	r2, r3, [r4, #1020]	; 0x3fc\n",
-  "  60:	f504 6280 	add.w	r2, r4, #1024	; 0x400\n",
-  "  64:	e9d2 2300 	ldrd	r2, r3, [r2]\n",
-  "  68:	f504 2280 	add.w	r2, r4, #262144	; 0x40000\n",
-  "  6c:	e9d2 2329 	ldrd	r2, r3, [r2, #164];	0xa4\n",
-  "  70:	f240 4200 	movw	r2, #1024	; 0x400\n",
-  "  74:	f2c0 0204 	movt	r2, #4\n",
-  "  78:	4422      	add	r2, r4\n",
-  "  7a:	e9d2 2300 	ldrd	r2, r3, [r2]\n",
-  "  7e:	f240 4c00 	movw	ip, #1024	; 0x400\n",
-  "  82:	f2c0 0c04 	movt	ip, #4\n",
-  "  86:	4464      	add	r4, ip\n",
-  "  88:	e9d4 4500 	ldrd	r4, r5, [r4]\n",
-  "  8c:	f8dc 000c 	ldr.w	r0, [ip, #12]\n",
-  "  90:	f5a4 1280 	sub.w	r2, r4, #1048576	; 0x100000\n",
-  "  94:	f8d2 20a4 	ldr.w	r2, [r2, #164]	; 0xa4\n",
-  "  98:	f994 200c 	ldrsb.w	r2, [r4, #12]\n",
-  "  9c:	7b22      	ldrb	r2, [r4, #12]\n",
-  "  9e:	f9b4 200c 	ldrsh.w	r2, [r4, #12]\n",
-  nullptr
-};
-const char* const StoreToOffsetResults[] = {
-  "   0:	60e2      	str	r2, [r4, #12]\n",
-  "   2:	f8c4 2fff 	str.w	r2, [r4, #4095]	; 0xfff\n",
-  "   6:	f504 5c80 	add.w	ip, r4, #4096	; 0x1000\n",
-  "   a:	f8cc 2000 	str.w	r2, [ip]\n",
-  "   e:	f504 1c80 	add.w	ip, r4, #1048576	; 0x100000\n",
-  "  12:	f8cc 20a4 	str.w	r2, [ip, #164]	; 0xa4\n",
-  "  16:	f241 0c00 	movw	ip, #4096	; 0x1000\n",
-  "  1a:	f2c0 0c10 	movt	ip, #16\n",
-  "  1e:	44a4      	add	ip, r4\n",
-  "  20:	f8cc 2000 	str.w	r2, [ip]\n",
-  "  24:	f241 0c00 	movw	ip, #4096	; 0x1000\n",
-  "  28:	f2c0 0c10 	movt	ip, #16\n",
-  "  2c:	44a4      	add	ip, r4\n",
-  "  2e:	f8cc 4000 	str.w	r4, [ip]\n",
-  "  32:	81a2      	strh	r2, [r4, #12]\n",
-  "  34:	f8a4 2fff 	strh.w	r2, [r4, #4095]	; 0xfff\n",
-  "  38:	f504 5c80 	add.w	ip, r4, #4096	; 0x1000\n",
-  "  3c:	f8ac 2000 	strh.w	r2, [ip]\n",
-  "  40:	f504 1c80 	add.w	ip, r4, #1048576	; 0x100000\n",
-  "  44:	f8ac 20a4 	strh.w	r2, [ip, #164]	; 0xa4\n",
-  "  48:	f241 0c00 	movw	ip, #4096	; 0x1000\n",
-  "  4c:	f2c0 0c10 	movt	ip, #16\n",
-  "  50:	44a4      	add	ip, r4\n",
-  "  52:	f8ac 2000 	strh.w	r2, [ip]\n",
-  "  56:	f241 0c00 	movw	ip, #4096	; 0x1000\n",
-  "  5a:	f2c0 0c10 	movt	ip, #16\n",
-  "  5e:	44a4      	add	ip, r4\n",
-  "  60:	f8ac 4000 	strh.w	r4, [ip]\n",
-  "  64:	e9c4 2303 	strd	r2, r3, [r4, #12]\n",
-  "  68:	e9c4 23ff 	strd	r2, r3, [r4, #1020]	; 0x3fc\n",
-  "  6c:	f504 6c80 	add.w	ip, r4, #1024	; 0x400\n",
-  "  70:	e9cc 2300 	strd	r2, r3, [ip]\n",
-  "  74:	f504 2c80 	add.w	ip, r4, #262144	; 0x40000\n",
-  "  78:	e9cc 2329 	strd	r2, r3, [ip, #164];	0xa4\n",
-  "  7c:	f240 4c00 	movw	ip, #1024	; 0x400\n",
-  "  80:	f2c0 0c04 	movt	ip, #4\n",
-  "  84:	44a4      	add	ip, r4\n",
-  "  86:	e9cc 2300 	strd	r2, r3, [ip]\n",
-  "  8a:	f240 4c00 	movw	ip, #1024	; 0x400\n",
-  "  8e:	f2c0 0c04 	movt	ip, #4\n",
-  "  92:	44a4      	add	ip, r4\n",
-  "  94:	e9cc 4500 	strd	r4, r5, [ip]\n",
-  "  98:	f8cc 000c 	str.w	r0, [ip, #12]\n",
-  "  9c:	f5a4 1c80 	sub.w	ip, r4, #1048576	; 0x100000\n",
-  "  a0:	f8cc 20a4 	str.w	r2, [ip, #164]	; 0xa4\n",
-  "  a4:	7322      	strb	r2, [r4, #12]\n",
-  nullptr
-};
-const char* const IfThenResults[] = {
-  "   0:	bf08      	it	eq\n",
-  "   2:	2101      	moveq	r1, #1\n",
-  "   4:	bf04      	itt	eq\n",
-  "   6:	2101      	moveq	r1, #1\n",
-  "   8:	2202      	moveq	r2, #2\n",
-  "   a:	bf0c      	ite	eq\n",
-  "   c:	2101      	moveq	r1, #1\n",
-  "   e:	2202      	movne	r2, #2\n",
-  "  10:	bf06      	itte	eq\n",
-  "  12:	2101      	moveq	r1, #1\n",
-  "  14:	2202      	moveq	r2, #2\n",
-  "  16:	2303      	movne	r3, #3\n",
-  "  18:	bf0e      	itee	eq\n",
-  "  1a:	2101      	moveq	r1, #1\n",
-  "  1c:	2202      	movne	r2, #2\n",
-  "  1e:	2303      	movne	r3, #3\n",
-  "  20:	bf03      	ittte	eq\n",
-  "  22:	2101      	moveq	r1, #1\n",
-  "  24:	2202      	moveq	r2, #2\n",
-  "  26:	2303      	moveq	r3, #3\n",
-  "  28:	2404      	movne	r4, #4\n",
-  nullptr
-};
-const char* const CbzCbnzResults[] = {
-  "   0:	b10a      	cbz	r2, 6 <CbzCbnz+0x6>\n",
-  "   2:	2103      	movs	r1, #3\n",
-  "   4:	2203      	movs	r2, #3\n",
-  "   6:	2204      	movs	r2, #4\n",
-  "   8:	b912      	cbnz	r2, 10 <CbzCbnz+0x10>\n",
-  "   a:	f04f 0803 	mov.w	r8, #3\n",
-  "   e:	2203      	movs	r2, #3\n",
-  "  10:	2204      	movs	r2, #4\n",
-  nullptr
-};
-const char* const MultiplyResults[] = {
-  "   0:	4348      	muls	r0, r1\n",
-  "   2:	fb01 f002 	mul.w	r0, r1, r2\n",
-  "   6:	fb09 f808 	mul.w	r8, r9, r8\n",
-  "   a:	fb09 f80a 	mul.w	r8, r9, sl\n",
-  "   e:	fb01 3002 	mla	r0, r1, r2, r3\n",
-  "  12:	fb09 9808 	mla	r8, r9, r8, r9\n",
-  "  16:	fb01 3012 	mls	r0, r1, r2, r3\n",
-  "  1a:	fb09 9818 	mls	r8, r9, r8, r9\n",
-  "  1e:	fba2 0103 	umull	r0, r1, r2, r3\n",
-  "  22:	fbaa 890b 	umull	r8, r9, sl, fp\n",
-  nullptr
-};
-const char* const DivideResults[] = {
-  "   0:	fb91 f0f2 	sdiv	r0, r1, r2\n",
-  "   4:	fb99 f8fa 	sdiv	r8, r9, sl\n",
-  "   8:	fbb1 f0f2 	udiv	r0, r1, r2\n",
-  "   c:	fbb9 f8fa 	udiv	r8, r9, sl\n",
-  nullptr
-};
-const char* const VMovResults[] = {
-  "   0:	eef7 0a00 	vmov.f32	s1, #112	; 0x70\n",
-  "   4:	eeb7 1b00 	vmov.f64	d1, #112	; 0x70\n",
-  "   8:	eef0 0a41 	vmov.f32	s1, s2\n",
-  "   c:	eeb0 1b42 	vmov.f64	d1, d2\n",
-  nullptr
-};
-const char* const BasicFloatingPointResults[] = {
-  "   0:	ee30 0a81 	vadd.f32	s0, s1, s2\n",
-  "   4:	ee30 0ac1 	vsub.f32	s0, s1, s2\n",
-  "   8:	ee20 0a81 	vmul.f32	s0, s1, s2\n",
-  "   c:	ee00 0a81 	vmla.f32	s0, s1, s2\n",
-  "  10:	ee00 0ac1 	vmls.f32	s0, s1, s2\n",
-  "  14:	ee80 0a81 	vdiv.f32	s0, s1, s2\n",
-  "  18:	eeb0 0ae0 	vabs.f32	s0, s1\n",
-  "  1c:	eeb1 0a60 	vneg.f32	s0, s1\n",
-  "  20:	eeb1 0ae0 	vsqrt.f32	s0, s1\n",
-  "  24:	ee31 0b02 	vadd.f64	d0, d1, d2\n",
-  "  28:	ee31 0b42 	vsub.f64	d0, d1, d2\n",
-  "  2c:	ee21 0b02 	vmul.f64	d0, d1, d2\n",
-  "  30:	ee01 0b02 	vmla.f64	d0, d1, d2\n",
-  "  34:	ee01 0b42 	vmls.f64	d0, d1, d2\n",
-  "  38:	ee81 0b02 	vdiv.f64	d0, d1, d2\n",
-  "  3c:	eeb0 0bc1 	vabs.f64	d0, d1\n",
-  "  40:	eeb1 0b41 	vneg.f64	d0, d1\n",
-  "  44:	eeb1 0bc1 	vsqrt.f64	d0, d1\n",
-  nullptr
-};
-const char* const FloatingPointConversionsResults[] = {
-  "   0:	eeb7 1bc2 	vcvt.f32.f64	s2, d2\n",
-  "   4:	eeb7 2ac1 	vcvt.f64.f32	d2, s2\n",
-  "   8:	eefd 0ac1 	vcvt.s32.f32	s1, s2\n",
-  "   c:	eef8 0ac1 	vcvt.f32.s32	s1, s2\n",
-  "  10:	eefd 0bc2 	vcvt.s32.f64	s1, d2\n",
-  "  14:	eeb8 1bc1 	vcvt.f64.s32	d1, s2\n",
-  "  18:	eefc 0ac1 	vcvt.u32.f32	s1, s2\n",
-  "  1c:	eef8 0a41 	vcvt.f32.u32	s1, s2\n",
-  "  20:	eefc 0bc2 	vcvt.u32.f64	s1, d2\n",
-  "  24:	eeb8 1b41 	vcvt.f64.u32	d1, s2\n",
-  nullptr
-};
-const char* const FloatingPointComparisonsResults[] = {
-  "   0:	eeb4 0a60 	vcmp.f32	s0, s1\n",
-  "   4:	eeb4 0b41 	vcmp.f64	d0, d1\n",
-  "   8:	eeb5 1a40 	vcmp.f32	s2, #0.0\n",
-  "   c:	eeb5 2b40 	vcmp.f64	d2, #0.0\n",
-  nullptr
-};
-const char* const CallsResults[] = {
-  "   0:	47f0      	blx	lr\n",
-  "   2:	4770      	bx	lr\n",
-  nullptr
-};
-const char* const BreakpointResults[] = {
-  "   0:	be00      	bkpt	0x0000\n",
-  nullptr
-};
-const char* const StrR1Results[] = {
-  "   0:	9111      	str	r1, [sp, #68]	; 0x44\n",
-  "   2:	f8cd 142c 	str.w	r1, [sp, #1068]	; 0x42c\n",
-  nullptr
-};
-const char* const VPushPopResults[] = {
-  "   0:	ed2d 1a04 	vpush	{s2-s5}\n",
-  "   4:	ed2d 2b08 	vpush	{d2-d5}\n",
-  "   8:	ecbd 1a04 	vpop	{s2-s5}\n",
-  "   c:	ecbd 2b08 	vpop	{d2-d5}\n",
-  nullptr
-};
-const char* const Max16BitBranchResults[] = {
-  "   0:	e3ff      	b.n	802 <Max16BitBranch+0x802>\n",
-  "   2:	2300      	movs	r3, #0\n",
-  "   4:	2302      	movs	r3, #2\n",
-  "   6:	2304      	movs	r3, #4\n",
-  "   8:	2306      	movs	r3, #6\n",
-  "   a:	2308      	movs	r3, #8\n",
-  "   c:	230a      	movs	r3, #10\n",
-  "   e:	230c      	movs	r3, #12\n",
-  "  10:	230e      	movs	r3, #14\n",
-  "  12:	2310      	movs	r3, #16\n",
-  "  14:	2312      	movs	r3, #18\n",
-  "  16:	2314      	movs	r3, #20\n",
-  "  18:	2316      	movs	r3, #22\n",
-  "  1a:	2318      	movs	r3, #24\n",
-  "  1c:	231a      	movs	r3, #26\n",
-  "  1e:	231c      	movs	r3, #28\n",
-  "  20:	231e      	movs	r3, #30\n",
-  "  22:	2320      	movs	r3, #32\n",
-  "  24:	2322      	movs	r3, #34	; 0x22\n",
-  "  26:	2324      	movs	r3, #36	; 0x24\n",
-  "  28:	2326      	movs	r3, #38	; 0x26\n",
-  "  2a:	2328      	movs	r3, #40	; 0x28\n",
-  "  2c:	232a      	movs	r3, #42	; 0x2a\n",
-  "  2e:	232c      	movs	r3, #44	; 0x2c\n",
-  "  30:	232e      	movs	r3, #46	; 0x2e\n",
-  "  32:	2330      	movs	r3, #48	; 0x30\n",
-  "  34:	2332      	movs	r3, #50	; 0x32\n",
-  "  36:	2334      	movs	r3, #52	; 0x34\n",
-  "  38:	2336      	movs	r3, #54	; 0x36\n",
-  "  3a:	2338      	movs	r3, #56	; 0x38\n",
-  "  3c:	233a      	movs	r3, #58	; 0x3a\n",
-  "  3e:	233c      	movs	r3, #60	; 0x3c\n",
-  "  40:	233e      	movs	r3, #62	; 0x3e\n",
-  "  42:	2340      	movs	r3, #64	; 0x40\n",
-  "  44:	2342      	movs	r3, #66	; 0x42\n",
-  "  46:	2344      	movs	r3, #68	; 0x44\n",
-  "  48:	2346      	movs	r3, #70	; 0x46\n",
-  "  4a:	2348      	movs	r3, #72	; 0x48\n",
-  "  4c:	234a      	movs	r3, #74	; 0x4a\n",
-  "  4e:	234c      	movs	r3, #76	; 0x4c\n",
-  "  50:	234e      	movs	r3, #78	; 0x4e\n",
-  "  52:	2350      	movs	r3, #80	; 0x50\n",
-  "  54:	2352      	movs	r3, #82	; 0x52\n",
-  "  56:	2354      	movs	r3, #84	; 0x54\n",
-  "  58:	2356      	movs	r3, #86	; 0x56\n",
-  "  5a:	2358      	movs	r3, #88	; 0x58\n",
-  "  5c:	235a      	movs	r3, #90	; 0x5a\n",
-  "  5e:	235c      	movs	r3, #92	; 0x5c\n",
-  "  60:	235e      	movs	r3, #94	; 0x5e\n",
-  "  62:	2360      	movs	r3, #96	; 0x60\n",
-  "  64:	2362      	movs	r3, #98	; 0x62\n",
-  "  66:	2364      	movs	r3, #100	; 0x64\n",
-  "  68:	2366      	movs	r3, #102	; 0x66\n",
-  "  6a:	2368      	movs	r3, #104	; 0x68\n",
-  "  6c:	236a      	movs	r3, #106	; 0x6a\n",
-  "  6e:	236c      	movs	r3, #108	; 0x6c\n",
-  "  70:	236e      	movs	r3, #110	; 0x6e\n",
-  "  72:	2370      	movs	r3, #112	; 0x70\n",
-  "  74:	2372      	movs	r3, #114	; 0x72\n",
-  "  76:	2374      	movs	r3, #116	; 0x74\n",
-  "  78:	2376      	movs	r3, #118	; 0x76\n",
-  "  7a:	2378      	movs	r3, #120	; 0x78\n",
-  "  7c:	237a      	movs	r3, #122	; 0x7a\n",
-  "  7e:	237c      	movs	r3, #124	; 0x7c\n",
-  "  80:	237e      	movs	r3, #126	; 0x7e\n",
-  "  82:	2380      	movs	r3, #128	; 0x80\n",
-  "  84:	2382      	movs	r3, #130	; 0x82\n",
-  "  86:	2384      	movs	r3, #132	; 0x84\n",
-  "  88:	2386      	movs	r3, #134	; 0x86\n",
-  "  8a:	2388      	movs	r3, #136	; 0x88\n",
-  "  8c:	238a      	movs	r3, #138	; 0x8a\n",
-  "  8e:	238c      	movs	r3, #140	; 0x8c\n",
-  "  90:	238e      	movs	r3, #142	; 0x8e\n",
-  "  92:	2390      	movs	r3, #144	; 0x90\n",
-  "  94:	2392      	movs	r3, #146	; 0x92\n",
-  "  96:	2394      	movs	r3, #148	; 0x94\n",
-  "  98:	2396      	movs	r3, #150	; 0x96\n",
-  "  9a:	2398      	movs	r3, #152	; 0x98\n",
-  "  9c:	239a      	movs	r3, #154	; 0x9a\n",
-  "  9e:	239c      	movs	r3, #156	; 0x9c\n",
-  "  a0:	239e      	movs	r3, #158	; 0x9e\n",
-  "  a2:	23a0      	movs	r3, #160	; 0xa0\n",
-  "  a4:	23a2      	movs	r3, #162	; 0xa2\n",
-  "  a6:	23a4      	movs	r3, #164	; 0xa4\n",
-  "  a8:	23a6      	movs	r3, #166	; 0xa6\n",
-  "  aa:	23a8      	movs	r3, #168	; 0xa8\n",
-  "  ac:	23aa      	movs	r3, #170	; 0xaa\n",
-  "  ae:	23ac      	movs	r3, #172	; 0xac\n",
-  "  b0:	23ae      	movs	r3, #174	; 0xae\n",
-  "  b2:	23b0      	movs	r3, #176	; 0xb0\n",
-  "  b4:	23b2      	movs	r3, #178	; 0xb2\n",
-  "  b6:	23b4      	movs	r3, #180	; 0xb4\n",
-  "  b8:	23b6      	movs	r3, #182	; 0xb6\n",
-  "  ba:	23b8      	movs	r3, #184	; 0xb8\n",
-  "  bc:	23ba      	movs	r3, #186	; 0xba\n",
-  "  be:	23bc      	movs	r3, #188	; 0xbc\n",
-  "  c0:	23be      	movs	r3, #190	; 0xbe\n",
-  "  c2:	23c0      	movs	r3, #192	; 0xc0\n",
-  "  c4:	23c2      	movs	r3, #194	; 0xc2\n",
-  "  c6:	23c4      	movs	r3, #196	; 0xc4\n",
-  "  c8:	23c6      	movs	r3, #198	; 0xc6\n",
-  "  ca:	23c8      	movs	r3, #200	; 0xc8\n",
-  "  cc:	23ca      	movs	r3, #202	; 0xca\n",
-  "  ce:	23cc      	movs	r3, #204	; 0xcc\n",
-  "  d0:	23ce      	movs	r3, #206	; 0xce\n",
-  "  d2:	23d0      	movs	r3, #208	; 0xd0\n",
-  "  d4:	23d2      	movs	r3, #210	; 0xd2\n",
-  "  d6:	23d4      	movs	r3, #212	; 0xd4\n",
-  "  d8:	23d6      	movs	r3, #214	; 0xd6\n",
-  "  da:	23d8      	movs	r3, #216	; 0xd8\n",
-  "  dc:	23da      	movs	r3, #218	; 0xda\n",
-  "  de:	23dc      	movs	r3, #220	; 0xdc\n",
-  "  e0:	23de      	movs	r3, #222	; 0xde\n",
-  "  e2:	23e0      	movs	r3, #224	; 0xe0\n",
-  "  e4:	23e2      	movs	r3, #226	; 0xe2\n",
-  "  e6:	23e4      	movs	r3, #228	; 0xe4\n",
-  "  e8:	23e6      	movs	r3, #230	; 0xe6\n",
-  "  ea:	23e8      	movs	r3, #232	; 0xe8\n",
-  "  ec:	23ea      	movs	r3, #234	; 0xea\n",
-  "  ee:	23ec      	movs	r3, #236	; 0xec\n",
-  "  f0:	23ee      	movs	r3, #238	; 0xee\n",
-  "  f2:	23f0      	movs	r3, #240	; 0xf0\n",
-  "  f4:	23f2      	movs	r3, #242	; 0xf2\n",
-  "  f6:	23f4      	movs	r3, #244	; 0xf4\n",
-  "  f8:	23f6      	movs	r3, #246	; 0xf6\n",
-  "  fa:	23f8      	movs	r3, #248	; 0xf8\n",
-  "  fc:	23fa      	movs	r3, #250	; 0xfa\n",
-  "  fe:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 100:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 102:	2300      	movs	r3, #0\n",
-  " 104:	2302      	movs	r3, #2\n",
-  " 106:	2304      	movs	r3, #4\n",
-  " 108:	2306      	movs	r3, #6\n",
-  " 10a:	2308      	movs	r3, #8\n",
-  " 10c:	230a      	movs	r3, #10\n",
-  " 10e:	230c      	movs	r3, #12\n",
-  " 110:	230e      	movs	r3, #14\n",
-  " 112:	2310      	movs	r3, #16\n",
-  " 114:	2312      	movs	r3, #18\n",
-  " 116:	2314      	movs	r3, #20\n",
-  " 118:	2316      	movs	r3, #22\n",
-  " 11a:	2318      	movs	r3, #24\n",
-  " 11c:	231a      	movs	r3, #26\n",
-  " 11e:	231c      	movs	r3, #28\n",
-  " 120:	231e      	movs	r3, #30\n",
-  " 122:	2320      	movs	r3, #32\n",
-  " 124:	2322      	movs	r3, #34	; 0x22\n",
-  " 126:	2324      	movs	r3, #36	; 0x24\n",
-  " 128:	2326      	movs	r3, #38	; 0x26\n",
-  " 12a:	2328      	movs	r3, #40	; 0x28\n",
-  " 12c:	232a      	movs	r3, #42	; 0x2a\n",
-  " 12e:	232c      	movs	r3, #44	; 0x2c\n",
-  " 130:	232e      	movs	r3, #46	; 0x2e\n",
-  " 132:	2330      	movs	r3, #48	; 0x30\n",
-  " 134:	2332      	movs	r3, #50	; 0x32\n",
-  " 136:	2334      	movs	r3, #52	; 0x34\n",
-  " 138:	2336      	movs	r3, #54	; 0x36\n",
-  " 13a:	2338      	movs	r3, #56	; 0x38\n",
-  " 13c:	233a      	movs	r3, #58	; 0x3a\n",
-  " 13e:	233c      	movs	r3, #60	; 0x3c\n",
-  " 140:	233e      	movs	r3, #62	; 0x3e\n",
-  " 142:	2340      	movs	r3, #64	; 0x40\n",
-  " 144:	2342      	movs	r3, #66	; 0x42\n",
-  " 146:	2344      	movs	r3, #68	; 0x44\n",
-  " 148:	2346      	movs	r3, #70	; 0x46\n",
-  " 14a:	2348      	movs	r3, #72	; 0x48\n",
-  " 14c:	234a      	movs	r3, #74	; 0x4a\n",
-  " 14e:	234c      	movs	r3, #76	; 0x4c\n",
-  " 150:	234e      	movs	r3, #78	; 0x4e\n",
-  " 152:	2350      	movs	r3, #80	; 0x50\n",
-  " 154:	2352      	movs	r3, #82	; 0x52\n",
-  " 156:	2354      	movs	r3, #84	; 0x54\n",
-  " 158:	2356      	movs	r3, #86	; 0x56\n",
-  " 15a:	2358      	movs	r3, #88	; 0x58\n",
-  " 15c:	235a      	movs	r3, #90	; 0x5a\n",
-  " 15e:	235c      	movs	r3, #92	; 0x5c\n",
-  " 160:	235e      	movs	r3, #94	; 0x5e\n",
-  " 162:	2360      	movs	r3, #96	; 0x60\n",
-  " 164:	2362      	movs	r3, #98	; 0x62\n",
-  " 166:	2364      	movs	r3, #100	; 0x64\n",
-  " 168:	2366      	movs	r3, #102	; 0x66\n",
-  " 16a:	2368      	movs	r3, #104	; 0x68\n",
-  " 16c:	236a      	movs	r3, #106	; 0x6a\n",
-  " 16e:	236c      	movs	r3, #108	; 0x6c\n",
-  " 170:	236e      	movs	r3, #110	; 0x6e\n",
-  " 172:	2370      	movs	r3, #112	; 0x70\n",
-  " 174:	2372      	movs	r3, #114	; 0x72\n",
-  " 176:	2374      	movs	r3, #116	; 0x74\n",
-  " 178:	2376      	movs	r3, #118	; 0x76\n",
-  " 17a:	2378      	movs	r3, #120	; 0x78\n",
-  " 17c:	237a      	movs	r3, #122	; 0x7a\n",
-  " 17e:	237c      	movs	r3, #124	; 0x7c\n",
-  " 180:	237e      	movs	r3, #126	; 0x7e\n",
-  " 182:	2380      	movs	r3, #128	; 0x80\n",
-  " 184:	2382      	movs	r3, #130	; 0x82\n",
-  " 186:	2384      	movs	r3, #132	; 0x84\n",
-  " 188:	2386      	movs	r3, #134	; 0x86\n",
-  " 18a:	2388      	movs	r3, #136	; 0x88\n",
-  " 18c:	238a      	movs	r3, #138	; 0x8a\n",
-  " 18e:	238c      	movs	r3, #140	; 0x8c\n",
-  " 190:	238e      	movs	r3, #142	; 0x8e\n",
-  " 192:	2390      	movs	r3, #144	; 0x90\n",
-  " 194:	2392      	movs	r3, #146	; 0x92\n",
-  " 196:	2394      	movs	r3, #148	; 0x94\n",
-  " 198:	2396      	movs	r3, #150	; 0x96\n",
-  " 19a:	2398      	movs	r3, #152	; 0x98\n",
-  " 19c:	239a      	movs	r3, #154	; 0x9a\n",
-  " 19e:	239c      	movs	r3, #156	; 0x9c\n",
-  " 1a0:	239e      	movs	r3, #158	; 0x9e\n",
-  " 1a2:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 1a4:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 1a6:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 1a8:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 1aa:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 1ac:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 1ae:	23ac      	movs	r3, #172	; 0xac\n",
-  " 1b0:	23ae      	movs	r3, #174	; 0xae\n",
-  " 1b2:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 1b4:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 1b6:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 1b8:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 1ba:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 1bc:	23ba      	movs	r3, #186	; 0xba\n",
-  " 1be:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 1c0:	23be      	movs	r3, #190	; 0xbe\n",
-  " 1c2:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 1c4:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 1c6:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 1c8:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 1ca:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 1cc:	23ca      	movs	r3, #202	; 0xca\n",
-  " 1ce:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 1d0:	23ce      	movs	r3, #206	; 0xce\n",
-  " 1d2:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 1d4:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 1d6:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 1d8:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 1da:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 1dc:	23da      	movs	r3, #218	; 0xda\n",
-  " 1de:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 1e0:	23de      	movs	r3, #222	; 0xde\n",
-  " 1e2:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 1e4:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 1e6:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 1e8:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 1ea:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 1ec:	23ea      	movs	r3, #234	; 0xea\n",
-  " 1ee:	23ec      	movs	r3, #236	; 0xec\n",
-  " 1f0:	23ee      	movs	r3, #238	; 0xee\n",
-  " 1f2:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 1f4:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 1f6:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 1f8:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 1fa:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 1fc:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 1fe:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 200:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 202:	2300      	movs	r3, #0\n",
-  " 204:	2302      	movs	r3, #2\n",
-  " 206:	2304      	movs	r3, #4\n",
-  " 208:	2306      	movs	r3, #6\n",
-  " 20a:	2308      	movs	r3, #8\n",
-  " 20c:	230a      	movs	r3, #10\n",
-  " 20e:	230c      	movs	r3, #12\n",
-  " 210:	230e      	movs	r3, #14\n",
-  " 212:	2310      	movs	r3, #16\n",
-  " 214:	2312      	movs	r3, #18\n",
-  " 216:	2314      	movs	r3, #20\n",
-  " 218:	2316      	movs	r3, #22\n",
-  " 21a:	2318      	movs	r3, #24\n",
-  " 21c:	231a      	movs	r3, #26\n",
-  " 21e:	231c      	movs	r3, #28\n",
-  " 220:	231e      	movs	r3, #30\n",
-  " 222:	2320      	movs	r3, #32\n",
-  " 224:	2322      	movs	r3, #34	; 0x22\n",
-  " 226:	2324      	movs	r3, #36	; 0x24\n",
-  " 228:	2326      	movs	r3, #38	; 0x26\n",
-  " 22a:	2328      	movs	r3, #40	; 0x28\n",
-  " 22c:	232a      	movs	r3, #42	; 0x2a\n",
-  " 22e:	232c      	movs	r3, #44	; 0x2c\n",
-  " 230:	232e      	movs	r3, #46	; 0x2e\n",
-  " 232:	2330      	movs	r3, #48	; 0x30\n",
-  " 234:	2332      	movs	r3, #50	; 0x32\n",
-  " 236:	2334      	movs	r3, #52	; 0x34\n",
-  " 238:	2336      	movs	r3, #54	; 0x36\n",
-  " 23a:	2338      	movs	r3, #56	; 0x38\n",
-  " 23c:	233a      	movs	r3, #58	; 0x3a\n",
-  " 23e:	233c      	movs	r3, #60	; 0x3c\n",
-  " 240:	233e      	movs	r3, #62	; 0x3e\n",
-  " 242:	2340      	movs	r3, #64	; 0x40\n",
-  " 244:	2342      	movs	r3, #66	; 0x42\n",
-  " 246:	2344      	movs	r3, #68	; 0x44\n",
-  " 248:	2346      	movs	r3, #70	; 0x46\n",
-  " 24a:	2348      	movs	r3, #72	; 0x48\n",
-  " 24c:	234a      	movs	r3, #74	; 0x4a\n",
-  " 24e:	234c      	movs	r3, #76	; 0x4c\n",
-  " 250:	234e      	movs	r3, #78	; 0x4e\n",
-  " 252:	2350      	movs	r3, #80	; 0x50\n",
-  " 254:	2352      	movs	r3, #82	; 0x52\n",
-  " 256:	2354      	movs	r3, #84	; 0x54\n",
-  " 258:	2356      	movs	r3, #86	; 0x56\n",
-  " 25a:	2358      	movs	r3, #88	; 0x58\n",
-  " 25c:	235a      	movs	r3, #90	; 0x5a\n",
-  " 25e:	235c      	movs	r3, #92	; 0x5c\n",
-  " 260:	235e      	movs	r3, #94	; 0x5e\n",
-  " 262:	2360      	movs	r3, #96	; 0x60\n",
-  " 264:	2362      	movs	r3, #98	; 0x62\n",
-  " 266:	2364      	movs	r3, #100	; 0x64\n",
-  " 268:	2366      	movs	r3, #102	; 0x66\n",
-  " 26a:	2368      	movs	r3, #104	; 0x68\n",
-  " 26c:	236a      	movs	r3, #106	; 0x6a\n",
-  " 26e:	236c      	movs	r3, #108	; 0x6c\n",
-  " 270:	236e      	movs	r3, #110	; 0x6e\n",
-  " 272:	2370      	movs	r3, #112	; 0x70\n",
-  " 274:	2372      	movs	r3, #114	; 0x72\n",
-  " 276:	2374      	movs	r3, #116	; 0x74\n",
-  " 278:	2376      	movs	r3, #118	; 0x76\n",
-  " 27a:	2378      	movs	r3, #120	; 0x78\n",
-  " 27c:	237a      	movs	r3, #122	; 0x7a\n",
-  " 27e:	237c      	movs	r3, #124	; 0x7c\n",
-  " 280:	237e      	movs	r3, #126	; 0x7e\n",
-  " 282:	2380      	movs	r3, #128	; 0x80\n",
-  " 284:	2382      	movs	r3, #130	; 0x82\n",
-  " 286:	2384      	movs	r3, #132	; 0x84\n",
-  " 288:	2386      	movs	r3, #134	; 0x86\n",
-  " 28a:	2388      	movs	r3, #136	; 0x88\n",
-  " 28c:	238a      	movs	r3, #138	; 0x8a\n",
-  " 28e:	238c      	movs	r3, #140	; 0x8c\n",
-  " 290:	238e      	movs	r3, #142	; 0x8e\n",
-  " 292:	2390      	movs	r3, #144	; 0x90\n",
-  " 294:	2392      	movs	r3, #146	; 0x92\n",
-  " 296:	2394      	movs	r3, #148	; 0x94\n",
-  " 298:	2396      	movs	r3, #150	; 0x96\n",
-  " 29a:	2398      	movs	r3, #152	; 0x98\n",
-  " 29c:	239a      	movs	r3, #154	; 0x9a\n",
-  " 29e:	239c      	movs	r3, #156	; 0x9c\n",
-  " 2a0:	239e      	movs	r3, #158	; 0x9e\n",
-  " 2a2:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 2a4:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 2a6:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 2a8:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 2aa:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 2ac:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 2ae:	23ac      	movs	r3, #172	; 0xac\n",
-  " 2b0:	23ae      	movs	r3, #174	; 0xae\n",
-  " 2b2:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 2b4:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 2b6:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 2b8:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 2ba:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 2bc:	23ba      	movs	r3, #186	; 0xba\n",
-  " 2be:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 2c0:	23be      	movs	r3, #190	; 0xbe\n",
-  " 2c2:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 2c4:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 2c6:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 2c8:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 2ca:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 2cc:	23ca      	movs	r3, #202	; 0xca\n",
-  " 2ce:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 2d0:	23ce      	movs	r3, #206	; 0xce\n",
-  " 2d2:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 2d4:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 2d6:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 2d8:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 2da:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 2dc:	23da      	movs	r3, #218	; 0xda\n",
-  " 2de:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 2e0:	23de      	movs	r3, #222	; 0xde\n",
-  " 2e2:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 2e4:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 2e6:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 2e8:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 2ea:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 2ec:	23ea      	movs	r3, #234	; 0xea\n",
-  " 2ee:	23ec      	movs	r3, #236	; 0xec\n",
-  " 2f0:	23ee      	movs	r3, #238	; 0xee\n",
-  " 2f2:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 2f4:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 2f6:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 2f8:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 2fa:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 2fc:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 2fe:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 300:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 302:	2300      	movs	r3, #0\n",
-  " 304:	2302      	movs	r3, #2\n",
-  " 306:	2304      	movs	r3, #4\n",
-  " 308:	2306      	movs	r3, #6\n",
-  " 30a:	2308      	movs	r3, #8\n",
-  " 30c:	230a      	movs	r3, #10\n",
-  " 30e:	230c      	movs	r3, #12\n",
-  " 310:	230e      	movs	r3, #14\n",
-  " 312:	2310      	movs	r3, #16\n",
-  " 314:	2312      	movs	r3, #18\n",
-  " 316:	2314      	movs	r3, #20\n",
-  " 318:	2316      	movs	r3, #22\n",
-  " 31a:	2318      	movs	r3, #24\n",
-  " 31c:	231a      	movs	r3, #26\n",
-  " 31e:	231c      	movs	r3, #28\n",
-  " 320:	231e      	movs	r3, #30\n",
-  " 322:	2320      	movs	r3, #32\n",
-  " 324:	2322      	movs	r3, #34	; 0x22\n",
-  " 326:	2324      	movs	r3, #36	; 0x24\n",
-  " 328:	2326      	movs	r3, #38	; 0x26\n",
-  " 32a:	2328      	movs	r3, #40	; 0x28\n",
-  " 32c:	232a      	movs	r3, #42	; 0x2a\n",
-  " 32e:	232c      	movs	r3, #44	; 0x2c\n",
-  " 330:	232e      	movs	r3, #46	; 0x2e\n",
-  " 332:	2330      	movs	r3, #48	; 0x30\n",
-  " 334:	2332      	movs	r3, #50	; 0x32\n",
-  " 336:	2334      	movs	r3, #52	; 0x34\n",
-  " 338:	2336      	movs	r3, #54	; 0x36\n",
-  " 33a:	2338      	movs	r3, #56	; 0x38\n",
-  " 33c:	233a      	movs	r3, #58	; 0x3a\n",
-  " 33e:	233c      	movs	r3, #60	; 0x3c\n",
-  " 340:	233e      	movs	r3, #62	; 0x3e\n",
-  " 342:	2340      	movs	r3, #64	; 0x40\n",
-  " 344:	2342      	movs	r3, #66	; 0x42\n",
-  " 346:	2344      	movs	r3, #68	; 0x44\n",
-  " 348:	2346      	movs	r3, #70	; 0x46\n",
-  " 34a:	2348      	movs	r3, #72	; 0x48\n",
-  " 34c:	234a      	movs	r3, #74	; 0x4a\n",
-  " 34e:	234c      	movs	r3, #76	; 0x4c\n",
-  " 350:	234e      	movs	r3, #78	; 0x4e\n",
-  " 352:	2350      	movs	r3, #80	; 0x50\n",
-  " 354:	2352      	movs	r3, #82	; 0x52\n",
-  " 356:	2354      	movs	r3, #84	; 0x54\n",
-  " 358:	2356      	movs	r3, #86	; 0x56\n",
-  " 35a:	2358      	movs	r3, #88	; 0x58\n",
-  " 35c:	235a      	movs	r3, #90	; 0x5a\n",
-  " 35e:	235c      	movs	r3, #92	; 0x5c\n",
-  " 360:	235e      	movs	r3, #94	; 0x5e\n",
-  " 362:	2360      	movs	r3, #96	; 0x60\n",
-  " 364:	2362      	movs	r3, #98	; 0x62\n",
-  " 366:	2364      	movs	r3, #100	; 0x64\n",
-  " 368:	2366      	movs	r3, #102	; 0x66\n",
-  " 36a:	2368      	movs	r3, #104	; 0x68\n",
-  " 36c:	236a      	movs	r3, #106	; 0x6a\n",
-  " 36e:	236c      	movs	r3, #108	; 0x6c\n",
-  " 370:	236e      	movs	r3, #110	; 0x6e\n",
-  " 372:	2370      	movs	r3, #112	; 0x70\n",
-  " 374:	2372      	movs	r3, #114	; 0x72\n",
-  " 376:	2374      	movs	r3, #116	; 0x74\n",
-  " 378:	2376      	movs	r3, #118	; 0x76\n",
-  " 37a:	2378      	movs	r3, #120	; 0x78\n",
-  " 37c:	237a      	movs	r3, #122	; 0x7a\n",
-  " 37e:	237c      	movs	r3, #124	; 0x7c\n",
-  " 380:	237e      	movs	r3, #126	; 0x7e\n",
-  " 382:	2380      	movs	r3, #128	; 0x80\n",
-  " 384:	2382      	movs	r3, #130	; 0x82\n",
-  " 386:	2384      	movs	r3, #132	; 0x84\n",
-  " 388:	2386      	movs	r3, #134	; 0x86\n",
-  " 38a:	2388      	movs	r3, #136	; 0x88\n",
-  " 38c:	238a      	movs	r3, #138	; 0x8a\n",
-  " 38e:	238c      	movs	r3, #140	; 0x8c\n",
-  " 390:	238e      	movs	r3, #142	; 0x8e\n",
-  " 392:	2390      	movs	r3, #144	; 0x90\n",
-  " 394:	2392      	movs	r3, #146	; 0x92\n",
-  " 396:	2394      	movs	r3, #148	; 0x94\n",
-  " 398:	2396      	movs	r3, #150	; 0x96\n",
-  " 39a:	2398      	movs	r3, #152	; 0x98\n",
-  " 39c:	239a      	movs	r3, #154	; 0x9a\n",
-  " 39e:	239c      	movs	r3, #156	; 0x9c\n",
-  " 3a0:	239e      	movs	r3, #158	; 0x9e\n",
-  " 3a2:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 3a4:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 3a6:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 3a8:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 3aa:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 3ac:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 3ae:	23ac      	movs	r3, #172	; 0xac\n",
-  " 3b0:	23ae      	movs	r3, #174	; 0xae\n",
-  " 3b2:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 3b4:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 3b6:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 3b8:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 3ba:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 3bc:	23ba      	movs	r3, #186	; 0xba\n",
-  " 3be:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 3c0:	23be      	movs	r3, #190	; 0xbe\n",
-  " 3c2:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 3c4:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 3c6:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 3c8:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 3ca:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 3cc:	23ca      	movs	r3, #202	; 0xca\n",
-  " 3ce:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 3d0:	23ce      	movs	r3, #206	; 0xce\n",
-  " 3d2:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 3d4:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 3d6:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 3d8:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 3da:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 3dc:	23da      	movs	r3, #218	; 0xda\n",
-  " 3de:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 3e0:	23de      	movs	r3, #222	; 0xde\n",
-  " 3e2:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 3e4:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 3e6:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 3e8:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 3ea:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 3ec:	23ea      	movs	r3, #234	; 0xea\n",
-  " 3ee:	23ec      	movs	r3, #236	; 0xec\n",
-  " 3f0:	23ee      	movs	r3, #238	; 0xee\n",
-  " 3f2:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 3f4:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 3f6:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 3f8:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 3fa:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 3fc:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 3fe:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 400:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 402:	2300      	movs	r3, #0\n",
-  " 404:	2302      	movs	r3, #2\n",
-  " 406:	2304      	movs	r3, #4\n",
-  " 408:	2306      	movs	r3, #6\n",
-  " 40a:	2308      	movs	r3, #8\n",
-  " 40c:	230a      	movs	r3, #10\n",
-  " 40e:	230c      	movs	r3, #12\n",
-  " 410:	230e      	movs	r3, #14\n",
-  " 412:	2310      	movs	r3, #16\n",
-  " 414:	2312      	movs	r3, #18\n",
-  " 416:	2314      	movs	r3, #20\n",
-  " 418:	2316      	movs	r3, #22\n",
-  " 41a:	2318      	movs	r3, #24\n",
-  " 41c:	231a      	movs	r3, #26\n",
-  " 41e:	231c      	movs	r3, #28\n",
-  " 420:	231e      	movs	r3, #30\n",
-  " 422:	2320      	movs	r3, #32\n",
-  " 424:	2322      	movs	r3, #34	; 0x22\n",
-  " 426:	2324      	movs	r3, #36	; 0x24\n",
-  " 428:	2326      	movs	r3, #38	; 0x26\n",
-  " 42a:	2328      	movs	r3, #40	; 0x28\n",
-  " 42c:	232a      	movs	r3, #42	; 0x2a\n",
-  " 42e:	232c      	movs	r3, #44	; 0x2c\n",
-  " 430:	232e      	movs	r3, #46	; 0x2e\n",
-  " 432:	2330      	movs	r3, #48	; 0x30\n",
-  " 434:	2332      	movs	r3, #50	; 0x32\n",
-  " 436:	2334      	movs	r3, #52	; 0x34\n",
-  " 438:	2336      	movs	r3, #54	; 0x36\n",
-  " 43a:	2338      	movs	r3, #56	; 0x38\n",
-  " 43c:	233a      	movs	r3, #58	; 0x3a\n",
-  " 43e:	233c      	movs	r3, #60	; 0x3c\n",
-  " 440:	233e      	movs	r3, #62	; 0x3e\n",
-  " 442:	2340      	movs	r3, #64	; 0x40\n",
-  " 444:	2342      	movs	r3, #66	; 0x42\n",
-  " 446:	2344      	movs	r3, #68	; 0x44\n",
-  " 448:	2346      	movs	r3, #70	; 0x46\n",
-  " 44a:	2348      	movs	r3, #72	; 0x48\n",
-  " 44c:	234a      	movs	r3, #74	; 0x4a\n",
-  " 44e:	234c      	movs	r3, #76	; 0x4c\n",
-  " 450:	234e      	movs	r3, #78	; 0x4e\n",
-  " 452:	2350      	movs	r3, #80	; 0x50\n",
-  " 454:	2352      	movs	r3, #82	; 0x52\n",
-  " 456:	2354      	movs	r3, #84	; 0x54\n",
-  " 458:	2356      	movs	r3, #86	; 0x56\n",
-  " 45a:	2358      	movs	r3, #88	; 0x58\n",
-  " 45c:	235a      	movs	r3, #90	; 0x5a\n",
-  " 45e:	235c      	movs	r3, #92	; 0x5c\n",
-  " 460:	235e      	movs	r3, #94	; 0x5e\n",
-  " 462:	2360      	movs	r3, #96	; 0x60\n",
-  " 464:	2362      	movs	r3, #98	; 0x62\n",
-  " 466:	2364      	movs	r3, #100	; 0x64\n",
-  " 468:	2366      	movs	r3, #102	; 0x66\n",
-  " 46a:	2368      	movs	r3, #104	; 0x68\n",
-  " 46c:	236a      	movs	r3, #106	; 0x6a\n",
-  " 46e:	236c      	movs	r3, #108	; 0x6c\n",
-  " 470:	236e      	movs	r3, #110	; 0x6e\n",
-  " 472:	2370      	movs	r3, #112	; 0x70\n",
-  " 474:	2372      	movs	r3, #114	; 0x72\n",
-  " 476:	2374      	movs	r3, #116	; 0x74\n",
-  " 478:	2376      	movs	r3, #118	; 0x76\n",
-  " 47a:	2378      	movs	r3, #120	; 0x78\n",
-  " 47c:	237a      	movs	r3, #122	; 0x7a\n",
-  " 47e:	237c      	movs	r3, #124	; 0x7c\n",
-  " 480:	237e      	movs	r3, #126	; 0x7e\n",
-  " 482:	2380      	movs	r3, #128	; 0x80\n",
-  " 484:	2382      	movs	r3, #130	; 0x82\n",
-  " 486:	2384      	movs	r3, #132	; 0x84\n",
-  " 488:	2386      	movs	r3, #134	; 0x86\n",
-  " 48a:	2388      	movs	r3, #136	; 0x88\n",
-  " 48c:	238a      	movs	r3, #138	; 0x8a\n",
-  " 48e:	238c      	movs	r3, #140	; 0x8c\n",
-  " 490:	238e      	movs	r3, #142	; 0x8e\n",
-  " 492:	2390      	movs	r3, #144	; 0x90\n",
-  " 494:	2392      	movs	r3, #146	; 0x92\n",
-  " 496:	2394      	movs	r3, #148	; 0x94\n",
-  " 498:	2396      	movs	r3, #150	; 0x96\n",
-  " 49a:	2398      	movs	r3, #152	; 0x98\n",
-  " 49c:	239a      	movs	r3, #154	; 0x9a\n",
-  " 49e:	239c      	movs	r3, #156	; 0x9c\n",
-  " 4a0:	239e      	movs	r3, #158	; 0x9e\n",
-  " 4a2:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 4a4:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 4a6:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 4a8:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 4aa:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 4ac:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 4ae:	23ac      	movs	r3, #172	; 0xac\n",
-  " 4b0:	23ae      	movs	r3, #174	; 0xae\n",
-  " 4b2:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 4b4:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 4b6:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 4b8:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 4ba:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 4bc:	23ba      	movs	r3, #186	; 0xba\n",
-  " 4be:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 4c0:	23be      	movs	r3, #190	; 0xbe\n",
-  " 4c2:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 4c4:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 4c6:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 4c8:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 4ca:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 4cc:	23ca      	movs	r3, #202	; 0xca\n",
-  " 4ce:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 4d0:	23ce      	movs	r3, #206	; 0xce\n",
-  " 4d2:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 4d4:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 4d6:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 4d8:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 4da:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 4dc:	23da      	movs	r3, #218	; 0xda\n",
-  " 4de:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 4e0:	23de      	movs	r3, #222	; 0xde\n",
-  " 4e2:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 4e4:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 4e6:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 4e8:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 4ea:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 4ec:	23ea      	movs	r3, #234	; 0xea\n",
-  " 4ee:	23ec      	movs	r3, #236	; 0xec\n",
-  " 4f0:	23ee      	movs	r3, #238	; 0xee\n",
-  " 4f2:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 4f4:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 4f6:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 4f8:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 4fa:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 4fc:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 4fe:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 500:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 502:	2300      	movs	r3, #0\n",
-  " 504:	2302      	movs	r3, #2\n",
-  " 506:	2304      	movs	r3, #4\n",
-  " 508:	2306      	movs	r3, #6\n",
-  " 50a:	2308      	movs	r3, #8\n",
-  " 50c:	230a      	movs	r3, #10\n",
-  " 50e:	230c      	movs	r3, #12\n",
-  " 510:	230e      	movs	r3, #14\n",
-  " 512:	2310      	movs	r3, #16\n",
-  " 514:	2312      	movs	r3, #18\n",
-  " 516:	2314      	movs	r3, #20\n",
-  " 518:	2316      	movs	r3, #22\n",
-  " 51a:	2318      	movs	r3, #24\n",
-  " 51c:	231a      	movs	r3, #26\n",
-  " 51e:	231c      	movs	r3, #28\n",
-  " 520:	231e      	movs	r3, #30\n",
-  " 522:	2320      	movs	r3, #32\n",
-  " 524:	2322      	movs	r3, #34	; 0x22\n",
-  " 526:	2324      	movs	r3, #36	; 0x24\n",
-  " 528:	2326      	movs	r3, #38	; 0x26\n",
-  " 52a:	2328      	movs	r3, #40	; 0x28\n",
-  " 52c:	232a      	movs	r3, #42	; 0x2a\n",
-  " 52e:	232c      	movs	r3, #44	; 0x2c\n",
-  " 530:	232e      	movs	r3, #46	; 0x2e\n",
-  " 532:	2330      	movs	r3, #48	; 0x30\n",
-  " 534:	2332      	movs	r3, #50	; 0x32\n",
-  " 536:	2334      	movs	r3, #52	; 0x34\n",
-  " 538:	2336      	movs	r3, #54	; 0x36\n",
-  " 53a:	2338      	movs	r3, #56	; 0x38\n",
-  " 53c:	233a      	movs	r3, #58	; 0x3a\n",
-  " 53e:	233c      	movs	r3, #60	; 0x3c\n",
-  " 540:	233e      	movs	r3, #62	; 0x3e\n",
-  " 542:	2340      	movs	r3, #64	; 0x40\n",
-  " 544:	2342      	movs	r3, #66	; 0x42\n",
-  " 546:	2344      	movs	r3, #68	; 0x44\n",
-  " 548:	2346      	movs	r3, #70	; 0x46\n",
-  " 54a:	2348      	movs	r3, #72	; 0x48\n",
-  " 54c:	234a      	movs	r3, #74	; 0x4a\n",
-  " 54e:	234c      	movs	r3, #76	; 0x4c\n",
-  " 550:	234e      	movs	r3, #78	; 0x4e\n",
-  " 552:	2350      	movs	r3, #80	; 0x50\n",
-  " 554:	2352      	movs	r3, #82	; 0x52\n",
-  " 556:	2354      	movs	r3, #84	; 0x54\n",
-  " 558:	2356      	movs	r3, #86	; 0x56\n",
-  " 55a:	2358      	movs	r3, #88	; 0x58\n",
-  " 55c:	235a      	movs	r3, #90	; 0x5a\n",
-  " 55e:	235c      	movs	r3, #92	; 0x5c\n",
-  " 560:	235e      	movs	r3, #94	; 0x5e\n",
-  " 562:	2360      	movs	r3, #96	; 0x60\n",
-  " 564:	2362      	movs	r3, #98	; 0x62\n",
-  " 566:	2364      	movs	r3, #100	; 0x64\n",
-  " 568:	2366      	movs	r3, #102	; 0x66\n",
-  " 56a:	2368      	movs	r3, #104	; 0x68\n",
-  " 56c:	236a      	movs	r3, #106	; 0x6a\n",
-  " 56e:	236c      	movs	r3, #108	; 0x6c\n",
-  " 570:	236e      	movs	r3, #110	; 0x6e\n",
-  " 572:	2370      	movs	r3, #112	; 0x70\n",
-  " 574:	2372      	movs	r3, #114	; 0x72\n",
-  " 576:	2374      	movs	r3, #116	; 0x74\n",
-  " 578:	2376      	movs	r3, #118	; 0x76\n",
-  " 57a:	2378      	movs	r3, #120	; 0x78\n",
-  " 57c:	237a      	movs	r3, #122	; 0x7a\n",
-  " 57e:	237c      	movs	r3, #124	; 0x7c\n",
-  " 580:	237e      	movs	r3, #126	; 0x7e\n",
-  " 582:	2380      	movs	r3, #128	; 0x80\n",
-  " 584:	2382      	movs	r3, #130	; 0x82\n",
-  " 586:	2384      	movs	r3, #132	; 0x84\n",
-  " 588:	2386      	movs	r3, #134	; 0x86\n",
-  " 58a:	2388      	movs	r3, #136	; 0x88\n",
-  " 58c:	238a      	movs	r3, #138	; 0x8a\n",
-  " 58e:	238c      	movs	r3, #140	; 0x8c\n",
-  " 590:	238e      	movs	r3, #142	; 0x8e\n",
-  " 592:	2390      	movs	r3, #144	; 0x90\n",
-  " 594:	2392      	movs	r3, #146	; 0x92\n",
-  " 596:	2394      	movs	r3, #148	; 0x94\n",
-  " 598:	2396      	movs	r3, #150	; 0x96\n",
-  " 59a:	2398      	movs	r3, #152	; 0x98\n",
-  " 59c:	239a      	movs	r3, #154	; 0x9a\n",
-  " 59e:	239c      	movs	r3, #156	; 0x9c\n",
-  " 5a0:	239e      	movs	r3, #158	; 0x9e\n",
-  " 5a2:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 5a4:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 5a6:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 5a8:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 5aa:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 5ac:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 5ae:	23ac      	movs	r3, #172	; 0xac\n",
-  " 5b0:	23ae      	movs	r3, #174	; 0xae\n",
-  " 5b2:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 5b4:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 5b6:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 5b8:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 5ba:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 5bc:	23ba      	movs	r3, #186	; 0xba\n",
-  " 5be:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 5c0:	23be      	movs	r3, #190	; 0xbe\n",
-  " 5c2:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 5c4:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 5c6:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 5c8:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 5ca:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 5cc:	23ca      	movs	r3, #202	; 0xca\n",
-  " 5ce:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 5d0:	23ce      	movs	r3, #206	; 0xce\n",
-  " 5d2:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 5d4:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 5d6:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 5d8:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 5da:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 5dc:	23da      	movs	r3, #218	; 0xda\n",
-  " 5de:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 5e0:	23de      	movs	r3, #222	; 0xde\n",
-  " 5e2:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 5e4:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 5e6:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 5e8:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 5ea:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 5ec:	23ea      	movs	r3, #234	; 0xea\n",
-  " 5ee:	23ec      	movs	r3, #236	; 0xec\n",
-  " 5f0:	23ee      	movs	r3, #238	; 0xee\n",
-  " 5f2:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 5f4:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 5f6:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 5f8:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 5fa:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 5fc:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 5fe:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 600:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 602:	2300      	movs	r3, #0\n",
-  " 604:	2302      	movs	r3, #2\n",
-  " 606:	2304      	movs	r3, #4\n",
-  " 608:	2306      	movs	r3, #6\n",
-  " 60a:	2308      	movs	r3, #8\n",
-  " 60c:	230a      	movs	r3, #10\n",
-  " 60e:	230c      	movs	r3, #12\n",
-  " 610:	230e      	movs	r3, #14\n",
-  " 612:	2310      	movs	r3, #16\n",
-  " 614:	2312      	movs	r3, #18\n",
-  " 616:	2314      	movs	r3, #20\n",
-  " 618:	2316      	movs	r3, #22\n",
-  " 61a:	2318      	movs	r3, #24\n",
-  " 61c:	231a      	movs	r3, #26\n",
-  " 61e:	231c      	movs	r3, #28\n",
-  " 620:	231e      	movs	r3, #30\n",
-  " 622:	2320      	movs	r3, #32\n",
-  " 624:	2322      	movs	r3, #34	; 0x22\n",
-  " 626:	2324      	movs	r3, #36	; 0x24\n",
-  " 628:	2326      	movs	r3, #38	; 0x26\n",
-  " 62a:	2328      	movs	r3, #40	; 0x28\n",
-  " 62c:	232a      	movs	r3, #42	; 0x2a\n",
-  " 62e:	232c      	movs	r3, #44	; 0x2c\n",
-  " 630:	232e      	movs	r3, #46	; 0x2e\n",
-  " 632:	2330      	movs	r3, #48	; 0x30\n",
-  " 634:	2332      	movs	r3, #50	; 0x32\n",
-  " 636:	2334      	movs	r3, #52	; 0x34\n",
-  " 638:	2336      	movs	r3, #54	; 0x36\n",
-  " 63a:	2338      	movs	r3, #56	; 0x38\n",
-  " 63c:	233a      	movs	r3, #58	; 0x3a\n",
-  " 63e:	233c      	movs	r3, #60	; 0x3c\n",
-  " 640:	233e      	movs	r3, #62	; 0x3e\n",
-  " 642:	2340      	movs	r3, #64	; 0x40\n",
-  " 644:	2342      	movs	r3, #66	; 0x42\n",
-  " 646:	2344      	movs	r3, #68	; 0x44\n",
-  " 648:	2346      	movs	r3, #70	; 0x46\n",
-  " 64a:	2348      	movs	r3, #72	; 0x48\n",
-  " 64c:	234a      	movs	r3, #74	; 0x4a\n",
-  " 64e:	234c      	movs	r3, #76	; 0x4c\n",
-  " 650:	234e      	movs	r3, #78	; 0x4e\n",
-  " 652:	2350      	movs	r3, #80	; 0x50\n",
-  " 654:	2352      	movs	r3, #82	; 0x52\n",
-  " 656:	2354      	movs	r3, #84	; 0x54\n",
-  " 658:	2356      	movs	r3, #86	; 0x56\n",
-  " 65a:	2358      	movs	r3, #88	; 0x58\n",
-  " 65c:	235a      	movs	r3, #90	; 0x5a\n",
-  " 65e:	235c      	movs	r3, #92	; 0x5c\n",
-  " 660:	235e      	movs	r3, #94	; 0x5e\n",
-  " 662:	2360      	movs	r3, #96	; 0x60\n",
-  " 664:	2362      	movs	r3, #98	; 0x62\n",
-  " 666:	2364      	movs	r3, #100	; 0x64\n",
-  " 668:	2366      	movs	r3, #102	; 0x66\n",
-  " 66a:	2368      	movs	r3, #104	; 0x68\n",
-  " 66c:	236a      	movs	r3, #106	; 0x6a\n",
-  " 66e:	236c      	movs	r3, #108	; 0x6c\n",
-  " 670:	236e      	movs	r3, #110	; 0x6e\n",
-  " 672:	2370      	movs	r3, #112	; 0x70\n",
-  " 674:	2372      	movs	r3, #114	; 0x72\n",
-  " 676:	2374      	movs	r3, #116	; 0x74\n",
-  " 678:	2376      	movs	r3, #118	; 0x76\n",
-  " 67a:	2378      	movs	r3, #120	; 0x78\n",
-  " 67c:	237a      	movs	r3, #122	; 0x7a\n",
-  " 67e:	237c      	movs	r3, #124	; 0x7c\n",
-  " 680:	237e      	movs	r3, #126	; 0x7e\n",
-  " 682:	2380      	movs	r3, #128	; 0x80\n",
-  " 684:	2382      	movs	r3, #130	; 0x82\n",
-  " 686:	2384      	movs	r3, #132	; 0x84\n",
-  " 688:	2386      	movs	r3, #134	; 0x86\n",
-  " 68a:	2388      	movs	r3, #136	; 0x88\n",
-  " 68c:	238a      	movs	r3, #138	; 0x8a\n",
-  " 68e:	238c      	movs	r3, #140	; 0x8c\n",
-  " 690:	238e      	movs	r3, #142	; 0x8e\n",
-  " 692:	2390      	movs	r3, #144	; 0x90\n",
-  " 694:	2392      	movs	r3, #146	; 0x92\n",
-  " 696:	2394      	movs	r3, #148	; 0x94\n",
-  " 698:	2396      	movs	r3, #150	; 0x96\n",
-  " 69a:	2398      	movs	r3, #152	; 0x98\n",
-  " 69c:	239a      	movs	r3, #154	; 0x9a\n",
-  " 69e:	239c      	movs	r3, #156	; 0x9c\n",
-  " 6a0:	239e      	movs	r3, #158	; 0x9e\n",
-  " 6a2:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 6a4:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 6a6:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 6a8:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 6aa:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 6ac:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 6ae:	23ac      	movs	r3, #172	; 0xac\n",
-  " 6b0:	23ae      	movs	r3, #174	; 0xae\n",
-  " 6b2:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 6b4:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 6b6:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 6b8:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 6ba:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 6bc:	23ba      	movs	r3, #186	; 0xba\n",
-  " 6be:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 6c0:	23be      	movs	r3, #190	; 0xbe\n",
-  " 6c2:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 6c4:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 6c6:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 6c8:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 6ca:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 6cc:	23ca      	movs	r3, #202	; 0xca\n",
-  " 6ce:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 6d0:	23ce      	movs	r3, #206	; 0xce\n",
-  " 6d2:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 6d4:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 6d6:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 6d8:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 6da:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 6dc:	23da      	movs	r3, #218	; 0xda\n",
-  " 6de:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 6e0:	23de      	movs	r3, #222	; 0xde\n",
-  " 6e2:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 6e4:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 6e6:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 6e8:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 6ea:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 6ec:	23ea      	movs	r3, #234	; 0xea\n",
-  " 6ee:	23ec      	movs	r3, #236	; 0xec\n",
-  " 6f0:	23ee      	movs	r3, #238	; 0xee\n",
-  " 6f2:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 6f4:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 6f6:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 6f8:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 6fa:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 6fc:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 6fe:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 700:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 702:	2300      	movs	r3, #0\n",
-  " 704:	2302      	movs	r3, #2\n",
-  " 706:	2304      	movs	r3, #4\n",
-  " 708:	2306      	movs	r3, #6\n",
-  " 70a:	2308      	movs	r3, #8\n",
-  " 70c:	230a      	movs	r3, #10\n",
-  " 70e:	230c      	movs	r3, #12\n",
-  " 710:	230e      	movs	r3, #14\n",
-  " 712:	2310      	movs	r3, #16\n",
-  " 714:	2312      	movs	r3, #18\n",
-  " 716:	2314      	movs	r3, #20\n",
-  " 718:	2316      	movs	r3, #22\n",
-  " 71a:	2318      	movs	r3, #24\n",
-  " 71c:	231a      	movs	r3, #26\n",
-  " 71e:	231c      	movs	r3, #28\n",
-  " 720:	231e      	movs	r3, #30\n",
-  " 722:	2320      	movs	r3, #32\n",
-  " 724:	2322      	movs	r3, #34	; 0x22\n",
-  " 726:	2324      	movs	r3, #36	; 0x24\n",
-  " 728:	2326      	movs	r3, #38	; 0x26\n",
-  " 72a:	2328      	movs	r3, #40	; 0x28\n",
-  " 72c:	232a      	movs	r3, #42	; 0x2a\n",
-  " 72e:	232c      	movs	r3, #44	; 0x2c\n",
-  " 730:	232e      	movs	r3, #46	; 0x2e\n",
-  " 732:	2330      	movs	r3, #48	; 0x30\n",
-  " 734:	2332      	movs	r3, #50	; 0x32\n",
-  " 736:	2334      	movs	r3, #52	; 0x34\n",
-  " 738:	2336      	movs	r3, #54	; 0x36\n",
-  " 73a:	2338      	movs	r3, #56	; 0x38\n",
-  " 73c:	233a      	movs	r3, #58	; 0x3a\n",
-  " 73e:	233c      	movs	r3, #60	; 0x3c\n",
-  " 740:	233e      	movs	r3, #62	; 0x3e\n",
-  " 742:	2340      	movs	r3, #64	; 0x40\n",
-  " 744:	2342      	movs	r3, #66	; 0x42\n",
-  " 746:	2344      	movs	r3, #68	; 0x44\n",
-  " 748:	2346      	movs	r3, #70	; 0x46\n",
-  " 74a:	2348      	movs	r3, #72	; 0x48\n",
-  " 74c:	234a      	movs	r3, #74	; 0x4a\n",
-  " 74e:	234c      	movs	r3, #76	; 0x4c\n",
-  " 750:	234e      	movs	r3, #78	; 0x4e\n",
-  " 752:	2350      	movs	r3, #80	; 0x50\n",
-  " 754:	2352      	movs	r3, #82	; 0x52\n",
-  " 756:	2354      	movs	r3, #84	; 0x54\n",
-  " 758:	2356      	movs	r3, #86	; 0x56\n",
-  " 75a:	2358      	movs	r3, #88	; 0x58\n",
-  " 75c:	235a      	movs	r3, #90	; 0x5a\n",
-  " 75e:	235c      	movs	r3, #92	; 0x5c\n",
-  " 760:	235e      	movs	r3, #94	; 0x5e\n",
-  " 762:	2360      	movs	r3, #96	; 0x60\n",
-  " 764:	2362      	movs	r3, #98	; 0x62\n",
-  " 766:	2364      	movs	r3, #100	; 0x64\n",
-  " 768:	2366      	movs	r3, #102	; 0x66\n",
-  " 76a:	2368      	movs	r3, #104	; 0x68\n",
-  " 76c:	236a      	movs	r3, #106	; 0x6a\n",
-  " 76e:	236c      	movs	r3, #108	; 0x6c\n",
-  " 770:	236e      	movs	r3, #110	; 0x6e\n",
-  " 772:	2370      	movs	r3, #112	; 0x70\n",
-  " 774:	2372      	movs	r3, #114	; 0x72\n",
-  " 776:	2374      	movs	r3, #116	; 0x74\n",
-  " 778:	2376      	movs	r3, #118	; 0x76\n",
-  " 77a:	2378      	movs	r3, #120	; 0x78\n",
-  " 77c:	237a      	movs	r3, #122	; 0x7a\n",
-  " 77e:	237c      	movs	r3, #124	; 0x7c\n",
-  " 780:	237e      	movs	r3, #126	; 0x7e\n",
-  " 782:	2380      	movs	r3, #128	; 0x80\n",
-  " 784:	2382      	movs	r3, #130	; 0x82\n",
-  " 786:	2384      	movs	r3, #132	; 0x84\n",
-  " 788:	2386      	movs	r3, #134	; 0x86\n",
-  " 78a:	2388      	movs	r3, #136	; 0x88\n",
-  " 78c:	238a      	movs	r3, #138	; 0x8a\n",
-  " 78e:	238c      	movs	r3, #140	; 0x8c\n",
-  " 790:	238e      	movs	r3, #142	; 0x8e\n",
-  " 792:	2390      	movs	r3, #144	; 0x90\n",
-  " 794:	2392      	movs	r3, #146	; 0x92\n",
-  " 796:	2394      	movs	r3, #148	; 0x94\n",
-  " 798:	2396      	movs	r3, #150	; 0x96\n",
-  " 79a:	2398      	movs	r3, #152	; 0x98\n",
-  " 79c:	239a      	movs	r3, #154	; 0x9a\n",
-  " 79e:	239c      	movs	r3, #156	; 0x9c\n",
-  " 7a0:	239e      	movs	r3, #158	; 0x9e\n",
-  " 7a2:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 7a4:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 7a6:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 7a8:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 7aa:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 7ac:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 7ae:	23ac      	movs	r3, #172	; 0xac\n",
-  " 7b0:	23ae      	movs	r3, #174	; 0xae\n",
-  " 7b2:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 7b4:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 7b6:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 7b8:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 7ba:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 7bc:	23ba      	movs	r3, #186	; 0xba\n",
-  " 7be:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 7c0:	23be      	movs	r3, #190	; 0xbe\n",
-  " 7c2:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 7c4:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 7c6:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 7c8:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 7ca:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 7cc:	23ca      	movs	r3, #202	; 0xca\n",
-  " 7ce:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 7d0:	23ce      	movs	r3, #206	; 0xce\n",
-  " 7d2:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 7d4:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 7d6:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 7d8:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 7da:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 7dc:	23da      	movs	r3, #218	; 0xda\n",
-  " 7de:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 7e0:	23de      	movs	r3, #222	; 0xde\n",
-  " 7e2:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 7e4:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 7e6:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 7e8:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 7ea:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 7ec:	23ea      	movs	r3, #234	; 0xea\n",
-  " 7ee:	23ec      	movs	r3, #236	; 0xec\n",
-  " 7f0:	23ee      	movs	r3, #238	; 0xee\n",
-  " 7f2:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 7f4:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 7f6:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 7f8:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 7fa:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 7fc:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 7fe:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 800:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 802:	4611      	mov	r1, r2\n",
-  nullptr
-};
-const char* const Branch32Results[] = {
-  "   0:	f000 bc01 	b.w	806 <Branch32+0x806>\n",
-  "   4:	2300      	movs	r3, #0\n",
-  "   6:	2302      	movs	r3, #2\n",
-  "   8:	2304      	movs	r3, #4\n",
-  "   a:	2306      	movs	r3, #6\n",
-  "   c:	2308      	movs	r3, #8\n",
-  "   e:	230a      	movs	r3, #10\n",
-  "  10:	230c      	movs	r3, #12\n",
-  "  12:	230e      	movs	r3, #14\n",
-  "  14:	2310      	movs	r3, #16\n",
-  "  16:	2312      	movs	r3, #18\n",
-  "  18:	2314      	movs	r3, #20\n",
-  "  1a:	2316      	movs	r3, #22\n",
-  "  1c:	2318      	movs	r3, #24\n",
-  "  1e:	231a      	movs	r3, #26\n",
-  "  20:	231c      	movs	r3, #28\n",
-  "  22:	231e      	movs	r3, #30\n",
-  "  24:	2320      	movs	r3, #32\n",
-  "  26:	2322      	movs	r3, #34	; 0x22\n",
-  "  28:	2324      	movs	r3, #36	; 0x24\n",
-  "  2a:	2326      	movs	r3, #38	; 0x26\n",
-  "  2c:	2328      	movs	r3, #40	; 0x28\n",
-  "  2e:	232a      	movs	r3, #42	; 0x2a\n",
-  "  30:	232c      	movs	r3, #44	; 0x2c\n",
-  "  32:	232e      	movs	r3, #46	; 0x2e\n",
-  "  34:	2330      	movs	r3, #48	; 0x30\n",
-  "  36:	2332      	movs	r3, #50	; 0x32\n",
-  "  38:	2334      	movs	r3, #52	; 0x34\n",
-  "  3a:	2336      	movs	r3, #54	; 0x36\n",
-  "  3c:	2338      	movs	r3, #56	; 0x38\n",
-  "  3e:	233a      	movs	r3, #58	; 0x3a\n",
-  "  40:	233c      	movs	r3, #60	; 0x3c\n",
-  "  42:	233e      	movs	r3, #62	; 0x3e\n",
-  "  44:	2340      	movs	r3, #64	; 0x40\n",
-  "  46:	2342      	movs	r3, #66	; 0x42\n",
-  "  48:	2344      	movs	r3, #68	; 0x44\n",
-  "  4a:	2346      	movs	r3, #70	; 0x46\n",
-  "  4c:	2348      	movs	r3, #72	; 0x48\n",
-  "  4e:	234a      	movs	r3, #74	; 0x4a\n",
-  "  50:	234c      	movs	r3, #76	; 0x4c\n",
-  "  52:	234e      	movs	r3, #78	; 0x4e\n",
-  "  54:	2350      	movs	r3, #80	; 0x50\n",
-  "  56:	2352      	movs	r3, #82	; 0x52\n",
-  "  58:	2354      	movs	r3, #84	; 0x54\n",
-  "  5a:	2356      	movs	r3, #86	; 0x56\n",
-  "  5c:	2358      	movs	r3, #88	; 0x58\n",
-  "  5e:	235a      	movs	r3, #90	; 0x5a\n",
-  "  60:	235c      	movs	r3, #92	; 0x5c\n",
-  "  62:	235e      	movs	r3, #94	; 0x5e\n",
-  "  64:	2360      	movs	r3, #96	; 0x60\n",
-  "  66:	2362      	movs	r3, #98	; 0x62\n",
-  "  68:	2364      	movs	r3, #100	; 0x64\n",
-  "  6a:	2366      	movs	r3, #102	; 0x66\n",
-  "  6c:	2368      	movs	r3, #104	; 0x68\n",
-  "  6e:	236a      	movs	r3, #106	; 0x6a\n",
-  "  70:	236c      	movs	r3, #108	; 0x6c\n",
-  "  72:	236e      	movs	r3, #110	; 0x6e\n",
-  "  74:	2370      	movs	r3, #112	; 0x70\n",
-  "  76:	2372      	movs	r3, #114	; 0x72\n",
-  "  78:	2374      	movs	r3, #116	; 0x74\n",
-  "  7a:	2376      	movs	r3, #118	; 0x76\n",
-  "  7c:	2378      	movs	r3, #120	; 0x78\n",
-  "  7e:	237a      	movs	r3, #122	; 0x7a\n",
-  "  80:	237c      	movs	r3, #124	; 0x7c\n",
-  "  82:	237e      	movs	r3, #126	; 0x7e\n",
-  "  84:	2380      	movs	r3, #128	; 0x80\n",
-  "  86:	2382      	movs	r3, #130	; 0x82\n",
-  "  88:	2384      	movs	r3, #132	; 0x84\n",
-  "  8a:	2386      	movs	r3, #134	; 0x86\n",
-  "  8c:	2388      	movs	r3, #136	; 0x88\n",
-  "  8e:	238a      	movs	r3, #138	; 0x8a\n",
-  "  90:	238c      	movs	r3, #140	; 0x8c\n",
-  "  92:	238e      	movs	r3, #142	; 0x8e\n",
-  "  94:	2390      	movs	r3, #144	; 0x90\n",
-  "  96:	2392      	movs	r3, #146	; 0x92\n",
-  "  98:	2394      	movs	r3, #148	; 0x94\n",
-  "  9a:	2396      	movs	r3, #150	; 0x96\n",
-  "  9c:	2398      	movs	r3, #152	; 0x98\n",
-  "  9e:	239a      	movs	r3, #154	; 0x9a\n",
-  "  a0:	239c      	movs	r3, #156	; 0x9c\n",
-  "  a2:	239e      	movs	r3, #158	; 0x9e\n",
-  "  a4:	23a0      	movs	r3, #160	; 0xa0\n",
-  "  a6:	23a2      	movs	r3, #162	; 0xa2\n",
-  "  a8:	23a4      	movs	r3, #164	; 0xa4\n",
-  "  aa:	23a6      	movs	r3, #166	; 0xa6\n",
-  "  ac:	23a8      	movs	r3, #168	; 0xa8\n",
-  "  ae:	23aa      	movs	r3, #170	; 0xaa\n",
-  "  b0:	23ac      	movs	r3, #172	; 0xac\n",
-  "  b2:	23ae      	movs	r3, #174	; 0xae\n",
-  "  b4:	23b0      	movs	r3, #176	; 0xb0\n",
-  "  b6:	23b2      	movs	r3, #178	; 0xb2\n",
-  "  b8:	23b4      	movs	r3, #180	; 0xb4\n",
-  "  ba:	23b6      	movs	r3, #182	; 0xb6\n",
-  "  bc:	23b8      	movs	r3, #184	; 0xb8\n",
-  "  be:	23ba      	movs	r3, #186	; 0xba\n",
-  "  c0:	23bc      	movs	r3, #188	; 0xbc\n",
-  "  c2:	23be      	movs	r3, #190	; 0xbe\n",
-  "  c4:	23c0      	movs	r3, #192	; 0xc0\n",
-  "  c6:	23c2      	movs	r3, #194	; 0xc2\n",
-  "  c8:	23c4      	movs	r3, #196	; 0xc4\n",
-  "  ca:	23c6      	movs	r3, #198	; 0xc6\n",
-  "  cc:	23c8      	movs	r3, #200	; 0xc8\n",
-  "  ce:	23ca      	movs	r3, #202	; 0xca\n",
-  "  d0:	23cc      	movs	r3, #204	; 0xcc\n",
-  "  d2:	23ce      	movs	r3, #206	; 0xce\n",
-  "  d4:	23d0      	movs	r3, #208	; 0xd0\n",
-  "  d6:	23d2      	movs	r3, #210	; 0xd2\n",
-  "  d8:	23d4      	movs	r3, #212	; 0xd4\n",
-  "  da:	23d6      	movs	r3, #214	; 0xd6\n",
-  "  dc:	23d8      	movs	r3, #216	; 0xd8\n",
-  "  de:	23da      	movs	r3, #218	; 0xda\n",
-  "  e0:	23dc      	movs	r3, #220	; 0xdc\n",
-  "  e2:	23de      	movs	r3, #222	; 0xde\n",
-  "  e4:	23e0      	movs	r3, #224	; 0xe0\n",
-  "  e6:	23e2      	movs	r3, #226	; 0xe2\n",
-  "  e8:	23e4      	movs	r3, #228	; 0xe4\n",
-  "  ea:	23e6      	movs	r3, #230	; 0xe6\n",
-  "  ec:	23e8      	movs	r3, #232	; 0xe8\n",
-  "  ee:	23ea      	movs	r3, #234	; 0xea\n",
-  "  f0:	23ec      	movs	r3, #236	; 0xec\n",
-  "  f2:	23ee      	movs	r3, #238	; 0xee\n",
-  "  f4:	23f0      	movs	r3, #240	; 0xf0\n",
-  "  f6:	23f2      	movs	r3, #242	; 0xf2\n",
-  "  f8:	23f4      	movs	r3, #244	; 0xf4\n",
-  "  fa:	23f6      	movs	r3, #246	; 0xf6\n",
-  "  fc:	23f8      	movs	r3, #248	; 0xf8\n",
-  "  fe:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 100:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 102:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 104:	2300      	movs	r3, #0\n",
-  " 106:	2302      	movs	r3, #2\n",
-  " 108:	2304      	movs	r3, #4\n",
-  " 10a:	2306      	movs	r3, #6\n",
-  " 10c:	2308      	movs	r3, #8\n",
-  " 10e:	230a      	movs	r3, #10\n",
-  " 110:	230c      	movs	r3, #12\n",
-  " 112:	230e      	movs	r3, #14\n",
-  " 114:	2310      	movs	r3, #16\n",
-  " 116:	2312      	movs	r3, #18\n",
-  " 118:	2314      	movs	r3, #20\n",
-  " 11a:	2316      	movs	r3, #22\n",
-  " 11c:	2318      	movs	r3, #24\n",
-  " 11e:	231a      	movs	r3, #26\n",
-  " 120:	231c      	movs	r3, #28\n",
-  " 122:	231e      	movs	r3, #30\n",
-  " 124:	2320      	movs	r3, #32\n",
-  " 126:	2322      	movs	r3, #34	; 0x22\n",
-  " 128:	2324      	movs	r3, #36	; 0x24\n",
-  " 12a:	2326      	movs	r3, #38	; 0x26\n",
-  " 12c:	2328      	movs	r3, #40	; 0x28\n",
-  " 12e:	232a      	movs	r3, #42	; 0x2a\n",
-  " 130:	232c      	movs	r3, #44	; 0x2c\n",
-  " 132:	232e      	movs	r3, #46	; 0x2e\n",
-  " 134:	2330      	movs	r3, #48	; 0x30\n",
-  " 136:	2332      	movs	r3, #50	; 0x32\n",
-  " 138:	2334      	movs	r3, #52	; 0x34\n",
-  " 13a:	2336      	movs	r3, #54	; 0x36\n",
-  " 13c:	2338      	movs	r3, #56	; 0x38\n",
-  " 13e:	233a      	movs	r3, #58	; 0x3a\n",
-  " 140:	233c      	movs	r3, #60	; 0x3c\n",
-  " 142:	233e      	movs	r3, #62	; 0x3e\n",
-  " 144:	2340      	movs	r3, #64	; 0x40\n",
-  " 146:	2342      	movs	r3, #66	; 0x42\n",
-  " 148:	2344      	movs	r3, #68	; 0x44\n",
-  " 14a:	2346      	movs	r3, #70	; 0x46\n",
-  " 14c:	2348      	movs	r3, #72	; 0x48\n",
-  " 14e:	234a      	movs	r3, #74	; 0x4a\n",
-  " 150:	234c      	movs	r3, #76	; 0x4c\n",
-  " 152:	234e      	movs	r3, #78	; 0x4e\n",
-  " 154:	2350      	movs	r3, #80	; 0x50\n",
-  " 156:	2352      	movs	r3, #82	; 0x52\n",
-  " 158:	2354      	movs	r3, #84	; 0x54\n",
-  " 15a:	2356      	movs	r3, #86	; 0x56\n",
-  " 15c:	2358      	movs	r3, #88	; 0x58\n",
-  " 15e:	235a      	movs	r3, #90	; 0x5a\n",
-  " 160:	235c      	movs	r3, #92	; 0x5c\n",
-  " 162:	235e      	movs	r3, #94	; 0x5e\n",
-  " 164:	2360      	movs	r3, #96	; 0x60\n",
-  " 166:	2362      	movs	r3, #98	; 0x62\n",
-  " 168:	2364      	movs	r3, #100	; 0x64\n",
-  " 16a:	2366      	movs	r3, #102	; 0x66\n",
-  " 16c:	2368      	movs	r3, #104	; 0x68\n",
-  " 16e:	236a      	movs	r3, #106	; 0x6a\n",
-  " 170:	236c      	movs	r3, #108	; 0x6c\n",
-  " 172:	236e      	movs	r3, #110	; 0x6e\n",
-  " 174:	2370      	movs	r3, #112	; 0x70\n",
-  " 176:	2372      	movs	r3, #114	; 0x72\n",
-  " 178:	2374      	movs	r3, #116	; 0x74\n",
-  " 17a:	2376      	movs	r3, #118	; 0x76\n",
-  " 17c:	2378      	movs	r3, #120	; 0x78\n",
-  " 17e:	237a      	movs	r3, #122	; 0x7a\n",
-  " 180:	237c      	movs	r3, #124	; 0x7c\n",
-  " 182:	237e      	movs	r3, #126	; 0x7e\n",
-  " 184:	2380      	movs	r3, #128	; 0x80\n",
-  " 186:	2382      	movs	r3, #130	; 0x82\n",
-  " 188:	2384      	movs	r3, #132	; 0x84\n",
-  " 18a:	2386      	movs	r3, #134	; 0x86\n",
-  " 18c:	2388      	movs	r3, #136	; 0x88\n",
-  " 18e:	238a      	movs	r3, #138	; 0x8a\n",
-  " 190:	238c      	movs	r3, #140	; 0x8c\n",
-  " 192:	238e      	movs	r3, #142	; 0x8e\n",
-  " 194:	2390      	movs	r3, #144	; 0x90\n",
-  " 196:	2392      	movs	r3, #146	; 0x92\n",
-  " 198:	2394      	movs	r3, #148	; 0x94\n",
-  " 19a:	2396      	movs	r3, #150	; 0x96\n",
-  " 19c:	2398      	movs	r3, #152	; 0x98\n",
-  " 19e:	239a      	movs	r3, #154	; 0x9a\n",
-  " 1a0:	239c      	movs	r3, #156	; 0x9c\n",
-  " 1a2:	239e      	movs	r3, #158	; 0x9e\n",
-  " 1a4:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 1a6:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 1a8:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 1aa:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 1ac:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 1ae:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 1b0:	23ac      	movs	r3, #172	; 0xac\n",
-  " 1b2:	23ae      	movs	r3, #174	; 0xae\n",
-  " 1b4:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 1b6:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 1b8:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 1ba:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 1bc:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 1be:	23ba      	movs	r3, #186	; 0xba\n",
-  " 1c0:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 1c2:	23be      	movs	r3, #190	; 0xbe\n",
-  " 1c4:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 1c6:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 1c8:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 1ca:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 1cc:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 1ce:	23ca      	movs	r3, #202	; 0xca\n",
-  " 1d0:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 1d2:	23ce      	movs	r3, #206	; 0xce\n",
-  " 1d4:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 1d6:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 1d8:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 1da:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 1dc:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 1de:	23da      	movs	r3, #218	; 0xda\n",
-  " 1e0:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 1e2:	23de      	movs	r3, #222	; 0xde\n",
-  " 1e4:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 1e6:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 1e8:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 1ea:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 1ec:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 1ee:	23ea      	movs	r3, #234	; 0xea\n",
-  " 1f0:	23ec      	movs	r3, #236	; 0xec\n",
-  " 1f2:	23ee      	movs	r3, #238	; 0xee\n",
-  " 1f4:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 1f6:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 1f8:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 1fa:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 1fc:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 1fe:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 200:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 202:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 204:	2300      	movs	r3, #0\n",
-  " 206:	2302      	movs	r3, #2\n",
-  " 208:	2304      	movs	r3, #4\n",
-  " 20a:	2306      	movs	r3, #6\n",
-  " 20c:	2308      	movs	r3, #8\n",
-  " 20e:	230a      	movs	r3, #10\n",
-  " 210:	230c      	movs	r3, #12\n",
-  " 212:	230e      	movs	r3, #14\n",
-  " 214:	2310      	movs	r3, #16\n",
-  " 216:	2312      	movs	r3, #18\n",
-  " 218:	2314      	movs	r3, #20\n",
-  " 21a:	2316      	movs	r3, #22\n",
-  " 21c:	2318      	movs	r3, #24\n",
-  " 21e:	231a      	movs	r3, #26\n",
-  " 220:	231c      	movs	r3, #28\n",
-  " 222:	231e      	movs	r3, #30\n",
-  " 224:	2320      	movs	r3, #32\n",
-  " 226:	2322      	movs	r3, #34	; 0x22\n",
-  " 228:	2324      	movs	r3, #36	; 0x24\n",
-  " 22a:	2326      	movs	r3, #38	; 0x26\n",
-  " 22c:	2328      	movs	r3, #40	; 0x28\n",
-  " 22e:	232a      	movs	r3, #42	; 0x2a\n",
-  " 230:	232c      	movs	r3, #44	; 0x2c\n",
-  " 232:	232e      	movs	r3, #46	; 0x2e\n",
-  " 234:	2330      	movs	r3, #48	; 0x30\n",
-  " 236:	2332      	movs	r3, #50	; 0x32\n",
-  " 238:	2334      	movs	r3, #52	; 0x34\n",
-  " 23a:	2336      	movs	r3, #54	; 0x36\n",
-  " 23c:	2338      	movs	r3, #56	; 0x38\n",
-  " 23e:	233a      	movs	r3, #58	; 0x3a\n",
-  " 240:	233c      	movs	r3, #60	; 0x3c\n",
-  " 242:	233e      	movs	r3, #62	; 0x3e\n",
-  " 244:	2340      	movs	r3, #64	; 0x40\n",
-  " 246:	2342      	movs	r3, #66	; 0x42\n",
-  " 248:	2344      	movs	r3, #68	; 0x44\n",
-  " 24a:	2346      	movs	r3, #70	; 0x46\n",
-  " 24c:	2348      	movs	r3, #72	; 0x48\n",
-  " 24e:	234a      	movs	r3, #74	; 0x4a\n",
-  " 250:	234c      	movs	r3, #76	; 0x4c\n",
-  " 252:	234e      	movs	r3, #78	; 0x4e\n",
-  " 254:	2350      	movs	r3, #80	; 0x50\n",
-  " 256:	2352      	movs	r3, #82	; 0x52\n",
-  " 258:	2354      	movs	r3, #84	; 0x54\n",
-  " 25a:	2356      	movs	r3, #86	; 0x56\n",
-  " 25c:	2358      	movs	r3, #88	; 0x58\n",
-  " 25e:	235a      	movs	r3, #90	; 0x5a\n",
-  " 260:	235c      	movs	r3, #92	; 0x5c\n",
-  " 262:	235e      	movs	r3, #94	; 0x5e\n",
-  " 264:	2360      	movs	r3, #96	; 0x60\n",
-  " 266:	2362      	movs	r3, #98	; 0x62\n",
-  " 268:	2364      	movs	r3, #100	; 0x64\n",
-  " 26a:	2366      	movs	r3, #102	; 0x66\n",
-  " 26c:	2368      	movs	r3, #104	; 0x68\n",
-  " 26e:	236a      	movs	r3, #106	; 0x6a\n",
-  " 270:	236c      	movs	r3, #108	; 0x6c\n",
-  " 272:	236e      	movs	r3, #110	; 0x6e\n",
-  " 274:	2370      	movs	r3, #112	; 0x70\n",
-  " 276:	2372      	movs	r3, #114	; 0x72\n",
-  " 278:	2374      	movs	r3, #116	; 0x74\n",
-  " 27a:	2376      	movs	r3, #118	; 0x76\n",
-  " 27c:	2378      	movs	r3, #120	; 0x78\n",
-  " 27e:	237a      	movs	r3, #122	; 0x7a\n",
-  " 280:	237c      	movs	r3, #124	; 0x7c\n",
-  " 282:	237e      	movs	r3, #126	; 0x7e\n",
-  " 284:	2380      	movs	r3, #128	; 0x80\n",
-  " 286:	2382      	movs	r3, #130	; 0x82\n",
-  " 288:	2384      	movs	r3, #132	; 0x84\n",
-  " 28a:	2386      	movs	r3, #134	; 0x86\n",
-  " 28c:	2388      	movs	r3, #136	; 0x88\n",
-  " 28e:	238a      	movs	r3, #138	; 0x8a\n",
-  " 290:	238c      	movs	r3, #140	; 0x8c\n",
-  " 292:	238e      	movs	r3, #142	; 0x8e\n",
-  " 294:	2390      	movs	r3, #144	; 0x90\n",
-  " 296:	2392      	movs	r3, #146	; 0x92\n",
-  " 298:	2394      	movs	r3, #148	; 0x94\n",
-  " 29a:	2396      	movs	r3, #150	; 0x96\n",
-  " 29c:	2398      	movs	r3, #152	; 0x98\n",
-  " 29e:	239a      	movs	r3, #154	; 0x9a\n",
-  " 2a0:	239c      	movs	r3, #156	; 0x9c\n",
-  " 2a2:	239e      	movs	r3, #158	; 0x9e\n",
-  " 2a4:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 2a6:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 2a8:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 2aa:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 2ac:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 2ae:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 2b0:	23ac      	movs	r3, #172	; 0xac\n",
-  " 2b2:	23ae      	movs	r3, #174	; 0xae\n",
-  " 2b4:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 2b6:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 2b8:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 2ba:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 2bc:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 2be:	23ba      	movs	r3, #186	; 0xba\n",
-  " 2c0:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 2c2:	23be      	movs	r3, #190	; 0xbe\n",
-  " 2c4:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 2c6:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 2c8:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 2ca:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 2cc:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 2ce:	23ca      	movs	r3, #202	; 0xca\n",
-  " 2d0:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 2d2:	23ce      	movs	r3, #206	; 0xce\n",
-  " 2d4:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 2d6:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 2d8:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 2da:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 2dc:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 2de:	23da      	movs	r3, #218	; 0xda\n",
-  " 2e0:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 2e2:	23de      	movs	r3, #222	; 0xde\n",
-  " 2e4:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 2e6:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 2e8:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 2ea:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 2ec:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 2ee:	23ea      	movs	r3, #234	; 0xea\n",
-  " 2f0:	23ec      	movs	r3, #236	; 0xec\n",
-  " 2f2:	23ee      	movs	r3, #238	; 0xee\n",
-  " 2f4:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 2f6:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 2f8:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 2fa:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 2fc:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 2fe:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 300:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 302:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 304:	2300      	movs	r3, #0\n",
-  " 306:	2302      	movs	r3, #2\n",
-  " 308:	2304      	movs	r3, #4\n",
-  " 30a:	2306      	movs	r3, #6\n",
-  " 30c:	2308      	movs	r3, #8\n",
-  " 30e:	230a      	movs	r3, #10\n",
-  " 310:	230c      	movs	r3, #12\n",
-  " 312:	230e      	movs	r3, #14\n",
-  " 314:	2310      	movs	r3, #16\n",
-  " 316:	2312      	movs	r3, #18\n",
-  " 318:	2314      	movs	r3, #20\n",
-  " 31a:	2316      	movs	r3, #22\n",
-  " 31c:	2318      	movs	r3, #24\n",
-  " 31e:	231a      	movs	r3, #26\n",
-  " 320:	231c      	movs	r3, #28\n",
-  " 322:	231e      	movs	r3, #30\n",
-  " 324:	2320      	movs	r3, #32\n",
-  " 326:	2322      	movs	r3, #34	; 0x22\n",
-  " 328:	2324      	movs	r3, #36	; 0x24\n",
-  " 32a:	2326      	movs	r3, #38	; 0x26\n",
-  " 32c:	2328      	movs	r3, #40	; 0x28\n",
-  " 32e:	232a      	movs	r3, #42	; 0x2a\n",
-  " 330:	232c      	movs	r3, #44	; 0x2c\n",
-  " 332:	232e      	movs	r3, #46	; 0x2e\n",
-  " 334:	2330      	movs	r3, #48	; 0x30\n",
-  " 336:	2332      	movs	r3, #50	; 0x32\n",
-  " 338:	2334      	movs	r3, #52	; 0x34\n",
-  " 33a:	2336      	movs	r3, #54	; 0x36\n",
-  " 33c:	2338      	movs	r3, #56	; 0x38\n",
-  " 33e:	233a      	movs	r3, #58	; 0x3a\n",
-  " 340:	233c      	movs	r3, #60	; 0x3c\n",
-  " 342:	233e      	movs	r3, #62	; 0x3e\n",
-  " 344:	2340      	movs	r3, #64	; 0x40\n",
-  " 346:	2342      	movs	r3, #66	; 0x42\n",
-  " 348:	2344      	movs	r3, #68	; 0x44\n",
-  " 34a:	2346      	movs	r3, #70	; 0x46\n",
-  " 34c:	2348      	movs	r3, #72	; 0x48\n",
-  " 34e:	234a      	movs	r3, #74	; 0x4a\n",
-  " 350:	234c      	movs	r3, #76	; 0x4c\n",
-  " 352:	234e      	movs	r3, #78	; 0x4e\n",
-  " 354:	2350      	movs	r3, #80	; 0x50\n",
-  " 356:	2352      	movs	r3, #82	; 0x52\n",
-  " 358:	2354      	movs	r3, #84	; 0x54\n",
-  " 35a:	2356      	movs	r3, #86	; 0x56\n",
-  " 35c:	2358      	movs	r3, #88	; 0x58\n",
-  " 35e:	235a      	movs	r3, #90	; 0x5a\n",
-  " 360:	235c      	movs	r3, #92	; 0x5c\n",
-  " 362:	235e      	movs	r3, #94	; 0x5e\n",
-  " 364:	2360      	movs	r3, #96	; 0x60\n",
-  " 366:	2362      	movs	r3, #98	; 0x62\n",
-  " 368:	2364      	movs	r3, #100	; 0x64\n",
-  " 36a:	2366      	movs	r3, #102	; 0x66\n",
-  " 36c:	2368      	movs	r3, #104	; 0x68\n",
-  " 36e:	236a      	movs	r3, #106	; 0x6a\n",
-  " 370:	236c      	movs	r3, #108	; 0x6c\n",
-  " 372:	236e      	movs	r3, #110	; 0x6e\n",
-  " 374:	2370      	movs	r3, #112	; 0x70\n",
-  " 376:	2372      	movs	r3, #114	; 0x72\n",
-  " 378:	2374      	movs	r3, #116	; 0x74\n",
-  " 37a:	2376      	movs	r3, #118	; 0x76\n",
-  " 37c:	2378      	movs	r3, #120	; 0x78\n",
-  " 37e:	237a      	movs	r3, #122	; 0x7a\n",
-  " 380:	237c      	movs	r3, #124	; 0x7c\n",
-  " 382:	237e      	movs	r3, #126	; 0x7e\n",
-  " 384:	2380      	movs	r3, #128	; 0x80\n",
-  " 386:	2382      	movs	r3, #130	; 0x82\n",
-  " 388:	2384      	movs	r3, #132	; 0x84\n",
-  " 38a:	2386      	movs	r3, #134	; 0x86\n",
-  " 38c:	2388      	movs	r3, #136	; 0x88\n",
-  " 38e:	238a      	movs	r3, #138	; 0x8a\n",
-  " 390:	238c      	movs	r3, #140	; 0x8c\n",
-  " 392:	238e      	movs	r3, #142	; 0x8e\n",
-  " 394:	2390      	movs	r3, #144	; 0x90\n",
-  " 396:	2392      	movs	r3, #146	; 0x92\n",
-  " 398:	2394      	movs	r3, #148	; 0x94\n",
-  " 39a:	2396      	movs	r3, #150	; 0x96\n",
-  " 39c:	2398      	movs	r3, #152	; 0x98\n",
-  " 39e:	239a      	movs	r3, #154	; 0x9a\n",
-  " 3a0:	239c      	movs	r3, #156	; 0x9c\n",
-  " 3a2:	239e      	movs	r3, #158	; 0x9e\n",
-  " 3a4:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 3a6:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 3a8:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 3aa:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 3ac:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 3ae:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 3b0:	23ac      	movs	r3, #172	; 0xac\n",
-  " 3b2:	23ae      	movs	r3, #174	; 0xae\n",
-  " 3b4:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 3b6:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 3b8:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 3ba:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 3bc:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 3be:	23ba      	movs	r3, #186	; 0xba\n",
-  " 3c0:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 3c2:	23be      	movs	r3, #190	; 0xbe\n",
-  " 3c4:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 3c6:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 3c8:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 3ca:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 3cc:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 3ce:	23ca      	movs	r3, #202	; 0xca\n",
-  " 3d0:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 3d2:	23ce      	movs	r3, #206	; 0xce\n",
-  " 3d4:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 3d6:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 3d8:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 3da:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 3dc:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 3de:	23da      	movs	r3, #218	; 0xda\n",
-  " 3e0:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 3e2:	23de      	movs	r3, #222	; 0xde\n",
-  " 3e4:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 3e6:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 3e8:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 3ea:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 3ec:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 3ee:	23ea      	movs	r3, #234	; 0xea\n",
-  " 3f0:	23ec      	movs	r3, #236	; 0xec\n",
-  " 3f2:	23ee      	movs	r3, #238	; 0xee\n",
-  " 3f4:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 3f6:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 3f8:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 3fa:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 3fc:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 3fe:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 400:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 402:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 404:	2300      	movs	r3, #0\n",
-  " 406:	2302      	movs	r3, #2\n",
-  " 408:	2304      	movs	r3, #4\n",
-  " 40a:	2306      	movs	r3, #6\n",
-  " 40c:	2308      	movs	r3, #8\n",
-  " 40e:	230a      	movs	r3, #10\n",
-  " 410:	230c      	movs	r3, #12\n",
-  " 412:	230e      	movs	r3, #14\n",
-  " 414:	2310      	movs	r3, #16\n",
-  " 416:	2312      	movs	r3, #18\n",
-  " 418:	2314      	movs	r3, #20\n",
-  " 41a:	2316      	movs	r3, #22\n",
-  " 41c:	2318      	movs	r3, #24\n",
-  " 41e:	231a      	movs	r3, #26\n",
-  " 420:	231c      	movs	r3, #28\n",
-  " 422:	231e      	movs	r3, #30\n",
-  " 424:	2320      	movs	r3, #32\n",
-  " 426:	2322      	movs	r3, #34	; 0x22\n",
-  " 428:	2324      	movs	r3, #36	; 0x24\n",
-  " 42a:	2326      	movs	r3, #38	; 0x26\n",
-  " 42c:	2328      	movs	r3, #40	; 0x28\n",
-  " 42e:	232a      	movs	r3, #42	; 0x2a\n",
-  " 430:	232c      	movs	r3, #44	; 0x2c\n",
-  " 432:	232e      	movs	r3, #46	; 0x2e\n",
-  " 434:	2330      	movs	r3, #48	; 0x30\n",
-  " 436:	2332      	movs	r3, #50	; 0x32\n",
-  " 438:	2334      	movs	r3, #52	; 0x34\n",
-  " 43a:	2336      	movs	r3, #54	; 0x36\n",
-  " 43c:	2338      	movs	r3, #56	; 0x38\n",
-  " 43e:	233a      	movs	r3, #58	; 0x3a\n",
-  " 440:	233c      	movs	r3, #60	; 0x3c\n",
-  " 442:	233e      	movs	r3, #62	; 0x3e\n",
-  " 444:	2340      	movs	r3, #64	; 0x40\n",
-  " 446:	2342      	movs	r3, #66	; 0x42\n",
-  " 448:	2344      	movs	r3, #68	; 0x44\n",
-  " 44a:	2346      	movs	r3, #70	; 0x46\n",
-  " 44c:	2348      	movs	r3, #72	; 0x48\n",
-  " 44e:	234a      	movs	r3, #74	; 0x4a\n",
-  " 450:	234c      	movs	r3, #76	; 0x4c\n",
-  " 452:	234e      	movs	r3, #78	; 0x4e\n",
-  " 454:	2350      	movs	r3, #80	; 0x50\n",
-  " 456:	2352      	movs	r3, #82	; 0x52\n",
-  " 458:	2354      	movs	r3, #84	; 0x54\n",
-  " 45a:	2356      	movs	r3, #86	; 0x56\n",
-  " 45c:	2358      	movs	r3, #88	; 0x58\n",
-  " 45e:	235a      	movs	r3, #90	; 0x5a\n",
-  " 460:	235c      	movs	r3, #92	; 0x5c\n",
-  " 462:	235e      	movs	r3, #94	; 0x5e\n",
-  " 464:	2360      	movs	r3, #96	; 0x60\n",
-  " 466:	2362      	movs	r3, #98	; 0x62\n",
-  " 468:	2364      	movs	r3, #100	; 0x64\n",
-  " 46a:	2366      	movs	r3, #102	; 0x66\n",
-  " 46c:	2368      	movs	r3, #104	; 0x68\n",
-  " 46e:	236a      	movs	r3, #106	; 0x6a\n",
-  " 470:	236c      	movs	r3, #108	; 0x6c\n",
-  " 472:	236e      	movs	r3, #110	; 0x6e\n",
-  " 474:	2370      	movs	r3, #112	; 0x70\n",
-  " 476:	2372      	movs	r3, #114	; 0x72\n",
-  " 478:	2374      	movs	r3, #116	; 0x74\n",
-  " 47a:	2376      	movs	r3, #118	; 0x76\n",
-  " 47c:	2378      	movs	r3, #120	; 0x78\n",
-  " 47e:	237a      	movs	r3, #122	; 0x7a\n",
-  " 480:	237c      	movs	r3, #124	; 0x7c\n",
-  " 482:	237e      	movs	r3, #126	; 0x7e\n",
-  " 484:	2380      	movs	r3, #128	; 0x80\n",
-  " 486:	2382      	movs	r3, #130	; 0x82\n",
-  " 488:	2384      	movs	r3, #132	; 0x84\n",
-  " 48a:	2386      	movs	r3, #134	; 0x86\n",
-  " 48c:	2388      	movs	r3, #136	; 0x88\n",
-  " 48e:	238a      	movs	r3, #138	; 0x8a\n",
-  " 490:	238c      	movs	r3, #140	; 0x8c\n",
-  " 492:	238e      	movs	r3, #142	; 0x8e\n",
-  " 494:	2390      	movs	r3, #144	; 0x90\n",
-  " 496:	2392      	movs	r3, #146	; 0x92\n",
-  " 498:	2394      	movs	r3, #148	; 0x94\n",
-  " 49a:	2396      	movs	r3, #150	; 0x96\n",
-  " 49c:	2398      	movs	r3, #152	; 0x98\n",
-  " 49e:	239a      	movs	r3, #154	; 0x9a\n",
-  " 4a0:	239c      	movs	r3, #156	; 0x9c\n",
-  " 4a2:	239e      	movs	r3, #158	; 0x9e\n",
-  " 4a4:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 4a6:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 4a8:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 4aa:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 4ac:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 4ae:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 4b0:	23ac      	movs	r3, #172	; 0xac\n",
-  " 4b2:	23ae      	movs	r3, #174	; 0xae\n",
-  " 4b4:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 4b6:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 4b8:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 4ba:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 4bc:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 4be:	23ba      	movs	r3, #186	; 0xba\n",
-  " 4c0:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 4c2:	23be      	movs	r3, #190	; 0xbe\n",
-  " 4c4:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 4c6:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 4c8:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 4ca:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 4cc:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 4ce:	23ca      	movs	r3, #202	; 0xca\n",
-  " 4d0:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 4d2:	23ce      	movs	r3, #206	; 0xce\n",
-  " 4d4:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 4d6:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 4d8:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 4da:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 4dc:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 4de:	23da      	movs	r3, #218	; 0xda\n",
-  " 4e0:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 4e2:	23de      	movs	r3, #222	; 0xde\n",
-  " 4e4:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 4e6:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 4e8:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 4ea:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 4ec:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 4ee:	23ea      	movs	r3, #234	; 0xea\n",
-  " 4f0:	23ec      	movs	r3, #236	; 0xec\n",
-  " 4f2:	23ee      	movs	r3, #238	; 0xee\n",
-  " 4f4:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 4f6:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 4f8:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 4fa:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 4fc:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 4fe:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 500:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 502:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 504:	2300      	movs	r3, #0\n",
-  " 506:	2302      	movs	r3, #2\n",
-  " 508:	2304      	movs	r3, #4\n",
-  " 50a:	2306      	movs	r3, #6\n",
-  " 50c:	2308      	movs	r3, #8\n",
-  " 50e:	230a      	movs	r3, #10\n",
-  " 510:	230c      	movs	r3, #12\n",
-  " 512:	230e      	movs	r3, #14\n",
-  " 514:	2310      	movs	r3, #16\n",
-  " 516:	2312      	movs	r3, #18\n",
-  " 518:	2314      	movs	r3, #20\n",
-  " 51a:	2316      	movs	r3, #22\n",
-  " 51c:	2318      	movs	r3, #24\n",
-  " 51e:	231a      	movs	r3, #26\n",
-  " 520:	231c      	movs	r3, #28\n",
-  " 522:	231e      	movs	r3, #30\n",
-  " 524:	2320      	movs	r3, #32\n",
-  " 526:	2322      	movs	r3, #34	; 0x22\n",
-  " 528:	2324      	movs	r3, #36	; 0x24\n",
-  " 52a:	2326      	movs	r3, #38	; 0x26\n",
-  " 52c:	2328      	movs	r3, #40	; 0x28\n",
-  " 52e:	232a      	movs	r3, #42	; 0x2a\n",
-  " 530:	232c      	movs	r3, #44	; 0x2c\n",
-  " 532:	232e      	movs	r3, #46	; 0x2e\n",
-  " 534:	2330      	movs	r3, #48	; 0x30\n",
-  " 536:	2332      	movs	r3, #50	; 0x32\n",
-  " 538:	2334      	movs	r3, #52	; 0x34\n",
-  " 53a:	2336      	movs	r3, #54	; 0x36\n",
-  " 53c:	2338      	movs	r3, #56	; 0x38\n",
-  " 53e:	233a      	movs	r3, #58	; 0x3a\n",
-  " 540:	233c      	movs	r3, #60	; 0x3c\n",
-  " 542:	233e      	movs	r3, #62	; 0x3e\n",
-  " 544:	2340      	movs	r3, #64	; 0x40\n",
-  " 546:	2342      	movs	r3, #66	; 0x42\n",
-  " 548:	2344      	movs	r3, #68	; 0x44\n",
-  " 54a:	2346      	movs	r3, #70	; 0x46\n",
-  " 54c:	2348      	movs	r3, #72	; 0x48\n",
-  " 54e:	234a      	movs	r3, #74	; 0x4a\n",
-  " 550:	234c      	movs	r3, #76	; 0x4c\n",
-  " 552:	234e      	movs	r3, #78	; 0x4e\n",
-  " 554:	2350      	movs	r3, #80	; 0x50\n",
-  " 556:	2352      	movs	r3, #82	; 0x52\n",
-  " 558:	2354      	movs	r3, #84	; 0x54\n",
-  " 55a:	2356      	movs	r3, #86	; 0x56\n",
-  " 55c:	2358      	movs	r3, #88	; 0x58\n",
-  " 55e:	235a      	movs	r3, #90	; 0x5a\n",
-  " 560:	235c      	movs	r3, #92	; 0x5c\n",
-  " 562:	235e      	movs	r3, #94	; 0x5e\n",
-  " 564:	2360      	movs	r3, #96	; 0x60\n",
-  " 566:	2362      	movs	r3, #98	; 0x62\n",
-  " 568:	2364      	movs	r3, #100	; 0x64\n",
-  " 56a:	2366      	movs	r3, #102	; 0x66\n",
-  " 56c:	2368      	movs	r3, #104	; 0x68\n",
-  " 56e:	236a      	movs	r3, #106	; 0x6a\n",
-  " 570:	236c      	movs	r3, #108	; 0x6c\n",
-  " 572:	236e      	movs	r3, #110	; 0x6e\n",
-  " 574:	2370      	movs	r3, #112	; 0x70\n",
-  " 576:	2372      	movs	r3, #114	; 0x72\n",
-  " 578:	2374      	movs	r3, #116	; 0x74\n",
-  " 57a:	2376      	movs	r3, #118	; 0x76\n",
-  " 57c:	2378      	movs	r3, #120	; 0x78\n",
-  " 57e:	237a      	movs	r3, #122	; 0x7a\n",
-  " 580:	237c      	movs	r3, #124	; 0x7c\n",
-  " 582:	237e      	movs	r3, #126	; 0x7e\n",
-  " 584:	2380      	movs	r3, #128	; 0x80\n",
-  " 586:	2382      	movs	r3, #130	; 0x82\n",
-  " 588:	2384      	movs	r3, #132	; 0x84\n",
-  " 58a:	2386      	movs	r3, #134	; 0x86\n",
-  " 58c:	2388      	movs	r3, #136	; 0x88\n",
-  " 58e:	238a      	movs	r3, #138	; 0x8a\n",
-  " 590:	238c      	movs	r3, #140	; 0x8c\n",
-  " 592:	238e      	movs	r3, #142	; 0x8e\n",
-  " 594:	2390      	movs	r3, #144	; 0x90\n",
-  " 596:	2392      	movs	r3, #146	; 0x92\n",
-  " 598:	2394      	movs	r3, #148	; 0x94\n",
-  " 59a:	2396      	movs	r3, #150	; 0x96\n",
-  " 59c:	2398      	movs	r3, #152	; 0x98\n",
-  " 59e:	239a      	movs	r3, #154	; 0x9a\n",
-  " 5a0:	239c      	movs	r3, #156	; 0x9c\n",
-  " 5a2:	239e      	movs	r3, #158	; 0x9e\n",
-  " 5a4:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 5a6:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 5a8:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 5aa:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 5ac:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 5ae:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 5b0:	23ac      	movs	r3, #172	; 0xac\n",
-  " 5b2:	23ae      	movs	r3, #174	; 0xae\n",
-  " 5b4:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 5b6:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 5b8:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 5ba:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 5bc:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 5be:	23ba      	movs	r3, #186	; 0xba\n",
-  " 5c0:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 5c2:	23be      	movs	r3, #190	; 0xbe\n",
-  " 5c4:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 5c6:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 5c8:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 5ca:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 5cc:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 5ce:	23ca      	movs	r3, #202	; 0xca\n",
-  " 5d0:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 5d2:	23ce      	movs	r3, #206	; 0xce\n",
-  " 5d4:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 5d6:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 5d8:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 5da:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 5dc:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 5de:	23da      	movs	r3, #218	; 0xda\n",
-  " 5e0:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 5e2:	23de      	movs	r3, #222	; 0xde\n",
-  " 5e4:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 5e6:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 5e8:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 5ea:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 5ec:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 5ee:	23ea      	movs	r3, #234	; 0xea\n",
-  " 5f0:	23ec      	movs	r3, #236	; 0xec\n",
-  " 5f2:	23ee      	movs	r3, #238	; 0xee\n",
-  " 5f4:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 5f6:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 5f8:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 5fa:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 5fc:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 5fe:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 600:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 602:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 604:	2300      	movs	r3, #0\n",
-  " 606:	2302      	movs	r3, #2\n",
-  " 608:	2304      	movs	r3, #4\n",
-  " 60a:	2306      	movs	r3, #6\n",
-  " 60c:	2308      	movs	r3, #8\n",
-  " 60e:	230a      	movs	r3, #10\n",
-  " 610:	230c      	movs	r3, #12\n",
-  " 612:	230e      	movs	r3, #14\n",
-  " 614:	2310      	movs	r3, #16\n",
-  " 616:	2312      	movs	r3, #18\n",
-  " 618:	2314      	movs	r3, #20\n",
-  " 61a:	2316      	movs	r3, #22\n",
-  " 61c:	2318      	movs	r3, #24\n",
-  " 61e:	231a      	movs	r3, #26\n",
-  " 620:	231c      	movs	r3, #28\n",
-  " 622:	231e      	movs	r3, #30\n",
-  " 624:	2320      	movs	r3, #32\n",
-  " 626:	2322      	movs	r3, #34	; 0x22\n",
-  " 628:	2324      	movs	r3, #36	; 0x24\n",
-  " 62a:	2326      	movs	r3, #38	; 0x26\n",
-  " 62c:	2328      	movs	r3, #40	; 0x28\n",
-  " 62e:	232a      	movs	r3, #42	; 0x2a\n",
-  " 630:	232c      	movs	r3, #44	; 0x2c\n",
-  " 632:	232e      	movs	r3, #46	; 0x2e\n",
-  " 634:	2330      	movs	r3, #48	; 0x30\n",
-  " 636:	2332      	movs	r3, #50	; 0x32\n",
-  " 638:	2334      	movs	r3, #52	; 0x34\n",
-  " 63a:	2336      	movs	r3, #54	; 0x36\n",
-  " 63c:	2338      	movs	r3, #56	; 0x38\n",
-  " 63e:	233a      	movs	r3, #58	; 0x3a\n",
-  " 640:	233c      	movs	r3, #60	; 0x3c\n",
-  " 642:	233e      	movs	r3, #62	; 0x3e\n",
-  " 644:	2340      	movs	r3, #64	; 0x40\n",
-  " 646:	2342      	movs	r3, #66	; 0x42\n",
-  " 648:	2344      	movs	r3, #68	; 0x44\n",
-  " 64a:	2346      	movs	r3, #70	; 0x46\n",
-  " 64c:	2348      	movs	r3, #72	; 0x48\n",
-  " 64e:	234a      	movs	r3, #74	; 0x4a\n",
-  " 650:	234c      	movs	r3, #76	; 0x4c\n",
-  " 652:	234e      	movs	r3, #78	; 0x4e\n",
-  " 654:	2350      	movs	r3, #80	; 0x50\n",
-  " 656:	2352      	movs	r3, #82	; 0x52\n",
-  " 658:	2354      	movs	r3, #84	; 0x54\n",
-  " 65a:	2356      	movs	r3, #86	; 0x56\n",
-  " 65c:	2358      	movs	r3, #88	; 0x58\n",
-  " 65e:	235a      	movs	r3, #90	; 0x5a\n",
-  " 660:	235c      	movs	r3, #92	; 0x5c\n",
-  " 662:	235e      	movs	r3, #94	; 0x5e\n",
-  " 664:	2360      	movs	r3, #96	; 0x60\n",
-  " 666:	2362      	movs	r3, #98	; 0x62\n",
-  " 668:	2364      	movs	r3, #100	; 0x64\n",
-  " 66a:	2366      	movs	r3, #102	; 0x66\n",
-  " 66c:	2368      	movs	r3, #104	; 0x68\n",
-  " 66e:	236a      	movs	r3, #106	; 0x6a\n",
-  " 670:	236c      	movs	r3, #108	; 0x6c\n",
-  " 672:	236e      	movs	r3, #110	; 0x6e\n",
-  " 674:	2370      	movs	r3, #112	; 0x70\n",
-  " 676:	2372      	movs	r3, #114	; 0x72\n",
-  " 678:	2374      	movs	r3, #116	; 0x74\n",
-  " 67a:	2376      	movs	r3, #118	; 0x76\n",
-  " 67c:	2378      	movs	r3, #120	; 0x78\n",
-  " 67e:	237a      	movs	r3, #122	; 0x7a\n",
-  " 680:	237c      	movs	r3, #124	; 0x7c\n",
-  " 682:	237e      	movs	r3, #126	; 0x7e\n",
-  " 684:	2380      	movs	r3, #128	; 0x80\n",
-  " 686:	2382      	movs	r3, #130	; 0x82\n",
-  " 688:	2384      	movs	r3, #132	; 0x84\n",
-  " 68a:	2386      	movs	r3, #134	; 0x86\n",
-  " 68c:	2388      	movs	r3, #136	; 0x88\n",
-  " 68e:	238a      	movs	r3, #138	; 0x8a\n",
-  " 690:	238c      	movs	r3, #140	; 0x8c\n",
-  " 692:	238e      	movs	r3, #142	; 0x8e\n",
-  " 694:	2390      	movs	r3, #144	; 0x90\n",
-  " 696:	2392      	movs	r3, #146	; 0x92\n",
-  " 698:	2394      	movs	r3, #148	; 0x94\n",
-  " 69a:	2396      	movs	r3, #150	; 0x96\n",
-  " 69c:	2398      	movs	r3, #152	; 0x98\n",
-  " 69e:	239a      	movs	r3, #154	; 0x9a\n",
-  " 6a0:	239c      	movs	r3, #156	; 0x9c\n",
-  " 6a2:	239e      	movs	r3, #158	; 0x9e\n",
-  " 6a4:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 6a6:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 6a8:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 6aa:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 6ac:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 6ae:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 6b0:	23ac      	movs	r3, #172	; 0xac\n",
-  " 6b2:	23ae      	movs	r3, #174	; 0xae\n",
-  " 6b4:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 6b6:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 6b8:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 6ba:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 6bc:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 6be:	23ba      	movs	r3, #186	; 0xba\n",
-  " 6c0:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 6c2:	23be      	movs	r3, #190	; 0xbe\n",
-  " 6c4:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 6c6:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 6c8:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 6ca:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 6cc:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 6ce:	23ca      	movs	r3, #202	; 0xca\n",
-  " 6d0:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 6d2:	23ce      	movs	r3, #206	; 0xce\n",
-  " 6d4:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 6d6:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 6d8:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 6da:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 6dc:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 6de:	23da      	movs	r3, #218	; 0xda\n",
-  " 6e0:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 6e2:	23de      	movs	r3, #222	; 0xde\n",
-  " 6e4:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 6e6:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 6e8:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 6ea:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 6ec:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 6ee:	23ea      	movs	r3, #234	; 0xea\n",
-  " 6f0:	23ec      	movs	r3, #236	; 0xec\n",
-  " 6f2:	23ee      	movs	r3, #238	; 0xee\n",
-  " 6f4:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 6f6:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 6f8:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 6fa:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 6fc:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 6fe:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 700:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 702:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 704:	2300      	movs	r3, #0\n",
-  " 706:	2302      	movs	r3, #2\n",
-  " 708:	2304      	movs	r3, #4\n",
-  " 70a:	2306      	movs	r3, #6\n",
-  " 70c:	2308      	movs	r3, #8\n",
-  " 70e:	230a      	movs	r3, #10\n",
-  " 710:	230c      	movs	r3, #12\n",
-  " 712:	230e      	movs	r3, #14\n",
-  " 714:	2310      	movs	r3, #16\n",
-  " 716:	2312      	movs	r3, #18\n",
-  " 718:	2314      	movs	r3, #20\n",
-  " 71a:	2316      	movs	r3, #22\n",
-  " 71c:	2318      	movs	r3, #24\n",
-  " 71e:	231a      	movs	r3, #26\n",
-  " 720:	231c      	movs	r3, #28\n",
-  " 722:	231e      	movs	r3, #30\n",
-  " 724:	2320      	movs	r3, #32\n",
-  " 726:	2322      	movs	r3, #34	; 0x22\n",
-  " 728:	2324      	movs	r3, #36	; 0x24\n",
-  " 72a:	2326      	movs	r3, #38	; 0x26\n",
-  " 72c:	2328      	movs	r3, #40	; 0x28\n",
-  " 72e:	232a      	movs	r3, #42	; 0x2a\n",
-  " 730:	232c      	movs	r3, #44	; 0x2c\n",
-  " 732:	232e      	movs	r3, #46	; 0x2e\n",
-  " 734:	2330      	movs	r3, #48	; 0x30\n",
-  " 736:	2332      	movs	r3, #50	; 0x32\n",
-  " 738:	2334      	movs	r3, #52	; 0x34\n",
-  " 73a:	2336      	movs	r3, #54	; 0x36\n",
-  " 73c:	2338      	movs	r3, #56	; 0x38\n",
-  " 73e:	233a      	movs	r3, #58	; 0x3a\n",
-  " 740:	233c      	movs	r3, #60	; 0x3c\n",
-  " 742:	233e      	movs	r3, #62	; 0x3e\n",
-  " 744:	2340      	movs	r3, #64	; 0x40\n",
-  " 746:	2342      	movs	r3, #66	; 0x42\n",
-  " 748:	2344      	movs	r3, #68	; 0x44\n",
-  " 74a:	2346      	movs	r3, #70	; 0x46\n",
-  " 74c:	2348      	movs	r3, #72	; 0x48\n",
-  " 74e:	234a      	movs	r3, #74	; 0x4a\n",
-  " 750:	234c      	movs	r3, #76	; 0x4c\n",
-  " 752:	234e      	movs	r3, #78	; 0x4e\n",
-  " 754:	2350      	movs	r3, #80	; 0x50\n",
-  " 756:	2352      	movs	r3, #82	; 0x52\n",
-  " 758:	2354      	movs	r3, #84	; 0x54\n",
-  " 75a:	2356      	movs	r3, #86	; 0x56\n",
-  " 75c:	2358      	movs	r3, #88	; 0x58\n",
-  " 75e:	235a      	movs	r3, #90	; 0x5a\n",
-  " 760:	235c      	movs	r3, #92	; 0x5c\n",
-  " 762:	235e      	movs	r3, #94	; 0x5e\n",
-  " 764:	2360      	movs	r3, #96	; 0x60\n",
-  " 766:	2362      	movs	r3, #98	; 0x62\n",
-  " 768:	2364      	movs	r3, #100	; 0x64\n",
-  " 76a:	2366      	movs	r3, #102	; 0x66\n",
-  " 76c:	2368      	movs	r3, #104	; 0x68\n",
-  " 76e:	236a      	movs	r3, #106	; 0x6a\n",
-  " 770:	236c      	movs	r3, #108	; 0x6c\n",
-  " 772:	236e      	movs	r3, #110	; 0x6e\n",
-  " 774:	2370      	movs	r3, #112	; 0x70\n",
-  " 776:	2372      	movs	r3, #114	; 0x72\n",
-  " 778:	2374      	movs	r3, #116	; 0x74\n",
-  " 77a:	2376      	movs	r3, #118	; 0x76\n",
-  " 77c:	2378      	movs	r3, #120	; 0x78\n",
-  " 77e:	237a      	movs	r3, #122	; 0x7a\n",
-  " 780:	237c      	movs	r3, #124	; 0x7c\n",
-  " 782:	237e      	movs	r3, #126	; 0x7e\n",
-  " 784:	2380      	movs	r3, #128	; 0x80\n",
-  " 786:	2382      	movs	r3, #130	; 0x82\n",
-  " 788:	2384      	movs	r3, #132	; 0x84\n",
-  " 78a:	2386      	movs	r3, #134	; 0x86\n",
-  " 78c:	2388      	movs	r3, #136	; 0x88\n",
-  " 78e:	238a      	movs	r3, #138	; 0x8a\n",
-  " 790:	238c      	movs	r3, #140	; 0x8c\n",
-  " 792:	238e      	movs	r3, #142	; 0x8e\n",
-  " 794:	2390      	movs	r3, #144	; 0x90\n",
-  " 796:	2392      	movs	r3, #146	; 0x92\n",
-  " 798:	2394      	movs	r3, #148	; 0x94\n",
-  " 79a:	2396      	movs	r3, #150	; 0x96\n",
-  " 79c:	2398      	movs	r3, #152	; 0x98\n",
-  " 79e:	239a      	movs	r3, #154	; 0x9a\n",
-  " 7a0:	239c      	movs	r3, #156	; 0x9c\n",
-  " 7a2:	239e      	movs	r3, #158	; 0x9e\n",
-  " 7a4:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 7a6:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 7a8:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 7aa:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 7ac:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 7ae:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 7b0:	23ac      	movs	r3, #172	; 0xac\n",
-  " 7b2:	23ae      	movs	r3, #174	; 0xae\n",
-  " 7b4:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 7b6:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 7b8:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 7ba:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 7bc:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 7be:	23ba      	movs	r3, #186	; 0xba\n",
-  " 7c0:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 7c2:	23be      	movs	r3, #190	; 0xbe\n",
-  " 7c4:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 7c6:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 7c8:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 7ca:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 7cc:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 7ce:	23ca      	movs	r3, #202	; 0xca\n",
-  " 7d0:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 7d2:	23ce      	movs	r3, #206	; 0xce\n",
-  " 7d4:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 7d6:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 7d8:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 7da:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 7dc:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 7de:	23da      	movs	r3, #218	; 0xda\n",
-  " 7e0:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 7e2:	23de      	movs	r3, #222	; 0xde\n",
-  " 7e4:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 7e6:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 7e8:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 7ea:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 7ec:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 7ee:	23ea      	movs	r3, #234	; 0xea\n",
-  " 7f0:	23ec      	movs	r3, #236	; 0xec\n",
-  " 7f2:	23ee      	movs	r3, #238	; 0xee\n",
-  " 7f4:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 7f6:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 7f8:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 7fa:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 7fc:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 7fe:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 800:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 802:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 804:	2300      	movs	r3, #0\n",
-  " 806:	4611      	mov	r1, r2\n",
-  nullptr
-};
-const char* const CompareAndBranchMaxResults[] = {
-  "   0:	b3fc      	cbz	r4, 82 <CompareAndBranchMax+0x82>\n",
-  "   2:	2300      	movs	r3, #0\n",
-  "   4:	2302      	movs	r3, #2\n",
-  "   6:	2304      	movs	r3, #4\n",
-  "   8:	2306      	movs	r3, #6\n",
-  "   a:	2308      	movs	r3, #8\n",
-  "   c:	230a      	movs	r3, #10\n",
-  "   e:	230c      	movs	r3, #12\n",
-  "  10:	230e      	movs	r3, #14\n",
-  "  12:	2310      	movs	r3, #16\n",
-  "  14:	2312      	movs	r3, #18\n",
-  "  16:	2314      	movs	r3, #20\n",
-  "  18:	2316      	movs	r3, #22\n",
-  "  1a:	2318      	movs	r3, #24\n",
-  "  1c:	231a      	movs	r3, #26\n",
-  "  1e:	231c      	movs	r3, #28\n",
-  "  20:	231e      	movs	r3, #30\n",
-  "  22:	2320      	movs	r3, #32\n",
-  "  24:	2322      	movs	r3, #34	; 0x22\n",
-  "  26:	2324      	movs	r3, #36	; 0x24\n",
-  "  28:	2326      	movs	r3, #38	; 0x26\n",
-  "  2a:	2328      	movs	r3, #40	; 0x28\n",
-  "  2c:	232a      	movs	r3, #42	; 0x2a\n",
-  "  2e:	232c      	movs	r3, #44	; 0x2c\n",
-  "  30:	232e      	movs	r3, #46	; 0x2e\n",
-  "  32:	2330      	movs	r3, #48	; 0x30\n",
-  "  34:	2332      	movs	r3, #50	; 0x32\n",
-  "  36:	2334      	movs	r3, #52	; 0x34\n",
-  "  38:	2336      	movs	r3, #54	; 0x36\n",
-  "  3a:	2338      	movs	r3, #56	; 0x38\n",
-  "  3c:	233a      	movs	r3, #58	; 0x3a\n",
-  "  3e:	233c      	movs	r3, #60	; 0x3c\n",
-  "  40:	233e      	movs	r3, #62	; 0x3e\n",
-  "  42:	2340      	movs	r3, #64	; 0x40\n",
-  "  44:	2342      	movs	r3, #66	; 0x42\n",
-  "  46:	2344      	movs	r3, #68	; 0x44\n",
-  "  48:	2346      	movs	r3, #70	; 0x46\n",
-  "  4a:	2348      	movs	r3, #72	; 0x48\n",
-  "  4c:	234a      	movs	r3, #74	; 0x4a\n",
-  "  4e:	234c      	movs	r3, #76	; 0x4c\n",
-  "  50:	234e      	movs	r3, #78	; 0x4e\n",
-  "  52:	2350      	movs	r3, #80	; 0x50\n",
-  "  54:	2352      	movs	r3, #82	; 0x52\n",
-  "  56:	2354      	movs	r3, #84	; 0x54\n",
-  "  58:	2356      	movs	r3, #86	; 0x56\n",
-  "  5a:	2358      	movs	r3, #88	; 0x58\n",
-  "  5c:	235a      	movs	r3, #90	; 0x5a\n",
-  "  5e:	235c      	movs	r3, #92	; 0x5c\n",
-  "  60:	235e      	movs	r3, #94	; 0x5e\n",
-  "  62:	2360      	movs	r3, #96	; 0x60\n",
-  "  64:	2362      	movs	r3, #98	; 0x62\n",
-  "  66:	2364      	movs	r3, #100	; 0x64\n",
-  "  68:	2366      	movs	r3, #102	; 0x66\n",
-  "  6a:	2368      	movs	r3, #104	; 0x68\n",
-  "  6c:	236a      	movs	r3, #106	; 0x6a\n",
-  "  6e:	236c      	movs	r3, #108	; 0x6c\n",
-  "  70:	236e      	movs	r3, #110	; 0x6e\n",
-  "  72:	2370      	movs	r3, #112	; 0x70\n",
-  "  74:	2372      	movs	r3, #114	; 0x72\n",
-  "  76:	2374      	movs	r3, #116	; 0x74\n",
-  "  78:	2376      	movs	r3, #118	; 0x76\n",
-  "  7a:	2378      	movs	r3, #120	; 0x78\n",
-  "  7c:	237a      	movs	r3, #122	; 0x7a\n",
-  "  7e:	237c      	movs	r3, #124	; 0x7c\n",
-  "  80:	237e      	movs	r3, #126	; 0x7e\n",
-  "  82:	4611      	mov	r1, r2\n",
-  nullptr
-};
-const char* const CompareAndBranchRelocation16Results[] = {
-  "   0:	2c00      	cmp	r4, #0\n",
-  "   2:	d040      	beq.n	86 <CompareAndBranchRelocation16+0x86>\n",
-  "   4:	2300      	movs	r3, #0\n",
-  "   6:	2302      	movs	r3, #2\n",
-  "   8:	2304      	movs	r3, #4\n",
-  "   a:	2306      	movs	r3, #6\n",
-  "   c:	2308      	movs	r3, #8\n",
-  "   e:	230a      	movs	r3, #10\n",
-  "  10:	230c      	movs	r3, #12\n",
-  "  12:	230e      	movs	r3, #14\n",
-  "  14:	2310      	movs	r3, #16\n",
-  "  16:	2312      	movs	r3, #18\n",
-  "  18:	2314      	movs	r3, #20\n",
-  "  1a:	2316      	movs	r3, #22\n",
-  "  1c:	2318      	movs	r3, #24\n",
-  "  1e:	231a      	movs	r3, #26\n",
-  "  20:	231c      	movs	r3, #28\n",
-  "  22:	231e      	movs	r3, #30\n",
-  "  24:	2320      	movs	r3, #32\n",
-  "  26:	2322      	movs	r3, #34	; 0x22\n",
-  "  28:	2324      	movs	r3, #36	; 0x24\n",
-  "  2a:	2326      	movs	r3, #38	; 0x26\n",
-  "  2c:	2328      	movs	r3, #40	; 0x28\n",
-  "  2e:	232a      	movs	r3, #42	; 0x2a\n",
-  "  30:	232c      	movs	r3, #44	; 0x2c\n",
-  "  32:	232e      	movs	r3, #46	; 0x2e\n",
-  "  34:	2330      	movs	r3, #48	; 0x30\n",
-  "  36:	2332      	movs	r3, #50	; 0x32\n",
-  "  38:	2334      	movs	r3, #52	; 0x34\n",
-  "  3a:	2336      	movs	r3, #54	; 0x36\n",
-  "  3c:	2338      	movs	r3, #56	; 0x38\n",
-  "  3e:	233a      	movs	r3, #58	; 0x3a\n",
-  "  40:	233c      	movs	r3, #60	; 0x3c\n",
-  "  42:	233e      	movs	r3, #62	; 0x3e\n",
-  "  44:	2340      	movs	r3, #64	; 0x40\n",
-  "  46:	2342      	movs	r3, #66	; 0x42\n",
-  "  48:	2344      	movs	r3, #68	; 0x44\n",
-  "  4a:	2346      	movs	r3, #70	; 0x46\n",
-  "  4c:	2348      	movs	r3, #72	; 0x48\n",
-  "  4e:	234a      	movs	r3, #74	; 0x4a\n",
-  "  50:	234c      	movs	r3, #76	; 0x4c\n",
-  "  52:	234e      	movs	r3, #78	; 0x4e\n",
-  "  54:	2350      	movs	r3, #80	; 0x50\n",
-  "  56:	2352      	movs	r3, #82	; 0x52\n",
-  "  58:	2354      	movs	r3, #84	; 0x54\n",
-  "  5a:	2356      	movs	r3, #86	; 0x56\n",
-  "  5c:	2358      	movs	r3, #88	; 0x58\n",
-  "  5e:	235a      	movs	r3, #90	; 0x5a\n",
-  "  60:	235c      	movs	r3, #92	; 0x5c\n",
-  "  62:	235e      	movs	r3, #94	; 0x5e\n",
-  "  64:	2360      	movs	r3, #96	; 0x60\n",
-  "  66:	2362      	movs	r3, #98	; 0x62\n",
-  "  68:	2364      	movs	r3, #100	; 0x64\n",
-  "  6a:	2366      	movs	r3, #102	; 0x66\n",
-  "  6c:	2368      	movs	r3, #104	; 0x68\n",
-  "  6e:	236a      	movs	r3, #106	; 0x6a\n",
-  "  70:	236c      	movs	r3, #108	; 0x6c\n",
-  "  72:	236e      	movs	r3, #110	; 0x6e\n",
-  "  74:	2370      	movs	r3, #112	; 0x70\n",
-  "  76:	2372      	movs	r3, #114	; 0x72\n",
-  "  78:	2374      	movs	r3, #116	; 0x74\n",
-  "  7a:	2376      	movs	r3, #118	; 0x76\n",
-  "  7c:	2378      	movs	r3, #120	; 0x78\n",
-  "  7e:	237a      	movs	r3, #122	; 0x7a\n",
-  "  80:	237c      	movs	r3, #124	; 0x7c\n",
-  "  82:	237e      	movs	r3, #126	; 0x7e\n",
-  "  84:	2380      	movs	r3, #128	; 0x80\n",
-  "  86:	4611      	mov	r1, r2\n",
-  nullptr
-};
-const char* const CompareAndBranchRelocation32Results[] = {
-  "   0:	2c00      	cmp	r4, #0\n",
-  "   2:	f000 8401 	beq.w	808 <CompareAndBranchRelocation32+0x808>\n",
-  "   6:	2300      	movs	r3, #0\n",
-  "   8:	2302      	movs	r3, #2\n",
-  "   a:	2304      	movs	r3, #4\n",
-  "   c:	2306      	movs	r3, #6\n",
-  "   e:	2308      	movs	r3, #8\n",
-  "  10:	230a      	movs	r3, #10\n",
-  "  12:	230c      	movs	r3, #12\n",
-  "  14:	230e      	movs	r3, #14\n",
-  "  16:	2310      	movs	r3, #16\n",
-  "  18:	2312      	movs	r3, #18\n",
-  "  1a:	2314      	movs	r3, #20\n",
-  "  1c:	2316      	movs	r3, #22\n",
-  "  1e:	2318      	movs	r3, #24\n",
-  "  20:	231a      	movs	r3, #26\n",
-  "  22:	231c      	movs	r3, #28\n",
-  "  24:	231e      	movs	r3, #30\n",
-  "  26:	2320      	movs	r3, #32\n",
-  "  28:	2322      	movs	r3, #34	; 0x22\n",
-  "  2a:	2324      	movs	r3, #36	; 0x24\n",
-  "  2c:	2326      	movs	r3, #38	; 0x26\n",
-  "  2e:	2328      	movs	r3, #40	; 0x28\n",
-  "  30:	232a      	movs	r3, #42	; 0x2a\n",
-  "  32:	232c      	movs	r3, #44	; 0x2c\n",
-  "  34:	232e      	movs	r3, #46	; 0x2e\n",
-  "  36:	2330      	movs	r3, #48	; 0x30\n",
-  "  38:	2332      	movs	r3, #50	; 0x32\n",
-  "  3a:	2334      	movs	r3, #52	; 0x34\n",
-  "  3c:	2336      	movs	r3, #54	; 0x36\n",
-  "  3e:	2338      	movs	r3, #56	; 0x38\n",
-  "  40:	233a      	movs	r3, #58	; 0x3a\n",
-  "  42:	233c      	movs	r3, #60	; 0x3c\n",
-  "  44:	233e      	movs	r3, #62	; 0x3e\n",
-  "  46:	2340      	movs	r3, #64	; 0x40\n",
-  "  48:	2342      	movs	r3, #66	; 0x42\n",
-  "  4a:	2344      	movs	r3, #68	; 0x44\n",
-  "  4c:	2346      	movs	r3, #70	; 0x46\n",
-  "  4e:	2348      	movs	r3, #72	; 0x48\n",
-  "  50:	234a      	movs	r3, #74	; 0x4a\n",
-  "  52:	234c      	movs	r3, #76	; 0x4c\n",
-  "  54:	234e      	movs	r3, #78	; 0x4e\n",
-  "  56:	2350      	movs	r3, #80	; 0x50\n",
-  "  58:	2352      	movs	r3, #82	; 0x52\n",
-  "  5a:	2354      	movs	r3, #84	; 0x54\n",
-  "  5c:	2356      	movs	r3, #86	; 0x56\n",
-  "  5e:	2358      	movs	r3, #88	; 0x58\n",
-  "  60:	235a      	movs	r3, #90	; 0x5a\n",
-  "  62:	235c      	movs	r3, #92	; 0x5c\n",
-  "  64:	235e      	movs	r3, #94	; 0x5e\n",
-  "  66:	2360      	movs	r3, #96	; 0x60\n",
-  "  68:	2362      	movs	r3, #98	; 0x62\n",
-  "  6a:	2364      	movs	r3, #100	; 0x64\n",
-  "  6c:	2366      	movs	r3, #102	; 0x66\n",
-  "  6e:	2368      	movs	r3, #104	; 0x68\n",
-  "  70:	236a      	movs	r3, #106	; 0x6a\n",
-  "  72:	236c      	movs	r3, #108	; 0x6c\n",
-  "  74:	236e      	movs	r3, #110	; 0x6e\n",
-  "  76:	2370      	movs	r3, #112	; 0x70\n",
-  "  78:	2372      	movs	r3, #114	; 0x72\n",
-  "  7a:	2374      	movs	r3, #116	; 0x74\n",
-  "  7c:	2376      	movs	r3, #118	; 0x76\n",
-  "  7e:	2378      	movs	r3, #120	; 0x78\n",
-  "  80:	237a      	movs	r3, #122	; 0x7a\n",
-  "  82:	237c      	movs	r3, #124	; 0x7c\n",
-  "  84:	237e      	movs	r3, #126	; 0x7e\n",
-  "  86:	2380      	movs	r3, #128	; 0x80\n",
-  "  88:	2382      	movs	r3, #130	; 0x82\n",
-  "  8a:	2384      	movs	r3, #132	; 0x84\n",
-  "  8c:	2386      	movs	r3, #134	; 0x86\n",
-  "  8e:	2388      	movs	r3, #136	; 0x88\n",
-  "  90:	238a      	movs	r3, #138	; 0x8a\n",
-  "  92:	238c      	movs	r3, #140	; 0x8c\n",
-  "  94:	238e      	movs	r3, #142	; 0x8e\n",
-  "  96:	2390      	movs	r3, #144	; 0x90\n",
-  "  98:	2392      	movs	r3, #146	; 0x92\n",
-  "  9a:	2394      	movs	r3, #148	; 0x94\n",
-  "  9c:	2396      	movs	r3, #150	; 0x96\n",
-  "  9e:	2398      	movs	r3, #152	; 0x98\n",
-  "  a0:	239a      	movs	r3, #154	; 0x9a\n",
-  "  a2:	239c      	movs	r3, #156	; 0x9c\n",
-  "  a4:	239e      	movs	r3, #158	; 0x9e\n",
-  "  a6:	23a0      	movs	r3, #160	; 0xa0\n",
-  "  a8:	23a2      	movs	r3, #162	; 0xa2\n",
-  "  aa:	23a4      	movs	r3, #164	; 0xa4\n",
-  "  ac:	23a6      	movs	r3, #166	; 0xa6\n",
-  "  ae:	23a8      	movs	r3, #168	; 0xa8\n",
-  "  b0:	23aa      	movs	r3, #170	; 0xaa\n",
-  "  b2:	23ac      	movs	r3, #172	; 0xac\n",
-  "  b4:	23ae      	movs	r3, #174	; 0xae\n",
-  "  b6:	23b0      	movs	r3, #176	; 0xb0\n",
-  "  b8:	23b2      	movs	r3, #178	; 0xb2\n",
-  "  ba:	23b4      	movs	r3, #180	; 0xb4\n",
-  "  bc:	23b6      	movs	r3, #182	; 0xb6\n",
-  "  be:	23b8      	movs	r3, #184	; 0xb8\n",
-  "  c0:	23ba      	movs	r3, #186	; 0xba\n",
-  "  c2:	23bc      	movs	r3, #188	; 0xbc\n",
-  "  c4:	23be      	movs	r3, #190	; 0xbe\n",
-  "  c6:	23c0      	movs	r3, #192	; 0xc0\n",
-  "  c8:	23c2      	movs	r3, #194	; 0xc2\n",
-  "  ca:	23c4      	movs	r3, #196	; 0xc4\n",
-  "  cc:	23c6      	movs	r3, #198	; 0xc6\n",
-  "  ce:	23c8      	movs	r3, #200	; 0xc8\n",
-  "  d0:	23ca      	movs	r3, #202	; 0xca\n",
-  "  d2:	23cc      	movs	r3, #204	; 0xcc\n",
-  "  d4:	23ce      	movs	r3, #206	; 0xce\n",
-  "  d6:	23d0      	movs	r3, #208	; 0xd0\n",
-  "  d8:	23d2      	movs	r3, #210	; 0xd2\n",
-  "  da:	23d4      	movs	r3, #212	; 0xd4\n",
-  "  dc:	23d6      	movs	r3, #214	; 0xd6\n",
-  "  de:	23d8      	movs	r3, #216	; 0xd8\n",
-  "  e0:	23da      	movs	r3, #218	; 0xda\n",
-  "  e2:	23dc      	movs	r3, #220	; 0xdc\n",
-  "  e4:	23de      	movs	r3, #222	; 0xde\n",
-  "  e6:	23e0      	movs	r3, #224	; 0xe0\n",
-  "  e8:	23e2      	movs	r3, #226	; 0xe2\n",
-  "  ea:	23e4      	movs	r3, #228	; 0xe4\n",
-  "  ec:	23e6      	movs	r3, #230	; 0xe6\n",
-  "  ee:	23e8      	movs	r3, #232	; 0xe8\n",
-  "  f0:	23ea      	movs	r3, #234	; 0xea\n",
-  "  f2:	23ec      	movs	r3, #236	; 0xec\n",
-  "  f4:	23ee      	movs	r3, #238	; 0xee\n",
-  "  f6:	23f0      	movs	r3, #240	; 0xf0\n",
-  "  f8:	23f2      	movs	r3, #242	; 0xf2\n",
-  "  fa:	23f4      	movs	r3, #244	; 0xf4\n",
-  "  fc:	23f6      	movs	r3, #246	; 0xf6\n",
-  "  fe:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 100:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 102:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 104:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 106:	2300      	movs	r3, #0\n",
-  " 108:	2302      	movs	r3, #2\n",
-  " 10a:	2304      	movs	r3, #4\n",
-  " 10c:	2306      	movs	r3, #6\n",
-  " 10e:	2308      	movs	r3, #8\n",
-  " 110:	230a      	movs	r3, #10\n",
-  " 112:	230c      	movs	r3, #12\n",
-  " 114:	230e      	movs	r3, #14\n",
-  " 116:	2310      	movs	r3, #16\n",
-  " 118:	2312      	movs	r3, #18\n",
-  " 11a:	2314      	movs	r3, #20\n",
-  " 11c:	2316      	movs	r3, #22\n",
-  " 11e:	2318      	movs	r3, #24\n",
-  " 120:	231a      	movs	r3, #26\n",
-  " 122:	231c      	movs	r3, #28\n",
-  " 124:	231e      	movs	r3, #30\n",
-  " 126:	2320      	movs	r3, #32\n",
-  " 128:	2322      	movs	r3, #34	; 0x22\n",
-  " 12a:	2324      	movs	r3, #36	; 0x24\n",
-  " 12c:	2326      	movs	r3, #38	; 0x26\n",
-  " 12e:	2328      	movs	r3, #40	; 0x28\n",
-  " 130:	232a      	movs	r3, #42	; 0x2a\n",
-  " 132:	232c      	movs	r3, #44	; 0x2c\n",
-  " 134:	232e      	movs	r3, #46	; 0x2e\n",
-  " 136:	2330      	movs	r3, #48	; 0x30\n",
-  " 138:	2332      	movs	r3, #50	; 0x32\n",
-  " 13a:	2334      	movs	r3, #52	; 0x34\n",
-  " 13c:	2336      	movs	r3, #54	; 0x36\n",
-  " 13e:	2338      	movs	r3, #56	; 0x38\n",
-  " 140:	233a      	movs	r3, #58	; 0x3a\n",
-  " 142:	233c      	movs	r3, #60	; 0x3c\n",
-  " 144:	233e      	movs	r3, #62	; 0x3e\n",
-  " 146:	2340      	movs	r3, #64	; 0x40\n",
-  " 148:	2342      	movs	r3, #66	; 0x42\n",
-  " 14a:	2344      	movs	r3, #68	; 0x44\n",
-  " 14c:	2346      	movs	r3, #70	; 0x46\n",
-  " 14e:	2348      	movs	r3, #72	; 0x48\n",
-  " 150:	234a      	movs	r3, #74	; 0x4a\n",
-  " 152:	234c      	movs	r3, #76	; 0x4c\n",
-  " 154:	234e      	movs	r3, #78	; 0x4e\n",
-  " 156:	2350      	movs	r3, #80	; 0x50\n",
-  " 158:	2352      	movs	r3, #82	; 0x52\n",
-  " 15a:	2354      	movs	r3, #84	; 0x54\n",
-  " 15c:	2356      	movs	r3, #86	; 0x56\n",
-  " 15e:	2358      	movs	r3, #88	; 0x58\n",
-  " 160:	235a      	movs	r3, #90	; 0x5a\n",
-  " 162:	235c      	movs	r3, #92	; 0x5c\n",
-  " 164:	235e      	movs	r3, #94	; 0x5e\n",
-  " 166:	2360      	movs	r3, #96	; 0x60\n",
-  " 168:	2362      	movs	r3, #98	; 0x62\n",
-  " 16a:	2364      	movs	r3, #100	; 0x64\n",
-  " 16c:	2366      	movs	r3, #102	; 0x66\n",
-  " 16e:	2368      	movs	r3, #104	; 0x68\n",
-  " 170:	236a      	movs	r3, #106	; 0x6a\n",
-  " 172:	236c      	movs	r3, #108	; 0x6c\n",
-  " 174:	236e      	movs	r3, #110	; 0x6e\n",
-  " 176:	2370      	movs	r3, #112	; 0x70\n",
-  " 178:	2372      	movs	r3, #114	; 0x72\n",
-  " 17a:	2374      	movs	r3, #116	; 0x74\n",
-  " 17c:	2376      	movs	r3, #118	; 0x76\n",
-  " 17e:	2378      	movs	r3, #120	; 0x78\n",
-  " 180:	237a      	movs	r3, #122	; 0x7a\n",
-  " 182:	237c      	movs	r3, #124	; 0x7c\n",
-  " 184:	237e      	movs	r3, #126	; 0x7e\n",
-  " 186:	2380      	movs	r3, #128	; 0x80\n",
-  " 188:	2382      	movs	r3, #130	; 0x82\n",
-  " 18a:	2384      	movs	r3, #132	; 0x84\n",
-  " 18c:	2386      	movs	r3, #134	; 0x86\n",
-  " 18e:	2388      	movs	r3, #136	; 0x88\n",
-  " 190:	238a      	movs	r3, #138	; 0x8a\n",
-  " 192:	238c      	movs	r3, #140	; 0x8c\n",
-  " 194:	238e      	movs	r3, #142	; 0x8e\n",
-  " 196:	2390      	movs	r3, #144	; 0x90\n",
-  " 198:	2392      	movs	r3, #146	; 0x92\n",
-  " 19a:	2394      	movs	r3, #148	; 0x94\n",
-  " 19c:	2396      	movs	r3, #150	; 0x96\n",
-  " 19e:	2398      	movs	r3, #152	; 0x98\n",
-  " 1a0:	239a      	movs	r3, #154	; 0x9a\n",
-  " 1a2:	239c      	movs	r3, #156	; 0x9c\n",
-  " 1a4:	239e      	movs	r3, #158	; 0x9e\n",
-  " 1a6:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 1a8:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 1aa:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 1ac:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 1ae:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 1b0:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 1b2:	23ac      	movs	r3, #172	; 0xac\n",
-  " 1b4:	23ae      	movs	r3, #174	; 0xae\n",
-  " 1b6:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 1b8:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 1ba:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 1bc:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 1be:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 1c0:	23ba      	movs	r3, #186	; 0xba\n",
-  " 1c2:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 1c4:	23be      	movs	r3, #190	; 0xbe\n",
-  " 1c6:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 1c8:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 1ca:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 1cc:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 1ce:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 1d0:	23ca      	movs	r3, #202	; 0xca\n",
-  " 1d2:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 1d4:	23ce      	movs	r3, #206	; 0xce\n",
-  " 1d6:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 1d8:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 1da:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 1dc:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 1de:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 1e0:	23da      	movs	r3, #218	; 0xda\n",
-  " 1e2:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 1e4:	23de      	movs	r3, #222	; 0xde\n",
-  " 1e6:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 1e8:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 1ea:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 1ec:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 1ee:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 1f0:	23ea      	movs	r3, #234	; 0xea\n",
-  " 1f2:	23ec      	movs	r3, #236	; 0xec\n",
-  " 1f4:	23ee      	movs	r3, #238	; 0xee\n",
-  " 1f6:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 1f8:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 1fa:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 1fc:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 1fe:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 200:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 202:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 204:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 206:	2300      	movs	r3, #0\n",
-  " 208:	2302      	movs	r3, #2\n",
-  " 20a:	2304      	movs	r3, #4\n",
-  " 20c:	2306      	movs	r3, #6\n",
-  " 20e:	2308      	movs	r3, #8\n",
-  " 210:	230a      	movs	r3, #10\n",
-  " 212:	230c      	movs	r3, #12\n",
-  " 214:	230e      	movs	r3, #14\n",
-  " 216:	2310      	movs	r3, #16\n",
-  " 218:	2312      	movs	r3, #18\n",
-  " 21a:	2314      	movs	r3, #20\n",
-  " 21c:	2316      	movs	r3, #22\n",
-  " 21e:	2318      	movs	r3, #24\n",
-  " 220:	231a      	movs	r3, #26\n",
-  " 222:	231c      	movs	r3, #28\n",
-  " 224:	231e      	movs	r3, #30\n",
-  " 226:	2320      	movs	r3, #32\n",
-  " 228:	2322      	movs	r3, #34	; 0x22\n",
-  " 22a:	2324      	movs	r3, #36	; 0x24\n",
-  " 22c:	2326      	movs	r3, #38	; 0x26\n",
-  " 22e:	2328      	movs	r3, #40	; 0x28\n",
-  " 230:	232a      	movs	r3, #42	; 0x2a\n",
-  " 232:	232c      	movs	r3, #44	; 0x2c\n",
-  " 234:	232e      	movs	r3, #46	; 0x2e\n",
-  " 236:	2330      	movs	r3, #48	; 0x30\n",
-  " 238:	2332      	movs	r3, #50	; 0x32\n",
-  " 23a:	2334      	movs	r3, #52	; 0x34\n",
-  " 23c:	2336      	movs	r3, #54	; 0x36\n",
-  " 23e:	2338      	movs	r3, #56	; 0x38\n",
-  " 240:	233a      	movs	r3, #58	; 0x3a\n",
-  " 242:	233c      	movs	r3, #60	; 0x3c\n",
-  " 244:	233e      	movs	r3, #62	; 0x3e\n",
-  " 246:	2340      	movs	r3, #64	; 0x40\n",
-  " 248:	2342      	movs	r3, #66	; 0x42\n",
-  " 24a:	2344      	movs	r3, #68	; 0x44\n",
-  " 24c:	2346      	movs	r3, #70	; 0x46\n",
-  " 24e:	2348      	movs	r3, #72	; 0x48\n",
-  " 250:	234a      	movs	r3, #74	; 0x4a\n",
-  " 252:	234c      	movs	r3, #76	; 0x4c\n",
-  " 254:	234e      	movs	r3, #78	; 0x4e\n",
-  " 256:	2350      	movs	r3, #80	; 0x50\n",
-  " 258:	2352      	movs	r3, #82	; 0x52\n",
-  " 25a:	2354      	movs	r3, #84	; 0x54\n",
-  " 25c:	2356      	movs	r3, #86	; 0x56\n",
-  " 25e:	2358      	movs	r3, #88	; 0x58\n",
-  " 260:	235a      	movs	r3, #90	; 0x5a\n",
-  " 262:	235c      	movs	r3, #92	; 0x5c\n",
-  " 264:	235e      	movs	r3, #94	; 0x5e\n",
-  " 266:	2360      	movs	r3, #96	; 0x60\n",
-  " 268:	2362      	movs	r3, #98	; 0x62\n",
-  " 26a:	2364      	movs	r3, #100	; 0x64\n",
-  " 26c:	2366      	movs	r3, #102	; 0x66\n",
-  " 26e:	2368      	movs	r3, #104	; 0x68\n",
-  " 270:	236a      	movs	r3, #106	; 0x6a\n",
-  " 272:	236c      	movs	r3, #108	; 0x6c\n",
-  " 274:	236e      	movs	r3, #110	; 0x6e\n",
-  " 276:	2370      	movs	r3, #112	; 0x70\n",
-  " 278:	2372      	movs	r3, #114	; 0x72\n",
-  " 27a:	2374      	movs	r3, #116	; 0x74\n",
-  " 27c:	2376      	movs	r3, #118	; 0x76\n",
-  " 27e:	2378      	movs	r3, #120	; 0x78\n",
-  " 280:	237a      	movs	r3, #122	; 0x7a\n",
-  " 282:	237c      	movs	r3, #124	; 0x7c\n",
-  " 284:	237e      	movs	r3, #126	; 0x7e\n",
-  " 286:	2380      	movs	r3, #128	; 0x80\n",
-  " 288:	2382      	movs	r3, #130	; 0x82\n",
-  " 28a:	2384      	movs	r3, #132	; 0x84\n",
-  " 28c:	2386      	movs	r3, #134	; 0x86\n",
-  " 28e:	2388      	movs	r3, #136	; 0x88\n",
-  " 290:	238a      	movs	r3, #138	; 0x8a\n",
-  " 292:	238c      	movs	r3, #140	; 0x8c\n",
-  " 294:	238e      	movs	r3, #142	; 0x8e\n",
-  " 296:	2390      	movs	r3, #144	; 0x90\n",
-  " 298:	2392      	movs	r3, #146	; 0x92\n",
-  " 29a:	2394      	movs	r3, #148	; 0x94\n",
-  " 29c:	2396      	movs	r3, #150	; 0x96\n",
-  " 29e:	2398      	movs	r3, #152	; 0x98\n",
-  " 2a0:	239a      	movs	r3, #154	; 0x9a\n",
-  " 2a2:	239c      	movs	r3, #156	; 0x9c\n",
-  " 2a4:	239e      	movs	r3, #158	; 0x9e\n",
-  " 2a6:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 2a8:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 2aa:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 2ac:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 2ae:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 2b0:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 2b2:	23ac      	movs	r3, #172	; 0xac\n",
-  " 2b4:	23ae      	movs	r3, #174	; 0xae\n",
-  " 2b6:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 2b8:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 2ba:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 2bc:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 2be:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 2c0:	23ba      	movs	r3, #186	; 0xba\n",
-  " 2c2:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 2c4:	23be      	movs	r3, #190	; 0xbe\n",
-  " 2c6:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 2c8:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 2ca:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 2cc:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 2ce:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 2d0:	23ca      	movs	r3, #202	; 0xca\n",
-  " 2d2:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 2d4:	23ce      	movs	r3, #206	; 0xce\n",
-  " 2d6:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 2d8:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 2da:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 2dc:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 2de:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 2e0:	23da      	movs	r3, #218	; 0xda\n",
-  " 2e2:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 2e4:	23de      	movs	r3, #222	; 0xde\n",
-  " 2e6:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 2e8:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 2ea:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 2ec:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 2ee:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 2f0:	23ea      	movs	r3, #234	; 0xea\n",
-  " 2f2:	23ec      	movs	r3, #236	; 0xec\n",
-  " 2f4:	23ee      	movs	r3, #238	; 0xee\n",
-  " 2f6:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 2f8:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 2fa:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 2fc:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 2fe:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 300:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 302:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 304:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 306:	2300      	movs	r3, #0\n",
-  " 308:	2302      	movs	r3, #2\n",
-  " 30a:	2304      	movs	r3, #4\n",
-  " 30c:	2306      	movs	r3, #6\n",
-  " 30e:	2308      	movs	r3, #8\n",
-  " 310:	230a      	movs	r3, #10\n",
-  " 312:	230c      	movs	r3, #12\n",
-  " 314:	230e      	movs	r3, #14\n",
-  " 316:	2310      	movs	r3, #16\n",
-  " 318:	2312      	movs	r3, #18\n",
-  " 31a:	2314      	movs	r3, #20\n",
-  " 31c:	2316      	movs	r3, #22\n",
-  " 31e:	2318      	movs	r3, #24\n",
-  " 320:	231a      	movs	r3, #26\n",
-  " 322:	231c      	movs	r3, #28\n",
-  " 324:	231e      	movs	r3, #30\n",
-  " 326:	2320      	movs	r3, #32\n",
-  " 328:	2322      	movs	r3, #34	; 0x22\n",
-  " 32a:	2324      	movs	r3, #36	; 0x24\n",
-  " 32c:	2326      	movs	r3, #38	; 0x26\n",
-  " 32e:	2328      	movs	r3, #40	; 0x28\n",
-  " 330:	232a      	movs	r3, #42	; 0x2a\n",
-  " 332:	232c      	movs	r3, #44	; 0x2c\n",
-  " 334:	232e      	movs	r3, #46	; 0x2e\n",
-  " 336:	2330      	movs	r3, #48	; 0x30\n",
-  " 338:	2332      	movs	r3, #50	; 0x32\n",
-  " 33a:	2334      	movs	r3, #52	; 0x34\n",
-  " 33c:	2336      	movs	r3, #54	; 0x36\n",
-  " 33e:	2338      	movs	r3, #56	; 0x38\n",
-  " 340:	233a      	movs	r3, #58	; 0x3a\n",
-  " 342:	233c      	movs	r3, #60	; 0x3c\n",
-  " 344:	233e      	movs	r3, #62	; 0x3e\n",
-  " 346:	2340      	movs	r3, #64	; 0x40\n",
-  " 348:	2342      	movs	r3, #66	; 0x42\n",
-  " 34a:	2344      	movs	r3, #68	; 0x44\n",
-  " 34c:	2346      	movs	r3, #70	; 0x46\n",
-  " 34e:	2348      	movs	r3, #72	; 0x48\n",
-  " 350:	234a      	movs	r3, #74	; 0x4a\n",
-  " 352:	234c      	movs	r3, #76	; 0x4c\n",
-  " 354:	234e      	movs	r3, #78	; 0x4e\n",
-  " 356:	2350      	movs	r3, #80	; 0x50\n",
-  " 358:	2352      	movs	r3, #82	; 0x52\n",
-  " 35a:	2354      	movs	r3, #84	; 0x54\n",
-  " 35c:	2356      	movs	r3, #86	; 0x56\n",
-  " 35e:	2358      	movs	r3, #88	; 0x58\n",
-  " 360:	235a      	movs	r3, #90	; 0x5a\n",
-  " 362:	235c      	movs	r3, #92	; 0x5c\n",
-  " 364:	235e      	movs	r3, #94	; 0x5e\n",
-  " 366:	2360      	movs	r3, #96	; 0x60\n",
-  " 368:	2362      	movs	r3, #98	; 0x62\n",
-  " 36a:	2364      	movs	r3, #100	; 0x64\n",
-  " 36c:	2366      	movs	r3, #102	; 0x66\n",
-  " 36e:	2368      	movs	r3, #104	; 0x68\n",
-  " 370:	236a      	movs	r3, #106	; 0x6a\n",
-  " 372:	236c      	movs	r3, #108	; 0x6c\n",
-  " 374:	236e      	movs	r3, #110	; 0x6e\n",
-  " 376:	2370      	movs	r3, #112	; 0x70\n",
-  " 378:	2372      	movs	r3, #114	; 0x72\n",
-  " 37a:	2374      	movs	r3, #116	; 0x74\n",
-  " 37c:	2376      	movs	r3, #118	; 0x76\n",
-  " 37e:	2378      	movs	r3, #120	; 0x78\n",
-  " 380:	237a      	movs	r3, #122	; 0x7a\n",
-  " 382:	237c      	movs	r3, #124	; 0x7c\n",
-  " 384:	237e      	movs	r3, #126	; 0x7e\n",
-  " 386:	2380      	movs	r3, #128	; 0x80\n",
-  " 388:	2382      	movs	r3, #130	; 0x82\n",
-  " 38a:	2384      	movs	r3, #132	; 0x84\n",
-  " 38c:	2386      	movs	r3, #134	; 0x86\n",
-  " 38e:	2388      	movs	r3, #136	; 0x88\n",
-  " 390:	238a      	movs	r3, #138	; 0x8a\n",
-  " 392:	238c      	movs	r3, #140	; 0x8c\n",
-  " 394:	238e      	movs	r3, #142	; 0x8e\n",
-  " 396:	2390      	movs	r3, #144	; 0x90\n",
-  " 398:	2392      	movs	r3, #146	; 0x92\n",
-  " 39a:	2394      	movs	r3, #148	; 0x94\n",
-  " 39c:	2396      	movs	r3, #150	; 0x96\n",
-  " 39e:	2398      	movs	r3, #152	; 0x98\n",
-  " 3a0:	239a      	movs	r3, #154	; 0x9a\n",
-  " 3a2:	239c      	movs	r3, #156	; 0x9c\n",
-  " 3a4:	239e      	movs	r3, #158	; 0x9e\n",
-  " 3a6:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 3a8:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 3aa:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 3ac:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 3ae:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 3b0:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 3b2:	23ac      	movs	r3, #172	; 0xac\n",
-  " 3b4:	23ae      	movs	r3, #174	; 0xae\n",
-  " 3b6:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 3b8:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 3ba:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 3bc:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 3be:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 3c0:	23ba      	movs	r3, #186	; 0xba\n",
-  " 3c2:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 3c4:	23be      	movs	r3, #190	; 0xbe\n",
-  " 3c6:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 3c8:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 3ca:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 3cc:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 3ce:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 3d0:	23ca      	movs	r3, #202	; 0xca\n",
-  " 3d2:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 3d4:	23ce      	movs	r3, #206	; 0xce\n",
-  " 3d6:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 3d8:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 3da:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 3dc:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 3de:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 3e0:	23da      	movs	r3, #218	; 0xda\n",
-  " 3e2:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 3e4:	23de      	movs	r3, #222	; 0xde\n",
-  " 3e6:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 3e8:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 3ea:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 3ec:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 3ee:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 3f0:	23ea      	movs	r3, #234	; 0xea\n",
-  " 3f2:	23ec      	movs	r3, #236	; 0xec\n",
-  " 3f4:	23ee      	movs	r3, #238	; 0xee\n",
-  " 3f6:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 3f8:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 3fa:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 3fc:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 3fe:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 400:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 402:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 404:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 406:	2300      	movs	r3, #0\n",
-  " 408:	2302      	movs	r3, #2\n",
-  " 40a:	2304      	movs	r3, #4\n",
-  " 40c:	2306      	movs	r3, #6\n",
-  " 40e:	2308      	movs	r3, #8\n",
-  " 410:	230a      	movs	r3, #10\n",
-  " 412:	230c      	movs	r3, #12\n",
-  " 414:	230e      	movs	r3, #14\n",
-  " 416:	2310      	movs	r3, #16\n",
-  " 418:	2312      	movs	r3, #18\n",
-  " 41a:	2314      	movs	r3, #20\n",
-  " 41c:	2316      	movs	r3, #22\n",
-  " 41e:	2318      	movs	r3, #24\n",
-  " 420:	231a      	movs	r3, #26\n",
-  " 422:	231c      	movs	r3, #28\n",
-  " 424:	231e      	movs	r3, #30\n",
-  " 426:	2320      	movs	r3, #32\n",
-  " 428:	2322      	movs	r3, #34	; 0x22\n",
-  " 42a:	2324      	movs	r3, #36	; 0x24\n",
-  " 42c:	2326      	movs	r3, #38	; 0x26\n",
-  " 42e:	2328      	movs	r3, #40	; 0x28\n",
-  " 430:	232a      	movs	r3, #42	; 0x2a\n",
-  " 432:	232c      	movs	r3, #44	; 0x2c\n",
-  " 434:	232e      	movs	r3, #46	; 0x2e\n",
-  " 436:	2330      	movs	r3, #48	; 0x30\n",
-  " 438:	2332      	movs	r3, #50	; 0x32\n",
-  " 43a:	2334      	movs	r3, #52	; 0x34\n",
-  " 43c:	2336      	movs	r3, #54	; 0x36\n",
-  " 43e:	2338      	movs	r3, #56	; 0x38\n",
-  " 440:	233a      	movs	r3, #58	; 0x3a\n",
-  " 442:	233c      	movs	r3, #60	; 0x3c\n",
-  " 444:	233e      	movs	r3, #62	; 0x3e\n",
-  " 446:	2340      	movs	r3, #64	; 0x40\n",
-  " 448:	2342      	movs	r3, #66	; 0x42\n",
-  " 44a:	2344      	movs	r3, #68	; 0x44\n",
-  " 44c:	2346      	movs	r3, #70	; 0x46\n",
-  " 44e:	2348      	movs	r3, #72	; 0x48\n",
-  " 450:	234a      	movs	r3, #74	; 0x4a\n",
-  " 452:	234c      	movs	r3, #76	; 0x4c\n",
-  " 454:	234e      	movs	r3, #78	; 0x4e\n",
-  " 456:	2350      	movs	r3, #80	; 0x50\n",
-  " 458:	2352      	movs	r3, #82	; 0x52\n",
-  " 45a:	2354      	movs	r3, #84	; 0x54\n",
-  " 45c:	2356      	movs	r3, #86	; 0x56\n",
-  " 45e:	2358      	movs	r3, #88	; 0x58\n",
-  " 460:	235a      	movs	r3, #90	; 0x5a\n",
-  " 462:	235c      	movs	r3, #92	; 0x5c\n",
-  " 464:	235e      	movs	r3, #94	; 0x5e\n",
-  " 466:	2360      	movs	r3, #96	; 0x60\n",
-  " 468:	2362      	movs	r3, #98	; 0x62\n",
-  " 46a:	2364      	movs	r3, #100	; 0x64\n",
-  " 46c:	2366      	movs	r3, #102	; 0x66\n",
-  " 46e:	2368      	movs	r3, #104	; 0x68\n",
-  " 470:	236a      	movs	r3, #106	; 0x6a\n",
-  " 472:	236c      	movs	r3, #108	; 0x6c\n",
-  " 474:	236e      	movs	r3, #110	; 0x6e\n",
-  " 476:	2370      	movs	r3, #112	; 0x70\n",
-  " 478:	2372      	movs	r3, #114	; 0x72\n",
-  " 47a:	2374      	movs	r3, #116	; 0x74\n",
-  " 47c:	2376      	movs	r3, #118	; 0x76\n",
-  " 47e:	2378      	movs	r3, #120	; 0x78\n",
-  " 480:	237a      	movs	r3, #122	; 0x7a\n",
-  " 482:	237c      	movs	r3, #124	; 0x7c\n",
-  " 484:	237e      	movs	r3, #126	; 0x7e\n",
-  " 486:	2380      	movs	r3, #128	; 0x80\n",
-  " 488:	2382      	movs	r3, #130	; 0x82\n",
-  " 48a:	2384      	movs	r3, #132	; 0x84\n",
-  " 48c:	2386      	movs	r3, #134	; 0x86\n",
-  " 48e:	2388      	movs	r3, #136	; 0x88\n",
-  " 490:	238a      	movs	r3, #138	; 0x8a\n",
-  " 492:	238c      	movs	r3, #140	; 0x8c\n",
-  " 494:	238e      	movs	r3, #142	; 0x8e\n",
-  " 496:	2390      	movs	r3, #144	; 0x90\n",
-  " 498:	2392      	movs	r3, #146	; 0x92\n",
-  " 49a:	2394      	movs	r3, #148	; 0x94\n",
-  " 49c:	2396      	movs	r3, #150	; 0x96\n",
-  " 49e:	2398      	movs	r3, #152	; 0x98\n",
-  " 4a0:	239a      	movs	r3, #154	; 0x9a\n",
-  " 4a2:	239c      	movs	r3, #156	; 0x9c\n",
-  " 4a4:	239e      	movs	r3, #158	; 0x9e\n",
-  " 4a6:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 4a8:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 4aa:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 4ac:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 4ae:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 4b0:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 4b2:	23ac      	movs	r3, #172	; 0xac\n",
-  " 4b4:	23ae      	movs	r3, #174	; 0xae\n",
-  " 4b6:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 4b8:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 4ba:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 4bc:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 4be:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 4c0:	23ba      	movs	r3, #186	; 0xba\n",
-  " 4c2:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 4c4:	23be      	movs	r3, #190	; 0xbe\n",
-  " 4c6:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 4c8:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 4ca:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 4cc:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 4ce:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 4d0:	23ca      	movs	r3, #202	; 0xca\n",
-  " 4d2:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 4d4:	23ce      	movs	r3, #206	; 0xce\n",
-  " 4d6:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 4d8:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 4da:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 4dc:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 4de:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 4e0:	23da      	movs	r3, #218	; 0xda\n",
-  " 4e2:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 4e4:	23de      	movs	r3, #222	; 0xde\n",
-  " 4e6:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 4e8:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 4ea:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 4ec:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 4ee:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 4f0:	23ea      	movs	r3, #234	; 0xea\n",
-  " 4f2:	23ec      	movs	r3, #236	; 0xec\n",
-  " 4f4:	23ee      	movs	r3, #238	; 0xee\n",
-  " 4f6:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 4f8:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 4fa:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 4fc:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 4fe:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 500:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 502:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 504:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 506:	2300      	movs	r3, #0\n",
-  " 508:	2302      	movs	r3, #2\n",
-  " 50a:	2304      	movs	r3, #4\n",
-  " 50c:	2306      	movs	r3, #6\n",
-  " 50e:	2308      	movs	r3, #8\n",
-  " 510:	230a      	movs	r3, #10\n",
-  " 512:	230c      	movs	r3, #12\n",
-  " 514:	230e      	movs	r3, #14\n",
-  " 516:	2310      	movs	r3, #16\n",
-  " 518:	2312      	movs	r3, #18\n",
-  " 51a:	2314      	movs	r3, #20\n",
-  " 51c:	2316      	movs	r3, #22\n",
-  " 51e:	2318      	movs	r3, #24\n",
-  " 520:	231a      	movs	r3, #26\n",
-  " 522:	231c      	movs	r3, #28\n",
-  " 524:	231e      	movs	r3, #30\n",
-  " 526:	2320      	movs	r3, #32\n",
-  " 528:	2322      	movs	r3, #34	; 0x22\n",
-  " 52a:	2324      	movs	r3, #36	; 0x24\n",
-  " 52c:	2326      	movs	r3, #38	; 0x26\n",
-  " 52e:	2328      	movs	r3, #40	; 0x28\n",
-  " 530:	232a      	movs	r3, #42	; 0x2a\n",
-  " 532:	232c      	movs	r3, #44	; 0x2c\n",
-  " 534:	232e      	movs	r3, #46	; 0x2e\n",
-  " 536:	2330      	movs	r3, #48	; 0x30\n",
-  " 538:	2332      	movs	r3, #50	; 0x32\n",
-  " 53a:	2334      	movs	r3, #52	; 0x34\n",
-  " 53c:	2336      	movs	r3, #54	; 0x36\n",
-  " 53e:	2338      	movs	r3, #56	; 0x38\n",
-  " 540:	233a      	movs	r3, #58	; 0x3a\n",
-  " 542:	233c      	movs	r3, #60	; 0x3c\n",
-  " 544:	233e      	movs	r3, #62	; 0x3e\n",
-  " 546:	2340      	movs	r3, #64	; 0x40\n",
-  " 548:	2342      	movs	r3, #66	; 0x42\n",
-  " 54a:	2344      	movs	r3, #68	; 0x44\n",
-  " 54c:	2346      	movs	r3, #70	; 0x46\n",
-  " 54e:	2348      	movs	r3, #72	; 0x48\n",
-  " 550:	234a      	movs	r3, #74	; 0x4a\n",
-  " 552:	234c      	movs	r3, #76	; 0x4c\n",
-  " 554:	234e      	movs	r3, #78	; 0x4e\n",
-  " 556:	2350      	movs	r3, #80	; 0x50\n",
-  " 558:	2352      	movs	r3, #82	; 0x52\n",
-  " 55a:	2354      	movs	r3, #84	; 0x54\n",
-  " 55c:	2356      	movs	r3, #86	; 0x56\n",
-  " 55e:	2358      	movs	r3, #88	; 0x58\n",
-  " 560:	235a      	movs	r3, #90	; 0x5a\n",
-  " 562:	235c      	movs	r3, #92	; 0x5c\n",
-  " 564:	235e      	movs	r3, #94	; 0x5e\n",
-  " 566:	2360      	movs	r3, #96	; 0x60\n",
-  " 568:	2362      	movs	r3, #98	; 0x62\n",
-  " 56a:	2364      	movs	r3, #100	; 0x64\n",
-  " 56c:	2366      	movs	r3, #102	; 0x66\n",
-  " 56e:	2368      	movs	r3, #104	; 0x68\n",
-  " 570:	236a      	movs	r3, #106	; 0x6a\n",
-  " 572:	236c      	movs	r3, #108	; 0x6c\n",
-  " 574:	236e      	movs	r3, #110	; 0x6e\n",
-  " 576:	2370      	movs	r3, #112	; 0x70\n",
-  " 578:	2372      	movs	r3, #114	; 0x72\n",
-  " 57a:	2374      	movs	r3, #116	; 0x74\n",
-  " 57c:	2376      	movs	r3, #118	; 0x76\n",
-  " 57e:	2378      	movs	r3, #120	; 0x78\n",
-  " 580:	237a      	movs	r3, #122	; 0x7a\n",
-  " 582:	237c      	movs	r3, #124	; 0x7c\n",
-  " 584:	237e      	movs	r3, #126	; 0x7e\n",
-  " 586:	2380      	movs	r3, #128	; 0x80\n",
-  " 588:	2382      	movs	r3, #130	; 0x82\n",
-  " 58a:	2384      	movs	r3, #132	; 0x84\n",
-  " 58c:	2386      	movs	r3, #134	; 0x86\n",
-  " 58e:	2388      	movs	r3, #136	; 0x88\n",
-  " 590:	238a      	movs	r3, #138	; 0x8a\n",
-  " 592:	238c      	movs	r3, #140	; 0x8c\n",
-  " 594:	238e      	movs	r3, #142	; 0x8e\n",
-  " 596:	2390      	movs	r3, #144	; 0x90\n",
-  " 598:	2392      	movs	r3, #146	; 0x92\n",
-  " 59a:	2394      	movs	r3, #148	; 0x94\n",
-  " 59c:	2396      	movs	r3, #150	; 0x96\n",
-  " 59e:	2398      	movs	r3, #152	; 0x98\n",
-  " 5a0:	239a      	movs	r3, #154	; 0x9a\n",
-  " 5a2:	239c      	movs	r3, #156	; 0x9c\n",
-  " 5a4:	239e      	movs	r3, #158	; 0x9e\n",
-  " 5a6:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 5a8:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 5aa:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 5ac:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 5ae:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 5b0:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 5b2:	23ac      	movs	r3, #172	; 0xac\n",
-  " 5b4:	23ae      	movs	r3, #174	; 0xae\n",
-  " 5b6:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 5b8:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 5ba:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 5bc:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 5be:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 5c0:	23ba      	movs	r3, #186	; 0xba\n",
-  " 5c2:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 5c4:	23be      	movs	r3, #190	; 0xbe\n",
-  " 5c6:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 5c8:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 5ca:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 5cc:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 5ce:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 5d0:	23ca      	movs	r3, #202	; 0xca\n",
-  " 5d2:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 5d4:	23ce      	movs	r3, #206	; 0xce\n",
-  " 5d6:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 5d8:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 5da:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 5dc:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 5de:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 5e0:	23da      	movs	r3, #218	; 0xda\n",
-  " 5e2:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 5e4:	23de      	movs	r3, #222	; 0xde\n",
-  " 5e6:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 5e8:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 5ea:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 5ec:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 5ee:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 5f0:	23ea      	movs	r3, #234	; 0xea\n",
-  " 5f2:	23ec      	movs	r3, #236	; 0xec\n",
-  " 5f4:	23ee      	movs	r3, #238	; 0xee\n",
-  " 5f6:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 5f8:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 5fa:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 5fc:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 5fe:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 600:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 602:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 604:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 606:	2300      	movs	r3, #0\n",
-  " 608:	2302      	movs	r3, #2\n",
-  " 60a:	2304      	movs	r3, #4\n",
-  " 60c:	2306      	movs	r3, #6\n",
-  " 60e:	2308      	movs	r3, #8\n",
-  " 610:	230a      	movs	r3, #10\n",
-  " 612:	230c      	movs	r3, #12\n",
-  " 614:	230e      	movs	r3, #14\n",
-  " 616:	2310      	movs	r3, #16\n",
-  " 618:	2312      	movs	r3, #18\n",
-  " 61a:	2314      	movs	r3, #20\n",
-  " 61c:	2316      	movs	r3, #22\n",
-  " 61e:	2318      	movs	r3, #24\n",
-  " 620:	231a      	movs	r3, #26\n",
-  " 622:	231c      	movs	r3, #28\n",
-  " 624:	231e      	movs	r3, #30\n",
-  " 626:	2320      	movs	r3, #32\n",
-  " 628:	2322      	movs	r3, #34	; 0x22\n",
-  " 62a:	2324      	movs	r3, #36	; 0x24\n",
-  " 62c:	2326      	movs	r3, #38	; 0x26\n",
-  " 62e:	2328      	movs	r3, #40	; 0x28\n",
-  " 630:	232a      	movs	r3, #42	; 0x2a\n",
-  " 632:	232c      	movs	r3, #44	; 0x2c\n",
-  " 634:	232e      	movs	r3, #46	; 0x2e\n",
-  " 636:	2330      	movs	r3, #48	; 0x30\n",
-  " 638:	2332      	movs	r3, #50	; 0x32\n",
-  " 63a:	2334      	movs	r3, #52	; 0x34\n",
-  " 63c:	2336      	movs	r3, #54	; 0x36\n",
-  " 63e:	2338      	movs	r3, #56	; 0x38\n",
-  " 640:	233a      	movs	r3, #58	; 0x3a\n",
-  " 642:	233c      	movs	r3, #60	; 0x3c\n",
-  " 644:	233e      	movs	r3, #62	; 0x3e\n",
-  " 646:	2340      	movs	r3, #64	; 0x40\n",
-  " 648:	2342      	movs	r3, #66	; 0x42\n",
-  " 64a:	2344      	movs	r3, #68	; 0x44\n",
-  " 64c:	2346      	movs	r3, #70	; 0x46\n",
-  " 64e:	2348      	movs	r3, #72	; 0x48\n",
-  " 650:	234a      	movs	r3, #74	; 0x4a\n",
-  " 652:	234c      	movs	r3, #76	; 0x4c\n",
-  " 654:	234e      	movs	r3, #78	; 0x4e\n",
-  " 656:	2350      	movs	r3, #80	; 0x50\n",
-  " 658:	2352      	movs	r3, #82	; 0x52\n",
-  " 65a:	2354      	movs	r3, #84	; 0x54\n",
-  " 65c:	2356      	movs	r3, #86	; 0x56\n",
-  " 65e:	2358      	movs	r3, #88	; 0x58\n",
-  " 660:	235a      	movs	r3, #90	; 0x5a\n",
-  " 662:	235c      	movs	r3, #92	; 0x5c\n",
-  " 664:	235e      	movs	r3, #94	; 0x5e\n",
-  " 666:	2360      	movs	r3, #96	; 0x60\n",
-  " 668:	2362      	movs	r3, #98	; 0x62\n",
-  " 66a:	2364      	movs	r3, #100	; 0x64\n",
-  " 66c:	2366      	movs	r3, #102	; 0x66\n",
-  " 66e:	2368      	movs	r3, #104	; 0x68\n",
-  " 670:	236a      	movs	r3, #106	; 0x6a\n",
-  " 672:	236c      	movs	r3, #108	; 0x6c\n",
-  " 674:	236e      	movs	r3, #110	; 0x6e\n",
-  " 676:	2370      	movs	r3, #112	; 0x70\n",
-  " 678:	2372      	movs	r3, #114	; 0x72\n",
-  " 67a:	2374      	movs	r3, #116	; 0x74\n",
-  " 67c:	2376      	movs	r3, #118	; 0x76\n",
-  " 67e:	2378      	movs	r3, #120	; 0x78\n",
-  " 680:	237a      	movs	r3, #122	; 0x7a\n",
-  " 682:	237c      	movs	r3, #124	; 0x7c\n",
-  " 684:	237e      	movs	r3, #126	; 0x7e\n",
-  " 686:	2380      	movs	r3, #128	; 0x80\n",
-  " 688:	2382      	movs	r3, #130	; 0x82\n",
-  " 68a:	2384      	movs	r3, #132	; 0x84\n",
-  " 68c:	2386      	movs	r3, #134	; 0x86\n",
-  " 68e:	2388      	movs	r3, #136	; 0x88\n",
-  " 690:	238a      	movs	r3, #138	; 0x8a\n",
-  " 692:	238c      	movs	r3, #140	; 0x8c\n",
-  " 694:	238e      	movs	r3, #142	; 0x8e\n",
-  " 696:	2390      	movs	r3, #144	; 0x90\n",
-  " 698:	2392      	movs	r3, #146	; 0x92\n",
-  " 69a:	2394      	movs	r3, #148	; 0x94\n",
-  " 69c:	2396      	movs	r3, #150	; 0x96\n",
-  " 69e:	2398      	movs	r3, #152	; 0x98\n",
-  " 6a0:	239a      	movs	r3, #154	; 0x9a\n",
-  " 6a2:	239c      	movs	r3, #156	; 0x9c\n",
-  " 6a4:	239e      	movs	r3, #158	; 0x9e\n",
-  " 6a6:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 6a8:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 6aa:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 6ac:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 6ae:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 6b0:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 6b2:	23ac      	movs	r3, #172	; 0xac\n",
-  " 6b4:	23ae      	movs	r3, #174	; 0xae\n",
-  " 6b6:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 6b8:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 6ba:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 6bc:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 6be:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 6c0:	23ba      	movs	r3, #186	; 0xba\n",
-  " 6c2:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 6c4:	23be      	movs	r3, #190	; 0xbe\n",
-  " 6c6:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 6c8:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 6ca:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 6cc:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 6ce:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 6d0:	23ca      	movs	r3, #202	; 0xca\n",
-  " 6d2:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 6d4:	23ce      	movs	r3, #206	; 0xce\n",
-  " 6d6:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 6d8:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 6da:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 6dc:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 6de:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 6e0:	23da      	movs	r3, #218	; 0xda\n",
-  " 6e2:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 6e4:	23de      	movs	r3, #222	; 0xde\n",
-  " 6e6:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 6e8:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 6ea:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 6ec:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 6ee:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 6f0:	23ea      	movs	r3, #234	; 0xea\n",
-  " 6f2:	23ec      	movs	r3, #236	; 0xec\n",
-  " 6f4:	23ee      	movs	r3, #238	; 0xee\n",
-  " 6f6:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 6f8:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 6fa:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 6fc:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 6fe:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 700:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 702:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 704:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 706:	2300      	movs	r3, #0\n",
-  " 708:	2302      	movs	r3, #2\n",
-  " 70a:	2304      	movs	r3, #4\n",
-  " 70c:	2306      	movs	r3, #6\n",
-  " 70e:	2308      	movs	r3, #8\n",
-  " 710:	230a      	movs	r3, #10\n",
-  " 712:	230c      	movs	r3, #12\n",
-  " 714:	230e      	movs	r3, #14\n",
-  " 716:	2310      	movs	r3, #16\n",
-  " 718:	2312      	movs	r3, #18\n",
-  " 71a:	2314      	movs	r3, #20\n",
-  " 71c:	2316      	movs	r3, #22\n",
-  " 71e:	2318      	movs	r3, #24\n",
-  " 720:	231a      	movs	r3, #26\n",
-  " 722:	231c      	movs	r3, #28\n",
-  " 724:	231e      	movs	r3, #30\n",
-  " 726:	2320      	movs	r3, #32\n",
-  " 728:	2322      	movs	r3, #34	; 0x22\n",
-  " 72a:	2324      	movs	r3, #36	; 0x24\n",
-  " 72c:	2326      	movs	r3, #38	; 0x26\n",
-  " 72e:	2328      	movs	r3, #40	; 0x28\n",
-  " 730:	232a      	movs	r3, #42	; 0x2a\n",
-  " 732:	232c      	movs	r3, #44	; 0x2c\n",
-  " 734:	232e      	movs	r3, #46	; 0x2e\n",
-  " 736:	2330      	movs	r3, #48	; 0x30\n",
-  " 738:	2332      	movs	r3, #50	; 0x32\n",
-  " 73a:	2334      	movs	r3, #52	; 0x34\n",
-  " 73c:	2336      	movs	r3, #54	; 0x36\n",
-  " 73e:	2338      	movs	r3, #56	; 0x38\n",
-  " 740:	233a      	movs	r3, #58	; 0x3a\n",
-  " 742:	233c      	movs	r3, #60	; 0x3c\n",
-  " 744:	233e      	movs	r3, #62	; 0x3e\n",
-  " 746:	2340      	movs	r3, #64	; 0x40\n",
-  " 748:	2342      	movs	r3, #66	; 0x42\n",
-  " 74a:	2344      	movs	r3, #68	; 0x44\n",
-  " 74c:	2346      	movs	r3, #70	; 0x46\n",
-  " 74e:	2348      	movs	r3, #72	; 0x48\n",
-  " 750:	234a      	movs	r3, #74	; 0x4a\n",
-  " 752:	234c      	movs	r3, #76	; 0x4c\n",
-  " 754:	234e      	movs	r3, #78	; 0x4e\n",
-  " 756:	2350      	movs	r3, #80	; 0x50\n",
-  " 758:	2352      	movs	r3, #82	; 0x52\n",
-  " 75a:	2354      	movs	r3, #84	; 0x54\n",
-  " 75c:	2356      	movs	r3, #86	; 0x56\n",
-  " 75e:	2358      	movs	r3, #88	; 0x58\n",
-  " 760:	235a      	movs	r3, #90	; 0x5a\n",
-  " 762:	235c      	movs	r3, #92	; 0x5c\n",
-  " 764:	235e      	movs	r3, #94	; 0x5e\n",
-  " 766:	2360      	movs	r3, #96	; 0x60\n",
-  " 768:	2362      	movs	r3, #98	; 0x62\n",
-  " 76a:	2364      	movs	r3, #100	; 0x64\n",
-  " 76c:	2366      	movs	r3, #102	; 0x66\n",
-  " 76e:	2368      	movs	r3, #104	; 0x68\n",
-  " 770:	236a      	movs	r3, #106	; 0x6a\n",
-  " 772:	236c      	movs	r3, #108	; 0x6c\n",
-  " 774:	236e      	movs	r3, #110	; 0x6e\n",
-  " 776:	2370      	movs	r3, #112	; 0x70\n",
-  " 778:	2372      	movs	r3, #114	; 0x72\n",
-  " 77a:	2374      	movs	r3, #116	; 0x74\n",
-  " 77c:	2376      	movs	r3, #118	; 0x76\n",
-  " 77e:	2378      	movs	r3, #120	; 0x78\n",
-  " 780:	237a      	movs	r3, #122	; 0x7a\n",
-  " 782:	237c      	movs	r3, #124	; 0x7c\n",
-  " 784:	237e      	movs	r3, #126	; 0x7e\n",
-  " 786:	2380      	movs	r3, #128	; 0x80\n",
-  " 788:	2382      	movs	r3, #130	; 0x82\n",
-  " 78a:	2384      	movs	r3, #132	; 0x84\n",
-  " 78c:	2386      	movs	r3, #134	; 0x86\n",
-  " 78e:	2388      	movs	r3, #136	; 0x88\n",
-  " 790:	238a      	movs	r3, #138	; 0x8a\n",
-  " 792:	238c      	movs	r3, #140	; 0x8c\n",
-  " 794:	238e      	movs	r3, #142	; 0x8e\n",
-  " 796:	2390      	movs	r3, #144	; 0x90\n",
-  " 798:	2392      	movs	r3, #146	; 0x92\n",
-  " 79a:	2394      	movs	r3, #148	; 0x94\n",
-  " 79c:	2396      	movs	r3, #150	; 0x96\n",
-  " 79e:	2398      	movs	r3, #152	; 0x98\n",
-  " 7a0:	239a      	movs	r3, #154	; 0x9a\n",
-  " 7a2:	239c      	movs	r3, #156	; 0x9c\n",
-  " 7a4:	239e      	movs	r3, #158	; 0x9e\n",
-  " 7a6:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 7a8:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 7aa:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 7ac:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 7ae:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 7b0:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 7b2:	23ac      	movs	r3, #172	; 0xac\n",
-  " 7b4:	23ae      	movs	r3, #174	; 0xae\n",
-  " 7b6:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 7b8:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 7ba:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 7bc:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 7be:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 7c0:	23ba      	movs	r3, #186	; 0xba\n",
-  " 7c2:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 7c4:	23be      	movs	r3, #190	; 0xbe\n",
-  " 7c6:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 7c8:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 7ca:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 7cc:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 7ce:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 7d0:	23ca      	movs	r3, #202	; 0xca\n",
-  " 7d2:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 7d4:	23ce      	movs	r3, #206	; 0xce\n",
-  " 7d6:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 7d8:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 7da:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 7dc:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 7de:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 7e0:	23da      	movs	r3, #218	; 0xda\n",
-  " 7e2:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 7e4:	23de      	movs	r3, #222	; 0xde\n",
-  " 7e6:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 7e8:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 7ea:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 7ec:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 7ee:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 7f0:	23ea      	movs	r3, #234	; 0xea\n",
-  " 7f2:	23ec      	movs	r3, #236	; 0xec\n",
-  " 7f4:	23ee      	movs	r3, #238	; 0xee\n",
-  " 7f6:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 7f8:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 7fa:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 7fc:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 7fe:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 800:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 802:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 804:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 806:	2300      	movs	r3, #0\n",
-  " 808:	4611      	mov	r1, r2\n",
-  nullptr
-};
-const char* const MixedBranch32Results[] = {
-  "   0:	f000 bc03 	b.w	80a <MixedBranch32+0x80a>\n",
-  "   4:	2300      	movs	r3, #0\n",
-  "   6:	2302      	movs	r3, #2\n",
-  "   8:	2304      	movs	r3, #4\n",
-  "   a:	2306      	movs	r3, #6\n",
-  "   c:	2308      	movs	r3, #8\n",
-  "   e:	230a      	movs	r3, #10\n",
-  "  10:	230c      	movs	r3, #12\n",
-  "  12:	230e      	movs	r3, #14\n",
-  "  14:	2310      	movs	r3, #16\n",
-  "  16:	2312      	movs	r3, #18\n",
-  "  18:	2314      	movs	r3, #20\n",
-  "  1a:	2316      	movs	r3, #22\n",
-  "  1c:	2318      	movs	r3, #24\n",
-  "  1e:	231a      	movs	r3, #26\n",
-  "  20:	231c      	movs	r3, #28\n",
-  "  22:	231e      	movs	r3, #30\n",
-  "  24:	2320      	movs	r3, #32\n",
-  "  26:	2322      	movs	r3, #34	; 0x22\n",
-  "  28:	2324      	movs	r3, #36	; 0x24\n",
-  "  2a:	2326      	movs	r3, #38	; 0x26\n",
-  "  2c:	2328      	movs	r3, #40	; 0x28\n",
-  "  2e:	232a      	movs	r3, #42	; 0x2a\n",
-  "  30:	232c      	movs	r3, #44	; 0x2c\n",
-  "  32:	232e      	movs	r3, #46	; 0x2e\n",
-  "  34:	2330      	movs	r3, #48	; 0x30\n",
-  "  36:	2332      	movs	r3, #50	; 0x32\n",
-  "  38:	2334      	movs	r3, #52	; 0x34\n",
-  "  3a:	2336      	movs	r3, #54	; 0x36\n",
-  "  3c:	2338      	movs	r3, #56	; 0x38\n",
-  "  3e:	233a      	movs	r3, #58	; 0x3a\n",
-  "  40:	233c      	movs	r3, #60	; 0x3c\n",
-  "  42:	233e      	movs	r3, #62	; 0x3e\n",
-  "  44:	2340      	movs	r3, #64	; 0x40\n",
-  "  46:	2342      	movs	r3, #66	; 0x42\n",
-  "  48:	2344      	movs	r3, #68	; 0x44\n",
-  "  4a:	2346      	movs	r3, #70	; 0x46\n",
-  "  4c:	2348      	movs	r3, #72	; 0x48\n",
-  "  4e:	234a      	movs	r3, #74	; 0x4a\n",
-  "  50:	234c      	movs	r3, #76	; 0x4c\n",
-  "  52:	234e      	movs	r3, #78	; 0x4e\n",
-  "  54:	2350      	movs	r3, #80	; 0x50\n",
-  "  56:	2352      	movs	r3, #82	; 0x52\n",
-  "  58:	2354      	movs	r3, #84	; 0x54\n",
-  "  5a:	2356      	movs	r3, #86	; 0x56\n",
-  "  5c:	2358      	movs	r3, #88	; 0x58\n",
-  "  5e:	235a      	movs	r3, #90	; 0x5a\n",
-  "  60:	235c      	movs	r3, #92	; 0x5c\n",
-  "  62:	235e      	movs	r3, #94	; 0x5e\n",
-  "  64:	2360      	movs	r3, #96	; 0x60\n",
-  "  66:	2362      	movs	r3, #98	; 0x62\n",
-  "  68:	2364      	movs	r3, #100	; 0x64\n",
-  "  6a:	2366      	movs	r3, #102	; 0x66\n",
-  "  6c:	2368      	movs	r3, #104	; 0x68\n",
-  "  6e:	236a      	movs	r3, #106	; 0x6a\n",
-  "  70:	236c      	movs	r3, #108	; 0x6c\n",
-  "  72:	236e      	movs	r3, #110	; 0x6e\n",
-  "  74:	2370      	movs	r3, #112	; 0x70\n",
-  "  76:	2372      	movs	r3, #114	; 0x72\n",
-  "  78:	2374      	movs	r3, #116	; 0x74\n",
-  "  7a:	2376      	movs	r3, #118	; 0x76\n",
-  "  7c:	2378      	movs	r3, #120	; 0x78\n",
-  "  7e:	237a      	movs	r3, #122	; 0x7a\n",
-  "  80:	237c      	movs	r3, #124	; 0x7c\n",
-  "  82:	237e      	movs	r3, #126	; 0x7e\n",
-  "  84:	2380      	movs	r3, #128	; 0x80\n",
-  "  86:	2382      	movs	r3, #130	; 0x82\n",
-  "  88:	2384      	movs	r3, #132	; 0x84\n",
-  "  8a:	2386      	movs	r3, #134	; 0x86\n",
-  "  8c:	2388      	movs	r3, #136	; 0x88\n",
-  "  8e:	238a      	movs	r3, #138	; 0x8a\n",
-  "  90:	238c      	movs	r3, #140	; 0x8c\n",
-  "  92:	238e      	movs	r3, #142	; 0x8e\n",
-  "  94:	2390      	movs	r3, #144	; 0x90\n",
-  "  96:	2392      	movs	r3, #146	; 0x92\n",
-  "  98:	2394      	movs	r3, #148	; 0x94\n",
-  "  9a:	2396      	movs	r3, #150	; 0x96\n",
-  "  9c:	2398      	movs	r3, #152	; 0x98\n",
-  "  9e:	239a      	movs	r3, #154	; 0x9a\n",
-  "  a0:	239c      	movs	r3, #156	; 0x9c\n",
-  "  a2:	239e      	movs	r3, #158	; 0x9e\n",
-  "  a4:	23a0      	movs	r3, #160	; 0xa0\n",
-  "  a6:	23a2      	movs	r3, #162	; 0xa2\n",
-  "  a8:	23a4      	movs	r3, #164	; 0xa4\n",
-  "  aa:	23a6      	movs	r3, #166	; 0xa6\n",
-  "  ac:	23a8      	movs	r3, #168	; 0xa8\n",
-  "  ae:	23aa      	movs	r3, #170	; 0xaa\n",
-  "  b0:	23ac      	movs	r3, #172	; 0xac\n",
-  "  b2:	23ae      	movs	r3, #174	; 0xae\n",
-  "  b4:	23b0      	movs	r3, #176	; 0xb0\n",
-  "  b6:	23b2      	movs	r3, #178	; 0xb2\n",
-  "  b8:	23b4      	movs	r3, #180	; 0xb4\n",
-  "  ba:	23b6      	movs	r3, #182	; 0xb6\n",
-  "  bc:	23b8      	movs	r3, #184	; 0xb8\n",
-  "  be:	23ba      	movs	r3, #186	; 0xba\n",
-  "  c0:	23bc      	movs	r3, #188	; 0xbc\n",
-  "  c2:	23be      	movs	r3, #190	; 0xbe\n",
-  "  c4:	23c0      	movs	r3, #192	; 0xc0\n",
-  "  c6:	23c2      	movs	r3, #194	; 0xc2\n",
-  "  c8:	23c4      	movs	r3, #196	; 0xc4\n",
-  "  ca:	23c6      	movs	r3, #198	; 0xc6\n",
-  "  cc:	23c8      	movs	r3, #200	; 0xc8\n",
-  "  ce:	23ca      	movs	r3, #202	; 0xca\n",
-  "  d0:	23cc      	movs	r3, #204	; 0xcc\n",
-  "  d2:	23ce      	movs	r3, #206	; 0xce\n",
-  "  d4:	23d0      	movs	r3, #208	; 0xd0\n",
-  "  d6:	23d2      	movs	r3, #210	; 0xd2\n",
-  "  d8:	23d4      	movs	r3, #212	; 0xd4\n",
-  "  da:	23d6      	movs	r3, #214	; 0xd6\n",
-  "  dc:	23d8      	movs	r3, #216	; 0xd8\n",
-  "  de:	23da      	movs	r3, #218	; 0xda\n",
-  "  e0:	23dc      	movs	r3, #220	; 0xdc\n",
-  "  e2:	23de      	movs	r3, #222	; 0xde\n",
-  "  e4:	23e0      	movs	r3, #224	; 0xe0\n",
-  "  e6:	23e2      	movs	r3, #226	; 0xe2\n",
-  "  e8:	23e4      	movs	r3, #228	; 0xe4\n",
-  "  ea:	23e6      	movs	r3, #230	; 0xe6\n",
-  "  ec:	23e8      	movs	r3, #232	; 0xe8\n",
-  "  ee:	23ea      	movs	r3, #234	; 0xea\n",
-  "  f0:	23ec      	movs	r3, #236	; 0xec\n",
-  "  f2:	23ee      	movs	r3, #238	; 0xee\n",
-  "  f4:	23f0      	movs	r3, #240	; 0xf0\n",
-  "  f6:	23f2      	movs	r3, #242	; 0xf2\n",
-  "  f8:	23f4      	movs	r3, #244	; 0xf4\n",
-  "  fa:	23f6      	movs	r3, #246	; 0xf6\n",
-  "  fc:	23f8      	movs	r3, #248	; 0xf8\n",
-  "  fe:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 100:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 102:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 104:	2300      	movs	r3, #0\n",
-  " 106:	2302      	movs	r3, #2\n",
-  " 108:	2304      	movs	r3, #4\n",
-  " 10a:	2306      	movs	r3, #6\n",
-  " 10c:	2308      	movs	r3, #8\n",
-  " 10e:	230a      	movs	r3, #10\n",
-  " 110:	230c      	movs	r3, #12\n",
-  " 112:	230e      	movs	r3, #14\n",
-  " 114:	2310      	movs	r3, #16\n",
-  " 116:	2312      	movs	r3, #18\n",
-  " 118:	2314      	movs	r3, #20\n",
-  " 11a:	2316      	movs	r3, #22\n",
-  " 11c:	2318      	movs	r3, #24\n",
-  " 11e:	231a      	movs	r3, #26\n",
-  " 120:	231c      	movs	r3, #28\n",
-  " 122:	231e      	movs	r3, #30\n",
-  " 124:	2320      	movs	r3, #32\n",
-  " 126:	2322      	movs	r3, #34	; 0x22\n",
-  " 128:	2324      	movs	r3, #36	; 0x24\n",
-  " 12a:	2326      	movs	r3, #38	; 0x26\n",
-  " 12c:	2328      	movs	r3, #40	; 0x28\n",
-  " 12e:	232a      	movs	r3, #42	; 0x2a\n",
-  " 130:	232c      	movs	r3, #44	; 0x2c\n",
-  " 132:	232e      	movs	r3, #46	; 0x2e\n",
-  " 134:	2330      	movs	r3, #48	; 0x30\n",
-  " 136:	2332      	movs	r3, #50	; 0x32\n",
-  " 138:	2334      	movs	r3, #52	; 0x34\n",
-  " 13a:	2336      	movs	r3, #54	; 0x36\n",
-  " 13c:	2338      	movs	r3, #56	; 0x38\n",
-  " 13e:	233a      	movs	r3, #58	; 0x3a\n",
-  " 140:	233c      	movs	r3, #60	; 0x3c\n",
-  " 142:	233e      	movs	r3, #62	; 0x3e\n",
-  " 144:	2340      	movs	r3, #64	; 0x40\n",
-  " 146:	2342      	movs	r3, #66	; 0x42\n",
-  " 148:	2344      	movs	r3, #68	; 0x44\n",
-  " 14a:	2346      	movs	r3, #70	; 0x46\n",
-  " 14c:	2348      	movs	r3, #72	; 0x48\n",
-  " 14e:	234a      	movs	r3, #74	; 0x4a\n",
-  " 150:	234c      	movs	r3, #76	; 0x4c\n",
-  " 152:	234e      	movs	r3, #78	; 0x4e\n",
-  " 154:	2350      	movs	r3, #80	; 0x50\n",
-  " 156:	2352      	movs	r3, #82	; 0x52\n",
-  " 158:	2354      	movs	r3, #84	; 0x54\n",
-  " 15a:	2356      	movs	r3, #86	; 0x56\n",
-  " 15c:	2358      	movs	r3, #88	; 0x58\n",
-  " 15e:	235a      	movs	r3, #90	; 0x5a\n",
-  " 160:	235c      	movs	r3, #92	; 0x5c\n",
-  " 162:	235e      	movs	r3, #94	; 0x5e\n",
-  " 164:	2360      	movs	r3, #96	; 0x60\n",
-  " 166:	2362      	movs	r3, #98	; 0x62\n",
-  " 168:	2364      	movs	r3, #100	; 0x64\n",
-  " 16a:	2366      	movs	r3, #102	; 0x66\n",
-  " 16c:	2368      	movs	r3, #104	; 0x68\n",
-  " 16e:	236a      	movs	r3, #106	; 0x6a\n",
-  " 170:	236c      	movs	r3, #108	; 0x6c\n",
-  " 172:	236e      	movs	r3, #110	; 0x6e\n",
-  " 174:	2370      	movs	r3, #112	; 0x70\n",
-  " 176:	2372      	movs	r3, #114	; 0x72\n",
-  " 178:	2374      	movs	r3, #116	; 0x74\n",
-  " 17a:	2376      	movs	r3, #118	; 0x76\n",
-  " 17c:	2378      	movs	r3, #120	; 0x78\n",
-  " 17e:	237a      	movs	r3, #122	; 0x7a\n",
-  " 180:	237c      	movs	r3, #124	; 0x7c\n",
-  " 182:	237e      	movs	r3, #126	; 0x7e\n",
-  " 184:	2380      	movs	r3, #128	; 0x80\n",
-  " 186:	2382      	movs	r3, #130	; 0x82\n",
-  " 188:	2384      	movs	r3, #132	; 0x84\n",
-  " 18a:	2386      	movs	r3, #134	; 0x86\n",
-  " 18c:	2388      	movs	r3, #136	; 0x88\n",
-  " 18e:	238a      	movs	r3, #138	; 0x8a\n",
-  " 190:	238c      	movs	r3, #140	; 0x8c\n",
-  " 192:	238e      	movs	r3, #142	; 0x8e\n",
-  " 194:	2390      	movs	r3, #144	; 0x90\n",
-  " 196:	2392      	movs	r3, #146	; 0x92\n",
-  " 198:	2394      	movs	r3, #148	; 0x94\n",
-  " 19a:	2396      	movs	r3, #150	; 0x96\n",
-  " 19c:	2398      	movs	r3, #152	; 0x98\n",
-  " 19e:	239a      	movs	r3, #154	; 0x9a\n",
-  " 1a0:	239c      	movs	r3, #156	; 0x9c\n",
-  " 1a2:	239e      	movs	r3, #158	; 0x9e\n",
-  " 1a4:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 1a6:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 1a8:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 1aa:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 1ac:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 1ae:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 1b0:	23ac      	movs	r3, #172	; 0xac\n",
-  " 1b2:	23ae      	movs	r3, #174	; 0xae\n",
-  " 1b4:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 1b6:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 1b8:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 1ba:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 1bc:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 1be:	23ba      	movs	r3, #186	; 0xba\n",
-  " 1c0:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 1c2:	23be      	movs	r3, #190	; 0xbe\n",
-  " 1c4:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 1c6:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 1c8:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 1ca:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 1cc:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 1ce:	23ca      	movs	r3, #202	; 0xca\n",
-  " 1d0:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 1d2:	23ce      	movs	r3, #206	; 0xce\n",
-  " 1d4:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 1d6:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 1d8:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 1da:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 1dc:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 1de:	23da      	movs	r3, #218	; 0xda\n",
-  " 1e0:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 1e2:	23de      	movs	r3, #222	; 0xde\n",
-  " 1e4:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 1e6:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 1e8:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 1ea:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 1ec:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 1ee:	23ea      	movs	r3, #234	; 0xea\n",
-  " 1f0:	23ec      	movs	r3, #236	; 0xec\n",
-  " 1f2:	23ee      	movs	r3, #238	; 0xee\n",
-  " 1f4:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 1f6:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 1f8:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 1fa:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 1fc:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 1fe:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 200:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 202:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 204:	2300      	movs	r3, #0\n",
-  " 206:	2302      	movs	r3, #2\n",
-  " 208:	2304      	movs	r3, #4\n",
-  " 20a:	2306      	movs	r3, #6\n",
-  " 20c:	2308      	movs	r3, #8\n",
-  " 20e:	230a      	movs	r3, #10\n",
-  " 210:	230c      	movs	r3, #12\n",
-  " 212:	230e      	movs	r3, #14\n",
-  " 214:	2310      	movs	r3, #16\n",
-  " 216:	2312      	movs	r3, #18\n",
-  " 218:	2314      	movs	r3, #20\n",
-  " 21a:	2316      	movs	r3, #22\n",
-  " 21c:	2318      	movs	r3, #24\n",
-  " 21e:	231a      	movs	r3, #26\n",
-  " 220:	231c      	movs	r3, #28\n",
-  " 222:	231e      	movs	r3, #30\n",
-  " 224:	2320      	movs	r3, #32\n",
-  " 226:	2322      	movs	r3, #34	; 0x22\n",
-  " 228:	2324      	movs	r3, #36	; 0x24\n",
-  " 22a:	2326      	movs	r3, #38	; 0x26\n",
-  " 22c:	2328      	movs	r3, #40	; 0x28\n",
-  " 22e:	232a      	movs	r3, #42	; 0x2a\n",
-  " 230:	232c      	movs	r3, #44	; 0x2c\n",
-  " 232:	232e      	movs	r3, #46	; 0x2e\n",
-  " 234:	2330      	movs	r3, #48	; 0x30\n",
-  " 236:	2332      	movs	r3, #50	; 0x32\n",
-  " 238:	2334      	movs	r3, #52	; 0x34\n",
-  " 23a:	2336      	movs	r3, #54	; 0x36\n",
-  " 23c:	2338      	movs	r3, #56	; 0x38\n",
-  " 23e:	233a      	movs	r3, #58	; 0x3a\n",
-  " 240:	233c      	movs	r3, #60	; 0x3c\n",
-  " 242:	233e      	movs	r3, #62	; 0x3e\n",
-  " 244:	2340      	movs	r3, #64	; 0x40\n",
-  " 246:	2342      	movs	r3, #66	; 0x42\n",
-  " 248:	2344      	movs	r3, #68	; 0x44\n",
-  " 24a:	2346      	movs	r3, #70	; 0x46\n",
-  " 24c:	2348      	movs	r3, #72	; 0x48\n",
-  " 24e:	234a      	movs	r3, #74	; 0x4a\n",
-  " 250:	234c      	movs	r3, #76	; 0x4c\n",
-  " 252:	234e      	movs	r3, #78	; 0x4e\n",
-  " 254:	2350      	movs	r3, #80	; 0x50\n",
-  " 256:	2352      	movs	r3, #82	; 0x52\n",
-  " 258:	2354      	movs	r3, #84	; 0x54\n",
-  " 25a:	2356      	movs	r3, #86	; 0x56\n",
-  " 25c:	2358      	movs	r3, #88	; 0x58\n",
-  " 25e:	235a      	movs	r3, #90	; 0x5a\n",
-  " 260:	235c      	movs	r3, #92	; 0x5c\n",
-  " 262:	235e      	movs	r3, #94	; 0x5e\n",
-  " 264:	2360      	movs	r3, #96	; 0x60\n",
-  " 266:	2362      	movs	r3, #98	; 0x62\n",
-  " 268:	2364      	movs	r3, #100	; 0x64\n",
-  " 26a:	2366      	movs	r3, #102	; 0x66\n",
-  " 26c:	2368      	movs	r3, #104	; 0x68\n",
-  " 26e:	236a      	movs	r3, #106	; 0x6a\n",
-  " 270:	236c      	movs	r3, #108	; 0x6c\n",
-  " 272:	236e      	movs	r3, #110	; 0x6e\n",
-  " 274:	2370      	movs	r3, #112	; 0x70\n",
-  " 276:	2372      	movs	r3, #114	; 0x72\n",
-  " 278:	2374      	movs	r3, #116	; 0x74\n",
-  " 27a:	2376      	movs	r3, #118	; 0x76\n",
-  " 27c:	2378      	movs	r3, #120	; 0x78\n",
-  " 27e:	237a      	movs	r3, #122	; 0x7a\n",
-  " 280:	237c      	movs	r3, #124	; 0x7c\n",
-  " 282:	237e      	movs	r3, #126	; 0x7e\n",
-  " 284:	2380      	movs	r3, #128	; 0x80\n",
-  " 286:	2382      	movs	r3, #130	; 0x82\n",
-  " 288:	2384      	movs	r3, #132	; 0x84\n",
-  " 28a:	2386      	movs	r3, #134	; 0x86\n",
-  " 28c:	2388      	movs	r3, #136	; 0x88\n",
-  " 28e:	238a      	movs	r3, #138	; 0x8a\n",
-  " 290:	238c      	movs	r3, #140	; 0x8c\n",
-  " 292:	238e      	movs	r3, #142	; 0x8e\n",
-  " 294:	2390      	movs	r3, #144	; 0x90\n",
-  " 296:	2392      	movs	r3, #146	; 0x92\n",
-  " 298:	2394      	movs	r3, #148	; 0x94\n",
-  " 29a:	2396      	movs	r3, #150	; 0x96\n",
-  " 29c:	2398      	movs	r3, #152	; 0x98\n",
-  " 29e:	239a      	movs	r3, #154	; 0x9a\n",
-  " 2a0:	239c      	movs	r3, #156	; 0x9c\n",
-  " 2a2:	239e      	movs	r3, #158	; 0x9e\n",
-  " 2a4:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 2a6:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 2a8:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 2aa:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 2ac:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 2ae:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 2b0:	23ac      	movs	r3, #172	; 0xac\n",
-  " 2b2:	23ae      	movs	r3, #174	; 0xae\n",
-  " 2b4:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 2b6:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 2b8:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 2ba:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 2bc:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 2be:	23ba      	movs	r3, #186	; 0xba\n",
-  " 2c0:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 2c2:	23be      	movs	r3, #190	; 0xbe\n",
-  " 2c4:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 2c6:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 2c8:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 2ca:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 2cc:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 2ce:	23ca      	movs	r3, #202	; 0xca\n",
-  " 2d0:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 2d2:	23ce      	movs	r3, #206	; 0xce\n",
-  " 2d4:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 2d6:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 2d8:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 2da:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 2dc:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 2de:	23da      	movs	r3, #218	; 0xda\n",
-  " 2e0:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 2e2:	23de      	movs	r3, #222	; 0xde\n",
-  " 2e4:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 2e6:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 2e8:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 2ea:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 2ec:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 2ee:	23ea      	movs	r3, #234	; 0xea\n",
-  " 2f0:	23ec      	movs	r3, #236	; 0xec\n",
-  " 2f2:	23ee      	movs	r3, #238	; 0xee\n",
-  " 2f4:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 2f6:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 2f8:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 2fa:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 2fc:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 2fe:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 300:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 302:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 304:	2300      	movs	r3, #0\n",
-  " 306:	2302      	movs	r3, #2\n",
-  " 308:	2304      	movs	r3, #4\n",
-  " 30a:	2306      	movs	r3, #6\n",
-  " 30c:	2308      	movs	r3, #8\n",
-  " 30e:	230a      	movs	r3, #10\n",
-  " 310:	230c      	movs	r3, #12\n",
-  " 312:	230e      	movs	r3, #14\n",
-  " 314:	2310      	movs	r3, #16\n",
-  " 316:	2312      	movs	r3, #18\n",
-  " 318:	2314      	movs	r3, #20\n",
-  " 31a:	2316      	movs	r3, #22\n",
-  " 31c:	2318      	movs	r3, #24\n",
-  " 31e:	231a      	movs	r3, #26\n",
-  " 320:	231c      	movs	r3, #28\n",
-  " 322:	231e      	movs	r3, #30\n",
-  " 324:	2320      	movs	r3, #32\n",
-  " 326:	2322      	movs	r3, #34	; 0x22\n",
-  " 328:	2324      	movs	r3, #36	; 0x24\n",
-  " 32a:	2326      	movs	r3, #38	; 0x26\n",
-  " 32c:	2328      	movs	r3, #40	; 0x28\n",
-  " 32e:	232a      	movs	r3, #42	; 0x2a\n",
-  " 330:	232c      	movs	r3, #44	; 0x2c\n",
-  " 332:	232e      	movs	r3, #46	; 0x2e\n",
-  " 334:	2330      	movs	r3, #48	; 0x30\n",
-  " 336:	2332      	movs	r3, #50	; 0x32\n",
-  " 338:	2334      	movs	r3, #52	; 0x34\n",
-  " 33a:	2336      	movs	r3, #54	; 0x36\n",
-  " 33c:	2338      	movs	r3, #56	; 0x38\n",
-  " 33e:	233a      	movs	r3, #58	; 0x3a\n",
-  " 340:	233c      	movs	r3, #60	; 0x3c\n",
-  " 342:	233e      	movs	r3, #62	; 0x3e\n",
-  " 344:	2340      	movs	r3, #64	; 0x40\n",
-  " 346:	2342      	movs	r3, #66	; 0x42\n",
-  " 348:	2344      	movs	r3, #68	; 0x44\n",
-  " 34a:	2346      	movs	r3, #70	; 0x46\n",
-  " 34c:	2348      	movs	r3, #72	; 0x48\n",
-  " 34e:	234a      	movs	r3, #74	; 0x4a\n",
-  " 350:	234c      	movs	r3, #76	; 0x4c\n",
-  " 352:	234e      	movs	r3, #78	; 0x4e\n",
-  " 354:	2350      	movs	r3, #80	; 0x50\n",
-  " 356:	2352      	movs	r3, #82	; 0x52\n",
-  " 358:	2354      	movs	r3, #84	; 0x54\n",
-  " 35a:	2356      	movs	r3, #86	; 0x56\n",
-  " 35c:	2358      	movs	r3, #88	; 0x58\n",
-  " 35e:	235a      	movs	r3, #90	; 0x5a\n",
-  " 360:	235c      	movs	r3, #92	; 0x5c\n",
-  " 362:	235e      	movs	r3, #94	; 0x5e\n",
-  " 364:	2360      	movs	r3, #96	; 0x60\n",
-  " 366:	2362      	movs	r3, #98	; 0x62\n",
-  " 368:	2364      	movs	r3, #100	; 0x64\n",
-  " 36a:	2366      	movs	r3, #102	; 0x66\n",
-  " 36c:	2368      	movs	r3, #104	; 0x68\n",
-  " 36e:	236a      	movs	r3, #106	; 0x6a\n",
-  " 370:	236c      	movs	r3, #108	; 0x6c\n",
-  " 372:	236e      	movs	r3, #110	; 0x6e\n",
-  " 374:	2370      	movs	r3, #112	; 0x70\n",
-  " 376:	2372      	movs	r3, #114	; 0x72\n",
-  " 378:	2374      	movs	r3, #116	; 0x74\n",
-  " 37a:	2376      	movs	r3, #118	; 0x76\n",
-  " 37c:	2378      	movs	r3, #120	; 0x78\n",
-  " 37e:	237a      	movs	r3, #122	; 0x7a\n",
-  " 380:	237c      	movs	r3, #124	; 0x7c\n",
-  " 382:	237e      	movs	r3, #126	; 0x7e\n",
-  " 384:	2380      	movs	r3, #128	; 0x80\n",
-  " 386:	2382      	movs	r3, #130	; 0x82\n",
-  " 388:	2384      	movs	r3, #132	; 0x84\n",
-  " 38a:	2386      	movs	r3, #134	; 0x86\n",
-  " 38c:	2388      	movs	r3, #136	; 0x88\n",
-  " 38e:	238a      	movs	r3, #138	; 0x8a\n",
-  " 390:	238c      	movs	r3, #140	; 0x8c\n",
-  " 392:	238e      	movs	r3, #142	; 0x8e\n",
-  " 394:	2390      	movs	r3, #144	; 0x90\n",
-  " 396:	2392      	movs	r3, #146	; 0x92\n",
-  " 398:	2394      	movs	r3, #148	; 0x94\n",
-  " 39a:	2396      	movs	r3, #150	; 0x96\n",
-  " 39c:	2398      	movs	r3, #152	; 0x98\n",
-  " 39e:	239a      	movs	r3, #154	; 0x9a\n",
-  " 3a0:	239c      	movs	r3, #156	; 0x9c\n",
-  " 3a2:	239e      	movs	r3, #158	; 0x9e\n",
-  " 3a4:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 3a6:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 3a8:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 3aa:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 3ac:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 3ae:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 3b0:	23ac      	movs	r3, #172	; 0xac\n",
-  " 3b2:	23ae      	movs	r3, #174	; 0xae\n",
-  " 3b4:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 3b6:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 3b8:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 3ba:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 3bc:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 3be:	23ba      	movs	r3, #186	; 0xba\n",
-  " 3c0:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 3c2:	23be      	movs	r3, #190	; 0xbe\n",
-  " 3c4:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 3c6:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 3c8:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 3ca:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 3cc:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 3ce:	23ca      	movs	r3, #202	; 0xca\n",
-  " 3d0:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 3d2:	23ce      	movs	r3, #206	; 0xce\n",
-  " 3d4:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 3d6:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 3d8:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 3da:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 3dc:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 3de:	23da      	movs	r3, #218	; 0xda\n",
-  " 3e0:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 3e2:	23de      	movs	r3, #222	; 0xde\n",
-  " 3e4:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 3e6:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 3e8:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 3ea:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 3ec:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 3ee:	23ea      	movs	r3, #234	; 0xea\n",
-  " 3f0:	23ec      	movs	r3, #236	; 0xec\n",
-  " 3f2:	23ee      	movs	r3, #238	; 0xee\n",
-  " 3f4:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 3f6:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 3f8:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 3fa:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 3fc:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 3fe:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 400:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 402:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 404:	2300      	movs	r3, #0\n",
-  " 406:	2302      	movs	r3, #2\n",
-  " 408:	2304      	movs	r3, #4\n",
-  " 40a:	2306      	movs	r3, #6\n",
-  " 40c:	2308      	movs	r3, #8\n",
-  " 40e:	230a      	movs	r3, #10\n",
-  " 410:	230c      	movs	r3, #12\n",
-  " 412:	230e      	movs	r3, #14\n",
-  " 414:	2310      	movs	r3, #16\n",
-  " 416:	2312      	movs	r3, #18\n",
-  " 418:	2314      	movs	r3, #20\n",
-  " 41a:	2316      	movs	r3, #22\n",
-  " 41c:	2318      	movs	r3, #24\n",
-  " 41e:	231a      	movs	r3, #26\n",
-  " 420:	231c      	movs	r3, #28\n",
-  " 422:	231e      	movs	r3, #30\n",
-  " 424:	2320      	movs	r3, #32\n",
-  " 426:	2322      	movs	r3, #34	; 0x22\n",
-  " 428:	2324      	movs	r3, #36	; 0x24\n",
-  " 42a:	2326      	movs	r3, #38	; 0x26\n",
-  " 42c:	2328      	movs	r3, #40	; 0x28\n",
-  " 42e:	232a      	movs	r3, #42	; 0x2a\n",
-  " 430:	232c      	movs	r3, #44	; 0x2c\n",
-  " 432:	232e      	movs	r3, #46	; 0x2e\n",
-  " 434:	2330      	movs	r3, #48	; 0x30\n",
-  " 436:	2332      	movs	r3, #50	; 0x32\n",
-  " 438:	2334      	movs	r3, #52	; 0x34\n",
-  " 43a:	2336      	movs	r3, #54	; 0x36\n",
-  " 43c:	2338      	movs	r3, #56	; 0x38\n",
-  " 43e:	233a      	movs	r3, #58	; 0x3a\n",
-  " 440:	233c      	movs	r3, #60	; 0x3c\n",
-  " 442:	233e      	movs	r3, #62	; 0x3e\n",
-  " 444:	2340      	movs	r3, #64	; 0x40\n",
-  " 446:	2342      	movs	r3, #66	; 0x42\n",
-  " 448:	2344      	movs	r3, #68	; 0x44\n",
-  " 44a:	2346      	movs	r3, #70	; 0x46\n",
-  " 44c:	2348      	movs	r3, #72	; 0x48\n",
-  " 44e:	234a      	movs	r3, #74	; 0x4a\n",
-  " 450:	234c      	movs	r3, #76	; 0x4c\n",
-  " 452:	234e      	movs	r3, #78	; 0x4e\n",
-  " 454:	2350      	movs	r3, #80	; 0x50\n",
-  " 456:	2352      	movs	r3, #82	; 0x52\n",
-  " 458:	2354      	movs	r3, #84	; 0x54\n",
-  " 45a:	2356      	movs	r3, #86	; 0x56\n",
-  " 45c:	2358      	movs	r3, #88	; 0x58\n",
-  " 45e:	235a      	movs	r3, #90	; 0x5a\n",
-  " 460:	235c      	movs	r3, #92	; 0x5c\n",
-  " 462:	235e      	movs	r3, #94	; 0x5e\n",
-  " 464:	2360      	movs	r3, #96	; 0x60\n",
-  " 466:	2362      	movs	r3, #98	; 0x62\n",
-  " 468:	2364      	movs	r3, #100	; 0x64\n",
-  " 46a:	2366      	movs	r3, #102	; 0x66\n",
-  " 46c:	2368      	movs	r3, #104	; 0x68\n",
-  " 46e:	236a      	movs	r3, #106	; 0x6a\n",
-  " 470:	236c      	movs	r3, #108	; 0x6c\n",
-  " 472:	236e      	movs	r3, #110	; 0x6e\n",
-  " 474:	2370      	movs	r3, #112	; 0x70\n",
-  " 476:	2372      	movs	r3, #114	; 0x72\n",
-  " 478:	2374      	movs	r3, #116	; 0x74\n",
-  " 47a:	2376      	movs	r3, #118	; 0x76\n",
-  " 47c:	2378      	movs	r3, #120	; 0x78\n",
-  " 47e:	237a      	movs	r3, #122	; 0x7a\n",
-  " 480:	237c      	movs	r3, #124	; 0x7c\n",
-  " 482:	237e      	movs	r3, #126	; 0x7e\n",
-  " 484:	2380      	movs	r3, #128	; 0x80\n",
-  " 486:	2382      	movs	r3, #130	; 0x82\n",
-  " 488:	2384      	movs	r3, #132	; 0x84\n",
-  " 48a:	2386      	movs	r3, #134	; 0x86\n",
-  " 48c:	2388      	movs	r3, #136	; 0x88\n",
-  " 48e:	238a      	movs	r3, #138	; 0x8a\n",
-  " 490:	238c      	movs	r3, #140	; 0x8c\n",
-  " 492:	238e      	movs	r3, #142	; 0x8e\n",
-  " 494:	2390      	movs	r3, #144	; 0x90\n",
-  " 496:	2392      	movs	r3, #146	; 0x92\n",
-  " 498:	2394      	movs	r3, #148	; 0x94\n",
-  " 49a:	2396      	movs	r3, #150	; 0x96\n",
-  " 49c:	2398      	movs	r3, #152	; 0x98\n",
-  " 49e:	239a      	movs	r3, #154	; 0x9a\n",
-  " 4a0:	239c      	movs	r3, #156	; 0x9c\n",
-  " 4a2:	239e      	movs	r3, #158	; 0x9e\n",
-  " 4a4:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 4a6:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 4a8:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 4aa:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 4ac:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 4ae:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 4b0:	23ac      	movs	r3, #172	; 0xac\n",
-  " 4b2:	23ae      	movs	r3, #174	; 0xae\n",
-  " 4b4:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 4b6:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 4b8:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 4ba:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 4bc:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 4be:	23ba      	movs	r3, #186	; 0xba\n",
-  " 4c0:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 4c2:	23be      	movs	r3, #190	; 0xbe\n",
-  " 4c4:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 4c6:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 4c8:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 4ca:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 4cc:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 4ce:	23ca      	movs	r3, #202	; 0xca\n",
-  " 4d0:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 4d2:	23ce      	movs	r3, #206	; 0xce\n",
-  " 4d4:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 4d6:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 4d8:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 4da:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 4dc:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 4de:	23da      	movs	r3, #218	; 0xda\n",
-  " 4e0:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 4e2:	23de      	movs	r3, #222	; 0xde\n",
-  " 4e4:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 4e6:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 4e8:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 4ea:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 4ec:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 4ee:	23ea      	movs	r3, #234	; 0xea\n",
-  " 4f0:	23ec      	movs	r3, #236	; 0xec\n",
-  " 4f2:	23ee      	movs	r3, #238	; 0xee\n",
-  " 4f4:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 4f6:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 4f8:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 4fa:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 4fc:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 4fe:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 500:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 502:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 504:	2300      	movs	r3, #0\n",
-  " 506:	2302      	movs	r3, #2\n",
-  " 508:	2304      	movs	r3, #4\n",
-  " 50a:	2306      	movs	r3, #6\n",
-  " 50c:	2308      	movs	r3, #8\n",
-  " 50e:	230a      	movs	r3, #10\n",
-  " 510:	230c      	movs	r3, #12\n",
-  " 512:	230e      	movs	r3, #14\n",
-  " 514:	2310      	movs	r3, #16\n",
-  " 516:	2312      	movs	r3, #18\n",
-  " 518:	2314      	movs	r3, #20\n",
-  " 51a:	2316      	movs	r3, #22\n",
-  " 51c:	2318      	movs	r3, #24\n",
-  " 51e:	231a      	movs	r3, #26\n",
-  " 520:	231c      	movs	r3, #28\n",
-  " 522:	231e      	movs	r3, #30\n",
-  " 524:	2320      	movs	r3, #32\n",
-  " 526:	2322      	movs	r3, #34	; 0x22\n",
-  " 528:	2324      	movs	r3, #36	; 0x24\n",
-  " 52a:	2326      	movs	r3, #38	; 0x26\n",
-  " 52c:	2328      	movs	r3, #40	; 0x28\n",
-  " 52e:	232a      	movs	r3, #42	; 0x2a\n",
-  " 530:	232c      	movs	r3, #44	; 0x2c\n",
-  " 532:	232e      	movs	r3, #46	; 0x2e\n",
-  " 534:	2330      	movs	r3, #48	; 0x30\n",
-  " 536:	2332      	movs	r3, #50	; 0x32\n",
-  " 538:	2334      	movs	r3, #52	; 0x34\n",
-  " 53a:	2336      	movs	r3, #54	; 0x36\n",
-  " 53c:	2338      	movs	r3, #56	; 0x38\n",
-  " 53e:	233a      	movs	r3, #58	; 0x3a\n",
-  " 540:	233c      	movs	r3, #60	; 0x3c\n",
-  " 542:	233e      	movs	r3, #62	; 0x3e\n",
-  " 544:	2340      	movs	r3, #64	; 0x40\n",
-  " 546:	2342      	movs	r3, #66	; 0x42\n",
-  " 548:	2344      	movs	r3, #68	; 0x44\n",
-  " 54a:	2346      	movs	r3, #70	; 0x46\n",
-  " 54c:	2348      	movs	r3, #72	; 0x48\n",
-  " 54e:	234a      	movs	r3, #74	; 0x4a\n",
-  " 550:	234c      	movs	r3, #76	; 0x4c\n",
-  " 552:	234e      	movs	r3, #78	; 0x4e\n",
-  " 554:	2350      	movs	r3, #80	; 0x50\n",
-  " 556:	2352      	movs	r3, #82	; 0x52\n",
-  " 558:	2354      	movs	r3, #84	; 0x54\n",
-  " 55a:	2356      	movs	r3, #86	; 0x56\n",
-  " 55c:	2358      	movs	r3, #88	; 0x58\n",
-  " 55e:	235a      	movs	r3, #90	; 0x5a\n",
-  " 560:	235c      	movs	r3, #92	; 0x5c\n",
-  " 562:	235e      	movs	r3, #94	; 0x5e\n",
-  " 564:	2360      	movs	r3, #96	; 0x60\n",
-  " 566:	2362      	movs	r3, #98	; 0x62\n",
-  " 568:	2364      	movs	r3, #100	; 0x64\n",
-  " 56a:	2366      	movs	r3, #102	; 0x66\n",
-  " 56c:	2368      	movs	r3, #104	; 0x68\n",
-  " 56e:	236a      	movs	r3, #106	; 0x6a\n",
-  " 570:	236c      	movs	r3, #108	; 0x6c\n",
-  " 572:	236e      	movs	r3, #110	; 0x6e\n",
-  " 574:	2370      	movs	r3, #112	; 0x70\n",
-  " 576:	2372      	movs	r3, #114	; 0x72\n",
-  " 578:	2374      	movs	r3, #116	; 0x74\n",
-  " 57a:	2376      	movs	r3, #118	; 0x76\n",
-  " 57c:	2378      	movs	r3, #120	; 0x78\n",
-  " 57e:	237a      	movs	r3, #122	; 0x7a\n",
-  " 580:	237c      	movs	r3, #124	; 0x7c\n",
-  " 582:	237e      	movs	r3, #126	; 0x7e\n",
-  " 584:	2380      	movs	r3, #128	; 0x80\n",
-  " 586:	2382      	movs	r3, #130	; 0x82\n",
-  " 588:	2384      	movs	r3, #132	; 0x84\n",
-  " 58a:	2386      	movs	r3, #134	; 0x86\n",
-  " 58c:	2388      	movs	r3, #136	; 0x88\n",
-  " 58e:	238a      	movs	r3, #138	; 0x8a\n",
-  " 590:	238c      	movs	r3, #140	; 0x8c\n",
-  " 592:	238e      	movs	r3, #142	; 0x8e\n",
-  " 594:	2390      	movs	r3, #144	; 0x90\n",
-  " 596:	2392      	movs	r3, #146	; 0x92\n",
-  " 598:	2394      	movs	r3, #148	; 0x94\n",
-  " 59a:	2396      	movs	r3, #150	; 0x96\n",
-  " 59c:	2398      	movs	r3, #152	; 0x98\n",
-  " 59e:	239a      	movs	r3, #154	; 0x9a\n",
-  " 5a0:	239c      	movs	r3, #156	; 0x9c\n",
-  " 5a2:	239e      	movs	r3, #158	; 0x9e\n",
-  " 5a4:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 5a6:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 5a8:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 5aa:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 5ac:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 5ae:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 5b0:	23ac      	movs	r3, #172	; 0xac\n",
-  " 5b2:	23ae      	movs	r3, #174	; 0xae\n",
-  " 5b4:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 5b6:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 5b8:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 5ba:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 5bc:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 5be:	23ba      	movs	r3, #186	; 0xba\n",
-  " 5c0:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 5c2:	23be      	movs	r3, #190	; 0xbe\n",
-  " 5c4:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 5c6:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 5c8:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 5ca:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 5cc:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 5ce:	23ca      	movs	r3, #202	; 0xca\n",
-  " 5d0:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 5d2:	23ce      	movs	r3, #206	; 0xce\n",
-  " 5d4:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 5d6:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 5d8:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 5da:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 5dc:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 5de:	23da      	movs	r3, #218	; 0xda\n",
-  " 5e0:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 5e2:	23de      	movs	r3, #222	; 0xde\n",
-  " 5e4:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 5e6:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 5e8:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 5ea:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 5ec:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 5ee:	23ea      	movs	r3, #234	; 0xea\n",
-  " 5f0:	23ec      	movs	r3, #236	; 0xec\n",
-  " 5f2:	23ee      	movs	r3, #238	; 0xee\n",
-  " 5f4:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 5f6:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 5f8:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 5fa:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 5fc:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 5fe:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 600:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 602:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 604:	2300      	movs	r3, #0\n",
-  " 606:	2302      	movs	r3, #2\n",
-  " 608:	2304      	movs	r3, #4\n",
-  " 60a:	2306      	movs	r3, #6\n",
-  " 60c:	2308      	movs	r3, #8\n",
-  " 60e:	230a      	movs	r3, #10\n",
-  " 610:	230c      	movs	r3, #12\n",
-  " 612:	230e      	movs	r3, #14\n",
-  " 614:	2310      	movs	r3, #16\n",
-  " 616:	2312      	movs	r3, #18\n",
-  " 618:	2314      	movs	r3, #20\n",
-  " 61a:	2316      	movs	r3, #22\n",
-  " 61c:	2318      	movs	r3, #24\n",
-  " 61e:	231a      	movs	r3, #26\n",
-  " 620:	231c      	movs	r3, #28\n",
-  " 622:	231e      	movs	r3, #30\n",
-  " 624:	2320      	movs	r3, #32\n",
-  " 626:	2322      	movs	r3, #34	; 0x22\n",
-  " 628:	2324      	movs	r3, #36	; 0x24\n",
-  " 62a:	2326      	movs	r3, #38	; 0x26\n",
-  " 62c:	2328      	movs	r3, #40	; 0x28\n",
-  " 62e:	232a      	movs	r3, #42	; 0x2a\n",
-  " 630:	232c      	movs	r3, #44	; 0x2c\n",
-  " 632:	232e      	movs	r3, #46	; 0x2e\n",
-  " 634:	2330      	movs	r3, #48	; 0x30\n",
-  " 636:	2332      	movs	r3, #50	; 0x32\n",
-  " 638:	2334      	movs	r3, #52	; 0x34\n",
-  " 63a:	2336      	movs	r3, #54	; 0x36\n",
-  " 63c:	2338      	movs	r3, #56	; 0x38\n",
-  " 63e:	233a      	movs	r3, #58	; 0x3a\n",
-  " 640:	233c      	movs	r3, #60	; 0x3c\n",
-  " 642:	233e      	movs	r3, #62	; 0x3e\n",
-  " 644:	2340      	movs	r3, #64	; 0x40\n",
-  " 646:	2342      	movs	r3, #66	; 0x42\n",
-  " 648:	2344      	movs	r3, #68	; 0x44\n",
-  " 64a:	2346      	movs	r3, #70	; 0x46\n",
-  " 64c:	2348      	movs	r3, #72	; 0x48\n",
-  " 64e:	234a      	movs	r3, #74	; 0x4a\n",
-  " 650:	234c      	movs	r3, #76	; 0x4c\n",
-  " 652:	234e      	movs	r3, #78	; 0x4e\n",
-  " 654:	2350      	movs	r3, #80	; 0x50\n",
-  " 656:	2352      	movs	r3, #82	; 0x52\n",
-  " 658:	2354      	movs	r3, #84	; 0x54\n",
-  " 65a:	2356      	movs	r3, #86	; 0x56\n",
-  " 65c:	2358      	movs	r3, #88	; 0x58\n",
-  " 65e:	235a      	movs	r3, #90	; 0x5a\n",
-  " 660:	235c      	movs	r3, #92	; 0x5c\n",
-  " 662:	235e      	movs	r3, #94	; 0x5e\n",
-  " 664:	2360      	movs	r3, #96	; 0x60\n",
-  " 666:	2362      	movs	r3, #98	; 0x62\n",
-  " 668:	2364      	movs	r3, #100	; 0x64\n",
-  " 66a:	2366      	movs	r3, #102	; 0x66\n",
-  " 66c:	2368      	movs	r3, #104	; 0x68\n",
-  " 66e:	236a      	movs	r3, #106	; 0x6a\n",
-  " 670:	236c      	movs	r3, #108	; 0x6c\n",
-  " 672:	236e      	movs	r3, #110	; 0x6e\n",
-  " 674:	2370      	movs	r3, #112	; 0x70\n",
-  " 676:	2372      	movs	r3, #114	; 0x72\n",
-  " 678:	2374      	movs	r3, #116	; 0x74\n",
-  " 67a:	2376      	movs	r3, #118	; 0x76\n",
-  " 67c:	2378      	movs	r3, #120	; 0x78\n",
-  " 67e:	237a      	movs	r3, #122	; 0x7a\n",
-  " 680:	237c      	movs	r3, #124	; 0x7c\n",
-  " 682:	237e      	movs	r3, #126	; 0x7e\n",
-  " 684:	2380      	movs	r3, #128	; 0x80\n",
-  " 686:	2382      	movs	r3, #130	; 0x82\n",
-  " 688:	2384      	movs	r3, #132	; 0x84\n",
-  " 68a:	2386      	movs	r3, #134	; 0x86\n",
-  " 68c:	2388      	movs	r3, #136	; 0x88\n",
-  " 68e:	238a      	movs	r3, #138	; 0x8a\n",
-  " 690:	238c      	movs	r3, #140	; 0x8c\n",
-  " 692:	238e      	movs	r3, #142	; 0x8e\n",
-  " 694:	2390      	movs	r3, #144	; 0x90\n",
-  " 696:	2392      	movs	r3, #146	; 0x92\n",
-  " 698:	2394      	movs	r3, #148	; 0x94\n",
-  " 69a:	2396      	movs	r3, #150	; 0x96\n",
-  " 69c:	2398      	movs	r3, #152	; 0x98\n",
-  " 69e:	239a      	movs	r3, #154	; 0x9a\n",
-  " 6a0:	239c      	movs	r3, #156	; 0x9c\n",
-  " 6a2:	239e      	movs	r3, #158	; 0x9e\n",
-  " 6a4:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 6a6:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 6a8:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 6aa:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 6ac:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 6ae:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 6b0:	23ac      	movs	r3, #172	; 0xac\n",
-  " 6b2:	23ae      	movs	r3, #174	; 0xae\n",
-  " 6b4:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 6b6:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 6b8:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 6ba:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 6bc:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 6be:	23ba      	movs	r3, #186	; 0xba\n",
-  " 6c0:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 6c2:	23be      	movs	r3, #190	; 0xbe\n",
-  " 6c4:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 6c6:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 6c8:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 6ca:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 6cc:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 6ce:	23ca      	movs	r3, #202	; 0xca\n",
-  " 6d0:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 6d2:	23ce      	movs	r3, #206	; 0xce\n",
-  " 6d4:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 6d6:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 6d8:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 6da:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 6dc:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 6de:	23da      	movs	r3, #218	; 0xda\n",
-  " 6e0:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 6e2:	23de      	movs	r3, #222	; 0xde\n",
-  " 6e4:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 6e6:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 6e8:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 6ea:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 6ec:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 6ee:	23ea      	movs	r3, #234	; 0xea\n",
-  " 6f0:	23ec      	movs	r3, #236	; 0xec\n",
-  " 6f2:	23ee      	movs	r3, #238	; 0xee\n",
-  " 6f4:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 6f6:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 6f8:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 6fa:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 6fc:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 6fe:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 700:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 702:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 704:	2300      	movs	r3, #0\n",
-  " 706:	2302      	movs	r3, #2\n",
-  " 708:	2304      	movs	r3, #4\n",
-  " 70a:	2306      	movs	r3, #6\n",
-  " 70c:	2308      	movs	r3, #8\n",
-  " 70e:	230a      	movs	r3, #10\n",
-  " 710:	230c      	movs	r3, #12\n",
-  " 712:	230e      	movs	r3, #14\n",
-  " 714:	2310      	movs	r3, #16\n",
-  " 716:	2312      	movs	r3, #18\n",
-  " 718:	2314      	movs	r3, #20\n",
-  " 71a:	2316      	movs	r3, #22\n",
-  " 71c:	2318      	movs	r3, #24\n",
-  " 71e:	231a      	movs	r3, #26\n",
-  " 720:	231c      	movs	r3, #28\n",
-  " 722:	231e      	movs	r3, #30\n",
-  " 724:	2320      	movs	r3, #32\n",
-  " 726:	2322      	movs	r3, #34	; 0x22\n",
-  " 728:	2324      	movs	r3, #36	; 0x24\n",
-  " 72a:	2326      	movs	r3, #38	; 0x26\n",
-  " 72c:	2328      	movs	r3, #40	; 0x28\n",
-  " 72e:	232a      	movs	r3, #42	; 0x2a\n",
-  " 730:	232c      	movs	r3, #44	; 0x2c\n",
-  " 732:	232e      	movs	r3, #46	; 0x2e\n",
-  " 734:	2330      	movs	r3, #48	; 0x30\n",
-  " 736:	2332      	movs	r3, #50	; 0x32\n",
-  " 738:	2334      	movs	r3, #52	; 0x34\n",
-  " 73a:	2336      	movs	r3, #54	; 0x36\n",
-  " 73c:	2338      	movs	r3, #56	; 0x38\n",
-  " 73e:	233a      	movs	r3, #58	; 0x3a\n",
-  " 740:	233c      	movs	r3, #60	; 0x3c\n",
-  " 742:	233e      	movs	r3, #62	; 0x3e\n",
-  " 744:	2340      	movs	r3, #64	; 0x40\n",
-  " 746:	2342      	movs	r3, #66	; 0x42\n",
-  " 748:	2344      	movs	r3, #68	; 0x44\n",
-  " 74a:	2346      	movs	r3, #70	; 0x46\n",
-  " 74c:	2348      	movs	r3, #72	; 0x48\n",
-  " 74e:	234a      	movs	r3, #74	; 0x4a\n",
-  " 750:	234c      	movs	r3, #76	; 0x4c\n",
-  " 752:	234e      	movs	r3, #78	; 0x4e\n",
-  " 754:	2350      	movs	r3, #80	; 0x50\n",
-  " 756:	2352      	movs	r3, #82	; 0x52\n",
-  " 758:	2354      	movs	r3, #84	; 0x54\n",
-  " 75a:	2356      	movs	r3, #86	; 0x56\n",
-  " 75c:	2358      	movs	r3, #88	; 0x58\n",
-  " 75e:	235a      	movs	r3, #90	; 0x5a\n",
-  " 760:	235c      	movs	r3, #92	; 0x5c\n",
-  " 762:	235e      	movs	r3, #94	; 0x5e\n",
-  " 764:	2360      	movs	r3, #96	; 0x60\n",
-  " 766:	2362      	movs	r3, #98	; 0x62\n",
-  " 768:	2364      	movs	r3, #100	; 0x64\n",
-  " 76a:	2366      	movs	r3, #102	; 0x66\n",
-  " 76c:	2368      	movs	r3, #104	; 0x68\n",
-  " 76e:	236a      	movs	r3, #106	; 0x6a\n",
-  " 770:	236c      	movs	r3, #108	; 0x6c\n",
-  " 772:	236e      	movs	r3, #110	; 0x6e\n",
-  " 774:	2370      	movs	r3, #112	; 0x70\n",
-  " 776:	2372      	movs	r3, #114	; 0x72\n",
-  " 778:	2374      	movs	r3, #116	; 0x74\n",
-  " 77a:	2376      	movs	r3, #118	; 0x76\n",
-  " 77c:	2378      	movs	r3, #120	; 0x78\n",
-  " 77e:	237a      	movs	r3, #122	; 0x7a\n",
-  " 780:	237c      	movs	r3, #124	; 0x7c\n",
-  " 782:	237e      	movs	r3, #126	; 0x7e\n",
-  " 784:	2380      	movs	r3, #128	; 0x80\n",
-  " 786:	2382      	movs	r3, #130	; 0x82\n",
-  " 788:	2384      	movs	r3, #132	; 0x84\n",
-  " 78a:	2386      	movs	r3, #134	; 0x86\n",
-  " 78c:	2388      	movs	r3, #136	; 0x88\n",
-  " 78e:	238a      	movs	r3, #138	; 0x8a\n",
-  " 790:	238c      	movs	r3, #140	; 0x8c\n",
-  " 792:	238e      	movs	r3, #142	; 0x8e\n",
-  " 794:	2390      	movs	r3, #144	; 0x90\n",
-  " 796:	2392      	movs	r3, #146	; 0x92\n",
-  " 798:	2394      	movs	r3, #148	; 0x94\n",
-  " 79a:	2396      	movs	r3, #150	; 0x96\n",
-  " 79c:	2398      	movs	r3, #152	; 0x98\n",
-  " 79e:	239a      	movs	r3, #154	; 0x9a\n",
-  " 7a0:	239c      	movs	r3, #156	; 0x9c\n",
-  " 7a2:	239e      	movs	r3, #158	; 0x9e\n",
-  " 7a4:	23a0      	movs	r3, #160	; 0xa0\n",
-  " 7a6:	23a2      	movs	r3, #162	; 0xa2\n",
-  " 7a8:	23a4      	movs	r3, #164	; 0xa4\n",
-  " 7aa:	23a6      	movs	r3, #166	; 0xa6\n",
-  " 7ac:	23a8      	movs	r3, #168	; 0xa8\n",
-  " 7ae:	23aa      	movs	r3, #170	; 0xaa\n",
-  " 7b0:	23ac      	movs	r3, #172	; 0xac\n",
-  " 7b2:	23ae      	movs	r3, #174	; 0xae\n",
-  " 7b4:	23b0      	movs	r3, #176	; 0xb0\n",
-  " 7b6:	23b2      	movs	r3, #178	; 0xb2\n",
-  " 7b8:	23b4      	movs	r3, #180	; 0xb4\n",
-  " 7ba:	23b6      	movs	r3, #182	; 0xb6\n",
-  " 7bc:	23b8      	movs	r3, #184	; 0xb8\n",
-  " 7be:	23ba      	movs	r3, #186	; 0xba\n",
-  " 7c0:	23bc      	movs	r3, #188	; 0xbc\n",
-  " 7c2:	23be      	movs	r3, #190	; 0xbe\n",
-  " 7c4:	23c0      	movs	r3, #192	; 0xc0\n",
-  " 7c6:	23c2      	movs	r3, #194	; 0xc2\n",
-  " 7c8:	23c4      	movs	r3, #196	; 0xc4\n",
-  " 7ca:	23c6      	movs	r3, #198	; 0xc6\n",
-  " 7cc:	23c8      	movs	r3, #200	; 0xc8\n",
-  " 7ce:	23ca      	movs	r3, #202	; 0xca\n",
-  " 7d0:	23cc      	movs	r3, #204	; 0xcc\n",
-  " 7d2:	23ce      	movs	r3, #206	; 0xce\n",
-  " 7d4:	23d0      	movs	r3, #208	; 0xd0\n",
-  " 7d6:	23d2      	movs	r3, #210	; 0xd2\n",
-  " 7d8:	23d4      	movs	r3, #212	; 0xd4\n",
-  " 7da:	23d6      	movs	r3, #214	; 0xd6\n",
-  " 7dc:	23d8      	movs	r3, #216	; 0xd8\n",
-  " 7de:	23da      	movs	r3, #218	; 0xda\n",
-  " 7e0:	23dc      	movs	r3, #220	; 0xdc\n",
-  " 7e2:	23de      	movs	r3, #222	; 0xde\n",
-  " 7e4:	23e0      	movs	r3, #224	; 0xe0\n",
-  " 7e6:	23e2      	movs	r3, #226	; 0xe2\n",
-  " 7e8:	23e4      	movs	r3, #228	; 0xe4\n",
-  " 7ea:	23e6      	movs	r3, #230	; 0xe6\n",
-  " 7ec:	23e8      	movs	r3, #232	; 0xe8\n",
-  " 7ee:	23ea      	movs	r3, #234	; 0xea\n",
-  " 7f0:	23ec      	movs	r3, #236	; 0xec\n",
-  " 7f2:	23ee      	movs	r3, #238	; 0xee\n",
-  " 7f4:	23f0      	movs	r3, #240	; 0xf0\n",
-  " 7f6:	23f2      	movs	r3, #242	; 0xf2\n",
-  " 7f8:	23f4      	movs	r3, #244	; 0xf4\n",
-  " 7fa:	23f6      	movs	r3, #246	; 0xf6\n",
-  " 7fc:	23f8      	movs	r3, #248	; 0xf8\n",
-  " 7fe:	23fa      	movs	r3, #250	; 0xfa\n",
-  " 800:	23fc      	movs	r3, #252	; 0xfc\n",
-  " 802:	23fe      	movs	r3, #254	; 0xfe\n",
-  " 804:	2300      	movs	r3, #0\n",
-  " 806:	f7ff bbfd 	b.w	4 <MixedBranch32+0x4>\n",
-  " 80a:	4611      	mov	r1, r2\n",
-  nullptr
-};
-const char* const ShiftsResults[] = {
-  "   0:	0148      	lsls	r0, r1, #5\n",
-  "   2:	0948      	lsrs	r0, r1, #5\n",
-  "   4:	1148      	asrs	r0, r1, #5\n",
-  "   6:	4088      	lsls	r0, r1\n",
-  "   8:	40c8      	lsrs	r0, r1\n",
-  "   a:	4108      	asrs	r0, r1\n",
-  "   c:	41c8      	rors	r0, r1\n",
-  "   e:	0148      	lsls	r0, r1, #5\n",
-  "  10:	0948      	lsrs	r0, r1, #5\n",
-  "  12:	1148      	asrs	r0, r1, #5\n",
-  "  14:	4088      	lsls	r0, r1\n",
-  "  16:	40c8      	lsrs	r0, r1\n",
-  "  18:	4108      	asrs	r0, r1\n",
-  "  1a:	41c8      	rors	r0, r1\n",
-  "  1c:	ea4f 1041 	mov.w	r0, r1, lsl #5\n",
-  "  20:	ea4f 1051 	mov.w	r0, r1, lsr #5\n",
-  "  24:	ea4f 1061 	mov.w	r0, r1, asr #5\n",
-  "  28:	fa00 f001 	lsl.w	r0, r0, r1\n",
-  "  2c:	fa20 f001 	lsr.w	r0, r0, r1\n",
-  "  30:	fa40 f001 	asr.w	r0, r0, r1\n",
-  "  34:	fa60 f001 	ror.w	r0, r0, r1\n",
-  "  38:	ea4f 1071 	mov.w	r0, r1, ror #5\n",
-  "  3c:	ea5f 1071 	movs.w	r0, r1, ror #5\n",
-  "  40:	ea4f 1071 	mov.w	r0, r1, ror #5\n",
-  "  44:	ea4f 1841 	mov.w	r8, r1, lsl #5\n",
-  "  48:	ea4f 1058 	mov.w	r0, r8, lsr #5\n",
-  "  4c:	ea4f 1861 	mov.w	r8, r1, asr #5\n",
-  "  50:	ea4f 1078 	mov.w	r0, r8, ror #5\n",
-  "  54:	fa01 f002 	lsl.w	r0, r1, r2\n",
-  "  58:	fa21 f002 	lsr.w	r0, r1, r2\n",
-  "  5c:	fa41 f002 	asr.w	r0, r1, r2\n",
-  "  60:	fa61 f002 	ror.w	r0, r1, r2\n",
-  "  64:	fa01 f802 	lsl.w	r8, r1, r2\n",
-  "  68:	fa28 f002 	lsr.w	r0, r8, r2\n",
-  "  6c:	fa41 f008 	asr.w	r0, r1, r8\n",
-  "  70:	ea5f 1841 	movs.w	r8, r1, lsl #5\n",
-  "  74:	ea5f 1058 	movs.w	r0, r8, lsr #5\n",
-  "  78:	ea5f 1861 	movs.w	r8, r1, asr #5\n",
-  "  7c:	ea5f 1078 	movs.w	r0, r8, ror #5\n",
-  "  80:	fa11 f002 	lsls.w	r0, r1, r2\n",
-  "  84:	fa31 f002 	lsrs.w	r0, r1, r2\n",
-  "  88:	fa51 f002 	asrs.w	r0, r1, r2\n",
-  "  8c:	fa71 f002 	rors.w	r0, r1, r2\n",
-  "  90:	fa11 f802 	lsls.w	r8, r1, r2\n",
-  "  94:	fa38 f002 	lsrs.w	r0, r8, r2\n",
-  "  98:	fa51 f008 	asrs.w	r0, r1, r8\n",
-  nullptr
-};
-const char* const LoadStoreRegOffsetResults[] = {
-  "   0:	5888      	ldr	r0, [r1, r2]\n",
-  "   2:	5088      	str	r0, [r1, r2]\n",
-  "   4:	f851 0012 	ldr.w	r0, [r1, r2, lsl #1]\n",
-  "   8:	f841 0012 	str.w	r0, [r1, r2, lsl #1]\n",
-  "   c:	f851 0032 	ldr.w	r0, [r1, r2, lsl #3]\n",
-  "  10:	f841 0032 	str.w	r0, [r1, r2, lsl #3]\n",
-  "  14:	f851 8002 	ldr.w	r8, [r1, r2]\n",
-  "  18:	f841 8002 	str.w	r8, [r1, r2]\n",
-  "  1c:	f858 1002 	ldr.w	r1, [r8, r2]\n",
-  "  20:	f848 2002 	str.w	r2, [r8, r2]\n",
-  "  24:	f851 0008 	ldr.w	r0, [r1, r8]\n",
-  "  28:	f841 0008 	str.w	r0, [r1, r8]\n",
-  nullptr
-};
-const char* const LoadStoreLimitsResults[] = {
-  "   0:   6fe0            ldr     r0, [r4, #124]  ; 0x7c\n",
-  "   2:   f8d4 0080       ldr.w   r0, [r4, #128]  ; 0x80\n",
-  "   6:   7fe0            ldrb    r0, [r4, #31]\n",
-  "   8:   f894 0020       ldrb.w  r0, [r4, #32]\n",
-  "   c:   8fe0            ldrh    r0, [r4, #62]   ; 0x3e\n",
-  "   e:   f8b4 0040       ldrh.w  r0, [r4, #64]   ; 0x40\n",
-  "  12:   f994 001f       ldrsb.w r0, [r4, #31]\n",
-  "  16:   f994 0020       ldrsb.w r0, [r4, #32]\n",
-  "  1a:   f9b4 003e       ldrsh.w r0, [r4, #62]   ; 0x3e\n",
-  "  1e:   f9b4 0040       ldrsh.w r0, [r4, #64]   ; 0x40\n",
-  "  22:   67e0            str     r0, [r4, #124]  ; 0x7c\n",
-  "  24:   f8c4 0080       str.w   r0, [r4, #128]  ; 0x80\n",
-  "  28:   77e0            strb    r0, [r4, #31]\n",
-  "  2a:   f884 0020       strb.w  r0, [r4, #32]\n",
-  "  2e:   87e0            strh    r0, [r4, #62]   ; 0x3e\n",
-  "  30:   f8a4 0040       strh.w  r0, [r4, #64]   ; 0x40\n",
-  nullptr
-};
-const char* const CompareAndBranchResults[] = {
-  "  0: b130        cbz r0, 10 <CompareAndBranch+0x10>\n",
-  "  2: f1bb 0f00   cmp.w fp, #0\n",
-  "  6: d003        beq.n 10 <CompareAndBranch+0x10>\n",
-  "  8: b910        cbnz r0, 10 <CompareAndBranch+0x10>\n",
-  "  a: f1bb 0f00   cmp.w fp, #0\n",
-  "  e: d1ff        bne.n 10 <CompareAndBranch+0x10>\n",
-  nullptr
-};
-
-const char* const AddConstantResults[] = {
-  "   0:	4608      	mov	r0, r1\n",
-  "   2:	1c48      	adds	r0, r1, #1\n",
-  "   4:	1dc8      	adds	r0, r1, #7\n",
-  "   6:	f101 0008 	add.w	r0, r1, #8\n",
-  "   a:	f101 00ff 	add.w	r0, r1, #255	; 0xff\n",
-  "   e:	f501 7080 	add.w	r0, r1, #256	; 0x100\n",
-  "  12:	f201 1001 	addw	r0, r1, #257	; 0x101\n",
-  "  16:	f601 70ff 	addw	r0, r1, #4095	; 0xfff\n",
-  "  1a:	f501 5080 	add.w	r0, r1, #4096	; 0x1000\n",
-  "  1e:	f46f 5080 	mvn.w	r0, #4096	; 0x1000\n",
-  "  22:	1a08      	subs	r0, r1, r0\n",
-  "  24:	f241 0002 	movw	r0, #4098	; 0x1002\n",
-  "  28:	1808      	adds	r0, r1, r0\n",
-  "  2a:	f64f 70ff 	movw	r0, #65535	; 0xffff\n",
-  "  2e:	1808      	adds	r0, r1, r0\n",
-  "  30:	f501 3080 	add.w	r0, r1, #65536	; 0x10000\n",
-  "  34:	f101 1001 	add.w	r0, r1, #65537	; 0x10001\n",
-  "  38:	f06f 1001 	mvn.w	r0, #65537	; 0x10001\n",
-  "  3c:	1a08      	subs	r0, r1, r0\n",
-  "  3e:	f240 0003 	movw	r0, #3\n",
-  "  42:	f2c0 0001 	movt	r0, #1\n",
-  "  46:	1808      	adds	r0, r1, r0\n",
-  "  48:	1e48      	subs	r0, r1, #1\n",
-  "  4a:	1fc8      	subs	r0, r1, #7\n",
-  "  4c:	f1a1 0008 	sub.w	r0, r1, #8\n",
-  "  50:	f1a1 00ff 	sub.w	r0, r1, #255	; 0xff\n",
-  "  54:	f5a1 7080 	sub.w	r0, r1, #256	; 0x100\n",
-  "  58:	f2a1 1001 	subw	r0, r1, #257	; 0x101\n",
-  "  5c:	f6a1 70ff 	subw	r0, r1, #4095	; 0xfff\n",
-  "  60:	f5a1 5080 	sub.w	r0, r1, #4096	; 0x1000\n",
-  "  64:	f46f 5080 	mvn.w	r0, #4096	; 0x1000\n",
-  "  68:	1808      	adds	r0, r1, r0\n",
-  "  6a:	f241 0002 	movw	r0, #4098	; 0x1002\n",
-  "  6e:	1a08      	subs	r0, r1, r0\n",
-  "  70:	f64f 70ff 	movw	r0, #65535	; 0xffff\n",
-  "  74:	1a08      	subs	r0, r1, r0\n",
-  "  76:	f5a1 3080 	sub.w	r0, r1, #65536	; 0x10000\n",
-  "  7a:	f1a1 1001 	sub.w	r0, r1, #65537	; 0x10001\n",
-  "  7e:	f06f 1001 	mvn.w	r0, #65537	; 0x10001\n",
-  "  82:	1808      	adds	r0, r1, r0\n",
-  "  84:	f64f 70fd 	movw	r0, #65533	; 0xfffd\n",
-  "  88:	f6cf 70fe 	movt	r0, #65534	; 0xfffe\n",
-  "  8c:	1808      	adds	r0, r1, r0\n",
-  "  8e:	3101      	adds	r1, #1\n",
-  "  90:	3007      	adds	r0, #7\n",
-  "  92:	3108      	adds	r1, #8\n",
-  "  94:	30ff      	adds	r0, #255	; 0xff\n",
-  "  96:	f501 7180 	add.w	r1, r1, #256	; 0x100\n",
-  "  9a:	f200 1001 	addw	r0, r0, #257	; 0x101\n",
-  "  9e:	f601 71ff 	addw	r1, r1, #4095	; 0xfff\n",
-  "  a2:	f500 5080 	add.w	r0, r0, #4096	; 0x1000\n",
-  "  a6:	f46f 5c80 	mvn.w	ip, #4096	; 0x1000\n",
-  "  aa:	eba1 010c 	sub.w	r1, r1, ip\n",
-  "  ae:	f241 0c02 	movw	ip, #4098	; 0x1002\n",
-  "  b2:	4460      	add	r0, ip\n",
-  "  b4:	f64f 7cff 	movw	ip, #65535	; 0xffff\n",
-  "  b8:	4461      	add	r1, ip\n",
-  "  ba:	f500 3080 	add.w	r0, r0, #65536	; 0x10000\n",
-  "  be:	f101 1101 	add.w	r1, r1, #65537	; 0x10001\n",
-  "  c2:	f06f 1c01 	mvn.w	ip, #65537	; 0x10001\n",
-  "  c6:	eba0 000c 	sub.w	r0, r0, ip\n",
-  "  ca:	f240 0c03 	movw	ip, #3\n",
-  "  ce:	f2c0 0c01 	movt	ip, #1\n",
-  "  d2:	4461      	add	r1, ip\n",
-  "  d4:	3801      	subs	r0, #1\n",
-  "  d6:	3907      	subs	r1, #7\n",
-  "  d8:	3808      	subs	r0, #8\n",
-  "  da:	39ff      	subs	r1, #255	; 0xff\n",
-  "  dc:	f5a0 7080 	sub.w	r0, r0, #256	; 0x100\n",
-  "  e0:	f2a1 1101 	subw	r1, r1, #257	; 0x101\n",
-  "  e4:	f6a0 70ff 	subw	r0, r0, #4095	; 0xfff\n",
-  "  e8:	f5a1 5180 	sub.w	r1, r1, #4096	; 0x1000\n",
-  "  ec:	f46f 5c80 	mvn.w	ip, #4096	; 0x1000\n",
-  "  f0:	4460      	add	r0, ip\n",
-  "  f2:	f241 0c02 	movw	ip, #4098	; 0x1002\n",
-  "  f6:	eba1 010c 	sub.w	r1, r1, ip\n",
-  "  fa:	f64f 7cff 	movw	ip, #65535	; 0xffff\n",
-  "  fe:	eba0 000c 	sub.w	r0, r0, ip\n",
-  " 102:	f5a1 3180 	sub.w	r1, r1, #65536	; 0x10000\n",
-  " 106:	f1a0 1001 	sub.w	r0, r0, #65537	; 0x10001\n",
-  " 10a:	f06f 1c01 	mvn.w	ip, #65537	; 0x10001\n",
-  " 10e:	4461      	add	r1, ip\n",
-  " 110:	f64f 7cfd 	movw	ip, #65533	; 0xfffd\n",
-  " 114:	f6cf 7cfe 	movt	ip, #65534	; 0xfffe\n",
-  " 118:	4460      	add	r0, ip\n",
-  " 11a:	f101 0801 	add.w	r8, r1, #1\n",
-  " 11e:	f108 0007 	add.w	r0, r8, #7\n",
-  " 122:	f108 0808 	add.w	r8, r8, #8\n",
-  " 126:	f101 08ff 	add.w	r8, r1, #255	; 0xff\n",
-  " 12a:	f508 7080 	add.w	r0, r8, #256	; 0x100\n",
-  " 12e:	f208 1801 	addw	r8, r8, #257	; 0x101\n",
-  " 132:	f601 78ff 	addw	r8, r1, #4095	; 0xfff\n",
-  " 136:	f508 5080 	add.w	r0, r8, #4096	; 0x1000\n",
-  " 13a:	f46f 5c80 	mvn.w	ip, #4096	; 0x1000\n",
-  " 13e:	eba8 080c 	sub.w	r8, r8, ip\n",
-  " 142:	f241 0002 	movw	r0, #4098	; 0x1002\n",
-  " 146:	1808      	adds	r0, r1, r0\n",
-  " 148:	f64f 70ff 	movw	r0, #65535	; 0xffff\n",
-  " 14c:	eb08 0000 	add.w	r0, r8, r0\n",
-  " 150:	f508 3880 	add.w	r8, r8, #65536	; 0x10000\n",
-  " 154:	f101 1801 	add.w	r8, r1, #65537	; 0x10001\n",
-  " 158:	f06f 1001 	mvn.w	r0, #65537	; 0x10001\n",
-  " 15c:	eba8 0000 	sub.w	r0, r8, r0\n",
-  " 160:	f240 0003 	movw	r0, #3\n",
-  " 164:	f2c0 0001 	movt	r0, #1\n",
-  " 168:	eb08 0000 	add.w	r0, r8, r0\n",
-  " 16c:	f108 38ff 	add.w	r8, r8, #4294967295	; 0xffffffff\n",
-  " 170:	f1a1 0807 	sub.w	r8, r1, #7\n",
-  " 174:	f1a8 0008 	sub.w	r0, r8, #8\n",
-  " 178:	f1a8 08ff 	sub.w	r8, r8, #255	; 0xff\n",
-  " 17c:	f5a1 7880 	sub.w	r8, r1, #256	; 0x100\n",
-  " 180:	f2a8 1001 	subw	r0, r8, #257	; 0x101\n",
-  " 184:	f6a8 78ff 	subw	r8, r8, #4095	; 0xfff\n",
-  " 188:	f5a1 5880 	sub.w	r8, r1, #4096	; 0x1000\n",
-  " 18c:	f46f 5080 	mvn.w	r0, #4096	; 0x1000\n",
-  " 190:	eb08 0000 	add.w	r0, r8, r0\n",
-  " 194:	f241 0002 	movw	r0, #4098	; 0x1002\n",
-  " 198:	1a08      	subs	r0, r1, r0\n",
-  " 19a:	f64f 78ff 	movw	r8, #65535	; 0xffff\n",
-  " 19e:	eba1 0808 	sub.w	r8, r1, r8\n",
-  " 1a2:	f5a8 3080 	sub.w	r0, r8, #65536	; 0x10000\n",
-  " 1a6:	f1a8 1801 	sub.w	r8, r8, #65537	; 0x10001\n",
-  " 1aa:	f06f 1801 	mvn.w	r8, #65537	; 0x10001\n",
-  " 1ae:	eb01 0808 	add.w	r8, r1, r8\n",
-  " 1b2:	f64f 70fd 	movw	r0, #65533	; 0xfffd\n",
-  " 1b6:	f6cf 70fe 	movt	r0, #65534	; 0xfffe\n",
-  " 1ba:	eb08 0000 	add.w	r0, r8, r0\n",
-  " 1be:	4608      	mov	r0, r1\n",
-  " 1c0:	f101 0001 	add.w	r0, r1, #1\n",
-  " 1c4:	f101 0007 	add.w	r0, r1, #7\n",
-  " 1c8:	f101 0008 	add.w	r0, r1, #8\n",
-  " 1cc:	f101 00ff 	add.w	r0, r1, #255	; 0xff\n",
-  " 1d0:	f501 7080 	add.w	r0, r1, #256	; 0x100\n",
-  " 1d4:	f201 1001 	addw	r0, r1, #257	; 0x101\n",
-  " 1d8:	f601 70ff 	addw	r0, r1, #4095	; 0xfff\n",
-  " 1dc:	f501 5080 	add.w	r0, r1, #4096	; 0x1000\n",
-  " 1e0:	f46f 5080 	mvn.w	r0, #4096	; 0x1000\n",
-  " 1e4:	eba1 0000 	sub.w	r0, r1, r0\n",
-  " 1e8:	f241 0002 	movw	r0, #4098	; 0x1002\n",
-  " 1ec:	eb01 0000 	add.w	r0, r1, r0\n",
-  " 1f0:	f64f 70ff 	movw	r0, #65535	; 0xffff\n",
-  " 1f4:	eb01 0000 	add.w	r0, r1, r0\n",
-  " 1f8:	f501 3080 	add.w	r0, r1, #65536	; 0x10000\n",
-  " 1fc:	f101 1001 	add.w	r0, r1, #65537	; 0x10001\n",
-  " 200:	f06f 1001 	mvn.w	r0, #65537	; 0x10001\n",
-  " 204:	eba1 0000 	sub.w	r0, r1, r0\n",
-  " 208:	f240 0003 	movw	r0, #3\n",
-  " 20c:	f2c0 0001 	movt	r0, #1\n",
-  " 210:	eb01 0000 	add.w	r0, r1, r0\n",
-  " 214:	f101 30ff 	add.w	r0, r1, #4294967295	; 0xffffffff\n",
-  " 218:	f1a1 0007 	sub.w	r0, r1, #7\n",
-  " 21c:	f1a1 0008 	sub.w	r0, r1, #8\n",
-  " 220:	f1a1 00ff 	sub.w	r0, r1, #255	; 0xff\n",
-  " 224:	f5a1 7080 	sub.w	r0, r1, #256	; 0x100\n",
-  " 228:	f2a1 1001 	subw	r0, r1, #257	; 0x101\n",
-  " 22c:	f6a1 70ff 	subw	r0, r1, #4095	; 0xfff\n",
-  " 230:	f5a1 5080 	sub.w	r0, r1, #4096	; 0x1000\n",
-  " 234:	f46f 5080 	mvn.w	r0, #4096	; 0x1000\n",
-  " 238:	eb01 0000 	add.w	r0, r1, r0\n",
-  " 23c:	f241 0002 	movw	r0, #4098	; 0x1002\n",
-  " 240:	eba1 0000 	sub.w	r0, r1, r0\n",
-  " 244:	f64f 70ff 	movw	r0, #65535	; 0xffff\n",
-  " 248:	eba1 0000 	sub.w	r0, r1, r0\n",
-  " 24c:	f5a1 3080 	sub.w	r0, r1, #65536	; 0x10000\n",
-  " 250:	f1a1 1001 	sub.w	r0, r1, #65537	; 0x10001\n",
-  " 254:	f06f 1001 	mvn.w	r0, #65537	; 0x10001\n",
-  " 258:	eb01 0000 	add.w	r0, r1, r0\n",
-  " 25c:	f64f 70fd 	movw	r0, #65533	; 0xfffd\n",
-  " 260:	f6cf 70fe 	movt	r0, #65534	; 0xfffe\n",
-  " 264:	eb01 0000 	add.w	r0, r1, r0\n",
-  " 268:	f101 0101 	add.w	r1, r1, #1\n",
-  " 26c:	f100 0007 	add.w	r0, r0, #7\n",
-  " 270:	f101 0108 	add.w	r1, r1, #8\n",
-  " 274:	f100 00ff 	add.w	r0, r0, #255	; 0xff\n",
-  " 278:	f501 7180 	add.w	r1, r1, #256	; 0x100\n",
-  " 27c:	f200 1001 	addw	r0, r0, #257	; 0x101\n",
-  " 280:	f601 71ff 	addw	r1, r1, #4095	; 0xfff\n",
-  " 284:	f500 5080 	add.w	r0, r0, #4096	; 0x1000\n",
-  " 288:	f46f 5c80 	mvn.w	ip, #4096	; 0x1000\n",
-  " 28c:	eba1 010c 	sub.w	r1, r1, ip\n",
-  " 290:	f241 0c02 	movw	ip, #4098	; 0x1002\n",
-  " 294:	4460      	add	r0, ip\n",
-  " 296:	f64f 7cff 	movw	ip, #65535	; 0xffff\n",
-  " 29a:	4461      	add	r1, ip\n",
-  " 29c:	f500 3080 	add.w	r0, r0, #65536	; 0x10000\n",
-  " 2a0:	f101 1101 	add.w	r1, r1, #65537	; 0x10001\n",
-  " 2a4:	f06f 1c01 	mvn.w	ip, #65537	; 0x10001\n",
-  " 2a8:	eba0 000c 	sub.w	r0, r0, ip\n",
-  " 2ac:	f240 0c03 	movw	ip, #3\n",
-  " 2b0:	f2c0 0c01 	movt	ip, #1\n",
-  " 2b4:	4461      	add	r1, ip\n",
-  " 2b6:	f100 30ff 	add.w	r0, r0, #4294967295	; 0xffffffff\n",
-  " 2ba:	f1a1 0107 	sub.w	r1, r1, #7\n",
-  " 2be:	f1a0 0008 	sub.w	r0, r0, #8\n",
-  " 2c2:	f1a1 01ff 	sub.w	r1, r1, #255	; 0xff\n",
-  " 2c6:	f5a0 7080 	sub.w	r0, r0, #256	; 0x100\n",
-  " 2ca:	f2a1 1101 	subw	r1, r1, #257	; 0x101\n",
-  " 2ce:	f6a0 70ff 	subw	r0, r0, #4095	; 0xfff\n",
-  " 2d2:	f5a1 5180 	sub.w	r1, r1, #4096	; 0x1000\n",
-  " 2d6:	f46f 5c80 	mvn.w	ip, #4096	; 0x1000\n",
-  " 2da:	4460      	add	r0, ip\n",
-  " 2dc:	f241 0c02 	movw	ip, #4098	; 0x1002\n",
-  " 2e0:	eba1 010c 	sub.w	r1, r1, ip\n",
-  " 2e4:	f64f 7cff 	movw	ip, #65535	; 0xffff\n",
-  " 2e8:	eba0 000c 	sub.w	r0, r0, ip\n",
-  " 2ec:	f5a1 3180 	sub.w	r1, r1, #65536	; 0x10000\n",
-  " 2f0:	f1a0 1001 	sub.w	r0, r0, #65537	; 0x10001\n",
-  " 2f4:	f06f 1c01 	mvn.w	ip, #65537	; 0x10001\n",
-  " 2f8:	4461      	add	r1, ip\n",
-  " 2fa:	f64f 7cfd 	movw	ip, #65533	; 0xfffd\n",
-  " 2fe:	f6cf 7cfe 	movt	ip, #65534	; 0xfffe\n",
-  " 302:	4460      	add	r0, ip\n",
-  " 304:	1c08      	adds	r0, r1, #0\n",
-  " 306:	1c48      	adds	r0, r1, #1\n",
-  " 308:	1dc8      	adds	r0, r1, #7\n",
-  " 30a:	f111 0008 	adds.w	r0, r1, #8\n",
-  " 30e:	f111 00ff 	adds.w	r0, r1, #255	; 0xff\n",
-  " 312:	f511 7080 	adds.w	r0, r1, #256	; 0x100\n",
-  " 316:	f46f 7080 	mvn.w	r0, #256	; 0x100\n",
-  " 31a:	1a08      	subs	r0, r1, r0\n",
-  " 31c:	f640 70ff 	movw	r0, #4095	; 0xfff\n",
-  " 320:	1808      	adds	r0, r1, r0\n",
-  " 322:	f511 5080 	adds.w	r0, r1, #4096	; 0x1000\n",
-  " 326:	f46f 5080 	mvn.w	r0, #4096	; 0x1000\n",
-  " 32a:	1a08      	subs	r0, r1, r0\n",
-  " 32c:	f241 0002 	movw	r0, #4098	; 0x1002\n",
-  " 330:	1808      	adds	r0, r1, r0\n",
-  " 332:	f64f 70ff 	movw	r0, #65535	; 0xffff\n",
-  " 336:	1808      	adds	r0, r1, r0\n",
-  " 338:	f511 3080 	adds.w	r0, r1, #65536	; 0x10000\n",
-  " 33c:	f111 1001 	adds.w	r0, r1, #65537	; 0x10001\n",
-  " 340:	f06f 1001 	mvn.w	r0, #65537	; 0x10001\n",
-  " 344:	1a08      	subs	r0, r1, r0\n",
-  " 346:	f240 0003 	movw	r0, #3\n",
-  " 34a:	f2c0 0001 	movt	r0, #1\n",
-  " 34e:	1808      	adds	r0, r1, r0\n",
-  " 350:	1e48      	subs	r0, r1, #1\n",
-  " 352:	1fc8      	subs	r0, r1, #7\n",
-  " 354:	f1b1 0008 	subs.w	r0, r1, #8\n",
-  " 358:	f1b1 00ff 	subs.w	r0, r1, #255	; 0xff\n",
-  " 35c:	f5b1 7080 	subs.w	r0, r1, #256	; 0x100\n",
-  " 360:	f46f 7080 	mvn.w	r0, #256	; 0x100\n",
-  " 364:	1808      	adds	r0, r1, r0\n",
-  " 366:	f640 70ff 	movw	r0, #4095	; 0xfff\n",
-  " 36a:	1a08      	subs	r0, r1, r0\n",
-  " 36c:	f5b1 5080 	subs.w	r0, r1, #4096	; 0x1000\n",
-  " 370:	f46f 5080 	mvn.w	r0, #4096	; 0x1000\n",
-  " 374:	1808      	adds	r0, r1, r0\n",
-  " 376:	f241 0002 	movw	r0, #4098	; 0x1002\n",
-  " 37a:	1a08      	subs	r0, r1, r0\n",
-  " 37c:	f64f 70ff 	movw	r0, #65535	; 0xffff\n",
-  " 380:	1a08      	subs	r0, r1, r0\n",
-  " 382:	f5b1 3080 	subs.w	r0, r1, #65536	; 0x10000\n",
-  " 386:	f1b1 1001 	subs.w	r0, r1, #65537	; 0x10001\n",
-  " 38a:	f06f 1001 	mvn.w	r0, #65537	; 0x10001\n",
-  " 38e:	1808      	adds	r0, r1, r0\n",
-  " 390:	f64f 70fd 	movw	r0, #65533	; 0xfffd\n",
-  " 394:	f6cf 70fe 	movt	r0, #65534	; 0xfffe\n",
-  " 398:	1808      	adds	r0, r1, r0\n",
-  " 39a:	3000      	adds	r0, #0\n",
-  " 39c:	3101      	adds	r1, #1\n",
-  " 39e:	3007      	adds	r0, #7\n",
-  " 3a0:	3108      	adds	r1, #8\n",
-  " 3a2:	30ff      	adds	r0, #255	; 0xff\n",
-  " 3a4:	f511 7180 	adds.w	r1, r1, #256	; 0x100\n",
-  " 3a8:	f46f 7c80 	mvn.w	ip, #256	; 0x100\n",
-  " 3ac:	ebb0 000c 	subs.w	r0, r0, ip\n",
-  " 3b0:	f640 7cff 	movw	ip, #4095	; 0xfff\n",
-  " 3b4:	eb11 010c 	adds.w	r1, r1, ip\n",
-  " 3b8:	f510 5080 	adds.w	r0, r0, #4096	; 0x1000\n",
-  " 3bc:	f46f 5c80 	mvn.w	ip, #4096	; 0x1000\n",
-  " 3c0:	ebb1 010c 	subs.w	r1, r1, ip\n",
-  " 3c4:	f241 0c02 	movw	ip, #4098	; 0x1002\n",
-  " 3c8:	eb10 000c 	adds.w	r0, r0, ip\n",
-  " 3cc:	f64f 7cff 	movw	ip, #65535	; 0xffff\n",
-  " 3d0:	eb11 010c 	adds.w	r1, r1, ip\n",
-  " 3d4:	f510 3080 	adds.w	r0, r0, #65536	; 0x10000\n",
-  " 3d8:	f111 1101 	adds.w	r1, r1, #65537	; 0x10001\n",
-  " 3dc:	f06f 1c01 	mvn.w	ip, #65537	; 0x10001\n",
-  " 3e0:	ebb0 000c 	subs.w	r0, r0, ip\n",
-  " 3e4:	f240 0c03 	movw	ip, #3\n",
-  " 3e8:	f2c0 0c01 	movt	ip, #1\n",
-  " 3ec:	eb11 010c 	adds.w	r1, r1, ip\n",
-  " 3f0:	3801      	subs	r0, #1\n",
-  " 3f2:	3907      	subs	r1, #7\n",
-  " 3f4:	3808      	subs	r0, #8\n",
-  " 3f6:	39ff      	subs	r1, #255	; 0xff\n",
-  " 3f8:	f5b0 7080 	subs.w	r0, r0, #256	; 0x100\n",
-  " 3fc:	f46f 7c80 	mvn.w	ip, #256	; 0x100\n",
-  " 400:	eb11 010c 	adds.w	r1, r1, ip\n",
-  " 404:	f640 7cff 	movw	ip, #4095	; 0xfff\n",
-  " 408:	ebb0 000c 	subs.w	r0, r0, ip\n",
-  " 40c:	f5b1 5180 	subs.w	r1, r1, #4096	; 0x1000\n",
-  " 410:	f46f 5c80 	mvn.w	ip, #4096	; 0x1000\n",
-  " 414:	eb10 000c 	adds.w	r0, r0, ip\n",
-  " 418:	f241 0c02 	movw	ip, #4098	; 0x1002\n",
-  " 41c:	ebb1 010c 	subs.w	r1, r1, ip\n",
-  " 420:	f64f 7cff 	movw	ip, #65535	; 0xffff\n",
-  " 424:	ebb0 000c 	subs.w	r0, r0, ip\n",
-  " 428:	f5b1 3180 	subs.w	r1, r1, #65536	; 0x10000\n",
-  " 42c:	f1b0 1001 	subs.w	r0, r0, #65537	; 0x10001\n",
-  " 430:	f06f 1c01 	mvn.w	ip, #65537	; 0x10001\n",
-  " 434:	eb11 010c 	adds.w	r1, r1, ip\n",
-  " 438:	f64f 7cfd 	movw	ip, #65533	; 0xfffd\n",
-  " 43c:	f6cf 7cfe 	movt	ip, #65534	; 0xfffe\n",
-  " 440:	eb10 000c 	adds.w	r0, r0, ip\n",
-  " 444:	bf08      	it	eq\n",
-  " 446:	f111 0001 	addseq.w	r0, r1, #1\n",
-  " 44a:	bf18      	it	ne\n",
-  " 44c:	1c48      	addne	r0, r1, #1\n",
-  " 44e:	bfa8      	it	ge\n",
-  " 450:	f110 0001 	addsge.w	r0, r0, #1\n",
-  " 454:	bfd8      	it	le\n",
-  " 456:	3001      	addle	r0, #1\n",
-  nullptr
-};
-
-const char* const CmpConstantResults[] = {
-  "   0:	2800      	cmp	r0, #0\n",
-  "   2:	2901      	cmp	r1, #1\n",
-  "   4:	2807      	cmp	r0, #7\n",
-  "   6:	2908      	cmp	r1, #8\n",
-  "   8:	28ff      	cmp	r0, #255	; 0xff\n",
-  "   a:	f5b1 7f80 	cmp.w	r1, #256	; 0x100\n",
-  "   e:	f46f 7c80 	mvn.w	ip, #256	; 0x100\n",
-  "  12:	eb10 0f0c 	cmn.w	r0, ip\n",
-  "  16:	f640 7cff 	movw	ip, #4095	; 0xfff\n",
-  "  1a:	4561      	cmp	r1, ip\n",
-  "  1c:	f5b0 5f80 	cmp.w	r0, #4096	; 0x1000\n",
-  "  20:	f46f 5c80 	mvn.w	ip, #4096	; 0x1000\n",
-  "  24:	eb11 0f0c 	cmn.w	r1, ip\n",
-  "  28:	f241 0c02 	movw	ip, #4098	; 0x1002\n",
-  "  2c:	4560      	cmp	r0, ip\n",
-  "  2e:	f64f 7cff 	movw	ip, #65535	; 0xffff\n",
-  "  32:	4561      	cmp	r1, ip\n",
-  "  34:	f5b0 3f80 	cmp.w	r0, #65536	; 0x10000\n",
-  "  38:	f1b1 1f01 	cmp.w	r1, #65537	; 0x10001\n",
-  "  3c:	f06f 1c01 	mvn.w	ip, #65537	; 0x10001\n",
-  "  40:	eb10 0f0c 	cmn.w	r0, ip\n",
-  "  44:	f240 0c03 	movw	ip, #3\n",
-  "  48:	f2c0 0c01 	movt	ip, #1\n",
-  "  4c:	4561      	cmp	r1, ip\n",
-  "  4e:	f1b0 3fff 	cmp.w	r0, #4294967295	; 0xffffffff\n",
-  "  52:	f111 0f07 	cmn.w	r1, #7\n",
-  "  56:	f110 0f08 	cmn.w	r0, #8\n",
-  "  5a:	f111 0fff 	cmn.w	r1, #255	; 0xff\n",
-  "  5e:	f510 7f80 	cmn.w	r0, #256	; 0x100\n",
-  "  62:	f46f 7c80 	mvn.w	ip, #256	; 0x100\n",
-  "  66:	4561      	cmp	r1, ip\n",
-  "  68:	f640 7cff 	movw	ip, #4095	; 0xfff\n",
-  "  6c:	eb10 0f0c 	cmn.w	r0, ip\n",
-  "  70:	f511 5f80 	cmn.w	r1, #4096	; 0x1000\n",
-  "  74:	f46f 5c80 	mvn.w	ip, #4096	; 0x1000\n",
-  "  78:	4560      	cmp	r0, ip\n",
-  "  7a:	f241 0c02 	movw	ip, #4098	; 0x1002\n",
-  "  7e:	eb11 0f0c 	cmn.w	r1, ip\n",
-  "  82:	f64f 7cff 	movw	ip, #65535	; 0xffff\n",
-  "  86:	eb10 0f0c 	cmn.w	r0, ip\n",
-  "  8a:	f511 3f80 	cmn.w	r1, #65536	; 0x10000\n",
-  "  8e:	f110 1f01 	cmn.w	r0, #65537	; 0x10001\n",
-  "  92:	f06f 1c01 	mvn.w	ip, #65537	; 0x10001\n",
-  "  96:	4561      	cmp	r1, ip\n",
-  "  98:	f64f 7cfd 	movw	ip, #65533	; 0xfffd\n",
-  "  9c:	f6cf 7cfe 	movt	ip, #65534	; 0xfffe\n",
-  "  a0:	4560      	cmp	r0, ip\n",
-  "  a2:	f1b8 0f00 	cmp.w	r8, #0\n",
-  "  a6:	f1b9 0f01 	cmp.w	r9, #1\n",
-  "  aa:	f1b8 0f07 	cmp.w	r8, #7\n",
-  "  ae:	f1b9 0f08 	cmp.w	r9, #8\n",
-  "  b2:	f1b8 0fff 	cmp.w	r8, #255	; 0xff\n",
-  "  b6:	f5b9 7f80 	cmp.w	r9, #256	; 0x100\n",
-  "  ba:	f46f 7c80 	mvn.w	ip, #256	; 0x100\n",
-  "  be:	eb18 0f0c 	cmn.w	r8, ip\n",
-  "  c2:	f640 7cff 	movw	ip, #4095	; 0xfff\n",
-  "  c6:	45e1      	cmp	r9, ip\n",
-  "  c8:	f5b8 5f80 	cmp.w	r8, #4096	; 0x1000\n",
-  "  cc:	f46f 5c80 	mvn.w	ip, #4096	; 0x1000\n",
-  "  d0:	eb19 0f0c 	cmn.w	r9, ip\n",
-  "  d4:	f241 0c02 	movw	ip, #4098	; 0x1002\n",
-  "  d8:	45e0      	cmp	r8, ip\n",
-  "  da:	f64f 7cff 	movw	ip, #65535	; 0xffff\n",
-  "  de:	45e1      	cmp	r9, ip\n",
-  "  e0:	f5b8 3f80 	cmp.w	r8, #65536	; 0x10000\n",
-  "  e4:	f1b9 1f01 	cmp.w	r9, #65537	; 0x10001\n",
-  "  e8:	f06f 1c01 	mvn.w	ip, #65537	; 0x10001\n",
-  "  ec:	eb18 0f0c 	cmn.w	r8, ip\n",
-  "  f0:	f240 0c03 	movw	ip, #3\n",
-  "  f4:	f2c0 0c01 	movt	ip, #1\n",
-  "  f8:	45e1      	cmp	r9, ip\n",
-  "  fa:	f1b8 3fff 	cmp.w	r8, #4294967295	; 0xffffffff\n",
-  "  fe:	f119 0f07 	cmn.w	r9, #7\n",
-  " 102:	f118 0f08 	cmn.w	r8, #8\n",
-  " 106:	f119 0fff 	cmn.w	r9, #255	; 0xff\n",
-  " 10a:	f518 7f80 	cmn.w	r8, #256	; 0x100\n",
-  " 10e:	f46f 7c80 	mvn.w	ip, #256	; 0x100\n",
-  " 112:	45e1      	cmp	r9, ip\n",
-  " 114:	f640 7cff 	movw	ip, #4095	; 0xfff\n",
-  " 118:	eb18 0f0c 	cmn.w	r8, ip\n",
-  " 11c:	f519 5f80 	cmn.w	r9, #4096	; 0x1000\n",
-  " 120:	f46f 5c80 	mvn.w	ip, #4096	; 0x1000\n",
-  " 124:	45e0      	cmp	r8, ip\n",
-  " 126:	f241 0c02 	movw	ip, #4098	; 0x1002\n",
-  " 12a:	eb19 0f0c 	cmn.w	r9, ip\n",
-  " 12e:	f64f 7cff 	movw	ip, #65535	; 0xffff\n",
-  " 132:	eb18 0f0c 	cmn.w	r8, ip\n",
-  " 136:	f519 3f80 	cmn.w	r9, #65536	; 0x10000\n",
-  " 13a:	f118 1f01 	cmn.w	r8, #65537	; 0x10001\n",
-  " 13e:	f06f 1c01 	mvn.w	ip, #65537	; 0x10001\n",
-  " 142:	45e1      	cmp	r9, ip\n",
-  " 144:	f64f 7cfd 	movw	ip, #65533	; 0xfffd\n",
-  " 148:	f6cf 7cfe 	movt	ip, #65534	; 0xfffe\n",
-  " 14c:	45e0      	cmp	r8, ip\n",
-  nullptr
-};
-
 const char* const VixlJniHelpersResults[] = {
   "   0:	e92d 4de0 	stmdb	sp!, {r5, r6, r7, r8, sl, fp, lr}\n",
   "   4:	ed2d 8a10 	vpush	{s16-s31}\n",
@@ -5595,7 +136,7 @@
   " 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",
+  " 1e8:	f000 b81b 	b.w	222 <VixlJniHelpers+0x222>\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",
@@ -5608,10 +149,12 @@
   " 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 c2c0 	ldr.w	ip, [r9, #704]	; 0x2c0\n",
-  " 222:	47e0      	blx	ip\n",
+  " 218:	e8bd 4de0 	ldmia.w	sp!, {r5, r6, r7, r8, sl, fp, lr}\n",
+  " 21c:	f8d9 8034 	ldr.w	r8, [r9, #52]	; 0x34\n",
+  " 220:	4770      	bx	lr\n",
+  " 222:	4660      	mov	r0, ip\n",
+  " 224:	f8d9 c2c0 	ldr.w	ip, [r9, #704]	; 0x2c0\n",
+  " 228:	47e0      	blx	ip\n",
   nullptr
 };
 
@@ -5718,55 +261,6 @@
 
 std::map<std::string, const char* const*> test_results;
 void setup_results() {
-    test_results["SimpleMov"] = SimpleMovResults;
-    test_results["SimpleMov32"] = SimpleMov32Results;
-    test_results["SimpleMovAdd"] = SimpleMovAddResults;
-    test_results["DataProcessingRegister"] = DataProcessingRegisterResults;
-    test_results["DataProcessingImmediate"] = DataProcessingImmediateResults;
-    test_results["DataProcessingModifiedImmediate"] = DataProcessingModifiedImmediateResults;
-    test_results["DataProcessingModifiedImmediates"] = DataProcessingModifiedImmediatesResults;
-    test_results["DataProcessingShiftedRegister"] = DataProcessingShiftedRegisterResults;
-    test_results["ShiftImmediate"] = ShiftImmediateResults;
-    test_results["BasicLoad"] = BasicLoadResults;
-    test_results["BasicStore"] = BasicStoreResults;
-    test_results["ComplexLoad"] = ComplexLoadResults;
-    test_results["ComplexStore"] = ComplexStoreResults;
-    test_results["NegativeLoadStore"] = NegativeLoadStoreResults;
-    test_results["SimpleLoadStoreDual"] = SimpleLoadStoreDualResults;
-    test_results["ComplexLoadStoreDual"] = ComplexLoadStoreDualResults;
-    test_results["NegativeLoadStoreDual"] = NegativeLoadStoreDualResults;
-    test_results["SimpleBranch"] = SimpleBranchResults;
-    test_results["LongBranch"] = LongBranchResults;
-    test_results["LoadMultiple"] = LoadMultipleResults;
-    test_results["StoreMultiple"] = StoreMultipleResults;
-    test_results["MovWMovT"] = MovWMovTResults;
-    test_results["SpecialAddSub"] = SpecialAddSubResults;
-    test_results["LoadFromOffset"] = LoadFromOffsetResults;
-    test_results["StoreToOffset"] = StoreToOffsetResults;
-    test_results["IfThen"] = IfThenResults;
-    test_results["CbzCbnz"] = CbzCbnzResults;
-    test_results["Multiply"] = MultiplyResults;
-    test_results["Divide"] = DivideResults;
-    test_results["VMov"] = VMovResults;
-    test_results["BasicFloatingPoint"] = BasicFloatingPointResults;
-    test_results["FloatingPointConversions"] = FloatingPointConversionsResults;
-    test_results["FloatingPointComparisons"] = FloatingPointComparisonsResults;
-    test_results["Calls"] = CallsResults;
-    test_results["Breakpoint"] = BreakpointResults;
-    test_results["StrR1"] = StrR1Results;
-    test_results["VPushPop"] = VPushPopResults;
-    test_results["Max16BitBranch"] = Max16BitBranchResults;
-    test_results["Branch32"] = Branch32Results;
-    test_results["CompareAndBranchMax"] = CompareAndBranchMaxResults;
-    test_results["CompareAndBranchRelocation16"] = CompareAndBranchRelocation16Results;
-    test_results["CompareAndBranchRelocation32"] = CompareAndBranchRelocation32Results;
-    test_results["MixedBranch32"] = MixedBranch32Results;
-    test_results["Shifts"] = ShiftsResults;
-    test_results["LoadStoreRegOffset"] = LoadStoreRegOffsetResults;
-    test_results["LoadStoreLimits"] = LoadStoreLimitsResults;
-    test_results["CompareAndBranch"] = CompareAndBranchResults;
-    test_results["AddConstant"] = AddConstantResults;
-    test_results["CmpConstant"] = CmpConstantResults;
     test_results["VixlJniHelpers"] = VixlJniHelpersResults;
     test_results["VixlStoreToOffset"] = VixlStoreToOffsetResults;
     test_results["VixlLoadFromOffset"] = VixlLoadFromOffsetResults;
diff --git a/compiler/utils/atomic_method_ref_map-inl.h b/compiler/utils/atomic_dex_ref_map-inl.h
similarity index 62%
rename from compiler/utils/atomic_method_ref_map-inl.h
rename to compiler/utils/atomic_dex_ref_map-inl.h
index ad3a099..c41d8fc 100644
--- a/compiler/utils/atomic_method_ref_map-inl.h
+++ b/compiler/utils/atomic_dex_ref_map-inl.h
@@ -14,72 +14,72 @@
  * limitations under the License.
  */
 
-#ifndef ART_COMPILER_UTILS_ATOMIC_METHOD_REF_MAP_INL_H_
-#define ART_COMPILER_UTILS_ATOMIC_METHOD_REF_MAP_INL_H_
+#ifndef ART_COMPILER_UTILS_ATOMIC_DEX_REF_MAP_INL_H_
+#define ART_COMPILER_UTILS_ATOMIC_DEX_REF_MAP_INL_H_
 
-#include "atomic_method_ref_map.h"
+#include "atomic_dex_ref_map.h"
 
 #include "dex_file-inl.h"
 
 namespace art {
 
 template <typename T>
-inline typename AtomicMethodRefMap<T>::InsertResult AtomicMethodRefMap<T>::Insert(
-    MethodReference ref,
+inline typename AtomicDexRefMap<T>::InsertResult AtomicDexRefMap<T>::Insert(
+    DexFileReference ref,
     const T& expected,
     const T& desired) {
   ElementArray* const array = GetArray(ref.dex_file);
   if (array == nullptr) {
     return kInsertResultInvalidDexFile;
   }
-  return (*array)[ref.dex_method_index].CompareExchangeStrongSequentiallyConsistent(
-      expected, desired)
+  DCHECK_LT(ref.index, array->size());
+  return (*array)[ref.index].CompareExchangeStrongSequentiallyConsistent(expected, desired)
       ? kInsertResultSuccess
       : kInsertResultCASFailure;
 }
 
 template <typename T>
-inline bool AtomicMethodRefMap<T>::Get(MethodReference ref, T* out) const {
+inline bool AtomicDexRefMap<T>::Get(DexFileReference ref, T* out) const {
   const ElementArray* const array = GetArray(ref.dex_file);
   if (array == nullptr) {
     return false;
   }
-  *out = (*array)[ref.dex_method_index].LoadRelaxed();
+  *out = (*array)[ref.index].LoadRelaxed();
   return true;
 }
 
 template <typename T>
-inline void AtomicMethodRefMap<T>::AddDexFile(const DexFile* dex_file) {
-  arrays_.Put(dex_file, std::move(ElementArray(dex_file->NumMethodIds())));
+inline void AtomicDexRefMap<T>::AddDexFile(const DexFile* dex_file, size_t max_index) {
+  arrays_.Put(dex_file, std::move(ElementArray(max_index)));
 }
 
 template <typename T>
-inline typename AtomicMethodRefMap<T>::ElementArray* AtomicMethodRefMap<T>::GetArray(
+inline typename AtomicDexRefMap<T>::ElementArray* AtomicDexRefMap<T>::GetArray(
     const DexFile* dex_file) {
   auto it = arrays_.find(dex_file);
   return (it != arrays_.end()) ? &it->second : nullptr;
 }
 
 template <typename T>
-inline const typename AtomicMethodRefMap<T>::ElementArray* AtomicMethodRefMap<T>::GetArray(
+inline const typename AtomicDexRefMap<T>::ElementArray* AtomicDexRefMap<T>::GetArray(
     const DexFile* dex_file) const {
   auto it = arrays_.find(dex_file);
   return (it != arrays_.end()) ? &it->second : nullptr;
 }
 
 template <typename T> template <typename Visitor>
-inline void AtomicMethodRefMap<T>::Visit(const Visitor& visitor) {
+inline void AtomicDexRefMap<T>::Visit(const Visitor& visitor) {
   for (auto& pair : arrays_) {
     const DexFile* dex_file = pair.first;
     const ElementArray& elements = pair.second;
     for (size_t i = 0; i < elements.size(); ++i) {
-      visitor(MethodReference(dex_file, i), elements[i].LoadRelaxed());
+      visitor(DexFileReference(dex_file, i), elements[i].LoadRelaxed());
     }
   }
 }
 
 template <typename T>
-inline void AtomicMethodRefMap<T>::ClearEntries() {
+inline void AtomicDexRefMap<T>::ClearEntries() {
   for (auto& it : arrays_) {
     for (auto& element : it.second) {
       element.StoreRelaxed(nullptr);
@@ -89,4 +89,4 @@
 
 }  // namespace art
 
-#endif  // ART_COMPILER_UTILS_ATOMIC_METHOD_REF_MAP_INL_H_
+#endif  // ART_COMPILER_UTILS_ATOMIC_DEX_REF_MAP_INL_H_
diff --git a/compiler/utils/atomic_method_ref_map.h b/compiler/utils/atomic_dex_ref_map.h
similarity index 80%
rename from compiler/utils/atomic_method_ref_map.h
rename to compiler/utils/atomic_dex_ref_map.h
index fed848f..2da4ffa 100644
--- a/compiler/utils/atomic_method_ref_map.h
+++ b/compiler/utils/atomic_dex_ref_map.h
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef ART_COMPILER_UTILS_ATOMIC_METHOD_REF_MAP_H_
-#define ART_COMPILER_UTILS_ATOMIC_METHOD_REF_MAP_H_
+#ifndef ART_COMPILER_UTILS_ATOMIC_DEX_REF_MAP_H_
+#define ART_COMPILER_UTILS_ATOMIC_DEX_REF_MAP_H_
 
 #include "base/dchecked_vector.h"
-#include "method_reference.h"
+#include "dex_file.h"
 #include "safe_map.h"
 
 namespace art {
@@ -27,10 +27,10 @@
 
 // Used by CompilerCallbacks to track verification information from the Runtime.
 template <typename T>
-class AtomicMethodRefMap {
+class AtomicDexRefMap {
  public:
-  explicit AtomicMethodRefMap() {}
-  ~AtomicMethodRefMap() {}
+  explicit AtomicDexRefMap() {}
+  ~AtomicDexRefMap() {}
 
   // Atomically swap the element in if the existing value matches expected.
   enum InsertResult {
@@ -38,14 +38,14 @@
     kInsertResultCASFailure,
     kInsertResultSuccess,
   };
-  InsertResult Insert(MethodReference ref, const T& expected, const T& desired);
+  InsertResult Insert(DexFileReference ref, const T& expected, const T& desired);
 
   // Retreive an item, returns false if the dex file is not added.
-  bool Get(MethodReference ref, T* out) const;
+  bool Get(DexFileReference ref, T* out) const;
 
   // Dex files must be added before method references belonging to them can be used as keys. Not
   // thread safe.
-  void AddDexFile(const DexFile* dex_file);
+  void AddDexFile(const DexFile* dex_file, size_t max_index);
 
   bool HaveDexFile(const DexFile* dex_file) const {
     return arrays_.find(dex_file) != arrays_.end();
@@ -70,4 +70,4 @@
 
 }  // namespace art
 
-#endif  // ART_COMPILER_UTILS_ATOMIC_METHOD_REF_MAP_H_
+#endif  // ART_COMPILER_UTILS_ATOMIC_DEX_REF_MAP_H_
diff --git a/compiler/utils/atomic_method_ref_map_test.cc b/compiler/utils/atomic_dex_ref_map_test.cc
similarity index 64%
rename from compiler/utils/atomic_method_ref_map_test.cc
rename to compiler/utils/atomic_dex_ref_map_test.cc
index 9e5bf4b..ae19a9c 100644
--- a/compiler/utils/atomic_method_ref_map_test.cc
+++ b/compiler/utils/atomic_dex_ref_map_test.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "atomic_method_ref_map-inl.h"
+#include "atomic_dex_ref_map-inl.h"
 
 #include <memory>
 
@@ -25,46 +25,46 @@
 
 namespace art {
 
-class AtomicMethodRefMapTest : public CommonRuntimeTest {};
+class AtomicDexRefMapTest : public CommonRuntimeTest {};
 
-TEST_F(AtomicMethodRefMapTest, RunTests) {
+TEST_F(AtomicDexRefMapTest, RunTests) {
   ScopedObjectAccess soa(Thread::Current());
   std::unique_ptr<const DexFile> dex(OpenTestDexFile("Interfaces"));
   ASSERT_TRUE(dex != nullptr);
-  using Map = AtomicMethodRefMap<int>;
+  using Map = AtomicDexRefMap<int>;
   Map map;
   int value = 123;
   // Error case: Not already inserted.
-  EXPECT_FALSE(map.Get(MethodReference(dex.get(), 1), &value));
+  EXPECT_FALSE(map.Get(DexFileReference(dex.get(), 1), &value));
   EXPECT_FALSE(map.HaveDexFile(dex.get()));
   // Error case: Dex file not registered.
-  EXPECT_TRUE(map.Insert(MethodReference(dex.get(), 1), 0, 1) == Map::kInsertResultInvalidDexFile);
-  map.AddDexFile(dex.get());
+  EXPECT_TRUE(map.Insert(DexFileReference(dex.get(), 1), 0, 1) == Map::kInsertResultInvalidDexFile);
+  map.AddDexFile(dex.get(), dex->NumMethodIds());
   EXPECT_TRUE(map.HaveDexFile(dex.get()));
   EXPECT_GT(dex->NumMethodIds(), 10u);
   // After we have added the get should succeed but return the default value.
-  EXPECT_TRUE(map.Get(MethodReference(dex.get(), 1), &value));
+  EXPECT_TRUE(map.Get(DexFileReference(dex.get(), 1), &value));
   EXPECT_EQ(value, 0);
   // Actually insert an item and make sure we can retreive it.
   static const int kInsertValue = 44;
-  EXPECT_TRUE(map.Insert(MethodReference(dex.get(), 1), 0, kInsertValue) ==
+  EXPECT_TRUE(map.Insert(DexFileReference(dex.get(), 1), 0, kInsertValue) ==
               Map::kInsertResultSuccess);
-  EXPECT_TRUE(map.Get(MethodReference(dex.get(), 1), &value));
+  EXPECT_TRUE(map.Get(DexFileReference(dex.get(), 1), &value));
   EXPECT_EQ(value, kInsertValue);
   static const int kInsertValue2 = 123;
-  EXPECT_TRUE(map.Insert(MethodReference(dex.get(), 2), 0, kInsertValue2) ==
+  EXPECT_TRUE(map.Insert(DexFileReference(dex.get(), 2), 0, kInsertValue2) ==
               Map::kInsertResultSuccess);
-  EXPECT_TRUE(map.Get(MethodReference(dex.get(), 1), &value));
+  EXPECT_TRUE(map.Get(DexFileReference(dex.get(), 1), &value));
   EXPECT_EQ(value, kInsertValue);
-  EXPECT_TRUE(map.Get(MethodReference(dex.get(), 2), &value));
+  EXPECT_TRUE(map.Get(DexFileReference(dex.get(), 2), &value));
   EXPECT_EQ(value, kInsertValue2);
   // Error case: Incorrect expected value for CAS.
-  EXPECT_TRUE(map.Insert(MethodReference(dex.get(), 1), 0, kInsertValue + 1) ==
+  EXPECT_TRUE(map.Insert(DexFileReference(dex.get(), 1), 0, kInsertValue + 1) ==
       Map::kInsertResultCASFailure);
   // Correctly overwrite the value and verify.
-  EXPECT_TRUE(map.Insert(MethodReference(dex.get(), 1), kInsertValue, kInsertValue + 1) ==
+  EXPECT_TRUE(map.Insert(DexFileReference(dex.get(), 1), kInsertValue, kInsertValue + 1) ==
       Map::kInsertResultSuccess);
-  EXPECT_TRUE(map.Get(MethodReference(dex.get(), 1), &value));
+  EXPECT_TRUE(map.Get(DexFileReference(dex.get(), 1), &value));
   EXPECT_EQ(value, kInsertValue + 1);
 }
 
diff --git a/compiler/utils/label.h b/compiler/utils/label.h
index 4c6ae8e..85710d0 100644
--- a/compiler/utils/label.h
+++ b/compiler/utils/label.h
@@ -26,10 +26,6 @@
 class AssemblerBuffer;
 class AssemblerFixup;
 
-namespace arm {
-  class ArmAssembler;
-  class Thumb2Assembler;
-}  // namespace arm
 namespace arm64 {
   class Arm64Assembler;
 }  // namespace arm64
@@ -116,8 +112,6 @@
     CHECK(IsLinked());
   }
 
-  friend class arm::ArmAssembler;
-  friend class arm::Thumb2Assembler;
   friend class arm64::Arm64Assembler;
   friend class mips::MipsAssembler;
   friend class mips64::Mips64Assembler;
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 0b05b75..c581f1c 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -2904,6 +2904,17 @@
                 static_cast<FRegister>(wt));
 }
 
+void MipsAssembler::ReplicateFPToVectorRegister(VectorRegister dst,
+                                                FRegister src,
+                                                bool is_double) {
+  // Float or double in FPU register Fx can be considered as 0th element in vector register Wx.
+  if (is_double) {
+    SplatiD(dst, static_cast<VectorRegister>(src), 0);
+  } else {
+    SplatiW(dst, static_cast<VectorRegister>(src), 0);
+  }
+}
+
 void MipsAssembler::LoadConst32(Register rd, int32_t value) {
   if (IsUint<16>(value)) {
     // Use OR with (unsigned) immediate to encode 16b unsigned int.
@@ -4440,6 +4451,106 @@
   CHECK_EQ(misalignment, offset & (kMipsDoublewordSize - 1));
 }
 
+void MipsAssembler::AdjustBaseOffsetAndElementSizeShift(Register& base,
+                                                        int32_t& offset,
+                                                        int& element_size_shift) {
+  // This method is used to adjust the base register, offset and element_size_shift
+  // for a vector load/store when the offset doesn't fit into allowed number of bits.
+  // MSA ld.df and st.df instructions take signed offsets as arguments, but maximum
+  // offset is dependant on the size of the data format df (10-bit offsets for ld.b,
+  // 11-bit for ld.h, 12-bit for ld.w and 13-bit for ld.d).
+  // If element_size_shift is non-negative at entry, it won't be changed, but offset
+  // will be checked for appropriate alignment. If negative at entry, it will be
+  // adjusted based on offset for maximum fit.
+  // It's assumed that `base` is a multiple of 8.
+  CHECK_NE(base, AT);  // Must not overwrite the register `base` while loading `offset`.
+
+  if (element_size_shift >= 0) {
+    CHECK_LE(element_size_shift, TIMES_8);
+    CHECK_GE(JAVASTYLE_CTZ(offset), element_size_shift);
+  } else if (IsAligned<kMipsDoublewordSize>(offset)) {
+    element_size_shift = TIMES_8;
+  } else if (IsAligned<kMipsWordSize>(offset)) {
+    element_size_shift = TIMES_4;
+  } else if (IsAligned<kMipsHalfwordSize>(offset)) {
+    element_size_shift = TIMES_2;
+  } else {
+    element_size_shift = TIMES_1;
+  }
+
+  const int low_len = 10 + element_size_shift;  // How many low bits of `offset` ld.df/st.df
+                                                // will take.
+  int16_t low = offset & ((1 << low_len) - 1);  // Isolate these bits.
+  low -= (low & (1 << (low_len - 1))) << 1;     // Sign-extend these bits.
+  if (low == offset) {
+    return;  // `offset` fits into ld.df/st.df.
+  }
+
+  // First, see if `offset` can be represented as a sum of two or three signed offsets.
+  // This can save an instruction or two.
+
+  // Max int16_t that's a multiple of element size.
+  const int32_t kMaxDeltaForSimpleAdjustment = 0x8000 - (1 << element_size_shift);
+  // Max ld.df/st.df offset that's a multiple of element size.
+  const int32_t kMaxLoadStoreOffset = 0x1ff << element_size_shift;
+  const int32_t kMaxOffsetForSimpleAdjustment = kMaxDeltaForSimpleAdjustment + kMaxLoadStoreOffset;
+  const int32_t kMinOffsetForMediumAdjustment = 2 * kMaxDeltaForSimpleAdjustment;
+  const int32_t kMaxOffsetForMediumAdjustment = kMinOffsetForMediumAdjustment + kMaxLoadStoreOffset;
+
+  if (IsInt<16>(offset)) {
+    Addiu(AT, base, offset);
+    offset = 0;
+  } else if (0 <= offset && offset <= kMaxOffsetForSimpleAdjustment) {
+    Addiu(AT, base, kMaxDeltaForSimpleAdjustment);
+    offset -= kMaxDeltaForSimpleAdjustment;
+  } else if (-kMaxOffsetForSimpleAdjustment <= offset && offset < 0) {
+    Addiu(AT, base, -kMaxDeltaForSimpleAdjustment);
+    offset += kMaxDeltaForSimpleAdjustment;
+  } else if (!IsR6() && 0 <= offset && offset <= kMaxOffsetForMediumAdjustment) {
+    Addiu(AT, base, kMaxDeltaForSimpleAdjustment);
+    if (offset <= kMinOffsetForMediumAdjustment) {
+      Addiu(AT, AT, offset - kMaxDeltaForSimpleAdjustment);
+      offset = 0;
+    } else {
+      Addiu(AT, AT, kMaxDeltaForSimpleAdjustment);
+      offset -= kMinOffsetForMediumAdjustment;
+    }
+  } else if (!IsR6() && -kMaxOffsetForMediumAdjustment <= offset && offset < 0) {
+    Addiu(AT, base, -kMaxDeltaForSimpleAdjustment);
+    if (-kMinOffsetForMediumAdjustment <= offset) {
+      Addiu(AT, AT, offset + kMaxDeltaForSimpleAdjustment);
+      offset = 0;
+    } else {
+      Addiu(AT, AT, -kMaxDeltaForSimpleAdjustment);
+      offset += kMinOffsetForMediumAdjustment;
+    }
+  } else {
+    // 16-bit or smaller parts of `offset`:
+    // |31  hi  16|15  mid  13-10|12-9  low  0|
+    //
+    // Instructions that supply each part as a signed integer addend:
+    // |aui       |addiu         |ld.df/st.df |
+    uint32_t tmp = static_cast<uint32_t>(offset) - low;  // Exclude `low` from the rest of `offset`
+                                                         // (accounts for sign of `low`).
+    tmp += (tmp & (UINT32_C(1) << 15)) << 1;  // Account for sign extension in addiu.
+    int16_t mid = Low16Bits(tmp);
+    int16_t hi = High16Bits(tmp);
+    if (IsR6()) {
+      Aui(AT, base, hi);
+    } else {
+      Lui(AT, hi);
+      Addu(AT, AT, base);
+    }
+    if (mid != 0) {
+      Addiu(AT, AT, mid);
+    }
+    offset = low;
+  }
+  base = AT;
+  CHECK_GE(JAVASTYLE_CTZ(offset), element_size_shift);
+  CHECK(IsInt<10>(offset >> element_size_shift));
+}
+
 void MipsAssembler::LoadFromOffset(LoadOperandType type,
                                    Register reg,
                                    Register base,
@@ -4455,6 +4566,10 @@
   LoadDFromOffset<>(reg, base, offset);
 }
 
+void MipsAssembler::LoadQFromOffset(FRegister reg, Register base, int32_t offset) {
+  LoadQFromOffset<>(reg, base, offset);
+}
+
 void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset,
                              size_t size) {
   MipsManagedRegister dst = m_dst.AsMips();
@@ -4494,6 +4609,10 @@
   StoreDToOffset<>(reg, base, offset);
 }
 
+void MipsAssembler::StoreQToOffset(FRegister reg, Register base, int32_t offset) {
+  StoreQToOffset<>(reg, base, offset);
+}
+
 static dwarf::Reg DWARFReg(Register reg) {
   return dwarf::Reg::MipsCore(static_cast<int>(reg));
 }
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index dd4ce6d..33803bb 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -47,14 +47,16 @@
   kLoadSignedHalfword,
   kLoadUnsignedHalfword,
   kLoadWord,
-  kLoadDoubleword
+  kLoadDoubleword,
+  kLoadQuadword
 };
 
 enum StoreOperandType {
   kStoreByte,
   kStoreHalfword,
   kStoreWord,
-  kStoreDoubleword
+  kStoreDoubleword,
+  kStoreQuadword
 };
 
 // Used to test the values returned by ClassS/ClassD.
@@ -610,6 +612,9 @@
   void IlvrW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
   void IlvrD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
 
+  // Helper for replicating floating point value in all destination elements.
+  void ReplicateFPToVectorRegister(VectorRegister dst, FRegister src, bool is_double);
+
   // Higher level composite instructions.
   void LoadConst32(Register rd, int32_t value);
   void LoadConst64(Register reg_hi, Register reg_lo, int64_t value);
@@ -646,6 +651,9 @@
                            int32_t& offset,
                            bool is_doubleword,
                            bool is_float = false);
+  void AdjustBaseOffsetAndElementSizeShift(Register& base,
+                                           int32_t& offset,
+                                           int& element_size_shift);
 
  private:
   // This will be used as an argument for loads/stores
@@ -793,6 +801,24 @@
   }
 
   template <typename ImplicitNullChecker = NoImplicitNullChecker>
+  void LoadQFromOffset(FRegister reg,
+                       Register base,
+                       int32_t offset,
+                       ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+    int element_size_shift = -1;
+    AdjustBaseOffsetAndElementSizeShift(base, offset, element_size_shift);
+    switch (element_size_shift) {
+      case TIMES_1: LdB(static_cast<VectorRegister>(reg), base, offset); break;
+      case TIMES_2: LdH(static_cast<VectorRegister>(reg), base, offset); break;
+      case TIMES_4: LdW(static_cast<VectorRegister>(reg), base, offset); break;
+      case TIMES_8: LdD(static_cast<VectorRegister>(reg), base, offset); break;
+      default:
+        LOG(FATAL) << "UNREACHABLE";
+    }
+    null_checker();
+  }
+
+  template <typename ImplicitNullChecker = NoImplicitNullChecker>
   void StoreToOffset(StoreOperandType type,
                      Register reg,
                      Register base,
@@ -861,12 +887,32 @@
     }
   }
 
+  template <typename ImplicitNullChecker = NoImplicitNullChecker>
+  void StoreQToOffset(FRegister reg,
+                      Register base,
+                      int32_t offset,
+                      ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+    int element_size_shift = -1;
+    AdjustBaseOffsetAndElementSizeShift(base, offset, element_size_shift);
+    switch (element_size_shift) {
+      case TIMES_1: StB(static_cast<VectorRegister>(reg), base, offset); break;
+      case TIMES_2: StH(static_cast<VectorRegister>(reg), base, offset); break;
+      case TIMES_4: StW(static_cast<VectorRegister>(reg), base, offset); break;
+      case TIMES_8: StD(static_cast<VectorRegister>(reg), base, offset); break;
+      default:
+        LOG(FATAL) << "UNREACHABLE";
+    }
+    null_checker();
+  }
+
   void LoadFromOffset(LoadOperandType type, Register reg, Register base, int32_t offset);
   void LoadSFromOffset(FRegister reg, Register base, int32_t offset);
   void LoadDFromOffset(FRegister reg, Register base, int32_t offset);
+  void LoadQFromOffset(FRegister reg, Register base, int32_t offset);
   void StoreToOffset(StoreOperandType type, Register reg, Register base, int32_t offset);
   void StoreSToOffset(FRegister reg, Register base, int32_t offset);
   void StoreDToOffset(FRegister reg, Register base, int32_t offset);
+  void StoreQToOffset(FRegister reg, Register base, int32_t offset);
 
   // Emit data (e.g. encoded instruction or immediate) to the instruction stream.
   void Emit(uint32_t value);
diff --git a/compiler/utils/mips/assembler_mips32r5_test.cc b/compiler/utils/mips/assembler_mips32r5_test.cc
new file mode 100644
index 0000000..24b09b5
--- /dev/null
+++ b/compiler/utils/mips/assembler_mips32r5_test.cc
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "assembler_mips.h"
+
+#include <map>
+
+#include "base/stl_util.h"
+#include "utils/assembler_test.h"
+
+#define __ GetAssembler()->
+
+namespace art {
+
+struct MIPSCpuRegisterCompare {
+  bool operator()(const mips::Register& a, const mips::Register& b) const {
+    return a < b;
+  }
+};
+
+class AssemblerMIPS32r5Test : public AssemblerTest<mips::MipsAssembler,
+                                                   mips::Register,
+                                                   mips::FRegister,
+                                                   uint32_t,
+                                                   mips::VectorRegister> {
+ public:
+  typedef AssemblerTest<mips::MipsAssembler,
+                        mips::Register,
+                        mips::FRegister,
+                        uint32_t,
+                        mips::VectorRegister> Base;
+
+  AssemblerMIPS32r5Test() :
+    instruction_set_features_(MipsInstructionSetFeatures::FromVariant("mips32r5", nullptr)) {
+  }
+
+ protected:
+  // Get the typically used name for this architecture, e.g., aarch64, x86-64, ...
+  std::string GetArchitectureString() OVERRIDE {
+    return "mips";
+  }
+
+  std::string GetAssemblerParameters() OVERRIDE {
+    return " --no-warn -32 -march=mips32r5 -mmsa";
+  }
+
+  void Pad(std::vector<uint8_t>& data) OVERRIDE {
+    // The GNU linker unconditionally pads the code segment with NOPs to a size that is a multiple
+    // of 16 and there doesn't appear to be a way to suppress this padding. Our assembler doesn't
+    // pad, so, in order for two assembler outputs to match, we need to match the padding as well.
+    // NOP is encoded as four zero bytes on MIPS.
+    size_t pad_size = RoundUp(data.size(), 16u) - data.size();
+    data.insert(data.end(), pad_size, 0);
+  }
+
+  std::string GetDisassembleParameters() OVERRIDE {
+    return " -D -bbinary -mmips:isa32r5";
+  }
+
+  mips::MipsAssembler* CreateAssembler(ArenaAllocator* arena) OVERRIDE {
+    return new (arena) mips::MipsAssembler(arena, instruction_set_features_.get());
+  }
+
+  void SetUpHelpers() OVERRIDE {
+    if (registers_.size() == 0) {
+      registers_.push_back(new mips::Register(mips::ZERO));
+      registers_.push_back(new mips::Register(mips::AT));
+      registers_.push_back(new mips::Register(mips::V0));
+      registers_.push_back(new mips::Register(mips::V1));
+      registers_.push_back(new mips::Register(mips::A0));
+      registers_.push_back(new mips::Register(mips::A1));
+      registers_.push_back(new mips::Register(mips::A2));
+      registers_.push_back(new mips::Register(mips::A3));
+      registers_.push_back(new mips::Register(mips::T0));
+      registers_.push_back(new mips::Register(mips::T1));
+      registers_.push_back(new mips::Register(mips::T2));
+      registers_.push_back(new mips::Register(mips::T3));
+      registers_.push_back(new mips::Register(mips::T4));
+      registers_.push_back(new mips::Register(mips::T5));
+      registers_.push_back(new mips::Register(mips::T6));
+      registers_.push_back(new mips::Register(mips::T7));
+      registers_.push_back(new mips::Register(mips::S0));
+      registers_.push_back(new mips::Register(mips::S1));
+      registers_.push_back(new mips::Register(mips::S2));
+      registers_.push_back(new mips::Register(mips::S3));
+      registers_.push_back(new mips::Register(mips::S4));
+      registers_.push_back(new mips::Register(mips::S5));
+      registers_.push_back(new mips::Register(mips::S6));
+      registers_.push_back(new mips::Register(mips::S7));
+      registers_.push_back(new mips::Register(mips::T8));
+      registers_.push_back(new mips::Register(mips::T9));
+      registers_.push_back(new mips::Register(mips::K0));
+      registers_.push_back(new mips::Register(mips::K1));
+      registers_.push_back(new mips::Register(mips::GP));
+      registers_.push_back(new mips::Register(mips::SP));
+      registers_.push_back(new mips::Register(mips::FP));
+      registers_.push_back(new mips::Register(mips::RA));
+
+      secondary_register_names_.emplace(mips::Register(mips::ZERO), "zero");
+      secondary_register_names_.emplace(mips::Register(mips::AT), "at");
+      secondary_register_names_.emplace(mips::Register(mips::V0), "v0");
+      secondary_register_names_.emplace(mips::Register(mips::V1), "v1");
+      secondary_register_names_.emplace(mips::Register(mips::A0), "a0");
+      secondary_register_names_.emplace(mips::Register(mips::A1), "a1");
+      secondary_register_names_.emplace(mips::Register(mips::A2), "a2");
+      secondary_register_names_.emplace(mips::Register(mips::A3), "a3");
+      secondary_register_names_.emplace(mips::Register(mips::T0), "t0");
+      secondary_register_names_.emplace(mips::Register(mips::T1), "t1");
+      secondary_register_names_.emplace(mips::Register(mips::T2), "t2");
+      secondary_register_names_.emplace(mips::Register(mips::T3), "t3");
+      secondary_register_names_.emplace(mips::Register(mips::T4), "t4");
+      secondary_register_names_.emplace(mips::Register(mips::T5), "t5");
+      secondary_register_names_.emplace(mips::Register(mips::T6), "t6");
+      secondary_register_names_.emplace(mips::Register(mips::T7), "t7");
+      secondary_register_names_.emplace(mips::Register(mips::S0), "s0");
+      secondary_register_names_.emplace(mips::Register(mips::S1), "s1");
+      secondary_register_names_.emplace(mips::Register(mips::S2), "s2");
+      secondary_register_names_.emplace(mips::Register(mips::S3), "s3");
+      secondary_register_names_.emplace(mips::Register(mips::S4), "s4");
+      secondary_register_names_.emplace(mips::Register(mips::S5), "s5");
+      secondary_register_names_.emplace(mips::Register(mips::S6), "s6");
+      secondary_register_names_.emplace(mips::Register(mips::S7), "s7");
+      secondary_register_names_.emplace(mips::Register(mips::T8), "t8");
+      secondary_register_names_.emplace(mips::Register(mips::T9), "t9");
+      secondary_register_names_.emplace(mips::Register(mips::K0), "k0");
+      secondary_register_names_.emplace(mips::Register(mips::K1), "k1");
+      secondary_register_names_.emplace(mips::Register(mips::GP), "gp");
+      secondary_register_names_.emplace(mips::Register(mips::SP), "sp");
+      secondary_register_names_.emplace(mips::Register(mips::FP), "fp");
+      secondary_register_names_.emplace(mips::Register(mips::RA), "ra");
+
+      fp_registers_.push_back(new mips::FRegister(mips::F0));
+      fp_registers_.push_back(new mips::FRegister(mips::F1));
+      fp_registers_.push_back(new mips::FRegister(mips::F2));
+      fp_registers_.push_back(new mips::FRegister(mips::F3));
+      fp_registers_.push_back(new mips::FRegister(mips::F4));
+      fp_registers_.push_back(new mips::FRegister(mips::F5));
+      fp_registers_.push_back(new mips::FRegister(mips::F6));
+      fp_registers_.push_back(new mips::FRegister(mips::F7));
+      fp_registers_.push_back(new mips::FRegister(mips::F8));
+      fp_registers_.push_back(new mips::FRegister(mips::F9));
+      fp_registers_.push_back(new mips::FRegister(mips::F10));
+      fp_registers_.push_back(new mips::FRegister(mips::F11));
+      fp_registers_.push_back(new mips::FRegister(mips::F12));
+      fp_registers_.push_back(new mips::FRegister(mips::F13));
+      fp_registers_.push_back(new mips::FRegister(mips::F14));
+      fp_registers_.push_back(new mips::FRegister(mips::F15));
+      fp_registers_.push_back(new mips::FRegister(mips::F16));
+      fp_registers_.push_back(new mips::FRegister(mips::F17));
+      fp_registers_.push_back(new mips::FRegister(mips::F18));
+      fp_registers_.push_back(new mips::FRegister(mips::F19));
+      fp_registers_.push_back(new mips::FRegister(mips::F20));
+      fp_registers_.push_back(new mips::FRegister(mips::F21));
+      fp_registers_.push_back(new mips::FRegister(mips::F22));
+      fp_registers_.push_back(new mips::FRegister(mips::F23));
+      fp_registers_.push_back(new mips::FRegister(mips::F24));
+      fp_registers_.push_back(new mips::FRegister(mips::F25));
+      fp_registers_.push_back(new mips::FRegister(mips::F26));
+      fp_registers_.push_back(new mips::FRegister(mips::F27));
+      fp_registers_.push_back(new mips::FRegister(mips::F28));
+      fp_registers_.push_back(new mips::FRegister(mips::F29));
+      fp_registers_.push_back(new mips::FRegister(mips::F30));
+      fp_registers_.push_back(new mips::FRegister(mips::F31));
+
+      vec_registers_.push_back(new mips::VectorRegister(mips::W0));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W1));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W2));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W3));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W4));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W5));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W6));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W7));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W8));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W9));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W10));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W11));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W12));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W13));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W14));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W15));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W16));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W17));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W18));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W19));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W20));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W21));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W22));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W23));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W24));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W25));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W26));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W27));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W28));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W29));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W30));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W31));
+    }
+  }
+
+  void TearDown() OVERRIDE {
+    AssemblerTest::TearDown();
+    STLDeleteElements(&registers_);
+    STLDeleteElements(&fp_registers_);
+    STLDeleteElements(&vec_registers_);
+  }
+
+  std::vector<mips::Register*> GetRegisters() OVERRIDE {
+    return registers_;
+  }
+
+  std::vector<mips::FRegister*> GetFPRegisters() OVERRIDE {
+    return fp_registers_;
+  }
+
+  std::vector<mips::VectorRegister*> GetVectorRegisters() OVERRIDE {
+    return vec_registers_;
+  }
+
+  uint32_t CreateImmediate(int64_t imm_value) OVERRIDE {
+    return imm_value;
+  }
+
+  std::string GetSecondaryRegisterName(const mips::Register& reg) OVERRIDE {
+    CHECK(secondary_register_names_.find(reg) != secondary_register_names_.end());
+    return secondary_register_names_[reg];
+  }
+
+  std::string RepeatInsn(size_t count, const std::string& insn) {
+    std::string result;
+    for (; count != 0u; --count) {
+      result += insn;
+    }
+    return result;
+  }
+
+ private:
+  std::vector<mips::Register*> registers_;
+  std::map<mips::Register, std::string, MIPSCpuRegisterCompare> secondary_register_names_;
+
+  std::vector<mips::FRegister*> fp_registers_;
+  std::vector<mips::VectorRegister*> vec_registers_;
+  std::unique_ptr<const MipsInstructionSetFeatures> instruction_set_features_;
+};
+
+TEST_F(AssemblerMIPS32r5Test, Toolchain) {
+  EXPECT_TRUE(CheckTools());
+}
+
+TEST_F(AssemblerMIPS32r5Test, LoadQFromOffset) {
+  __ LoadQFromOffset(mips::F0, mips::A0, 0);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4);
+  __ LoadQFromOffset(mips::F0, mips::A0, 8);
+  __ LoadQFromOffset(mips::F0, mips::A0, 511);
+  __ LoadQFromOffset(mips::F0, mips::A0, 512);
+  __ LoadQFromOffset(mips::F0, mips::A0, 513);
+  __ LoadQFromOffset(mips::F0, mips::A0, 514);
+  __ LoadQFromOffset(mips::F0, mips::A0, 516);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1022);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1024);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1025);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1026);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1028);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2044);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2048);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2049);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2050);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2052);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4088);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4096);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4097);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4098);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4100);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4104);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x7FFC);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x8000);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x10000);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x12345678);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x12350078);
+  __ LoadQFromOffset(mips::F0, mips::A0, -256);
+  __ LoadQFromOffset(mips::F0, mips::A0, -511);
+  __ LoadQFromOffset(mips::F0, mips::A0, -513);
+  __ LoadQFromOffset(mips::F0, mips::A0, -1022);
+  __ LoadQFromOffset(mips::F0, mips::A0, -1026);
+  __ LoadQFromOffset(mips::F0, mips::A0, -2044);
+  __ LoadQFromOffset(mips::F0, mips::A0, -2052);
+  __ LoadQFromOffset(mips::F0, mips::A0, -4096);
+  __ LoadQFromOffset(mips::F0, mips::A0, -4104);
+  __ LoadQFromOffset(mips::F0, mips::A0, -32768);
+  __ LoadQFromOffset(mips::F0, mips::A0, -36856);
+  __ LoadQFromOffset(mips::F0, mips::A0, 36856);
+  __ LoadQFromOffset(mips::F0, mips::A0, -69608);
+  __ LoadQFromOffset(mips::F0, mips::A0, 69608);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0xABCDEF00);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x7FFFABCD);
+
+  const char* expected =
+      "ld.d $w0, 0($a0)\n"
+      "ld.b $w0, 1($a0)\n"
+      "ld.h $w0, 2($a0)\n"
+      "ld.w $w0, 4($a0)\n"
+      "ld.d $w0, 8($a0)\n"
+      "ld.b $w0, 511($a0)\n"
+      "ld.d $w0, 512($a0)\n"
+      "addiu $at, $a0, 513\n"
+      "ld.b $w0, 0($at)\n"
+      "ld.h $w0, 514($a0)\n"
+      "ld.w $w0, 516($a0)\n"
+      "ld.h $w0, 1022($a0)\n"
+      "ld.d $w0, 1024($a0)\n"
+      "addiu $at, $a0, 1025\n"
+      "ld.b $w0, 0($at)\n"
+      "addiu $at, $a0, 1026\n"
+      "ld.h $w0, 0($at)\n"
+      "ld.w $w0, 1028($a0)\n"
+      "ld.w $w0, 2044($a0)\n"
+      "ld.d $w0, 2048($a0)\n"
+      "addiu $at, $a0, 2049\n"
+      "ld.b $w0, 0($at)\n"
+      "addiu $at, $a0, 2050\n"
+      "ld.h $w0, 0($at)\n"
+      "addiu $at, $a0, 2052\n"
+      "ld.w $w0, 0($at)\n"
+      "ld.d $w0, 4088($a0)\n"
+      "addiu $at, $a0, 4096\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, 4097\n"
+      "ld.b $w0, 0($at)\n"
+      "addiu $at, $a0, 4098\n"
+      "ld.h $w0, 0($at)\n"
+      "addiu $at, $a0, 4100\n"
+      "ld.w $w0, 0($at)\n"
+      "addiu $at, $a0, 4104\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, 0x7FFC\n"
+      "ld.w $w0, 0($at)\n"
+      "addiu $at, $a0, 0x7FF8\n"
+      "ld.d $w0, 8($at)\n"
+      "addiu $at, $a0, 32760\n"
+      "addiu $at, $at, 32760\n"
+      "ld.d $w0, 16($at)\n"
+      "lui $at, 4660\n"
+      "addu $at, $at, $a0\n"
+      "addiu $at, $at, 24576\n"
+      "ld.d $w0, -2440($at) # 0xF678\n"
+      "lui $at, 4661\n"
+      "addu $at, $at, $a0\n"
+      "ld.d $w0, 120($at)\n"
+      "ld.d $w0, -256($a0)\n"
+      "ld.b $w0, -511($a0)\n"
+      "addiu $at, $a0, -513\n"
+      "ld.b $w0, 0($at)\n"
+      "ld.h $w0, -1022($a0)\n"
+      "addiu $at, $a0, -1026\n"
+      "ld.h $w0, 0($at)\n"
+      "ld.w $w0, -2044($a0)\n"
+      "addiu $at, $a0, -2052\n"
+      "ld.w $w0, 0($at)\n"
+      "ld.d $w0, -4096($a0)\n"
+      "addiu $at, $a0, -4104\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, -32768\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, -32760\n"
+      "addiu $at, $at, -4096\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, 32760\n"
+      "addiu $at, $at, 4096\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, -32760\n"
+      "addiu $at, $at, -32760\n"
+      "ld.d $w0, -4088($at)\n"
+      "addiu $at, $a0, 32760\n"
+      "addiu $at, $at, 32760\n"
+      "ld.d $w0, 4088($at)\n"
+      "lui $at, 0xABCE\n"
+      "addu $at, $at, $a0\n"
+      "addiu $at, $at, -8192 # 0xE000\n"
+      "ld.d $w0, 0xF00($at)\n"
+      "lui $at, 0x8000\n"
+      "addu $at, $at, $a0\n"
+      "addiu $at, $at, -21504 # 0xAC00\n"
+      "ld.b $w0, -51($at) # 0xFFCD\n";
+  DriverStr(expected, "LoadQFromOffset");
+}
+
+TEST_F(AssemblerMIPS32r5Test, StoreQToOffset) {
+  __ StoreQToOffset(mips::F0, mips::A0, 0);
+  __ StoreQToOffset(mips::F0, mips::A0, 1);
+  __ StoreQToOffset(mips::F0, mips::A0, 2);
+  __ StoreQToOffset(mips::F0, mips::A0, 4);
+  __ StoreQToOffset(mips::F0, mips::A0, 8);
+  __ StoreQToOffset(mips::F0, mips::A0, 511);
+  __ StoreQToOffset(mips::F0, mips::A0, 512);
+  __ StoreQToOffset(mips::F0, mips::A0, 513);
+  __ StoreQToOffset(mips::F0, mips::A0, 514);
+  __ StoreQToOffset(mips::F0, mips::A0, 516);
+  __ StoreQToOffset(mips::F0, mips::A0, 1022);
+  __ StoreQToOffset(mips::F0, mips::A0, 1024);
+  __ StoreQToOffset(mips::F0, mips::A0, 1025);
+  __ StoreQToOffset(mips::F0, mips::A0, 1026);
+  __ StoreQToOffset(mips::F0, mips::A0, 1028);
+  __ StoreQToOffset(mips::F0, mips::A0, 2044);
+  __ StoreQToOffset(mips::F0, mips::A0, 2048);
+  __ StoreQToOffset(mips::F0, mips::A0, 2049);
+  __ StoreQToOffset(mips::F0, mips::A0, 2050);
+  __ StoreQToOffset(mips::F0, mips::A0, 2052);
+  __ StoreQToOffset(mips::F0, mips::A0, 4088);
+  __ StoreQToOffset(mips::F0, mips::A0, 4096);
+  __ StoreQToOffset(mips::F0, mips::A0, 4097);
+  __ StoreQToOffset(mips::F0, mips::A0, 4098);
+  __ StoreQToOffset(mips::F0, mips::A0, 4100);
+  __ StoreQToOffset(mips::F0, mips::A0, 4104);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x7FFC);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x8000);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x10000);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x12345678);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x12350078);
+  __ StoreQToOffset(mips::F0, mips::A0, -256);
+  __ StoreQToOffset(mips::F0, mips::A0, -511);
+  __ StoreQToOffset(mips::F0, mips::A0, -513);
+  __ StoreQToOffset(mips::F0, mips::A0, -1022);
+  __ StoreQToOffset(mips::F0, mips::A0, -1026);
+  __ StoreQToOffset(mips::F0, mips::A0, -2044);
+  __ StoreQToOffset(mips::F0, mips::A0, -2052);
+  __ StoreQToOffset(mips::F0, mips::A0, -4096);
+  __ StoreQToOffset(mips::F0, mips::A0, -4104);
+  __ StoreQToOffset(mips::F0, mips::A0, -32768);
+  __ StoreQToOffset(mips::F0, mips::A0, -36856);
+  __ StoreQToOffset(mips::F0, mips::A0, 36856);
+  __ StoreQToOffset(mips::F0, mips::A0, -69608);
+  __ StoreQToOffset(mips::F0, mips::A0, 69608);
+  __ StoreQToOffset(mips::F0, mips::A0, 0xABCDEF00);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x7FFFABCD);
+
+  const char* expected =
+      "st.d $w0, 0($a0)\n"
+      "st.b $w0, 1($a0)\n"
+      "st.h $w0, 2($a0)\n"
+      "st.w $w0, 4($a0)\n"
+      "st.d $w0, 8($a0)\n"
+      "st.b $w0, 511($a0)\n"
+      "st.d $w0, 512($a0)\n"
+      "addiu $at, $a0, 513\n"
+      "st.b $w0, 0($at)\n"
+      "st.h $w0, 514($a0)\n"
+      "st.w $w0, 516($a0)\n"
+      "st.h $w0, 1022($a0)\n"
+      "st.d $w0, 1024($a0)\n"
+      "addiu $at, $a0, 1025\n"
+      "st.b $w0, 0($at)\n"
+      "addiu $at, $a0, 1026\n"
+      "st.h $w0, 0($at)\n"
+      "st.w $w0, 1028($a0)\n"
+      "st.w $w0, 2044($a0)\n"
+      "st.d $w0, 2048($a0)\n"
+      "addiu $at, $a0, 2049\n"
+      "st.b $w0, 0($at)\n"
+      "addiu $at, $a0, 2050\n"
+      "st.h $w0, 0($at)\n"
+      "addiu $at, $a0, 2052\n"
+      "st.w $w0, 0($at)\n"
+      "st.d $w0, 4088($a0)\n"
+      "addiu $at, $a0, 4096\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, 4097\n"
+      "st.b $w0, 0($at)\n"
+      "addiu $at, $a0, 4098\n"
+      "st.h $w0, 0($at)\n"
+      "addiu $at, $a0, 4100\n"
+      "st.w $w0, 0($at)\n"
+      "addiu $at, $a0, 4104\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, 0x7FFC\n"
+      "st.w $w0, 0($at)\n"
+      "addiu $at, $a0, 0x7FF8\n"
+      "st.d $w0, 8($at)\n"
+      "addiu $at, $a0, 32760\n"
+      "addiu $at, $at, 32760\n"
+      "st.d $w0, 16($at)\n"
+      "lui $at, 4660\n"
+      "addu $at, $at, $a0\n"
+      "addiu $at, $at, 24576\n"
+      "st.d $w0, -2440($at) # 0xF678\n"
+      "lui $at, 4661\n"
+      "addu $at, $at, $a0\n"
+      "st.d $w0, 120($at)\n"
+      "st.d $w0, -256($a0)\n"
+      "st.b $w0, -511($a0)\n"
+      "addiu $at, $a0, -513\n"
+      "st.b $w0, 0($at)\n"
+      "st.h $w0, -1022($a0)\n"
+      "addiu $at, $a0, -1026\n"
+      "st.h $w0, 0($at)\n"
+      "st.w $w0, -2044($a0)\n"
+      "addiu $at, $a0, -2052\n"
+      "st.w $w0, 0($at)\n"
+      "st.d $w0, -4096($a0)\n"
+      "addiu $at, $a0, -4104\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, -32768\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, -32760\n"
+      "addiu $at, $at, -4096\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, 32760\n"
+      "addiu $at, $at, 4096\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, -32760\n"
+      "addiu $at, $at, -32760\n"
+      "st.d $w0, -4088($at)\n"
+      "addiu $at, $a0, 32760\n"
+      "addiu $at, $at, 32760\n"
+      "st.d $w0, 4088($at)\n"
+      "lui $at, 0xABCE\n"
+      "addu $at, $at, $a0\n"
+      "addiu $at, $at, -8192 # 0xE000\n"
+      "st.d $w0, 0xF00($at)\n"
+      "lui $at, 0x8000\n"
+      "addu $at, $at, $a0\n"
+      "addiu $at, $at, -21504 # 0xAC00\n"
+      "st.b $w0, -51($at) # 0xFFCD\n";
+  DriverStr(expected, "StoreQToOffset");
+}
+
+#undef __
+}  // namespace art
diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc
index d464260..6ee2a5c 100644
--- a/compiler/utils/mips/assembler_mips32r6_test.cc
+++ b/compiler/utils/mips/assembler_mips32r6_test.cc
@@ -627,6 +627,124 @@
   DriverStr(expected, "LoadDFromOffset");
 }
 
+TEST_F(AssemblerMIPS32r6Test, LoadQFromOffset) {
+  __ LoadQFromOffset(mips::F0, mips::A0, 0);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4);
+  __ LoadQFromOffset(mips::F0, mips::A0, 8);
+  __ LoadQFromOffset(mips::F0, mips::A0, 511);
+  __ LoadQFromOffset(mips::F0, mips::A0, 512);
+  __ LoadQFromOffset(mips::F0, mips::A0, 513);
+  __ LoadQFromOffset(mips::F0, mips::A0, 514);
+  __ LoadQFromOffset(mips::F0, mips::A0, 516);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1022);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1024);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1025);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1026);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1028);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2044);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2048);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2049);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2050);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2052);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4088);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4096);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4097);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4098);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4100);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4104);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x7FFC);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x8000);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x10000);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x12345678);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x12350078);
+  __ LoadQFromOffset(mips::F0, mips::A0, -256);
+  __ LoadQFromOffset(mips::F0, mips::A0, -511);
+  __ LoadQFromOffset(mips::F0, mips::A0, -513);
+  __ LoadQFromOffset(mips::F0, mips::A0, -1022);
+  __ LoadQFromOffset(mips::F0, mips::A0, -1026);
+  __ LoadQFromOffset(mips::F0, mips::A0, -2044);
+  __ LoadQFromOffset(mips::F0, mips::A0, -2052);
+  __ LoadQFromOffset(mips::F0, mips::A0, -4096);
+  __ LoadQFromOffset(mips::F0, mips::A0, -4104);
+  __ LoadQFromOffset(mips::F0, mips::A0, -32768);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0xABCDEF00);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x7FFFABCD);
+
+  const char* expected =
+      "ld.d $w0, 0($a0)\n"
+      "ld.b $w0, 1($a0)\n"
+      "ld.h $w0, 2($a0)\n"
+      "ld.w $w0, 4($a0)\n"
+      "ld.d $w0, 8($a0)\n"
+      "ld.b $w0, 511($a0)\n"
+      "ld.d $w0, 512($a0)\n"
+      "addiu $at, $a0, 513\n"
+      "ld.b $w0, 0($at)\n"
+      "ld.h $w0, 514($a0)\n"
+      "ld.w $w0, 516($a0)\n"
+      "ld.h $w0, 1022($a0)\n"
+      "ld.d $w0, 1024($a0)\n"
+      "addiu $at, $a0, 1025\n"
+      "ld.b $w0, 0($at)\n"
+      "addiu $at, $a0, 1026\n"
+      "ld.h $w0, 0($at)\n"
+      "ld.w $w0, 1028($a0)\n"
+      "ld.w $w0, 2044($a0)\n"
+      "ld.d $w0, 2048($a0)\n"
+      "addiu $at, $a0, 2049\n"
+      "ld.b $w0, 0($at)\n"
+      "addiu $at, $a0, 2050\n"
+      "ld.h $w0, 0($at)\n"
+      "addiu $at, $a0, 2052\n"
+      "ld.w $w0, 0($at)\n"
+      "ld.d $w0, 4088($a0)\n"
+      "addiu $at, $a0, 4096\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, 4097\n"
+      "ld.b $w0, 0($at)\n"
+      "addiu $at, $a0, 4098\n"
+      "ld.h $w0, 0($at)\n"
+      "addiu $at, $a0, 4100\n"
+      "ld.w $w0, 0($at)\n"
+      "addiu $at, $a0, 4104\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, 0x7FFC\n"
+      "ld.w $w0, 0($at)\n"
+      "addiu $at, $a0, 0x7FF8\n"
+      "ld.d $w0, 8($at)\n"
+      "aui $at, $a0, 0x1\n"
+      "ld.d $w0, 0($at)\n"
+      "aui $at, $a0, 0x1234\n"
+      "addiu $at, $at, 0x6000\n"
+      "ld.d $w0, -2440($at) # 0xF678\n"
+      "aui $at, $a0, 0x1235\n"
+      "ld.d $w0, 0x78($at)\n"
+      "ld.d $w0, -256($a0)\n"
+      "ld.b $w0, -511($a0)\n"
+      "addiu $at, $a0, -513\n"
+      "ld.b $w0, 0($at)\n"
+      "ld.h $w0, -1022($a0)\n"
+      "addiu $at, $a0, -1026\n"
+      "ld.h $w0, 0($at)\n"
+      "ld.w $w0, -2044($a0)\n"
+      "addiu $at, $a0, -2052\n"
+      "ld.w $w0, 0($at)\n"
+      "ld.d $w0, -4096($a0)\n"
+      "addiu $at, $a0, -4104\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, -32768\n"
+      "ld.d $w0, 0($at)\n"
+      "aui $at, $a0, 0xABCE\n"
+      "addiu $at, $at, -8192 # 0xE000\n"
+      "ld.d $w0, 0xF00($at)\n"
+      "aui $at, $a0, 0x8000\n"
+      "addiu $at, $at, -21504 # 0xAC00\n"
+      "ld.b $w0, -51($at) # 0xFFCD\n";
+  DriverStr(expected, "LoadQFromOffset");
+}
+
 TEST_F(AssemblerMIPS32r6Test, StoreDToOffset) {
   __ StoreDToOffset(mips::F0, mips::A0, -0x8000);
   __ StoreDToOffset(mips::F0, mips::A0, +0);
@@ -711,6 +829,124 @@
   DriverStr(expected, "StoreDToOffset");
 }
 
+TEST_F(AssemblerMIPS32r6Test, StoreQToOffset) {
+  __ StoreQToOffset(mips::F0, mips::A0, 0);
+  __ StoreQToOffset(mips::F0, mips::A0, 1);
+  __ StoreQToOffset(mips::F0, mips::A0, 2);
+  __ StoreQToOffset(mips::F0, mips::A0, 4);
+  __ StoreQToOffset(mips::F0, mips::A0, 8);
+  __ StoreQToOffset(mips::F0, mips::A0, 511);
+  __ StoreQToOffset(mips::F0, mips::A0, 512);
+  __ StoreQToOffset(mips::F0, mips::A0, 513);
+  __ StoreQToOffset(mips::F0, mips::A0, 514);
+  __ StoreQToOffset(mips::F0, mips::A0, 516);
+  __ StoreQToOffset(mips::F0, mips::A0, 1022);
+  __ StoreQToOffset(mips::F0, mips::A0, 1024);
+  __ StoreQToOffset(mips::F0, mips::A0, 1025);
+  __ StoreQToOffset(mips::F0, mips::A0, 1026);
+  __ StoreQToOffset(mips::F0, mips::A0, 1028);
+  __ StoreQToOffset(mips::F0, mips::A0, 2044);
+  __ StoreQToOffset(mips::F0, mips::A0, 2048);
+  __ StoreQToOffset(mips::F0, mips::A0, 2049);
+  __ StoreQToOffset(mips::F0, mips::A0, 2050);
+  __ StoreQToOffset(mips::F0, mips::A0, 2052);
+  __ StoreQToOffset(mips::F0, mips::A0, 4088);
+  __ StoreQToOffset(mips::F0, mips::A0, 4096);
+  __ StoreQToOffset(mips::F0, mips::A0, 4097);
+  __ StoreQToOffset(mips::F0, mips::A0, 4098);
+  __ StoreQToOffset(mips::F0, mips::A0, 4100);
+  __ StoreQToOffset(mips::F0, mips::A0, 4104);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x7FFC);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x8000);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x10000);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x12345678);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x12350078);
+  __ StoreQToOffset(mips::F0, mips::A0, -256);
+  __ StoreQToOffset(mips::F0, mips::A0, -511);
+  __ StoreQToOffset(mips::F0, mips::A0, -513);
+  __ StoreQToOffset(mips::F0, mips::A0, -1022);
+  __ StoreQToOffset(mips::F0, mips::A0, -1026);
+  __ StoreQToOffset(mips::F0, mips::A0, -2044);
+  __ StoreQToOffset(mips::F0, mips::A0, -2052);
+  __ StoreQToOffset(mips::F0, mips::A0, -4096);
+  __ StoreQToOffset(mips::F0, mips::A0, -4104);
+  __ StoreQToOffset(mips::F0, mips::A0, -32768);
+  __ StoreQToOffset(mips::F0, mips::A0, 0xABCDEF00);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x7FFFABCD);
+
+  const char* expected =
+      "st.d $w0, 0($a0)\n"
+      "st.b $w0, 1($a0)\n"
+      "st.h $w0, 2($a0)\n"
+      "st.w $w0, 4($a0)\n"
+      "st.d $w0, 8($a0)\n"
+      "st.b $w0, 511($a0)\n"
+      "st.d $w0, 512($a0)\n"
+      "addiu $at, $a0, 513\n"
+      "st.b $w0, 0($at)\n"
+      "st.h $w0, 514($a0)\n"
+      "st.w $w0, 516($a0)\n"
+      "st.h $w0, 1022($a0)\n"
+      "st.d $w0, 1024($a0)\n"
+      "addiu $at, $a0, 1025\n"
+      "st.b $w0, 0($at)\n"
+      "addiu $at, $a0, 1026\n"
+      "st.h $w0, 0($at)\n"
+      "st.w $w0, 1028($a0)\n"
+      "st.w $w0, 2044($a0)\n"
+      "st.d $w0, 2048($a0)\n"
+      "addiu $at, $a0, 2049\n"
+      "st.b $w0, 0($at)\n"
+      "addiu $at, $a0, 2050\n"
+      "st.h $w0, 0($at)\n"
+      "addiu $at, $a0, 2052\n"
+      "st.w $w0, 0($at)\n"
+      "st.d $w0, 4088($a0)\n"
+      "addiu $at, $a0, 4096\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, 4097\n"
+      "st.b $w0, 0($at)\n"
+      "addiu $at, $a0, 4098\n"
+      "st.h $w0, 0($at)\n"
+      "addiu $at, $a0, 4100\n"
+      "st.w $w0, 0($at)\n"
+      "addiu $at, $a0, 4104\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, 0x7FFC\n"
+      "st.w $w0, 0($at)\n"
+      "addiu $at, $a0, 0x7FF8\n"
+      "st.d $w0, 8($at)\n"
+      "aui $at, $a0, 0x1\n"
+      "st.d $w0, 0($at)\n"
+      "aui $at, $a0, 0x1234\n"
+      "addiu $at, $at, 0x6000\n"
+      "st.d $w0, -2440($at) # 0xF678\n"
+      "aui $at, $a0, 0x1235\n"
+      "st.d $w0, 0x78($at)\n"
+      "st.d $w0, -256($a0)\n"
+      "st.b $w0, -511($a0)\n"
+      "addiu $at, $a0, -513\n"
+      "st.b $w0, 0($at)\n"
+      "st.h $w0, -1022($a0)\n"
+      "addiu $at, $a0, -1026\n"
+      "st.h $w0, 0($at)\n"
+      "st.w $w0, -2044($a0)\n"
+      "addiu $at, $a0, -2052\n"
+      "st.w $w0, 0($at)\n"
+      "st.d $w0, -4096($a0)\n"
+      "addiu $at, $a0, -4104\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, -32768\n"
+      "st.d $w0, 0($at)\n"
+      "aui $at, $a0, 0xABCE\n"
+      "addiu $at, $at, -8192 # 0xE000\n"
+      "st.d $w0, 0xF00($at)\n"
+      "aui $at, $a0, 0x8000\n"
+      "addiu $at, $at, -21504 # 0xAC00\n"
+      "st.b $w0, -51($at) # 0xFFCD\n";
+  DriverStr(expected, "StoreQToOffset");
+}
+
 TEST_F(AssemblerMIPS32r6Test, LoadFarthestNearLabelAddress) {
   mips::MipsLabel label;
   __ LoadLabelAddress(mips::V0, mips::ZERO, &label);
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 7e616a7..686da21 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -33,7 +33,7 @@
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread.h"
-#include "utils/atomic_method_ref_map-inl.h"
+#include "utils/atomic_dex_ref_map-inl.h"
 #include "verifier/method_verifier-inl.h"
 
 namespace art {
@@ -97,9 +97,9 @@
     callbacks_->SetVerifierDeps(nullptr);
     // Clear entries in the verification results to avoid hitting a DCHECK that
     // we always succeed inserting a new entry after verifying.
-    AtomicMethodRefMap<const VerifiedMethod*>* map =
+    AtomicDexRefMap<const VerifiedMethod*>* map =
         &compiler_driver_->GetVerificationResults()->atomic_verified_methods_;
-    map->Visit([](const MethodReference& ref ATTRIBUTE_UNUSED, const VerifiedMethod* method) {
+    map->Visit([](const DexFileReference& ref ATTRIBUTE_UNUSED, const VerifiedMethod* method) {
       delete method;
     });
     map->ClearEntries();
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index ca0bae1..113bdb5 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -49,6 +49,7 @@
 #include "base/timing_logger.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
+#include "class_loader_context.h"
 #include "compiler.h"
 #include "compiler_callbacks.h"
 #include "debug/elf_debug_writer.h"
@@ -97,6 +98,9 @@
 static constexpr size_t kDefaultMinDexFilesForSwap = 2;
 static constexpr size_t kDefaultMinDexFileCumulativeSizeForSwap = 20 * MB;
 
+// Compiler filter override for very large apps.
+static constexpr CompilerFilter::Filter kLargeAppFilter = CompilerFilter::kVerify;
+
 static int original_argc;
 static char** original_argv;
 
@@ -376,7 +380,7 @@
   UsageError("      Default: %zu", kDefaultMinDexFilesForSwap);
   UsageError("");
   UsageError("  --very-large-app-threshold=<size>: specifies the minimum total dex file size in");
-  UsageError("      bytes to consider the input \"very large\" and punt on the compilation.");
+  UsageError("      bytes to consider the input \"very large\" and reduce compilation done.");
   UsageError("      Example: --very-large-app-threshold=100000000");
   UsageError("");
   UsageError("  --app-image-fd=<file-descriptor>: specify output file descriptor for app image.");
@@ -400,6 +404,27 @@
   UsageError("");
   UsageError("  --classpath-dir=<directory-path>: directory used to resolve relative class paths.");
   UsageError("");
+  UsageError("  --class-loader-context=<string spec>: a string specifying the intended");
+  UsageError("      runtime loading context for the compiled dex files.");
+  UsageError("      ");
+  UsageError("      It describes how the class loader chain should be built in order to ensure");
+  UsageError("      classes are resolved during dex2aot as they would be resolved at runtime.");
+  UsageError("      This spec will be encoded in the oat file. If at runtime the dex file is");
+  UsageError("      loaded in a different context, the oat file will be rejected.");
+  UsageError("      ");
+  UsageError("      The chain is interpreted in the natural 'parent order', meaning that class");
+  UsageError("      loader 'i+1' will be the parent of class loader 'i'.");
+  UsageError("      The compilation sources will be added to the classpath of the last class");
+  UsageError("      loader. This allows the compiled dex files to be loaded at runtime in");
+  UsageError("      a class loader that contains other dex files as well (e.g. shared libraries).");
+  UsageError("      ");
+  UsageError("      Note that the compiler will be tolerant if the source dex files specified");
+  UsageError("      with --dex-file are found in the classpath. The source dex files will be");
+  UsageError("      removed from any class loader's classpath possibly resulting in empty");
+  UsageError("      class loaders.");
+  UsageError("      ");
+  UsageError("      Example: --classloader-spec=PCL[lib1.dex:lib2.dex];DLC[lib3.dex]");
+  UsageError("");
   std::cerr << "See log for usage error information\n";
   exit(EXIT_FAILURE);
 }
@@ -1271,6 +1296,12 @@
         force_determinism_ = true;
       } else if (option.starts_with("--classpath-dir=")) {
         classpath_dir_ = option.substr(strlen("--classpath-dir=")).data();
+      } else if (option.starts_with("--class-loader-context=")) {
+        class_loader_context_ = ClassLoaderContext::Create(
+            option.substr(strlen("--class-loader-context=")).data());
+        if (class_loader_context_== nullptr) {
+          Usage("Option --class-loader-context has an incorrect format: %s", option.data());
+        }
       } else if (!compiler_options_->ParseCompilerOption(option, Usage)) {
         Usage("Unknown argument %s", option.data());
       }
@@ -1485,7 +1516,10 @@
       return dex2oat::ReturnCode::kOther;
     }
 
-    verification_results_.reset(new VerificationResults(compiler_options_.get()));
+    if (CompilerFilter::IsAnyCompilationEnabled(compiler_options_->GetCompilerFilter())) {
+      // Only modes with compilation require verification results.
+      verification_results_.reset(new VerificationResults(compiler_options_.get()));
+    }
     callbacks_.reset(new QuickCompilerCallbacks(
         verification_results_.get(),
         IsBootImage() ?
@@ -1542,25 +1576,45 @@
       }
 
       // Open dex files for class path.
-      std::vector<std::string> class_path_locations =
-          GetClassPathLocations(runtime_->GetClassPathString());
-      OpenClassPathFiles(class_path_locations,
-                         &class_path_files_,
-                         &opened_oat_files_,
-                         runtime_->GetInstructionSet(),
-                         classpath_dir_);
-
-      // Store the classpath we have right now.
-      std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_);
-      std::string encoded_class_path;
-      if (class_path_locations.size() == 1 &&
-          class_path_locations[0] == OatFile::kSpecialSharedLibrary) {
-        // When passing the special shared library as the classpath, it is the only path.
-        encoded_class_path = OatFile::kSpecialSharedLibrary;
-      } else {
-        encoded_class_path = OatFile::EncodeDexFileDependencies(class_path_files, classpath_dir_);
+      if (class_loader_context_ == nullptr) {
+        // TODO(calin): Temporary workaround while we transition to use
+        // --class-loader-context instead of --runtime-arg -cp
+        if (runtime_->GetClassPathString().empty()) {
+          class_loader_context_ = std::unique_ptr<ClassLoaderContext>(
+              new ClassLoaderContext());
+        } else {
+          std::string spec = runtime_->GetClassPathString() == OatFile::kSpecialSharedLibrary
+              ? OatFile::kSpecialSharedLibrary
+              : "PCL[" + runtime_->GetClassPathString() + "]";
+          class_loader_context_ = ClassLoaderContext::Create(spec);
+        }
       }
-      key_value_store_->Put(OatHeader::kClassPathKey, encoded_class_path);
+      CHECK(class_loader_context_ != nullptr);
+      DCHECK_EQ(oat_writers_.size(), 1u);
+
+      // Note: Ideally we would reject context where the source dex files are also
+      // specified in the classpath (as it doesn't make sense). However this is currently
+      // needed for non-prebuild tests and benchmarks which expects on the fly compilation.
+      // Also, for secondary dex files we do not have control on the actual classpath.
+      // Instead of aborting, remove all the source location from the context classpaths.
+      if (class_loader_context_->RemoveLocationsFromClassPaths(
+            oat_writers_[0]->GetSourceLocations())) {
+        LOG(WARNING) << "The source files to be compiled are also in the classpath.";
+      }
+
+      // We need to open the dex files before encoding the context in the oat file.
+      // (because the encoding adds the dex checksum...)
+      // TODO(calin): consider redesigning this so we don't have to open the dex files before
+      // creating the actual class loader.
+      if (!class_loader_context_->OpenDexFiles(runtime_->GetInstructionSet(), classpath_dir_)) {
+        // Do not abort if we couldn't open files from the classpath. They might be
+        // apks without dex files and right now are opening flow will fail them.
+        LOG(WARNING) << "Failed to open classpath dex files";
+      }
+
+      // Store the class loader context in the oat header.
+      key_value_store_->Put(OatHeader::kClassPathKey,
+                            class_loader_context_->EncodeContextForOatFile(classpath_dir_));
     }
 
     // Now that we have finalized key_value_store_, start writing the oat file.
@@ -1623,14 +1677,13 @@
 
     // If we need to downgrade the compiler-filter for size reasons, do that check now.
     if (!IsBootImage() && IsVeryLarge(dex_files_)) {
-      if (!CompilerFilter::IsAsGoodAs(CompilerFilter::kExtract,
-                                      compiler_options_->GetCompilerFilter())) {
-        LOG(INFO) << "Very large app, downgrading to extract.";
+      if (!CompilerFilter::IsAsGoodAs(kLargeAppFilter, compiler_options_->GetCompilerFilter())) {
+        LOG(INFO) << "Very large app, downgrading to verify.";
         // Note: this change won't be reflected in the key-value store, as that had to be
         //       finalized before loading the dex files. This setup is currently required
         //       to get the size from the DexFile objects.
         // TODO: refactor. b/29790079
-        compiler_options_->SetCompilerFilter(CompilerFilter::kExtract);
+        compiler_options_->SetCompilerFilter(kLargeAppFilter);
       }
     }
 
@@ -1656,17 +1709,7 @@
       if (kSaveDexInput) {
         SaveDexInput();
       }
-
-      // Handle and ClassLoader creation needs to come after Runtime::Create.
-      ScopedObjectAccess soa(self);
-
-      // Classpath: first the class-path given.
-      std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_);
-
-      // Then the dex files we'll compile. Thus we'll resolve the class-path first.
-      class_path_files.insert(class_path_files.end(), dex_files_.begin(), dex_files_.end());
-
-      class_loader_ = class_linker->CreatePathClassLoader(self, class_path_files);
+      class_loader_ = class_loader_context_->CreateClassLoader(dex_files_);
     }
 
     // Ensure opened dex files are writable for dex-to-dex transformations.
@@ -1692,7 +1735,11 @@
       }
       // Pre-register dex files so that we can access verification results without locks during
       // compilation and verification.
-      verification_results_->AddDexFile(dex_file);
+      if (verification_results_ != nullptr) {
+        // Verification results are only required for modes that have any compilation. Avoid
+        // adding the dex files if possible to prevent allocating large arrays.
+        verification_results_->AddDexFile(dex_file);
+      }
     }
 
     return dex2oat::ReturnCode::kNoFailure;
@@ -1721,7 +1768,12 @@
 
     if (!no_inline_filters.empty()) {
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-      std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_);
+      std::vector<const DexFile*> class_path_files;
+      if (!IsBootImage()) {
+        // The class loader context is used only for apps.
+        class_path_files = class_loader_context_->FlattenOpenedDexFiles();
+      }
+
       std::vector<const std::vector<const DexFile*>*> dex_file_vectors = {
           &class_linker->GetBootClassPath(),
           &class_path_files,
@@ -2224,8 +2276,8 @@
     DCHECK(!IsBootImage());
     DCHECK_EQ(oat_writers_.size(), 1u);
     std::vector<std::string> dex_files_canonical_locations;
-    for (const char* location : oat_writers_[0]->GetSourceLocations()) {
-      dex_files_canonical_locations.push_back(DexFile::GetDexCanonicalLocation(location));
+    for (const std::string& location : oat_writers_[0]->GetSourceLocations()) {
+      dex_files_canonical_locations.push_back(DexFile::GetDexCanonicalLocation(location.c_str()));
     }
 
     std::vector<std::string> parsed;
@@ -2240,48 +2292,6 @@
     return parsed;
   }
 
-  // Opens requested class path files and appends them to opened_dex_files. If the dex files have
-  // been stripped, this opens them from their oat files and appends them to opened_oat_files.
-  static void OpenClassPathFiles(std::vector<std::string>& class_path_locations,
-                                 std::vector<std::unique_ptr<const DexFile>>* opened_dex_files,
-                                 std::vector<std::unique_ptr<OatFile>>* opened_oat_files,
-                                 InstructionSet isa,
-                                 std::string& classpath_dir) {
-    DCHECK(opened_dex_files != nullptr) << "OpenClassPathFiles dex out-param is nullptr";
-    DCHECK(opened_oat_files != nullptr) << "OpenClassPathFiles oat out-param is nullptr";
-    for (std::string& location : class_path_locations) {
-      // Stop early if we detect the special shared library, which may be passed as the classpath
-      // for dex2oat when we want to skip the shared libraries check.
-      if (location == OatFile::kSpecialSharedLibrary) {
-        break;
-      }
-      // If path is relative, append it to the provided base directory.
-      if (!classpath_dir.empty() && location[0] != '/') {
-        location = classpath_dir + '/' + location;
-      }
-      static constexpr bool kVerifyChecksum = true;
-      std::string error_msg;
-      if (!DexFile::Open(
-          location.c_str(), location.c_str(), kVerifyChecksum, &error_msg, opened_dex_files)) {
-        // If we fail to open the dex file because it's been stripped, try to open the dex file
-        // from its corresponding oat file.
-        OatFileAssistant oat_file_assistant(location.c_str(), isa, false);
-        std::unique_ptr<OatFile> oat_file(oat_file_assistant.GetBestOatFile());
-        if (oat_file == nullptr) {
-          LOG(WARNING) << "Failed to open dex file and associated oat file for '" << location
-                       << "': " << error_msg;
-        } else {
-          std::vector<std::unique_ptr<const DexFile>> oat_dex_files =
-              oat_file_assistant.LoadDexFiles(*oat_file, location.c_str());
-          opened_oat_files->push_back(std::move(oat_file));
-          opened_dex_files->insert(opened_dex_files->end(),
-                                   std::make_move_iterator(oat_dex_files.begin()),
-                                   std::make_move_iterator(oat_dex_files.end()));
-        }
-      }
-    }
-  }
-
   bool PrepareImageClasses() {
     // If --image-classes was specified, calculate the full list of classes to include in the image.
     if (image_classes_filename_ != nullptr) {
@@ -2737,8 +2747,8 @@
 
   std::unique_ptr<Runtime> runtime_;
 
-  // Ownership for the class path files.
-  std::vector<std::unique_ptr<const DexFile>> class_path_files_;
+  // The spec describing how the class loader should be setup for compilation.
+  std::unique_ptr<ClassLoaderContext> class_loader_context_;
 
   size_t thread_count_;
   uint64_t start_ns_;
@@ -2792,9 +2802,9 @@
   std::unique_ptr<CompilerDriver> driver_;
 
   std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps_;
-  std::vector<std::unique_ptr<OatFile>> opened_oat_files_;
   std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
 
+  // Note that this might contain pointers owned by class_loader_context_.
   std::vector<const DexFile*> no_inline_from_dex_files_;
 
   bool dump_stats_;
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 148af0d..95fb16d 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -260,7 +260,8 @@
     std::cout << "Compiled all classes sizes " << compiled_all_classes_sizes << std::endl;
     // Check that oat size is smaller since we didn't compile everything.
     EXPECT_EQ(compiled_all_classes_sizes.art_size, base_sizes.art_size);
-    EXPECT_EQ(compiled_all_classes_sizes.oat_size, base_sizes.oat_size);
+    // TODO(mathieuc): Find a reliable way to check compiled code.
+    // EXPECT_EQ(compiled_all_classes_sizes.oat_size, base_sizes.oat_size);
     EXPECT_EQ(compiled_all_classes_sizes.vdex_size, base_sizes.vdex_size);
   }
   // Test compiled classes.
@@ -274,7 +275,8 @@
     classes.Close();
     std::cout << "Compiled classes sizes " << compiled_classes_sizes << std::endl;
     // Check that oat size is smaller since we didn't compile everything.
-    EXPECT_LT(compiled_classes_sizes.oat_size, base_sizes.oat_size);
+    // TODO(mathieuc): Find a reliable way to check compiled code.
+    // EXPECT_LT(compiled_classes_sizes.oat_size, base_sizes.oat_size);
     // Art file should be smaller than image classes version since we included fewer classes in the
     // list.
     EXPECT_LT(compiled_classes_sizes.art_size, image_classes_sizes.art_size);
@@ -289,7 +291,8 @@
     methods.Close();
     std::cout << "Compiled all methods sizes " << compiled_all_methods_sizes << std::endl;
     EXPECT_EQ(compiled_all_classes_sizes.art_size, base_sizes.art_size);
-    EXPECT_EQ(compiled_all_classes_sizes.oat_size, base_sizes.oat_size);
+    // TODO(mathieuc): Find a reliable way to check compiled code. b/63746626
+    // EXPECT_EQ(compiled_all_classes_sizes.oat_size, base_sizes.oat_size);
     EXPECT_EQ(compiled_all_classes_sizes.vdex_size, base_sizes.vdex_size);
   }
   static size_t kMethodFrequency = 3;
@@ -329,10 +332,12 @@
     // the range is within expected margins (+-5%).
     const double kRatio = 0.95;
     EXPECT_LE(profile_sizes.art_size * kRatio, compiled_methods_sizes.art_size);
-    EXPECT_LE(profile_sizes.oat_size * kRatio, compiled_methods_sizes.oat_size);
+    // TODO(mathieuc): Find a reliable way to check compiled code. b/63746626
+    // EXPECT_LE(profile_sizes.oat_size * kRatio, compiled_methods_sizes.oat_size);
     EXPECT_LE(profile_sizes.vdex_size * kRatio, compiled_methods_sizes.vdex_size);
     EXPECT_GE(profile_sizes.art_size / kRatio, compiled_methods_sizes.art_size);
-    EXPECT_GE(profile_sizes.oat_size / kRatio, compiled_methods_sizes.oat_size);
+    // TODO(mathieuc): Find a reliable way to check compiled code. b/63746626
+    // EXPECT_GE(profile_sizes.oat_size / kRatio, compiled_methods_sizes.oat_size);
     EXPECT_GE(profile_sizes.vdex_size / kRatio, compiled_methods_sizes.vdex_size);
   }
 }
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index b604e8b..ed1aee6 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -29,6 +29,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/mutex-inl.h"
+#include "bytecode_utils.h"
 #include "dex_file-inl.h"
 #include "dex2oat_environment_test.h"
 #include "dex2oat_return_codes.h"
@@ -89,7 +90,8 @@
                            CompilerFilter::Filter filter,
                            const std::vector<std::string>& extra_args = {},
                            bool expect_success = true,
-                           bool use_fd = false) {
+                           bool use_fd = false,
+                           std::function<void(const OatFile&)> check_oat = [](const OatFile&) {}) {
     std::string error_msg;
     int status = GenerateOdexForTestWithStatus(dex_location,
                                                odex_location,
@@ -113,6 +115,7 @@
       ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
 
       CheckFilter(filter, odex_file->GetCompilerFilter());
+      check_oat(*(odex_file.get()));
     } else {
       ASSERT_FALSE(success) << output_;
 
@@ -542,11 +545,11 @@
   void CheckHostResult(bool expect_large) {
     if (!kIsTargetBuild) {
       if (expect_large) {
-        EXPECT_NE(output_.find("Very large app, downgrading to extract."),
+        EXPECT_NE(output_.find("Very large app, downgrading to"),
                   std::string::npos)
             << output_;
       } else {
-        EXPECT_EQ(output_.find("Very large app, downgrading to extract."),
+        EXPECT_EQ(output_.find("Very large app, downgrading to"),
                   std::string::npos)
             << output_;
       }
@@ -831,6 +834,84 @@
   RunTestVDex();
 }
 
+class Dex2oatUnquickenTest : public Dex2oatTest {
+ protected:
+  void RunUnquickenMultiDex() {
+    std::string dex_location = GetScratchDir() + "/UnquickenMultiDex.jar";
+    std::string odex_location = GetOdexDir() + "/UnquickenMultiDex.odex";
+    std::string vdex_location = GetOdexDir() + "/UnquickenMultiDex.vdex";
+    Copy(GetTestDexFileName("MultiDex"), dex_location);
+
+    std::unique_ptr<File> vdex_file1(OS::CreateEmptyFile(vdex_location.c_str()));
+    CHECK(vdex_file1 != nullptr) << vdex_location;
+    // Quicken the dex file into a vdex file.
+    {
+      std::string input_vdex = "--input-vdex-fd=-1";
+      std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file1->Fd());
+      GenerateOdexForTest(dex_location,
+                          odex_location,
+                          CompilerFilter::kQuicken,
+                          { input_vdex, output_vdex },
+                          /* expect_success */ true,
+                          /* use_fd */ true);
+      EXPECT_GT(vdex_file1->GetLength(), 0u);
+    }
+    // Unquicken by running the verify compiler filter on the vdex file.
+    {
+      std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_file1->Fd());
+      std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file1->Fd());
+      GenerateOdexForTest(dex_location,
+                          odex_location,
+                          CompilerFilter::kVerify,
+                          { input_vdex, output_vdex },
+                          /* expect_success */ true,
+                          /* use_fd */ true);
+    }
+    ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file";
+    CheckResult(dex_location, odex_location);
+    ASSERT_TRUE(success_);
+  }
+
+  void CheckResult(const std::string& dex_location, const std::string& odex_location) {
+    std::string error_msg;
+    std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+                                                     odex_location.c_str(),
+                                                     nullptr,
+                                                     nullptr,
+                                                     false,
+                                                     /*low_4gb*/false,
+                                                     dex_location.c_str(),
+                                                     &error_msg));
+    ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+    ASSERT_GE(odex_file->GetOatDexFiles().size(), 1u);
+
+    // Iterate over the dex files and ensure there is no quickened instruction.
+    for (const OatDexFile* oat_dex_file : odex_file->GetOatDexFiles()) {
+      std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+      for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
+        const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
+        const uint8_t* class_data = dex_file->GetClassData(class_def);
+        if (class_data != nullptr) {
+          for (ClassDataItemIterator class_it(*dex_file, class_data);
+               class_it.HasNext();
+               class_it.Next()) {
+            if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) {
+              for (CodeItemIterator it(*class_it.GetMethodCodeItem()); !it.Done(); it.Advance()) {
+                Instruction* inst = const_cast<Instruction*>(&it.CurrentInstruction());
+                ASSERT_FALSE(inst->IsQuickened());
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+};
+
+TEST_F(Dex2oatUnquickenTest, UnquickenMultiDex) {
+  RunUnquickenMultiDex();
+}
+
 class Dex2oatWatchdogTest : public Dex2oatTest {
  protected:
   void RunTest(bool expect_success, const std::vector<std::string>& extra_args = {}) {
@@ -895,4 +976,130 @@
   EXPECT_EQ(static_cast<int>(dex2oat::ReturnCode::kCreateRuntime), WEXITSTATUS(status)) << output_;
 }
 
+class Dex2oatClassLoaderContextTest : public Dex2oatTest {
+ protected:
+  void RunTest(const char* class_loader_context,
+               const char* expected_classpath_key,
+               bool expected_success,
+               bool use_second_source = false) {
+    std::string dex_location = GetUsedDexLocation();
+    std::string odex_location = GetUsedOatLocation();
+
+    Copy(use_second_source ? GetDexSrc2() : GetDexSrc1(), dex_location);
+
+    std::string error_msg;
+    std::vector<std::string> extra_args;
+    if (class_loader_context != nullptr) {
+      extra_args.push_back(std::string("--class-loader-context=") + class_loader_context);
+    }
+    auto check_oat = [expected_classpath_key](const OatFile& oat_file) {
+      ASSERT_TRUE(expected_classpath_key != nullptr);
+      const char* classpath = oat_file.GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey);
+      ASSERT_TRUE(classpath != nullptr);
+      ASSERT_STREQ(expected_classpath_key, classpath);
+    };
+
+    GenerateOdexForTest(dex_location,
+                        odex_location,
+                        CompilerFilter::kQuicken,
+                        extra_args,
+                        expected_success,
+                        /*use_fd*/ false,
+                        check_oat);
+  }
+
+  std::string GetUsedDexLocation() {
+    return GetScratchDir() + "/Context.jar";
+  }
+
+  std::string GetUsedOatLocation() {
+    return GetOdexDir() + "/Context.odex";
+  }
+
+  const char* kEmptyClassPathKey = "PCL[]";
+};
+
+TEST_F(Dex2oatClassLoaderContextTest, InvalidContext) {
+  RunTest("Invalid[]", /*expected_classpath_key*/ nullptr, /*expected_success*/ false);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, EmptyContext) {
+  RunTest("PCL[]", kEmptyClassPathKey, /*expected_success*/ true);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, SpecialContext) {
+  RunTest(OatFile::kSpecialSharedLibrary,
+          OatFile::kSpecialSharedLibrary,
+          /*expected_success*/ true);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, ContextWithTheSourceDexFiles) {
+  std::string context = "PCL[" + GetUsedDexLocation() + "]";
+  RunTest(context.c_str(), kEmptyClassPathKey, /*expected_success*/ true);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, ContextWithOtherDexFiles) {
+  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("Nested");
+
+  std::string context = "PCL[" + dex_files[0]->GetLocation() + "]";
+  std::string expected_classpath_key = "PCL[" +
+      dex_files[0]->GetLocation() + "*" + std::to_string(dex_files[0]->GetLocationChecksum()) + "]";
+  RunTest(context.c_str(), expected_classpath_key.c_str(), true);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, ContextWithStrippedDexFiles) {
+  std::string stripped_classpath = GetScratchDir() + "/stripped_classpath.jar";
+  Copy(GetStrippedDexSrc1(), stripped_classpath);
+
+  std::string context = "PCL[" + stripped_classpath + "]";
+  // Expect an empty context because stripped dex files cannot be open.
+  RunTest(context.c_str(), kEmptyClassPathKey , /*expected_success*/ true);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, ContextWithStrippedDexFilesBackedByOdex) {
+  std::string stripped_classpath = GetScratchDir() + "/stripped_classpath.jar";
+  std::string odex_for_classpath = GetOdexDir() + "/stripped_classpath.odex";
+
+  Copy(GetDexSrc1(), stripped_classpath);
+
+  GenerateOdexForTest(stripped_classpath,
+                      odex_for_classpath,
+                      CompilerFilter::kQuicken,
+                      {},
+                      true);
+
+  // Strip the dex file
+  Copy(GetStrippedDexSrc1(), stripped_classpath);
+
+  std::string context = "PCL[" + stripped_classpath + "]";
+  std::string expected_classpath_key;
+  {
+    // Open the oat file to get the expected classpath.
+    OatFileAssistant oat_file_assistant(stripped_classpath.c_str(), kRuntimeISA, false);
+    std::unique_ptr<OatFile> oat_file(oat_file_assistant.GetBestOatFile());
+    std::vector<std::unique_ptr<const DexFile>> oat_dex_files =
+        OatFileAssistant::LoadDexFiles(*oat_file, stripped_classpath.c_str());
+    expected_classpath_key = "PCL[";
+    for (size_t i = 0; i < oat_dex_files.size(); i++) {
+      if (i > 0) {
+        expected_classpath_key + ":";
+      }
+      expected_classpath_key += oat_dex_files[i]->GetLocation() + "*" +
+          std::to_string(oat_dex_files[i]->GetLocationChecksum());
+    }
+    expected_classpath_key += "]";
+  }
+
+  RunTest(context.c_str(),
+          expected_classpath_key.c_str(),
+          /*expected_success*/ true,
+          /*use_second_source*/ true);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, ContextWithNotExistentDexFiles) {
+  std::string context = "PCL[does_not_exists.dex]";
+  // Expect an empty context because stripped dex files cannot be open.
+  RunTest(context.c_str(), kEmptyClassPathKey, /*expected_success*/ true);
+}
+
 }  // namespace art
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index f886de2..c0478bd 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -1536,11 +1536,21 @@
   std::vector<bool> from_hot_method(num_strings, false);
   for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_->GetCollections().ClassDefs()) {
     // A name of a profile class is probably going to get looked up by ClassTable::Lookup, mark it
-    // as hot.
+    // as hot. Add its super class and interfaces as well, which can be used during initialization.
     const bool is_profile_class =
         info_->ContainsClass(*dex_file, dex::TypeIndex(class_def->ClassType()->GetIndex()));
     if (is_profile_class) {
       from_hot_method[class_def->ClassType()->GetStringId()->GetIndex()] = true;
+      const dex_ir::TypeId* superclass = class_def->Superclass();
+      if (superclass != nullptr) {
+        from_hot_method[superclass->GetStringId()->GetIndex()] = true;
+      }
+      const dex_ir::TypeList* interfaces = class_def->Interfaces();
+      if (interfaces != nullptr) {
+        for (const dex_ir::TypeId* interface_type : *interfaces->GetTypeList()) {
+          from_hot_method[interface_type->GetStringId()->GetIndex()] = true;
+        }
+      }
     }
     dex_ir::ClassData* data = class_def->GetClassData();
     if (data == nullptr) {
@@ -1566,18 +1576,25 @@
         if (fixups == nullptr) {
           continue;
         }
-        if (fixups->StringIds() != nullptr) {
-          // Add const-strings.
-          for (dex_ir::StringId* id : *fixups->StringIds()) {
-            from_hot_method[id->GetIndex()] = true;
-          }
+        // Add const-strings.
+        for (dex_ir::StringId* id : *fixups->StringIds()) {
+          from_hot_method[id->GetIndex()] = true;
         }
-        // TODO: Only visit field ids from static getters and setters.
+        // Add field classes, names, and types.
         for (dex_ir::FieldId* id : *fixups->FieldIds()) {
-          // Add the field names and types from getters and setters.
+          // TODO: Only visit field ids from static getters and setters.
+          from_hot_method[id->Class()->GetStringId()->GetIndex()] = true;
           from_hot_method[id->Name()->GetIndex()] = true;
           from_hot_method[id->Type()->GetStringId()->GetIndex()] = true;
         }
+        // For clinits, add referenced method classes, names, and protos.
+        if (is_clinit) {
+          for (dex_ir::MethodId* id : *fixups->MethodIds()) {
+            from_hot_method[id->Class()->GetStringId()->GetIndex()] = true;
+            from_hot_method[id->Name()->GetIndex()] = true;
+            is_shorty[id->Proto()->Shorty()->GetIndex()] = true;
+          }
+        }
       }
     }
   }
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index 531bc98..ed011d6 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -58,7 +58,8 @@
   bool show_section_headers_ = false;
   bool show_section_statistics_ = false;
   bool verbose_ = false;
-  bool verify_output_ = false;
+  // TODO: Set verify_output_ back to false by default. Was set to true for debugging b/62840842.
+  bool verify_output_ = true;
   bool visualize_pattern_ = false;
   OutputFormat output_format_ = kOutputPlain;
   const char* output_dex_directory_ = nullptr;
diff --git a/disassembler/disassembler.cc b/disassembler/disassembler.cc
index 8eecc62..5af51c1 100644
--- a/disassembler/disassembler.cc
+++ b/disassembler/disassembler.cc
@@ -40,8 +40,10 @@
     return new arm::DisassemblerArm(options);
   } else if (instruction_set == kArm64) {
     return new arm64::DisassemblerArm64(options);
-  } else if (instruction_set == kMips || instruction_set == kMips64) {
-    return new mips::DisassemblerMips(options);
+  } else if (instruction_set == kMips) {
+    return new mips::DisassemblerMips(options, /* is_o32_abi */ true);
+  } else if (instruction_set == kMips64) {
+    return new mips::DisassemblerMips(options, /* is_o32_abi */ false);
   } else if (instruction_set == kX86) {
     return new x86::DisassemblerX86(options, false);
   } else if (instruction_set == kX86_64) {
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index 91203cb..7cb216e 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -40,6 +40,20 @@
   }
 };
 
+static const char* gO32AbiRegNames[]  = {
+  "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+  "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+  "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+  "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
+static const char* gN64AbiRegNames[]  = {
+  "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+  "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3",
+  "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+  "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
 static const uint32_t kOpcodeShift = 26;
 
 static const uint32_t kCop1 = (17 << kOpcodeShift);
@@ -470,6 +484,14 @@
   return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
 }
 
+const char* DisassemblerMips::RegName(uint32_t reg) {
+  if (is_o32_abi_) {
+    return gO32AbiRegNames[reg];
+  } else {
+    return gN64AbiRegNames[reg];
+  }
+}
+
 size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) {
   uint32_t instruction = ReadU32(instr_ptr);
 
@@ -518,7 +540,7 @@
           case 'c':  // Floating-point condition code flag in bc1f/bc1t and movf/movt.
             args << "cc" << (rt >> 2);
             break;
-          case 'D': args << 'r' << rd; break;
+          case 'D': args << RegName(rd); break;
           case 'd': args << 'f' << rd; break;
           case 'a': args << 'f' << sa; break;
           case 'F': args << (sa + 32); break;  // dinsu position.
@@ -553,13 +575,13 @@
           case 'l':  // 9-bit signed offset
             {
               int32_t offset = static_cast<int16_t>(instruction) >> 7;
-              args << StringPrintf("%+d(r%d)", offset, rs);
+              args << StringPrintf("%+d(%s)", offset, RegName(rs));
             }
             break;
           case 'O':  // +x(rs)
             {
               int32_t offset = static_cast<int16_t>(instruction & 0xffff);
-              args << StringPrintf("%+d(r%d)", offset, rs);
+              args << StringPrintf("%+d(%s)", offset, RegName(rs));
               if (rs == 17) {
                 args << "  ; ";
                 GetDisassemblerOptions()->thread_offset_name_function_(args, offset);
@@ -595,13 +617,13 @@
           case 'p':  // 19-bit offset in addiupc.
             {
               int32_t offset = (instruction & 0x7ffff) - ((instruction & 0x40000) << 1);
-              args << offset << "  ; move r" << rs << ", ";
+              args << offset << "  ; move " << RegName(rs) << ", ";
               args << FormatInstructionPointer(instr_ptr + (offset << 2));
             }
             break;
-          case 'S': args << 'r' << rs; break;
+          case 'S': args << RegName(rs); break;
           case 's': args << 'f' << rs; break;
-          case 'T': args << 'r' << rt; break;
+          case 'T': args << RegName(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, dinsu size).
@@ -683,7 +705,7 @@
                 case 2: opcode += ".w"; break;
                 case 3: opcode += ".d"; break;
               }
-              args << StringPrintf("%+d(r%d)", s10 << df, rd);
+              args << StringPrintf("%+d(%s)", s10 << df, RegName(rd));
               break;
             }
           case 'X':  // MSA df/n - ws[x].
diff --git a/disassembler/disassembler_mips.h b/disassembler/disassembler_mips.h
index 6342f22..afa6af3 100644
--- a/disassembler/disassembler_mips.h
+++ b/disassembler/disassembler_mips.h
@@ -26,11 +26,13 @@
 
 class DisassemblerMips FINAL : public Disassembler {
  public:
-  explicit DisassemblerMips(DisassemblerOptions* options)
+  explicit DisassemblerMips(DisassemblerOptions* options, bool is_o32_abi)
       : Disassembler(options),
         last_ptr_(nullptr),
-        last_instr_(0) {}
+        last_instr_(0),
+        is_o32_abi_(is_o32_abi) {}
 
+  const char* RegName(uint32_t reg);
   size_t Dump(std::ostream& os, const uint8_t* begin) OVERRIDE;
   void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) OVERRIDE;
 
@@ -39,6 +41,7 @@
   // Needed to produce more readable disassembly of certain 2-instruction sequences.
   const uint8_t* last_ptr_;
   uint32_t last_instr_;
+  const bool is_o32_abi_;
 
   DISALLOW_COPY_AND_ASSIGN(DisassemblerMips);
 };
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 066c66a..ae26e7d 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -42,6 +42,7 @@
 #include "dex_instruction-inl.h"
 #include "disassembler.h"
 #include "elf_builder.h"
+#include "gc/accounting/space_bitmap-inl.h"
 #include "gc/space/image_space.h"
 #include "gc/space/large_object_space.h"
 #include "gc/space/space-inl.h"
@@ -1930,9 +1931,12 @@
           }
         }
       }
+      auto dump_visitor = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+        DumpObject(obj);
+      };
       ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
       // Dump the normal objects before ArtMethods.
-      image_space_.GetLiveBitmap()->Walk(ImageDumper::Callback, this);
+      image_space_.GetLiveBitmap()->Walk(dump_visitor);
       indent_os << "\n";
       // TODO: Dump fields.
       // Dump methods after.
@@ -1941,7 +1945,7 @@
                                           image_space_.Begin(),
                                           image_header_.GetPointerSize());
       // Dump the large objects separately.
-      heap->GetLargeObjectsSpace()->GetLiveBitmap()->Walk(ImageDumper::Callback, this);
+      heap->GetLargeObjectsSpace()->GetLiveBitmap()->Walk(dump_visitor);
       indent_os << "\n";
     }
     os << "STATS:\n" << std::flush;
@@ -2156,20 +2160,18 @@
     return oat_code_begin + GetQuickOatCodeSize(m);
   }
 
-  static void Callback(mirror::Object* obj, void* arg) REQUIRES_SHARED(Locks::mutator_lock_) {
+  void DumpObject(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
     DCHECK(obj != nullptr);
-    DCHECK(arg != nullptr);
-    ImageDumper* state = reinterpret_cast<ImageDumper*>(arg);
-    if (!state->InDumpSpace(obj)) {
+    if (!InDumpSpace(obj)) {
       return;
     }
 
     size_t object_bytes = obj->SizeOf();
     size_t alignment_bytes = RoundUp(object_bytes, kObjectAlignment) - object_bytes;
-    state->stats_.object_bytes += object_bytes;
-    state->stats_.alignment_bytes += alignment_bytes;
+    stats_.object_bytes += object_bytes;
+    stats_.alignment_bytes += alignment_bytes;
 
-    std::ostream& os = state->vios_.Stream();
+    std::ostream& os = vios_.Stream();
 
     mirror::Class* obj_class = obj->GetClass();
     if (obj_class->IsArrayClass()) {
@@ -2186,9 +2188,9 @@
     } else {
       os << StringPrintf("%p: %s\n", obj, obj_class->PrettyDescriptor().c_str());
     }
-    ScopedIndentation indent1(&state->vios_);
+    ScopedIndentation indent1(&vios_);
     DumpFields(os, obj, obj_class);
-    const PointerSize image_pointer_size = state->image_header_.GetPointerSize();
+    const PointerSize image_pointer_size = image_header_.GetPointerSize();
     if (obj->IsObjectArray()) {
       auto* obj_array = obj->AsObjectArray<mirror::Object>();
       for (int32_t i = 0, length = obj_array->GetLength(); i < length; i++) {
@@ -2215,22 +2217,22 @@
       mirror::Class* klass = obj->AsClass();
       if (klass->NumStaticFields() != 0) {
         os << "STATICS:\n";
-        ScopedIndentation indent2(&state->vios_);
+        ScopedIndentation indent2(&vios_);
         for (ArtField& field : klass->GetSFields()) {
           PrintField(os, &field, field.GetDeclaringClass());
         }
       }
     } else {
-      auto it = state->dex_caches_.find(obj);
-      if (it != state->dex_caches_.end()) {
+      auto it = dex_caches_.find(obj);
+      if (it != dex_caches_.end()) {
         auto* dex_cache = down_cast<mirror::DexCache*>(obj);
-        const auto& field_section = state->image_header_.GetImageSection(
+        const auto& field_section = image_header_.GetImageSection(
             ImageHeader::kSectionArtFields);
-        const auto& method_section = state->image_header_.GetMethodsSection();
+        const auto& method_section = image_header_.GetMethodsSection();
         size_t num_methods = dex_cache->NumResolvedMethods();
         if (num_methods != 0u) {
           os << "Methods (size=" << num_methods << "):\n";
-          ScopedIndentation indent2(&state->vios_);
+          ScopedIndentation indent2(&vios_);
           auto* resolved_methods = dex_cache->GetResolvedMethods();
           for (size_t i = 0, length = dex_cache->NumResolvedMethods(); i < length; ++i) {
             auto* elem = mirror::DexCache::GetElementPtrSize(resolved_methods,
@@ -2254,7 +2256,7 @@
             if (elem == nullptr) {
               msg = "null";
             } else if (method_section.Contains(
-                reinterpret_cast<uint8_t*>(elem) - state->image_space_.Begin())) {
+                reinterpret_cast<uint8_t*>(elem) - image_space_.Begin())) {
               msg = reinterpret_cast<ArtMethod*>(elem)->PrettyMethod();
             } else {
               msg = "<not in method section>";
@@ -2265,7 +2267,7 @@
         size_t num_fields = dex_cache->NumResolvedFields();
         if (num_fields != 0u) {
           os << "Fields (size=" << num_fields << "):\n";
-          ScopedIndentation indent2(&state->vios_);
+          ScopedIndentation indent2(&vios_);
           auto* resolved_fields = dex_cache->GetResolvedFields();
           for (size_t i = 0, length = dex_cache->NumResolvedFields(); i < length; ++i) {
             auto* elem = mirror::DexCache::GetNativePairPtrSize(
@@ -2288,7 +2290,7 @@
             if (elem == nullptr) {
               msg = "null";
             } else if (field_section.Contains(
-                reinterpret_cast<uint8_t*>(elem) - state->image_space_.Begin())) {
+                reinterpret_cast<uint8_t*>(elem) - image_space_.Begin())) {
               msg = reinterpret_cast<ArtField*>(elem)->PrettyField();
             } else {
               msg = "<not in field section>";
@@ -2299,7 +2301,7 @@
         size_t num_types = dex_cache->NumResolvedTypes();
         if (num_types != 0u) {
           os << "Types (size=" << num_types << "):\n";
-          ScopedIndentation indent2(&state->vios_);
+          ScopedIndentation indent2(&vios_);
           auto* resolved_types = dex_cache->GetResolvedTypes();
           for (size_t i = 0; i < num_types; ++i) {
             auto pair = resolved_types[i].load(std::memory_order_relaxed);
@@ -2331,7 +2333,7 @@
       }
     }
     std::string temp;
-    state->stats_.Update(obj_class->GetDescriptor(&temp), object_bytes);
+    stats_.Update(obj_class->GetDescriptor(&temp), object_bytes);
   }
 
   void DumpMethod(ArtMethod* method, std::ostream& indent_os)
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 149960e..a93969f 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -614,7 +614,10 @@
     TimingLogger::ScopedTiming t("Walk Bitmap", timings_);
     // Walk the bitmap.
     WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
-    bitmap_->Walk(PatchOat::BitmapCallback, this);
+    auto visitor = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+      VisitObject(obj);
+    };
+    bitmap_->Walk(visitor);
   }
   return true;
 }
@@ -638,7 +641,7 @@
   copy_->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(off, moved_object);
 }
 
-// Called by BitmapCallback
+// Called by PatchImage.
 void PatchOat::VisitObject(mirror::Object* object) {
   mirror::Object* copy = RelocatedCopyOf(object);
   CHECK(copy != nullptr);
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index e15a6bc..182ce94 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -79,11 +79,6 @@
   static bool ReplaceOatFileWithSymlink(const std::string& input_oat_filename,
                                         const std::string& output_oat_filename);
 
-  static void BitmapCallback(mirror::Object* obj, void* arg)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    reinterpret_cast<PatchOat*>(arg)->VisitObject(obj);
-  }
-
   void VisitObject(mirror::Object* obj)
       REQUIRES_SHARED(Locks::mutator_lock_);
   void FixupMethod(ArtMethod* object, ArtMethod* copy)
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 75f8ec9..c78d34e 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -639,10 +639,13 @@
   // Method that doesn't add the class since its only in one profile. Should still show up in the
   // boot profile.
   const std::string kOtherMethod = "Ljava/util/HashMap;-><init>()V";
+  // Method that gets marked as hot since it's in multiple profiles.
+  const std::string kMultiMethod = "Ljava/util/ArrayList;->clear()V";
 
   // Thresholds for this test.
   static const size_t kDirtyThreshold = 3;
   static const size_t kCleanThreshold = 2;
+  static const size_t kMethodThreshold = 2;
 
   // Create a bunch of boot profiles.
   std::string dex1 =
@@ -659,6 +662,7 @@
       kCleanClass + "\n" +
       kDirtyClass + "\n" +
       "P" + kHotMethod + "\n" +
+      "P" + kMultiMethod + "\n" +
       kUncommonDirtyClass;
   profiles.emplace_back(ScratchFile());
   EXPECT_TRUE(CreateProfile(dex2, profiles.back().GetFilename(), core_dex));
@@ -667,6 +671,7 @@
   std::string dex3 =
       "S" + kHotMethod + "\n" +
       "P" + kOtherMethod + "\n" +
+      "P" + kMultiMethod + "\n" +
       kDirtyClass + "\n";
   profiles.emplace_back(ScratchFile());
   EXPECT_TRUE(CreateProfile(dex3, profiles.back().GetFilename(), core_dex));
@@ -678,6 +683,7 @@
   args.push_back("--generate-boot-image-profile");
   args.push_back("--boot-image-class-threshold=" + std::to_string(kDirtyThreshold));
   args.push_back("--boot-image-clean-class-threshold=" + std::to_string(kCleanThreshold));
+  args.push_back("--boot-image-sampled-method-threshold=" + std::to_string(kMethodThreshold));
   args.push_back("--reference-profile-file=" + out_profile.GetFilename());
   args.push_back("--apk=" + core_dex);
   args.push_back("--dex-location=" + core_dex);
@@ -708,11 +714,18 @@
   // Aggregated methods hotness information.
   EXPECT_NE(output_file_contents.find("HSP" + kHotMethod), std::string::npos)
       << output_file_contents;
-  EXPECT_NE(output_file_contents.find(kOtherMethod), std::string::npos)
+  EXPECT_NE(output_file_contents.find("P" + kOtherMethod), std::string::npos)
       << output_file_contents;
   // Not inferred class, method is only in one profile.
   EXPECT_EQ(output_file_contents.find("Ljava/util/HashMap;\n"), std::string::npos)
       << output_file_contents;
+  // Test the sampled methods that became hot.
+  // Other method is in only one profile, it should not become hot.
+  EXPECT_EQ(output_file_contents.find("HP" + kOtherMethod), std::string::npos)
+      << output_file_contents;
+  // Multi method is in at least two profiles, it should become hot.
+  EXPECT_NE(output_file_contents.find("HP" + kMultiMethod), std::string::npos)
+      << output_file_contents;
 }
 
 TEST_F(ProfileAssistantTest, TestProfileCreationOneNotMatched) {
diff --git a/profman/profman.cc b/profman/profman.cc
index 94e81c7..6c8ca56 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -142,6 +142,9 @@
   UsageError("      occurrences to include a class in the boot image profile. A clean class is a");
   UsageError("      class that doesn't have any static fields or native methods and is likely to");
   UsageError("      remain clean in the image. Default is 3.");
+  UsageError("  --boot-image-sampled-method-threshold=<value>: minimum number of profiles a");
+  UsageError("      non-hot method needs to be in order to be hot in the output profile. The");
+  UsageError("      default is max int.");
   UsageError("");
 
   exit(EXIT_FAILURE);
@@ -225,6 +228,11 @@
                         "--boot-image-clean-class-threshold",
                         &boot_image_options_.image_class_clean_theshold,
                         Usage);
+      } else if (option.starts_with("--boot-image-sampled-method-threshold=")) {
+        ParseUintOption(option,
+                        "--boot-image-sampled-method-threshold",
+                        &boot_image_options_.compiled_method_threshold,
+                        Usage);
       } else if (option.starts_with("--profile-file=")) {
         profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString());
       } else if (option.starts_with("--profile-file-fd=")) {
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 0dfc60d..8d15c34 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -48,6 +48,7 @@
         "cha.cc",
         "check_jni.cc",
         "class_linker.cc",
+        "class_loader_context.cc",
         "class_table.cc",
         "code_simulator_container.cc",
         "common_throws.cc",
@@ -542,6 +543,7 @@
         "base/unix_file/fd_file_test.cc",
         "cha_test.cc",
         "class_linker_test.cc",
+        "class_loader_context_test.cc",
         "class_table_test.cc",
         "compiler_filter_test.cc",
         "dex_file_test.cc",
diff --git a/runtime/arch/arm/asm_support_arm.S b/runtime/arch/arm/asm_support_arm.S
index 9eca862..eeac743 100644
--- a/runtime/arch/arm/asm_support_arm.S
+++ b/runtime/arch/arm/asm_support_arm.S
@@ -26,6 +26,13 @@
 // Register holding Thread::Current().
 #define rSELF r9
 
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+// Marking Register, holding Thread::Current()->GetIsGcMarking().
+// Only used with the Concurrent Copying (CC) garbage
+// collector, with the Baker read barrier configuration.
+#define rMR r8
+#endif
+
 .syntax unified
 .arch armv7-a
 .thumb
@@ -121,14 +128,14 @@
     END \name
 .endm
 
-// Macros to poison (negate) the reference for heap poisoning.
+// Macro to poison (negate) the reference for heap poisoning.
 .macro POISON_HEAP_REF rRef
 #ifdef USE_HEAP_POISONING
     rsb \rRef, \rRef, #0
 #endif  // USE_HEAP_POISONING
 .endm
 
-// Macros to unpoison (negate) the reference for heap poisoning.
+// Macro to unpoison (negate) the reference for heap poisoning.
 .macro UNPOISON_HEAP_REF rRef
 #ifdef USE_HEAP_POISONING
     rsb \rRef, \rRef, #0
diff --git a/runtime/arch/arm/context_arm.cc b/runtime/arch/arm/context_arm.cc
index 0db14fb..711452c 100644
--- a/runtime/arch/arm/context_arm.cc
+++ b/runtime/arch/arm/context_arm.cc
@@ -108,7 +108,9 @@
   for (size_t i = 0; i < kNumberOfSRegisters; ++i) {
     fprs[i] = fprs_[i] != nullptr ? *fprs_[i] : ArmContext::kBadFprBase + i;
   }
+  // Ensure the Thread Register contains the address of the current thread.
   DCHECK_EQ(reinterpret_cast<uintptr_t>(Thread::Current()), gprs[TR]);
+  // The Marking Register will be updated by art_quick_do_long_jump.
   art_quick_do_long_jump(gprs, fprs);
 }
 
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 676efc4..0de5905 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -67,6 +67,9 @@
      * Runtime::CreateCalleeSaveMethod(kSaveRefsOnly).
      */
 .macro SETUP_SAVE_REFS_ONLY_FRAME rTemp
+    // Note: We could avoid saving R8 in the case of Baker read
+    // barriers, as it is overwritten by REFRESH_MARKING_REGISTER
+    // later; but it's not worth handling this special case.
     push {r5-r8, r10-r11, lr}                     @ 7 words of callee saves
     .cfi_adjust_cfa_offset 28
     .cfi_rel_offset r5, 0
@@ -93,6 +96,9 @@
 .macro RESTORE_SAVE_REFS_ONLY_FRAME
     add sp, #4               @ bottom word holds Method*
     .cfi_adjust_cfa_offset -4
+    // Note: Likewise, we could avoid restoring R8 in the case of Baker
+    // read barriers, as it is overwritten by REFRESH_MARKING_REGISTER
+    // later; but it's not worth handling this special case.
     pop {r5-r8, r10-r11, lr} @ 7 words of callee saves
     .cfi_restore r5
     .cfi_restore r6
@@ -104,16 +110,14 @@
     .cfi_adjust_cfa_offset -28
 .endm
 
-.macro RESTORE_SAVE_REFS_ONLY_FRAME_AND_RETURN
-    RESTORE_SAVE_REFS_ONLY_FRAME
-    bx  lr                   @ return
-.endm
-
     /*
      * Macro that sets up the callee save frame to conform with
      * Runtime::CreateCalleeSaveMethod(kSaveRefsAndArgs).
      */
 .macro SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY
+    // Note: We could avoid saving R8 in the case of Baker read
+    // barriers, as it is overwritten by REFRESH_MARKING_REGISTER
+    // later; but it's not worth handling this special case.
     push {r1-r3, r5-r8, r10-r11, lr}   @ 10 words of callee saves and args.
     .cfi_adjust_cfa_offset 40
     .cfi_rel_offset r1, 0
@@ -156,6 +160,9 @@
     .cfi_adjust_cfa_offset -8
     vpop {s0-s15}
     .cfi_adjust_cfa_offset -64
+    // Note: Likewise, we could avoid restoring X20 in the case of Baker
+    // read barriers, as it is overwritten by REFRESH_MARKING_REGISTER
+    // later; but it's not worth handling this special case.
     pop {r1-r3, r5-r8, r10-r11, lr}  @ 10 words of callee saves
     .cfi_restore r1
     .cfi_restore r2
@@ -227,6 +234,7 @@
     .cfi_restore r1
     .cfi_restore r2
     .cfi_restore r3
+    .cfi_restore r4
     .cfi_restore r5
     .cfi_restore r6
     .cfi_restore r7
@@ -251,6 +259,7 @@
     .cfi_restore r1
     .cfi_restore r2
     .cfi_restore r3
+    .cfi_restore r4
     .cfi_restore r5
     .cfi_restore r6
     .cfi_restore r7
@@ -263,6 +272,17 @@
     .cfi_adjust_cfa_offset -52
 .endm
 
+// Macro to refresh the Marking Register (R8).
+//
+// This macro must be called at the end of functions implementing
+// entrypoints that possibly (directly or indirectly) perform a
+// suspend check (before they return).
+.macro REFRESH_MARKING_REGISTER
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+    ldr rMR, [rSELF, #THREAD_IS_GC_MARKING_OFFSET]
+#endif
+.endm
+
 .macro RETURN_IF_RESULT_IS_ZERO
     cbnz   r0, 1f              @ result non-zero branch over
     bx     lr                  @ return
@@ -359,6 +379,7 @@
     mov    r1, r9                        @ pass Thread::Current
     bl     \entrypoint                   @ (uint32_t field_idx, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     \return
 END \name
 .endm
@@ -370,6 +391,7 @@
     mov    r2, r9                        @ pass Thread::Current
     bl     \entrypoint                   @ (field_idx, Object*, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     \return
 END \name
 .endm
@@ -381,6 +403,7 @@
     mov    r3, r9                        @ pass Thread::Current
     bl     \entrypoint                   @ (field_idx, Object*, new_val, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME         @ TODO: we can clearly save an add here
+    REFRESH_MARKING_REGISTER
     \return
 END \name
 .endm
@@ -464,6 +487,8 @@
      *
      * On success this wrapper will restore arguments and *jump* to the target, leaving the lr
      * pointing back to the original caller.
+     *
+     * Clobbers IP (R12).
      */
 .macro INVOKE_TRAMPOLINE_BODY cxx_name
     .extern \cxx_name
@@ -473,6 +498,7 @@
     bl     \cxx_name                      @ (method_idx, this, Thread*, SP)
     mov    r12, r1                        @ save Method*->code_
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    REFRESH_MARKING_REGISTER
     cbz    r0, 1f                         @ did we find the target? if not go to exception delivery
     bx     r12                            @ tail call to target
 1:
@@ -549,6 +575,8 @@
     mov    r4, #SUSPEND_CHECK_INTERVAL     @ reset r4 to suspend check interval
 #endif
 
+    REFRESH_MARKING_REGISTER
+
     ldr    ip, [r0, #ART_METHOD_QUICK_CODE_OFFSET_32]  @ get pointer to the code
     blx    ip                              @ call the method
 
@@ -580,7 +608,8 @@
     mov    r11, sp                         @ Save the stack pointer
     mov    r10, r1                         @ Save size of stack
     ldr    r9, [r11, #40]                  @ Move managed thread pointer into r9
-    mov    r8, r2                          @ Save the pc to call
+    REFRESH_MARKING_REGISTER
+    mov    r6, r2                          @ Save the pc to call
     sub    r7, sp, #12                     @ Reserve space for stack pointer,
                                            @    JValue* result, and ArtMethod* slot.
     and    r7, #0xFFFFFFF0                 @ Align stack pointer
@@ -612,7 +641,7 @@
 .Losr_entry:
     sub r10, r10, #4
     str lr, [sp, r10]                     @ Store link register per the compiler ABI
-    bx r8
+    bx r6
 END art_quick_osr_stub
 
     /*
@@ -624,6 +653,7 @@
     ldr  r14, [r0, #56]   @ (LR from gprs_ 56=4*14)
     add  r0, r0, #12      @ increment r0 to skip gprs_[0..2] 12=4*3
     ldm  r0, {r3-r13}     @ load remaining gprs from argument gprs_
+    REFRESH_MARKING_REGISTER
     ldr  r0, [r0, #-12]   @ load r0 value
     mov  r1, #0           @ clear result register r1
     bx   r2               @ do long jump
@@ -677,6 +707,7 @@
     mov    r1, r9                     @ pass Thread::Current
     bl     artLockObjectFromCode      @ (Object* obj, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     RETURN_IF_RESULT_IS_ZERO
     DELIVER_PENDING_EXCEPTION
 END art_quick_lock_object
@@ -686,6 +717,7 @@
     mov    r1, r9                     @ pass Thread::Current
     bl     artLockObjectFromCode      @ (Object* obj, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     RETURN_IF_RESULT_IS_ZERO
     DELIVER_PENDING_EXCEPTION
 END art_quick_lock_object_no_inline
@@ -743,6 +775,7 @@
     mov    r1, r9                     @ pass Thread::Current
     bl     artUnlockObjectFromCode    @ (Object* obj, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     RETURN_IF_RESULT_IS_ZERO
     DELIVER_PENDING_EXCEPTION
 END art_quick_unlock_object
@@ -753,6 +786,7 @@
     mov    r1, r9                     @ pass Thread::Current
     bl     artUnlockObjectFromCode    @ (Object* obj, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     RETURN_IF_RESULT_IS_ZERO
     DELIVER_PENDING_EXCEPTION
 END art_quick_unlock_object_no_inline
@@ -921,6 +955,7 @@
     mov    r1, r9                     @ pass Thread::Current
     bl     \entrypoint     @ (uint32_t type_idx, Method* method, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     \return
 END \name
 .endm
@@ -933,6 +968,7 @@
     mov    r2, r9                     @ pass Thread::Current
     bl     \entrypoint     @ (uint32_t type_idx, Method* method, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     \return
 END \name
 .endm
@@ -946,6 +982,7 @@
     @ (uint32_t type_idx, Method* method, int32_t component_count, Thread*)
     bl     \entrypoint
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     \return
 END \name
 .endm
@@ -961,6 +998,7 @@
     add    sp, #16                    @ strip the extra frame
     .cfi_adjust_cfa_offset -16
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     \return
 END \name
 .endm
@@ -975,6 +1013,7 @@
     cbz    r0, 1f                     @ If result is null, deliver the OOME.
     .cfi_remember_state
     RESTORE_SAVE_EVERYTHING_FRAME_KEEP_R0
+    REFRESH_MARKING_REGISTER
     bx     lr
     .cfi_restore_state
 1:
@@ -987,6 +1026,9 @@
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
 
+// Note: Functions `art{Get,Set}<Kind>{Static,Instance>FromCompiledCode` are
+// defined by macros in runtime/entrypoints/quick/quick_field_entrypoints.cc.
+
     /*
      * Called by managed code to resolve a static field and load a non-wide value.
      */
@@ -1006,6 +1048,7 @@
     bl     artGet64StaticFromCompiledCode        @ (uint32_t field_idx, Thread*)
     ldr    r2, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     cbnz   r2, 1f                        @ success if no exception pending
     bx     lr                            @ return on success
 1:
@@ -1031,6 +1074,7 @@
     bl     artGet64InstanceFromCompiledCode      @ (field_idx, Object*, Thread*)
     ldr    r2, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     cbnz   r2, 1f                        @ success if no exception pending
     bx     lr                            @ return on success
 1:
@@ -1066,6 +1110,7 @@
     add    sp, #16                       @ release out args
     .cfi_adjust_cfa_offset -16
     RESTORE_SAVE_REFS_ONLY_FRAME         @ TODO: we can clearly save an add here
+    REFRESH_MARKING_REGISTER
     RETURN_IF_RESULT_IS_ZERO
     DELIVER_PENDING_EXCEPTION
 END art_quick_set64_instance
@@ -1080,6 +1125,7 @@
     add    sp, #16                        @ release out args
     .cfi_adjust_cfa_offset -16
     RESTORE_SAVE_REFS_ONLY_FRAME          @ TODO: we can clearly save an add here
+    REFRESH_MARKING_REGISTER
     RETURN_IF_RESULT_IS_ZERO
     DELIVER_PENDING_EXCEPTION
 END art_quick_set64_static
@@ -1223,6 +1269,7 @@
     mov    r1, r9                     @ pass Thread::Current
     bl     \cxx_name                  @ (mirror::Class* cls, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 END \c_name
 .endm
@@ -1315,6 +1362,7 @@
     mov    r1, r9                                             // Pass Thread::Current.
     bl     \entrypoint                                        // (mirror::Class* klass, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 END \name
 .endm
@@ -1331,7 +1379,7 @@
 // r0: type r1: component_count r2: total_size r9: Thread::Current, r3, r12: free.
 // Need to preserve r0 and r1 to the slow path.
 .macro ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED_WITH_SIZE slowPathLabel
-    and    r2, r2, #OBJECT_ALIGNMENT_MASK_TOGGLED             // Apply alignemnt mask
+    and    r2, r2, #OBJECT_ALIGNMENT_MASK_TOGGLED             // Apply alignment mask
                                                               // (addr + 7) & ~7.
 
                                                               // Load thread_local_pos (r3) and
@@ -1386,6 +1434,7 @@
     mov    r2, r9                  // pass Thread::Current
     bl     \entrypoint
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 END \name
 .endm
@@ -1462,8 +1511,8 @@
     add    r2, r2, #(MIRROR_WIDE_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK)
 .endm
 
-# TODO(ngeoffray): art_quick_alloc_array_resolved_region_tlab is not used for arm, remove
-# the entrypoint once all backends have been updated to use the size variants.
+// TODO(ngeoffray): art_quick_alloc_array_resolved_region_tlab is not used for arm, remove
+// the entrypoint once all backends have been updated to use the size variants.
 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_UNKNOWN
 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved8_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_8
 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_16
@@ -1492,6 +1541,7 @@
     mov    r0, rSELF
     bl     artTestSuspendFromCode               @ (Thread*)
     RESTORE_SAVE_EVERYTHING_FRAME
+    REFRESH_MARKING_REGISTER
     bx     lr
 END art_quick_test_suspend
 
@@ -1499,7 +1549,9 @@
     mov    r0, rSELF
     SETUP_SAVE_REFS_ONLY_FRAME r1             @ save callee saves for stack crawl
     bl     artTestSuspendFromCode             @ (Thread*)
-    RESTORE_SAVE_REFS_ONLY_FRAME_AND_RETURN
+    RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
+    bx     lr
 END art_quick_implicit_suspend
 
     /*
@@ -1518,6 +1570,7 @@
     add     sp, #(FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY)
     .cfi_adjust_cfa_offset -(FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     cbnz    r2, 1f                 @ success if no exception is pending
     vmov    d0, r0, r1             @ store into fpr, for when it's a fpr return...
     bx      lr                     @ return on success
@@ -1567,8 +1620,9 @@
     blx     artQuickResolutionTrampoline  @ (Method* called, receiver, Thread*, SP)
     cbz     r0, 1f                 @ is code pointer null? goto exception
     mov     r12, r0
-    ldr  r0, [sp, #0]              @ load resolved method in r0
+    ldr     r0, [sp, #0]           @ load resolved method in r0
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    REFRESH_MARKING_REGISTER
     bx      r12                    @ tail-call into actual code
 1:
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
@@ -1649,6 +1703,7 @@
     add     sp, #FRAME_SIZE_SAVE_REFS_AND_ARGS-FRAME_SIZE_SAVE_REFS_ONLY
     .cfi_adjust_cfa_offset -(FRAME_SIZE_SAVE_REFS_AND_ARGS-FRAME_SIZE_SAVE_REFS_ONLY)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
 
     // store into fpr, for when it's a fpr return...
     vmov d0, r0, r1
@@ -1675,6 +1730,7 @@
     add     sp, #(FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY)
     .cfi_adjust_cfa_offset -(FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     cbnz    r2, 1f                 @ success if no exception is pending
     vmov    d0, r0, r1             @ store into fpr, for when it's a fpr return...
     bx      lr                     @ return on success
@@ -1705,6 +1761,7 @@
     mov   r12, r0        @ r12 holds reference to code
     ldr   r0, [sp, #4]   @ restore r0
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    REFRESH_MARKING_REGISTER
     blx   r12            @ call method with lr set to art_quick_instrumentation_exit
 @ Deliberate fall-through into art_quick_instrumentation_exit.
     .type art_quick_instrumentation_exit, #function
@@ -1734,6 +1791,7 @@
     .cfi_restore r0
     .cfi_restore r1
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     cbz   r2, .Ldo_deliver_instrumentation_exception
                          @ Deliver exception if we got nullptr as function.
     bx    r2             @ Otherwise, return
@@ -1787,7 +1845,7 @@
      */
     /* mul-long vAA, vBB, vCC */
 ENTRY art_quick_mul_long
-    push    {r9 - r10}
+    push    {r9-r10}
     .cfi_adjust_cfa_offset 8
     .cfi_rel_offset r9, 0
     .cfi_rel_offset r10, 4
@@ -1797,7 +1855,7 @@
     add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
     mov     r0,r9
     mov     r1,r10
-    pop     {r9 - r10}
+    pop     {r9-r10}
     .cfi_adjust_cfa_offset -8
     .cfi_restore r9
     .cfi_restore r10
@@ -2544,6 +2602,7 @@
     add     sp, #8
     .cfi_adjust_cfa_offset -8
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    REFRESH_MARKING_REGISTER
     RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r2
 
 .macro HANDLER_TABLE_OFFSET handler_label
diff --git a/runtime/arch/arm/registers_arm.h b/runtime/arch/arm/registers_arm.h
index 932095d..d39a2a2 100644
--- a/runtime/arch/arm/registers_arm.h
+++ b/runtime/arch/arm/registers_arm.h
@@ -40,7 +40,8 @@
   R13 = 13,
   R14 = 14,
   R15 = 15,
-  TR  = 9,  // thread register
+  MR  = 8,  // ART Marking Register
+  TR  = 9,  // ART Thread Register
   FP  = 11,
   IP  = 12,
   SP  = 13,
diff --git a/runtime/arch/arm64/asm_support_arm64.S b/runtime/arch/arm64/asm_support_arm64.S
index bcf55e3..715fc35 100644
--- a/runtime/arch/arm64/asm_support_arm64.S
+++ b/runtime/arch/arm64/asm_support_arm64.S
@@ -33,6 +33,12 @@
 #define xIP1 x17
 #define wIP1 w17
 
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+// Marking Register, holding Thread::Current()->GetIsGcMarking().
+// Only used with the Concurrent Copying (CC) garbage
+// collector, with the Baker read barrier configuration.
+#define wMR w20
+#endif
 
 .macro ENTRY name
     .type \name, #function
@@ -55,14 +61,14 @@
     END \name
 .endm
 
-// Macros to poison (negate) the reference for heap poisoning.
+// Macro to poison (negate) the reference for heap poisoning.
 .macro POISON_HEAP_REF rRef
 #ifdef USE_HEAP_POISONING
     neg \rRef, \rRef
 #endif  // USE_HEAP_POISONING
 .endm
 
-// Macros to unpoison (negate) the reference for heap poisoning.
+// Macro to unpoison (negate) the reference for heap poisoning.
 .macro UNPOISON_HEAP_REF rRef
 #ifdef USE_HEAP_POISONING
     neg \rRef, \rRef
diff --git a/runtime/arch/arm64/context_arm64.cc b/runtime/arch/arm64/context_arm64.cc
index 0465c1e..0f0814a 100644
--- a/runtime/arch/arm64/context_arm64.cc
+++ b/runtime/arch/arm64/context_arm64.cc
@@ -137,7 +137,9 @@
   for (size_t i = 0; i < kNumberOfDRegisters; ++i) {
     fprs[i] = fprs_[i] != nullptr ? *fprs_[i] : Arm64Context::kBadFprBase + i;
   }
+  // Ensure the Thread Register contains the address of the current thread.
   DCHECK_EQ(reinterpret_cast<uintptr_t>(Thread::Current()), gprs[TR]);
+  // The Marking Register will be updated by art_quick_do_long_jump.
   art_quick_do_long_jump(gprs, fprs);
 }
 
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 138dbf9..e097a33 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -39,6 +39,18 @@
     .cfi_restore \reg
 .endm
 
+.macro SAVE_REG_INCREASE_FRAME reg, frame_adjustment
+    str \reg, [sp, #-(\frame_adjustment)]!
+    .cfi_adjust_cfa_offset (\frame_adjustment)
+    .cfi_rel_offset \reg, 0
+.endm
+
+.macro RESTORE_REG_DECREASE_FRAME reg, frame_adjustment
+    ldr \reg, [sp], #(\frame_adjustment)
+    .cfi_restore \reg
+    .cfi_adjust_cfa_offset -(\frame_adjustment)
+.endm
+
 .macro SAVE_TWO_REGS reg1, reg2, offset
     stp \reg1, \reg2, [sp, #(\offset)]
     .cfi_rel_offset \reg1, (\offset)
@@ -140,6 +152,9 @@
     SAVE_TWO_REGS x29, xLR, 80
 
     // Store ArtMethod* Runtime::callee_save_methods_[kSaveRefsOnly].
+    // Note: We could avoid saving X20 in the case of Baker read
+    // barriers, as it is overwritten by REFRESH_MARKING_REGISTER
+    // later; but it's not worth handling this special case.
     stp xIP0, x20, [sp]
     .cfi_rel_offset x20, 8
 
@@ -151,6 +166,9 @@
 // TODO: Probably no need to restore registers preserved by aapcs64.
 .macro RESTORE_SAVE_REFS_ONLY_FRAME
     // Callee-saves.
+    // Note: Likewise, we could avoid restoring X20 in the case of Baker
+    // read barriers, as it is overwritten by REFRESH_MARKING_REGISTER
+    // later; but it's not worth handling this special case.
     RESTORE_REG x20, 8
     RESTORE_TWO_REGS x21, x22, 16
     RESTORE_TWO_REGS x23, x24, 32
@@ -165,11 +183,6 @@
     DECREASE_FRAME 96
 .endm
 
-.macro RESTORE_SAVE_REFS_ONLY_FRAME_AND_RETURN
-    RESTORE_SAVE_REFS_ONLY_FRAME
-    ret
-.endm
-
 
 .macro SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL
     INCREASE_FRAME 224
@@ -192,6 +205,9 @@
     SAVE_TWO_REGS x5, x6, 112
 
     // x7, Callee-saves.
+    // Note: We could avoid saving X20 in the case of Baker read
+    // barriers, as it is overwritten by REFRESH_MARKING_REGISTER
+    // later; but it's not worth handling this special case.
     SAVE_TWO_REGS x7, x20, 128
     SAVE_TWO_REGS x21, x22, 144
     SAVE_TWO_REGS x23, x24, 160
@@ -250,6 +266,9 @@
     RESTORE_TWO_REGS x5, x6, 112
 
     // x7, Callee-saves.
+    // Note: Likewise, we could avoid restoring X20 in the case of Baker
+    // read barriers, as it is overwritten by REFRESH_MARKING_REGISTER
+    // later; but it's not worth handling this special case.
     RESTORE_TWO_REGS x7, x20, 128
     RESTORE_TWO_REGS x21, x22, 144
     RESTORE_TWO_REGS x23, x24, 160
@@ -358,7 +377,7 @@
     ldp d29, d30, [sp, #240]
     ldr d31,      [sp, #256]
 
-    // Restore core registers.
+    // Restore core registers, except x0.
     RESTORE_TWO_REGS  x1,  x2, 272
     RESTORE_TWO_REGS  x3,  x4, 288
     RESTORE_TWO_REGS  x5,  x6, 304
@@ -379,10 +398,21 @@
 .endm
 
 .macro RESTORE_SAVE_EVERYTHING_FRAME
-    RESTORE_REG            x0, 264
+    RESTORE_REG       x0,      264
     RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0
 .endm
 
+// Macro to refresh the Marking Register (W20).
+//
+// This macro must be called at the end of functions implementing
+// entrypoints that possibly (directly or indirectly) perform a
+// suspend check (before they return).
+.macro REFRESH_MARKING_REGISTER
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+    ldr wMR, [xSELF, #THREAD_IS_GC_MARKING_OFFSET]
+#endif
+.endm
+
 .macro RETURN_IF_RESULT_IS_ZERO
     cbnz x0, 1f                // result non-zero branch over
     ret                        // return
@@ -562,6 +592,7 @@
     bl     \cxx_name                      // (method_idx, this, Thread*, SP)
     mov    xIP0, x1                       // save Method*->code_
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    REFRESH_MARKING_REGISTER
     cbz    x0, 1f                         // did we find the target? if not go to exception delivery
     br     xIP0                           // tail call to target
 1:
@@ -661,13 +692,15 @@
 
 .macro INVOKE_STUB_CALL_AND_RETURN
 
+    REFRESH_MARKING_REGISTER
+
     // load method-> METHOD_QUICK_CODE_OFFSET
     ldr x9, [x0, #ART_METHOD_QUICK_CODE_OFFSET_64]
     // Branch to method.
     blr x9
 
     // Restore return value address and shorty address.
-    ldp x4,x5, [xFP, #16]
+    ldp x4, x5, [xFP, #16]
     .cfi_restore x4
     .cfi_restore x5
 
@@ -1046,6 +1079,7 @@
     stp x3, x4, [sp, #16]                 // Save result and shorty addresses.
     stp xFP, xLR, [sp]                    // Store LR & FP.
     mov xSELF, x5                         // Move thread pointer into SELF register.
+    REFRESH_MARKING_REGISTER
 
     sub sp, sp, #16
     str xzr, [sp]                         // Store null for ArtMethod* slot
@@ -1152,7 +1186,7 @@
     ldp x24, x25, [x0], #-16
     ldp x22, x23, [x0], #-16
     ldp x20, x21, [x0], #-16
-    ldp x18, x19, [x0], #-16
+    ldp x18, x19, [x0], #-16         // X18 & xSELF
     ldp x16, x17, [x0], #-16
     ldp x14, x15, [x0], #-16
     ldp x12, x13, [x0], #-16
@@ -1163,6 +1197,8 @@
     ldp x2, x3, [x0], #-16
     mov sp, x1
 
+    REFRESH_MARKING_REGISTER
+
     // Need to load PC, it's at the end (after the space for the unused XZR). Use x1.
     ldr x1, [x0, #33*8]
     // And the value of x0.
@@ -1213,6 +1249,7 @@
     mov    x1, xSELF                  // pass Thread::Current
     bl     artLockObjectFromCode      // (Object* obj, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     RETURN_IF_W0_IS_ZERO_OR_DELIVER
 END art_quick_lock_object
 
@@ -1221,6 +1258,7 @@
     mov    x1, xSELF                  // pass Thread::Current
     bl     artLockObjectFromCode      // (Object* obj, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     RETURN_IF_W0_IS_ZERO_OR_DELIVER
 END art_quick_lock_object_no_inline
 
@@ -1275,6 +1313,7 @@
     mov    x1, xSELF                  // pass Thread::Current
     bl     artUnlockObjectFromCode    // (Object* obj, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     RETURN_IF_W0_IS_ZERO_OR_DELIVER
 END art_quick_unlock_object
 
@@ -1283,6 +1322,7 @@
     mov    x1, xSELF                  // pass Thread::Current
     bl     artUnlockObjectFromCode    // (Object* obj, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     RETURN_IF_W0_IS_ZERO_OR_DELIVER
 END art_quick_unlock_object_no_inline
 
@@ -1356,7 +1396,7 @@
      */
 .macro READ_BARRIER xDest, wDest, xObj, xTemp, wTemp, offset, number
 #ifdef USE_READ_BARRIER
-#ifdef USE_BAKER_READ_BARRIER
+# ifdef USE_BAKER_READ_BARRIER
     ldr \wTemp, [\xObj, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
     tbnz \wTemp, #LOCK_WORD_READ_BARRIER_STATE_SHIFT, .Lrb_slowpath\number
     // False dependency to avoid needing load/load fence.
@@ -1364,7 +1404,7 @@
     ldr \wDest, [\xObj, #\offset]   // Heap reference = 32b. This also zero-extends to \xDest.
     UNPOISON_HEAP_REF \wDest
     b .Lrb_exit\number
-#endif
+# endif  // USE_BAKER_READ_BARRIER
 .Lrb_slowpath\number:
     // Store registers used in art_quick_aput_obj (x0-x4, LR), stack is 16B aligned.
     SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 48
@@ -1471,6 +1511,7 @@
     mov    x1, xSELF                  // pass Thread::Current
     bl     \entrypoint                // (uint32_t type_idx, Method* method, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     \return
 END \name
 .endm
@@ -1483,6 +1524,7 @@
     mov    x2, xSELF                  // pass Thread::Current
     bl     \entrypoint                // (uint32_t type_idx, Method* method, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     \return
 END \name
 .endm
@@ -1495,6 +1537,7 @@
     mov    x3, xSELF                  // pass Thread::Current
     bl     \entrypoint
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     \return
 END \name
 .endm
@@ -1507,8 +1550,8 @@
     mov    x4, xSELF                  // pass Thread::Current
     bl     \entrypoint                //
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     \return
-    DELIVER_PENDING_EXCEPTION
 END \name
 .endm
 
@@ -1520,6 +1563,7 @@
     mov    x1, xSELF                  // pass Thread::Current
     bl     \entrypoint                // (uint32_t type_idx, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     \return
 END \name
 .endm
@@ -1531,6 +1575,7 @@
     mov    x2, xSELF                  // pass Thread::Current
     bl     \entrypoint
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     \return
 END \name
 .endm
@@ -1542,6 +1587,7 @@
     mov    x3, xSELF                  // pass Thread::Current
     bl     \entrypoint
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     \return
 END \name
 .endm
@@ -1556,6 +1602,7 @@
     cbz   w0, 1f                      // If result is null, deliver the OOME.
     .cfi_remember_state
     RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0
+    REFRESH_MARKING_REGISTER
     ret                        // return
     .cfi_restore_state
     .cfi_def_cfa_offset FRAME_SIZE_SAVE_EVERYTHING  // workaround for clang bug: 31975598
@@ -1588,6 +1635,9 @@
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
 
+// Note: Functions `art{Get,Set}<Kind>{Static,Instance>FromCompiledCode` are
+// defined by macros in runtime/entrypoints/quick/quick_field_entrypoints.cc.
+
 ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
 ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
 ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
@@ -1752,6 +1802,7 @@
     mov    x1, xSELF                                // pass Thread::Current
     bl     \cxx_name
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 END \c_name
 .endm
@@ -1815,6 +1866,7 @@
     mov    x1, xSELF                           // Pass Thread::Current.
     bl     \entrypoint                         // (mirror::Class*, Thread*)
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 END \name
 .endm
@@ -1825,7 +1877,7 @@
 GENERATE_ALLOC_OBJECT_RESOLVED_TLAB art_quick_alloc_object_initialized_tlab, artAllocObjectFromCodeInitializedTLAB, /* isInitialized */ 1
 
 .macro ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED_WITH_SIZE slowPathLabel, xClass, wClass, xCount, wCount, xTemp0, wTemp0, xTemp1, wTemp1, xTemp2, wTemp2
-    and    \xTemp1, \xTemp1, #OBJECT_ALIGNMENT_MASK_TOGGLED64 // Apply alignemnt mask
+    and    \xTemp1, \xTemp1, #OBJECT_ALIGNMENT_MASK_TOGGLED64 // Apply alignment mask
                                                               // (addr + 7) & ~7. The mask must
                                                               // be 64 bits to keep high bits in
                                                               // case of overflow.
@@ -1887,6 +1939,7 @@
     mov    x2, xSELF                  // pass Thread::Current
     bl     \entrypoint
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 END \name
 .endm
@@ -1937,8 +1990,8 @@
     add    \xTemp1, \xTemp1, #(MIRROR_WIDE_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK)
 .endm
 
-# TODO(ngeoffray): art_quick_alloc_array_resolved_region_tlab is not used for arm64, remove
-# the entrypoint once all backends have been updated to use the size variants.
+// TODO(ngeoffray): art_quick_alloc_array_resolved_region_tlab is not used for arm64, remove
+// the entrypoint once all backends have been updated to use the size variants.
 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_UNKNOWN
 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved8_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_8
 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_16
@@ -1959,6 +2012,7 @@
     mov    x0, xSELF
     bl     artTestSuspendFromCode             // (Thread*)
     RESTORE_SAVE_EVERYTHING_FRAME
+    REFRESH_MARKING_REGISTER
     ret
 END art_quick_test_suspend
 
@@ -1966,7 +2020,9 @@
     mov    x0, xSELF
     SETUP_SAVE_REFS_ONLY_FRAME                // save callee saves for stack crawl
     bl     artTestSuspendFromCode             // (Thread*)
-    RESTORE_SAVE_REFS_ONLY_FRAME_AND_RETURN
+    RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
+    ret
 END art_quick_implicit_suspend
 
      /*
@@ -1983,6 +2039,7 @@
     ldr     x2, [xSELF, THREAD_EXCEPTION_OFFSET]
     cbnz    x2, .Lexception_in_proxy    // success if no exception is pending
     RESTORE_SAVE_REFS_AND_ARGS_FRAME    // Restore frame
+    REFRESH_MARKING_REGISTER
     fmov    d0, x0                      // Store result in d0 in case it was float or double
     ret                                 // return on success
 .Lexception_in_proxy:
@@ -2035,6 +2092,7 @@
     mov xIP0, x0            // Remember returned code pointer in xIP0.
     ldr x0, [sp, #0]        // artQuickResolutionTrampoline puts called method in *SP.
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    REFRESH_MARKING_REGISTER
     br xIP0
 1:
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
@@ -2170,6 +2228,7 @@
 
     // Tear down the callee-save frame.
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    REFRESH_MARKING_REGISTER
 
     // store into fpr, for when it's a fpr return...
     fmov d0, x0
@@ -2202,6 +2261,7 @@
     bl   artQuickToInterpreterBridge
 
     RESTORE_SAVE_REFS_AND_ARGS_FRAME       // TODO: no need to restore arguments in this case.
+    REFRESH_MARKING_REGISTER
 
     fmov d0, x0
 
@@ -2231,6 +2291,7 @@
     mov   x0, x20             // Reload method reference.
 
     RESTORE_SAVE_REFS_AND_ARGS_FRAME  // Note: will restore xSELF
+    REFRESH_MARKING_REGISTER
     cbz   xIP0, 1f            // Deliver the pending exception if method is null.
     adr   xLR, art_quick_instrumentation_exit
     br    xIP0                // Tail-call method with lr set to art_quick_instrumentation_exit.
@@ -2263,6 +2324,7 @@
     .cfi_adjust_cfa_offset -16
 
     RESTORE_SAVE_REFS_ONLY_FRAME
+    REFRESH_MARKING_REGISTER
     cbz   xIP0, 1f            // Handle error
     br    xIP0                // Tail-call out.
 1:
@@ -2831,6 +2893,7 @@
 .Lcleanup_and_return:
     DECREASE_FRAME 16
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    REFRESH_MARKING_REGISTER
     RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
 
     .section    .rodata                           // Place handler table in read-only section away from text.
diff --git a/runtime/arch/arm64/registers_arm64.h b/runtime/arch/arm64/registers_arm64.h
index 4683fc3..d4c9192 100644
--- a/runtime/arch/arm64/registers_arm64.h
+++ b/runtime/arch/arm64/registers_arm64.h
@@ -61,6 +61,7 @@
   kNumberOfXRegisters = 33,
   // Aliases.
   TR  = X19,     // ART Thread Register - Managed Runtime (Callee Saved Reg)
+  MR  = X20,     // ART Marking Register - Managed Runtime (Callee Saved Reg)
   IP0 = X16,     // Used as scratch by VIXL.
   IP1 = X17,     // Used as scratch by ART JNI Assembler.
   FP  = X29,
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index a5a65e6..00e3d67 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1662,13 +1662,37 @@
 .endm
 
 // Generate the allocation entrypoints for each allocator.
-GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR
+GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS
+// Comment out allocators that have mips specific asm.
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED32(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED64(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
+
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_tlab, TLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_tlab, TLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED32(_tlab, TLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED64(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB)
 
 // A hand-written override for:
 //   GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
 //   GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc)
-.macro ART_QUICK_ALLOC_OBJECT_ROSALLOC c_name, cxx_name
-ENTRY \c_name
+.macro ART_QUICK_ALLOC_OBJECT_ROSALLOC c_name, cxx_name, isInitialized
+ENTRY_NO_GP \c_name
     # Fast path rosalloc allocation
     # a0: type
     # s1: Thread::Current
@@ -1688,6 +1712,11 @@
     li    $t5, ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE          # Check if size is for a thread local
                                                                # allocation. Also does the
                                                                # initialized and finalizable checks.
+    # When isInitialized == 0, then the class is potentially not yet initialized.
+    # If the class is not yet initialized, the object size will be very large to force the branch
+    # below to be taken.
+    #
+    # See InitializeClassVisitors in class-inl.h for more details.
     bgtu  $t1, $t5, .Lslow_path_\c_name
 
     # Compute the rosalloc bracket index from the size. Since the size is already aligned we can
@@ -1728,12 +1757,19 @@
     addiu $t5, $t5, -1
     sw    $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2)
 
+.if \isInitialized == 0
+    # This barrier is only necessary when the allocation also requires a class initialization check.
+    #
+    # If the class is already observably initialized, then new-instance allocations are protected
+    # from publishing by the compiler which inserts its own StoreStore barrier.
     sync                                                          # Fence.
-
+.endif
     jalr  $zero, $ra
     nop
 
   .Lslow_path_\c_name:
+    addiu $t9, $t9, (.Lslow_path_\c_name - \c_name) + 4
+    .cpload $t9
     SETUP_SAVE_REFS_ONLY_FRAME
     la    $t9, \cxx_name
     jalr  $t9
@@ -1742,11 +1778,197 @@
 END \c_name
 .endm
 
-ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_resolved_rosalloc, artAllocObjectFromCodeResolvedRosAlloc
-ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, artAllocObjectFromCodeInitializedRosAlloc
+ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_resolved_rosalloc, artAllocObjectFromCodeResolvedRosAlloc, /* isInitialized */ 0
+ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, artAllocObjectFromCodeInitializedRosAlloc, /* isInitialized */ 1
 
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
+// The common fast path code for art_quick_alloc_object_resolved/initialized_tlab
+// and art_quick_alloc_object_resolved/initialized_region_tlab.
+//
+// a0: type, s1(rSELF): Thread::Current.
+// Need to preserve a0 to the slow path.
+//
+// If isInitialized=1 then the compiler assumes the object's class has already been initialized.
+// If isInitialized=0 the compiler can only assume it's been at least resolved.
+.macro ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH slowPathLabel isInitialized
+    lw    $v0, THREAD_LOCAL_POS_OFFSET(rSELF)          # Load thread_local_pos.
+    lw    $a2, THREAD_LOCAL_END_OFFSET(rSELF)          # Load thread_local_end.
+    subu  $a3, $a2, $v0                                # Compute the remaining buffer size.
+    lw    $t0, MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET($a0)  # Load the object size.
+
+    # When isInitialized == 0, then the class is potentially not yet initialized.
+    # If the class is not yet initialized, the object size will be very large to force the branch
+    # below to be taken.
+    #
+    # See InitializeClassVisitors in class-inl.h for more details.
+    bgtu  $t0, $a3, \slowPathLabel                     # Check if it fits.
+    addu  $t1, $v0, $t0                                # Add object size to tlab pos (in branch
+                                                       # delay slot).
+    # "Point of no slow path". Won't go to the slow path from here on.
+    sw    $t1, THREAD_LOCAL_POS_OFFSET(rSELF)          # Store new thread_local_pos.
+    lw    $a2, THREAD_LOCAL_OBJECTS_OFFSET(rSELF)      # Increment thread_local_objects.
+    addiu $a2, $a2, 1
+    sw    $a2, THREAD_LOCAL_OBJECTS_OFFSET(rSELF)
+    POISON_HEAP_REF $a0
+    sw    $a0, MIRROR_OBJECT_CLASS_OFFSET($v0)         # Store the class pointer.
+
+.if \isInitialized == 0
+    # This barrier is only necessary when the allocation also requires a class initialization check.
+    #
+    # If the class is already observably initialized, then new-instance allocations are protected
+    # from publishing by the compiler which inserts its own StoreStore barrier.
+    sync                                               # Fence.
+.endif
+    jalr  $zero, $ra
+    nop
+.endm
+
+// The common code for art_quick_alloc_object_resolved/initialized_tlab
+// and art_quick_alloc_object_resolved/initialized_region_tlab.
+.macro GENERATE_ALLOC_OBJECT_TLAB name, entrypoint, isInitialized
+ENTRY_NO_GP \name
+    # Fast path tlab allocation.
+    # a0: type, s1(rSELF): Thread::Current.
+    ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lslow_path_\name, \isInitialized
+.Lslow_path_\name:
+    addiu $t9, $t9, (.Lslow_path_\name - \name) + 4
+    .cpload $t9
+    SETUP_SAVE_REFS_ONLY_FRAME                         # Save callee saves in case of GC.
+    la    $t9, \entrypoint
+    jalr  $t9                                          # (mirror::Class*, Thread*)
+    move  $a1, rSELF                                   # Pass Thread::Current.
+    RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+END \name
+.endm
+
+GENERATE_ALLOC_OBJECT_TLAB art_quick_alloc_object_resolved_region_tlab, artAllocObjectFromCodeResolvedRegionTLAB, /* isInitialized */ 0
+GENERATE_ALLOC_OBJECT_TLAB art_quick_alloc_object_initialized_region_tlab, artAllocObjectFromCodeInitializedRegionTLAB, /* isInitialized */ 1
+GENERATE_ALLOC_OBJECT_TLAB art_quick_alloc_object_resolved_tlab, artAllocObjectFromCodeResolvedTLAB, /* isInitialized */ 0
+GENERATE_ALLOC_OBJECT_TLAB art_quick_alloc_object_initialized_tlab, artAllocObjectFromCodeInitializedTLAB, /* isInitialized */ 1
+
+// The common fast path code for art_quick_alloc_array_resolved/initialized_tlab
+// and art_quick_alloc_array_resolved/initialized_region_tlab.
+//
+// a0: type, a1: component_count, a2: total_size, s1(rSELF): Thread::Current.
+// Need to preserve a0 and a1 to the slow path.
+.macro ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED_WITH_SIZE slowPathLabel
+    li    $a3, OBJECT_ALIGNMENT_MASK_TOGGLED           # Apply alignemnt mask
+    and   $a2, $a2, $a3                                # (addr + 7) & ~7.
+
+    lw    $v0, THREAD_LOCAL_POS_OFFSET(rSELF)          # Load thread_local_pos.
+    lw    $t1, THREAD_LOCAL_END_OFFSET(rSELF)          # Load thread_local_end.
+    subu  $t2, $t1, $v0                                # Compute the remaining buffer size.
+    bgtu  $a2, $t2, \slowPathLabel                     # Check if it fits.
+    addu  $a2, $v0, $a2                                # Add object size to tlab pos (in branch
+                                                       # delay slot).
+
+    # "Point of no slow path". Won't go to the slow path from here on.
+    sw    $a2, THREAD_LOCAL_POS_OFFSET(rSELF)          # Store new thread_local_pos.
+    lw    $a2, THREAD_LOCAL_OBJECTS_OFFSET(rSELF)      # Increment thread_local_objects.
+    addiu $a2, $a2, 1
+    sw    $a2, THREAD_LOCAL_OBJECTS_OFFSET(rSELF)
+    POISON_HEAP_REF $a0
+    sw    $a0, MIRROR_OBJECT_CLASS_OFFSET($v0)         # Store the class pointer.
+    jalr  $zero, $ra
+    sw    $a1, MIRROR_ARRAY_LENGTH_OFFSET($v0)         # Store the array length.
+.endm
+
+.macro GENERATE_ALLOC_ARRAY_TLAB name, entrypoint, size_setup
+ENTRY_NO_GP \name
+    # Fast path array allocation for region tlab allocation.
+    # a0: mirror::Class* type
+    # a1: int32_t component_count
+    # s1(rSELF): Thread::Current
+    \size_setup .Lslow_path_\name
+    ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED_WITH_SIZE .Lslow_path_\name
+.Lslow_path_\name:
+    # a0: mirror::Class* type
+    # a1: int32_t component_count
+    # a2: Thread* self
+    addiu $t9, $t9, (.Lslow_path_\name - \name) + 4
+    .cpload $t9
+    SETUP_SAVE_REFS_ONLY_FRAME                         # Save callee saves in case of GC.
+    la    $t9, \entrypoint
+    jalr  $t9
+    move  $a2, rSELF                                   # Pass Thread::Current.
+    RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+END \name
+.endm
+
+.macro COMPUTE_ARRAY_SIZE_UNKNOWN slow_path
+    break                                              # We should never enter here.
+                                                       # Code below is for reference.
+                                                       # Possibly a large object, go slow.
+                                                       # Also does negative array size check.
+    li    $a2, ((MIN_LARGE_OBJECT_THRESHOLD - MIRROR_WIDE_ARRAY_DATA_OFFSET) / 8)
+    bgtu  $a1, $a2, \slow_path
+                                                       # Array classes are never finalizable
+                                                       # or uninitialized, no need to check.
+    lw    $a3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET($a0) # Load component type.
+    UNPOISON_HEAP_REF $a3
+    lw    $a3, MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET($a3)
+    srl   $a3, $a3, PRIMITIVE_TYPE_SIZE_SHIFT_SHIFT    # Component size shift is in high 16 bits.
+    sllv  $a2, $a1, $a3                                # Calculate data size.
+                                                       # Add array data offset and alignment.
+    addiu $a2, $a2, (MIRROR_INT_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK)
+#if MIRROR_WIDE_ARRAY_DATA_OFFSET != MIRROR_INT_ARRAY_DATA_OFFSET + 4
+#error Long array data offset must be 4 greater than int array data offset.
+#endif
+
+    addiu $a3, $a3, 1                                  # Add 4 to the length only if the component
+    andi  $a3, $a3, 4                                  # size shift is 3 (for 64 bit alignment).
+    addu  $a2, $a2, $a3
+.endm
+
+.macro COMPUTE_ARRAY_SIZE_8 slow_path
+    # Possibly a large object, go slow.
+    # Also does negative array size check.
+    li    $a2, (MIN_LARGE_OBJECT_THRESHOLD - MIRROR_INT_ARRAY_DATA_OFFSET)
+    bgtu  $a1, $a2, \slow_path
+    # Add array data offset and alignment (in branch delay slot).
+    addiu $a2, $a1, (MIRROR_INT_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK)
+.endm
+
+.macro COMPUTE_ARRAY_SIZE_16 slow_path
+    # Possibly a large object, go slow.
+    # Also does negative array size check.
+    li    $a2, ((MIN_LARGE_OBJECT_THRESHOLD - MIRROR_INT_ARRAY_DATA_OFFSET) / 2)
+    bgtu  $a1, $a2, \slow_path
+    sll   $a2, $a1, 1
+    # Add array data offset and alignment.
+    addiu $a2, $a2, (MIRROR_INT_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK)
+.endm
+
+.macro COMPUTE_ARRAY_SIZE_32 slow_path
+    # Possibly a large object, go slow.
+    # Also does negative array size check.
+    li    $a2, ((MIN_LARGE_OBJECT_THRESHOLD - MIRROR_INT_ARRAY_DATA_OFFSET) / 4)
+    bgtu  $a1, $a2, \slow_path
+    sll   $a2, $a1, 2
+    # Add array data offset and alignment.
+    addiu $a2, $a2, (MIRROR_INT_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK)
+.endm
+
+.macro COMPUTE_ARRAY_SIZE_64 slow_path
+    # Possibly a large object, go slow.
+    # Also does negative array size check.
+    li    $a2, ((MIN_LARGE_OBJECT_THRESHOLD - MIRROR_LONG_ARRAY_DATA_OFFSET) / 8)
+    bgtu  $a1, $a2, \slow_path
+    sll   $a2, $a1, 3
+    # Add array data offset and alignment.
+    addiu $a2, $a2, (MIRROR_WIDE_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK)
+.endm
+
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_UNKNOWN
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved8_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_8
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_16
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_32
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_64
+
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_UNKNOWN
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved8_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_8
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_16
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64
 
 // Macro for string and type resolution and initialization.
 // $a0 is both input and output.
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 10074fd..d427fe3 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1611,13 +1611,37 @@
 .endm
 
 // Generate the allocation entrypoints for each allocator.
-GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR
+GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS
+// Comment out allocators that have mips64 specific asm.
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED32(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED64(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
+
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(_tlab, TLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(_tlab, TLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED32(_tlab, TLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED64(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB)
 
 // A hand-written override for:
 //   GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
 //   GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc)
-.macro ART_QUICK_ALLOC_OBJECT_ROSALLOC c_name, cxx_name
-ENTRY \c_name
+.macro ART_QUICK_ALLOC_OBJECT_ROSALLOC c_name, cxx_name, isInitialized
+ENTRY_NO_GP \c_name
     # Fast path rosalloc allocation
     # a0: type
     # s1: Thread::Current
@@ -1637,6 +1661,11 @@
     li     $a5, ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE      # Check if size is for a thread local
                                                             # allocation. Also does the initialized
                                                             # and finalizable checks.
+    # When isInitialized == 0, then the class is potentially not yet initialized.
+    # If the class is not yet initialized, the object size will be very large to force the branch
+    # below to be taken.
+    #
+    # See InitializeClassVisitors in class-inl.h for more details.
     bltuc  $a5, $t1, .Lslow_path_\c_name
 
     # Compute the rosalloc bracket index from the size. Since the size is already aligned we can
@@ -1667,7 +1696,7 @@
 
     # Push the new object onto the thread local allocation stack and increment the thread local
     # allocation stack top.
-    sd     $v0, 0($t3)
+    sw     $v0, 0($t3)
     daddiu $t3, $t3, COMPRESSED_REFERENCE_SIZE
     sd     $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1)
 
@@ -1676,12 +1705,17 @@
     addiu  $a5, $a5, -1
     sw     $a5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2)
 
+.if \isInitialized == 0
+    # This barrier is only necessary when the allocation also requires a class initialization check.
+    #
+    # If the class is already observably initialized, then new-instance allocations are protected
+    # from publishing by the compiler which inserts its own StoreStore barrier.
     sync                                         # Fence.
-
-    jalr   $zero, $ra
-    .cpreturn                                    # Restore gp from t8 in branch delay slot.
+.endif
+    jic    $ra, 0
 
 .Lslow_path_\c_name:
+    SETUP_GP
     SETUP_SAVE_REFS_ONLY_FRAME
     jal    \cxx_name
     move   $a1 ,$s1                              # Pass self as argument.
@@ -1689,11 +1723,180 @@
 END \c_name
 .endm
 
-ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_resolved_rosalloc, artAllocObjectFromCodeResolvedRosAlloc
-ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, artAllocObjectFromCodeInitializedRosAlloc
+ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_resolved_rosalloc, artAllocObjectFromCodeResolvedRosAlloc, /* isInitialized */ 0
+ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, artAllocObjectFromCodeInitializedRosAlloc, /* isInitialized */ 1
 
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
+// The common fast path code for art_quick_alloc_object_resolved/initialized_tlab
+// and art_quick_alloc_object_resolved/initialized_region_tlab.
+//
+// a0: type, s1(rSELF): Thread::Current
+// Need to preserve a0 to the slow path.
+//
+// If isInitialized=1 then the compiler assumes the object's class has already been initialized.
+// If isInitialized=0 the compiler can only assume it's been at least resolved.
+.macro ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH slowPathLabel isInitialized
+    ld     $v0, THREAD_LOCAL_POS_OFFSET(rSELF)         # Load thread_local_pos.
+    ld     $a2, THREAD_LOCAL_END_OFFSET(rSELF)         # Load thread_local_end.
+    lwu    $t0, MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET($a0)  # Load the object size.
+    daddu  $a3, $v0, $t0                               # Add object size to tlab pos.
+
+    # When isInitialized == 0, then the class is potentially not yet initialized.
+    # If the class is not yet initialized, the object size will be very large to force the branch
+    # below to be taken.
+    #
+    # See InitializeClassVisitors in class-inl.h for more details.
+    bltuc  $a2, $a3, \slowPathLabel                    # Check if it fits, overflow works since the
+                                                       # tlab pos and end are 32 bit values.
+    # "Point of no slow path". Won't go to the slow path from here on.
+    sd     $a3, THREAD_LOCAL_POS_OFFSET(rSELF)         # Store new thread_local_pos.
+    ld     $a2, THREAD_LOCAL_OBJECTS_OFFSET(rSELF)     # Increment thread_local_objects.
+    daddiu $a2, $a2, 1
+    sd     $a2, THREAD_LOCAL_OBJECTS_OFFSET(rSELF)
+    POISON_HEAP_REF $a0
+    sw     $a0, MIRROR_OBJECT_CLASS_OFFSET($v0)        # Store the class pointer.
+
+.if \isInitialized == 0
+    # This barrier is only necessary when the allocation also requires a class initialization check.
+    #
+    # If the class is already observably initialized, then new-instance allocations are protected
+    # from publishing by the compiler which inserts its own StoreStore barrier.
+    sync                                               # Fence.
+.endif
+    jic    $ra, 0
+.endm
+
+// The common code for art_quick_alloc_object_resolved/initialized_tlab
+// and art_quick_alloc_object_resolved/initialized_region_tlab.
+.macro GENERATE_ALLOC_OBJECT_TLAB name, entrypoint, isInitialized
+ENTRY_NO_GP \name
+    # Fast path tlab allocation.
+    # a0: type, s1(rSELF): Thread::Current.
+    ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lslow_path_\name, \isInitialized
+.Lslow_path_\name:
+    SETUP_GP
+    SETUP_SAVE_REFS_ONLY_FRAME                         # Save callee saves in case of GC.
+    jal    \entrypoint                                 # (mirror::Class*, Thread*)
+    move   $a1, rSELF                                  # Pass Thread::Current.
+    RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+END \name
+.endm
+
+GENERATE_ALLOC_OBJECT_TLAB art_quick_alloc_object_resolved_region_tlab, artAllocObjectFromCodeResolvedRegionTLAB, /* isInitialized */ 0
+GENERATE_ALLOC_OBJECT_TLAB art_quick_alloc_object_initialized_region_tlab, artAllocObjectFromCodeInitializedRegionTLAB, /* isInitialized */ 1
+GENERATE_ALLOC_OBJECT_TLAB art_quick_alloc_object_resolved_tlab, artAllocObjectFromCodeResolvedTLAB, /* isInitialized */ 0
+GENERATE_ALLOC_OBJECT_TLAB art_quick_alloc_object_initialized_tlab, artAllocObjectFromCodeInitializedTLAB, /* isInitialized */ 1
+
+// The common fast path code for art_quick_alloc_array_resolved/initialized_tlab
+// and art_quick_alloc_array_resolved/initialized_region_tlab.
+//
+// a0: type, a1: component_count, a2: total_size, s1(rSELF): Thread::Current.
+// Need to preserve a0 and a1 to the slow path.
+.macro ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED_WITH_SIZE slowPathLabel
+    dli    $a3, OBJECT_ALIGNMENT_MASK_TOGGLED64        # Apply alignemnt mask (addr + 7) & ~7.
+    and    $a2, $a2, $a3                               # The mask must be 64 bits to keep high
+                                                       # bits in case of overflow.
+    # Negative sized arrays are handled here since a1 holds a zero extended 32 bit value.
+    # Negative ints become large 64 bit unsigned ints which will always be larger than max signed
+    # 32 bit int. Since the max shift for arrays is 3, it can not become a negative 64 bit int.
+    dli    $a3, MIN_LARGE_OBJECT_THRESHOLD
+    bgeuc  $a2, $a3, \slowPathLabel                    # Possibly a large object, go slow path.
+
+    ld     $v0, THREAD_LOCAL_POS_OFFSET(rSELF)         # Load thread_local_pos.
+    ld     $t1, THREAD_LOCAL_END_OFFSET(rSELF)         # Load thread_local_end.
+    dsubu  $t2, $t1, $v0                               # Compute the remaining buffer size.
+    bltuc  $t2, $a2, \slowPathLabel                    # Check tlab for space, note that we use
+                                                       # (end - begin) to handle negative size
+                                                       # arrays. It is assumed that a negative size
+                                                       # will always be greater unsigned than region
+                                                       # size.
+
+    # "Point of no slow path". Won't go to the slow path from here on.
+    daddu  $a2, $v0, $a2                               # Add object size to tlab pos.
+    sd     $a2, THREAD_LOCAL_POS_OFFSET(rSELF)         # Store new thread_local_pos.
+    ld     $a2, THREAD_LOCAL_OBJECTS_OFFSET(rSELF)     # Increment thread_local_objects.
+    daddiu $a2, $a2, 1
+    sd     $a2, THREAD_LOCAL_OBJECTS_OFFSET(rSELF)
+    POISON_HEAP_REF $a0
+    sw     $a0, MIRROR_OBJECT_CLASS_OFFSET($v0)        # Store the class pointer.
+    sw     $a1, MIRROR_ARRAY_LENGTH_OFFSET($v0)        # Store the array length.
+
+    jic    $ra, 0
+.endm
+
+.macro GENERATE_ALLOC_ARRAY_TLAB name, entrypoint, size_setup
+ENTRY_NO_GP \name
+    # Fast path array allocation for region tlab allocation.
+    # a0: mirror::Class* type
+    # a1: int32_t component_count
+    # s1(rSELF): Thread::Current
+    dext   $a4, $a1, 0, 32                             # Create zero-extended component_count. Value
+                                                       # in a1 is preserved in a case of slow path.
+    \size_setup .Lslow_path_\name
+    ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED_WITH_SIZE .Lslow_path_\name
+.Lslow_path_\name:
+    # a0: mirror::Class* type
+    # a1: int32_t component_count
+    # a2: Thread* self
+    SETUP_GP
+    SETUP_SAVE_REFS_ONLY_FRAME                         # Save callee saves in case of GC.
+    jal    \entrypoint
+    move   $a2, rSELF                                  # Pass Thread::Current.
+    RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+END \name
+.endm
+
+.macro COMPUTE_ARRAY_SIZE_UNKNOWN slow_path
+    # Array classes are never finalizable or uninitialized, no need to check.
+    lwu    $a3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET($a0) # Load component type.
+    UNPOISON_HEAP_REF $a3
+    lw     $a3, MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET($a3)
+    dsrl   $a3, $a3, PRIMITIVE_TYPE_SIZE_SHIFT_SHIFT   # Component size shift is in high 16 bits.
+    dsllv  $a2, $a4, $a3                               # Calculate data size.
+                                                       # Add array data offset and alignment.
+    daddiu $a2, $a2, (MIRROR_INT_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK)
+#if MIRROR_WIDE_ARRAY_DATA_OFFSET != MIRROR_INT_ARRAY_DATA_OFFSET + 4
+#error Long array data offset must be 4 greater than int array data offset.
+#endif
+
+    daddiu $a3, $a3, 1                                 # Add 4 to the length only if the component
+    andi   $a3, $a3, 4                                 # size shift is 3 (for 64 bit alignment).
+    daddu  $a2, $a2, $a3
+.endm
+
+.macro COMPUTE_ARRAY_SIZE_8 slow_path
+    # Add array data offset and alignment.
+    daddiu $a2, $a4, (MIRROR_INT_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK)
+.endm
+
+.macro COMPUTE_ARRAY_SIZE_16 slow_path
+    dsll   $a2, $a4, 1
+    # Add array data offset and alignment.
+    daddiu $a2, $a2, (MIRROR_INT_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK)
+.endm
+
+.macro COMPUTE_ARRAY_SIZE_32 slow_path
+    dsll   $a2, $a4, 2
+    # Add array data offset and alignment.
+    daddiu $a2, $a2, (MIRROR_INT_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK)
+.endm
+
+.macro COMPUTE_ARRAY_SIZE_64 slow_path
+    dsll   $a2, $a4, 3
+    # Add array data offset and alignment.
+    daddiu $a2, $a2, (MIRROR_WIDE_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK)
+.endm
+
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_UNKNOWN
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved8_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_8
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_16
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_32
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, COMPUTE_ARRAY_SIZE_64
+
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_UNKNOWN
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved8_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_8
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_16
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32
+GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64
 
 // Macro for string and type resolution and initialization.
 // $a0 is both input and output.
diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S
index 2b3525b..c091b0e 100644
--- a/runtime/arch/quick_alloc_entrypoints.S
+++ b/runtime/arch/quick_alloc_entrypoints.S
@@ -53,7 +53,7 @@
 .endm
 
 // Generate the allocation entrypoints for each allocator. This is used as an alternative to
-// GNERATE_ALL_ALLOC_ENTRYPOINTS for selectively implementing allocation fast paths in
+// GENERATE_ALL_ALLOC_ENTRYPOINTS for selectively implementing allocation fast paths in
 // hand-written assembly.
 #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(c_suffix, cxx_suffix) \
   ONE_ARG_DOWNCALL art_quick_alloc_object_resolved ## c_suffix, artAllocObjectFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
@@ -78,11 +78,6 @@
 #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED64(c_suffix, cxx_suffix) \
   TWO_ARG_DOWNCALL art_quick_alloc_array_resolved64 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 
-.macro GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR
-GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_REGION_TLAB_ALLOCATORS
-GENERATE_ALLOC_ENTRYPOINTS_FOR_REGION_TLAB_ALLOCATOR
-.endm
-
 .macro GENERATE_ALLOC_ENTRYPOINTS_FOR_REGION_TLAB_ALLOCATOR
 // This is to be separately defined for each architecture to allow a hand-written assembly fast path.
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 3e7ed97..45dd596 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -44,6 +44,7 @@
 #include "oat_file-inl.h"
 #include "runtime_callbacks.h"
 #include "scoped_thread_state_change-inl.h"
+#include "vdex_file.h"
 #include "well_known_classes.h"
 
 namespace art {
@@ -584,25 +585,20 @@
 }
 
 const uint8_t* ArtMethod::GetQuickenedInfo(PointerSize pointer_size) {
-  bool found = false;
-  OatFile::OatMethod oat_method = FindOatMethodFor(this, pointer_size, &found);
-  if (!found || (oat_method.GetQuickCode() != nullptr)) {
-    return nullptr;
-  }
   if (kIsVdexEnabled) {
-    const OatQuickMethodHeader* header = oat_method.GetOatQuickMethodHeader();
-    // OatMethod without a header: no quickening table.
-    if (header == nullptr) {
+    const DexFile& dex_file = GetDeclaringClass()->GetDexFile();
+    const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
+    if (oat_dex_file == nullptr || (oat_dex_file->GetOatFile() == nullptr)) {
       return nullptr;
     }
-    // The table is in the .vdex file.
-    const OatFile::OatDexFile* oat_dex_file = GetDexCache()->GetDexFile()->GetOatDexFile();
-    const OatFile* oat_file = oat_dex_file->GetOatFile();
-    if (oat_file == nullptr) {
-      return nullptr;
-    }
-    return oat_file->DexBegin() + header->GetVmapTableOffset();
+    return oat_dex_file->GetOatFile()->GetVdexFile()->GetQuickenedInfoOf(
+        dex_file, GetCodeItemOffset());
   } else {
+    bool found = false;
+    OatFile::OatMethod oat_method = FindOatMethodFor(this, pointer_size, &found);
+    if (!found || (oat_method.GetQuickCode() != nullptr)) {
+      return nullptr;
+    }
     return oat_method.GetVmapTable();
   }
 }
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index ebde82d..a484c5c 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -336,7 +336,8 @@
     auto* end = reinterpret_cast<uint8_t*>(ptr) + aligned_ptr_size;
     // If we haven't allocated anything else, we can safely extend.
     if (end == ptr_) {
-      DCHECK(!IsRunningOnMemoryTool());  // Red zone prevents end == ptr_.
+      // Red zone prevents end == ptr_ (unless input = allocator state = null).
+      DCHECK(!IsRunningOnMemoryTool() || ptr_ == nullptr);
       const size_t aligned_new_size = RoundUp(new_size, kAlignment);
       const size_t size_delta = aligned_new_size - aligned_ptr_size;
       // Check remain space.
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index b0394a5..a472b67 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -68,6 +68,7 @@
 Mutex* Locks::thread_suspend_count_lock_ = nullptr;
 Mutex* Locks::trace_lock_ = nullptr;
 Mutex* Locks::unexpected_signal_lock_ = nullptr;
+Mutex* Locks::user_code_suspension_lock_ = nullptr;
 Uninterruptible Roles::uninterruptible_;
 ReaderWriterMutex* Locks::jni_globals_lock_ = nullptr;
 Mutex* Locks::jni_weak_globals_lock_ = nullptr;
@@ -1029,6 +1030,7 @@
     DCHECK(thread_suspend_count_lock_ != nullptr);
     DCHECK(trace_lock_ != nullptr);
     DCHECK(unexpected_signal_lock_ != nullptr);
+    DCHECK(user_code_suspension_lock_ != nullptr);
     DCHECK(dex_lock_ != nullptr);
   } else {
     // Create global locks in level order from highest lock level to lowest.
@@ -1045,6 +1047,10 @@
       } \
       current_lock_level = new_level;
 
+    UPDATE_CURRENT_LOCK_LEVEL(kUserCodeSuspensionLock);
+    DCHECK(user_code_suspension_lock_ == nullptr);
+    user_code_suspension_lock_ = new Mutex("user code suspension lock", current_lock_level);
+
     UPDATE_CURRENT_LOCK_LEVEL(kMutatorLock);
     DCHECK(mutator_lock_ == nullptr);
     mutator_lock_ = new MutatorMutex("mutator lock", current_lock_level);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index e77d8d7..7a472e7 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -116,6 +116,7 @@
   kTraceLock,
   kHeapBitmapLock,
   kMutatorLock,
+  kUserCodeSuspensionLock,
   kInstrumentEntrypointsLock,
   kZygoteCreationLock,
 
@@ -578,6 +579,11 @@
   // Guards allocation entrypoint instrumenting.
   static Mutex* instrument_entrypoints_lock_;
 
+  // Guards code that deals with user-code suspension. This mutex must be held when suspending or
+  // resuming threads with SuspendReason::kForUserCode. It may be held by a suspended thread, but
+  // only if the suspension is not due to SuspendReason::kForUserCode.
+  static Mutex* user_code_suspension_lock_ ACQUIRED_AFTER(instrument_entrypoints_lock_);
+
   // A barrier is used to synchronize the GC/Debugger thread with mutator threads. When GC/Debugger
   // thread wants to suspend all mutator threads, it needs to wait for all mutator threads to pass
   // a barrier. Threads that are already suspended will get their barrier passed by the GC/Debugger
@@ -613,7 +619,7 @@
   //    state is changed                           |  .. running ..
   //  - if the CAS operation fails then goto x     |  .. running ..
   //  .. running ..                                |  .. running ..
-  static MutatorMutex* mutator_lock_ ACQUIRED_AFTER(instrument_entrypoints_lock_);
+  static MutatorMutex* mutator_lock_ ACQUIRED_AFTER(user_code_suspension_lock_);
 
   // Allow reader-writer mutual exclusion on the mark and live bitmaps of the heap.
   static ReaderWriterMutex* heap_bitmap_lock_ ACQUIRED_AFTER(mutator_lock_);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 7aab9de..41adae4 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -55,7 +55,9 @@
 #include "gc_root-inl.h"
 #include "gc/accounting/card_table-inl.h"
 #include "gc/accounting/heap_bitmap-inl.h"
+#include "gc/accounting/space_bitmap-inl.h"
 #include "gc/heap.h"
+#include "gc/heap-visit-objects-inl.h"
 #include "gc/scoped_gc_critical_section.h"
 #include "gc/space/image_space.h"
 #include "gc/space/space-inl.h"
@@ -88,6 +90,7 @@
 #include "mirror/method_handles_lookup.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
+#include "mirror/object-refvisitor-inl.h"
 #include "mirror/proxy.h"
 #include "mirror/reference-inl.h"
 #include "mirror/stack_trace_element.h"
@@ -861,24 +864,6 @@
   bool error;
 };
 
-static void CheckTrampolines(mirror::Object* obj, void* arg) NO_THREAD_SAFETY_ANALYSIS {
-  if (obj->IsClass()) {
-    ObjPtr<mirror::Class> klass = obj->AsClass();
-    TrampolineCheckData* d = reinterpret_cast<TrampolineCheckData*>(arg);
-    for (ArtMethod& m : klass->GetMethods(d->pointer_size)) {
-      const void* entrypoint = m.GetEntryPointFromQuickCompiledCodePtrSize(d->pointer_size);
-      if (entrypoint == d->quick_resolution_trampoline ||
-          entrypoint == d->quick_imt_conflict_trampoline ||
-          entrypoint == d->quick_generic_jni_trampoline ||
-          entrypoint == d->quick_to_interpreter_bridge_trampoline) {
-        d->m = &m;
-        d->error = true;
-        return;
-      }
-    }
-  }
-}
-
 bool ClassLinker::InitFromBootImage(std::string* error_msg) {
   VLOG(startup) << __FUNCTION__ << " entering";
   CHECK(!init_done_);
@@ -943,7 +928,24 @@
         data.quick_generic_jni_trampoline = ith_quick_generic_jni_trampoline;
         data.quick_to_interpreter_bridge_trampoline = ith_quick_to_interpreter_bridge_trampoline;
         ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
-        spaces[i]->GetLiveBitmap()->Walk(CheckTrampolines, &data);
+        auto visitor = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+          if (obj->IsClass()) {
+            ObjPtr<mirror::Class> klass = obj->AsClass();
+            for (ArtMethod& m : klass->GetMethods(data.pointer_size)) {
+              const void* entrypoint =
+                  m.GetEntryPointFromQuickCompiledCodePtrSize(data.pointer_size);
+              if (entrypoint == data.quick_resolution_trampoline ||
+                  entrypoint == data.quick_imt_conflict_trampoline ||
+                  entrypoint == data.quick_generic_jni_trampoline ||
+                  entrypoint == data.quick_to_interpreter_bridge_trampoline) {
+                data.m = &m;
+                data.error = true;
+                return;
+              }
+            }
+          }
+        };
+        spaces[i]->GetLiveBitmap()->Walk(visitor);
         if (data.error) {
           ArtMethod* m = data.m;
           LOG(ERROR) << "Found a broken ArtMethod: " << ArtMethod::PrettyMethod(m);
@@ -1193,6 +1195,63 @@
   gc::accounting::HeapBitmap* const live_bitmap_;
 };
 
+class FixupInternVisitor {
+ public:
+  ALWAYS_INLINE ObjPtr<mirror::Object> TryInsertIntern(mirror::Object* obj) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (obj != nullptr && obj->IsString()) {
+      const auto intern = Runtime::Current()->GetInternTable()->InternStrong(obj->AsString());
+      return intern;
+    }
+    return obj;
+  }
+
+  ALWAYS_INLINE void VisitRootIfNonNull(
+      mirror::CompressedReference<mirror::Object>* root) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (!root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    root->Assign(TryInsertIntern(root->AsMirrorPtr()));
+  }
+
+  // Visit Class Fields
+  ALWAYS_INLINE void operator()(ObjPtr<mirror::Object> obj,
+                                MemberOffset offset,
+                                bool is_static ATTRIBUTE_UNUSED) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    // There could be overlap between ranges, we must avoid visiting the same reference twice.
+    // Avoid the class field since we already fixed it up in FixupClassVisitor.
+    if (offset.Uint32Value() != mirror::Object::ClassOffset().Uint32Value()) {
+      // Updating images, don't do a read barrier.
+      // Only string fields are fixed, don't do a verify.
+      mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(
+          offset);
+      obj->SetFieldObject<false, false>(offset, TryInsertIntern(ref));
+    }
+  }
+
+  void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
+                  ObjPtr<mirror::Reference> ref) const
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
+    this->operator()(ref, mirror::Reference::ReferentOffset(), false);
+  }
+
+  void operator()(mirror::Object* obj) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (obj->IsDexCache()) {
+      obj->VisitReferences<true, kVerifyNone, kWithoutReadBarrier>(*this, *this);
+    } else {
+      // Don't visit native roots for non-dex-cache
+      obj->VisitReferences<false, kVerifyNone, kWithoutReadBarrier>(*this, *this);
+    }
+  }
+};
+
 // Copies data from one array to another array at the same position
 // if pred returns false. If there is a page of continuous data in
 // the src array for which pred consistently returns true then
@@ -1285,6 +1344,7 @@
         return false;
       }
     }
+
     // Only add the classes to the class loader after the points where we can return false.
     for (size_t i = 0; i < num_dex_caches; i++) {
       ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
@@ -1448,6 +1508,21 @@
       }
     }
   }
+  {
+    // Fixup all the literal strings happens at app images which are supposed to be interned.
+    ScopedTrace timing("Fixup String Intern in image and dex_cache");
+    const auto& image_header = space->GetImageHeader();
+    const auto bitmap = space->GetMarkBitmap();  // bitmap of objects
+    const uint8_t* target_base = space->GetMemMap()->Begin();
+    const ImageSection& objects_section =
+        image_header.GetImageSection(ImageHeader::kSectionObjects);
+
+    uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset());
+    uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End());
+
+    FixupInternVisitor fixup_intern_visitor;
+    bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_intern_visitor);
+  }
   if (*out_forward_dex_cache_array) {
     ScopedTrace timing("Fixup ArtMethod dex cache arrays");
     FixupArtMethodArrayVisitor visitor(header);
@@ -1545,7 +1620,46 @@
   static void CheckObjects(gc::Heap* heap, ClassLinker* class_linker)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     ImageSanityChecks isc(heap, class_linker);
-    heap->VisitObjects(ImageSanityChecks::SanityCheckObjectsCallback, &isc);
+    auto visitor = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+      DCHECK(obj != nullptr);
+      CHECK(obj->GetClass() != nullptr) << "Null class in object " << obj;
+      CHECK(obj->GetClass()->GetClass() != nullptr) << "Null class class " << obj;
+      if (obj->IsClass()) {
+        auto klass = obj->AsClass();
+        for (ArtField& field : klass->GetIFields()) {
+          CHECK_EQ(field.GetDeclaringClass(), klass);
+        }
+        for (ArtField& field : klass->GetSFields()) {
+          CHECK_EQ(field.GetDeclaringClass(), klass);
+        }
+        const auto pointer_size = isc.pointer_size_;
+        for (auto& m : klass->GetMethods(pointer_size)) {
+          isc.SanityCheckArtMethod(&m, klass);
+        }
+        auto* vtable = klass->GetVTable();
+        if (vtable != nullptr) {
+          isc.SanityCheckArtMethodPointerArray(vtable, nullptr);
+        }
+        if (klass->ShouldHaveImt()) {
+          ImTable* imt = klass->GetImt(pointer_size);
+          for (size_t i = 0; i < ImTable::kSize; ++i) {
+            isc.SanityCheckArtMethod(imt->Get(i, pointer_size), nullptr);
+          }
+        }
+        if (klass->ShouldHaveEmbeddedVTable()) {
+          for (int32_t i = 0; i < klass->GetEmbeddedVTableLength(); ++i) {
+            isc.SanityCheckArtMethod(klass->GetEmbeddedVTableEntry(i, pointer_size), nullptr);
+          }
+        }
+        mirror::IfTable* iftable = klass->GetIfTable();
+        for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) {
+          if (iftable->GetMethodArrayCount(i) > 0) {
+            isc.SanityCheckArtMethodPointerArray(iftable->GetMethodArray(i), nullptr);
+          }
+        }
+      }
+    };
+    heap->VisitObjects(visitor);
   }
 
   static void CheckPointerArray(gc::Heap* heap,
@@ -1557,49 +1671,6 @@
     isc.SanityCheckArtMethodPointerArray(arr, size);
   }
 
-  static void SanityCheckObjectsCallback(mirror::Object* obj, void* arg)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(obj != nullptr);
-    CHECK(obj->GetClass() != nullptr) << "Null class in object " << obj;
-    CHECK(obj->GetClass()->GetClass() != nullptr) << "Null class class " << obj;
-    if (obj->IsClass()) {
-      ImageSanityChecks* isc = reinterpret_cast<ImageSanityChecks*>(arg);
-
-      auto klass = obj->AsClass();
-      for (ArtField& field : klass->GetIFields()) {
-        CHECK_EQ(field.GetDeclaringClass(), klass);
-      }
-      for (ArtField& field : klass->GetSFields()) {
-        CHECK_EQ(field.GetDeclaringClass(), klass);
-      }
-      const auto pointer_size = isc->pointer_size_;
-      for (auto& m : klass->GetMethods(pointer_size)) {
-        isc->SanityCheckArtMethod(&m, klass);
-      }
-      auto* vtable = klass->GetVTable();
-      if (vtable != nullptr) {
-        isc->SanityCheckArtMethodPointerArray(vtable, nullptr);
-      }
-      if (klass->ShouldHaveImt()) {
-        ImTable* imt = klass->GetImt(pointer_size);
-        for (size_t i = 0; i < ImTable::kSize; ++i) {
-          isc->SanityCheckArtMethod(imt->Get(i, pointer_size), nullptr);
-        }
-      }
-      if (klass->ShouldHaveEmbeddedVTable()) {
-        for (int32_t i = 0; i < klass->GetEmbeddedVTableLength(); ++i) {
-          isc->SanityCheckArtMethod(klass->GetEmbeddedVTableEntry(i, pointer_size), nullptr);
-        }
-      }
-      mirror::IfTable* iftable = klass->GetIfTable();
-      for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) {
-        if (iftable->GetMethodArrayCount(i) > 0) {
-          isc->SanityCheckArtMethodPointerArray(iftable->GetMethodArray(i), nullptr);
-        }
-      }
-    }
-  }
-
  private:
   ImageSanityChecks(gc::Heap* heap, ClassLinker* class_linker)
      :  spaces_(heap->GetBootImageSpaces()),
@@ -2410,74 +2481,121 @@
   return ClassPathEntry(nullptr, nullptr);
 }
 
+// Returns true if the given class loader is either a PathClassLoader or a DexClassLoader.
+// (they both have the same behaviour with respect to class lockup order)
+static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+                                   Handle<mirror::ClassLoader> class_loader)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  mirror::Class* class_loader_class = class_loader->GetClass();
+  return
+      (class_loader_class ==
+          soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) ||
+      (class_loader_class ==
+          soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader));
+}
+
+static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+                                      Handle<mirror::ClassLoader> class_loader)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  mirror::Class* class_loader_class = class_loader->GetClass();
+  return class_loader_class ==
+      soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
+}
+
 bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
                                                 Thread* self,
                                                 const char* descriptor,
                                                 size_t hash,
                                                 Handle<mirror::ClassLoader> class_loader,
                                                 ObjPtr<mirror::Class>* result) {
-  // Termination case: boot class-loader.
+  // Termination case: boot class loader.
   if (IsBootClassLoader(soa, class_loader.Get())) {
-    // The boot class loader, search the boot class path.
-    ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
-    if (pair.second != nullptr) {
-      ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, nullptr);
-      if (klass != nullptr) {
-        *result = EnsureResolved(self, descriptor, klass);
-      } else {
-        *result = DefineClass(self,
-                              descriptor,
-                              hash,
-                              ScopedNullHandle<mirror::ClassLoader>(),
-                              *pair.first,
-                              *pair.second);
-      }
-      if (*result == nullptr) {
-        CHECK(self->IsExceptionPending()) << descriptor;
-        self->ClearException();
-      }
+    *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash);
+    return true;
+  }
+
+  if (IsPathOrDexClassLoader(soa, class_loader)) {
+    // For regular path or dex class loader the search order is:
+    //    - parent
+    //    - class loader dex files
+
+    // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
+    StackHandleScope<1> hs(self);
+    Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
+    if (!FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result)) {
+      return false;  // One of the parents is not supported.
+    }
+    if (*result != nullptr) {
+      return true;  // Found the class up the chain.
+    }
+
+    // Search the current class loader classpath.
+    *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
+    return true;
+  }
+
+  if (IsDelegateLastClassLoader(soa, class_loader)) {
+    // For delegate last, the search order is:
+    //    - boot class path
+    //    - class loader dex files
+    //    - parent
+    *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash);
+    if (*result != nullptr) {
+      return true;  // The class is part of the boot class path.
+    }
+
+    *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
+    if (*result != nullptr) {
+      return true;  // Found the class in the current class loader
+    }
+
+    // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
+    StackHandleScope<1> hs(self);
+    Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
+    return FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result);
+  }
+
+  // Unsupported class loader.
+  *result = nullptr;
+  return false;
+}
+
+// Finds the class in the boot class loader.
+// If the class is found the method returns the resolved class. Otherwise it returns null.
+ObjPtr<mirror::Class> ClassLinker::FindClassInBootClassLoaderClassPath(Thread* self,
+                                                                       const char* descriptor,
+                                                                       size_t hash) {
+  ObjPtr<mirror::Class> result = nullptr;
+  ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
+  if (pair.second != nullptr) {
+    ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, nullptr);
+    if (klass != nullptr) {
+      result = EnsureResolved(self, descriptor, klass);
     } else {
-      *result = nullptr;
+      result = DefineClass(self,
+                           descriptor,
+                           hash,
+                           ScopedNullHandle<mirror::ClassLoader>(),
+                           *pair.first,
+                           *pair.second);
     }
-    return true;
-  }
-
-  // Unsupported class-loader?
-  if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) !=
-      class_loader->GetClass()) {
-    // PathClassLoader is the most common case, so it's the one we check first. For secondary dex
-    // files, we also check DexClassLoader here.
-    if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader) !=
-        class_loader->GetClass()) {
-      *result = nullptr;
-      return false;
+    if (result == nullptr) {
+      CHECK(self->IsExceptionPending()) << descriptor;
+      self->ClearException();
     }
   }
+  return result;
+}
 
-  // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
-  StackHandleScope<4> hs(self);
-  Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
-  bool recursive_result = FindClassInBaseDexClassLoader(soa,
-                                                        self,
-                                                        descriptor,
-                                                        hash,
-                                                        h_parent,
-                                                        result);
+ObjPtr<mirror::Class> ClassLinker::FindClassInBaseDexClassLoaderClassPath(
+    ScopedObjectAccessAlreadyRunnable& soa,
+    const char* descriptor,
+    size_t hash,
+    Handle<mirror::ClassLoader> class_loader) {
+  CHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader))
+      << "Unexpected class loader for descriptor " << descriptor;
 
-  if (!recursive_result) {
-    // Something wrong up the chain.
-    return false;
-  }
-
-  if (*result != nullptr) {
-    // Found the class up the chain.
-    return true;
-  }
-
-  // Handle this step.
-  // Handle as if this is the child PathClassLoader.
-  // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
-  // We need to get the DexPathList and loop through it.
+  Thread* self = soa.Self();
   ArtField* const cookie_field =
       jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
   ArtField* const dex_file_field =
@@ -2489,10 +2607,11 @@
     // DexPathList has an array dexElements of Elements[] which each contain a dex file.
     ObjPtr<mirror::Object> dex_elements_obj =
         jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
-        GetObject(dex_path_list);
+            GetObject(dex_path_list);
     // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
     // at the mCookie which is a DexFile vector.
     if (dex_elements_obj != nullptr) {
+      StackHandleScope<1> hs(self);
       Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
           hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
       for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
@@ -2518,19 +2637,18 @@
                 OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash);
             if (dex_class_def != nullptr) {
               ObjPtr<mirror::Class> klass = DefineClass(self,
-                                                 descriptor,
-                                                 hash,
-                                                 class_loader,
-                                                 *cp_dex_file,
-                                                 *dex_class_def);
+                                                        descriptor,
+                                                        hash,
+                                                        class_loader,
+                                                        *cp_dex_file,
+                                                        *dex_class_def);
               if (klass == nullptr) {
                 CHECK(self->IsExceptionPending()) << descriptor;
                 self->ClearException();
                 // TODO: Is it really right to break here, and not check the other dex files?
-                return true;
+                return nullptr;
               }
-              *result = klass;
-              return true;
+              return klass;
             }
           }
         }
@@ -2538,9 +2656,7 @@
     }
     self->AssertNoPendingException();
   }
-
-  // Result is still null from the parent call, no need to set it again...
-  return true;
+  return nullptr;
 }
 
 mirror::Class* ClassLinker::FindClass(Thread* self,
@@ -4285,8 +4401,7 @@
 
   uint16_t class_def_index = klass->GetDexClassDefIndex();
   oat_file_class_status = oat_dex_file->GetOatClass(class_def_index).GetStatus();
-  if (oat_file_class_status == mirror::Class::kStatusVerified ||
-      oat_file_class_status == mirror::Class::kStatusInitialized) {
+  if (oat_file_class_status >= mirror::Class::kStatusVerified) {
     return true;
   }
   // If we only verified a subset of the classes at compile time, we can end up with classes that
@@ -4765,7 +4880,13 @@
       return WaitForInitializeClass(klass, self, lock);
     }
 
-    if (!ValidateSuperClassDescriptors(klass)) {
+    bool has_oat_class = false;
+    const OatFile::OatClass oat_class =
+        (Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler())
+            ? OatFile::FindOatClass(klass->GetDexFile(), klass->GetDexClassDefIndex(), &has_oat_class)
+            : OatFile::OatClass::Invalid();
+    if (oat_class.GetStatus() < mirror::Class::kStatusSuperclassValidated &&
+        !ValidateSuperClassDescriptors(klass)) {
       mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
       return false;
     }
@@ -8249,159 +8370,73 @@
   return type.Get();
 }
 
-mirror::MethodHandle* ClassLinker::ResolveMethodHandle(uint32_t method_handle_idx,
-                                                       ArtMethod* referrer)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  Thread* const self = Thread::Current();
-  const DexFile* const dex_file = referrer->GetDexFile();
-  const DexFile::MethodHandleItem& mh = dex_file->GetMethodHandle(method_handle_idx);
-
-  ArtField* target_field = nullptr;
-  ArtMethod* target_method = nullptr;
-
+mirror::MethodHandle* ClassLinker::ResolveMethodHandleForField(
+    Thread* self,
+    const DexFile::MethodHandleItem& method_handle,
+    ArtMethod* referrer) {
   DexFile::MethodHandleType handle_type =
-      static_cast<DexFile::MethodHandleType>(mh.method_handle_type_);
-  switch (handle_type) {
-    case DexFile::MethodHandleType::kStaticPut: {
-      target_field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */);
-      break;
-    }
-    case DexFile::MethodHandleType::kStaticGet: {
-      target_field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */);
-      break;
-    }
-    case DexFile::MethodHandleType::kInstancePut: {
-      target_field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */);
-      break;
-    }
-    case DexFile::MethodHandleType::kInstanceGet: {
-      target_field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */);
-      break;
-    }
-    case DexFile::MethodHandleType::kInvokeStatic: {
-      target_method = ResolveMethod<kNoICCECheckForCache>(self,
-                                                          mh.field_or_method_idx_,
-                                                          referrer,
-                                                          InvokeType::kStatic);
-      break;
-    }
-    case DexFile::MethodHandleType::kInvokeInstance: {
-      target_method = ResolveMethod<kNoICCECheckForCache>(self,
-                                                          mh.field_or_method_idx_,
-                                                          referrer,
-                                                          InvokeType::kVirtual);
-      break;
-    }
-    case DexFile::MethodHandleType::kInvokeConstructor: {
-      UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform.";
-      break;
-    }
-    case DexFile::MethodHandleType::kInvokeDirect: {
-      target_method = ResolveMethod<kNoICCECheckForCache>(self,
-                                                          mh.field_or_method_idx_,
-                                                          referrer,
-                                                          InvokeType::kDirect);
-      break;
-    }
-    case DexFile::MethodHandleType::kInvokeInterface: {
-      target_method = ResolveMethod<kNoICCECheckForCache>(self,
-                                                          mh.field_or_method_idx_,
-                                                          referrer,
-                                                          InvokeType::kInterface);
-      break;
-    }
-  }
-
-  if (target_field != nullptr) {
-    ObjPtr<mirror::Class> target_class = target_field->GetDeclaringClass();
-    ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
-    if (!referring_class->CanAccessMember(target_class, target_field->GetAccessFlags())) {
-      ThrowIllegalAccessErrorField(referring_class, target_field);
-      return nullptr;
-    }
-  } else if (target_method != nullptr) {
-    ObjPtr<mirror::Class> target_class = target_method->GetDeclaringClass();
-    ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
-    if (!referring_class->CanAccessMember(target_class, target_method->GetAccessFlags())) {
-      ThrowIllegalAccessErrorMethod(referring_class, target_method);
-      return nullptr;
-    }
-  } else {
-    // Common check for resolution failure.
-    DCHECK(Thread::Current()->IsExceptionPending());
-    return nullptr;
-  }
-
-  // Determine the kind and number of parameters after it's safe to
-  // follow the field or method pointer.
+      static_cast<DexFile::MethodHandleType>(method_handle.method_handle_type_);
   mirror::MethodHandle::Kind kind;
-  uint32_t num_params;
+  bool is_static;
+  int32_t num_params;
   switch (handle_type) {
     case DexFile::MethodHandleType::kStaticPut: {
       kind = mirror::MethodHandle::Kind::kStaticPut;
+      is_static = true;
       num_params = 1;
       break;
     }
     case DexFile::MethodHandleType::kStaticGet: {
       kind = mirror::MethodHandle::Kind::kStaticGet;
+      is_static = true;
       num_params = 0;
       break;
     }
     case DexFile::MethodHandleType::kInstancePut: {
       kind = mirror::MethodHandle::Kind::kInstancePut;
+      is_static = false;
       num_params = 2;
       break;
     }
     case DexFile::MethodHandleType::kInstanceGet: {
       kind = mirror::MethodHandle::Kind::kInstanceGet;
+      is_static = false;
       num_params = 1;
       break;
     }
-    case DexFile::MethodHandleType::kInvokeStatic: {
-      kind = mirror::MethodHandle::Kind::kInvokeStatic;
-      uint32_t shorty_length;
-      target_method->GetShorty(&shorty_length);
-      num_params = shorty_length - 1;  // Remove 1 for the return value.
-      break;
-    }
-    case DexFile::MethodHandleType::kInvokeInstance: {
-      kind = mirror::MethodHandle::Kind::kInvokeVirtual;
-      uint32_t shorty_length;
-      target_method->GetShorty(&shorty_length);
-      num_params = shorty_length;  // Add 1 for the receiver, remove 1 for the return value.
-      break;
-    }
-    case DexFile::MethodHandleType::kInvokeConstructor: {
-      UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform.";
-      num_params = 0;
-      break;
-    }
-    case DexFile::MethodHandleType::kInvokeDirect: {
-      kind = mirror::MethodHandle::Kind::kInvokeDirect;
-      uint32_t shorty_length;
-      target_method->GetShorty(&shorty_length);
-      num_params = shorty_length;  // Add 1 for the receiver, remove 1 for the return value.
-      break;
-    }
-    case DexFile::MethodHandleType::kInvokeInterface: {
-      kind = mirror::MethodHandle::Kind::kInvokeInterface;
-      uint32_t shorty_length;
-      target_method->GetShorty(&shorty_length);
-      num_params = shorty_length;  // Add 1 for the receiver, remove 1 for the return value.
-      break;
-    }
+    case DexFile::MethodHandleType::kInvokeStatic:
+    case DexFile::MethodHandleType::kInvokeInstance:
+    case DexFile::MethodHandleType::kInvokeConstructor:
+    case DexFile::MethodHandleType::kInvokeDirect:
+    case DexFile::MethodHandleType::kInvokeInterface:
+      UNREACHABLE();
   }
 
-  StackHandleScope<5> hs(self);
+  ArtField* target_field =
+      ResolveField(method_handle.field_or_method_idx_, referrer, is_static);
+  if (LIKELY(target_field != nullptr)) {
+    ObjPtr<mirror::Class> target_class = target_field->GetDeclaringClass();
+    ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
+    if (UNLIKELY(!referring_class->CanAccessMember(target_class, target_field->GetAccessFlags()))) {
+      ThrowIllegalAccessErrorField(referring_class, target_field);
+      return nullptr;
+    }
+  } else {
+    DCHECK(Thread::Current()->IsExceptionPending());
+    return nullptr;
+  }
+
+  StackHandleScope<4> hs(self);
   ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
   ObjPtr<mirror::Class> array_of_class = FindArrayClass(self, &class_type);
   Handle<mirror::ObjectArray<mirror::Class>> method_params(hs.NewHandle(
       mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, num_params)));
-  if (method_params.Get() == nullptr) {
+  if (UNLIKELY(method_params.Get() == nullptr)) {
     DCHECK(self->IsExceptionPending());
     return nullptr;
   }
 
+  Handle<mirror::Class> constructor_class;
   Handle<mirror::Class> return_type;
   switch (handle_type) {
     case DexFile::MethodHandleType::kStaticPut: {
@@ -8424,59 +8459,228 @@
       return_type = hs.NewHandle(target_field->GetType<true>());
       break;
     }
-    case DexFile::MethodHandleType::kInvokeDirect:
+    case DexFile::MethodHandleType::kInvokeStatic:
     case DexFile::MethodHandleType::kInvokeInstance:
+    case DexFile::MethodHandleType::kInvokeConstructor:
+    case DexFile::MethodHandleType::kInvokeDirect:
     case DexFile::MethodHandleType::kInvokeInterface:
-    case DexFile::MethodHandleType::kInvokeStatic: {
-      // TODO(oth): This will not work for varargs methods as this
-      // requires instantiating a Transformer. This resolution step
-      // would be best done in managed code rather than in the run
-      // time (b/35235705)
-      Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
-      Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
-      DexFileParameterIterator it(*dex_file, target_method->GetPrototype());
-      int32_t index = 0;
-      if (handle_type != DexFile::MethodHandleType::kInvokeStatic) {
-        method_params->Set(index++, target_method->GetDeclaringClass());
-      }
-      while (it.HasNext()) {
-        const dex::TypeIndex type_idx = it.GetTypeIdx();
-        mirror::Class* klass = ResolveType(*dex_file, type_idx, dex_cache, class_loader);
-        if (nullptr == klass) {
-          DCHECK(self->IsExceptionPending());
-          return nullptr;
-        }
-        method_params->Set(index++, klass);
-        it.Next();
-      }
-      return_type = hs.NewHandle(target_method->GetReturnType(true));
-      break;
-    }
-    case DexFile::MethodHandleType::kInvokeConstructor: {
-      // TODO(oth): b/35235705
-      UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform.";
+      UNREACHABLE();
+  }
+
+  for (int32_t i = 0; i < num_params; ++i) {
+    if (UNLIKELY(method_params->Get(i) == nullptr)) {
+      DCHECK(self->IsExceptionPending());
+      return nullptr;
     }
   }
 
-  if (return_type.IsNull()) {
+  if (UNLIKELY(return_type.IsNull())) {
     DCHECK(self->IsExceptionPending());
     return nullptr;
   }
 
   Handle<mirror::MethodType>
-      mt(hs.NewHandle(mirror::MethodType::Create(self, return_type, method_params)));
-  if (mt.IsNull()) {
+      method_type(hs.NewHandle(mirror::MethodType::Create(self, return_type, method_params)));
+  if (UNLIKELY(method_type.IsNull())) {
     DCHECK(self->IsExceptionPending());
     return nullptr;
   }
 
-  uintptr_t target;
-  if (target_field != nullptr) {
-    target = reinterpret_cast<uintptr_t>(target_field);
-  } else {
-    target = reinterpret_cast<uintptr_t>(target_method);
+  uintptr_t target = reinterpret_cast<uintptr_t>(target_field);
+  return mirror::MethodHandleImpl::Create(self, target, kind, method_type);
+}
+
+mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod(
+    Thread* self,
+    const DexFile* const dex_file,
+    const DexFile::MethodHandleItem& method_handle,
+    ArtMethod* referrer) {
+  DexFile::MethodHandleType handle_type =
+      static_cast<DexFile::MethodHandleType>(method_handle.method_handle_type_);
+  mirror::MethodHandle::Kind kind;
+  uint32_t receiver_count = 0;
+  ArtMethod* target_method = nullptr;
+  switch (handle_type) {
+    case DexFile::MethodHandleType::kStaticPut:
+    case DexFile::MethodHandleType::kStaticGet:
+    case DexFile::MethodHandleType::kInstancePut:
+    case DexFile::MethodHandleType::kInstanceGet:
+      UNREACHABLE();
+    case DexFile::MethodHandleType::kInvokeStatic: {
+      kind = mirror::MethodHandle::Kind::kInvokeStatic;
+      receiver_count = 0;
+      target_method = ResolveMethod<kNoICCECheckForCache>(self,
+                                                          method_handle.field_or_method_idx_,
+                                                          referrer,
+                                                          InvokeType::kStatic);
+      break;
+    }
+    case DexFile::MethodHandleType::kInvokeInstance: {
+      kind = mirror::MethodHandle::Kind::kInvokeVirtual;
+      receiver_count = 1;
+      target_method = ResolveMethod<kNoICCECheckForCache>(self,
+                                                          method_handle.field_or_method_idx_,
+                                                          referrer,
+                                                          InvokeType::kVirtual);
+      break;
+    }
+    case DexFile::MethodHandleType::kInvokeConstructor: {
+      // Constructors are currently implemented as a transform. They
+      // are special cased later in this method.
+      kind = mirror::MethodHandle::Kind::kInvokeTransform;
+      receiver_count = 0;
+      target_method = ResolveMethod<kNoICCECheckForCache>(self,
+                                                          method_handle.field_or_method_idx_,
+                                                          referrer,
+                                                          InvokeType::kDirect);
+      break;
+    }
+    case DexFile::MethodHandleType::kInvokeDirect: {
+      kind = mirror::MethodHandle::Kind::kInvokeDirect;
+      receiver_count = 1;
+      StackHandleScope<2> hs(self);
+      // A constant method handle with type kInvokeDirect can refer to
+      // a method that is private or to a method in a super class. To
+      // disambiguate the two options, we resolve the method ignoring
+      // the invocation type to determine if the method is private. We
+      // then resolve again specifying the intended invocation type to
+      // force the appropriate checks.
+      target_method = ResolveMethodWithoutInvokeType(*dex_file,
+                                                     method_handle.field_or_method_idx_,
+                                                     hs.NewHandle(referrer->GetDexCache()),
+                                                     hs.NewHandle(referrer->GetClassLoader()));
+      if (UNLIKELY(target_method == nullptr)) {
+        break;
+      }
+
+      if (target_method->IsPrivate()) {
+        kind = mirror::MethodHandle::Kind::kInvokeDirect;
+        target_method = ResolveMethod<kNoICCECheckForCache>(self,
+                                                            method_handle.field_or_method_idx_,
+                                                            referrer,
+                                                            InvokeType::kDirect);
+      } else {
+        kind = mirror::MethodHandle::Kind::kInvokeSuper;
+        target_method = ResolveMethod<kNoICCECheckForCache>(self,
+                                                            method_handle.field_or_method_idx_,
+                                                            referrer,
+                                                            InvokeType::kSuper);
+        if (UNLIKELY(target_method == nullptr)) {
+          break;
+        }
+        // Find the method specified in the parent in referring class
+        // so invoke-super invokes the method in the parent of the
+        // referrer.
+        target_method =
+            referrer->GetDeclaringClass()->FindVirtualMethodForVirtual(target_method,
+                                                                       kRuntimePointerSize);
+      }
+      break;
+    }
+    case DexFile::MethodHandleType::kInvokeInterface: {
+      kind = mirror::MethodHandle::Kind::kInvokeInterface;
+      receiver_count = 1;
+      target_method = ResolveMethod<kNoICCECheckForCache>(self,
+                                                          method_handle.field_or_method_idx_,
+                                                          referrer,
+                                                          InvokeType::kInterface);
+      break;
+    }
   }
-  return mirror::MethodHandleImpl::Create(self, target, kind, mt);
+
+  if (UNLIKELY(target_method == nullptr)) {
+    DCHECK(Thread::Current()->IsExceptionPending());
+    return nullptr;
+  }
+
+  ObjPtr<mirror::Class> target_class = target_method->GetDeclaringClass();
+  ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
+  uint32_t access_flags = target_method->GetAccessFlags();
+  if (UNLIKELY(!referring_class->CanAccessMember(target_class, access_flags))) {
+    ThrowIllegalAccessErrorMethod(referring_class, target_method);
+    return nullptr;
+  }
+
+  // Calculate the number of parameters from the method shorty. We add the
+  // receiver count (0 or 1) and deduct one for the return value.
+  uint32_t shorty_length;
+  target_method->GetShorty(&shorty_length);
+  int32_t num_params = static_cast<int32_t>(shorty_length + receiver_count - 1);
+
+  StackHandleScope<7> hs(self);
+  ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
+  ObjPtr<mirror::Class> array_of_class = FindArrayClass(self, &class_type);
+  Handle<mirror::ObjectArray<mirror::Class>> method_params(hs.NewHandle(
+      mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, num_params)));
+  if (method_params.Get() == nullptr) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
+  int32_t index = 0;
+
+  if (receiver_count != 0) {
+    // Insert receiver
+    method_params->Set(index++, target_method->GetDeclaringClass());
+  }
+
+  DexFileParameterIterator it(*dex_file, target_method->GetPrototype());
+  while (it.HasNext()) {
+    const dex::TypeIndex type_idx = it.GetTypeIdx();
+    mirror::Class* klass = ResolveType(*dex_file, type_idx, dex_cache, class_loader);
+    if (nullptr == klass) {
+      DCHECK(self->IsExceptionPending());
+      return nullptr;
+    }
+    method_params->Set(index++, klass);
+    it.Next();
+  }
+
+  Handle<mirror::Class> return_type = hs.NewHandle(target_method->GetReturnType(true));
+  if (UNLIKELY(return_type.IsNull())) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  Handle<mirror::MethodType>
+      method_type(hs.NewHandle(mirror::MethodType::Create(self, return_type, method_params)));
+  if (UNLIKELY(method_type.IsNull())) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  if (UNLIKELY(handle_type == DexFile::MethodHandleType::kInvokeConstructor)) {
+    Handle<mirror::Class> constructor_class = hs.NewHandle(target_method->GetDeclaringClass());
+    Handle<mirror::MethodHandlesLookup> lookup =
+        hs.NewHandle(mirror::MethodHandlesLookup::GetDefault(self));
+    return lookup->FindConstructor(self, constructor_class, method_type);
+  }
+
+  uintptr_t target = reinterpret_cast<uintptr_t>(target_method);
+  return mirror::MethodHandleImpl::Create(self, target, kind, method_type);
+}
+
+mirror::MethodHandle* ClassLinker::ResolveMethodHandle(uint32_t method_handle_idx,
+                                                       ArtMethod* referrer)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  Thread* const self = Thread::Current();
+  const DexFile* const dex_file = referrer->GetDexFile();
+  const DexFile::MethodHandleItem& method_handle = dex_file->GetMethodHandle(method_handle_idx);
+  switch (static_cast<DexFile::MethodHandleType>(method_handle.method_handle_type_)) {
+    case DexFile::MethodHandleType::kStaticPut:
+    case DexFile::MethodHandleType::kStaticGet:
+    case DexFile::MethodHandleType::kInstancePut:
+    case DexFile::MethodHandleType::kInstanceGet:
+      return ResolveMethodHandleForField(self, method_handle, referrer);
+    case DexFile::MethodHandleType::kInvokeStatic:
+    case DexFile::MethodHandleType::kInvokeInstance:
+    case DexFile::MethodHandleType::kInvokeConstructor:
+    case DexFile::MethodHandleType::kInvokeDirect:
+    case DexFile::MethodHandleType::kInvokeInterface:
+      return ResolveMethodHandleForMethod(self, dex_file, method_handle, referrer);
+  }
 }
 
 bool ClassLinker::IsQuickResolutionStub(const void* entry_point) const {
@@ -8640,8 +8844,15 @@
   return descriptor;
 }
 
-jobject ClassLinker::CreatePathClassLoader(Thread* self,
-                                           const std::vector<const DexFile*>& dex_files) {
+jobject ClassLinker::CreateWellKnownClassLoader(Thread* self,
+                                               const std::vector<const DexFile*>& dex_files,
+                                               jclass loader_class,
+                                               jobject parent_loader) {
+  CHECK(self->GetJniEnv()->IsSameObject(loader_class,
+                                        WellKnownClasses::dalvik_system_PathClassLoader) ||
+        self->GetJniEnv()->IsSameObject(loader_class,
+                                        WellKnownClasses::dalvik_system_DelegateLastClassLoader));
+
   // SOAAlreadyRunnable is protected, and we need something to add a global reference.
   // We could move the jobject to the callers, but all call-sites do this...
   ScopedObjectAccessUnchecked soa(self);
@@ -8677,8 +8888,8 @@
   for (const DexFile* dex_file : dex_files) {
     StackHandleScope<4> hs2(self);
 
-    // CreatePathClassLoader is only used by gtests. Index 0 of h_long_array is supposed to be the
-    // oat file but we can leave it null.
+    // CreateWellKnownClassLoader is only used by gtests and compiler.
+    // Index 0 of h_long_array is supposed to be the oat file but we can leave it null.
     Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc(
         self,
         kDexFileIndexStart + 1));
@@ -8713,36 +8924,44 @@
   // Set elements.
   dex_elements_field->SetObject<false>(h_dex_path_list.Get(), h_dex_elements.Get());
 
-  // Create PathClassLoader.
-  Handle<mirror::Class> h_path_class_class = hs.NewHandle(
-      soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
-  Handle<mirror::Object> h_path_class_loader = hs.NewHandle(
-      h_path_class_class->AllocObject(self));
-  DCHECK(h_path_class_loader != nullptr);
+  // Create the class loader..
+  Handle<mirror::Class> h_loader_class = hs.NewHandle(soa.Decode<mirror::Class>(loader_class));
+  Handle<mirror::Object> h_class_loader = hs.NewHandle(h_loader_class->AllocObject(self));
+  DCHECK(h_class_loader != nullptr);
   // Set DexPathList.
   ArtField* path_list_field =
       jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
   DCHECK(path_list_field != nullptr);
-  path_list_field->SetObject<false>(h_path_class_loader.Get(), h_dex_path_list.Get());
+  path_list_field->SetObject<false>(h_class_loader.Get(), h_dex_path_list.Get());
 
   // Make a pretend boot-classpath.
   // TODO: Should we scan the image?
   ArtField* const parent_field =
       mirror::Class::FindField(self,
-                               h_path_class_loader->GetClass(),
+                               h_class_loader->GetClass(),
                                "parent",
                                "Ljava/lang/ClassLoader;");
   DCHECK(parent_field != nullptr);
-  ObjPtr<mirror::Object> boot_cl =
-      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self);
-  parent_field->SetObject<false>(h_path_class_loader.Get(), boot_cl);
+
+  ObjPtr<mirror::Object> parent = (parent_loader != nullptr)
+      ? soa.Decode<mirror::ClassLoader>(parent_loader)
+      : soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self);
+  parent_field->SetObject<false>(h_class_loader.Get(), parent);
 
   // Make it a global ref and return.
   ScopedLocalRef<jobject> local_ref(
-      soa.Env(), soa.Env()->AddLocalReference<jobject>(h_path_class_loader.Get()));
+      soa.Env(), soa.Env()->AddLocalReference<jobject>(h_class_loader.Get()));
   return soa.Env()->NewGlobalRef(local_ref.get());
 }
 
+jobject ClassLinker::CreatePathClassLoader(Thread* self,
+                                           const std::vector<const DexFile*>& dex_files) {
+  return CreateWellKnownClassLoader(self,
+                                    dex_files,
+                                    WellKnownClasses::dalvik_system_PathClassLoader,
+                                    nullptr);
+}
+
 void ClassLinker::DropFindArrayClassCache() {
   std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr));
   find_array_class_cache_next_victim_ = 0;
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 1e8125e..864d37f 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -553,8 +553,24 @@
       REQUIRES(!Locks::classlinker_classes_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Creates a GlobalRef PathClassLoader that can be used to load classes from the given dex files.
+  // Creates a GlobalRef PathClassLoader or DelegateLastClassLoader (specified by loader_class)
+  // that can be used to load classes from the given dex files. The parent of the class loader
+  // will be set to `parent_loader`. If `parent_loader` is null the parent will be
+  // the boot class loader.
+  // If class_loader points to a different class than PathClassLoader or DelegateLastClassLoader
+  // this method will abort.
   // Note: the objects are not completely set up. Do not use this outside of tests and the compiler.
+  jobject CreateWellKnownClassLoader(Thread* self,
+                                     const std::vector<const DexFile*>& dex_files,
+                                     jclass loader_class,
+                                     jobject parent_loader)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!Locks::dex_lock_);
+
+  // Calls CreateWellKnownClassLoader(self,
+  //                                  dex_files,
+  //                                  WellKnownClasses::dalvik_system_PathClassLoader,
+  //                                  nullptr)
   jobject CreatePathClassLoader(Thread* self, const std::vector<const DexFile*>& dex_files)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_);
@@ -655,6 +671,14 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_);
 
+  // Visit all of the class loaders in the class linker.
+  void VisitClassLoaders(ClassLoaderVisitor* visitor) const
+      REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+
+  // Checks that a class and its superclass from another class loader have the same virtual methods.
+  bool ValidateSuperClassDescriptors(Handle<mirror::Class> klass)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   struct DexCacheData {
     // Construct an invalid data object.
     DexCacheData()
@@ -704,9 +728,6 @@
   static void DeleteClassLoader(Thread* self, const ClassLoaderData& data)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  void VisitClassLoaders(ClassLoaderVisitor* visitor) const
-      REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
-
   void VisitClassesInternal(ClassVisitor* visitor)
       REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
 
@@ -819,6 +840,27 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_);
 
+  // Finds the class in the classpath of the given class loader. It only searches the class loader
+  // dex files and does not recurse into its parent.
+  // The method checks that the provided class loader is either a PathClassLoader or a
+  // DexClassLoader.
+  // If the class is found the method returns the resolved class. Otherwise it returns null.
+  ObjPtr<mirror::Class> FindClassInBaseDexClassLoaderClassPath(
+          ScopedObjectAccessAlreadyRunnable& soa,
+          const char* descriptor,
+          size_t hash,
+          Handle<mirror::ClassLoader> class_loader)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!Locks::dex_lock_);
+
+  // Finds the class in the boot class loader.
+  // If the class is found the method returns the resolved class. Otherwise it returns null.
+  ObjPtr<mirror::Class> FindClassInBootClassLoaderClassPath(Thread* self,
+                                                            const char* descriptor,
+                                                            size_t hash)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!Locks::dex_lock_);
+
   // Finds a class by its descriptor, returning NULL if it isn't wasn't loaded
   // by the given 'class_loader'. Uses the provided hash for the descriptor.
   mirror::Class* LookupClass(Thread* self,
@@ -869,8 +911,6 @@
   bool WaitForInitializeClass(Handle<mirror::Class> klass,
                               Thread* self,
                               ObjectLock<mirror::Class>& lock);
-  bool ValidateSuperClassDescriptors(Handle<mirror::Class> klass)
-      REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool IsSameDescriptorInDifferentClassContexts(Thread* self,
                                                 const char* descriptor,
@@ -906,6 +946,17 @@
                    ArtMethod** out_imt)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  mirror::MethodHandle* ResolveMethodHandleForField(Thread* self,
+                                                    const DexFile::MethodHandleItem& method_handle,
+                                                    ArtMethod* referrer)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  mirror::MethodHandle* ResolveMethodHandleForMethod(Thread* self,
+                                                     const DexFile* const dex_file,
+                                                     const DexFile::MethodHandleItem& method_handle,
+                                                     ArtMethod* referrer)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   // A wrapper class representing the result of a method translation used for linking methods and
   // updating superclass default methods. For each method in a classes vtable there are 4 states it
   // could be in:
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 684a261..03cc6c5 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -1533,4 +1533,110 @@
   ASSERT_TRUE(method1_type.Get() != method2_type.Get());
 }
 
+// Verify that ClassLinker's CreateWellknownClassLoader works as expected
+// by creating a chain of class loaders with various dex files.
+TEST_F(ClassLinkerTest, CreateWellKnownClassLoader) {
+  // LoadDexIn*ClassLoader methods already assert that the parent loader is the expected one.
+  // No need to check again.
+  jobject class_loader_a = LoadDexInPathClassLoader("MyClass", nullptr);
+  jobject class_loader_b = LoadDexInDelegateLastClassLoader("Nested", class_loader_a);
+  jobject class_loader_c = LoadDexInPathClassLoader("MultiDex", class_loader_b);
+  LoadDexInDelegateLastClassLoader("Interfaces", class_loader_c);
+}
+
+class ClassLinkerClassLoaderTest : public ClassLinkerTest {
+ protected:
+  // Verifies that the class identified by the given descriptor is loaded with
+  // the expected_class_loader_obj when search from class_loader_to_search_obj.
+  // When expected_class_loader_obj is null the check will be done against BootClassLoader.
+  void VerifyClassResolution(const std::string& descriptor,
+                             jobject class_loader_to_search_obj,
+                             jobject expected_class_loader_obj,
+                             bool should_find = true) {
+    Thread* self = Thread::Current();
+    ScopedObjectAccess soa(self);
+    StackHandleScope<3> hs(self);
+    Handle<mirror::ClassLoader> class_loader_to_search(
+        hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_to_search_obj)));
+
+    Handle<mirror::Class> klass = hs.NewHandle(
+        class_linker_->FindClass(soa.Self(), descriptor.c_str(), class_loader_to_search));
+
+    if (!should_find) {
+      if (self->IsExceptionPending()) {
+        self->ClearException();
+      }
+      ASSERT_TRUE(klass == nullptr);
+    } else if (expected_class_loader_obj == nullptr) {
+      ASSERT_TRUE(ClassLinker::IsBootClassLoader(soa, klass->GetClassLoader()));
+    } else {
+      ASSERT_TRUE(klass != nullptr) << descriptor;
+      Handle<mirror::ClassLoader> expected_class_loader(
+          hs.NewHandle(soa.Decode<mirror::ClassLoader>(expected_class_loader_obj)));
+      ASSERT_EQ(klass->GetClassLoader(), expected_class_loader.Get());
+    }
+  }
+};
+
+TEST_F(ClassLinkerClassLoaderTest, CreatePathClassLoader) {
+  jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
+  VerifyClassResolution("LDefinedInA;", class_loader_a, class_loader_a);
+  VerifyClassResolution("Ljava/lang/String;", class_loader_a, nullptr);
+  VerifyClassResolution("LDefinedInB;", class_loader_a, nullptr, /*should_find*/ false);
+}
+
+TEST_F(ClassLinkerClassLoaderTest, CreateDelegateLastClassLoader) {
+  jobject class_loader_a = LoadDexInDelegateLastClassLoader("ForClassLoaderA", nullptr);
+  VerifyClassResolution("LDefinedInA;", class_loader_a, class_loader_a);
+  VerifyClassResolution("Ljava/lang/String;", class_loader_a, nullptr);
+  VerifyClassResolution("LDefinedInB;", class_loader_a, nullptr, /*should_find*/ false);
+}
+
+TEST_F(ClassLinkerClassLoaderTest, CreateClassLoaderChain) {
+  // The chain is
+  //    ClassLoaderA (PathClassLoader, defines: A, AB, AC, AD)
+  //       ^
+  //       |
+  //    ClassLoaderB (DelegateLastClassLoader, defines: B, AB, BC, BD)
+  //       ^
+  //       |
+  //    ClassLoaderC (PathClassLoader, defines: C, AC, BC, CD)
+  //       ^
+  //       |
+  //    ClassLoaderD (DelegateLastClassLoader, defines: D, AD, BD, CD)
+
+  jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
+  jobject class_loader_b = LoadDexInDelegateLastClassLoader("ForClassLoaderB", class_loader_a);
+  jobject class_loader_c = LoadDexInPathClassLoader("ForClassLoaderC", class_loader_b);
+  jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c);
+
+  // Verify exclusive classes (present in only one class loader).
+  VerifyClassResolution("LDefinedInD;", class_loader_d, class_loader_d);
+  VerifyClassResolution("LDefinedInC;", class_loader_d, class_loader_c);
+  VerifyClassResolution("LDefinedInB;", class_loader_d, class_loader_b);
+  VerifyClassResolution("LDefinedInA;", class_loader_d, class_loader_a);
+
+  // Verify classes that are defined in multiple classloader.
+
+  // Classes defined in B should be found in B even if they are defined in A or C because
+  // B is a DelegateLastClassLoader.
+  VerifyClassResolution("LDefinedInAB;", class_loader_d, class_loader_b);
+  VerifyClassResolution("LDefinedInABC;", class_loader_d, class_loader_b);
+  VerifyClassResolution("LDefinedInBC;", class_loader_d, class_loader_b);
+
+  // Classes defined in D should be found in D even if they are defined in parent class loaders
+  // as well because D is a DelegateLastClassLoader.
+  VerifyClassResolution("LDefinedInAD;", class_loader_d, class_loader_d);
+  VerifyClassResolution("LDefinedInBD;", class_loader_d, class_loader_d);
+  VerifyClassResolution("LDefinedInCD;", class_loader_d, class_loader_d);
+
+
+  // Classes not defined in the DelegateLastClassLoaders (i.e. D or B) should be found
+  // in the top parent.
+  VerifyClassResolution("LDefinedInAC;", class_loader_d, class_loader_a);
+
+  // Sanity check that we don't find an undefined class.
+  VerifyClassResolution("LNotDefined;", class_loader_d, nullptr, /*should_find*/ false);
+}
+
 }  // namespace art
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
new file mode 100644
index 0000000..2bed1d5
--- /dev/null
+++ b/runtime/class_loader_context.cc
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "class_loader_context.h"
+
+#include "base/dchecked_vector.h"
+#include "base/stl_util.h"
+#include "class_linker.h"
+#include "dex_file.h"
+#include "oat_file_assistant.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread.h"
+
+namespace art {
+
+static constexpr char kPathClassLoaderString[] = "PCL";
+static constexpr char kDelegateLastClassLoaderString[] = "DLC";
+static constexpr char kClassLoaderOpeningMark = '[';
+static constexpr char kClassLoaderClosingMark = ']';
+static constexpr char kClassLoaderSeparator = ';';
+static constexpr char kClasspathSeparator = ':';
+static constexpr char kDexFileChecksumSeparator = '*';
+
+ClassLoaderContext::ClassLoaderContext()
+    : special_shared_library_(false),
+      dex_files_open_attempted_(false),
+      dex_files_open_result_(false) {}
+
+std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Create(const std::string& spec) {
+  std::unique_ptr<ClassLoaderContext> result(new ClassLoaderContext());
+  if (result->Parse(spec)) {
+    return result;
+  } else {
+    return nullptr;
+  }
+}
+
+// The expected format is: "ClassLoaderType1[ClasspathElem1*Checksum1:ClasspathElem2*Checksum2...]".
+// The checksum part of the format is expected only if parse_cheksums is true.
+bool ClassLoaderContext::ParseClassLoaderSpec(const std::string& class_loader_spec,
+                                              ClassLoaderType class_loader_type,
+                                              bool parse_checksums) {
+  const char* class_loader_type_str = GetClassLoaderTypeName(class_loader_type);
+  size_t type_str_size = strlen(class_loader_type_str);
+
+  CHECK_EQ(0, class_loader_spec.compare(0, type_str_size, class_loader_type_str));
+
+  // Check the opening and closing markers.
+  if (class_loader_spec[type_str_size] != kClassLoaderOpeningMark) {
+    return false;
+  }
+  if (class_loader_spec[class_loader_spec.length() - 1] != kClassLoaderClosingMark) {
+    return false;
+  }
+
+  // At this point we know the format is ok; continue and extract the classpath.
+  // Note that class loaders with an empty class path are allowed.
+  std::string classpath = class_loader_spec.substr(type_str_size + 1,
+                                                   class_loader_spec.length() - type_str_size - 2);
+
+  class_loader_chain_.push_back(ClassLoaderInfo(class_loader_type));
+
+  if (!parse_checksums) {
+    Split(classpath, kClasspathSeparator, &class_loader_chain_.back().classpath);
+  } else {
+    std::vector<std::string> classpath_elements;
+    Split(classpath, kClasspathSeparator, &classpath_elements);
+    for (const std::string& element : classpath_elements) {
+      std::vector<std::string> dex_file_with_checksum;
+      Split(element, kDexFileChecksumSeparator, &dex_file_with_checksum);
+      if (dex_file_with_checksum.size() != 2) {
+        return false;
+      }
+      uint32_t checksum = 0;
+      if (!ParseInt(dex_file_with_checksum[1].c_str(), &checksum)) {
+        return false;
+      }
+      class_loader_chain_.back().classpath.push_back(dex_file_with_checksum[0]);
+      class_loader_chain_.back().checksums.push_back(checksum);
+    }
+  }
+
+  return true;
+}
+
+// Extracts the class loader type from the given spec.
+// Return ClassLoaderContext::kInvalidClassLoader if the class loader type is not
+// recognized.
+ClassLoaderContext::ClassLoaderType
+ClassLoaderContext::ExtractClassLoaderType(const std::string& class_loader_spec) {
+  const ClassLoaderType kValidTypes[] = {kPathClassLoader, kDelegateLastClassLoader};
+  for (const ClassLoaderType& type : kValidTypes) {
+    const char* type_str = GetClassLoaderTypeName(type);
+    if (class_loader_spec.compare(0, strlen(type_str), type_str) == 0) {
+      return type;
+    }
+  }
+  return kInvalidClassLoader;
+}
+
+// The format: ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]...
+// ClassLoaderType is either "PCL" (PathClassLoader) or "DLC" (DelegateLastClassLoader).
+// ClasspathElem is the path of dex/jar/apk file.
+bool ClassLoaderContext::Parse(const std::string& spec, bool parse_checksums) {
+  if (spec.empty()) {
+    return true;
+  }
+
+  // Stop early if we detect the special shared library, which may be passed as the classpath
+  // for dex2oat when we want to skip the shared libraries check.
+  if (spec == OatFile::kSpecialSharedLibrary) {
+    LOG(INFO) << "The ClassLoaderContext is a special shared library.";
+    special_shared_library_ = true;
+    return true;
+  }
+
+  std::vector<std::string> class_loaders;
+  Split(spec, kClassLoaderSeparator, &class_loaders);
+
+  for (const std::string& class_loader : class_loaders) {
+    ClassLoaderType type = ExtractClassLoaderType(class_loader);
+    if (type == kInvalidClassLoader) {
+      LOG(ERROR) << "Invalid class loader type: " << class_loader;
+      return false;
+    }
+    if (!ParseClassLoaderSpec(class_loader, type, parse_checksums)) {
+      LOG(ERROR) << "Invalid class loader spec: " << class_loader;
+      return false;
+    }
+  }
+  return true;
+}
+
+// Opens requested class path files and appends them to opened_dex_files. If the dex files have
+// been stripped, this opens them from their oat files (which get added to opened_oat_files).
+bool ClassLoaderContext::OpenDexFiles(InstructionSet isa, const std::string& classpath_dir) {
+  CHECK(!dex_files_open_attempted_) << "OpenDexFiles should not be called twice";
+
+  dex_files_open_attempted_ = true;
+  // Assume we can open all dex files. If not, we will set this to false as we go.
+  dex_files_open_result_ = true;
+
+  if (special_shared_library_) {
+    // Nothing to open if the context is a special shared library.
+    return true;
+  }
+
+  // Note that we try to open all dex files even if some fail.
+  // We may get resource-only apks which we cannot load.
+  // TODO(calin): Refine the dex opening interface to be able to tell if an archive contains
+  // no dex files. So that we can distinguish the real failures...
+  for (ClassLoaderInfo& info : class_loader_chain_) {
+    for (const std::string& cp_elem : info.classpath) {
+      // If path is relative, append it to the provided base directory.
+      std::string location = cp_elem;
+      if (location[0] != '/') {
+        location = classpath_dir + '/' + location;
+      }
+      std::string error_msg;
+      // When opening the dex files from the context we expect their checksum to match their
+      // contents. So pass true to verify_checksum.
+      if (!DexFile::Open(location.c_str(),
+                         location.c_str(),
+                         /*verify_checksum*/ true,
+                         &error_msg,
+                         &info.opened_dex_files)) {
+        // If we fail to open the dex file because it's been stripped, try to open the dex file
+        // from its corresponding oat file.
+        // This could happen when we need to recompile a pre-build whose dex code has been stripped.
+        // (for example, if the pre-build is only quicken and we want to re-compile it
+        // speed-profile).
+        // TODO(calin): Use the vdex directly instead of going through the oat file.
+        OatFileAssistant oat_file_assistant(location.c_str(), isa, false);
+        std::unique_ptr<OatFile> oat_file(oat_file_assistant.GetBestOatFile());
+        std::vector<std::unique_ptr<const DexFile>> oat_dex_files;
+        if (oat_file != nullptr &&
+            OatFileAssistant::LoadDexFiles(*oat_file, location, &oat_dex_files)) {
+          info.opened_oat_files.push_back(std::move(oat_file));
+          info.opened_dex_files.insert(info.opened_dex_files.end(),
+                                       std::make_move_iterator(oat_dex_files.begin()),
+                                       std::make_move_iterator(oat_dex_files.end()));
+        } else {
+          LOG(WARNING) << "Could not open dex files from location: " << location;
+          dex_files_open_result_ = false;
+        }
+      }
+    }
+  }
+
+  return dex_files_open_result_;
+}
+
+bool ClassLoaderContext::RemoveLocationsFromClassPaths(
+    const dchecked_vector<std::string>& locations) {
+  CHECK(!dex_files_open_attempted_)
+      << "RemoveLocationsFromClasspaths cannot be call after OpenDexFiles";
+
+  std::set<std::string> canonical_locations;
+  for (const std::string& location : locations) {
+    canonical_locations.insert(DexFile::GetDexCanonicalLocation(location.c_str()));
+  }
+  bool removed_locations = false;
+  for (ClassLoaderInfo& info : class_loader_chain_) {
+    size_t initial_size = info.classpath.size();
+    auto kept_it = std::remove_if(
+        info.classpath.begin(),
+        info.classpath.end(),
+        [canonical_locations](const std::string& location) {
+            return ContainsElement(canonical_locations,
+                                   DexFile::GetDexCanonicalLocation(location.c_str()));
+        });
+    info.classpath.erase(kept_it, info.classpath.end());
+    if (initial_size != info.classpath.size()) {
+      removed_locations = true;
+    }
+  }
+  return removed_locations;
+}
+
+std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_dir) const {
+  CheckDexFilesOpened("EncodeContextForOatFile");
+  if (special_shared_library_) {
+    return OatFile::kSpecialSharedLibrary;
+  }
+
+  if (class_loader_chain_.empty()) {
+    return "";
+  }
+
+  std::ostringstream out;
+
+  for (size_t i = 0; i < class_loader_chain_.size(); i++) {
+    const ClassLoaderInfo& info = class_loader_chain_[i];
+    if (i > 0) {
+      out << kClassLoaderSeparator;
+    }
+    out << GetClassLoaderTypeName(info.type);
+    out << kClassLoaderOpeningMark;
+    for (size_t k = 0; k < info.opened_dex_files.size(); k++) {
+      const std::unique_ptr<const DexFile>& dex_file = info.opened_dex_files[k];
+      const std::string& location = dex_file->GetLocation();
+      if (k > 0) {
+        out << kClasspathSeparator;
+      }
+      // Find paths that were relative and convert them back from absolute.
+      if (!base_dir.empty() && location.substr(0, base_dir.length()) == base_dir) {
+        out << location.substr(base_dir.length() + 1).c_str();
+      } else {
+        out << dex_file->GetLocation().c_str();
+      }
+      out << kDexFileChecksumSeparator;
+      out << dex_file->GetLocationChecksum();
+    }
+    out << kClassLoaderClosingMark;
+  }
+  return out.str();
+}
+
+jobject ClassLoaderContext::CreateClassLoader(
+    const std::vector<const DexFile*>& compilation_sources) const {
+  CheckDexFilesOpened("CreateClassLoader");
+
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+
+  std::vector<const DexFile*> class_path_files;
+
+  // TODO(calin): Transition period: assume we only have a classloader until
+  // the oat file assistant implements the full class loader check.
+  if (!class_loader_chain_.empty()) {
+    CHECK_EQ(1u, class_loader_chain_.size());
+    CHECK_EQ(kPathClassLoader, class_loader_chain_[0].type);
+    class_path_files = MakeNonOwningPointerVector(class_loader_chain_[0].opened_dex_files);
+  }
+
+  // Classpath: first the class-path given; then the dex files we'll compile.
+  // Thus we'll resolve the class-path first.
+  class_path_files.insert(class_path_files.end(),
+                          compilation_sources.begin(),
+                          compilation_sources.end());
+
+  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+  return class_linker->CreatePathClassLoader(self, class_path_files);
+}
+
+std::vector<const DexFile*> ClassLoaderContext::FlattenOpenedDexFiles() const {
+  CheckDexFilesOpened("FlattenOpenedDexFiles");
+
+  std::vector<const DexFile*> result;
+  for (const ClassLoaderInfo& info : class_loader_chain_) {
+    for (const std::unique_ptr<const DexFile>& dex_file : info.opened_dex_files) {
+      result.push_back(dex_file.get());
+    }
+  }
+  return result;
+}
+
+const char* ClassLoaderContext::GetClassLoaderTypeName(ClassLoaderType type) {
+  switch (type) {
+    case kPathClassLoader: return kPathClassLoaderString;
+    case kDelegateLastClassLoader: return kDelegateLastClassLoaderString;
+    default:
+      LOG(FATAL) << "Invalid class loader type " << type;
+      UNREACHABLE();
+  }
+}
+
+void ClassLoaderContext::CheckDexFilesOpened(const std::string& calling_method) const {
+  CHECK(dex_files_open_attempted_)
+      << "Dex files were not successfully opened before the call to " << calling_method
+      << "attempt=" << dex_files_open_attempted_ << ", result=" << dex_files_open_result_;
+}
+
+bool ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey(
+    const std::string& context_spec,
+    std::vector<std::string>* out_classpath,
+    std::vector<uint32_t>* out_checksums,
+    bool* out_is_special_shared_library) {
+  ClassLoaderContext context;
+  if (!context.Parse(context_spec, /*parse_checksums*/ true)) {
+    LOG(ERROR) << "Invalid class loader context: " << context_spec;
+    return false;
+  }
+
+  *out_is_special_shared_library = context.special_shared_library_;
+  if (context.special_shared_library_) {
+    return true;
+  }
+
+  if (context.class_loader_chain_.empty()) {
+    return true;
+  }
+
+  // TODO(calin): assert that we only have a PathClassLoader until the logic for
+  // checking the context covers all case.
+  CHECK_EQ(1u, context.class_loader_chain_.size());
+  const ClassLoaderInfo& info = context.class_loader_chain_[0];
+  CHECK_EQ(kPathClassLoader, info.type);
+  DCHECK_EQ(info.classpath.size(), info.checksums.size());
+
+  *out_classpath = info.classpath;
+  *out_checksums = info.checksums;
+  return true;
+}
+}  // namespace art
+
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
new file mode 100644
index 0000000..9727a3b
--- /dev/null
+++ b/runtime/class_loader_context.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_CLASS_LOADER_CONTEXT_H_
+#define ART_RUNTIME_CLASS_LOADER_CONTEXT_H_
+
+#include <string>
+#include <vector>
+
+#include "arch/instruction_set.h"
+#include "base/dchecked_vector.h"
+#include "jni.h"
+
+namespace art {
+
+class DexFile;
+class OatFile;
+
+// Utility class which holds the class loader context used during compilation/verification.
+class ClassLoaderContext {
+ public:
+  // Creates an empty context (with no class loaders).
+  ClassLoaderContext();
+
+  // Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files.
+  // If the dex files have been stripped, the method opens them from their oat files which are added
+  // to ClassLoaderInfo::opened_oat_files. The 'classpath_dir' argument specifies the directory to
+  // use for the relative class paths.
+  // Returns true if all dex files where successfully opened.
+  // It may be called only once per ClassLoaderContext. The second call will abort.
+  //
+  // Note that a "false" return could mean that either an apk/jar contained no dex files or
+  // that we hit a I/O or checksum mismatch error.
+  // TODO(calin): Currently there's no easy way to tell the difference.
+  //
+  // TODO(calin): we're forced to complicate the flow in this class with a different
+  // OpenDexFiles step because the current dex2oat flow requires the dex files be opened before
+  // the class loader is created. Consider reworking the dex2oat part.
+  bool OpenDexFiles(InstructionSet isa, const std::string& classpath_dir);
+
+  // Remove the specified compilation sources from all classpaths present in this context.
+  // Should only be called before the first call to OpenDexFiles().
+  bool RemoveLocationsFromClassPaths(const dchecked_vector<std::string>& compilation_sources);
+
+  // Creates the entire class loader hierarchy according to the current context.
+  // The compilation sources are appended to the classpath of the top class loader
+  // (i.e the class loader whose parent is the BootClassLoader).
+  // Should only be called if OpenDexFiles() returned true.
+  // If the context is empty, this method only creates a single PathClassLoader with the
+  // given compilation_sources.
+  jobject CreateClassLoader(const std::vector<const DexFile*>& compilation_sources) const;
+
+  // Encodes the context as a string suitable to be added in oat files.
+  // (so that it can be read and verified at runtime against the actual class
+  // loader hierarchy).
+  // Should only be called if OpenDexFiles() returned true.
+  // E.g. if the context is PCL[a.dex:b.dex] this will return "a.dex*a_checksum*b.dex*a_checksum".
+  std::string EncodeContextForOatFile(const std::string& base_dir) const;
+
+  // Flattens the opened dex files into the given vector.
+  // Should only be called if OpenDexFiles() returned true.
+  std::vector<const DexFile*> FlattenOpenedDexFiles() const;
+
+  // Creates the class loader context from the given string.
+  // The format: ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]...
+  // ClassLoaderType is either "PCL" (PathClassLoader) or "DLC" (DelegateLastClassLoader).
+  // ClasspathElem is the path of dex/jar/apk file.
+  // Note that we allowed class loaders with an empty class path in order to support a custom
+  // class loader for the source dex files.
+  static std::unique_ptr<ClassLoaderContext> Create(const std::string& spec);
+
+  // Decodes the class loader context stored in the oat file with EncodeContextForOatFile.
+  // Returns true if the format matches, or false otherwise. If the return is true, the out
+  // arguments will contain the classpath dex files, their checksums and whether or not the
+  // context is a special shared library.
+  // The method asserts that the context is made out of only one PathClassLoader.
+  static bool DecodePathClassLoaderContextFromOatFileKey(
+      const std::string& context_spec,
+      std::vector<std::string>* out_classpath,
+      std::vector<uint32_t>* out_checksums,
+      bool* out_is_special_shared_library);
+
+ private:
+  enum ClassLoaderType {
+    kInvalidClassLoader = 0,
+    kPathClassLoader = 1,
+    kDelegateLastClassLoader = 2
+  };
+
+  struct ClassLoaderInfo {
+    // The type of this class loader.
+    ClassLoaderType type;
+    // The list of class path elements that this loader loads.
+    // Note that this list may contain relative paths.
+    std::vector<std::string> classpath;
+    // The list of class path elements checksums.
+    // May be empty if the checksums are not given when the context is created.
+    std::vector<uint32_t> checksums;
+    // After OpenDexFiles is called this holds the opened dex files.
+    std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
+    // After OpenDexFiles, in case some of the dex files were opened from their oat files
+    // this holds the list of opened oat files.
+    std::vector<std::unique_ptr<OatFile>> opened_oat_files;
+
+    explicit ClassLoaderInfo(ClassLoaderType cl_type) : type(cl_type) {}
+  };
+
+  // Reads the class loader spec in place and returns true if the spec is valid and the
+  // compilation context was constructed.
+  bool Parse(const std::string& spec, bool parse_checksums = false);
+
+  // Attempts to parse a single class loader spec for the given class_loader_type.
+  // If successful the class loader spec will be added to the chain.
+  // Returns whether or not the operation was successful.
+  bool ParseClassLoaderSpec(const std::string& class_loader_spec,
+                            ClassLoaderType class_loader_type,
+                            bool parse_checksums = false);
+
+  // Extracts the class loader type from the given spec.
+  // Return ClassLoaderContext::kInvalidClassLoader if the class loader type is not
+  // recognized.
+  static ClassLoaderType ExtractClassLoaderType(const std::string& class_loader_spec);
+
+  // Returns the string representation of the class loader type.
+  // The returned format can be used when parsing a context spec.
+  static const char* GetClassLoaderTypeName(ClassLoaderType type);
+
+  // CHECKs that the dex files were opened (OpenDexFiles was called). Aborts if not.
+  void CheckDexFilesOpened(const std::string& calling_method) const;
+
+  // The class loader chain represented as a vector.
+  // The parent of class_loader_chain_[i] is class_loader_chain_[i++].
+  // The parent of the last element is assumed to be the boot class loader.
+  std::vector<ClassLoaderInfo> class_loader_chain_;
+
+  // Whether or not the class loader context should be ignored at runtime when loading the oat
+  // files. When true, dex2oat will use OatFile::kSpecialSharedLibrary as the classpath key in
+  // the oat file.
+  // TODO(calin): Can we get rid of this and cover all relevant use cases?
+  // (e.g. packages using prebuild system packages as shared libraries b/36480683)
+  bool special_shared_library_;
+
+  // Whether or not OpenDexFiles() was called.
+  bool dex_files_open_attempted_;
+  // The result of the last OpenDexFiles() operation.
+  bool dex_files_open_result_;
+
+  friend class ClassLoaderContextTest;
+
+  DISALLOW_COPY_AND_ASSIGN(ClassLoaderContext);
+};
+
+}  // namespace art
+#endif  // ART_RUNTIME_CLASS_LOADER_CONTEXT_H_
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
new file mode 100644
index 0000000..03eb0e4
--- /dev/null
+++ b/runtime/class_loader_context_test.cc
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+
+#include "class_loader_context.h"
+#include "common_runtime_test.h"
+
+#include "base/dchecked_vector.h"
+#include "base/stl_util.h"
+#include "class_linker.h"
+#include "dex_file.h"
+#include "handle_scope-inl.h"
+#include "mirror/class.h"
+#include "mirror/class_loader.h"
+#include "mirror/object-inl.h"
+#include "oat_file_assistant.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread.h"
+#include "well_known_classes.h"
+
+namespace art {
+
+class ClassLoaderContextTest : public CommonRuntimeTest {
+ public:
+  void VerifyContextSize(ClassLoaderContext* context, size_t expected_size) {
+    ASSERT_TRUE(context != nullptr);
+    ASSERT_EQ(expected_size, context->class_loader_chain_.size());
+  }
+
+  void VerifyClassLoaderPCL(ClassLoaderContext* context,
+                            size_t index,
+                            std::string classpath) {
+    VerifyClassLoaderInfo(
+        context, index, ClassLoaderContext::kPathClassLoader, classpath);
+  }
+
+  void VerifyClassLoaderDLC(ClassLoaderContext* context,
+                            size_t index,
+                            std::string classpath) {
+    VerifyClassLoaderInfo(
+        context, index, ClassLoaderContext::kDelegateLastClassLoader, classpath);
+  }
+
+  void VerifyOpenDexFiles(
+      ClassLoaderContext* context,
+      size_t index,
+      std::vector<std::vector<std::unique_ptr<const DexFile>>*>& all_dex_files) {
+    ASSERT_TRUE(context != nullptr);
+    ASSERT_TRUE(context->dex_files_open_attempted_);
+    ASSERT_TRUE(context->dex_files_open_result_);
+    ClassLoaderContext::ClassLoaderInfo& info = context->class_loader_chain_[index];
+    ASSERT_EQ(all_dex_files.size(), info.classpath.size());
+    size_t cur_open_dex_index = 0;
+    for (size_t k = 0; k < all_dex_files.size(); k++) {
+      std::vector<std::unique_ptr<const DexFile>>& dex_files_for_cp_elem = *(all_dex_files[k]);
+      for (size_t i = 0; i < dex_files_for_cp_elem.size(); i++) {
+        ASSERT_LT(cur_open_dex_index, info.opened_dex_files.size());
+
+        std::unique_ptr<const DexFile>& opened_dex_file =
+            info.opened_dex_files[cur_open_dex_index++];
+        std::unique_ptr<const DexFile>& expected_dex_file = dex_files_for_cp_elem[i];
+
+        ASSERT_EQ(expected_dex_file->GetLocation(), opened_dex_file->GetLocation());
+        ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_dex_file->GetLocationChecksum());
+        ASSERT_EQ(info.classpath[k], opened_dex_file->GetBaseLocation());
+      }
+    }
+  }
+
+ private:
+  void VerifyClassLoaderInfo(ClassLoaderContext* context,
+                             size_t index,
+                             ClassLoaderContext::ClassLoaderType type,
+                             std::string classpath) {
+    ASSERT_TRUE(context != nullptr);
+    ASSERT_GT(context->class_loader_chain_.size(), index);
+    ClassLoaderContext::ClassLoaderInfo& info = context->class_loader_chain_[index];
+    ASSERT_EQ(type, info.type);
+    std::vector<std::string> expected_classpath;
+    Split(classpath, ':', &expected_classpath);
+    ASSERT_EQ(expected_classpath, info.classpath);
+  }
+};
+
+TEST_F(ClassLoaderContextTest, ParseValidContextPCL) {
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create("PCL[a.dex]");
+  VerifyContextSize(context.get(), 1);
+  VerifyClassLoaderPCL(context.get(), 0, "a.dex");
+}
+
+TEST_F(ClassLoaderContextTest, ParseValidContextDLC) {
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create("DLC[a.dex]");
+  VerifyContextSize(context.get(), 1);
+  VerifyClassLoaderDLC(context.get(), 0, "a.dex");
+}
+
+TEST_F(ClassLoaderContextTest, ParseValidContextChain) {
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create("PCL[a.dex:b.dex];DLC[c.dex:d.dex];PCL[e.dex]");
+  VerifyContextSize(context.get(), 3);
+  VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex");
+  VerifyClassLoaderDLC(context.get(), 1, "c.dex:d.dex");
+  VerifyClassLoaderPCL(context.get(), 2, "e.dex");
+}
+
+TEST_F(ClassLoaderContextTest, ParseValidEmptyContextDLC) {
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create("DLC[]");
+  VerifyContextSize(context.get(), 1);
+  VerifyClassLoaderDLC(context.get(), 0, "");
+}
+
+TEST_F(ClassLoaderContextTest, ParseValidContextSpecialSymbol) {
+  std::unique_ptr<ClassLoaderContext> context =
+    ClassLoaderContext::Create(OatFile::kSpecialSharedLibrary);
+  VerifyContextSize(context.get(), 0);
+}
+
+TEST_F(ClassLoaderContextTest, ParseInvalidValidContexts) {
+  ASSERT_TRUE(nullptr == ClassLoaderContext::Create("ABC[a.dex]"));
+  ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL"));
+  ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL[a.dex"));
+  ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCLa.dex]"));
+  ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{a.dex}"));
+  ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL[a.dex];DLC[b.dex"));
+}
+
+TEST_F(ClassLoaderContextTest, OpenInvalidDexFiles) {
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create("PCL[does_not_exist.dex]");
+  VerifyContextSize(context.get(), 1);
+  ASSERT_FALSE(context->OpenDexFiles(InstructionSet::kArm, "."));
+}
+
+TEST_F(ClassLoaderContextTest, OpenValidDexFiles) {
+  std::string multidex_name = GetTestDexFileName("MultiDex");
+  std::vector<std::unique_ptr<const DexFile>> multidex_files = OpenTestDexFiles("MultiDex");
+  std::string myclass_dex_name = GetTestDexFileName("MyClass");
+  std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
+  std::string dex_name = GetTestDexFileName("Main");
+  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("Main");
+
+
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create(
+          "PCL[" + multidex_name + ":" + myclass_dex_name + "];" +
+          "DLC[" + dex_name + "]");
+
+  ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, /*classpath_dir*/ ""));
+
+  VerifyContextSize(context.get(), 2);
+  std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files0;
+  all_dex_files0.push_back(&multidex_files);
+  all_dex_files0.push_back(&myclass_dex_files);
+  std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files1;
+  all_dex_files1.push_back(&dex_files);
+
+  VerifyOpenDexFiles(context.get(), 0, all_dex_files0);
+  VerifyOpenDexFiles(context.get(), 1, all_dex_files1);
+}
+
+TEST_F(ClassLoaderContextTest, OpenInvalidDexFilesMix) {
+  std::string dex_name = GetTestDexFileName("Main");
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create("PCL[does_not_exist.dex];DLC[" + dex_name + "]");
+  ASSERT_FALSE(context->OpenDexFiles(InstructionSet::kArm, ""));
+}
+
+TEST_F(ClassLoaderContextTest, CreateClassLoader) {
+  std::string dex_name = GetTestDexFileName("Main");
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create("PCL[" + dex_name + "]");
+  ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, ""));
+
+  std::vector<std::unique_ptr<const DexFile>> classpath_dex = OpenTestDexFiles("Main");
+  std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex");
+
+  std::vector<const DexFile*> compilation_sources_raw =
+      MakeNonOwningPointerVector(compilation_sources);
+  jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw);
+  ASSERT_TRUE(jclass_loader != nullptr);
+
+  ScopedObjectAccess soa(Thread::Current());
+
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader = hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(jclass_loader));
+
+  ASSERT_TRUE(class_loader->GetClass() ==
+      soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
+  ASSERT_TRUE(class_loader->GetParent()->GetClass() ==
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+
+
+  std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(jclass_loader);
+  ASSERT_EQ(classpath_dex.size() + compilation_sources.size(), class_loader_dex_files.size());
+
+  // The classpath dex files must come first.
+  for (size_t i = 0; i < classpath_dex.size(); i++) {
+    ASSERT_EQ(classpath_dex[i]->GetLocation(),
+              class_loader_dex_files[i]->GetLocation());
+    ASSERT_EQ(classpath_dex[i]->GetLocationChecksum(),
+              class_loader_dex_files[i]->GetLocationChecksum());
+  }
+
+  // The compilation dex files must come second.
+  for (size_t i = 0, k = classpath_dex.size(); i < compilation_sources.size(); i++, k++) {
+    ASSERT_EQ(compilation_sources[i]->GetLocation(),
+              class_loader_dex_files[k]->GetLocation());
+    ASSERT_EQ(compilation_sources[i]->GetLocationChecksum(),
+              class_loader_dex_files[k]->GetLocationChecksum());
+  }
+}
+
+TEST_F(ClassLoaderContextTest, CreateClassLoaderWithEmptyContext) {
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create("");
+  ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, ""));
+
+  std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex");
+
+  std::vector<const DexFile*> compilation_sources_raw =
+      MakeNonOwningPointerVector(compilation_sources);
+  jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw);
+  ASSERT_TRUE(jclass_loader != nullptr);
+
+  ScopedObjectAccess soa(Thread::Current());
+
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader = hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(jclass_loader));
+
+  ASSERT_TRUE(class_loader->GetClass() ==
+      soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
+  ASSERT_TRUE(class_loader->GetParent()->GetClass() ==
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+
+
+  std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(jclass_loader);
+
+  // The compilation sources should be the only files present in the class loader
+  ASSERT_EQ(compilation_sources.size(), class_loader_dex_files.size());
+  for (size_t i = 0; i < compilation_sources.size(); i++) {
+    ASSERT_EQ(compilation_sources[i]->GetLocation(),
+        class_loader_dex_files[i]->GetLocation());
+    ASSERT_EQ(compilation_sources[i]->GetLocationChecksum(),
+        class_loader_dex_files[i]->GetLocationChecksum());
+  }
+}
+
+TEST_F(ClassLoaderContextTest, RemoveSourceLocations) {
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create("PCL[a.dex]");
+  dchecked_vector<std::string> classpath_dex;
+  classpath_dex.push_back("a.dex");
+  dchecked_vector<std::string> compilation_sources;
+  compilation_sources.push_back("src.dex");
+
+  // Nothing should be removed.
+  ASSERT_FALSE(context->RemoveLocationsFromClassPaths(compilation_sources));
+  VerifyClassLoaderPCL(context.get(), 0, "a.dex");
+  // Classes should be removed.
+  ASSERT_TRUE(context->RemoveLocationsFromClassPaths(classpath_dex));
+  VerifyClassLoaderPCL(context.get(), 0, "");
+}
+
+TEST_F(ClassLoaderContextTest, EncodeInOatFile) {
+  std::string dex1_name = GetTestDexFileName("Main");
+  std::string dex2_name = GetTestDexFileName("MyClass");
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create("PCL[" + dex1_name + ":" + dex2_name + "]");
+  ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, ""));
+
+  std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("Main");
+  std::vector<std::unique_ptr<const DexFile>> dex2 = OpenTestDexFiles("MyClass");
+  std::string encoding = context->EncodeContextForOatFile("");
+  std::string expected_encoding = "PCL[" +
+      dex1[0]->GetLocation() + "*" + std::to_string(dex1[0]->GetLocationChecksum()) + ":" +
+      dex2[0]->GetLocation() + "*" + std::to_string(dex2[0]->GetLocationChecksum()) + "]";
+  ASSERT_EQ(expected_encoding, context->EncodeContextForOatFile(""));
+}
+
+TEST_F(ClassLoaderContextTest, DecodeOatFileKey) {
+  std::string oat_file_encoding = "PCL[a.dex*123:b.dex*456]";
+  std::vector<std::string> classpath;
+  std::vector<uint32_t> checksums;
+  bool is_special_shared_library;
+  bool result = ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey(
+      oat_file_encoding,
+      &classpath,
+      &checksums,
+      &is_special_shared_library);
+  ASSERT_TRUE(result);
+  ASSERT_FALSE(is_special_shared_library);
+  ASSERT_EQ(2u, classpath.size());
+  ASSERT_EQ(2u, checksums.size());
+  ASSERT_EQ("a.dex", classpath[0]);
+  ASSERT_EQ(123u, checksums[0]);
+  ASSERT_EQ("b.dex", classpath[1]);
+  ASSERT_EQ(456u, checksums[1]);
+}
+
+TEST_F(ClassLoaderContextTest, DecodeOatFileKeySpecialLibrary) {
+  std::string oat_file_encoding = "&";
+  std::vector<std::string> classpath;
+  std::vector<uint32_t> checksums;
+  bool is_special_shared_library;
+  bool result = ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey(
+      oat_file_encoding,
+      &classpath,
+      &checksums,
+      &is_special_shared_library);
+  ASSERT_TRUE(result);
+  ASSERT_TRUE(is_special_shared_library);
+  ASSERT_TRUE(classpath.empty());
+  ASSERT_TRUE(checksums.empty());
+}
+
+}  // namespace art
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 6441a44..659c7e4 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -680,19 +680,66 @@
 }
 
 jobject CommonRuntimeTestImpl::LoadDex(const char* dex_name) {
-  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name);
+  jobject class_loader = LoadDexInPathClassLoader(dex_name, nullptr);
+  Thread::Current()->SetClassLoaderOverride(class_loader);
+  return class_loader;
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInWellKnownClassLoader(const std::string& dex_name,
+                                                             jclass loader_class,
+                                                             jobject parent_loader) {
+  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name.c_str());
   std::vector<const DexFile*> class_path;
   CHECK_NE(0U, dex_files.size());
   for (auto& dex_file : dex_files) {
     class_path.push_back(dex_file.get());
     loaded_dex_files_.push_back(std::move(dex_file));
   }
-
   Thread* self = Thread::Current();
-  jobject class_loader = Runtime::Current()->GetClassLinker()->CreatePathClassLoader(self,
-                                                                                     class_path);
-  self->SetClassLoaderOverride(class_loader);
-  return class_loader;
+  ScopedObjectAccess soa(self);
+
+  jobject result = Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader(
+      self,
+      class_path,
+      loader_class,
+      parent_loader);
+
+  {
+    // Verify we build the correct chain.
+
+    ObjPtr<mirror::ClassLoader> actual_class_loader = soa.Decode<mirror::ClassLoader>(result);
+    // Verify that the result has the correct class.
+    CHECK_EQ(soa.Decode<mirror::Class>(loader_class), actual_class_loader->GetClass());
+    // Verify that the parent is not null. The boot class loader will be set up as a
+    // proper object.
+    ObjPtr<mirror::ClassLoader> actual_parent(actual_class_loader->GetParent());
+    CHECK(actual_parent != nullptr);
+
+    if (parent_loader != nullptr) {
+      // We were given a parent. Verify that it's what we expect.
+      ObjPtr<mirror::ClassLoader> expected_parent = soa.Decode<mirror::ClassLoader>(parent_loader);
+      CHECK_EQ(expected_parent, actual_parent);
+    } else {
+      // No parent given. The parent must be the BootClassLoader.
+      CHECK(Runtime::Current()->GetClassLinker()->IsBootClassLoader(soa, actual_parent));
+    }
+  }
+
+  return result;
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInPathClassLoader(const std::string& dex_name,
+                                                        jobject parent_loader) {
+  return LoadDexInWellKnownClassLoader(dex_name,
+                                       WellKnownClasses::dalvik_system_PathClassLoader,
+                                       parent_loader);
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInDelegateLastClassLoader(const std::string& dex_name,
+                                                                jobject parent_loader) {
+  return LoadDexInWellKnownClassLoader(dex_name,
+                                       WellKnownClasses::dalvik_system_DelegateLastClassLoader,
+                                       parent_loader);
 }
 
 std::string CommonRuntimeTestImpl::GetCoreFileLocation(const char* suffix) {
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 3b3e6c5..fcf3a31 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -134,10 +134,20 @@
 
   std::unique_ptr<const DexFile> OpenTestDexFile(const char* name);
 
+  // Loads the test dex file identified by the given dex_name into a PathClassLoader.
+  // Returns the created class loader.
   jobject LoadDex(const char* dex_name) REQUIRES_SHARED(Locks::mutator_lock_);
+  // Loads the test dex file identified by the given first_dex_name and second_dex_name
+  // into a PathClassLoader. Returns the created class loader.
   jobject LoadMultiDex(const char* first_dex_name, const char* second_dex_name)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  jobject LoadDexInPathClassLoader(const std::string& dex_name, jobject parent_loader);
+  jobject LoadDexInDelegateLastClassLoader(const std::string& dex_name, jobject parent_loader);
+  jobject LoadDexInWellKnownClassLoader(const std::string& dex_name,
+                                        jclass loader_class,
+                                        jobject parent_loader);
+
   std::string android_data_;
   std::string dalvik_cache_;
 
@@ -237,6 +247,12 @@
     return; \
   }
 
+#define TEST_DISABLED_WITHOUT_BAKER_READ_BARRIERS() \
+  if (!kEmitCompilerReadBarrier || !kUseBakerReadBarrier) { \
+    printf("WARNING: TEST DISABLED FOR GC WITHOUT BAKER READ BARRIER\n"); \
+    return; \
+  }
+
 #define TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS() \
   if (!kHostStaticBuildEnabled) { \
     printf("WARNING: TEST DISABLED FOR NON-STATIC HOST BUILDS\n"); \
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index c94a8e0..778b928 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -18,7 +18,9 @@
 
 #include <sys/uio.h>
 
+#include <memory>
 #include <set>
+#include <vector>
 
 #include "android-base/stringprintf.h"
 
@@ -26,6 +28,7 @@
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "base/enums.h"
+#include "base/strlcpy.h"
 #include "base/time_utils.h"
 #include "class_linker.h"
 #include "class_linker-inl.h"
@@ -36,6 +39,7 @@
 #include "gc/accounting/card_table-inl.h"
 #include "gc/allocation_record.h"
 #include "gc/scoped_gc_critical_section.h"
+#include "gc/space/bump_pointer_space-walk-inl.h"
 #include "gc/space/large_object_space.h"
 #include "gc/space/space-inl.h"
 #include "handle_scope-inl.h"
@@ -2477,7 +2481,8 @@
     needs_resume = thread->GetDebugSuspendCount() > 0;
   }
   if (needs_resume) {
-    Runtime::Current()->GetThreadList()->Resume(thread, SuspendReason::kForDebugger);
+    bool resumed = Runtime::Current()->GetThreadList()->Resume(thread, SuspendReason::kForDebugger);
+    DCHECK(resumed);
   }
 }
 
@@ -3718,7 +3723,9 @@
 
   ~ScopedDebuggerThreadSuspension() {
     if (other_suspend_) {
-      Runtime::Current()->GetThreadList()->Resume(thread_, SuspendReason::kForDebugger);
+      bool resumed = Runtime::Current()->GetThreadList()->Resume(thread_,
+                                                                 SuspendReason::kForDebugger);
+      DCHECK(resumed);
     }
   }
 
@@ -4040,7 +4047,8 @@
     thread_list->UndoDebuggerSuspensions();
   } else {
     VLOG(jdwp) << "      Resuming event thread only";
-    thread_list->Resume(targetThread, SuspendReason::kForDebugger);
+    bool resumed = thread_list->Resume(targetThread, SuspendReason::kForDebugger);
+    DCHECK(resumed);
   }
 
   return JDWP::ERR_NONE;
@@ -4806,13 +4814,6 @@
   DISALLOW_COPY_AND_ASSIGN(HeapChunkContext);
 };
 
-static void BumpPointerSpaceCallback(mirror::Object* obj, void* arg)
-    REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
-  const size_t size = RoundUp(obj->SizeOf(), kObjectAlignment);
-  HeapChunkContext::HeapChunkJavaCallback(
-      obj, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(obj) + size), size, arg);
-}
-
 void Dbg::DdmSendHeapSegments(bool native) {
   Dbg::HpsgWhen when = native ? gDdmNhsgWhen : gDdmHpsgWhen;
   Dbg::HpsgWhat what = native ? gDdmNhsgWhat : gDdmHpsgWhat;
@@ -4832,6 +4833,12 @@
 
   // Send a series of heap segment chunks.
   HeapChunkContext context(what == HPSG_WHAT_MERGED_OBJECTS, native);
+  auto bump_pointer_space_visitor = [&](mirror::Object* obj)
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
+    const size_t size = RoundUp(obj->SizeOf(), kObjectAlignment);
+    HeapChunkContext::HeapChunkJavaCallback(
+        obj, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(obj) + size), size, &context);
+  };
   if (native) {
     UNIMPLEMENTED(WARNING) << "Native heap inspection is not supported";
   } else {
@@ -4854,7 +4861,7 @@
       } else if (space->IsBumpPointerSpace()) {
         ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
         context.SetChunkOverhead(0);
-        space->AsBumpPointerSpace()->Walk(BumpPointerSpaceCallback, &context);
+        space->AsBumpPointerSpace()->Walk(bump_pointer_space_visitor);
         HeapChunkContext::HeapChunkJavaCallback(nullptr, nullptr, 0, &context);
       } else if (space->IsRegionSpace()) {
         heap->IncrementDisableMovingGC(self);
@@ -4863,7 +4870,7 @@
           ScopedSuspendAll ssa(__FUNCTION__);
           ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
           context.SetChunkOverhead(0);
-          space->AsRegionSpace()->Walk(BumpPointerSpaceCallback, &context);
+          space->AsRegionSpace()->Walk(bump_pointer_space_visitor);
           HeapChunkContext::HeapChunkJavaCallback(nullptr, nullptr, 0, &context);
         }
         heap->DecrementDisableMovingGC(self);
@@ -4922,24 +4929,81 @@
 }
 
 class StringTable {
+ private:
+  struct Entry {
+    explicit Entry(const char* data_in)
+        : data(data_in), hash(ComputeModifiedUtf8Hash(data_in)), index(0) {
+    }
+    Entry(const Entry& entry) = default;
+    Entry(Entry&& entry) = default;
+
+    // Pointer to the actual string data.
+    const char* data;
+
+    // The hash of the data.
+    const uint32_t hash;
+
+    // The index. This will be filled in on Finish and is not part of the ordering, so mark it
+    // mutable.
+    mutable uint32_t index;
+
+    bool operator==(const Entry& other) const {
+      return strcmp(data, other.data) == 0;
+    }
+  };
+  struct EntryHash {
+    size_t operator()(const Entry& entry) const {
+      return entry.hash;
+    }
+  };
+
  public:
-  StringTable() {
+  StringTable() : finished_(false) {
   }
 
-  void Add(const std::string& str) {
-    table_.insert(str);
+  void Add(const char* str, bool copy_string) {
+    DCHECK(!finished_);
+    if (UNLIKELY(copy_string)) {
+      // Check whether it's already there.
+      Entry entry(str);
+      if (table_.find(entry) != table_.end()) {
+        return;
+      }
+
+      // Make a copy.
+      size_t str_len = strlen(str);
+      char* copy = new char[str_len + 1];
+      strlcpy(copy, str, str_len + 1);
+      string_backup_.emplace_back(copy);
+      str = copy;
+    }
+    Entry entry(str);
+    table_.insert(entry);
   }
 
-  void Add(const char* str) {
-    table_.insert(str);
+  // Update all entries and give them an index. Note that this is likely not the insertion order,
+  // as the set will with high likelihood reorder elements. Thus, Add must not be called after
+  // Finish, and Finish must be called before IndexOf. In that case, WriteTo will walk in
+  // the same order as Finish, and indices will agree. The order invariant, as well as indices,
+  // are enforced through debug checks.
+  void Finish() {
+    DCHECK(!finished_);
+    finished_ = true;
+    uint32_t index = 0;
+    for (auto& entry : table_) {
+      entry.index = index;
+      ++index;
+    }
   }
 
   size_t IndexOf(const char* s) const {
-    auto it = table_.find(s);
+    DCHECK(finished_);
+    Entry entry(s);
+    auto it = table_.find(entry);
     if (it == table_.end()) {
       LOG(FATAL) << "IndexOf(\"" << s << "\") failed";
     }
-    return std::distance(table_.begin(), it);
+    return it->index;
   }
 
   size_t Size() const {
@@ -4947,17 +5011,24 @@
   }
 
   void WriteTo(std::vector<uint8_t>& bytes) const {
-    for (const std::string& str : table_) {
-      const char* s = str.c_str();
-      size_t s_len = CountModifiedUtf8Chars(s);
+    DCHECK(finished_);
+    uint32_t cur_index = 0;
+    for (const auto& entry : table_) {
+      DCHECK_EQ(cur_index++, entry.index);
+
+      size_t s_len = CountModifiedUtf8Chars(entry.data);
       std::unique_ptr<uint16_t[]> s_utf16(new uint16_t[s_len]);
-      ConvertModifiedUtf8ToUtf16(s_utf16.get(), s);
+      ConvertModifiedUtf8ToUtf16(s_utf16.get(), entry.data);
       JDWP::AppendUtf16BE(bytes, s_utf16.get(), s_len);
     }
   }
 
  private:
-  std::set<std::string> table_;
+  std::unordered_set<Entry, EntryHash> table_;
+  std::vector<std::unique_ptr<char[]>> string_backup_;
+
+  bool finished_;
+
   DISALLOW_COPY_AND_ASSIGN(StringTable);
 };
 
@@ -5038,21 +5109,40 @@
     StringTable method_names;
     StringTable filenames;
 
+    VLOG(jdwp) << "Collecting StringTables.";
+
     const uint16_t capped_count = CappedAllocRecordCount(records->GetRecentAllocationSize());
     uint16_t count = capped_count;
+    size_t alloc_byte_count = 0;
     for (auto it = records->RBegin(), end = records->REnd();
          count > 0 && it != end; count--, it++) {
       const gc::AllocRecord* record = &it->second;
       std::string temp;
-      class_names.Add(record->GetClassDescriptor(&temp));
+      const char* class_descr = record->GetClassDescriptor(&temp);
+      class_names.Add(class_descr, !temp.empty());
+
+      // Size + tid + class name index + stack depth.
+      alloc_byte_count += 4u + 2u + 2u + 1u;
+
       for (size_t i = 0, depth = record->GetDepth(); i < depth; i++) {
         ArtMethod* m = record->StackElement(i).GetMethod();
-        class_names.Add(m->GetDeclaringClassDescriptor());
-        method_names.Add(m->GetName());
-        filenames.Add(GetMethodSourceFile(m));
+        class_names.Add(m->GetDeclaringClassDescriptor(), false);
+        method_names.Add(m->GetName(), false);
+        filenames.Add(GetMethodSourceFile(m), false);
       }
+
+      // Depth * (class index + method name index + file name index + line number).
+      alloc_byte_count += record->GetDepth() * (2u + 2u + 2u + 2u);
     }
 
+    class_names.Finish();
+    method_names.Finish();
+    filenames.Finish();
+    VLOG(jdwp) << "Done collecting StringTables:" << std::endl
+               << "  ClassNames: " << class_names.Size() << std::endl
+               << "  MethodNames: " << method_names.Size() << std::endl
+               << "  Filenames: " << filenames.Size();
+
     LOG(INFO) << "recent allocation records: " << capped_count;
     LOG(INFO) << "allocation records all objects: " << records->Size();
 
@@ -5082,6 +5172,12 @@
     JDWP::Append2BE(bytes, method_names.Size());
     JDWP::Append2BE(bytes, filenames.Size());
 
+    VLOG(jdwp) << "Dumping allocations with stacks";
+
+    // Enlarge the vector for the allocation data.
+    size_t reserve_size = bytes.size() + alloc_byte_count;
+    bytes.reserve(reserve_size);
+
     std::string temp;
     count = capped_count;
     // The last "count" number of allocation records in "records" are the most recent "count" number
@@ -5119,6 +5215,9 @@
       }
     }
 
+    CHECK_EQ(bytes.size(), reserve_size);
+    VLOG(jdwp) << "Dumping tables.";
+
     // (xb) class name strings
     // (xb) method name strings
     // (xb) source file strings
@@ -5126,6 +5225,8 @@
     class_names.WriteTo(bytes);
     method_names.WriteTo(bytes);
     filenames.WriteTo(bytes);
+
+    VLOG(jdwp) << "GetRecentAllocations: data created. " << bytes.size();
   }
   JNIEnv* env = self->GetJniEnv();
   jbyteArray result = env->NewByteArray(bytes.size());
diff --git a/runtime/dex_file_tracking_registrar.cc b/runtime/dex_file_tracking_registrar.cc
index d958568..3411586 100644
--- a/runtime/dex_file_tracking_registrar.cc
+++ b/runtime/dex_file_tracking_registrar.cc
@@ -58,6 +58,15 @@
   // Additionally unpoisons the entire Code Item when method is a class
   // initializer.
   kCodeItemNonInsnsNoClinitTracking,
+  // Poisons the size and offset information along with the first instruction.
+  // This is so that accessing multiple instructions while accessing a code item
+  // once will not trigger unnecessary accesses.
+  kCodeItemStartTracking,
+  // Poisons all String Data Items of a Dex Files when set.
+  kStringDataItemTracking,
+  // Poisons the first byte of the utf16_size value and the first byte of the
+  // data section for all String Data Items of a Dex File.
+  kStringDataItemStartTracking,
   // Poisons based on a custom tracking system which can be specified in
   // SetDexSections
   kCustomTracking,
@@ -89,10 +98,21 @@
         SetAllInsnsRegistration(false);
         SetCodeItemRegistration("<clinit>", false);
         break;
+      case kCodeItemStartTracking:
+        SetAllCodeItemStartRegistration(true);
+        break;
+      case kStringDataItemTracking:
+        SetAllStringDataRegistration(true);
+        break;
+      case kStringDataItemStartTracking:
+        SetAllStringDataStartRegistration(true);
+        break;
       case kCustomTracking:
         // TODO: Add/remove additional calls here to (un)poison sections of
         // dex_file_
         break;
+      default:
+        break;
     }
   }
 }
@@ -151,6 +171,28 @@
   }
 }
 
+void DexFileTrackingRegistrar::SetAllCodeItemStartRegistration(bool should_poison) {
+  for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) {
+    const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr);
+    const uint8_t* class_data = dex_file_->GetClassData(cd);
+    if (class_data != nullptr) {
+      ClassDataItemIterator cdit(*dex_file_, class_data);
+      cdit.SkipAllFields();
+      while (cdit.HasNextDirectMethod()) {
+        const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem();
+        if (code_item != nullptr) {
+          const void* code_item_begin = reinterpret_cast<const void*>(code_item);
+          size_t code_item_start = reinterpret_cast<size_t>(code_item);
+          size_t code_item_start_end = reinterpret_cast<size_t>(&code_item->insns_[1]);
+          size_t code_item_start_size = code_item_start_end - code_item_start;
+          range_values_.push_back(std::make_tuple(code_item_begin, code_item_start_size, should_poison));
+        }
+        cdit.Next();
+      }
+    }
+  }
+}
+
 void DexFileTrackingRegistrar::SetAllInsnsRegistration(bool should_poison) {
   for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) {
     const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr);
@@ -186,8 +228,7 @@
         if (code_item != nullptr && strcmp(methodid_name, class_name) == 0) {
           const void* code_item_begin = reinterpret_cast<const void*>(code_item);
           size_t code_item_size = DexFile::GetCodeItemSize(*code_item);
-          range_values_.push_back(
-              std::make_tuple(code_item_begin, code_item_size, should_poison));
+          range_values_.push_back(std::make_tuple(code_item_begin, code_item_size, should_poison));
         }
         cdit.Next();
       }
@@ -195,6 +236,31 @@
   }
 }
 
+void DexFileTrackingRegistrar::SetAllStringDataStartRegistration(bool should_poison) {
+  for (size_t stringid_ctr = 0; stringid_ctr < dex_file_->NumStringIds(); ++stringid_ctr) {
+    const DexFile::StringId & string_id = dex_file_->GetStringId(StringIndex(stringid_ctr));
+    const void* string_data_begin = reinterpret_cast<const void*>(dex_file_->Begin() + string_id.string_data_off_);
+    // Data Section of String Data Item
+    const void* string_data_data_begin = reinterpret_cast<const void*>(dex_file_->GetStringData(string_id));
+    range_values_.push_back(std::make_tuple(string_data_begin, 1, should_poison));
+    range_values_.push_back(std::make_tuple(string_data_data_begin, 1, should_poison));
+  }
+}
+
+void DexFileTrackingRegistrar::SetAllStringDataRegistration(bool should_poison) {
+  size_t map_offset = dex_file_->GetHeader().map_off_;
+  auto map_list = reinterpret_cast<const DexFile::MapList*>(dex_file_->Begin() + map_offset);
+  for (size_t map_ctr = 0; map_ctr < map_list->size_; ++map_ctr) {
+    const DexFile::MapItem& map_item = map_list->list_[map_ctr];
+    if (map_item.type_ == DexFile::kDexTypeStringDataItem) {
+      const DexFile::MapItem& next_map_item = map_list->list_[map_ctr + 1];
+      const void* string_data_begin = reinterpret_cast<const void*>(dex_file_->Begin() + map_item.offset_);
+      size_t string_data_size = next_map_item.offset_ - map_item.offset_;
+      range_values_.push_back(std::make_tuple(string_data_begin, string_data_size, should_poison));
+    }
+  }
+}
+
 }  // namespace tracking
 }  // namespace dex
 }  // namespace art
diff --git a/runtime/dex_file_tracking_registrar.h b/runtime/dex_file_tracking_registrar.h
index b0fa275..5c0e0f5 100644
--- a/runtime/dex_file_tracking_registrar.h
+++ b/runtime/dex_file_tracking_registrar.h
@@ -54,6 +54,15 @@
   void SetAllInsnsRegistration(bool should_poison);
   // This function finds the code item of a class based on class name.
   void SetCodeItemRegistration(const char* class_name, bool should_poison);
+  // Sets the size and offset information along with first instruction in insns_
+  // section of all code items.
+  void SetAllCodeItemStartRegistration(bool should_poison);
+
+  // Set of functions concerning String Data Items of dex_file_
+  void SetAllStringDataRegistration(bool should_poison);
+  // Sets the first byte of size value and data section of all string data
+  // items.
+  void SetAllStringDataStartRegistration(bool should_poison);
 
   // Contains tuples of all ranges of memory that need to be explicitly
   // (un)poisoned by the memory tool.
diff --git a/runtime/dex_to_dex_decompiler.cc b/runtime/dex_to_dex_decompiler.cc
index c15c9ec..908405b 100644
--- a/runtime/dex_to_dex_decompiler.cc
+++ b/runtime/dex_to_dex_decompiler.cc
@@ -18,9 +18,10 @@
 
 #include "base/logging.h"
 #include "base/mutex.h"
+#include "bytecode_utils.h"
 #include "dex_file-inl.h"
 #include "dex_instruction-inl.h"
-#include "bytecode_utils.h"
+#include "quicken_info.h"
 
 namespace art {
 namespace optimizer {
@@ -31,27 +32,21 @@
                 const ArrayRef<const uint8_t>& quickened_info,
                 bool decompile_return_instruction)
     : code_item_(code_item),
-      quickened_info_ptr_(quickened_info.data()),
-      quickened_info_start_(quickened_info.data()),
-      quickened_info_end_(quickened_info.data() + quickened_info.size()),
+      quicken_info_(quickened_info.data()),
+      quicken_info_number_of_indices_(QuickenInfoTable::NumberOfIndices(quickened_info.size())),
       decompile_return_instruction_(decompile_return_instruction) {}
 
   bool Decompile();
 
  private:
-  void DecompileInstanceFieldAccess(Instruction* inst,
-                                    uint32_t dex_pc,
-                                    Instruction::Code new_opcode) {
-    uint16_t index = GetIndexAt(dex_pc);
+  void DecompileInstanceFieldAccess(Instruction* inst, Instruction::Code new_opcode) {
+    uint16_t index = NextIndex();
     inst->SetOpcode(new_opcode);
     inst->SetVRegC_22c(index);
   }
 
-  void DecompileInvokeVirtual(Instruction* inst,
-                              uint32_t dex_pc,
-                              Instruction::Code new_opcode,
-                              bool is_range) {
-    uint16_t index = GetIndexAt(dex_pc);
+  void DecompileInvokeVirtual(Instruction* inst, Instruction::Code new_opcode, bool is_range) {
+    const uint16_t index = NextIndex();
     inst->SetOpcode(new_opcode);
     if (is_range) {
       inst->SetVRegB_3rc(index);
@@ -60,40 +55,32 @@
     }
   }
 
-  void DecompileNop(Instruction* inst, uint32_t dex_pc) {
-    if (quickened_info_ptr_ == quickened_info_end_) {
+  void DecompileNop(Instruction* inst) {
+    const uint16_t reference_index = NextIndex();
+    if (reference_index == DexFile::kDexNoIndex16) {
+      // This means it was a normal nop and not a check-cast.
       return;
     }
-    const uint8_t* temporary_pointer = quickened_info_ptr_;
-    uint32_t quickened_pc = DecodeUnsignedLeb128(&temporary_pointer);
-    if (quickened_pc != dex_pc) {
-      return;
-    }
-    uint16_t reference_index = GetIndexAt(dex_pc);
-    uint16_t type_index = GetIndexAt(dex_pc);
+    const uint16_t type_index = NextIndex();
     inst->SetOpcode(Instruction::CHECK_CAST);
     inst->SetVRegA_21c(reference_index);
     inst->SetVRegB_21c(type_index);
   }
 
-  uint16_t GetIndexAt(uint32_t dex_pc) {
-    // Note that as a side effect, DecodeUnsignedLeb128 update the given pointer
-    // to the new position in the buffer.
-    DCHECK_LT(quickened_info_ptr_, quickened_info_end_);
-    uint32_t quickened_pc = DecodeUnsignedLeb128(&quickened_info_ptr_);
-    DCHECK_LT(quickened_info_ptr_, quickened_info_end_);
-    uint16_t index = DecodeUnsignedLeb128(&quickened_info_ptr_);
-    DCHECK_LE(quickened_info_ptr_, quickened_info_end_);
-    DCHECK_EQ(quickened_pc, dex_pc);
-    return index;
+  uint16_t NextIndex() {
+    DCHECK_LT(quicken_index_, quicken_info_number_of_indices_);
+    const uint16_t ret = quicken_info_.GetData(quicken_index_);
+    quicken_index_++;
+    return ret;
   }
 
   const DexFile::CodeItem& code_item_;
-  const uint8_t* quickened_info_ptr_;
-  const uint8_t* const quickened_info_start_;
-  const uint8_t* const quickened_info_end_;
+  const QuickenInfoTable quicken_info_;
+  const size_t quicken_info_number_of_indices_;
   const bool decompile_return_instruction_;
 
+  size_t quicken_index_ = 0u;
+
   DISALLOW_COPY_AND_ASSIGN(DexDecompiler);
 };
 
@@ -103,7 +90,6 @@
   // unquickening is a rare need and not performance sensitive, it is not worth the
   // added storage to also add the RETURN_VOID quickening in the quickened data.
   for (CodeItemIterator it(code_item_); !it.Done(); it.Advance()) {
-    uint32_t dex_pc = it.CurrentDexPc();
     Instruction* inst = const_cast<Instruction*>(&it.CurrentInstruction());
 
     switch (inst->Opcode()) {
@@ -114,71 +100,76 @@
         break;
 
       case Instruction::NOP:
-        DecompileNop(inst, dex_pc);
+        if (quicken_info_number_of_indices_ > 0) {
+          // Only try to decompile NOP if there are more than 0 indices. Not having
+          // any index happens when we unquicken a code item that only has
+          // RETURN_VOID_NO_BARRIER as quickened instruction.
+          DecompileNop(inst);
+        }
         break;
 
       case Instruction::IGET_QUICK:
-        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET);
+        DecompileInstanceFieldAccess(inst, Instruction::IGET);
         break;
 
       case Instruction::IGET_WIDE_QUICK:
-        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE);
+        DecompileInstanceFieldAccess(inst, Instruction::IGET_WIDE);
         break;
 
       case Instruction::IGET_OBJECT_QUICK:
-        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT);
+        DecompileInstanceFieldAccess(inst, Instruction::IGET_OBJECT);
         break;
 
       case Instruction::IGET_BOOLEAN_QUICK:
-        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BOOLEAN);
+        DecompileInstanceFieldAccess(inst, Instruction::IGET_BOOLEAN);
         break;
 
       case Instruction::IGET_BYTE_QUICK:
-        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BYTE);
+        DecompileInstanceFieldAccess(inst, Instruction::IGET_BYTE);
         break;
 
       case Instruction::IGET_CHAR_QUICK:
-        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_CHAR);
+        DecompileInstanceFieldAccess(inst, Instruction::IGET_CHAR);
         break;
 
       case Instruction::IGET_SHORT_QUICK:
-        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_SHORT);
+        DecompileInstanceFieldAccess(inst, Instruction::IGET_SHORT);
         break;
 
       case Instruction::IPUT_QUICK:
-        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT);
+        DecompileInstanceFieldAccess(inst, Instruction::IPUT);
         break;
 
       case Instruction::IPUT_BOOLEAN_QUICK:
-        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BOOLEAN);
+        DecompileInstanceFieldAccess(inst, Instruction::IPUT_BOOLEAN);
         break;
 
       case Instruction::IPUT_BYTE_QUICK:
-        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BYTE);
+        DecompileInstanceFieldAccess(inst, Instruction::IPUT_BYTE);
         break;
 
       case Instruction::IPUT_CHAR_QUICK:
-        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_CHAR);
+        DecompileInstanceFieldAccess(inst, Instruction::IPUT_CHAR);
         break;
 
       case Instruction::IPUT_SHORT_QUICK:
-        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_SHORT);
+        DecompileInstanceFieldAccess(inst, Instruction::IPUT_SHORT);
         break;
 
       case Instruction::IPUT_WIDE_QUICK:
-        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE);
+        DecompileInstanceFieldAccess(inst, Instruction::IPUT_WIDE);
         break;
 
       case Instruction::IPUT_OBJECT_QUICK:
-        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT);
+        DecompileInstanceFieldAccess(inst, Instruction::IPUT_OBJECT);
         break;
 
       case Instruction::INVOKE_VIRTUAL_QUICK:
-        DecompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL, false);
+        DecompileInvokeVirtual(inst, Instruction::INVOKE_VIRTUAL, false);
         break;
 
       case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
-        DecompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE, true);
+        DecompileInvokeVirtual(inst, Instruction::INVOKE_VIRTUAL_RANGE, true);
         break;
 
       default:
@@ -186,14 +177,14 @@
     }
   }
 
-  if (quickened_info_ptr_ != quickened_info_end_) {
-    if (quickened_info_start_ == quickened_info_ptr_) {
+  if (quicken_index_ != quicken_info_number_of_indices_) {
+    if (quicken_index_ == 0) {
       LOG(WARNING) << "Failed to use any value in quickening info,"
                    << " potentially due to duplicate methods.";
     } else {
       LOG(FATAL) << "Failed to use all values in quickening info."
-                 << " Actual: " << std::hex << reinterpret_cast<uintptr_t>(quickened_info_ptr_)
-                 << " Expected: " << reinterpret_cast<uintptr_t>(quickened_info_end_);
+                 << " Actual: " << std::hex << quicken_index_
+                 << " Expected: " << quicken_info_number_of_indices_;
       return false;
     }
   }
diff --git a/runtime/gc/accounting/heap_bitmap-inl.h b/runtime/gc/accounting/heap_bitmap-inl.h
index 8fcc87d..edf2e5b 100644
--- a/runtime/gc/accounting/heap_bitmap-inl.h
+++ b/runtime/gc/accounting/heap_bitmap-inl.h
@@ -26,7 +26,7 @@
 namespace accounting {
 
 template <typename Visitor>
-inline void HeapBitmap::Visit(const Visitor& visitor) {
+inline void HeapBitmap::Visit(Visitor&& visitor) {
   for (const auto& bitmap : continuous_space_bitmaps_) {
     bitmap->VisitMarkedRange(bitmap->HeapBegin(), bitmap->HeapLimit(), visitor);
   }
diff --git a/runtime/gc/accounting/heap_bitmap.cc b/runtime/gc/accounting/heap_bitmap.cc
index a5d59bf..1d729ff 100644
--- a/runtime/gc/accounting/heap_bitmap.cc
+++ b/runtime/gc/accounting/heap_bitmap.cc
@@ -71,15 +71,6 @@
   large_object_bitmaps_.erase(it);
 }
 
-void HeapBitmap::Walk(ObjectCallback* callback, void* arg) {
-  for (const auto& bitmap : continuous_space_bitmaps_) {
-    bitmap->Walk(callback, arg);
-  }
-  for (const auto& bitmap : large_object_bitmaps_) {
-    bitmap->Walk(callback, arg);
-  }
-}
-
 }  // namespace accounting
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/accounting/heap_bitmap.h b/runtime/gc/accounting/heap_bitmap.h
index 7097f87..36426e9 100644
--- a/runtime/gc/accounting/heap_bitmap.h
+++ b/runtime/gc/accounting/heap_bitmap.h
@@ -47,11 +47,8 @@
   ContinuousSpaceBitmap* GetContinuousSpaceBitmap(const mirror::Object* obj) const;
   LargeObjectBitmap* GetLargeObjectBitmap(const mirror::Object* obj) const;
 
-  void Walk(ObjectCallback* callback, void* arg)
-      REQUIRES_SHARED(Locks::heap_bitmap_lock_);
-
   template <typename Visitor>
-  void Visit(const Visitor& visitor)
+  ALWAYS_INLINE void Visit(Visitor&& visitor)
       REQUIRES(Locks::heap_bitmap_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index 57c290e..2901995 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -27,6 +27,7 @@
 #include "gc/space/space.h"
 #include "mirror/object-inl.h"
 #include "mirror/object-refvisitor-inl.h"
+#include "object_callbacks.h"
 #include "space_bitmap-inl.h"
 #include "thread-current-inl.h"
 
@@ -383,7 +384,7 @@
   }
 }
 
-void ModUnionTableReferenceCache::VisitObjects(ObjectCallback* callback, void* arg) {
+void ModUnionTableReferenceCache::VisitObjects(ObjectCallback callback, void* arg) {
   CardTable* const card_table = heap_->GetCardTable();
   ContinuousSpaceBitmap* live_bitmap = space_->GetLiveBitmap();
   for (uint8_t* card : cleared_cards_) {
@@ -550,7 +551,7 @@
       0, RoundUp(space_->Size(), CardTable::kCardSize) / CardTable::kCardSize, bit_visitor);
 }
 
-void ModUnionTableCardCache::VisitObjects(ObjectCallback* callback, void* arg) {
+void ModUnionTableCardCache::VisitObjects(ObjectCallback callback, void* arg) {
   card_bitmap_->VisitSetBits(
       0,
       RoundUp(space_->Size(), CardTable::kCardSize) / CardTable::kCardSize,
diff --git a/runtime/gc/accounting/mod_union_table.h b/runtime/gc/accounting/mod_union_table.h
index 591365f..9e261fd 100644
--- a/runtime/gc/accounting/mod_union_table.h
+++ b/runtime/gc/accounting/mod_union_table.h
@@ -21,21 +21,25 @@
 #include "base/allocator.h"
 #include "card_table.h"
 #include "globals.h"
-#include "object_callbacks.h"
+#include "mirror/object_reference.h"
 #include "safe_map.h"
 
 #include <set>
 #include <vector>
 
 namespace art {
+
 namespace mirror {
 class Object;
 }  // namespace mirror
 
+class MarkObjectVisitor;
+
 namespace gc {
 namespace space {
   class ContinuousSpace;
 }  // namespace space
+
 class Heap;
 
 namespace accounting {
@@ -44,6 +48,9 @@
 // cleared between GC phases, reducing the number of dirty cards that need to be scanned.
 class ModUnionTable {
  public:
+  // A callback for visiting an object in the heap.
+  using ObjectCallback = void (*)(mirror::Object*, void*);
+
   typedef std::set<uint8_t*, std::less<uint8_t*>,
                    TrackingAllocator<uint8_t*, kAllocatorTagModUnionCardSet>> CardSet;
   typedef MemoryRangeBitmap<CardTable::kCardSize> CardBitmap;
@@ -72,7 +79,7 @@
   virtual void UpdateAndMarkReferences(MarkObjectVisitor* visitor) = 0;
 
   // Visit all of the objects that may contain references to other spaces.
-  virtual void VisitObjects(ObjectCallback* callback, void* arg) = 0;
+  virtual void VisitObjects(ObjectCallback callback, void* arg) = 0;
 
   // Verification, sanity checks that we don't have clean cards which conflict with out cached data
   // for said cards. Exclusive lock is required since verify sometimes uses
@@ -124,7 +131,7 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(Locks::heap_bitmap_lock_);
 
-  virtual void VisitObjects(ObjectCallback* callback, void* arg) OVERRIDE
+  virtual void VisitObjects(ObjectCallback callback, void* arg) OVERRIDE
       REQUIRES(Locks::heap_bitmap_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -171,7 +178,7 @@
       REQUIRES(Locks::heap_bitmap_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  virtual void VisitObjects(ObjectCallback* callback, void* arg) OVERRIDE
+  virtual void VisitObjects(ObjectCallback callback, void* arg) OVERRIDE
       REQUIRES(Locks::heap_bitmap_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h
index 9feaf41..b37dd96 100644
--- a/runtime/gc/accounting/space_bitmap-inl.h
+++ b/runtime/gc/accounting/space_bitmap-inl.h
@@ -62,8 +62,9 @@
 }
 
 template<size_t kAlignment> template<typename Visitor>
-inline void SpaceBitmap<kAlignment>::VisitMarkedRange(uintptr_t visit_begin, uintptr_t visit_end,
-                                                      const Visitor& visitor) const {
+inline void SpaceBitmap<kAlignment>::VisitMarkedRange(uintptr_t visit_begin,
+                                                      uintptr_t visit_end,
+                                                      Visitor&& visitor) const {
   DCHECK_LE(visit_begin, visit_end);
 #if 0
   for (uintptr_t i = visit_begin; i < visit_end; i += kAlignment) {
@@ -155,6 +156,26 @@
 #endif
 }
 
+template<size_t kAlignment> template<typename Visitor>
+void SpaceBitmap<kAlignment>::Walk(Visitor&& visitor) {
+  CHECK(bitmap_begin_ != nullptr);
+
+  uintptr_t end = OffsetToIndex(HeapLimit() - heap_begin_ - 1);
+  Atomic<uintptr_t>* bitmap_begin = bitmap_begin_;
+  for (uintptr_t i = 0; i <= end; ++i) {
+    uintptr_t w = bitmap_begin[i].LoadRelaxed();
+    if (w != 0) {
+      uintptr_t ptr_base = IndexToOffset(i) + heap_begin_;
+      do {
+        const size_t shift = CTZ(w);
+        mirror::Object* obj = reinterpret_cast<mirror::Object*>(ptr_base + shift * kAlignment);
+        visitor(obj);
+        w ^= (static_cast<uintptr_t>(1)) << shift;
+      } while (w != 0);
+    }
+  }
+}
+
 template<size_t kAlignment> template<bool kSetBit>
 inline bool SpaceBitmap<kAlignment>::Modify(const mirror::Object* obj) {
   uintptr_t addr = reinterpret_cast<uintptr_t>(obj);
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index eb9f039..317e2fc 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -137,27 +137,6 @@
 }
 
 template<size_t kAlignment>
-void SpaceBitmap<kAlignment>::Walk(ObjectCallback* callback, void* arg) {
-  CHECK(bitmap_begin_ != nullptr);
-  CHECK(callback != nullptr);
-
-  uintptr_t end = OffsetToIndex(HeapLimit() - heap_begin_ - 1);
-  Atomic<uintptr_t>* bitmap_begin = bitmap_begin_;
-  for (uintptr_t i = 0; i <= end; ++i) {
-    uintptr_t w = bitmap_begin[i].LoadRelaxed();
-    if (w != 0) {
-      uintptr_t ptr_base = IndexToOffset(i) + heap_begin_;
-      do {
-        const size_t shift = CTZ(w);
-        mirror::Object* obj = reinterpret_cast<mirror::Object*>(ptr_base + shift * kAlignment);
-        (*callback)(obj, arg);
-        w ^= (static_cast<uintptr_t>(1)) << shift;
-      } while (w != 0);
-    }
-  }
-}
-
-template<size_t kAlignment>
 void SpaceBitmap<kAlignment>::SweepWalk(const SpaceBitmap<kAlignment>& live_bitmap,
                                         const SpaceBitmap<kAlignment>& mark_bitmap,
                                         uintptr_t sweep_begin, uintptr_t sweep_end,
diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h
index 889f57b..2fe6394 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -34,9 +34,6 @@
 }  // namespace mirror
 class MemMap;
 
-// Same as in object_callbacks.h. Just avoid the include.
-typedef void (ObjectCallback)(mirror::Object* obj, void* arg);
-
 namespace gc {
 namespace accounting {
 
@@ -108,8 +105,6 @@
     return index < bitmap_size_ / sizeof(intptr_t);
   }
 
-  void VisitRange(uintptr_t base, uintptr_t max, ObjectCallback* callback, void* arg) const;
-
   class ClearVisitor {
    public:
     explicit ClearVisitor(SpaceBitmap* const bitmap)
@@ -134,13 +129,14 @@
   // TODO: Use lock annotations when clang is fixed.
   // REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_);
   template <typename Visitor>
-  void VisitMarkedRange(uintptr_t visit_begin, uintptr_t visit_end, const Visitor& visitor) const
+  void VisitMarkedRange(uintptr_t visit_begin, uintptr_t visit_end, Visitor&& visitor) const
       NO_THREAD_SAFETY_ANALYSIS;
 
   // Visits set bits in address order.  The callback is not permitted to change the bitmap bits or
   // max during the traversal.
-  void Walk(ObjectCallback* callback, void* arg)
-      REQUIRES_SHARED(Locks::heap_bitmap_lock_);
+  template <typename Visitor>
+  void Walk(Visitor&& visitor)
+      REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
   // Walk through the bitmaps in increasing address order, and find the object pointers that
   // correspond to garbage objects.  Call <callback> zero or more times with lists of these object
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index c0d6481..9d672b1 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -166,7 +166,7 @@
   }
   if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects) {
     // Switch to read barrier mark entrypoints before we gray the objects. This is required in case
-    // a mutator sees a gray bit and dispatches on the entrpoint. (b/37876887).
+    // a mutator sees a gray bit and dispatches on the entrypoint. (b/37876887).
     ActivateReadBarrierEntrypoints();
     // Gray dirty immune objects concurrently to reduce GC pause times. We re-process gray cards in
     // the pause.
@@ -583,23 +583,22 @@
   ObjPtr<mirror::Object> const holder_;
 };
 
-void ConcurrentCopying::VerifyNoMissingCardMarkCallback(mirror::Object* obj, void* arg) {
-  auto* collector = reinterpret_cast<ConcurrentCopying*>(arg);
-  // Objects not on dirty or aged cards should never have references to newly allocated regions.
-  if (collector->heap_->GetCardTable()->GetCard(obj) == gc::accounting::CardTable::kCardClean) {
-    VerifyNoMissingCardMarkVisitor visitor(collector, /*holder*/ obj);
-    obj->VisitReferences</*kVisitNativeRoots*/true, kVerifyNone, kWithoutReadBarrier>(
-        visitor,
-        visitor);
-  }
-}
-
 void ConcurrentCopying::VerifyNoMissingCardMarks() {
+  auto visitor = [&](mirror::Object* obj)
+      REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!mark_stack_lock_) {
+    // Objects not on dirty or aged cards should never have references to newly allocated regions.
+    if (heap_->GetCardTable()->GetCard(obj) == gc::accounting::CardTable::kCardClean) {
+      VerifyNoMissingCardMarkVisitor internal_visitor(this, /*holder*/ obj);
+      obj->VisitReferences</*kVisitNativeRoots*/true, kVerifyNone, kWithoutReadBarrier>(
+          internal_visitor, internal_visitor);
+    }
+  };
   TimingLogger::ScopedTiming split(__FUNCTION__, GetTimings());
-  region_space_->Walk(&VerifyNoMissingCardMarkCallback, this);
+  region_space_->Walk(visitor);
   {
     ReaderMutexLock rmu(Thread::Current(), *Locks::heap_bitmap_lock_);
-    heap_->GetLiveBitmap()->Walk(&VerifyNoMissingCardMarkCallback, this);
+    heap_->GetLiveBitmap()->Visit(visitor);
   }
 }
 
@@ -1212,34 +1211,6 @@
   ConcurrentCopying* const collector_;
 };
 
-class ConcurrentCopying::VerifyNoFromSpaceRefsObjectVisitor {
- public:
-  explicit VerifyNoFromSpaceRefsObjectVisitor(ConcurrentCopying* collector)
-      : collector_(collector) {}
-  void operator()(mirror::Object* obj) const
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    ObjectCallback(obj, collector_);
-  }
-  static void ObjectCallback(mirror::Object* obj, void *arg)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    CHECK(obj != nullptr);
-    ConcurrentCopying* collector = reinterpret_cast<ConcurrentCopying*>(arg);
-    space::RegionSpace* region_space = collector->RegionSpace();
-    CHECK(!region_space->IsInFromSpace(obj)) << "Scanning object " << obj << " in from space";
-    VerifyNoFromSpaceRefsFieldVisitor visitor(collector);
-    obj->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>(
-        visitor,
-        visitor);
-    if (kUseBakerReadBarrier) {
-      CHECK_EQ(obj->GetReadBarrierState(), ReadBarrier::WhiteState())
-          << "obj=" << obj << " non-white rb_state " << obj->GetReadBarrierState();
-    }
-  }
-
- private:
-  ConcurrentCopying* const collector_;
-};
-
 // Verify there's no from-space references left after the marking phase.
 void ConcurrentCopying::VerifyNoFromSpaceReferences() {
   Thread* self = Thread::Current();
@@ -1252,7 +1223,21 @@
       CHECK(!thread->GetIsGcMarking());
     }
   }
-  VerifyNoFromSpaceRefsObjectVisitor visitor(this);
+
+  auto verify_no_from_space_refs_visitor = [&](mirror::Object* obj)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    CHECK(obj != nullptr);
+    space::RegionSpace* region_space = RegionSpace();
+    CHECK(!region_space->IsInFromSpace(obj)) << "Scanning object " << obj << " in from space";
+    VerifyNoFromSpaceRefsFieldVisitor visitor(this);
+    obj->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>(
+        visitor,
+        visitor);
+    if (kUseBakerReadBarrier) {
+      CHECK_EQ(obj->GetReadBarrierState(), ReadBarrier::WhiteState())
+          << "obj=" << obj << " non-white rb_state " << obj->GetReadBarrierState();
+    }
+  };
   // Roots.
   {
     ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
@@ -1260,11 +1245,11 @@
     Runtime::Current()->VisitRoots(&ref_visitor);
   }
   // The to-space.
-  region_space_->WalkToSpace(VerifyNoFromSpaceRefsObjectVisitor::ObjectCallback, this);
+  region_space_->WalkToSpace(verify_no_from_space_refs_visitor);
   // Non-moving spaces.
   {
     WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
-    heap_->GetMarkBitmap()->Visit(visitor);
+    heap_->GetMarkBitmap()->Visit(verify_no_from_space_refs_visitor);
   }
   // The alloc stack.
   {
@@ -1275,7 +1260,7 @@
       if (obj != nullptr && obj->GetClass() != nullptr) {
         // TODO: need to call this only if obj is alive?
         ref_visitor(obj);
-        visitor(obj);
+        verify_no_from_space_refs_visitor(obj);
       }
     }
   }
@@ -1337,31 +1322,6 @@
   ConcurrentCopying* const collector_;
 };
 
-class ConcurrentCopying::AssertToSpaceInvariantObjectVisitor {
- public:
-  explicit AssertToSpaceInvariantObjectVisitor(ConcurrentCopying* collector)
-      : collector_(collector) {}
-  void operator()(mirror::Object* obj) const
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    ObjectCallback(obj, collector_);
-  }
-  static void ObjectCallback(mirror::Object* obj, void *arg)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    CHECK(obj != nullptr);
-    ConcurrentCopying* collector = reinterpret_cast<ConcurrentCopying*>(arg);
-    space::RegionSpace* region_space = collector->RegionSpace();
-    CHECK(!region_space->IsInFromSpace(obj)) << "Scanning object " << obj << " in from space";
-    collector->AssertToSpaceInvariant(nullptr, MemberOffset(0), obj);
-    AssertToSpaceInvariantFieldVisitor visitor(collector);
-    obj->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>(
-        visitor,
-        visitor);
-  }
-
- private:
-  ConcurrentCopying* const collector_;
-};
-
 class ConcurrentCopying::RevokeThreadLocalMarkStackCheckpoint : public Closure {
  public:
   RevokeThreadLocalMarkStackCheckpoint(ConcurrentCopying* concurrent_copying,
@@ -1599,8 +1559,14 @@
     region_space_->AddLiveBytes(to_ref, alloc_size);
   }
   if (ReadBarrier::kEnableToSpaceInvariantChecks) {
-    AssertToSpaceInvariantObjectVisitor visitor(this);
-    visitor(to_ref);
+    CHECK(to_ref != nullptr);
+    space::RegionSpace* region_space = RegionSpace();
+    CHECK(!region_space->IsInFromSpace(to_ref)) << "Scanning object " << to_ref << " in from space";
+    AssertToSpaceInvariant(nullptr, MemberOffset(0), to_ref);
+    AssertToSpaceInvariantFieldVisitor visitor(this);
+    to_ref->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>(
+        visitor,
+        visitor);
   }
 }
 
@@ -2287,7 +2253,9 @@
   // Note that from_ref is a from space ref so the SizeOf() call will access the from-space meta
   // objects, but it's ok and necessary.
   size_t obj_size = from_ref->SizeOf<kDefaultVerifyFlags>();
-  size_t region_space_alloc_size = RoundUp(obj_size, space::RegionSpace::kAlignment);
+  size_t region_space_alloc_size = (obj_size <= space::RegionSpace::kRegionSize)
+      ? RoundUp(obj_size, space::RegionSpace::kAlignment)
+      : RoundUp(obj_size, space::RegionSpace::kRegionSize);
   size_t region_space_bytes_allocated = 0U;
   size_t non_moving_space_bytes_allocated = 0U;
   size_t bytes_allocated = 0U;
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index 7b4340e..ab60990 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -181,9 +181,6 @@
   void VerifyGrayImmuneObjects()
       REQUIRES(Locks::mutator_lock_)
       REQUIRES(!mark_stack_lock_);
-  static void VerifyNoMissingCardMarkCallback(mirror::Object* obj, void* arg)
-      REQUIRES(Locks::mutator_lock_)
-      REQUIRES(!mark_stack_lock_);
   void VerifyNoMissingCardMarks()
       REQUIRES(Locks::mutator_lock_)
       REQUIRES(!mark_stack_lock_);
@@ -348,7 +345,6 @@
   class ActivateReadBarrierEntrypointsCallback;
   class ActivateReadBarrierEntrypointsCheckpoint;
   class AssertToSpaceInvariantFieldVisitor;
-  class AssertToSpaceInvariantObjectVisitor;
   class AssertToSpaceInvariantRefsVisitor;
   class ClearBlackPtrsVisitor;
   class ComputeUnevacFromSpaceLiveRatioVisitor;
@@ -365,7 +361,6 @@
   class ThreadFlipVisitor;
   class VerifyGrayImmuneObjectsVisitor;
   class VerifyNoFromSpaceRefsFieldVisitor;
-  class VerifyNoFromSpaceRefsObjectVisitor;
   class VerifyNoFromSpaceRefsVisitor;
   class VerifyNoMissingCardMarkVisitor;
 
diff --git a/runtime/gc/heap-visit-objects-inl.h b/runtime/gc/heap-visit-objects-inl.h
new file mode 100644
index 0000000..b6ccb277
--- /dev/null
+++ b/runtime/gc/heap-visit-objects-inl.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_HEAP_VISIT_OBJECTS_INL_H_
+#define ART_RUNTIME_GC_HEAP_VISIT_OBJECTS_INL_H_
+
+#include "heap.h"
+
+#include "base/mutex-inl.h"
+#include "gc/accounting/heap_bitmap-inl.h"
+#include "gc/space/bump_pointer_space-walk-inl.h"
+#include "gc/space/region_space-inl.h"
+#include "mirror/object-inl.h"
+#include "obj_ptr-inl.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-current-inl.h"
+#include "thread_list.h"
+
+namespace art {
+namespace gc {
+
+// Visit objects when threads aren't suspended. If concurrent moving
+// GC, disable moving GC and suspend threads and then visit objects.
+template <typename Visitor>
+inline void Heap::VisitObjects(Visitor&& visitor) {
+  Thread* self = Thread::Current();
+  Locks::mutator_lock_->AssertSharedHeld(self);
+  DCHECK(!Locks::mutator_lock_->IsExclusiveHeld(self)) << "Call VisitObjectsPaused() instead";
+  if (IsGcConcurrentAndMoving()) {
+    // Concurrent moving GC. Just suspending threads isn't sufficient
+    // because a collection isn't one big pause and we could suspend
+    // threads in the middle (between phases) of a concurrent moving
+    // collection where it's not easily known which objects are alive
+    // (both the region space and the non-moving space) or which
+    // copies of objects to visit, and the to-space invariant could be
+    // easily broken. Visit objects while GC isn't running by using
+    // IncrementDisableMovingGC() and threads are suspended.
+    IncrementDisableMovingGC(self);
+    {
+      ScopedThreadSuspension sts(self, kWaitingForVisitObjects);
+      ScopedSuspendAll ssa(__FUNCTION__);
+      VisitObjectsInternalRegionSpace(visitor);
+      VisitObjectsInternal(visitor);
+    }
+    DecrementDisableMovingGC(self);
+  } else {
+    // Since concurrent moving GC has thread suspension, also poison ObjPtr the normal case to
+    // catch bugs.
+    self->PoisonObjectPointers();
+    // GCs can move objects, so don't allow this.
+    ScopedAssertNoThreadSuspension ants("Visiting objects");
+    DCHECK(region_space_ == nullptr);
+    VisitObjectsInternal(visitor);
+    self->PoisonObjectPointers();
+  }
+}
+
+template <typename Visitor>
+inline void Heap::VisitObjectsPaused(Visitor&& visitor) {
+  Thread* self = Thread::Current();
+  Locks::mutator_lock_->AssertExclusiveHeld(self);
+  VisitObjectsInternalRegionSpace(visitor);
+  VisitObjectsInternal(visitor);
+}
+
+// Visit objects in the region spaces.
+template <typename Visitor>
+inline void Heap::VisitObjectsInternalRegionSpace(Visitor&& visitor) {
+  Thread* self = Thread::Current();
+  Locks::mutator_lock_->AssertExclusiveHeld(self);
+  if (region_space_ != nullptr) {
+    DCHECK(IsGcConcurrentAndMoving());
+    if (!zygote_creation_lock_.IsExclusiveHeld(self)) {
+      // Exclude the pre-zygote fork time where the semi-space collector
+      // calls VerifyHeapReferences() as part of the zygote compaction
+      // which then would call here without the moving GC disabled,
+      // which is fine.
+      bool is_thread_running_gc = false;
+      if (kIsDebugBuild) {
+        MutexLock mu(self, *gc_complete_lock_);
+        is_thread_running_gc = self == thread_running_gc_;
+      }
+      // If we are not the thread running the GC on in a GC exclusive region, then moving GC
+      // must be disabled.
+      DCHECK(is_thread_running_gc || IsMovingGCDisabled(self));
+    }
+    region_space_->Walk(visitor);
+  }
+}
+
+// Visit objects in the other spaces.
+template <typename Visitor>
+inline void Heap::VisitObjectsInternal(Visitor&& visitor) {
+  if (bump_pointer_space_ != nullptr) {
+    // Visit objects in bump pointer space.
+    bump_pointer_space_->Walk(visitor);
+  }
+  // TODO: Switch to standard begin and end to use ranged a based loop.
+  for (auto* it = allocation_stack_->Begin(), *end = allocation_stack_->End(); it < end; ++it) {
+    mirror::Object* const obj = it->AsMirrorPtr();
+
+    mirror::Class* kls = nullptr;
+    if (obj != nullptr && (kls = obj->GetClass()) != nullptr) {
+      // Below invariant is safe regardless of what space the Object is in.
+      // For speed reasons, only perform it when Rosalloc could possibly be used.
+      // (Disabled for read barriers because it never uses Rosalloc).
+      // (See the DCHECK in RosAllocSpace constructor).
+      if (!kUseReadBarrier) {
+        // Rosalloc has a race in allocation. Objects can be written into the allocation
+        // stack before their header writes are visible to this thread.
+        // See b/28790624 for more details.
+        //
+        // obj.class will either be pointing to a valid Class*, or it will point
+        // to a rosalloc free buffer.
+        //
+        // If it's pointing to a valid Class* then that Class's Class will be the
+        // ClassClass (whose Class is itself).
+        //
+        // A rosalloc free buffer will point to another rosalloc free buffer
+        // (or to null), and never to itself.
+        //
+        // Either way dereferencing while its not-null is safe because it will
+        // always point to another valid pointer or to null.
+        mirror::Class* klsClass = kls->GetClass();
+
+        if (klsClass == nullptr) {
+          continue;
+        } else if (klsClass->GetClass() != klsClass) {
+          continue;
+        }
+      } else {
+        // Ensure the invariant is not broken for non-rosalloc cases.
+        DCHECK(Heap::rosalloc_space_ == nullptr)
+            << "unexpected rosalloc with read barriers";
+        DCHECK(kls->GetClass() != nullptr)
+            << "invalid object: class does not have a class";
+        DCHECK_EQ(kls->GetClass()->GetClass(), kls->GetClass())
+            << "invalid object: class's class is not ClassClass";
+      }
+
+      // Avoid the race condition caused by the object not yet being written into the allocation
+      // stack or the class not yet being written in the object. Or, if
+      // kUseThreadLocalAllocationStack, there can be nulls on the allocation stack.
+      visitor(obj);
+    }
+  }
+  {
+    ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
+    GetLiveBitmap()->Visit<Visitor>(visitor);
+  }
+}
+
+}  // namespace gc
+}  // namespace art
+
+#endif  // ART_RUNTIME_GC_HEAP_VISIT_OBJECTS_INL_H_
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index ad4c0d5..6ab9827 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -65,6 +65,7 @@
 #include "gc_pause_listener.h"
 #include "gc_root.h"
 #include "heap-inl.h"
+#include "heap-visit-objects-inl.h"
 #include "image.h"
 #include "intern_table.h"
 #include "java_vm_ext.h"
@@ -905,134 +906,6 @@
   }
 }
 
-// Visit objects when threads aren't suspended. If concurrent moving
-// GC, disable moving GC and suspend threads and then visit objects.
-void Heap::VisitObjects(ObjectCallback callback, void* arg) {
-  Thread* self = Thread::Current();
-  Locks::mutator_lock_->AssertSharedHeld(self);
-  DCHECK(!Locks::mutator_lock_->IsExclusiveHeld(self)) << "Call VisitObjectsPaused() instead";
-  if (IsGcConcurrentAndMoving()) {
-    // Concurrent moving GC. Just suspending threads isn't sufficient
-    // because a collection isn't one big pause and we could suspend
-    // threads in the middle (between phases) of a concurrent moving
-    // collection where it's not easily known which objects are alive
-    // (both the region space and the non-moving space) or which
-    // copies of objects to visit, and the to-space invariant could be
-    // easily broken. Visit objects while GC isn't running by using
-    // IncrementDisableMovingGC() and threads are suspended.
-    IncrementDisableMovingGC(self);
-    {
-      ScopedThreadSuspension sts(self, kWaitingForVisitObjects);
-      ScopedSuspendAll ssa(__FUNCTION__);
-      VisitObjectsInternalRegionSpace(callback, arg);
-      VisitObjectsInternal(callback, arg);
-    }
-    DecrementDisableMovingGC(self);
-  } else {
-    // Since concurrent moving GC has thread suspension, also poison ObjPtr the normal case to
-    // catch bugs.
-    self->PoisonObjectPointers();
-    // GCs can move objects, so don't allow this.
-    ScopedAssertNoThreadSuspension ants("Visiting objects");
-    DCHECK(region_space_ == nullptr);
-    VisitObjectsInternal(callback, arg);
-    self->PoisonObjectPointers();
-  }
-}
-
-// Visit objects when threads are already suspended.
-void Heap::VisitObjectsPaused(ObjectCallback callback, void* arg) {
-  Thread* self = Thread::Current();
-  Locks::mutator_lock_->AssertExclusiveHeld(self);
-  VisitObjectsInternalRegionSpace(callback, arg);
-  VisitObjectsInternal(callback, arg);
-}
-
-// Visit objects in the region spaces.
-void Heap::VisitObjectsInternalRegionSpace(ObjectCallback callback, void* arg) {
-  Thread* self = Thread::Current();
-  Locks::mutator_lock_->AssertExclusiveHeld(self);
-  if (region_space_ != nullptr) {
-    DCHECK(IsGcConcurrentAndMoving());
-    if (!zygote_creation_lock_.IsExclusiveHeld(self)) {
-      // Exclude the pre-zygote fork time where the semi-space collector
-      // calls VerifyHeapReferences() as part of the zygote compaction
-      // which then would call here without the moving GC disabled,
-      // which is fine.
-      bool is_thread_running_gc = false;
-      if (kIsDebugBuild) {
-        MutexLock mu(self, *gc_complete_lock_);
-        is_thread_running_gc = self == thread_running_gc_;
-      }
-      // If we are not the thread running the GC on in a GC exclusive region, then moving GC
-      // must be disabled.
-      DCHECK(is_thread_running_gc || IsMovingGCDisabled(self));
-    }
-    region_space_->Walk(callback, arg);
-  }
-}
-
-// Visit objects in the other spaces.
-void Heap::VisitObjectsInternal(ObjectCallback callback, void* arg) {
-  if (bump_pointer_space_ != nullptr) {
-    // Visit objects in bump pointer space.
-    bump_pointer_space_->Walk(callback, arg);
-  }
-  // TODO: Switch to standard begin and end to use ranged a based loop.
-  for (auto* it = allocation_stack_->Begin(), *end = allocation_stack_->End(); it < end; ++it) {
-    mirror::Object* const obj = it->AsMirrorPtr();
-
-    mirror::Class* kls = nullptr;
-    if (obj != nullptr && (kls = obj->GetClass()) != nullptr) {
-      // Below invariant is safe regardless of what space the Object is in.
-      // For speed reasons, only perform it when Rosalloc could possibly be used.
-      // (Disabled for read barriers because it never uses Rosalloc).
-      // (See the DCHECK in RosAllocSpace constructor).
-      if (!kUseReadBarrier) {
-        // Rosalloc has a race in allocation. Objects can be written into the allocation
-        // stack before their header writes are visible to this thread.
-        // See b/28790624 for more details.
-        //
-        // obj.class will either be pointing to a valid Class*, or it will point
-        // to a rosalloc free buffer.
-        //
-        // If it's pointing to a valid Class* then that Class's Class will be the
-        // ClassClass (whose Class is itself).
-        //
-        // A rosalloc free buffer will point to another rosalloc free buffer
-        // (or to null), and never to itself.
-        //
-        // Either way dereferencing while its not-null is safe because it will
-        // always point to another valid pointer or to null.
-        mirror::Class* klsClass = kls->GetClass();
-
-        if (klsClass == nullptr) {
-          continue;
-        } else if (klsClass->GetClass() != klsClass) {
-          continue;
-        }
-      } else {
-        // Ensure the invariant is not broken for non-rosalloc cases.
-        DCHECK(Heap::rosalloc_space_ == nullptr)
-            << "unexpected rosalloc with read barriers";
-        DCHECK(kls->GetClass() != nullptr)
-            << "invalid object: class does not have a class";
-        DCHECK_EQ(kls->GetClass()->GetClass(), kls->GetClass())
-            << "invalid object: class's class is not ClassClass";
-      }
-
-      // Avoid the race condition caused by the object not yet being written into the allocation
-      // stack or the class not yet being written in the object. Or, if
-      // kUseThreadLocalAllocationStack, there can be nulls on the allocation stack.
-      callback(obj, arg);
-    }
-  }
-  {
-    ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
-    GetLiveBitmap()->Walk(callback, arg);
-  }
-}
-
 void Heap::MarkAllocStackAsLive(accounting::ObjectStack* stack) {
   space::ContinuousSpace* space1 = main_space_ != nullptr ? main_space_ : non_moving_space_;
   space::ContinuousSpace* space2 = non_moving_space_;
@@ -1639,13 +1512,17 @@
   }
 }
 
-void Heap::VerificationCallback(mirror::Object* obj, void* arg) {
-  reinterpret_cast<Heap*>(arg)->VerifyObjectBody(obj);
-}
-
 void Heap::VerifyHeap() {
   ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
-  GetLiveBitmap()->Walk(Heap::VerificationCallback, this);
+  auto visitor = [&](mirror::Object* obj) {
+    VerifyObjectBody(obj);
+  };
+  // Technically we need the mutator lock here to call Visit. However, VerifyObjectBody is already
+  // NO_THREAD_SAFETY_ANALYSIS.
+  auto no_thread_safety_analysis = [&]() NO_THREAD_SAFETY_ANALYSIS {
+    GetLiveBitmap()->Visit(visitor);
+  };
+  no_thread_safety_analysis();
 }
 
 void Heap::RecordFree(uint64_t freed_objects, int64_t freed_bytes) {
@@ -1918,138 +1795,84 @@
   return GetBytesFreedEver() + GetBytesAllocated();
 }
 
-class InstanceCounter {
- public:
-  InstanceCounter(const std::vector<Handle<mirror::Class>>& classes,
-                  bool use_is_assignable_from,
-                  uint64_t* counts)
-      REQUIRES_SHARED(Locks::mutator_lock_)
-      : classes_(classes), use_is_assignable_from_(use_is_assignable_from), counts_(counts) {}
-
-  static void Callback(mirror::Object* obj, void* arg)
-      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
-    InstanceCounter* instance_counter = reinterpret_cast<InstanceCounter*>(arg);
-    mirror::Class* instance_class = obj->GetClass();
-    CHECK(instance_class != nullptr);
-    for (size_t i = 0; i < instance_counter->classes_.size(); ++i) {
-      ObjPtr<mirror::Class> klass = instance_counter->classes_[i].Get();
-      if (instance_counter->use_is_assignable_from_) {
-        if (klass != nullptr && klass->IsAssignableFrom(instance_class)) {
-          ++instance_counter->counts_[i];
-        }
-      } else if (instance_class == klass) {
-        ++instance_counter->counts_[i];
-      }
-    }
-  }
-
- private:
-  const std::vector<Handle<mirror::Class>>& classes_;
-  bool use_is_assignable_from_;
-  uint64_t* const counts_;
-  DISALLOW_COPY_AND_ASSIGN(InstanceCounter);
-};
-
 void Heap::CountInstances(const std::vector<Handle<mirror::Class>>& classes,
                           bool use_is_assignable_from,
                           uint64_t* counts) {
-  InstanceCounter counter(classes, use_is_assignable_from, counts);
-  VisitObjects(InstanceCounter::Callback, &counter);
-}
-
-class InstanceCollector {
- public:
-  InstanceCollector(VariableSizedHandleScope& scope,
-                    Handle<mirror::Class> c,
-                    int32_t max_count,
-                    std::vector<Handle<mirror::Object>>& instances)
-      REQUIRES_SHARED(Locks::mutator_lock_)
-      : scope_(scope),
-        class_(c),
-        max_count_(max_count),
-        instances_(instances) {}
-
-  static void Callback(mirror::Object* obj, void* arg)
-      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
-    DCHECK(arg != nullptr);
-    InstanceCollector* instance_collector = reinterpret_cast<InstanceCollector*>(arg);
-    if (obj->GetClass() == instance_collector->class_.Get()) {
-      if (instance_collector->max_count_ == 0 ||
-          instance_collector->instances_.size() < instance_collector->max_count_) {
-        instance_collector->instances_.push_back(instance_collector->scope_.NewHandle(obj));
+  auto instance_counter = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+    mirror::Class* instance_class = obj->GetClass();
+    CHECK(instance_class != nullptr);
+    for (size_t i = 0; i < classes.size(); ++i) {
+      ObjPtr<mirror::Class> klass = classes[i].Get();
+      if (use_is_assignable_from) {
+        if (klass != nullptr && klass->IsAssignableFrom(instance_class)) {
+          ++counts[i];
+        }
+      } else if (instance_class == klass) {
+        ++counts[i];
       }
     }
-  }
-
- private:
-  VariableSizedHandleScope& scope_;
-  Handle<mirror::Class> const class_;
-  const uint32_t max_count_;
-  std::vector<Handle<mirror::Object>>& instances_;
-  DISALLOW_COPY_AND_ASSIGN(InstanceCollector);
-};
-
-void Heap::GetInstances(VariableSizedHandleScope& scope,
-                        Handle<mirror::Class> c,
-                        int32_t max_count,
-                        std::vector<Handle<mirror::Object>>& instances) {
-  InstanceCollector collector(scope, c, max_count, instances);
-  VisitObjects(&InstanceCollector::Callback, &collector);
+  };
+  VisitObjects(instance_counter);
 }
 
-class ReferringObjectsFinder {
- public:
-  ReferringObjectsFinder(VariableSizedHandleScope& scope,
-                         Handle<mirror::Object> object,
-                         int32_t max_count,
-                         std::vector<Handle<mirror::Object>>& referring_objects)
-      REQUIRES_SHARED(Locks::mutator_lock_)
-      : scope_(scope),
-        object_(object),
-        max_count_(max_count),
-        referring_objects_(referring_objects) {}
-
-  static void Callback(mirror::Object* obj, void* arg)
-      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
-    reinterpret_cast<ReferringObjectsFinder*>(arg)->operator()(obj);
-  }
-
-  // For bitmap Visit.
-  // TODO: Fix lock analysis to not use NO_THREAD_SAFETY_ANALYSIS, requires support for
-  // annotalysis on visitors.
-  void operator()(ObjPtr<mirror::Object> o) const NO_THREAD_SAFETY_ANALYSIS {
-    o->VisitReferences(*this, VoidFunctor());
-  }
-
-  // For Object::VisitReferences.
-  void operator()(ObjPtr<mirror::Object> obj,
-                  MemberOffset offset,
-                  bool is_static ATTRIBUTE_UNUSED) const
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    mirror::Object* ref = obj->GetFieldObject<mirror::Object>(offset);
-    if (ref == object_.Get() && (max_count_ == 0 || referring_objects_.size() < max_count_)) {
-      referring_objects_.push_back(scope_.NewHandle(obj));
+void Heap::GetInstances(VariableSizedHandleScope& scope,
+                        Handle<mirror::Class> h_class,
+                        int32_t max_count,
+                        std::vector<Handle<mirror::Object>>& instances) {
+  DCHECK_GE(max_count, 0);
+  auto instance_collector = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (obj->GetClass() == h_class.Get()) {
+      if (max_count == 0 || instances.size() < static_cast<size_t>(max_count)) {
+        instances.push_back(scope.NewHandle(obj));
+      }
     }
-  }
-
-  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED)
-      const {}
-  void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
-
- private:
-  VariableSizedHandleScope& scope_;
-  Handle<mirror::Object> const object_;
-  const uint32_t max_count_;
-  std::vector<Handle<mirror::Object>>& referring_objects_;
-  DISALLOW_COPY_AND_ASSIGN(ReferringObjectsFinder);
-};
+  };
+  VisitObjects(instance_collector);
+}
 
 void Heap::GetReferringObjects(VariableSizedHandleScope& scope,
                                Handle<mirror::Object> o,
                                int32_t max_count,
                                std::vector<Handle<mirror::Object>>& referring_objects) {
+  class ReferringObjectsFinder {
+   public:
+    ReferringObjectsFinder(VariableSizedHandleScope& scope_in,
+                           Handle<mirror::Object> object_in,
+                           int32_t max_count_in,
+                           std::vector<Handle<mirror::Object>>& referring_objects_in)
+        REQUIRES_SHARED(Locks::mutator_lock_)
+        : scope_(scope_in),
+          object_(object_in),
+          max_count_(max_count_in),
+          referring_objects_(referring_objects_in) {}
+
+    // For Object::VisitReferences.
+    void operator()(ObjPtr<mirror::Object> obj,
+                    MemberOffset offset,
+                    bool is_static ATTRIBUTE_UNUSED) const
+        REQUIRES_SHARED(Locks::mutator_lock_) {
+      mirror::Object* ref = obj->GetFieldObject<mirror::Object>(offset);
+      if (ref == object_.Get() && (max_count_ == 0 || referring_objects_.size() < max_count_)) {
+        referring_objects_.push_back(scope_.NewHandle(obj));
+      }
+    }
+
+    void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED)
+        const {}
+    void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
+
+   private:
+    VariableSizedHandleScope& scope_;
+    Handle<mirror::Object> const object_;
+    const uint32_t max_count_;
+    std::vector<Handle<mirror::Object>>& referring_objects_;
+    DISALLOW_COPY_AND_ASSIGN(ReferringObjectsFinder);
+  };
   ReferringObjectsFinder finder(scope, o, max_count, referring_objects);
-  VisitObjects(&ReferringObjectsFinder::Callback, &finder);
+  auto referring_objects_finder = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+    obj->VisitReferences(finder, VoidFunctor());
+  };
+  VisitObjects(referring_objects_finder);
 }
 
 void Heap::CollectGarbage(bool clear_soft_references) {
@@ -2357,24 +2180,25 @@
         bin_mark_bitmap_(nullptr),
         is_running_on_memory_tool_(is_running_on_memory_tool) {}
 
-  void BuildBins(space::ContinuousSpace* space) {
+  void BuildBins(space::ContinuousSpace* space) REQUIRES_SHARED(Locks::mutator_lock_) {
     bin_live_bitmap_ = space->GetLiveBitmap();
     bin_mark_bitmap_ = space->GetMarkBitmap();
-    BinContext context;
-    context.prev_ = reinterpret_cast<uintptr_t>(space->Begin());
-    context.collector_ = this;
+    uintptr_t prev = reinterpret_cast<uintptr_t>(space->Begin());
     WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
     // Note: This requires traversing the space in increasing order of object addresses.
-    bin_live_bitmap_->Walk(Callback, reinterpret_cast<void*>(&context));
+    auto visitor = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+      uintptr_t object_addr = reinterpret_cast<uintptr_t>(obj);
+      size_t bin_size = object_addr - prev;
+      // Add the bin consisting of the end of the previous object to the start of the current object.
+      AddBin(bin_size, prev);
+      prev = object_addr + RoundUp(obj->SizeOf<kDefaultVerifyFlags>(), kObjectAlignment);
+    };
+    bin_live_bitmap_->Walk(visitor);
     // Add the last bin which spans after the last object to the end of the space.
-    AddBin(reinterpret_cast<uintptr_t>(space->End()) - context.prev_, context.prev_);
+    AddBin(reinterpret_cast<uintptr_t>(space->End()) - prev, prev);
   }
 
  private:
-  struct BinContext {
-    uintptr_t prev_;  // The end of the previous object.
-    ZygoteCompactingCollector* collector_;
-  };
   // Maps from bin sizes to locations.
   std::multimap<size_t, uintptr_t> bins_;
   // Live bitmap of the space which contains the bins.
@@ -2383,18 +2207,6 @@
   accounting::ContinuousSpaceBitmap* bin_mark_bitmap_;
   const bool is_running_on_memory_tool_;
 
-  static void Callback(mirror::Object* obj, void* arg)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(arg != nullptr);
-    BinContext* context = reinterpret_cast<BinContext*>(arg);
-    ZygoteCompactingCollector* collector = context->collector_;
-    uintptr_t object_addr = reinterpret_cast<uintptr_t>(obj);
-    size_t bin_size = object_addr - context->prev_;
-    // Add the bin consisting of the end of the previous object to the start of the current object.
-    collector->AddBin(bin_size, context->prev_);
-    context->prev_ = object_addr + RoundUp(obj->SizeOf<kDefaultVerifyFlags>(), kObjectAlignment);
-  }
-
   void AddBin(size_t size, uintptr_t position) {
     if (is_running_on_memory_tool_) {
       MEMORY_TOOL_MAKE_DEFINED(reinterpret_cast<void*>(position), size);
@@ -2935,7 +2747,7 @@
 class VerifyReferenceVisitor : public SingleRootVisitor {
  public:
   VerifyReferenceVisitor(Heap* heap, Atomic<size_t>* fail_count, bool verify_referent)
-      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_)
+      REQUIRES_SHARED(Locks::mutator_lock_)
       : heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {}
 
   size_t GetFailureCount() const {
@@ -3089,8 +2901,7 @@
   VerifyObjectVisitor(Heap* heap, Atomic<size_t>* fail_count, bool verify_referent)
       : heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {}
 
-  void operator()(mirror::Object* obj)
-      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
+  void operator()(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
     // Note: we are verifying the references in obj but not obj itself, this is because obj must
     // be live or else how did we find it in the live bitmap?
     VerifyReferenceVisitor visitor(heap_, fail_count_, verify_referent_);
@@ -3098,12 +2909,6 @@
     obj->VisitReferences(visitor, visitor);
   }
 
-  static void VisitCallback(mirror::Object* obj, void* arg)
-      REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
-    VerifyObjectVisitor* visitor = reinterpret_cast<VerifyObjectVisitor*>(arg);
-    visitor->operator()(obj);
-  }
-
   void VerifyRoots() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::heap_bitmap_lock_) {
     ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
     VerifyReferenceVisitor visitor(heap_, fail_count_, verify_referent_);
@@ -3175,7 +2980,7 @@
   // 2. Allocated during the GC (pre sweep GC verification).
   // We don't want to verify the objects in the live stack since they themselves may be
   // pointing to dead objects if they are not reachable.
-  VisitObjectsPaused(VerifyObjectVisitor::VisitCallback, &visitor);
+  VisitObjectsPaused(visitor);
   // Verify the roots:
   visitor.VerifyRoots();
   if (visitor.GetFailureCount() > 0) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 9e55081..e172d2d 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -25,6 +25,7 @@
 #include "allocator_type.h"
 #include "arch/instruction_set.h"
 #include "atomic.h"
+#include "base/mutex.h"
 #include "base/time_utils.h"
 #include "gc/gc_cause.h"
 #include "gc/collector/gc_type.h"
@@ -51,9 +52,6 @@
 class TimingLogger;
 class VariableSizedHandleScope;
 
-// Same as in object_callbacks.h. Just avoid the include.
-typedef void (ObjectCallback)(mirror::Object* obj, void* arg);
-
 namespace mirror {
   class Class;
   class Object;
@@ -250,10 +248,12 @@
   }
 
   // Visit all of the live objects in the heap.
-  void VisitObjects(ObjectCallback callback, void* arg)
+  template <typename Visitor>
+  ALWAYS_INLINE void VisitObjects(Visitor&& visitor)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_);
-  void VisitObjectsPaused(ObjectCallback callback, void* arg)
+  template <typename Visitor>
+  ALWAYS_INLINE void VisitObjectsPaused(Visitor&& visitor)
       REQUIRES(Locks::mutator_lock_, !Locks::heap_bitmap_lock_, !*gc_complete_lock_);
 
   void CheckPreconditionsForAllocObject(ObjPtr<mirror::Class> c, size_t byte_count)
@@ -1007,9 +1007,6 @@
 
   size_t GetPercentFree();
 
-  static void VerificationCallback(mirror::Object* obj, void* arg)
-      REQUIRES_SHARED(Locks::heap_bitmap_lock_);
-
   // Swap the allocation stack with the live stack.
   void SwapStacks() REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -1051,10 +1048,12 @@
   // Trim 0 pages at the end of reference tables.
   void TrimIndirectReferenceTables(Thread* self);
 
-  void VisitObjectsInternal(ObjectCallback callback, void* arg)
+  template <typename Visitor>
+  ALWAYS_INLINE void VisitObjectsInternal(Visitor&& visitor)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_);
-  void VisitObjectsInternalRegionSpace(ObjectCallback callback, void* arg)
+  template <typename Visitor>
+  ALWAYS_INLINE void VisitObjectsInternalRegionSpace(Visitor&& visitor)
       REQUIRES(Locks::mutator_lock_, !Locks::heap_bitmap_lock_, !*gc_complete_lock_);
 
   void UpdateGcCountRateHistograms() REQUIRES(gc_complete_lock_);
diff --git a/runtime/gc/space/bump_pointer_space-walk-inl.h b/runtime/gc/space/bump_pointer_space-walk-inl.h
new file mode 100644
index 0000000..5d05ea2
--- /dev/null
+++ b/runtime/gc/space/bump_pointer_space-walk-inl.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_WALK_INL_H_
+#define ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_WALK_INL_H_
+
+#include "bump_pointer_space.h"
+
+#include "base/bit_utils.h"
+#include "mirror/object-inl.h"
+#include "thread-current-inl.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+template <typename Visitor>
+inline void BumpPointerSpace::Walk(Visitor&& visitor) {
+  uint8_t* pos = Begin();
+  uint8_t* end = End();
+  uint8_t* main_end = pos;
+  // Internal indirection w/ NO_THREAD_SAFETY_ANALYSIS. Optimally, we'd like to have an annotation
+  // like
+  //   REQUIRES_AS(visitor.operator(mirror::Object*))
+  // on Walk to expose the interprocedural nature of locks here without having to duplicate the
+  // function.
+  //
+  // NO_THREAD_SAFETY_ANALYSIS is a workaround. The problem with the workaround of course is that
+  // it doesn't complain at the callsite. However, that is strictly not worse than the
+  // ObjectCallback version it replaces.
+  auto no_thread_safety_analysis_visit = [&](mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS {
+    visitor(obj);
+  };
+
+  {
+    MutexLock mu(Thread::Current(), block_lock_);
+    // If we have 0 blocks then we need to update the main header since we have bump pointer style
+    // allocation into an unbounded region (actually bounded by Capacity()).
+    if (num_blocks_ == 0) {
+      UpdateMainBlock();
+    }
+    main_end = Begin() + main_block_size_;
+    if (num_blocks_ == 0) {
+      // We don't have any other blocks, this means someone else may be allocating into the main
+      // block. In this case, we don't want to try and visit the other blocks after the main block
+      // since these could actually be part of the main block.
+      end = main_end;
+    }
+  }
+  // Walk all of the objects in the main block first.
+  while (pos < main_end) {
+    mirror::Object* obj = reinterpret_cast<mirror::Object*>(pos);
+    // No read barrier because obj may not be a valid object.
+    if (obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() == nullptr) {
+      // There is a race condition where a thread has just allocated an object but not set the
+      // class. We can't know the size of this object, so we don't visit it and exit the function
+      // since there is guaranteed to be not other blocks.
+      return;
+    } else {
+      no_thread_safety_analysis_visit(obj);
+      pos = reinterpret_cast<uint8_t*>(GetNextObject(obj));
+    }
+  }
+  // Walk the other blocks (currently only TLABs).
+  while (pos < end) {
+    BlockHeader* header = reinterpret_cast<BlockHeader*>(pos);
+    size_t block_size = header->size_;
+    pos += sizeof(BlockHeader);  // Skip the header so that we know where the objects
+    mirror::Object* obj = reinterpret_cast<mirror::Object*>(pos);
+    const mirror::Object* end_obj = reinterpret_cast<const mirror::Object*>(pos + block_size);
+    CHECK_LE(reinterpret_cast<const uint8_t*>(end_obj), End());
+    // We don't know how many objects are allocated in the current block. When we hit a null class
+    // assume its the end. TODO: Have a thread update the header when it flushes the block?
+    // No read barrier because obj may not be a valid object.
+    while (obj < end_obj && obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) {
+      no_thread_safety_analysis_visit(obj);
+      obj = GetNextObject(obj);
+    }
+    pos += block_size;
+  }
+}
+
+}  // namespace space
+}  // namespace gc
+}  // namespace art
+
+#endif  // ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_WALK_INL_H_
diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc
index bb1ede1..5d91f4b 100644
--- a/runtime/gc/space/bump_pointer_space.cc
+++ b/runtime/gc/space/bump_pointer_space.cc
@@ -153,58 +153,6 @@
   return storage;
 }
 
-void BumpPointerSpace::Walk(ObjectCallback* callback, void* arg) {
-  uint8_t* pos = Begin();
-  uint8_t* end = End();
-  uint8_t* main_end = pos;
-  {
-    MutexLock mu(Thread::Current(), block_lock_);
-    // If we have 0 blocks then we need to update the main header since we have bump pointer style
-    // allocation into an unbounded region (actually bounded by Capacity()).
-    if (num_blocks_ == 0) {
-      UpdateMainBlock();
-    }
-    main_end = Begin() + main_block_size_;
-    if (num_blocks_ == 0) {
-      // We don't have any other blocks, this means someone else may be allocating into the main
-      // block. In this case, we don't want to try and visit the other blocks after the main block
-      // since these could actually be part of the main block.
-      end = main_end;
-    }
-  }
-  // Walk all of the objects in the main block first.
-  while (pos < main_end) {
-    mirror::Object* obj = reinterpret_cast<mirror::Object*>(pos);
-    // No read barrier because obj may not be a valid object.
-    if (obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() == nullptr) {
-      // There is a race condition where a thread has just allocated an object but not set the
-      // class. We can't know the size of this object, so we don't visit it and exit the function
-      // since there is guaranteed to be not other blocks.
-      return;
-    } else {
-      callback(obj, arg);
-      pos = reinterpret_cast<uint8_t*>(GetNextObject(obj));
-    }
-  }
-  // Walk the other blocks (currently only TLABs).
-  while (pos < end) {
-    BlockHeader* header = reinterpret_cast<BlockHeader*>(pos);
-    size_t block_size = header->size_;
-    pos += sizeof(BlockHeader);  // Skip the header so that we know where the objects
-    mirror::Object* obj = reinterpret_cast<mirror::Object*>(pos);
-    const mirror::Object* end_obj = reinterpret_cast<const mirror::Object*>(pos + block_size);
-    CHECK_LE(reinterpret_cast<const uint8_t*>(end_obj), End());
-    // We don't know how many objects are allocated in the current block. When we hit a null class
-    // assume its the end. TODO: Have a thread update the header when it flushes the block?
-    // No read barrier because obj may not be a valid object.
-    while (obj < end_obj && obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) {
-      callback(obj, arg);
-      obj = GetNextObject(obj);
-    }
-    pos += block_size;
-  }
-}
-
 accounting::ContinuousSpaceBitmap::SweepCallback* BumpPointerSpace::GetSweepCallback() {
   UNIMPLEMENTED(FATAL);
   UNREACHABLE();
diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h
index 566dc5d..4197d0c 100644
--- a/runtime/gc/space/bump_pointer_space.h
+++ b/runtime/gc/space/bump_pointer_space.h
@@ -25,9 +25,6 @@
 class Object;
 }
 
-// Same as in object_callbacks.h. Just avoid the include.
-typedef void (ObjectCallback)(mirror::Object* obj, void* arg);
-
 namespace gc {
 
 namespace collector {
@@ -149,8 +146,10 @@
   }
 
   // Go through all of the blocks and visit the continuous objects.
-  void Walk(ObjectCallback* callback, void* arg)
-      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!block_lock_);
+  template <typename Visitor>
+  ALWAYS_INLINE void Walk(Visitor&& visitor)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!block_lock_);
 
   accounting::ContinuousSpaceBitmap::SweepCallback* GetSweepCallback() OVERRIDE;
 
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 1bf9285..3ae382e 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1008,6 +1008,20 @@
         }
       }
 
+      if (obj->IsClass()) {
+        mirror::Class* klass = obj->AsClass<kVerifyNone, kWithoutReadBarrier>();
+        // Fixup super class before visiting instance fields which require
+        // information from their super class to calculate offsets.
+        mirror::Class* super_class = klass->GetSuperClass<kVerifyNone, kWithoutReadBarrier>();
+        if (super_class != nullptr) {
+          mirror::Class* new_super_class = down_cast<mirror::Class*>(ForwardObject(super_class));
+          if (new_super_class != super_class && IsInAppImage(new_super_class)) {
+            // Recursively fix all dependencies.
+            operator()(new_super_class);
+          }
+        }
+      }
+
       obj->VisitReferences</*visit native roots*/false, kVerifyNone, kWithoutReadBarrier>(
           *this,
           *this);
diff --git a/runtime/gc/space/region_space-inl.h b/runtime/gc/space/region_space-inl.h
index 2e67f34..a3b53b4 100644
--- a/runtime/gc/space/region_space-inl.h
+++ b/runtime/gc/space/region_space-inl.h
@@ -184,8 +184,8 @@
   return bytes;
 }
 
-template<bool kToSpaceOnly>
-void RegionSpace::WalkInternal(ObjectCallback* callback, void* arg) {
+template<bool kToSpaceOnly, typename Visitor>
+void RegionSpace::WalkInternal(Visitor&& visitor) {
   // TODO: MutexLock on region_lock_ won't work due to lock order
   // issues (the classloader classes lock and the monitor lock). We
   // call this with threads suspended.
@@ -201,7 +201,7 @@
       DCHECK_GT(r->LiveBytes(), 0u) << "Visiting dead large object";
       mirror::Object* obj = reinterpret_cast<mirror::Object*>(r->Begin());
       DCHECK(obj->GetClass() != nullptr);
-      callback(obj, arg);
+      visitor(obj);
     } else if (r->IsLargeTail()) {
       // Do nothing.
     } else {
@@ -215,14 +215,12 @@
         GetLiveBitmap()->VisitMarkedRange(
             reinterpret_cast<uintptr_t>(pos),
             reinterpret_cast<uintptr_t>(top),
-            [callback, arg](mirror::Object* obj) {
-          callback(obj, arg);
-        });
+            visitor);
       } else {
         while (pos < top) {
           mirror::Object* obj = reinterpret_cast<mirror::Object*>(pos);
           if (obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) {
-            callback(obj, arg);
+            visitor(obj);
             pos = reinterpret_cast<uint8_t*>(GetNextObject(obj));
           } else {
             break;
@@ -277,18 +275,21 @@
       DCHECK(first_reg->IsFree());
       first_reg->UnfreeLarge(this, time_);
       ++num_non_free_regions_;
-      first_reg->SetTop(first_reg->Begin() + num_bytes);
+      size_t allocated = num_regs * kRegionSize;
+      // We make 'top' all usable bytes, as the caller of this
+      // allocation may use all of 'usable_size' (see mirror::Array::Alloc).
+      first_reg->SetTop(first_reg->Begin() + allocated);
       for (size_t p = left + 1; p < right; ++p) {
         DCHECK_LT(p, num_regions_);
         DCHECK(regions_[p].IsFree());
         regions_[p].UnfreeLargeTail(this, time_);
         ++num_non_free_regions_;
       }
-      *bytes_allocated = num_bytes;
+      *bytes_allocated = allocated;
       if (usable_size != nullptr) {
-        *usable_size = num_regs * kRegionSize;
+        *usable_size = allocated;
       }
-      *bytes_tl_bulk_allocated = num_bytes;
+      *bytes_tl_bulk_allocated = allocated;
       return reinterpret_cast<mirror::Object*>(first_reg->Begin());
     } else {
       // right points to the non-free region. Start with the one after it.
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
index b8f1e8f..fe3c1c0 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -290,6 +290,7 @@
       clear_region(r);
     } else if (r->IsInUnevacFromSpace()) {
       if (r->LiveBytes() == 0) {
+        DCHECK(!r->IsLargeTail());
         // Special case for 0 live bytes, this means all of the objects in the region are dead and
         // we can clear it. This is important for large objects since we must not visit dead ones in
         // RegionSpace::Walk because they may contain dangling references to invalid objects.
@@ -312,28 +313,29 @@
             reinterpret_cast<mirror::Object*>(r->Begin() + free_regions * kRegionSize));
         continue;
       }
-      size_t full_count = 0;
-      while (r->IsInUnevacFromSpace()) {
-        Region* const cur = &regions_[i + full_count];
-        if (i + full_count >= num_regions_ ||
-            cur->LiveBytes() != static_cast<size_t>(cur->Top() - cur->Begin())) {
-          break;
-        }
-        DCHECK(cur->IsInUnevacFromSpace());
-        if (full_count != 0) {
-          cur->SetUnevacFromSpaceAsToSpace();
-        }
-        ++full_count;
-      }
-      // Note that r is the full_count == 0 iteration since it is not handled by the loop.
       r->SetUnevacFromSpaceAsToSpace();
-      if (full_count >= 1) {
+      if (r->AllAllocatedBytesAreLive()) {
+        // Try to optimize the number of ClearRange calls by checking whether the next regions
+        // can also be cleared.
+        size_t regions_to_clear_bitmap = 1;
+        while (i + regions_to_clear_bitmap < num_regions_) {
+          Region* const cur = &regions_[i + regions_to_clear_bitmap];
+          if (!cur->AllAllocatedBytesAreLive()) {
+            DCHECK(!cur->IsLargeTail());
+            break;
+          }
+          CHECK(cur->IsInUnevacFromSpace());
+          cur->SetUnevacFromSpaceAsToSpace();
+          ++regions_to_clear_bitmap;
+        }
+
         GetLiveBitmap()->ClearRange(
             reinterpret_cast<mirror::Object*>(r->Begin()),
-            reinterpret_cast<mirror::Object*>(r->Begin() + full_count * kRegionSize));
-        // Skip over extra regions we cleared.
+            reinterpret_cast<mirror::Object*>(r->Begin() + regions_to_clear_bitmap * kRegionSize));
+        // Skip over extra regions we cleared the bitmaps: we don't need to clear them, as they
+        // are unevac region sthat are live.
         // Subtract one for the for loop.
-        i += full_count - 1;
+        i += regions_to_clear_bitmap - 1;
       }
     }
     // Note r != last_checked_region if r->IsInUnevacFromSpace() was true above.
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
index 8907b07..77d76fb 100644
--- a/runtime/gc/space/region_space.h
+++ b/runtime/gc/space/region_space.h
@@ -17,7 +17,8 @@
 #ifndef ART_RUNTIME_GC_SPACE_REGION_SPACE_H_
 #define ART_RUNTIME_GC_SPACE_REGION_SPACE_H_
 
-#include "object_callbacks.h"
+#include "base/macros.h"
+#include "base/mutex.h"
 #include "space.h"
 #include "thread.h"
 
@@ -152,14 +153,14 @@
   }
 
   // Go through all of the blocks and visit the continuous objects.
-  void Walk(ObjectCallback* callback, void* arg)
-      REQUIRES(Locks::mutator_lock_) {
-    WalkInternal<false>(callback, arg);
+  template <typename Visitor>
+  ALWAYS_INLINE void Walk(Visitor&& visitor) REQUIRES(Locks::mutator_lock_) {
+    WalkInternal<false /* kToSpaceOnly */>(visitor);
   }
-
-  void WalkToSpace(ObjectCallback* callback, void* arg)
+  template <typename Visitor>
+  ALWAYS_INLINE void WalkToSpace(Visitor&& visitor)
       REQUIRES(Locks::mutator_lock_) {
-    WalkInternal<true>(callback, arg);
+    WalkInternal<true>(visitor);
   }
 
   accounting::ContinuousSpaceBitmap::SweepCallback* GetSweepCallback() OVERRIDE {
@@ -247,8 +248,8 @@
  private:
   RegionSpace(const std::string& name, MemMap* mem_map);
 
-  template<bool kToSpaceOnly>
-  void WalkInternal(ObjectCallback* callback, void* arg) NO_THREAD_SAFETY_ANALYSIS;
+  template<bool kToSpaceOnly, typename Visitor>
+  ALWAYS_INLINE void WalkInternal(Visitor&& visitor) NO_THREAD_SAFETY_ANALYSIS;
 
   class Region {
    public:
@@ -387,10 +388,16 @@
       DCHECK(IsInUnevacFromSpace());
       DCHECK(!IsLargeTail());
       DCHECK_NE(live_bytes_, static_cast<size_t>(-1));
-      live_bytes_ += live_bytes;
+      // For large allocations, we always consider all bytes in the
+      // regions live.
+      live_bytes_ += IsLarge() ? Top() - begin_ : live_bytes;
       DCHECK_LE(live_bytes_, BytesAllocated());
     }
 
+    bool AllAllocatedBytesAreLive() const {
+      return LiveBytes() == static_cast<size_t>(Top() - Begin());
+    }
+
     size_t LiveBytes() const {
       return live_bytes_;
     }
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index 3a7f21d..06e4704 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -48,7 +48,7 @@
 DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_OBJECT_CLASS_OFFSET), (static_cast<int32_t>(art::mirror::Object:: ClassOffset().Int32Value())))
 #define MIRROR_OBJECT_LOCK_WORD_OFFSET 4
 DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_OBJECT_LOCK_WORD_OFFSET), (static_cast<int32_t>(art::mirror::Object:: MonitorOffset().Int32Value())))
-#define MIRROR_CLASS_STATUS_INITIALIZED 0xa
+#define MIRROR_CLASS_STATUS_INITIALIZED 0xb
 DEFINE_CHECK_EQ(static_cast<uint32_t>(MIRROR_CLASS_STATUS_INITIALIZED), (static_cast<uint32_t>((art::mirror::Class::kStatusInitialized))))
 #define ACCESS_FLAGS_CLASS_IS_FINALIZABLE 0x80000000
 DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_FINALIZABLE), (static_cast<uint32_t>((art::kAccClassIsFinalizable))))
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index ec860c7..f428bc2 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -52,6 +52,7 @@
 #include "gc/allocation_record.h"
 #include "gc/scoped_gc_critical_section.h"
 #include "gc/heap.h"
+#include "gc/heap-visit-objects-inl.h"
 #include "gc/space/space.h"
 #include "globals.h"
 #include "jdwp/jdwp.h"
@@ -485,13 +486,6 @@
   }
 
  private:
-  static void VisitObjectCallback(mirror::Object* obj, void* arg)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(obj != nullptr);
-    DCHECK(arg != nullptr);
-    reinterpret_cast<Hprof*>(arg)->DumpHeapObject(obj);
-  }
-
   void DumpHeapObject(mirror::Object* obj)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -534,8 +528,11 @@
     simple_roots_.clear();
     runtime->VisitRoots(this);
     runtime->VisitImageRoots(this);
-    runtime->GetHeap()->VisitObjectsPaused(VisitObjectCallback, this);
-
+    auto dump_object = [this](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+      DCHECK(obj != nullptr);
+      DumpHeapObject(obj);
+    };
+    runtime->GetHeap()->VisitObjectsPaused(dump_object);
     output_->StartNewRecord(HPROF_TAG_HEAP_DUMP_END, kHprofTime);
     output_->EndRecord();
   }
diff --git a/runtime/image.cc b/runtime/image.cc
index 489a53b..ac36d7c 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -26,7 +26,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '4', '4', '\0' };  // Thread.interrupted
+const uint8_t ImageHeader::kImageVersion[] = { '0', '4', '5', '\0' };  // Fix DexCache fields.
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index b57e2b2..0687b75 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -951,13 +951,20 @@
   // Test whether to use the interpreter or compiler entrypoint, and save that result to pass to
   // PerformCall. A deoptimization could occur at any time, and we shouldn't change which
   // entrypoint to use once we start building the shadow frame.
-  bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint(
-      called_method, called_method->GetEntryPointFromQuickCompiledCode());
+
+  // For unstarted runtimes, always use the interpreter entrypoint. This fixes the case where we are
+  // doing cross compilation. Note that GetEntryPointFromQuickCompiledCode doesn't use the image
+  // pointer size here and this may case an overflow if it is called from the compiler. b/62402160
+  const bool use_interpreter_entrypoint = !Runtime::Current()->IsStarted() ||
+      ClassLinker::ShouldUseInterpreterEntrypoint(
+          called_method,
+          called_method->GetEntryPointFromQuickCompiledCode());
   if (LIKELY(code_item != nullptr)) {
     // When transitioning to compiled code, space only needs to be reserved for the input registers.
     // The rest of the frame gets discarded. This also prevents accessing the called method's code
     // item, saving memory by keeping code items of compiled code untouched.
-    if (Runtime::Current()->IsStarted() && !use_interpreter_entrypoint) {
+    if (!use_interpreter_entrypoint) {
+      DCHECK(!Runtime::Current()->IsAotCompiler()) << "Compiler should use interpreter entrypoint";
       num_regs = number_of_inputs;
     } else {
       num_regs = code_item->registers_size_;
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 38edc7a..74fec48 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -241,7 +241,7 @@
   }
   CHECK(receiver->GetClass()->ShouldHaveEmbeddedVTable());
   ArtMethod* const called_method = receiver->GetClass()->GetEmbeddedVTableEntry(
-      vtable_idx, kRuntimePointerSize);
+      vtable_idx, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
   if (UNLIKELY(called_method == nullptr)) {
     CHECK(self->IsExceptionPending());
     result->SetJ(0);
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index c314f3c..c2ef724 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -386,8 +386,9 @@
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
   mirror::Class* klass = mirror::String::GetJavaLangString();
-  ArtMethod* method = klass->FindDeclaredDirectMethod("<init>", "(Ljava/lang/String;)V",
-                                                      kRuntimePointerSize);
+  ArtMethod* method =
+      klass->FindDeclaredDirectMethod("<init>", "(Ljava/lang/String;)V",
+                                      Runtime::Current()->GetClassLinker()->GetImagePointerSize());
 
   // create instruction data for invoke-direct {v0, v1} of method with fake index
   uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 };
@@ -1335,10 +1336,16 @@
   ArtMethod* throw_cons = throw_class->FindDeclaredDirectMethod(
       "<init>", "(Ljava/lang/String;)V", class_linker->GetImagePointerSize());
   ASSERT_TRUE(throw_cons != nullptr);
-
-  Handle<mirror::Constructor> cons = hs.NewHandle(
-      mirror::Constructor::CreateFromArtMethod<kRuntimePointerSize, false>(self, throw_cons));
-  ASSERT_TRUE(cons != nullptr);
+  Handle<mirror::Constructor> cons;
+  if (class_linker->GetImagePointerSize() == PointerSize::k64) {
+     cons = hs.NewHandle(
+        mirror::Constructor::CreateFromArtMethod<PointerSize::k64, false>(self, throw_cons));
+    ASSERT_TRUE(cons != nullptr);
+  } else {
+    cons = hs.NewHandle(
+        mirror::Constructor::CreateFromArtMethod<PointerSize::k32, false>(self, throw_cons));
+    ASSERT_TRUE(cons != nullptr);
+  }
 
   Handle<mirror::ObjectArray<mirror::Object>> args = hs.NewHandle(
       mirror::ObjectArray<mirror::Object>::Alloc(
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 147173c..a247b56 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -336,7 +336,9 @@
         methods_region_size +
         dex_data.bitmap_storage.size();
   }
-  if (required_capacity > kProfileSizeErrorThresholdInBytes) {
+  // Allow large profiles for non target builds for the case where we are merging many profiles
+  // to generate a boot image profile.
+  if (kIsTargetBuild && required_capacity > kProfileSizeErrorThresholdInBytes) {
     LOG(ERROR) << "Profile data size exceeds "
                << std::to_string(kProfileSizeErrorThresholdInBytes)
                << " bytes. Profile will not be written to disk.";
@@ -1030,8 +1032,9 @@
   if (status != kProfileLoadSuccess) {
     return status;
   }
-
-  if (uncompressed_data_size > kProfileSizeErrorThresholdInBytes) {
+  // Allow large profiles for non target builds for the case where we are merging many profiles
+  // to generate a boot image profile.
+  if (kIsTargetBuild && uncompressed_data_size > kProfileSizeErrorThresholdInBytes) {
     LOG(ERROR) << "Profile data size exceeds "
                << std::to_string(kProfileSizeErrorThresholdInBytes)
                << " bytes";
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 10dddae..61e5be3 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -29,6 +29,7 @@
 #include "base/stl_util.h"
 #include "base/systrace.h"
 #include "base/time_utils.h"
+#include "class_table-inl.h"
 #include "compiler_filter.h"
 #include "dex_reference_collection.h"
 #include "gc/collector_type.h"
@@ -121,7 +122,7 @@
     }
     total_ms_of_sleep_ += options_.GetSaveResolvedClassesDelayMs();
   }
-  FetchAndCacheResolvedClassesAndMethods();
+  FetchAndCacheResolvedClassesAndMethods(/*startup*/ true);
 
   // Loop for the profiled methods.
   while (!ShuttingDown(self)) {
@@ -210,64 +211,6 @@
   }
 }
 
-using MethodReferenceCollection = DexReferenceCollection<uint16_t, ScopedArenaAllocatorAdapter>;
-using TypeReferenceCollection = DexReferenceCollection<dex::TypeIndex,
-                                                       ScopedArenaAllocatorAdapter>;
-
-// Get resolved methods that have a profile info or more than kStartupMethodSamples samples.
-// Excludes native methods and classes in the boot image.
-class GetClassesAndMethodsVisitor : public ClassVisitor {
- public:
-  GetClassesAndMethodsVisitor(MethodReferenceCollection* hot_methods,
-                              MethodReferenceCollection* sampled_methods,
-                              TypeReferenceCollection* resolved_classes,
-                              uint32_t hot_method_sample_threshold,
-                              bool profile_boot_class_path)
-    : hot_methods_(hot_methods),
-      sampled_methods_(sampled_methods),
-      resolved_classes_(resolved_classes),
-      hot_method_sample_threshold_(hot_method_sample_threshold),
-      profile_boot_class_path_(profile_boot_class_path) {}
-
-  virtual bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
-    if (klass->IsProxyClass() ||
-        klass->IsArrayClass() ||
-        klass->IsPrimitive() ||
-        !klass->IsResolved() ||
-        klass->IsErroneousResolved() ||
-        (!profile_boot_class_path_ && klass->GetClassLoader() == nullptr)) {
-      return true;
-    }
-    CHECK(klass->GetDexCache() != nullptr) << klass->PrettyClass();
-    resolved_classes_->AddReference(&klass->GetDexFile(), klass->GetDexTypeIndex());
-    for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) {
-      if (!method.IsNative()) {
-        DCHECK(!method.IsProxyMethod());
-        const uint16_t counter = method.GetCounter();
-        // Mark startup methods as hot if they have more than hot_method_sample_threshold_ samples.
-        // This means they will get compiled by the compiler driver.
-        if (method.GetProfilingInfo(kRuntimePointerSize) != nullptr ||
-            (method.GetAccessFlags() & kAccPreviouslyWarm) != 0 ||
-            counter >= hot_method_sample_threshold_) {
-          hot_methods_->AddReference(method.GetDexFile(), method.GetDexMethodIndex());
-        } else if (counter != 0) {
-          sampled_methods_->AddReference(method.GetDexFile(), method.GetDexMethodIndex());
-        }
-      } else {
-        CHECK_EQ(method.GetCounter(), 0u);
-      }
-    }
-    return true;
-  }
-
- private:
-  MethodReferenceCollection* const hot_methods_;
-  MethodReferenceCollection* const sampled_methods_;
-  TypeReferenceCollection* const resolved_classes_;
-  uint32_t hot_method_sample_threshold_;
-  const bool profile_boot_class_path_;
-};
-
 class ScopedDefaultPriority {
  public:
   explicit ScopedDefaultPriority(pthread_t thread) : thread_(thread) {
@@ -282,7 +225,146 @@
   const pthread_t thread_;
 };
 
-void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
+// GetClassLoadersVisitor takes a snapshot of the class loaders and stores them in the out
+// class_loaders argument. Not affected by class unloading since there are no suspend points in
+// the caller.
+class GetClassLoadersVisitor : public ClassLoaderVisitor {
+ public:
+  explicit GetClassLoadersVisitor(VariableSizedHandleScope* hs,
+                                  std::vector<Handle<mirror::ClassLoader>>* class_loaders)
+      : hs_(hs),
+        class_loaders_(class_loaders) {}
+
+  void Visit(ObjPtr<mirror::ClassLoader> class_loader)
+      REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
+    class_loaders_->push_back(hs_->NewHandle(class_loader));
+  }
+
+ private:
+  VariableSizedHandleScope* const hs_;
+  std::vector<Handle<mirror::ClassLoader>>* const class_loaders_;
+};
+
+// GetClassesVisitor takes a snapshot of the loaded classes that we may want to visit and stores
+// them in the out argument. Not affected by class unloading since there are no suspend points in
+// the caller.
+class GetClassesVisitor : public ClassVisitor {
+ public:
+  explicit GetClassesVisitor(bool profile_boot_class_path,
+                             ScopedArenaVector<ObjPtr<mirror::Class>>* out)
+      : profile_boot_class_path_(profile_boot_class_path),
+        out_(out) {}
+
+  virtual bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (klass->IsProxyClass() ||
+        klass->IsArrayClass() ||
+        klass->IsPrimitive() ||
+        !klass->IsResolved() ||
+        klass->IsErroneousResolved() ||
+        (!profile_boot_class_path_ && klass->GetClassLoader() == nullptr)) {
+      return true;
+    }
+    out_->push_back(klass);
+    return true;
+  }
+
+ private:
+  const bool profile_boot_class_path_;
+  ScopedArenaVector<ObjPtr<mirror::Class>>* const out_;
+};
+
+using MethodReferenceCollection = DexReferenceCollection<uint16_t, ScopedArenaAllocatorAdapter>;
+using TypeReferenceCollection = DexReferenceCollection<dex::TypeIndex,
+                                                       ScopedArenaAllocatorAdapter>;
+
+// Iterate over all of the loaded classes and visit each one. For each class, add it to the
+// resolved_classes out argument if startup is true.
+// Add methods to the hot_methods out argument if the number of samples is greater or equal to
+// hot_method_sample_threshold, add it to sampled_methods if it has at least one sample.
+static void SampleClassesAndExecutedMethods(pthread_t profiler_pthread,
+                                            bool profile_boot_class_path,
+                                            ScopedArenaAllocator* allocator,
+                                            uint32_t hot_method_sample_threshold,
+                                            bool startup,
+                                            TypeReferenceCollection* resolved_classes,
+                                            MethodReferenceCollection* hot_methods,
+                                            MethodReferenceCollection* sampled_methods) {
+  Thread* const self = Thread::Current();
+  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+  // Restore profile saver thread priority during the GC critical section. This helps prevent
+  // priority inversions blocking the GC for long periods of time.
+  std::unique_ptr<ScopedDefaultPriority> sdp;
+  // Only restore default priority if we are the profile saver thread. Other threads that call this
+  // are threads calling Stop and the signal catcher (for SIGUSR1).
+  if (pthread_self() == profiler_pthread) {
+    sdp.reset(new ScopedDefaultPriority(profiler_pthread));
+  }
+
+  // Do ScopedGCCriticalSection before acquiring mutator lock to prevent the GC running and
+  // blocking threads during thread root flipping. Since the GC is a background thread, blocking it
+  // is not a problem.
+  ScopedObjectAccess soa(self);
+  gc::ScopedGCCriticalSection sgcs(self,
+                                   gc::kGcCauseProfileSaver,
+                                   gc::kCollectorTypeCriticalSection);
+  VariableSizedHandleScope hs(soa.Self());
+  std::vector<Handle<mirror::ClassLoader>> class_loaders;
+  if (profile_boot_class_path) {
+    // First add the boot class loader since visit classloaders doesn't visit it.
+    class_loaders.push_back(hs.NewHandle<mirror::ClassLoader>(nullptr));
+  }
+  GetClassLoadersVisitor class_loader_visitor(&hs, &class_loaders);
+  {
+    // Read the class loaders into a temporary array to prevent contention problems on the
+    // class_linker_classes_lock.
+    ScopedTrace trace2("Get class loaders");
+    ReaderMutexLock mu(soa.Self(), *Locks::classlinker_classes_lock_);
+    class_linker->VisitClassLoaders(&class_loader_visitor);
+  }
+  ScopedArenaVector<ObjPtr<mirror::Class>> classes(allocator->Adapter());
+  for (Handle<mirror::ClassLoader> class_loader : class_loaders) {
+    ClassTable* table = class_linker->ClassTableForClassLoader(class_loader.Get());
+    if (table == nullptr) {
+      // If the class loader has not loaded any classes, it may have a null table.
+      continue;
+    }
+    GetClassesVisitor get_classes_visitor(profile_boot_class_path, &classes);
+    {
+      // Collect the classes into a temporary array to prevent lock contention on the class
+      // table lock. We want to avoid blocking class loading in other threads as much as
+      // possible.
+      ScopedTrace trace3("Visiting class table");
+      table->Visit(get_classes_visitor);
+    }
+    for (ObjPtr<mirror::Class> klass : classes) {
+      if (startup) {
+        // We only record classes for the startup case. This may change in the future.
+        resolved_classes->AddReference(&klass->GetDexFile(), klass->GetDexTypeIndex());
+      }
+      // Visit all of the methods in the class to see which ones were executed.
+      for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) {
+        if (!method.IsNative()) {
+          DCHECK(!method.IsProxyMethod());
+          const uint16_t counter = method.GetCounter();
+          // Mark startup methods as hot if they have more than hot_method_sample_threshold
+          // samples. This means they will get compiled by the compiler driver.
+          if (method.GetProfilingInfo(kRuntimePointerSize) != nullptr ||
+              (method.GetAccessFlags() & kAccPreviouslyWarm) != 0 ||
+              counter >= hot_method_sample_threshold) {
+            hot_methods->AddReference(method.GetDexFile(), method.GetDexMethodIndex());
+          } else if (counter != 0) {
+            sampled_methods->AddReference(method.GetDexFile(), method.GetDexMethodIndex());
+          }
+        } else {
+          CHECK_EQ(method.GetCounter(), 0u);
+        }
+      }
+    }
+    classes.clear();
+  }
+}
+
+void ProfileSaver::FetchAndCacheResolvedClassesAndMethods(bool startup) {
   ScopedTrace trace(__PRETTY_FUNCTION__);
   const uint64_t start_time = NanoTime();
 
@@ -294,34 +376,25 @@
   ArenaStack stack(runtime->GetArenaPool());
   ScopedArenaAllocator allocator(&stack);
   MethodReferenceCollection hot_methods(allocator.Adapter(), allocator.Adapter());
-  MethodReferenceCollection startup_methods(allocator.Adapter(), allocator.Adapter());
+  MethodReferenceCollection sampled_methods(allocator.Adapter(), allocator.Adapter());
   TypeReferenceCollection resolved_classes(allocator.Adapter(), allocator.Adapter());
   const bool is_low_ram = Runtime::Current()->GetHeap()->IsLowMemoryMode();
-  const size_t hot_threshold = options_.GetHotStartupMethodSamples(is_low_ram);
   pthread_t profiler_pthread;
   {
     MutexLock mu(self, *Locks::profiler_lock_);
     profiler_pthread = profiler_pthread_;
   }
-  {
-    // Restore profile saver thread priority during the GC critical section. This helps prevent
-    // priority inversions blocking the GC for long periods of time.
-    ScopedDefaultPriority sdp(profiler_pthread);
-    ScopedObjectAccess soa(self);
-    gc::ScopedGCCriticalSection sgcs(self,
-                                     gc::kGcCauseProfileSaver,
-                                     gc::kCollectorTypeCriticalSection);
-    {
-      ScopedTrace trace2("Get hot methods");
-      GetClassesAndMethodsVisitor visitor(&hot_methods,
-                                          &startup_methods,
-                                          &resolved_classes,
-                                          hot_threshold,
-                                          options_.GetProfileBootClassPath());
-      runtime->GetClassLinker()->VisitClasses(&visitor);
-    }
-  }
-
+  const uint32_t hot_method_sample_threshold = startup ?
+      options_.GetHotStartupMethodSamples(is_low_ram) :
+      std::numeric_limits<uint32_t>::max();
+  SampleClassesAndExecutedMethods(profiler_pthread,
+                                  options_.GetProfileBootClassPath(),
+                                  &allocator,
+                                  hot_method_sample_threshold,
+                                  startup,
+                                  &resolved_classes,
+                                  &hot_methods,
+                                  &sampled_methods);
   MutexLock mu(self, *Locks::profiler_lock_);
   uint64_t total_number_of_profile_entries_cached = 0;
   using Hotness = ProfileCompilationInfo::MethodHotness;
@@ -329,9 +402,12 @@
   for (const auto& it : tracked_dex_base_locations_) {
     std::set<DexCacheResolvedClasses> resolved_classes_for_location;
     const std::string& filename = it.first;
-    auto info_it = profile_cache_.Put(
-        filename,
-        new ProfileCompilationInfo(Runtime::Current()->GetArenaPool()));
+    auto info_it = profile_cache_.find(filename);
+    if (info_it == profile_cache_.end()) {
+      info_it = profile_cache_.Put(
+          filename,
+          new ProfileCompilationInfo(Runtime::Current()->GetArenaPool()));
+    }
     ProfileCompilationInfo* cached_info = info_it->second;
 
     const std::set<std::string>& locations = it.second;
@@ -339,18 +415,20 @@
       const DexFile* const dex_file = pair.first;
       if (locations.find(dex_file->GetBaseLocation()) != locations.end()) {
         const MethodReferenceCollection::IndexVector& indices = pair.second;
+        uint8_t flags = Hotness::kFlagHot;
+        flags |= startup ? Hotness::kFlagStartup : Hotness::kFlagPostStartup;
         cached_info->AddMethodsForDex(
-            static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagStartup),
+            static_cast<Hotness::Flag>(flags),
             dex_file,
             indices.begin(),
             indices.end());
       }
     }
-    for (const auto& pair : startup_methods.GetMap()) {
+    for (const auto& pair : sampled_methods.GetMap()) {
       const DexFile* const dex_file = pair.first;
       if (locations.find(dex_file->GetBaseLocation()) != locations.end()) {
         const MethodReferenceCollection::IndexVector& indices = pair.second;
-        cached_info->AddMethodsForDex(Hotness::kFlagStartup,
+        cached_info->AddMethodsForDex(startup ? Hotness::kFlagStartup : Hotness::kFlagPostStartup,
                                       dex_file,
                                       indices.begin(),
                                       indices.end());
@@ -375,8 +453,9 @@
       max_number_of_profile_entries_cached_,
       total_number_of_profile_entries_cached);
   VLOG(profiler) << "Profile saver recorded " << hot_methods.NumReferences() << " hot methods and "
-                 << startup_methods.NumReferences() << " startup methods with threshold "
-                 << hot_threshold << " in " << PrettyDuration(NanoTime() - start_time);
+                 << sampled_methods.NumReferences() << " sampled methods with threshold "
+                 << hot_method_sample_threshold << " in "
+                 << PrettyDuration(NanoTime() - start_time);
 }
 
 bool ProfileSaver::ProcessProfilingInfo(bool force_save, /*out*/uint16_t* number_of_new_methods) {
@@ -397,6 +476,10 @@
     *number_of_new_methods = 0;
   }
 
+  // We only need to do this once, not once per dex location.
+  // TODO: Figure out a way to only do it when stuff has changed? It takes 30-50ms.
+  FetchAndCacheResolvedClassesAndMethods(/*startup*/ false);
+
   for (const auto& it : tracked_locations) {
     if (!force_save && ShuttingDown(Thread::Current())) {
       // The ProfileSaver is in shutdown mode, meaning a stop request was made and
@@ -442,6 +525,7 @@
         total_number_of_skipped_writes_++;
         continue;
       }
+
       if (number_of_new_methods != nullptr) {
         *number_of_new_methods =
             std::max(static_cast<uint16_t>(delta_number_of_methods),
@@ -473,11 +557,12 @@
         total_number_of_failed_writes_++;
       }
     }
-    // Trim the maps to madvise the pages used for profile info.
-    // It is unlikely we will need them again in the near feature.
-    Runtime::Current()->GetArenaPool()->TrimMaps();
   }
 
+  // Trim the maps to madvise the pages used for profile info.
+  // It is unlikely we will need them again in the near feature.
+  Runtime::Current()->GetArenaPool()->TrimMaps();
+
   return profile_file_saved;
 }
 
@@ -621,12 +706,13 @@
     profile_saver->period_condition_.Signal(Thread::Current());
   }
 
+  // Force save everything before destroying the thread since we want profiler_pthread_ to remain
+  // valid.
+  instance_->ProcessProfilingInfo(/*force_save*/true, /*number_of_new_methods*/nullptr);
+
   // Wait for the saver thread to stop.
   CHECK_PTHREAD_CALL(pthread_join, (profiler_pthread, nullptr), "profile saver thread shutdown");
 
-  // Force save everything before destroying the instance.
-  instance_->ProcessProfilingInfo(/*force_save*/true, /*number_of_new_methods*/nullptr);
-
   {
     MutexLock profiler_mutex(Thread::Current(), *Locks::profiler_lock_);
     if (dump_info) {
@@ -713,16 +799,18 @@
   }
 }
 
-bool ProfileSaver::HasSeenMethod(const std::string& profile,
-                                 const DexFile* dex_file,
-                                 uint16_t method_idx) {
+bool ProfileSaver::HasSeenMethod(const std::string& profile, bool hot, MethodReference ref) {
   MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
   if (instance_ != nullptr) {
     ProfileCompilationInfo info(Runtime::Current()->GetArenaPool());
     if (!info.Load(profile, /*clear_if_invalid*/false)) {
       return false;
     }
-    return info.GetMethodHotness(MethodReference(dex_file, method_idx)).IsInProfile();
+    ProfileCompilationInfo::MethodHotness hotness = info.GetMethodHotness(ref);
+    // Ignore hot parameter for now since it was causing test 595 to be flaky. TODO: Investigate.
+    // b/63635729
+    UNUSED(hot);
+    return hotness.IsInProfile();
   }
   return false;
 }
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 01d72fe..ce8233b 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -19,6 +19,7 @@
 
 #include "base/mutex.h"
 #include "jit_code_cache.h"
+#include "method_reference.h"
 #include "profile_compilation_info.h"
 #include "profile_saver_options.h"
 #include "safe_map.h"
@@ -55,10 +56,8 @@
   // For testing or manual purposes (SIGUSR1).
   static void ForceProcessProfiles();
 
-  // Just for testing purpose.
-  static bool HasSeenMethod(const std::string& profile,
-                            const DexFile* dex_file,
-                            uint16_t method_idx);
+  // Just for testing purposes.
+  static bool HasSeenMethod(const std::string& profile, bool hot, MethodReference ref);
 
  private:
   ProfileSaver(const ProfileSaverOptions& options,
@@ -97,7 +96,7 @@
 
   // Fetches the current resolved classes and methods from the ClassLinker and stores them in the
   // profile_cache_ for later save.
-  void FetchAndCacheResolvedClassesAndMethods();
+  void FetchAndCacheResolvedClassesAndMethods(bool startup);
 
   void DumpInfo(std::ostream& os);
 
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 419a4db..003cd4e 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -673,11 +673,7 @@
 }
 
 inline void Class::SetClinitThreadId(pid_t new_clinit_thread_id) {
-  if (Runtime::Current()->IsActiveTransaction()) {
-    SetField32<true>(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_), new_clinit_thread_id);
-  } else {
-    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_), new_clinit_thread_id);
-  }
+  SetField32Transaction(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_), new_clinit_thread_id);
 }
 
 inline String* Class::GetName() {
@@ -685,11 +681,7 @@
 }
 
 inline void Class::SetName(ObjPtr<String> name) {
-  if (Runtime::Current()->IsActiveTransaction()) {
-    SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, name_), name);
-  } else {
-    SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, name_), name);
-  }
+    SetFieldObjectTransaction(OFFSET_OF_OBJECT_MEMBER(Class, name_), name);
 }
 
 template<VerifyObjectFlags kVerifyFlags>
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index e4b5320..b0e5b6a 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -191,7 +191,7 @@
 }
 
 void Class::SetDexCache(ObjPtr<DexCache> new_dex_cache) {
-  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache);
+  SetFieldObjectTransaction(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache);
 }
 
 void Class::SetClassSize(uint32_t new_class_size) {
@@ -200,8 +200,7 @@
     LOG(FATAL_WITHOUT_ABORT) << new_class_size << " vs " << GetClassSize();
     LOG(FATAL) << "class=" << PrettyTypeOf();
   }
-  // Not called within a transaction.
-  SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), new_class_size);
+  SetField32Transaction(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), new_class_size);
 }
 
 // Return the class' name. The exact format is bizarre, but it's the specified behavior for
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 00498bc..e516a06 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -138,9 +138,10 @@
     kStatusRetryVerificationAtRuntime = 6,  // Compile time verification failed, retry at runtime.
     kStatusVerifyingAtRuntime = 7,  // Retrying verification at runtime.
     kStatusVerified = 8,  // Logically part of linking; done pre-init.
-    kStatusInitializing = 9,  // Class init in progress.
-    kStatusInitialized = 10,  // Ready to go.
-    kStatusMax = 11,
+    kStatusSuperclassValidated = 9,  // Superclass validation part of init done.
+    kStatusInitializing = 10,  // Class init in progress.
+    kStatusInitialized = 11,  // Ready to go.
+    kStatusMax = 12,
   };
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
@@ -408,7 +409,7 @@
     DCHECK_EQ(v32 & kPrimitiveTypeMask, v32) << "upper 16 bits aren't zero";
     // Store the component size shift in the upper 16 bits.
     v32 |= Primitive::ComponentSizeShift(new_type) << kPrimitiveTypeSizeShiftShift;
-    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), v32);
+    SetField32Transaction(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), v32);
   }
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
@@ -1169,8 +1170,7 @@
   }
 
   void SetDexClassDefIndex(uint16_t class_def_idx) REQUIRES_SHARED(Locks::mutator_lock_) {
-    // Not called within a transaction.
-    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_class_def_idx_), class_def_idx);
+    SetField32Transaction(OFFSET_OF_OBJECT_MEMBER(Class, dex_class_def_idx_), class_def_idx);
   }
 
   dex::TypeIndex GetDexTypeIndex() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1179,8 +1179,7 @@
   }
 
   void SetDexTypeIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_) {
-    // Not called within a transaction.
-    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), type_idx.index_);
+    SetField32Transaction(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), type_idx.index_);
   }
 
   dex::TypeIndex FindTypeIndexInOtherDexFile(const DexFile& dex_file)
diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc
index 0c25fa8..9eada6d 100644
--- a/runtime/mirror/method_handles_lookup.cc
+++ b/runtime/mirror/method_handles_lookup.cc
@@ -20,7 +20,10 @@
 #include "gc_root-inl.h"
 #include "object-inl.h"
 #include "handle_scope.h"
+#include "jni_internal.h"
+#include "mirror/method_handle_impl.h"
 #include "modifiers.h"
+#include "well_known_classes.h"
 
 namespace art {
 namespace mirror {
@@ -54,5 +57,27 @@
   return mhl.Get();
 }
 
+MethodHandlesLookup* MethodHandlesLookup::GetDefault(Thread* const self) {
+  ArtMethod* lookup = jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandles_lookup);
+  JValue result;
+  lookup->Invoke(self, nullptr, 0, &result, "L");
+  return down_cast<MethodHandlesLookup*>(result.GetL());
+}
+
+MethodHandle* MethodHandlesLookup::FindConstructor(Thread* const self,
+                                                           Handle<Class> klass,
+                                                           Handle<MethodType> method_type) {
+  ArtMethod* findConstructor =
+      jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandles_Lookup_findConstructor);
+  uint32_t args[] = {
+    static_cast<uint32_t>(reinterpret_cast<uintptr_t>(this)),
+    static_cast<uint32_t>(reinterpret_cast<uintptr_t>(klass.Get())),
+    static_cast<uint32_t>(reinterpret_cast<uintptr_t>(method_type.Get()))
+  };
+  JValue result;
+  findConstructor->Invoke(self, args, sizeof(args), &result, "LLL");
+  return down_cast<MethodHandle*>(result.GetL());
+}
+
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/mirror/method_handles_lookup.h b/runtime/mirror/method_handles_lookup.h
index 63eb428..2109f60 100644
--- a/runtime/mirror/method_handles_lookup.h
+++ b/runtime/mirror/method_handles_lookup.h
@@ -30,6 +30,9 @@
 
 namespace mirror {
 
+class MethodHandle;
+class MethodType;
+
 // C++ mirror of java.lang.invoke.MethodHandles.Lookup
 class MANAGED MethodHandlesLookup : public Object {
  public:
@@ -45,6 +48,16 @@
   static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
   static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Returns the result of java.lang.invoke.MethodHandles.lookup().
+  static mirror::MethodHandlesLookup* GetDefault(Thread* const self)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  // Find constructor using java.lang.invoke.MethodHandles$Lookup.findConstructor().
+  mirror::MethodHandle* FindConstructor(Thread* const self,
+                                        Handle<Class> klass,
+                                        Handle<MethodType> method_type)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
   static MemberOffset AllowedModesOffset() {
     return MemberOffset(OFFSETOF_MEMBER(MethodHandlesLookup, allowed_modes_));
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 95f829d..43d70b7 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -560,6 +560,15 @@
   SetField32<kTransactionActive, kCheckTransaction, kVerifyFlags, true>(field_offset, new_value);
 }
 
+template<bool kCheckTransaction, VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
+inline void Object::SetField32Transaction(MemberOffset field_offset, int32_t new_value) {
+  if (Runtime::Current()->IsActiveTransaction()) {
+    SetField32<true, kCheckTransaction, kVerifyFlags, kIsVolatile>(field_offset, new_value);
+  } else {
+    SetField32<false, kCheckTransaction, kVerifyFlags, kIsVolatile>(field_offset, new_value);
+  }
+}
+
 // TODO: Pass memory_order_ and strong/weak as arguments to avoid code duplication?
 
 template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
@@ -657,6 +666,15 @@
                                                                                new_value);
 }
 
+template<bool kCheckTransaction, VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
+inline void Object::SetField64Transaction(MemberOffset field_offset, int32_t new_value) {
+  if (Runtime::Current()->IsActiveTransaction()) {
+    SetField64<true, kCheckTransaction, kVerifyFlags, kIsVolatile>(field_offset, new_value);
+  } else {
+    SetField64<false, kCheckTransaction, kVerifyFlags, kIsVolatile>(field_offset, new_value);
+  }
+}
+
 template<typename kSize>
 inline kSize Object::GetFieldAcquire(MemberOffset field_offset) {
   const uint8_t* raw_addr = reinterpret_cast<const uint8_t*>(this) + field_offset.Int32Value();
@@ -775,6 +793,15 @@
                                                                             new_value);
 }
 
+template<bool kCheckTransaction, VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
+inline void Object::SetFieldObjectTransaction(MemberOffset field_offset, ObjPtr<Object> new_value) {
+  if (Runtime::Current()->IsActiveTransaction()) {
+    SetFieldObject<true, kCheckTransaction, kVerifyFlags, kIsVolatile>(field_offset, new_value);
+  } else {
+    SetFieldObject<false, kCheckTransaction, kVerifyFlags, kIsVolatile>(field_offset, new_value);
+  }
+}
+
 template <VerifyObjectFlags kVerifyFlags>
 inline HeapReference<Object>* Object::GetFieldObjectReferenceAddr(MemberOffset field_offset) {
   if (kVerifyFlags & kVerifyThis) {
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 9cf4252..886780f 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -312,6 +312,11 @@
                                             ObjPtr<Object> new_value)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  template<bool kCheckTransaction = true, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
+           bool kIsVolatile = false>
+  ALWAYS_INLINE void SetFieldObjectTransaction(MemberOffset field_offset, ObjPtr<Object> new_value)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   template<bool kTransactionActive,
            bool kCheckTransaction = true,
            VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
@@ -470,6 +475,12 @@
   ALWAYS_INLINE void SetField32Volatile(MemberOffset field_offset, int32_t new_value)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  template<bool kCheckTransaction = true,
+           VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
+           bool kIsVolatile = false>
+  ALWAYS_INLINE void SetField32Transaction(MemberOffset field_offset, int32_t new_value)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   template<bool kTransactionActive, bool kCheckTransaction = true,
       VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ALWAYS_INLINE bool CasFieldWeakSequentiallyConsistent32(MemberOffset field_offset,
@@ -525,6 +536,12 @@
   ALWAYS_INLINE void SetField64Volatile(MemberOffset field_offset, int64_t new_value)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  template<bool kCheckTransaction = true,
+           VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
+           bool kIsVolatile = false>
+  ALWAYS_INLINE void SetField64Transaction(MemberOffset field_offset, int32_t new_value)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   template<bool kTransactionActive, bool kCheckTransaction = true,
       VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool CasFieldWeakSequentiallyConsistent64(MemberOffset field_offset, int64_t old_value,
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 7560639..84587c8 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -251,6 +251,7 @@
                                           Handle<ByteArray> array, int32_t offset,
                                           int32_t high_byte, gc::AllocatorType allocator_type) {
   const uint8_t* const src = reinterpret_cast<uint8_t*>(array->GetData()) + offset;
+  high_byte &= 0xff;  // Extract the relevant bits before determining `compressible`.
   const bool compressible =
       kUseStringCompression && String::AllASCII<uint8_t>(src, byte_length) && (high_byte == 0);
   const int32_t length_with_flag = String::GetFlaggedCount(byte_length, compressible);
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index 7027410..aee4b19 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -26,7 +26,6 @@
 #include "object-inl.h"
 #include "object_array.h"
 #include "object_array-inl.h"
-#include "object_callbacks.h"
 #include "stack_trace_element.h"
 #include "string.h"
 #include "utils.h"
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 3e3eaae..5c63dca 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -910,7 +910,8 @@
         // Go ahead and inflate the lock.
         Inflate(self, owner, obj.Get(), hash_code);
       }
-      thread_list->Resume(owner, SuspendReason::kInternal);
+      bool resumed = thread_list->Resume(owner, SuspendReason::kInternal);
+      DCHECK(resumed);
     }
     self->SetMonitorEnterObject(nullptr);
   }
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index 7d2d0e5..2aeef60 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -62,7 +62,8 @@
         trace = thread->CreateInternalStackTrace<false>(soa);
       }
       // Restart suspended thread.
-      thread_list->Resume(thread, SuspendReason::kInternal);
+      bool resumed = thread_list->Resume(thread, SuspendReason::kInternal);
+      DCHECK(resumed);
     } else if (timed_out) {
       LOG(ERROR) << "Trying to get thread's stack failed as the thread failed to suspend within a "
           "generous timeout.";
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index 8b76327..4ce72ed 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -155,7 +155,8 @@
       ScopedObjectAccess soa(env);
       thread->SetThreadName(name.c_str());
     }
-    thread_list->Resume(thread, SuspendReason::kInternal);
+    bool resumed = thread_list->Resume(thread, SuspendReason::kInternal);
+    DCHECK(resumed);
   } else if (timed_out) {
     LOG(ERROR) << "Trying to set thread name to '" << name.c_str() << "' failed as the thread "
         "failed to suspend within a generous timeout.";
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index c516b66..125d737 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -76,7 +76,8 @@
         trace = Thread::InternalStackTraceToStackTraceElementArray(soa, internal_trace);
       }
       // Restart suspended thread.
-      thread_list->Resume(thread, SuspendReason::kInternal);
+      bool resumed = thread_list->Resume(thread, SuspendReason::kInternal);
+      DCHECK(resumed);
     } else {
       if (timed_out) {
         LOG(ERROR) << "Trying to get thread's stack by id failed as the thread failed to suspend "
diff --git a/runtime/oat.h b/runtime/oat.h
index 521cc40..f4edb16 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  static constexpr uint8_t kOatVersion[] = { '1', '2', '7', '\0' };  // .bss ArtMethod* section.
+  // Last oat version changed reason: add new class status to skip superclass validation.
+  static constexpr uint8_t kOatVersion[] = { '1', '2', '9', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 888de45..1c1189d 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1574,28 +1574,6 @@
   return GetOatHeader().GetCompilerFilter();
 }
 
-static constexpr char kDexClassPathEncodingSeparator = '*';
-
-std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files,
-                                               std::string& base_dir) {
-  std::ostringstream out;
-
-  for (const DexFile* dex_file : dex_files) {
-    const std::string& location = dex_file->GetLocation();
-    // Find paths that were relative and convert them back from absolute.
-    if (!base_dir.empty() && location.substr(0, base_dir.length()) == base_dir) {
-      out << location.substr(base_dir.length() + 1).c_str();
-    } else {
-      out << dex_file->GetLocation().c_str();
-    }
-    out << kDexClassPathEncodingSeparator;
-    out << dex_file->GetLocationChecksum();
-    out << kDexClassPathEncodingSeparator;
-  }
-
-  return out.str();
-}
-
 OatFile::OatClass OatFile::FindOatClass(const DexFile& dex_file,
                                         uint16_t class_def_idx,
                                         bool* found) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 66ed44f..b112b84 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -296,11 +296,6 @@
   static std::string ResolveRelativeEncodedDexLocation(
       const char* abs_dex_location, const std::string& rel_dex_location);
 
-  // Create a dependency list (dex locations and checksums) for the given dex files.
-  // Removes dex file paths prefixed with base_dir to convert them back to relative paths.
-  static std::string EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files,
-                                               std::string& base_dir);
-
   // Finds the associated oat class for a dex_file and descriptor. Returns an invalid OatClass on
   // error and sets found to false.
   static OatClass FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found);
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 4820feb..c876657 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -298,28 +298,38 @@
 }
 
 std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
-    const OatFile& oat_file, const char* dex_location) {
+    const OatFile &oat_file, const char *dex_location) {
   std::vector<std::unique_ptr<const DexFile>> dex_files;
+  if (LoadDexFiles(oat_file, dex_location, &dex_files)) {
+    return dex_files;
+  } else {
+    return std::vector<std::unique_ptr<const DexFile>>();
+  }
+}
 
+bool OatFileAssistant::LoadDexFiles(
+    const OatFile &oat_file,
+    const std::string& dex_location,
+    std::vector<std::unique_ptr<const DexFile>>* out_dex_files) {
   // Load the main dex file.
   std::string error_msg;
   const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(
-      dex_location, nullptr, &error_msg);
+      dex_location.c_str(), nullptr, &error_msg);
   if (oat_dex_file == nullptr) {
     LOG(WARNING) << error_msg;
-    return std::vector<std::unique_ptr<const DexFile>>();
+    return false;
   }
 
   std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
   if (dex_file.get() == nullptr) {
     LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
-    return std::vector<std::unique_ptr<const DexFile>>();
+    return false;
   }
-  dex_files.push_back(std::move(dex_file));
+  out_dex_files->push_back(std::move(dex_file));
 
   // Load the rest of the multidex entries
-  for (size_t i = 1; ; i++) {
-    std::string multidex_dex_location = DexFile::GetMultiDexLocation(i, dex_location);
+  for (size_t i = 1;; i++) {
+    std::string multidex_dex_location = DexFile::GetMultiDexLocation(i, dex_location.c_str());
     oat_dex_file = oat_file.GetOatDexFile(multidex_dex_location.c_str(), nullptr);
     if (oat_dex_file == nullptr) {
       // There are no more multidex entries to load.
@@ -329,11 +339,11 @@
     dex_file = oat_dex_file->OpenDexFile(&error_msg);
     if (dex_file.get() == nullptr) {
       LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
-      return std::vector<std::unique_ptr<const DexFile>>();
+      return false;
     }
-    dex_files.push_back(std::move(dex_file));
+    out_dex_files->push_back(std::move(dex_file));
   }
-  return dex_files;
+  return true;
 }
 
 bool OatFileAssistant::HasOriginalDexFiles() {
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 03d9ca3..92d87ea 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -207,6 +207,13 @@
   static std::vector<std::unique_ptr<const DexFile>> LoadDexFiles(
       const OatFile& oat_file, const char* dex_location);
 
+  // Same as `std::vector<std::unique_ptr<const DexFile>> LoadDexFiles(...)` with the difference:
+  //   - puts the dex files in the given vector
+  //   - returns whether or not all dex files were successfully opened
+  static bool LoadDexFiles(const OatFile& oat_file,
+                           const std::string& dex_location,
+                           std::vector<std::unique_ptr<const DexFile>>* out_dex_files);
+
   // Returns true if there are dex files in the original dex location that can
   // be compiled with dex2oat for this dex location.
   // Returns false if there is no original dex file, or if the original dex
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 3619129..1ecdd0d 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -23,6 +23,7 @@
 
 #include "art_field-inl.h"
 #include "class_linker-inl.h"
+#include "common_runtime_test.h"
 #include "dexopt_test.h"
 #include "oat_file_assistant.h"
 #include "oat_file_manager.h"
@@ -1059,7 +1060,7 @@
     const OatFile* oat_file = nullptr;
     dex_files = Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(
         dex_location_.c_str(),
-        /*class_loader*/nullptr,
+        Runtime::Current()->GetSystemClassLoader(),
         /*dex_elements*/nullptr,
         &oat_file,
         &error_msgs);
@@ -1089,6 +1090,10 @@
   std::string dex_location = GetScratchDir() + "/RaceToGenerate.jar";
   std::string oat_location = GetOdexDir() + "/RaceToGenerate.oat";
 
+  // Start the runtime to initialize the system's class loader.
+  Thread::Current()->TransitionFromSuspendedToRunnable();
+  runtime_->Start();
+
   // We use the lib core dex file, because it's large, and hopefully should
   // take a while to generate.
   Copy(GetLibCoreDexFileNames()[0], dex_location);
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 630945a..dc542d4 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -28,6 +28,7 @@
 #include "base/stl_util.h"
 #include "base/systrace.h"
 #include "class_linker.h"
+#include "class_loader_context.h"
 #include "dex_file-inl.h"
 #include "dex_file_tracking_registrar.h"
 #include "gc/scoped_gc_critical_section.h"
@@ -421,38 +422,47 @@
   }
 }
 
-static bool AreSharedLibrariesOk(const std::string& shared_libraries,
-                                 std::vector<const DexFile*>& dex_files) {
-  // If no shared libraries, we expect no dex files.
-  if (shared_libraries.empty()) {
-    return dex_files.empty();
-  }
-  // If we find the special shared library, skip the shared libraries check.
-  if (shared_libraries.compare(OatFile::kSpecialSharedLibrary) == 0) {
-    return true;
-  }
-  // Shared libraries is a series of dex file paths and their checksums, each separated by '*'.
-  std::vector<std::string> shared_libraries_split;
-  Split(shared_libraries, '*', &shared_libraries_split);
-
-  // Sanity check size of dex files and split shared libraries. Should be 2x as many entries in
-  // the split shared libraries since it contains pairs of filename/checksum.
-  if (dex_files.size() * 2 != shared_libraries_split.size()) {
+static bool AreSharedLibrariesOk(const std::string& context_spec,
+                                 std::vector<const DexFile*>& dex_files,
+                                 std::string* error_msg) {
+  std::vector<std::string> classpath;
+  std::vector<uint32_t> checksums;
+  bool is_special_shared_library;
+  if (!ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey(
+          context_spec, &classpath, &checksums, &is_special_shared_library)) {
+    *error_msg = "Could not decode the class loader context from the oat file key.";
     return false;
   }
 
+  DCHECK_EQ(classpath.size(), checksums.size());
+
+  // The classpath size should match the number of dex files.
+  if (classpath.size() != dex_files.size()) {
+    *error_msg = "The number of loaded dex files does not match the number of files "
+        "specified in the context. Expected=" + std::to_string(classpath.size()) +
+        ", found=" + std::to_string(dex_files.size());
+    return false;
+  }
+
+  // If we find the special shared library, skip the shared libraries check.
+  if (is_special_shared_library) {
+    return true;
+  }
+
   // Check that the loaded dex files have the same order and checksums as the shared libraries.
   for (size_t i = 0; i < dex_files.size(); ++i) {
+    const std::string& dex_location = dex_files[i]->GetLocation();
+    uint32_t dex_location_checksum = dex_files[i]->GetLocationChecksum();
     std::string absolute_library_path =
-        OatFile::ResolveRelativeEncodedDexLocation(dex_files[i]->GetLocation().c_str(),
-                                                   shared_libraries_split[i * 2]);
-    if (dex_files[i]->GetLocation() != absolute_library_path) {
+        OatFile::ResolveRelativeEncodedDexLocation(dex_location.c_str(), classpath[i]);
+    if (dex_location != absolute_library_path) {
+      *error_msg = "SharedLibraryCheck: expected=" + absolute_library_path + ", found=" +
+          dex_location;
       return false;
     }
-    char* end;
-    size_t shared_lib_checksum = strtoul(shared_libraries_split[i * 2 + 1].c_str(), &end, 10);
-    uint32_t dex_checksum = dex_files[i]->GetLocationChecksum();
-    if (*end != '\0' || dex_checksum != shared_lib_checksum) {
+    if (dex_location_checksum  != checksums[i]) {
+      *error_msg = "SharedLibraryCheck: checksum mismatch for " + dex_location + ". Expected=" +
+          std::to_string(checksums[i]) + ", found=" + std::to_string(dex_location_checksum);
       return false;
     }
   }
@@ -586,7 +596,7 @@
   // Exit if shared libraries are ok. Do a full duplicate classes check otherwise.
   const std::string
       shared_libraries(oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
-  if (AreSharedLibrariesOk(shared_libraries, dex_files_loaded)) {
+  if (AreSharedLibrariesOk(shared_libraries, dex_files_loaded, error_msg)) {
     return false;
   }
 
@@ -655,7 +665,10 @@
   // Get the oat file on disk.
   std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
 
-  if (oat_file != nullptr) {
+  // Prevent oat files from being loaded if no class_loader or dex_elements are provided.
+  // This can happen when the deprecated DexFile.<init>(String) is called directly, and it
+  // could load oat files without checking the classpath, which would be incorrect.
+  if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
     // Take the file only if it has no collisions, or we must take it because of preopting.
     bool accept_oat_file =
         !HasCollisions(oat_file.get(), class_loader, dex_elements, /*out*/ &error_msg);
diff --git a/runtime/object_callbacks.h b/runtime/object_callbacks.h
index ea5e698..9eccb5a 100644
--- a/runtime/object_callbacks.h
+++ b/runtime/object_callbacks.h
@@ -25,9 +25,6 @@
   template<class MirrorType> class HeapReference;
 }  // namespace mirror
 
-// A callback for visiting an object in the heap.
-typedef void (ObjectCallback)(mirror::Object* obj, void* arg);
-
 class IsMarkedVisitor {
  public:
   virtual ~IsMarkedVisitor() {}
diff --git a/runtime/openjdkjvm/OpenjdkJvm.cc b/runtime/openjdkjvm/OpenjdkJvm.cc
index 4560dda..6a8f2ce 100644
--- a/runtime/openjdkjvm/OpenjdkJvm.cc
+++ b/runtime/openjdkjvm/OpenjdkJvm.cc
@@ -432,7 +432,8 @@
       art::ScopedObjectAccess soa(env);
       thread->SetThreadName(name.c_str());
     }
-    thread_list->Resume(thread, art::SuspendReason::kInternal);
+    bool resumed = thread_list->Resume(thread, art::SuspendReason::kInternal);
+    DCHECK(resumed);
   } else if (timed_out) {
     LOG(ERROR) << "Trying to set thread name to '" << name.c_str() << "' failed as the thread "
         "failed to suspend within a generous timeout.";
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 21239fe..3c1311b 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -133,34 +133,34 @@
     return ThreadUtil::GetAllThreads(env, threads_count_ptr, threads_ptr);
   }
 
-  static jvmtiError SuspendThread(jvmtiEnv* env, jthread thread ATTRIBUTE_UNUSED) {
+  static jvmtiError SuspendThread(jvmtiEnv* env, jthread thread) {
     ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_suspend);
-    return ERR(NOT_IMPLEMENTED);
+    return ThreadUtil::SuspendThread(env, thread);
   }
 
   static jvmtiError SuspendThreadList(jvmtiEnv* env,
-                                      jint request_count ATTRIBUTE_UNUSED,
-                                      const jthread* request_list ATTRIBUTE_UNUSED,
-                                      jvmtiError* results ATTRIBUTE_UNUSED) {
+                                      jint request_count,
+                                      const jthread* request_list,
+                                      jvmtiError* results) {
     ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_suspend);
-    return ERR(NOT_IMPLEMENTED);
+    return ThreadUtil::SuspendThreadList(env, request_count, request_list, results);
   }
 
-  static jvmtiError ResumeThread(jvmtiEnv* env, jthread thread ATTRIBUTE_UNUSED) {
+  static jvmtiError ResumeThread(jvmtiEnv* env, jthread thread) {
     ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_suspend);
-    return ERR(NOT_IMPLEMENTED);
+    return ThreadUtil::ResumeThread(env, thread);
   }
 
   static jvmtiError ResumeThreadList(jvmtiEnv* env,
-                                     jint request_count ATTRIBUTE_UNUSED,
-                                     const jthread* request_list ATTRIBUTE_UNUSED,
-                                     jvmtiError* results ATTRIBUTE_UNUSED) {
+                                     jint request_count,
+                                     const jthread* request_list,
+                                     jvmtiError* results) {
     ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_suspend);
-    return ERR(NOT_IMPLEMENTED);
+    return ThreadUtil::ResumeThreadList(env, request_count, request_list, results);
   }
 
   static jvmtiError StopThread(jvmtiEnv* env,
@@ -913,12 +913,12 @@
   }
 
   static jvmtiError GetBytecodes(jvmtiEnv* env,
-                                 jmethodID method ATTRIBUTE_UNUSED,
-                                 jint* bytecode_count_ptr ATTRIBUTE_UNUSED,
-                                 unsigned char** bytecodes_ptr ATTRIBUTE_UNUSED) {
+                                 jmethodID method,
+                                 jint* bytecode_count_ptr,
+                                 unsigned char** bytecodes_ptr) {
     ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_bytecodes);
-    return ERR(NOT_IMPLEMENTED);
+    return MethodUtil::GetBytecodes(env, method, bytecode_count_ptr, bytecodes_ptr);
   }
 
   static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr) {
@@ -1208,6 +1208,23 @@
       return error;
     }
 
+    error = add_extension(
+        reinterpret_cast<jvmtiExtensionFunction>(AllocUtil::GetGlobalJvmtiAllocationState),
+        "com.android.art.alloc.get_global_jvmti_allocation_state",
+        "Returns the total amount of memory currently allocated by all jvmtiEnvs through the"
+        " 'Allocate' jvmti function. This does not include any memory that has been deallocated"
+        " through the 'Deallocate' function. This number is approximate and might not correspond"
+        " exactly to the sum of the sizes of all not freed allocations.",
+        1,
+        {                                                          // NOLINT [whitespace/braces] [4]
+            { "currently_allocated", JVMTI_KIND_OUT, JVMTI_TYPE_JLONG, false},
+        },
+        1,
+        { ERR(NULL_POINTER) });
+    if (error != ERR(NONE)) {
+      return error;
+    }
+
     // Copy into output buffer.
 
     *extension_count_ptr = ext_vector.size();
@@ -1481,10 +1498,11 @@
 
   static jvmtiError DisposeEnvironment(jvmtiEnv* env) {
     ENSURE_VALID_ENV(env);
-    gEventHandler.RemoveArtJvmTiEnv(ArtJvmTiEnv::AsArtJvmTiEnv(env));
-    art::Runtime::Current()->RemoveSystemWeakHolder(
-        ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table.get());
-    delete env;
+    ArtJvmTiEnv* tienv = ArtJvmTiEnv::AsArtJvmTiEnv(env);
+    gEventHandler.RemoveArtJvmTiEnv(tienv);
+    art::Runtime::Current()->RemoveSystemWeakHolder(tienv->object_tag_table.get());
+    ThreadUtil::RemoveEnvironment(tienv);
+    delete tienv;
     return OK;
   }
 
@@ -1654,6 +1672,7 @@
 }
 
 extern const jvmtiInterface_1 gJvmtiInterface;
+
 ArtJvmTiEnv::ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler)
     : art_vm(runtime),
       local_data(nullptr),
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 2d5d527..4d5bb95 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -216,7 +216,7 @@
     .can_tag_objects                                 = 1,
     .can_generate_field_modification_events          = 1,
     .can_generate_field_access_events                = 1,
-    .can_get_bytecodes                               = 0,
+    .can_get_bytecodes                               = 1,
     .can_get_synthetic_attribute                     = 1,
     .can_get_owned_monitor_info                      = 0,
     .can_get_current_contended_monitor               = 0,
@@ -233,7 +233,7 @@
     .can_generate_exception_events                   = 0,
     .can_generate_frame_pop_events                   = 0,
     .can_generate_breakpoint_events                  = 1,
-    .can_suspend                                     = 0,
+    .can_suspend                                     = 1,
     .can_redefine_any_class                          = 0,
     .can_get_current_thread_cpu_time                 = 0,
     .can_get_thread_cpu_time                         = 0,
diff --git a/runtime/openjdkjvmti/fixed_up_dex_file.cc b/runtime/openjdkjvmti/fixed_up_dex_file.cc
index 29aebae..5bfa5ca 100644
--- a/runtime/openjdkjvmti/fixed_up_dex_file.cc
+++ b/runtime/openjdkjvmti/fixed_up_dex_file.cc
@@ -45,12 +45,7 @@
       dex_file->CalculateChecksum();
 }
 
-// TODO This is more complicated then it seems like it should be.
-// The fact we don't keep around the data of where in the flat binary log of dex-quickening changes
-// each dex file starts means we need to search for it. Since JVMTI is the exception though we are
-// not going to put in the effort to optimize for it.
-static void DoDexUnquicken(const art::DexFile& new_dex_file,
-                           const art::DexFile& original_dex_file)
+static void DoDexUnquicken(const art::DexFile& new_dex_file, const art::DexFile& original_dex_file)
     REQUIRES_SHARED(art::Locks::mutator_lock_) {
   const art::OatDexFile* oat_dex = original_dex_file.GetOatDexFile();
   if (oat_dex == nullptr) {
@@ -61,57 +56,10 @@
     return;
   }
   const art::VdexFile* vdex = oat_file->GetVdexFile();
-  if (vdex == nullptr || vdex->GetQuickeningInfo().size() == 0) {
+  if (vdex == nullptr) {
     return;
   }
-  const art::ArrayRef<const uint8_t> quickening_info(vdex->GetQuickeningInfo());
-  const uint8_t* quickening_info_ptr = quickening_info.data();
-  for (const art::OatDexFile* cur_oat_dex : oat_file->GetOatDexFiles()) {
-    std::string error;
-    std::unique_ptr<const art::DexFile> cur_dex_file(cur_oat_dex->OpenDexFile(&error));
-    DCHECK(cur_dex_file.get() != nullptr);
-    // Is this the dex file we are looking for?
-    if (UNLIKELY(cur_dex_file->Begin() == original_dex_file.Begin())) {
-      // Simple sanity check.
-      CHECK_EQ(new_dex_file.NumClassDefs(), original_dex_file.NumClassDefs());
-      for (uint32_t i = 0; i < new_dex_file.NumClassDefs(); ++i) {
-        const art::DexFile::ClassDef& class_def = new_dex_file.GetClassDef(i);
-        const uint8_t* class_data = new_dex_file.GetClassData(class_def);
-        if (class_data == nullptr) {
-          continue;
-        }
-        for (art::ClassDataItemIterator it(new_dex_file, class_data); it.HasNext(); it.Next()) {
-          if (it.IsAtMethod() && it.GetMethodCodeItem() != nullptr) {
-            uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
-            quickening_info_ptr += sizeof(uint32_t);
-            art::optimizer::ArtDecompileDEX(
-                *it.GetMethodCodeItem(),
-                art::ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
-                /*decompile_return_instruction*/true);
-            quickening_info_ptr += quickening_size;
-          }
-        }
-      }
-      // We don't need to bother looking through the rest of the dex-files.
-      break;
-    } else {
-      // Not the dex file we want. Skip over all the quickening info for all its classes.
-      for (uint32_t i = 0; i < cur_dex_file->NumClassDefs(); ++i) {
-        const art::DexFile::ClassDef& class_def = cur_dex_file->GetClassDef(i);
-        const uint8_t* class_data = cur_dex_file->GetClassData(class_def);
-        if (class_data == nullptr) {
-          continue;
-        }
-        for (art::ClassDataItemIterator it(*cur_dex_file, class_data); it.HasNext(); it.Next()) {
-          if (it.IsAtMethod() && it.GetMethodCodeItem() != nullptr) {
-            uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
-            quickening_info_ptr += sizeof(uint32_t);
-            quickening_info_ptr += quickening_size;
-          }
-        }
-      }
-    }
-  }
+  vdex->FullyUnquickenDexFile(new_dex_file, original_dex_file);
 }
 
 std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& original) {
diff --git a/runtime/openjdkjvmti/jvmti_allocator.h b/runtime/openjdkjvmti/jvmti_allocator.h
index 1225c14..44b1cb1 100644
--- a/runtime/openjdkjvmti/jvmti_allocator.h
+++ b/runtime/openjdkjvmti/jvmti_allocator.h
@@ -36,6 +36,8 @@
 #include "base/macros.h"
 #include "jvmti.h"
 
+#include "ti_allocator.h"
+
 namespace openjdkjvmti {
 
 template <typename T> class JvmtiAllocator;
@@ -53,6 +55,7 @@
   };
 
   explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {}
+  explicit JvmtiAllocator() : env_(nullptr) {}
 
   template <typename U>
   JvmtiAllocator(const JvmtiAllocator<U>& other)  // NOLINT, implicit
@@ -89,6 +92,7 @@
   };
 
   explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {}
+  explicit JvmtiAllocator() : env_(nullptr) {}
 
   template <typename U>
   JvmtiAllocator(const JvmtiAllocator<U>& other)  // NOLINT, implicit
@@ -108,8 +112,8 @@
   pointer allocate(size_type n, JvmtiAllocator<void>::pointer hint ATTRIBUTE_UNUSED = nullptr) {
     DCHECK_LE(n, max_size());
     if (env_ == nullptr) {
-      T* result = reinterpret_cast<T*>(malloc(n * sizeof(T)));
-      CHECK(result != nullptr || n == 0u);  // Abort if malloc() fails.
+      T* result = reinterpret_cast<T*>(AllocUtil::AllocateImpl(n * sizeof(T)));
+      CHECK(result != nullptr || n == 0u);  // Abort if AllocateImpl() fails.
       return result;
     } else {
       unsigned char* result;
@@ -120,7 +124,7 @@
   }
   void deallocate(pointer p, size_type n ATTRIBUTE_UNUSED) {
     if (env_ == nullptr) {
-      free(p);
+      AllocUtil::DeallocateImpl(reinterpret_cast<unsigned char*>(p));
     } else {
       jvmtiError dealloc_error = env_->Deallocate(reinterpret_cast<unsigned char*>(p));
       CHECK(dealloc_error == JVMTI_ERROR_NONE);
diff --git a/runtime/openjdkjvmti/jvmti_weak_table.h b/runtime/openjdkjvmti/jvmti_weak_table.h
index 01c24b1..a5175a4 100644
--- a/runtime/openjdkjvmti/jvmti_weak_table.h
+++ b/runtime/openjdkjvmti/jvmti_weak_table.h
@@ -40,6 +40,7 @@
 #include "gc_root-inl.h"
 #include "globals.h"
 #include "jvmti.h"
+#include "jvmti_allocator.h"
 #include "mirror/object.h"
 #include "thread-current-inl.h"
 
@@ -191,7 +192,7 @@
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(allow_disallow_lock_);
 
-  template <typename Storage, class Allocator = std::allocator<T>>
+  template <typename Storage, class Allocator = JvmtiAllocator<T>>
   struct ReleasableContainer;
 
   struct HashGcRoot {
@@ -209,10 +210,12 @@
     }
   };
 
+  using TagAllocator = JvmtiAllocator<std::pair<const art::GcRoot<art::mirror::Object>, T>>;
   std::unordered_map<art::GcRoot<art::mirror::Object>,
                      T,
                      HashGcRoot,
-                     EqGcRoot> tagged_objects_
+                     EqGcRoot,
+                     TagAllocator> tagged_objects_
       GUARDED_BY(allow_disallow_lock_)
       GUARDED_BY(art::Locks::mutator_lock_);
   // To avoid repeatedly scanning the whole table, remember if we did that since the last sweep.
diff --git a/runtime/openjdkjvmti/ti_allocator.cc b/runtime/openjdkjvmti/ti_allocator.cc
index 603a43f..575558d 100644
--- a/runtime/openjdkjvmti/ti_allocator.cc
+++ b/runtime/openjdkjvmti/ti_allocator.cc
@@ -31,23 +31,31 @@
 
 #include "ti_allocator.h"
 
+#if defined(__APPLE__)
+// Apple doesn't have malloc.h. Just give this function a non-functional definition.
+#define malloc_usable_size(P) 0
+#else
+#include <malloc.h>
+#endif
+
+#include <atomic>
+
 #include "art_jvmti.h"
-#include "art_method-inl.h"
 #include "base/enums.h"
-#include "dex_file_annotations.h"
-#include "events-inl.h"
-#include "jni_internal.h"
-#include "mirror/object_array-inl.h"
-#include "modifiers.h"
-#include "runtime_callbacks.h"
-#include "scoped_thread_state_change-inl.h"
-#include "ScopedLocalRef.h"
-#include "thread-current-inl.h"
-#include "thread_list.h"
-#include "ti_phase.h"
 
 namespace openjdkjvmti {
 
+std::atomic<jlong> AllocUtil::allocated;
+
+jvmtiError AllocUtil::GetGlobalJvmtiAllocationState(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                                    jlong* allocated_ptr) {
+  if (allocated_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+  *allocated_ptr = allocated.load();
+  return OK;
+}
+
 jvmtiError AllocUtil::Allocate(jvmtiEnv* env ATTRIBUTE_UNUSED,
                                jlong size,
                                unsigned char** mem_ptr) {
@@ -57,15 +65,31 @@
     *mem_ptr = nullptr;
     return OK;
   }
-  *mem_ptr = static_cast<unsigned char*>(malloc(size));
-  return (*mem_ptr != nullptr) ? OK : ERR(OUT_OF_MEMORY);
+  *mem_ptr = AllocateImpl(size);
+  if (UNLIKELY(*mem_ptr == nullptr)) {
+    return ERR(OUT_OF_MEMORY);
+  }
+  return OK;
+}
+
+unsigned char* AllocUtil::AllocateImpl(jlong size) {
+  unsigned char* ret = size != 0 ? reinterpret_cast<unsigned char*>(malloc(size)) : nullptr;
+  if (LIKELY(ret != nullptr)) {
+    allocated += malloc_usable_size(ret);
+  }
+  return ret;
 }
 
 jvmtiError AllocUtil::Deallocate(jvmtiEnv* env ATTRIBUTE_UNUSED, unsigned char* mem) {
+  DeallocateImpl(mem);
+  return OK;
+}
+
+void AllocUtil::DeallocateImpl(unsigned char* mem) {
   if (mem != nullptr) {
+    allocated -= malloc_usable_size(mem);
     free(mem);
   }
-  return OK;
 }
 
 }  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_allocator.h b/runtime/openjdkjvmti/ti_allocator.h
index 7f1aa6d..35575c3 100644
--- a/runtime/openjdkjvmti/ti_allocator.h
+++ b/runtime/openjdkjvmti/ti_allocator.h
@@ -35,12 +35,28 @@
 #include "jni.h"
 #include "jvmti.h"
 
+#include <atomic>
+#include <memory>
+
 namespace openjdkjvmti {
 
+template<typename T>
+class JvmtiAllocator;
+
 class AllocUtil {
  public:
   static jvmtiError Allocate(jvmtiEnv* env, jlong size, unsigned char** mem_ptr);
   static jvmtiError Deallocate(jvmtiEnv* env, unsigned char* mem);
+  static jvmtiError GetGlobalJvmtiAllocationState(jvmtiEnv* env, jlong* total_allocated);
+
+ private:
+  static void DeallocateImpl(unsigned char* mem);
+  static unsigned char* AllocateImpl(jlong size);
+
+  static std::atomic<jlong> allocated;
+
+  template <typename T>
+  friend class JvmtiAllocator;
 };
 
 }  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index b8e7955..99dfcfe 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -46,6 +46,7 @@
 #include "events-inl.h"
 #include "fixed_up_dex_file.h"
 #include "gc/heap.h"
+#include "gc/heap-visit-objects-inl.h"
 #include "gc_root.h"
 #include "handle.h"
 #include "jni_env_ext-inl.h"
@@ -544,21 +545,15 @@
         LOG(FATAL) << "Unreachable";
       }
 
-      static void AllObjectsCallback(art::mirror::Object* obj, void* arg)
-          REQUIRES_SHARED(art::Locks::mutator_lock_) {
-        HeapFixupVisitor* hfv = reinterpret_cast<HeapFixupVisitor*>(arg);
-
-        // Visit references, not native roots.
-        obj->VisitReferences<false>(*hfv, *hfv);
-      }
-
      private:
       const art::mirror::Class* input_;
       art::mirror::Class* output_;
     };
     HeapFixupVisitor hfv(input, output);
-    art::Runtime::Current()->GetHeap()->VisitObjectsPaused(HeapFixupVisitor::AllObjectsCallback,
-                                                           &hfv);
+    auto object_visitor = [&](art::mirror::Object* obj) {
+      obj->VisitReferences<false>(hfv, hfv);  // Visit references, not native roots.
+    };
+    art::Runtime::Current()->GetHeap()->VisitObjectsPaused(object_visitor);
   }
 
   // A set of all the temp classes we have handed out. We have to fix up references to these.
diff --git a/runtime/openjdkjvmti/ti_heap.cc b/runtime/openjdkjvmti/ti_heap.cc
index 29658d9..91fdaca 100644
--- a/runtime/openjdkjvmti/ti_heap.cc
+++ b/runtime/openjdkjvmti/ti_heap.cc
@@ -22,6 +22,7 @@
 #include "base/mutex.h"
 #include "class_linker.h"
 #include "gc/heap.h"
+#include "gc/heap-visit-objects-inl.h"
 #include "gc_root-inl.h"
 #include "java_frame_root_info.h"
 #include "jni_env_ext.h"
@@ -30,7 +31,6 @@
 #include "mirror/class.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
-#include "object_callbacks.h"
 #include "object_tagging.h"
 #include "obj_ptr-inl.h"
 #include "primitive.h"
@@ -653,33 +653,25 @@
   art::Runtime::Current()->RemoveSystemWeakHolder(&gIndexCachingTable);
 }
 
-template <typename Callback>
-struct IterateThroughHeapData {
-  IterateThroughHeapData(Callback _cb,
-                         ObjectTagTable* _tag_table,
-                         jvmtiEnv* _env,
-                         art::ObjPtr<art::mirror::Class> klass,
-                         jint _heap_filter,
-                         const jvmtiHeapCallbacks* _callbacks,
-                         const void* _user_data)
-      : cb(_cb),
-        tag_table(_tag_table),
-        heap_filter(_heap_filter),
-        filter_klass(klass),
-        env(_env),
-        callbacks(_callbacks),
-        user_data(_user_data),
-        stop_reports(false) {
+template <typename T>
+static jvmtiError DoIterateThroughHeap(T fn,
+                                       jvmtiEnv* env,
+                                       ObjectTagTable* tag_table,
+                                       jint heap_filter_int,
+                                       jclass klass,
+                                       const jvmtiHeapCallbacks* callbacks,
+                                       const void* user_data) {
+  if (callbacks == nullptr) {
+    return ERR(NULL_POINTER);
   }
 
-  static void ObjectCallback(art::mirror::Object* obj, void* arg)
-      REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    IterateThroughHeapData* ithd = reinterpret_cast<IterateThroughHeapData*>(arg);
-    ithd->ObjectCallback(obj);
-  }
+  art::Thread* self = art::Thread::Current();
+  art::ScopedObjectAccess soa(self);      // Now we know we have the shared lock.
 
-  void ObjectCallback(art::mirror::Object* obj)
-      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  bool stop_reports = false;
+  const HeapFilter heap_filter(heap_filter_int);
+  art::ObjPtr<art::mirror::Class> filter_klass = soa.Decode<art::mirror::Class>(klass);
+  auto visitor = [&](art::mirror::Object* obj) REQUIRES_SHARED(art::Locks::mutator_lock_) {
     // Early return, as we can't really stop visiting.
     if (stop_reports) {
       return;
@@ -713,7 +705,7 @@
     }
 
     jlong saved_tag = tag;
-    jint ret = cb(obj, callbacks, class_tag, size, &tag, length, const_cast<void*>(user_data));
+    jint ret = fn(obj, callbacks, class_tag, size, &tag, length, const_cast<void*>(user_data));
 
     if (tag != saved_tag) {
       tag_table->Set(obj, tag);
@@ -734,44 +726,8 @@
     if (!stop_reports) {
       stop_reports = ReportPrimitiveField::Report(obj, tag_table, callbacks, user_data);
     }
-  }
-
-  Callback cb;
-  ObjectTagTable* tag_table;
-  const HeapFilter heap_filter;
-  art::ObjPtr<art::mirror::Class> filter_klass;
-  jvmtiEnv* env;
-  const jvmtiHeapCallbacks* callbacks;
-  const void* user_data;
-
-  bool stop_reports;
-};
-
-template <typename T>
-static jvmtiError DoIterateThroughHeap(T fn,
-                                       jvmtiEnv* env,
-                                       ObjectTagTable* tag_table,
-                                       jint heap_filter,
-                                       jclass klass,
-                                       const jvmtiHeapCallbacks* callbacks,
-                                       const void* user_data) {
-  if (callbacks == nullptr) {
-    return ERR(NULL_POINTER);
-  }
-
-  art::Thread* self = art::Thread::Current();
-  art::ScopedObjectAccess soa(self);      // Now we know we have the shared lock.
-
-  using Iterator = IterateThroughHeapData<T>;
-  Iterator ithd(fn,
-                tag_table,
-                env,
-                soa.Decode<art::mirror::Class>(klass),
-                heap_filter,
-                callbacks,
-                user_data);
-
-  art::Runtime::Current()->GetHeap()->VisitObjects(Iterator::ObjectCallback, &ithd);
+  };
+  art::Runtime::Current()->GetHeap()->VisitObjects(visitor);
 
   return ERR(NONE);
 }
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index beb639e..9b5b964 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -91,6 +91,40 @@
   runtime->GetRuntimeCallbacks()->RemoveMethodCallback(&gMethodCallback);
 }
 
+jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env,
+                                    jmethodID method,
+                                    jint* size_ptr,
+                                    unsigned char** bytecode_ptr) {
+  if (method == nullptr) {
+    return ERR(INVALID_METHODID);
+  }
+  art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+  if (art_method->IsNative()) {
+    return ERR(NATIVE_METHOD);
+  }
+
+  if (size_ptr == nullptr || bytecode_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  const art::DexFile::CodeItem* code_item = art_method->GetCodeItem();
+  if (code_item == nullptr) {
+    *size_ptr = 0;
+    *bytecode_ptr = nullptr;
+    return OK;
+  }
+  // 2 bytes per instruction for dex code.
+  *size_ptr = code_item->insns_size_in_code_units_ * 2;
+  jvmtiError err = env->Allocate(*size_ptr, bytecode_ptr);
+  if (err != OK) {
+    return err;
+  }
+  memcpy(*bytecode_ptr, code_item->insns_, *size_ptr);
+  return OK;
+}
+
 jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
                                         jmethodID method,
                                         jint* size_ptr) {
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
index cc161c8..d95a81b 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -44,6 +44,11 @@
   static void Register(EventHandler* event_handler);
   static void Unregister();
 
+  static jvmtiError GetBytecodes(jvmtiEnv* env,
+                                 jmethodID method,
+                                 jint* count_ptr,
+                                 unsigned char** bytecodes);
+
   static jvmtiError GetArgumentsSize(jvmtiEnv* env, jmethodID method, jint* size_ptr);
 
   static jvmtiError GetMaxLocals(jvmtiEnv* env, jmethodID method, jint* max_ptr);
diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc
index 2cc2a26..fe0e3bb 100644
--- a/runtime/openjdkjvmti/ti_thread.cc
+++ b/runtime/openjdkjvmti/ti_thread.cc
@@ -159,6 +159,17 @@
   return ERR(NONE);
 }
 
+static art::Thread* GetNativeThreadLocked(jthread thread,
+                                          const art::ScopedObjectAccessAlreadyRunnable& soa)
+    REQUIRES_SHARED(art::Locks::mutator_lock_)
+    REQUIRES(art::Locks::thread_list_lock_) {
+  if (thread == nullptr) {
+    return art::Thread::Current();
+  }
+
+  return art::Thread::FromManagedThread(soa, thread);
+}
+
 // Get the native thread. The spec says a null object denotes the current thread.
 static art::Thread* GetNativeThread(jthread thread,
                                     const art::ScopedObjectAccessAlreadyRunnable& soa)
@@ -289,35 +300,51 @@
   return ERR(NONE);
 }
 
-// Return the thread's (or current thread, if null) thread state. Return kStarting in case
-// there's no native counterpart (thread hasn't been started, yet, or is dead).
-static art::ThreadState GetNativeThreadState(jthread thread,
-                                             const art::ScopedObjectAccessAlreadyRunnable& soa,
-                                             art::Thread** native_thread)
-    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+struct InternalThreadState {
+  art::Thread* native_thread;
+  art::ThreadState art_state;
+  int thread_user_code_suspend_count;
+};
+
+// Return the thread's (or current thread, if null) thread state.
+static InternalThreadState GetNativeThreadState(jthread thread,
+                                                const art::ScopedObjectAccessAlreadyRunnable& soa)
+    REQUIRES_SHARED(art::Locks::mutator_lock_)
+    REQUIRES(art::Locks::user_code_suspension_lock_) {
   art::Thread* self = nullptr;
-  art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
+  art::MutexLock tll_mu(soa.Self(), *art::Locks::thread_list_lock_);
   if (thread == nullptr) {
     self = art::Thread::Current();
   } else {
     self = art::Thread::FromManagedThread(soa, thread);
   }
-  *native_thread = self;
+  InternalThreadState thread_state = {};
+  art::MutexLock tscl_mu(soa.Self(), *art::Locks::thread_suspend_count_lock_);
+  thread_state.native_thread = self;
   if (self == nullptr || self->IsStillStarting()) {
-    return art::ThreadState::kStarting;
+    thread_state.art_state = art::ThreadState::kStarting;
+    thread_state.thread_user_code_suspend_count = 0;
+  } else {
+    thread_state.art_state = self->GetState();
+    thread_state.thread_user_code_suspend_count = self->GetUserCodeSuspendCount();
   }
-  return self->GetState();
+  return thread_state;
 }
 
-static jint GetJvmtiThreadStateFromInternal(art::ThreadState internal_thread_state) {
+static jint GetJvmtiThreadStateFromInternal(const InternalThreadState& state) {
+  art::ThreadState internal_thread_state = state.art_state;
   jint jvmti_state = JVMTI_THREAD_STATE_ALIVE;
 
-  if (internal_thread_state == art::ThreadState::kSuspended) {
+  if (state.thread_user_code_suspend_count != 0) {
     jvmti_state |= JVMTI_THREAD_STATE_SUSPENDED;
     // Note: We do not have data about the previous state. Otherwise we should load the previous
     //       state here.
   }
 
+  if (state.native_thread->IsInterrupted()) {
+    jvmti_state |= JVMTI_THREAD_STATE_INTERRUPTED;
+  }
+
   if (internal_thread_state == art::ThreadState::kNative) {
     jvmti_state |= JVMTI_THREAD_STATE_IN_NATIVE;
   }
@@ -354,8 +381,8 @@
   return jvmti_state;
 }
 
-static jint GetJavaStateFromInternal(art::ThreadState internal_thread_state) {
-  switch (internal_thread_state) {
+static jint GetJavaStateFromInternal(const InternalThreadState& state) {
+  switch (state.art_state) {
     case art::ThreadState::kTerminated:
       return JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED;
 
@@ -397,6 +424,14 @@
   UNREACHABLE();
 }
 
+// Suspends the current thread if it has any suspend requests on it.
+static void SuspendCheck(art::Thread* self)
+    REQUIRES(!art::Locks::mutator_lock_, !art::Locks::user_code_suspension_lock_) {
+  art::ScopedObjectAccess soa(self);
+  // Really this is only needed if we are in FastJNI and actually have the mutator_lock_ already.
+  self->FullSuspendCheck();
+}
+
 jvmtiError ThreadUtil::GetThreadState(jvmtiEnv* env ATTRIBUTE_UNUSED,
                                       jthread thread,
                                       jint* thread_state_ptr) {
@@ -404,16 +439,35 @@
     return ERR(NULL_POINTER);
   }
 
-  art::ScopedObjectAccess soa(art::Thread::Current());
-  art::Thread* native_thread = nullptr;
-  art::ThreadState internal_thread_state = GetNativeThreadState(thread, soa, &native_thread);
+  art::Thread* self = art::Thread::Current();
+  InternalThreadState state = {};
+  // Loop since we need to bail out and try again if we would end up getting suspended while holding
+  // the user_code_suspension_lock_ due to a SuspendReason::kForUserCode. In this situation we
+  // release the lock, wait to get resumed and try again.
+  do {
+    SuspendCheck(self);
+    art::MutexLock ucsl_mu(self, *art::Locks::user_code_suspension_lock_);
+    {
+      art::MutexLock tscl_mu(self, *art::Locks::thread_suspend_count_lock_);
+      if (self->GetUserCodeSuspendCount() != 0) {
+        // Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_
+        // by a user-code suspension. We retry and do another SuspendCheck to clear this.
+        continue;
+      }
+    }
+    art::ScopedObjectAccess soa(self);
+    state = GetNativeThreadState(thread, soa);
+    break;
+  } while (true);
 
-  if (internal_thread_state == art::ThreadState::kStarting) {
+  if (state.art_state == art::ThreadState::kStarting) {
     if (thread == nullptr) {
       // No native thread, and no Java thread? We must be starting up. Report as wrong phase.
       return ERR(WRONG_PHASE);
     }
 
+    art::ScopedObjectAccess soa(self);
+
     // Need to read the Java "started" field to know whether this is starting or terminated.
     art::ObjPtr<art::mirror::Object> peer = soa.Decode<art::mirror::Object>(thread);
     art::ObjPtr<art::mirror::Class> klass = peer->GetClass();
@@ -426,18 +480,16 @@
     *thread_state_ptr = started ? kTerminatedState : kStartedState;
     return ERR(NONE);
   }
-  DCHECK(native_thread != nullptr);
+  DCHECK(state.native_thread != nullptr);
 
   // Translate internal thread state to JVMTI and Java state.
-  jint jvmti_state = GetJvmtiThreadStateFromInternal(internal_thread_state);
-  if (native_thread->IsInterrupted()) {
-    jvmti_state |= JVMTI_THREAD_STATE_INTERRUPTED;
-  }
+  jint jvmti_state = GetJvmtiThreadStateFromInternal(state);
 
   // Java state is derived from nativeGetState.
-  // Note: Our implementation assigns "runnable" to suspended. As such, we will have slightly
-  //       different mask. However, this is for consistency with the Java view.
-  jint java_state = GetJavaStateFromInternal(internal_thread_state);
+  // TODO: Our implementation assigns "runnable" to suspended. As such, we will have slightly
+  //       different mask if a thread got suspended due to user-code. However, this is for
+  //       consistency with the Java view.
+  jint java_state = GetJavaStateFromInternal(state);
 
   *thread_state_ptr = jvmti_state | java_state;
 
@@ -492,40 +544,82 @@
   return ERR(NONE);
 }
 
-jvmtiError ThreadUtil::SetThreadLocalStorage(jvmtiEnv* env ATTRIBUTE_UNUSED,
-                                             jthread thread,
-                                             const void* data) {
-  art::ScopedObjectAccess soa(art::Thread::Current());
-  art::Thread* self = GetNativeThread(thread, soa);
-  if (self == nullptr && thread == nullptr) {
+// The struct that we store in the art::Thread::custom_tls_ that maps the jvmtiEnvs to the data
+// stored with that thread. This is needed since different jvmtiEnvs are not supposed to share TLS
+// data but we only have a single slot in Thread objects to store data.
+struct JvmtiGlobalTLSData {
+  std::unordered_map<jvmtiEnv*, const void*> data GUARDED_BY(art::Locks::thread_list_lock_);
+};
+
+static void RemoveTLSData(art::Thread* target, void* ctx) REQUIRES(art::Locks::thread_list_lock_) {
+  jvmtiEnv* env = reinterpret_cast<jvmtiEnv*>(ctx);
+  art::Locks::thread_list_lock_->AssertHeld(art::Thread::Current());
+  JvmtiGlobalTLSData* global_tls = reinterpret_cast<JvmtiGlobalTLSData*>(target->GetCustomTLS());
+  if (global_tls != nullptr) {
+    global_tls->data.erase(env);
+  }
+}
+
+void ThreadUtil::RemoveEnvironment(jvmtiEnv* env) {
+  art::Thread* self = art::Thread::Current();
+  art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+  art::ThreadList* list = art::Runtime::Current()->GetThreadList();
+  list->ForEach(RemoveTLSData, env);
+}
+
+jvmtiError ThreadUtil::SetThreadLocalStorage(jvmtiEnv* env, jthread thread, const void* data) {
+  art::Thread* self = art::Thread::Current();
+  art::ScopedObjectAccess soa(self);
+  art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+  art::Thread* target = GetNativeThreadLocked(thread, soa);
+  if (target == nullptr && thread == nullptr) {
     return ERR(INVALID_THREAD);
   }
-  if (self == nullptr) {
+  if (target == nullptr) {
     return ERR(THREAD_NOT_ALIVE);
   }
 
-  self->SetCustomTLS(data);
+  JvmtiGlobalTLSData* global_tls = reinterpret_cast<JvmtiGlobalTLSData*>(target->GetCustomTLS());
+  if (global_tls == nullptr) {
+    target->SetCustomTLS(new JvmtiGlobalTLSData);
+    global_tls = reinterpret_cast<JvmtiGlobalTLSData*>(target->GetCustomTLS());
+  }
+
+  global_tls->data[env] = data;
 
   return ERR(NONE);
 }
 
-jvmtiError ThreadUtil::GetThreadLocalStorage(jvmtiEnv* env ATTRIBUTE_UNUSED,
+jvmtiError ThreadUtil::GetThreadLocalStorage(jvmtiEnv* env,
                                              jthread thread,
                                              void** data_ptr) {
   if (data_ptr == nullptr) {
     return ERR(NULL_POINTER);
   }
 
-  art::ScopedObjectAccess soa(art::Thread::Current());
-  art::Thread* self = GetNativeThread(thread, soa);
-  if (self == nullptr && thread == nullptr) {
+  art::Thread* self = art::Thread::Current();
+  art::ScopedObjectAccess soa(self);
+  art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+  art::Thread* target = GetNativeThreadLocked(thread, soa);
+  if (target == nullptr && thread == nullptr) {
     return ERR(INVALID_THREAD);
   }
-  if (self == nullptr) {
+  if (target == nullptr) {
     return ERR(THREAD_NOT_ALIVE);
   }
 
-  *data_ptr = const_cast<void*>(self->GetCustomTLS());
+  JvmtiGlobalTLSData* global_tls = reinterpret_cast<JvmtiGlobalTLSData*>(target->GetCustomTLS());
+  if (global_tls == nullptr) {
+    *data_ptr = nullptr;
+    return OK;
+  }
+  auto it = global_tls->data.find(env);
+  if (it != global_tls->data.end()) {
+    *data_ptr = const_cast<void*>(it->second);
+  } else {
+    *data_ptr = nullptr;
+  }
+
   return ERR(NONE);
 }
 
@@ -605,4 +699,192 @@
   return ERR(NONE);
 }
 
+jvmtiError ThreadUtil::SuspendOther(art::Thread* self,
+                                    jthread target_jthread,
+                                    art::Thread* target) {
+  // Loop since we need to bail out and try again if we would end up getting suspended while holding
+  // the user_code_suspension_lock_ due to a SuspendReason::kForUserCode. In this situation we
+  // release the lock, wait to get resumed and try again.
+  do {
+    // Suspend ourself if we have any outstanding suspends. This is so we won't suspend due to
+    // another SuspendThread in the middle of suspending something else potentially causing a
+    // deadlock. We need to do this in the loop because if we ended up back here then we had
+    // outstanding SuspendReason::kForUserCode suspensions and we should wait for them to be cleared
+    // before continuing.
+    SuspendCheck(self);
+    art::MutexLock mu(self, *art::Locks::user_code_suspension_lock_);
+    {
+      art::MutexLock thread_list_mu(self, *art::Locks::thread_suspend_count_lock_);
+      // Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_ by
+      // a user-code suspension. We retry and do another SuspendCheck to clear this.
+      if (self->GetUserCodeSuspendCount() != 0) {
+        continue;
+      } else if (target->GetUserCodeSuspendCount() != 0) {
+        return ERR(THREAD_SUSPENDED);
+      }
+    }
+    bool timeout = true;
+    while (timeout) {
+      art::ThreadState state = target->GetState();
+      if (state == art::ThreadState::kTerminated || state == art::ThreadState::kStarting) {
+        return ERR(THREAD_NOT_ALIVE);
+      }
+      target = art::Runtime::Current()->GetThreadList()->SuspendThreadByPeer(
+          target_jthread,
+          /* request_suspension */ true,
+          art::SuspendReason::kForUserCode,
+          &timeout);
+      if (target == nullptr && !timeout) {
+        // TODO It would be good to get more information about why exactly the thread failed to
+        // suspend.
+        return ERR(INTERNAL);
+      }
+    }
+    return OK;
+  } while (true);
+  UNREACHABLE();
+}
+
+jvmtiError ThreadUtil::SuspendSelf(art::Thread* self) {
+  CHECK(self == art::Thread::Current());
+  {
+    art::MutexLock mu(self, *art::Locks::user_code_suspension_lock_);
+    art::MutexLock thread_list_mu(self, *art::Locks::thread_suspend_count_lock_);
+    if (self->GetUserCodeSuspendCount() != 0) {
+      // This can only happen if we race with another thread to suspend 'self' and we lose.
+      return ERR(THREAD_SUSPENDED);
+    }
+    // We shouldn't be able to fail this.
+    if (!self->ModifySuspendCount(self, +1, nullptr, art::SuspendReason::kForUserCode)) {
+      // TODO More specific error would be nice.
+      return ERR(INTERNAL);
+    }
+  }
+  // Once we have requested the suspend we actually go to sleep. We need to do this after releasing
+  // the suspend_lock to make sure we can be woken up. This call gains the mutator lock causing us
+  // to go to sleep until we are resumed.
+  SuspendCheck(self);
+  return OK;
+}
+
+jvmtiError ThreadUtil::SuspendThread(jvmtiEnv* env ATTRIBUTE_UNUSED, jthread thread) {
+  art::Thread* self = art::Thread::Current();
+  art::Thread* target;
+  {
+    art::ScopedObjectAccess soa(self);
+    target = GetNativeThread(thread, soa);
+  }
+  if (target == nullptr) {
+    return ERR(INVALID_THREAD);
+  }
+  if (target == self) {
+    return SuspendSelf(self);
+  } else {
+    return SuspendOther(self, thread, target);
+  }
+}
+
+jvmtiError ThreadUtil::ResumeThread(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                    jthread thread) {
+  if (thread == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+  art::Thread* self = art::Thread::Current();
+  art::Thread* target;
+  {
+    // NB This does a SuspendCheck (during thread state change) so we need to make sure we don't
+    // have the 'suspend_lock' locked here.
+    art::ScopedObjectAccess soa(self);
+    target = GetNativeThread(thread, soa);
+  }
+  if (target == nullptr) {
+    return ERR(INVALID_THREAD);
+  } else if (target == self) {
+    // We would have paused until we aren't suspended anymore due to the ScopedObjectAccess so we
+    // can just return THREAD_NOT_SUSPENDED. Unfortunately we cannot do any real DCHECKs about
+    // current state since it's all concurrent.
+    return ERR(THREAD_NOT_SUSPENDED);
+  }
+  // Now that we know we aren't getting suspended ourself (since we have a mutator lock) we lock the
+  // suspend_lock to start suspending.
+  art::MutexLock mu(self, *art::Locks::user_code_suspension_lock_);
+  {
+    // The JVMTI spec requires us to return THREAD_NOT_SUSPENDED if it is alive but we really cannot
+    // tell why resume failed.
+    art::MutexLock thread_list_mu(self, *art::Locks::thread_suspend_count_lock_);
+    if (target->GetUserCodeSuspendCount() == 0) {
+      return ERR(THREAD_NOT_SUSPENDED);
+    }
+  }
+  if (target->GetState() == art::ThreadState::kTerminated) {
+    return ERR(THREAD_NOT_ALIVE);
+  }
+  DCHECK(target != self);
+  if (!art::Runtime::Current()->GetThreadList()->Resume(target, art::SuspendReason::kForUserCode)) {
+    // TODO Give a better error.
+    // This is most likely THREAD_NOT_SUSPENDED but we cannot really be sure.
+    return ERR(INTERNAL);
+  }
+  return OK;
+}
+
+// Suspends all the threads in the list at the same time. Getting this behavior is a little tricky
+// since we can have threads in the list multiple times. This generally doesn't matter unless the
+// current thread is present multiple times. In that case we need to suspend only once and either
+// return the same error code in all the other slots if it failed or return ERR(THREAD_SUSPENDED) if
+// it didn't. We also want to handle the current thread last to make the behavior of the code
+// simpler to understand.
+jvmtiError ThreadUtil::SuspendThreadList(jvmtiEnv* env,
+                                         jint request_count,
+                                         const jthread* threads,
+                                         jvmtiError* results) {
+  if (request_count == 0) {
+    return ERR(ILLEGAL_ARGUMENT);
+  } else if (results == nullptr || threads == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+  // This is the list of the indexes in 'threads' and 'results' that correspond to the currently
+  // running thread. These indexes we need to handle specially since we need to only actually
+  // suspend a single time.
+  std::vector<jint> current_thread_indexes;
+  art::Thread* self = art::Thread::Current();
+  for (jint i = 0; i < request_count; i++) {
+    {
+      art::ScopedObjectAccess soa(self);
+      if (threads[i] == nullptr || GetNativeThread(threads[i], soa) == self) {
+        current_thread_indexes.push_back(i);
+        continue;
+      }
+    }
+    results[i] = env->SuspendThread(threads[i]);
+  }
+  if (!current_thread_indexes.empty()) {
+    jint first_current_thread_index = current_thread_indexes[0];
+    // Suspend self.
+    jvmtiError res = env->SuspendThread(threads[first_current_thread_index]);
+    results[first_current_thread_index] = res;
+    // Fill in the rest of the error values as appropriate.
+    jvmtiError other_results = (res != OK) ? res : ERR(THREAD_SUSPENDED);
+    for (auto it = ++current_thread_indexes.begin(); it != current_thread_indexes.end(); ++it) {
+      results[*it] = other_results;
+    }
+  }
+  return OK;
+}
+
+jvmtiError ThreadUtil::ResumeThreadList(jvmtiEnv* env,
+                                        jint request_count,
+                                        const jthread* threads,
+                                        jvmtiError* results) {
+  if (request_count == 0) {
+    return ERR(ILLEGAL_ARGUMENT);
+  } else if (results == nullptr || threads == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+  for (jint i = 0; i < request_count; i++) {
+    results[i] = env->ResumeThread(threads[i]);
+  }
+  return OK;
+}
+
 }  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_thread.h b/runtime/openjdkjvmti/ti_thread.h
index 939aea7..d07dc06 100644
--- a/runtime/openjdkjvmti/ti_thread.h
+++ b/runtime/openjdkjvmti/ti_thread.h
@@ -35,8 +35,11 @@
 #include "jni.h"
 #include "jvmti.h"
 
+#include "base/mutex.h"
+
 namespace art {
 class ArtField;
+class Thread;
 }  // namespace art
 
 namespace openjdkjvmti {
@@ -51,6 +54,9 @@
   // To be called when it is safe to cache data.
   static void CacheData();
 
+  // Handle a jvmtiEnv going away.
+  static void RemoveEnvironment(jvmtiEnv* env);
+
   static jvmtiError GetAllThreads(jvmtiEnv* env, jint* threads_count_ptr, jthread** threads_ptr);
 
   static jvmtiError GetCurrentThread(jvmtiEnv* env, jthread* thread_ptr);
@@ -68,7 +74,33 @@
                                    const void* arg,
                                    jint priority);
 
+  static jvmtiError SuspendThread(jvmtiEnv* env, jthread thread);
+  static jvmtiError ResumeThread(jvmtiEnv* env, jthread thread);
+
+  static jvmtiError SuspendThreadList(jvmtiEnv* env,
+                                      jint request_count,
+                                      const jthread* threads,
+                                      jvmtiError* results);
+  static jvmtiError ResumeThreadList(jvmtiEnv* env,
+                                     jint request_count,
+                                     const jthread* threads,
+                                     jvmtiError* results);
+
  private:
+  // We need to make sure only one thread tries to suspend threads at a time so we can get the
+  // 'suspend-only-once' behavior the spec requires. Internally, ART considers suspension to be a
+  // counted state, allowing a single thread to be suspended multiple times by different users. This
+  // makes mapping into the JVMTI idea of thread suspension difficult. We have decided to split the
+  // difference and ensure that JVMTI tries to treat suspension as the boolean flag as much as
+  // possible with the suspend/resume methods but only do best effort. On the other hand
+  // GetThreadState will be totally accurate as much as possible. This means that calling
+  // ResumeThread on a thread that has state JVMTI_THREAD_STATE_SUSPENDED will not necessarily
+  // cause the thread to wake up if the thread is suspended for the debugger or gc or something.
+  static jvmtiError SuspendSelf(art::Thread* self)
+      REQUIRES(!art::Locks::mutator_lock_, !art::Locks::user_code_suspension_lock_);
+  static jvmtiError SuspendOther(art::Thread* self, jthread target_jthread, art::Thread* target)
+      REQUIRES(!art::Locks::mutator_lock_, !art::Locks::user_code_suspension_lock_);
+
   static art::ArtField* context_class_loader_;
 };
 
diff --git a/runtime/quicken_info.h b/runtime/quicken_info.h
new file mode 100644
index 0000000..5b72468
--- /dev/null
+++ b/runtime/quicken_info.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_QUICKEN_INFO_H_
+#define ART_RUNTIME_QUICKEN_INFO_H_
+
+#include "dex_instruction.h"
+
+namespace art {
+
+// QuickenInfoTable is a table of 16 bit dex indices. There is one slot fo every instruction that is
+// possibly dequickenable.
+class QuickenInfoTable {
+ public:
+  explicit QuickenInfoTable(const uint8_t* data) : data_(data) {}
+
+  bool IsNull() const {
+    return data_ == nullptr;
+  }
+
+  uint16_t GetData(size_t index) const {
+    return data_[index * 2] | (static_cast<uint16_t>(data_[index * 2 + 1]) << 8);
+  }
+
+  // Returns true if the dex instruction has an index in the table. (maybe dequickenable).
+  static bool NeedsIndexForInstruction(const Instruction* inst) {
+    return inst->IsQuickened() || inst->Opcode() == Instruction::NOP;
+  }
+
+  static size_t NumberOfIndices(size_t bytes) {
+    return bytes / sizeof(uint16_t);
+  }
+
+ private:
+  const uint8_t* const data_;
+
+  DISALLOW_COPY_AND_ASSIGN(QuickenInfoTable);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_QUICKEN_INFO_H_
diff --git a/runtime/safe_map.h b/runtime/safe_map.h
index b54f587..f298691 100644
--- a/runtime/safe_map.h
+++ b/runtime/safe_map.h
@@ -79,6 +79,9 @@
   iterator lower_bound(const K& k) { return map_.lower_bound(k); }
   const_iterator lower_bound(const K& k) const { return map_.lower_bound(k); }
 
+  iterator upper_bound(const K& k) { return map_.upper_bound(k); }
+  const_iterator upper_bound(const K& k) const { return map_.upper_bound(k); }
+
   size_type count(const K& k) const { return map_.count(k); }
 
   // Note that unlike std::map's operator[], this doesn't return a reference to the value.
diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc
index 8c934d5..f0b6ee4 100644
--- a/runtime/signal_catcher.cc
+++ b/runtime/signal_catcher.cc
@@ -168,7 +168,7 @@
   }
 
 #if defined(ART_TARGET_ANDROID)
-  if (!tombstoned_notify_completion(tombstone_fd)) {
+  if (use_tombstoned_stack_trace_fd_ && !tombstoned_notify_completion(tombstone_fd)) {
     LOG(WARNING) << "Unable to notify tombstoned of dump completion.";
   }
 #endif
diff --git a/runtime/suspend_reason.h b/runtime/suspend_reason.h
index 27c4d32..289a1a4 100644
--- a/runtime/suspend_reason.h
+++ b/runtime/suspend_reason.h
@@ -28,6 +28,8 @@
   kInternal,
   // Suspending for debugger (code in Dbg::*, runtime/jdwp/, etc.).
   kForDebugger,
+  // Suspending due to non-runtime, user controlled, code. (For example Thread#Suspend()).
+  kForUserCode,
 };
 
 std::ostream& operator<<(std::ostream& os, const SuspendReason& thread);
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index 95608b5..b5a9626 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -121,10 +121,20 @@
     return false;
   }
   for (int i = kLockLevelCount - 1; i >= 0; --i) {
-    if (i != kMutatorLock && GetHeldMutex(static_cast<LockLevel>(i)) != nullptr) {
+    if (i != kMutatorLock &&
+        i != kUserCodeSuspensionLock &&
+        GetHeldMutex(static_cast<LockLevel>(i)) != nullptr) {
       return false;
     }
   }
+  // Thread autoanalysis isn't able to understand that the GetHeldMutex(...) or AssertHeld means we
+  // have the mutex meaning we need to do this hack.
+  auto is_suspending_for_user_code = [this]() NO_THREAD_SAFETY_ANALYSIS {
+    return tls32_.user_code_suspend_count != 0;
+  };
+  if (GetHeldMutex(kUserCodeSuspensionLock) != nullptr && is_suspending_for_user_code()) {
+    return false;
+  }
   return true;
 }
 
@@ -136,8 +146,9 @@
     if (check_locks) {
       bool bad_mutexes_held = false;
       for (int i = kLockLevelCount - 1; i >= 0; --i) {
-        // We expect no locks except the mutator_lock_ or thread list suspend thread lock.
-        if (i != kMutatorLock) {
+        // We expect no locks except the mutator_lock_. User code suspension lock is OK as long as
+        // we aren't going to be held suspended due to SuspendReason::kForUserCode.
+        if (i != kMutatorLock && i != kUserCodeSuspensionLock) {
           BaseMutex* held_mutex = GetHeldMutex(static_cast<LockLevel>(i));
           if (held_mutex != nullptr) {
             LOG(ERROR) << "holding \"" << held_mutex->GetName()
@@ -146,6 +157,19 @@
           }
         }
       }
+      // Make sure that if we hold the user_code_suspension_lock_ we aren't suspending due to
+      // user_code_suspend_count which would prevent the thread from ever waking up.  Thread
+      // autoanalysis isn't able to understand that the GetHeldMutex(...) or AssertHeld means we
+      // have the mutex meaning we need to do this hack.
+      auto is_suspending_for_user_code = [this]() NO_THREAD_SAFETY_ANALYSIS {
+        return tls32_.user_code_suspend_count != 0;
+      };
+      if (GetHeldMutex(kUserCodeSuspensionLock) != nullptr && is_suspending_for_user_code()) {
+        LOG(ERROR) << "suspending due to user-code while holding \""
+                   << Locks::user_code_suspension_lock_->GetName() << "\"! Thread would never "
+                   << "wake up.";
+        bad_mutexes_held = true;
+      }
       if (gAborting == 0) {
         CHECK(!bad_mutexes_held);
       }
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 36ecd33..004b68e 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1208,6 +1208,15 @@
       Locks::thread_list_lock_->AssertHeld(self);
     }
   }
+  // User code suspensions need to be checked more closely since they originate from code outside of
+  // the runtime's control.
+  if (UNLIKELY(reason == SuspendReason::kForUserCode)) {
+    Locks::user_code_suspension_lock_->AssertHeld(self);
+    if (UNLIKELY(delta + tls32_.user_code_suspend_count < 0)) {
+      LOG(ERROR) << "attempting to modify suspend count in an illegal way.";
+      return false;
+    }
+  }
   if (UNLIKELY(delta < 0 && tls32_.suspend_count <= 0)) {
     UnsafeLogFatalForSuspendCount(self, this);
     return false;
@@ -1241,6 +1250,9 @@
     case SuspendReason::kForDebugger:
       tls32_.debug_suspend_count += delta;
       break;
+    case SuspendReason::kForUserCode:
+      tls32_.user_code_suspend_count += delta;
+      break;
     case SuspendReason::kInternal:
       break;
   }
@@ -2134,6 +2146,10 @@
     ScopedObjectAccess soa(self);
     // We may need to call user-supplied managed code, do this before final clean-up.
     HandleUncaughtExceptions(soa);
+    Runtime* runtime = Runtime::Current();
+    if (runtime != nullptr) {
+      runtime->GetRuntimeCallbacks()->ThreadDeath(self);
+    }
     RemoveFromThreadGroup(soa);
 
     // this.nativePeer = 0;
@@ -2144,11 +2160,6 @@
       jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer)
           ->SetLong<false>(tlsPtr_.opeer, 0);
     }
-    Runtime* runtime = Runtime::Current();
-    if (runtime != nullptr) {
-      runtime->GetRuntimeCallbacks()->ThreadDeath(self);
-    }
-
 
     // Thread.join() is implemented as an Object.wait() on the Thread.lock object. Signal anyone
     // who is waiting.
@@ -2862,6 +2873,7 @@
   DO_THREAD_OFFSET(SelfOffset<ptr_size>(), "self")
   DO_THREAD_OFFSET(StackEndOffset<ptr_size>(), "stack_end")
   DO_THREAD_OFFSET(ThinLockIdOffset<ptr_size>(), "thin_lock_thread_id")
+  DO_THREAD_OFFSET(IsGcMarkingOffset<ptr_size>(), "is_gc_marking")
   DO_THREAD_OFFSET(TopOfManagedStackOffset<ptr_size>(), "top_quick_frame_method")
   DO_THREAD_OFFSET(TopShadowFrameOffset<ptr_size>(), "top_shadow_frame")
   DO_THREAD_OFFSET(TopHandleScopeOffset<ptr_size>(), "top_handle_scope")
diff --git a/runtime/thread.h b/runtime/thread.h
index e785ddc..e1102ed 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -228,6 +228,11 @@
     return tls32_.suspend_count;
   }
 
+  int GetUserCodeSuspendCount() const REQUIRES(Locks::thread_suspend_count_lock_,
+                                               Locks::user_code_suspension_lock_) {
+    return tls32_.user_code_suspend_count;
+  }
+
   int GetDebugSuspendCount() const REQUIRES(Locks::thread_suspend_count_lock_) {
     return tls32_.debug_suspend_count;
   }
@@ -656,6 +661,17 @@
         OFFSETOF_MEMBER(tls_ptr_sized_values, jni_entrypoints) + jni_entrypoint_offset);
   }
 
+  // Return the entry point offset integer value for ReadBarrierMarkRegX, where X is `reg`.
+  template <PointerSize pointer_size>
+  static int32_t ReadBarrierMarkEntryPointsOffset(size_t reg) {
+    // The entry point list defines 30 ReadBarrierMarkRegX entry points.
+    DCHECK_LT(reg, 30u);
+    // The ReadBarrierMarkRegX entry points are ordered by increasing
+    // register number in Thread::tls_Ptr_.quick_entrypoints.
+    return QUICK_ENTRYPOINT_OFFSET(pointer_size, pReadBarrierMarkReg00).Int32Value()
+        + static_cast<size_t>(pointer_size) * reg;
+  }
+
   template<PointerSize pointer_size>
   static ThreadOffset<pointer_size> SelfOffset() {
     return ThreadOffsetFromTlsPtr<pointer_size>(OFFSETOF_MEMBER(tls_ptr_sized_values, self));
@@ -1166,11 +1182,11 @@
     return debug_disallow_read_barrier_;
   }
 
-  const void* GetCustomTLS() const {
+  void* GetCustomTLS() const REQUIRES(Locks::thread_list_lock_) {
     return custom_tls_;
   }
 
-  void SetCustomTLS(const void* data) {
+  void SetCustomTLS(void* data) REQUIRES(Locks::thread_list_lock_) {
     custom_tls_ = data;
   }
 
@@ -1381,7 +1397,7 @@
       thread_exit_check_count(0), handling_signal_(false),
       is_transitioning_to_runnable(false), ready_for_debug_invoke(false),
       debug_method_entry_(false), is_gc_marking(false), weak_ref_access_enabled(true),
-      disable_thread_flip_count(0) {
+      disable_thread_flip_count(0), user_code_suspend_count(0) {
     }
 
     union StateAndFlags state_and_flags;
@@ -1456,6 +1472,12 @@
     // levels of (nested) JNI critical sections the thread is in and is used to detect a nested JNI
     // critical section enter.
     uint32_t disable_thread_flip_count;
+
+    // How much of 'suspend_count_' is by request of user code, used to distinguish threads
+    // suspended by the runtime from those suspended by user code.
+    // This should have GUARDED_BY(Locks::user_code_suspension_lock_) but auto analysis cannot be
+    // told that AssertHeld should be good enough.
+    int user_code_suspend_count GUARDED_BY(Locks::thread_suspend_count_lock_);
   } tls32_;
 
   struct PACKED(8) tls_64bit_sized_values {
@@ -1661,7 +1683,7 @@
 
   // Custom TLS field that can be used by plugins.
   // TODO: Generalize once we have more plugins.
-  const void* custom_tls_;
+  void* custom_tls_;
 
   // True if the thread is allowed to call back into java (for e.g. during class resolution).
   // By default this is true.
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index fc767ed..9c938ff 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -828,7 +828,7 @@
   }
 }
 
-void ThreadList::Resume(Thread* thread, SuspendReason reason) {
+bool ThreadList::Resume(Thread* thread, SuspendReason reason) {
   // This assumes there was an ATRACE_BEGIN when we suspended the thread.
   ATRACE_END();
 
@@ -841,16 +841,23 @@
     MutexLock mu(self, *Locks::thread_list_lock_);
     // To check IsSuspended.
     MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
-    DCHECK(thread->IsSuspended());
+    if (UNLIKELY(!thread->IsSuspended())) {
+      LOG(ERROR) << "Resume(" << reinterpret_cast<void*>(thread)
+          << ") thread not suspended";
+      return false;
+    }
     if (!Contains(thread)) {
       // We only expect threads within the thread-list to have been suspended otherwise we can't
       // stop such threads from delete-ing themselves.
       LOG(ERROR) << "Resume(" << reinterpret_cast<void*>(thread)
           << ") thread not within thread list";
-      return;
+      return false;
     }
-    bool updated = thread->ModifySuspendCount(self, -1, nullptr, reason);
-    DCHECK(updated);
+    if (UNLIKELY(!thread->ModifySuspendCount(self, -1, nullptr, reason))) {
+      LOG(ERROR) << "Resume(" << reinterpret_cast<void*>(thread)
+                 << ") could not modify suspend count.";
+      return false;
+    }
   }
 
   {
@@ -860,6 +867,7 @@
   }
 
   VLOG(threads) << "Resume(" << reinterpret_cast<void*>(thread) << ") complete";
+  return true;
 }
 
 static void ThreadSuspendByPeerWarning(Thread* self,
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 41c5e32..11f272c 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -65,8 +65,8 @@
   void ResumeAll()
       REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_)
       UNLOCK_FUNCTION(Locks::mutator_lock_);
-  void Resume(Thread* thread, SuspendReason reason = SuspendReason::kInternal)
-      REQUIRES(!Locks::thread_suspend_count_lock_);
+  bool Resume(Thread* thread, SuspendReason reason = SuspendReason::kInternal)
+      REQUIRES(!Locks::thread_suspend_count_lock_) WARN_UNUSED;
 
   // Suspends all threads and gets exclusive access to the mutator_lock_.
   // If long_suspend is true, then other threads who try to suspend will never timeout.
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
index 95904af..72f63c6 100644
--- a/runtime/utils/dex_cache_arrays_layout-inl.h
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -132,7 +132,7 @@
   if (num_elements < cache_size) {
     cache_size = num_elements;
   }
-  return 2u * static_cast<size_t>(pointer_size_) * num_elements;
+  return 2u * static_cast<size_t>(pointer_size_) * cache_size;
 }
 
 inline size_t DexCacheArraysLayout::FieldsAlignment() const {
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 464af04..e8f947c 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -164,60 +164,148 @@
   return true;
 }
 
+// Utility class to easily iterate over the quickening data.
+class QuickeningInfoIterator {
+ public:
+  QuickeningInfoIterator(uint32_t dex_file_index,
+                         uint32_t number_of_dex_files,
+                         const ArrayRef<const uint8_t>& quickening_info)
+      : quickening_info_(quickening_info) {
+    const unaligned_uint32_t* dex_file_indices = reinterpret_cast<const unaligned_uint32_t*>(
+            quickening_info.data() +
+            quickening_info.size() -
+            number_of_dex_files * sizeof(uint32_t));
+    current_code_item_end_ = (dex_file_index == number_of_dex_files - 1)
+        ? dex_file_indices
+        : reinterpret_cast<const unaligned_uint32_t*>(
+              quickening_info_.data() + dex_file_indices[dex_file_index + 1]);
+    current_code_item_ptr_ = reinterpret_cast<const uint32_t*>(
+        quickening_info_.data() + dex_file_indices[dex_file_index]);
+  }
+
+  bool Done() const {
+    return current_code_item_ptr_ == current_code_item_end_;
+  }
+
+  void Advance() {
+    current_code_item_ptr_ += 2;
+  }
+
+  uint32_t GetCurrentCodeItemOffset() const {
+    return current_code_item_ptr_[0];
+  }
+
+  const ArrayRef<const uint8_t> GetCurrentQuickeningInfo() const {
+    return ArrayRef<const uint8_t>(
+        // Add sizeof(uint32_t) to remove the length from the data pointer.
+        quickening_info_.data() + current_code_item_ptr_[1] + sizeof(uint32_t),
+        *reinterpret_cast<const unaligned_uint32_t*>(
+            quickening_info_.data() + current_code_item_ptr_[1]));
+  }
+
+ private:
+  typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t;
+  const ArrayRef<const uint8_t>& quickening_info_;
+  const unaligned_uint32_t* current_code_item_ptr_;
+  const unaligned_uint32_t* current_code_item_end_;
+
+  DISALLOW_COPY_AND_ASSIGN(QuickeningInfoIterator);
+};
+
 void VdexFile::Unquicken(const std::vector<const DexFile*>& dex_files,
                          const ArrayRef<const uint8_t>& quickening_info) {
   if (quickening_info.size() == 0) {
-    // If there is no quickening info, we bail early, as the code below expects at
-    // least the size of quickening data for each method that has a code item.
+    // Bail early if there is no quickening info.
     return;
   }
   // 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).
   constexpr bool kDecompileReturnInstruction = false;
-  const uint8_t* quickening_info_ptr = quickening_info.data();
-  const uint8_t* const quickening_info_end = quickening_info.data() + quickening_info.size();
-  for (const DexFile* dex_file : dex_files) {
-    for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
-      const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
-      const uint8_t* class_data = dex_file->GetClassData(class_def);
-      if (class_data == nullptr) {
-        continue;
-      }
-      ClassDataItemIterator it(*dex_file, class_data);
-      it.SkipAllFields();
-
-      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),
-                                     kDecompileReturnInstruction);
-          quickening_info_ptr += quickening_size;
-        }
-        it.Next();
-      }
-
-      while (it.HasNextVirtualMethod()) {
-        const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
-        if (code_item != nullptr) {
-          uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
-          quickening_info_ptr += sizeof(uint32_t);
-          optimizer::ArtDecompileDEX(*code_item,
-                                     ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
-                                     kDecompileReturnInstruction);
-          quickening_info_ptr += quickening_size;
-        }
-        it.Next();
-      }
-      DCHECK(!it.HasNext());
+  for (uint32_t i = 0; i < dex_files.size(); ++i) {
+    for (QuickeningInfoIterator it(i, dex_files.size(), quickening_info);
+         !it.Done();
+         it.Advance()) {
+      optimizer::ArtDecompileDEX(
+          *dex_files[i]->GetCodeItem(it.GetCurrentCodeItemOffset()),
+          it.GetCurrentQuickeningInfo(),
+          kDecompileReturnInstruction);
     }
   }
-  if (quickening_info_ptr != quickening_info_end) {
-    LOG(FATAL) << "Failed to use all quickening info";
+}
+
+static constexpr uint32_t kNoDexFile = -1;
+
+uint32_t VdexFile::GetDexFileIndex(const DexFile& dex_file) const {
+  uint32_t dex_index = 0;
+  for (const uint8_t* dex_file_start = GetNextDexFileData(nullptr);
+       dex_file_start != dex_file.Begin();
+       dex_file_start = GetNextDexFileData(dex_file_start)) {
+    if (dex_file_start == nullptr) {
+      return kNoDexFile;
+    }
+    dex_index++;
   }
+  return dex_index;
+}
+
+void VdexFile::FullyUnquickenDexFile(const DexFile& target_dex_file,
+                                     const DexFile& original_dex_file) const {
+  uint32_t dex_index = GetDexFileIndex(original_dex_file);
+  if (dex_index == kNoDexFile) {
+    return;
+  }
+
+  constexpr bool kDecompileReturnInstruction = true;
+  QuickeningInfoIterator it(dex_index, GetHeader().GetNumberOfDexFiles(), GetQuickeningInfo());
+  // Iterate over the class definitions. Even if there is no quickening info,
+  // we want to unquicken RETURN_VOID_NO_BARRIER instruction.
+  for (uint32_t i = 0; i < target_dex_file.NumClassDefs(); ++i) {
+    const DexFile::ClassDef& class_def = target_dex_file.GetClassDef(i);
+    const uint8_t* class_data = target_dex_file.GetClassData(class_def);
+    if (class_data != nullptr) {
+      for (ClassDataItemIterator class_it(target_dex_file, class_data);
+           class_it.HasNext();
+           class_it.Next()) {
+        if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) {
+          uint32_t offset = class_it.GetMethodCodeItemOffset();
+          if (!it.Done() && offset == it.GetCurrentCodeItemOffset()) {
+            optimizer::ArtDecompileDEX(
+                *class_it.GetMethodCodeItem(),
+                it.GetCurrentQuickeningInfo(),
+                kDecompileReturnInstruction);
+            it.Advance();
+          } else {
+            optimizer::ArtDecompileDEX(*class_it.GetMethodCodeItem(),
+                                       ArrayRef<const uint8_t>(nullptr, 0),
+                                       kDecompileReturnInstruction);
+          }
+        }
+      }
+    }
+  }
+}
+
+const uint8_t* VdexFile::GetQuickenedInfoOf(const DexFile& dex_file,
+                                            uint32_t code_item_offset) const {
+  if (GetQuickeningInfo().size() == 0) {
+    // Bail early if there is no quickening info.
+    return nullptr;
+  }
+
+  uint32_t dex_index = GetDexFileIndex(dex_file);
+  if (dex_index == kNoDexFile) {
+    return nullptr;
+  }
+
+  for (QuickeningInfoIterator it(dex_index, GetHeader().GetNumberOfDexFiles(), GetQuickeningInfo());
+       !it.Done();
+       it.Advance()) {
+    if (code_item_offset == it.GetCurrentCodeItemOffset()) {
+      return it.GetCurrentQuickeningInfo().data();
+    }
+  }
+  return nullptr;
 }
 
 }  // namespace art
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 93d282b..ea480f4 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -39,7 +39,14 @@
 //   DEX[1]              the bytecode may have been quickened
 //   ...
 //   DEX[D]
-//
+//   QuickeningInfo
+//     uint8[]                     quickening data
+//     unaligned_uint32_t[2][]     table of offsets pair:
+//                                    uint32_t[0] contains code_item_offset
+//                                    uint32_t[1] contains quickening data offset from the start
+//                                                of QuickeningInfo
+//     unalgined_uint32_t[D]       start offsets (from the start of QuickeningInfo) in previous
+//                                 table for each dex file
 
 class VdexFile {
  public:
@@ -65,8 +72,8 @@
 
    private:
     static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' };
-    // Last update: Disable in-place vdex update
-    static constexpr uint8_t kVdexVersion[] = { '0', '0', '6', '\0' };
+    // Last update: Change quickening info format.
+    static constexpr uint8_t kVdexVersion[] = { '0', '0', '8', '\0' };
 
     uint8_t magic_[4];
     uint8_t version_[4];
@@ -131,7 +138,7 @@
     return reinterpret_cast<const uint32_t*>(Begin() + sizeof(Header))[dex_file_index];
   }
 
-  // Opens all the dex files contained in this vdex file.
+  // Open all the dex files contained in this vdex file.
   bool OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_files,
                        std::string* error_msg);
 
@@ -139,6 +146,14 @@
   static void Unquicken(const std::vector<const DexFile*>& dex_files,
                         const ArrayRef<const uint8_t>& quickening_info);
 
+  // Fully unquicken `target_dex_file` based on quickening info stored
+  // in this vdex file for `original_dex_file`.
+  void FullyUnquickenDexFile(const DexFile& target_dex_file,
+                             const DexFile& original_dex_file) const;
+
+  // Return the quickening info of the given code item.
+  const uint8_t* GetQuickenedInfoOf(const DexFile& dex_file, uint32_t code_item_offset) const;
+
  private:
   explicit VdexFile(MemMap* mmap) : mmap_(mmap) {}
 
@@ -158,6 +173,8 @@
     return sizeof(VdexChecksum) * GetHeader().GetNumberOfDexFiles();
   }
 
+  uint32_t GetDexFileIndex(const DexFile& dex_file) const;
+
   std::unique_ptr<MemMap> mmap_;
 
   DISALLOW_COPY_AND_ASSIGN(VdexFile);
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 24f194b..f72fdb4 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -37,6 +37,7 @@
 jclass WellKnownClasses::dalvik_annotation_optimization_CriticalNative;
 jclass WellKnownClasses::dalvik_annotation_optimization_FastNative;
 jclass WellKnownClasses::dalvik_system_BaseDexClassLoader;
+jclass WellKnownClasses::dalvik_system_DelegateLastClassLoader;
 jclass WellKnownClasses::dalvik_system_DexClassLoader;
 jclass WellKnownClasses::dalvik_system_DexFile;
 jclass WellKnownClasses::dalvik_system_DexPathList;
@@ -93,6 +94,8 @@
 jmethodID WellKnownClasses::java_lang_Integer_valueOf;
 jmethodID WellKnownClasses::java_lang_invoke_MethodHandle_invoke;
 jmethodID WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact;
+jmethodID WellKnownClasses::java_lang_invoke_MethodHandles_lookup;
+jmethodID WellKnownClasses::java_lang_invoke_MethodHandles_Lookup_findConstructor;
 jmethodID WellKnownClasses::java_lang_Long_valueOf;
 jmethodID WellKnownClasses::java_lang_ref_FinalizerReference_add;
 jmethodID WellKnownClasses::java_lang_ref_ReferenceQueue_add;
@@ -172,8 +175,8 @@
   return fid;
 }
 
-jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static,
-                      const char* name, const char* signature) {
+static jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static,
+                             const char* name, const char* signature) {
   jmethodID mid = is_static ? env->GetStaticMethodID(c, name, signature) :
       env->GetMethodID(c, name, signature);
   if (mid == nullptr) {
@@ -189,6 +192,12 @@
   return mid;
 }
 
+static jmethodID CacheMethod(JNIEnv* env, const char* klass, bool is_static,
+                      const char* name, const char* signature) {
+  ScopedLocalRef<jclass> java_class(env, env->FindClass(klass));
+  return CacheMethod(env, java_class.get(), is_static, name, signature);
+}
+
 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",
@@ -270,6 +279,7 @@
       CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
   dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative");
   dalvik_system_BaseDexClassLoader = CacheClass(env, "dalvik/system/BaseDexClassLoader");
+  dalvik_system_DelegateLastClassLoader = CacheClass(env, "dalvik/system/DelegateLastClassLoader");
   dalvik_system_DexClassLoader = CacheClass(env, "dalvik/system/DexClassLoader");
   dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
   dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
@@ -320,16 +330,12 @@
   java_lang_Daemons_requestHeapTrim = CacheMethod(env, java_lang_Daemons, true, "requestHeapTrim", "()V");
   java_lang_Daemons_start = CacheMethod(env, java_lang_Daemons, true, "start", "()V");
   java_lang_Daemons_stop = CacheMethod(env, java_lang_Daemons, true, "stop", "()V");
-  java_lang_invoke_MethodHandle_invoke =
-      CacheMethod(env, java_lang_invoke_MethodHandle, false,
-                  "invoke", "([Ljava/lang/Object;)Ljava/lang/Object;");
-  java_lang_invoke_MethodHandle_invokeExact =
-      CacheMethod(env, java_lang_invoke_MethodHandle, false,
-                  "invokeExact", "([Ljava/lang/Object;)Ljava/lang/Object;");
-  ScopedLocalRef<jclass> java_lang_ref_FinalizerReference(env, env->FindClass("java/lang/ref/FinalizerReference"));
-  java_lang_ref_FinalizerReference_add = CacheMethod(env, java_lang_ref_FinalizerReference.get(), true, "add", "(Ljava/lang/Object;)V");
-  ScopedLocalRef<jclass> java_lang_ref_ReferenceQueue(env, env->FindClass("java/lang/ref/ReferenceQueue"));
-  java_lang_ref_ReferenceQueue_add = CacheMethod(env, java_lang_ref_ReferenceQueue.get(), true, "add", "(Ljava/lang/ref/Reference;)V");
+  java_lang_invoke_MethodHandle_invoke = CacheMethod(env, java_lang_invoke_MethodHandle, false, "invoke", "([Ljava/lang/Object;)Ljava/lang/Object;");
+  java_lang_invoke_MethodHandle_invokeExact = CacheMethod(env, java_lang_invoke_MethodHandle, false, "invokeExact", "([Ljava/lang/Object;)Ljava/lang/Object;");
+  java_lang_invoke_MethodHandles_lookup = CacheMethod(env, "java/lang/invoke/MethodHandles", true, "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;");
+  java_lang_invoke_MethodHandles_Lookup_findConstructor = CacheMethod(env, "java/lang/invoke/MethodHandles$Lookup", false, "findConstructor", "(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;");
+  java_lang_ref_FinalizerReference_add = CacheMethod(env, "java/lang/ref/FinalizerReference", true, "add", "(Ljava/lang/Object;)V");
+  java_lang_ref_ReferenceQueue_add = CacheMethod(env, "java/lang/ref/ReferenceQueue", true, "add", "(Ljava/lang/ref/Reference;)V");
 
   java_lang_reflect_Parameter_init = CacheMethod(env, java_lang_reflect_Parameter, false, "<init>", "(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V");
   java_lang_String_charAt = CacheMethod(env, java_lang_String, false, "charAt", "(I)C");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index c184731..2f2f1ad 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -33,8 +33,6 @@
 // them up. Similar to libcore's JniConstants (except there's no overlap, so
 // we keep them separate).
 
-jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static, const char* name, const char* signature);
-
 struct WellKnownClasses {
  public:
   static void Init(JNIEnv* env);  // Run before native methods are registered.
@@ -47,6 +45,7 @@
   static jclass dalvik_annotation_optimization_CriticalNative;
   static jclass dalvik_annotation_optimization_FastNative;
   static jclass dalvik_system_BaseDexClassLoader;
+  static jclass dalvik_system_DelegateLastClassLoader;
   static jclass dalvik_system_DexClassLoader;
   static jclass dalvik_system_DexFile;
   static jclass dalvik_system_DexPathList;
@@ -103,6 +102,8 @@
   static jmethodID java_lang_Integer_valueOf;
   static jmethodID java_lang_invoke_MethodHandle_invoke;
   static jmethodID java_lang_invoke_MethodHandle_invokeExact;
+  static jmethodID java_lang_invoke_MethodHandles_lookup;
+  static jmethodID java_lang_invoke_MethodHandles_Lookup_findConstructor;
   static jmethodID java_lang_Long_valueOf;
   static jmethodID java_lang_ref_FinalizerReference_add;
   static jmethodID java_lang_ref_ReferenceQueue_add;
diff --git a/test.py b/test.py
index 414d779..047d812 100755
--- a/test.py
+++ b/test.py
@@ -28,14 +28,15 @@
 ANDROID_BUILD_TOP = os.environ.get('ANDROID_BUILD_TOP', os.getcwd())
 
 parser = argparse.ArgumentParser()
-parser.add_argument('-j', default='', dest='n_threads')
-parser.add_argument('--run-test', '-r', action='store_true', dest='run_test')
-parser.add_argument('--gtest', '-g', action='store_true', dest='gtest')
-parser.add_argument('--target', action='store_true', dest='target')
-parser.add_argument('--host', action='store_true', dest='host')
+parser.add_argument('-j', default='', dest='n_threads', help='specify number of concurrent tests')
+parser.add_argument('--run-test', '-r', action='store_true', dest='run_test', help='execute run tests')
+parser.add_argument('--gtest', '-g', action='store_true', dest='gtest', help='execute gtest tests')
+parser.add_argument('--target', action='store_true', dest='target', help='test on target system')
+parser.add_argument('--host', action='store_true', dest='host', help='test on build host system')
+parser.add_argument('--help-runner', action='store_true', dest='help_runner', help='show help for optional run test arguments')
 options, unknown = parser.parse_known_args()
 
-if options.run_test or not options.gtest:
+if options.run_test or options.help_runner or not options.gtest:
   testrunner = os.path.join('./',
                           ANDROID_BUILD_TOP,
                             'art/test/testrunner/testrunner.py')
@@ -44,11 +45,14 @@
     if arg == '--run-test' or arg == '--gtest' \
     or arg == '-r' or arg == '-g':
       continue
+    if arg == '--help-runner':
+      run_test_args = ['--help']
+      break
     run_test_args.append(arg)
 
   test_runner_cmd = [testrunner] + run_test_args
   print test_runner_cmd
-  if subprocess.call(test_runner_cmd):
+  if subprocess.call(test_runner_cmd) or options.help_runner:
     sys.exit(1)
 
 if options.gtest or not options.run_test:
diff --git a/test/021-string2/src/Main.java b/test/021-string2/src/Main.java
index 194f4a1..3b81d8e 100644
--- a/test/021-string2/src/Main.java
+++ b/test/021-string2/src/Main.java
@@ -700,6 +700,11 @@
             $noinline$constNonAsciiString35Equals("\u0440123456789012345678901234567890123x"));
         Assert.assertFalse(
             $noinline$constNonAsciiString35Equals("01234567890123456789012345678901234"));
+
+        // Regression test for incorrectly creating an uncompressed string when the
+        // string should be compressed. Only the low 8 bits are relevant but the whole
+        // `hibyte` was erroneously tested. Bug: 63661357
+        Assert.assertTrue("A".equals(new String(new byte[] { (byte)'A' }, /* hibyte */ 0x100)));
     }
 
     public static boolean $noinline$equalsConstString0(String s) {
diff --git a/test/064-field-access/jasmin/SubClassUsingInaccessibleField.j b/test/064-field-access/jasmin/SubClassUsingInaccessibleField.j
new file mode 100644
index 0000000..3422f85
--- /dev/null
+++ b/test/064-field-access/jasmin/SubClassUsingInaccessibleField.j
@@ -0,0 +1,36 @@
+; Copyright (C) 2017 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class                   public SubClassUsingInaccessibleField
+.super                   other/PublicClass
+
+.method                  public <init>()V
+   .limit stack          1
+   .limit locals         1
+   aload_0
+   invokespecial         other/PublicClass/<init>()V
+   return
+.end method
+
+; Regression test for compiler DCHECK() failure (bogus check) when referencing
+; a package-private field from an indirectly inherited package-private class,
+; using this very class as the declaring class in the FieldId, bug: 27684368 .
+.method                  public test()I
+   .limit stack          1
+   .limit locals         1
+   aload_0
+   getfield              SubClassUsingInaccessibleField/otherProtectedClassPackageIntInstanceField I
+   ireturn
+.end method
+
diff --git a/test/064-field-access/smali/SubClassUsingInaccessibleField.smali b/test/064-field-access/smali/SubClassUsingInaccessibleField.smali
deleted file mode 100644
index 224b431..0000000
--- a/test/064-field-access/smali/SubClassUsingInaccessibleField.smali
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-.class public LSubClassUsingInaccessibleField;
-
-.super Lother/PublicClass;
-
-.method public constructor <init>()V
-    .registers 1
-    invoke-direct {p0}, Lother/PublicClass;-><init>()V
-    return-void
-.end method
-
-# Regression test for compiler DCHECK() failure (bogus check) when referencing
-# a package-private field from an indirectly inherited package-private class,
-# using this very class as the declaring class in the FieldId, bug: 27684368 .
-.method public test()I
-    .registers 2
-    iget v0, p0, LSubClassUsingInaccessibleField;->otherProtectedClassPackageIntInstanceField:I
-    return v0
-.end method
diff --git a/test/079-phantom/src/Bitmap.java b/test/079-phantom/src/Bitmap.java
index ff43749..0d6e2d8 100644
--- a/test/079-phantom/src/Bitmap.java
+++ b/test/079-phantom/src/Bitmap.java
@@ -17,6 +17,7 @@
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.PhantomReference;
 import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
 
 public class Bitmap {
     String mName;           /* for debugging */
@@ -76,11 +77,14 @@
         PhantomWrapper phan = new PhantomWrapper(wrapper, sPhantomQueue,
                 nativeData);
         sPhantomList.add(phan);
+        wrapper.mPhantomWrapper = phan;
         return wrapper;
     }
 
-    static void freeNativeStorage(int nativeDataPtr) {
+    static void freeNativeStorage(int nativeDataPtr, CountDownLatch freeSignal) {
         System.out.println("freeNativeStorage: " + nativeDataPtr);
+        // Wake up the main thread that is [or will be] blocked until this native data is freed.
+        freeSignal.countDown();
     }
 
     /*
@@ -93,6 +97,9 @@
         }
         public int mNativeData;
 
+        // The PhantomWrapper corresponding to this NativeWrapper.
+        public PhantomWrapper mPhantomWrapper;
+
         /*
         @Override
         protected void finalize() throws Throwable {
@@ -118,6 +125,8 @@
     }
 
     public int mNativeData;
+    // This will be signaled once mNativeData has been freed.
+    public CountDownLatch mFreeSignal = new CountDownLatch(1);
 }
 
 /*
@@ -137,8 +146,7 @@
                 PhantomWrapper ref = (PhantomWrapper) mQueue.remove();
                 //System.out.println("dequeued ref " + ref.mNativeData +
                 //    " - " + ref);
-                Bitmap.freeNativeStorage(ref.mNativeData);
-                //ref.clear();
+                Bitmap.freeNativeStorage(ref.mNativeData, ref.mFreeSignal);
             } catch (InterruptedException ie) {
                 System.out.println("intr");
                 break;
diff --git a/test/079-phantom/src/Main.java b/test/079-phantom/src/Main.java
index daead2e..ae2c688 100644
--- a/test/079-phantom/src/Main.java
+++ b/test/079-phantom/src/Main.java
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
+import java.util.concurrent.CountDownLatch;
+
 public class Main {
     Bitmap mBitmap1, mBitmap2, mBitmap3, mBitmap4;
+    CountDownLatch mFreeSignalA, mFreeSignalB;
 
     public static void sleep(int ms) {
         try {
@@ -31,7 +34,6 @@
         Main main = new Main();
         main.run();
 
-        sleep(1000);
         System.out.println("done");
     }
 
@@ -46,22 +48,30 @@
         System.out.println("nulling 1");
         mBitmap1 = null;
         Runtime.getRuntime().gc();
-        sleep(500);
+        try {
+          mFreeSignalA.await();  // Block until dataA is definitely freed.
+        } catch (InterruptedException e) {
+          System.out.println("got unexpected InterruptedException e: " + e);
+        }
 
         System.out.println("nulling 2");
         mBitmap2 = null;
         Runtime.getRuntime().gc();
-        sleep(500);
+        sleep(200);
 
         System.out.println("nulling 3");
         mBitmap3 = null;
         Runtime.getRuntime().gc();
-        sleep(500);
+        sleep(200);
 
         System.out.println("nulling 4");
         mBitmap4 = null;
         Runtime.getRuntime().gc();
-        sleep(500);
+        try {
+          mFreeSignalB.await();  // Block until dataB is definitely freed.
+        } catch (InterruptedException e) {
+          System.out.println("got unexpected InterruptedException e: " + e);
+        }
 
         Bitmap.shutDown();
     }
@@ -77,7 +87,10 @@
      */
     public void createBitmaps() {
         Bitmap.NativeWrapper dataA = Bitmap.allocNativeStorage(10, 10);
+        mFreeSignalA = dataA.mPhantomWrapper.mFreeSignal;
         Bitmap.NativeWrapper dataB = Bitmap.allocNativeStorage(20, 20);
+        mFreeSignalB = dataB.mPhantomWrapper.mFreeSignal;
+
         mBitmap1 = new Bitmap("one", 10, 10, dataA);
         mBitmap2 = new Bitmap("two", 20, 20, dataB);
         mBitmap3 = mBitmap4 = new Bitmap("three/four", 20, 20, dataB);
diff --git a/test/098-ddmc/src/Main.java b/test/098-ddmc/src/Main.java
index 72c5a28..e9a11d7 100644
--- a/test/098-ddmc/src/Main.java
+++ b/test/098-ddmc/src/Main.java
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
+import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
 
 public class Main {
     public static void main(String[] args) throws Exception {
@@ -27,6 +30,8 @@
         testRecentAllocationTracking();
     }
 
+    private static ArrayList<Object> staticHolder = new ArrayList<>(100000);
+
     private static void testRecentAllocationTracking() throws Exception {
         System.out.println("Confirm empty");
         Allocations empty = new Allocations(DdmVmInternal.getRecentAllocations());
@@ -44,18 +49,15 @@
         System.out.println("Confirm when we overflow, we don't roll over to zero. b/17392248");
         final int overflowAllocations = 64 * 1024;  // Won't fit in unsigned 16-bit value.
         for (int i = 0; i < overflowAllocations; i++) {
-            new Object() {
-                // Add a finalizer so that the allocation won't be eliminated.
-                public void finalize() {
-                    System.out.print("");
-                }
-            };
+            allocate(i, 0);
         }
         Allocations after = new Allocations(DdmVmInternal.getRecentAllocations());
         System.out.println("before < overflowAllocations=" + (before.numberOfEntries < overflowAllocations));
         System.out.println("after > before=" + (after.numberOfEntries > before.numberOfEntries));
         System.out.println("after.numberOfEntries=" + after.numberOfEntries);
 
+        staticHolder.clear();  // Free the allocated objects.
+
         System.out.println("Disable and confirm back to empty");
         DdmVmInternal.enableRecentAllocations(false);
         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
@@ -72,7 +74,7 @@
         DdmVmInternal.enableRecentAllocations(true);
         System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
         for (int i = 0; i < 16 * 1024; i++) {
-            new String("fnord");
+            staticHolder.add(new String("fnord"));
         }
         Allocations first = new Allocations(DdmVmInternal.getRecentAllocations());
         DdmVmInternal.enableRecentAllocations(true);
@@ -86,6 +88,50 @@
         System.out.println("goodbye=" + goodbye);
     }
 
+    // Allocate a simple object. Use depth for a reasonably deep stack.
+    private static final int ALLOCATE1_DEPTH = 50;
+
+    private static Object createProxy() {
+        try {
+            InvocationHandler handler = new InvocationHandler() {
+                public Object invoke(Object proxy, Method method, Object[] args) {
+                    // Don't expect to be invoked.
+                    return null;
+                }
+            };
+            return Proxy.newProxyInstance(Main.class.getClassLoader(),
+                    new Class[] { Runnable.class }, handler);
+        } catch (Exception e) {
+            // We don't really expect exceptions here.
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static void allocate(int i, int depth) {
+        if (depth >= ALLOCATE1_DEPTH) {
+            // Mix proxies, int arrays and Objects to test the different descriptor paths.
+            switch (i) {
+                case 0:
+                    staticHolder.add(createProxy());
+                    break;
+
+                case 1:
+                    staticHolder.add(new int[0]);
+                    break;
+
+                case 2:
+                    staticHolder.add(new Object[0]);
+                    break;
+
+                default:
+                    staticHolder.add(new Object());
+                    break;
+            }
+        } else {
+            allocate(i, depth + 1);
+        }
+    }
+
     private static class Allocations {
         final int messageHeaderLen;
         final int entryHeaderLen;
diff --git a/test/138-duplicate-classes-check2/src/FancyLoader.java b/test/138-duplicate-classes-check2/src/FancyLoader.java
deleted file mode 100644
index 58b7ec4..0000000
--- a/test/138-duplicate-classes-check2/src/FancyLoader.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.lang.reflect.InvocationTargetException;
-
-/**
- * A class loader with atypical behavior: we try to load a private
- * class implementation before asking the system or boot loader.  This
- * is used to create multiple classes with identical names in a single VM.
- *
- * If DexFile is available, we use that; if not, we assume we're not in
- * Dalvik and instantiate the class with defineClass().
- *
- * The location of the DEX files and class data is dependent upon the
- * test framework.
- */
-public class FancyLoader extends ClassLoader {
-    /* this is where the "alternate" .class files live */
-    static final String CLASS_PATH = "classes-ex/";
-
-    /* this is the "alternate" DEX/Jar file */
-    static final String DEX_FILE = System.getenv("DEX_LOCATION") +
-            "/138-duplicate-classes-check2-ex.jar";
-
-    /* on Dalvik, this is a DexFile; otherwise, it's null */
-    private Class<?> mDexClass;
-
-    private Object mDexFile;
-
-    /**
-     * Construct FancyLoader, grabbing a reference to the DexFile class
-     * if we're running under Dalvik.
-     */
-    public FancyLoader(ClassLoader parent) {
-        super(parent);
-
-        try {
-            mDexClass = parent.loadClass("dalvik.system.DexFile");
-        } catch (ClassNotFoundException cnfe) {
-            // ignore -- not running Dalvik
-        }
-    }
-
-    /**
-     * Finds the class with the specified binary name.
-     *
-     * We search for a file in CLASS_PATH or pull an entry from DEX_FILE.
-     * If we don't find a match, we throw an exception.
-     */
-    protected Class<?> findClass(String name) throws ClassNotFoundException
-    {
-        if (mDexClass != null) {
-            return findClassDalvik(name);
-        } else {
-            return findClassNonDalvik(name);
-        }
-    }
-
-    /**
-     * Finds the class with the specified binary name, from a DEX file.
-     */
-    private Class<?> findClassDalvik(String name)
-        throws ClassNotFoundException {
-
-        if (mDexFile == null) {
-            synchronized (FancyLoader.class) {
-                Constructor<?> ctor;
-                /*
-                 * Construct a DexFile object through reflection.
-                 */
-                try {
-                    ctor = mDexClass.getConstructor(String.class);
-                } catch (NoSuchMethodException nsme) {
-                    throw new ClassNotFoundException("getConstructor failed",
-                        nsme);
-                }
-
-                try {
-                    mDexFile = ctor.newInstance(DEX_FILE);
-                } catch (InstantiationException ie) {
-                    throw new ClassNotFoundException("newInstance failed", ie);
-                } catch (IllegalAccessException iae) {
-                    throw new ClassNotFoundException("newInstance failed", iae);
-                } catch (InvocationTargetException ite) {
-                    throw new ClassNotFoundException("newInstance failed", ite);
-                }
-            }
-        }
-
-        /*
-         * Call DexFile.loadClass(String, ClassLoader).
-         */
-        Method meth;
-
-        try {
-            meth = mDexClass.getMethod("loadClass", String.class, ClassLoader.class);
-        } catch (NoSuchMethodException nsme) {
-            throw new ClassNotFoundException("getMethod failed", nsme);
-        }
-
-        try {
-            meth.invoke(mDexFile, name, this);
-        } catch (IllegalAccessException iae) {
-            throw new ClassNotFoundException("loadClass failed", iae);
-        } catch (InvocationTargetException ite) {
-            throw new ClassNotFoundException("loadClass failed",
-                ite.getCause());
-        }
-
-        return null;
-    }
-
-    /**
-     * Finds the class with the specified binary name, from .class files.
-     */
-    private Class<?> findClassNonDalvik(String name)
-        throws ClassNotFoundException {
-
-        String pathName = CLASS_PATH + name + ".class";
-        //System.out.println("--- Fancy: looking for " + pathName);
-
-        File path = new File(pathName);
-        RandomAccessFile raf;
-
-        try {
-            raf = new RandomAccessFile(path, "r");
-        } catch (FileNotFoundException fnfe) {
-            throw new ClassNotFoundException("Not found: " + pathName);
-        }
-
-        /* read the entire file in */
-        byte[] fileData;
-        try {
-            fileData = new byte[(int) raf.length()];
-            raf.readFully(fileData);
-        } catch (IOException ioe) {
-            throw new ClassNotFoundException("Read error: " + pathName);
-        } finally {
-            try {
-                raf.close();
-            } catch (IOException ioe) {
-                // drop
-            }
-        }
-
-        /* create the class */
-        //System.out.println("--- Fancy: defining " + name);
-        try {
-            return defineClass(name, fileData, 0, fileData.length);
-        } catch (Throwable th) {
-            throw new ClassNotFoundException("defineClass failed", th);
-        }
-    }
-
-    /**
-     * Load a class.
-     *
-     * Normally a class loader wouldn't override this, but we want our
-     * version of the class to take precedence over an already-loaded
-     * version.
-     *
-     * We still want the system classes (e.g. java.lang.Object) from the
-     * bootstrap class loader.
-     */
-    protected Class<?> loadClass(String name, boolean resolve)
-        throws ClassNotFoundException
-    {
-        Class<?> res;
-
-        /*
-         * 1. Invoke findLoadedClass(String) to check if the class has
-         * already been loaded.
-         *
-         * This doesn't change.
-         */
-        res = findLoadedClass(name);
-        if (res != null) {
-            System.out.println("FancyLoader.loadClass: "
-                + name + " already loaded");
-            if (resolve)
-                resolveClass(res);
-            return res;
-        }
-
-        /*
-         * 3. Invoke the findClass(String) method to find the class.
-         */
-        try {
-            res = findClass(name);
-            if (resolve)
-                resolveClass(res);
-        }
-        catch (ClassNotFoundException e) {
-            // we couldn't find it, so eat the exception and keep going
-        }
-
-        /*
-         * 2. Invoke the loadClass method on the parent class loader.  If
-         * the parent loader is null the class loader built-in to the
-         * virtual machine is used, instead.
-         *
-         * (Since we're not in java.lang, we can't actually invoke the
-         * parent's loadClass() method, but we passed our parent to the
-         * super-class which can take care of it for us.)
-         */
-        res = super.loadClass(name, resolve);   // returns class or throws
-        return res;
-    }
-}
diff --git a/test/138-duplicate-classes-check2/src/Main.java b/test/138-duplicate-classes-check2/src/Main.java
index faf8b5d..588e5eb 100644
--- a/test/138-duplicate-classes-check2/src/Main.java
+++ b/test/138-duplicate-classes-check2/src/Main.java
@@ -15,12 +15,30 @@
  */
 
 import java.io.File;
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 
 /**
  * Structural hazard test.
  */
 public class Main {
+    public static String TEST_NAME = "138-duplicate-classes-check2";
+
+    public static ClassLoader getClassLoaderFor(String location) throws Exception {
+        try {
+            Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader");
+            Constructor<?> ctor =
+                    class_loader_class.getConstructor(String.class, ClassLoader.class);
+            /* on Dalvik, this is a DexFile; otherwise, it's null */
+            return (ClassLoader) ctor.newInstance(location + "/" + TEST_NAME + "-ex.jar",
+                                                  Main.class.getClassLoader());
+        } catch (ClassNotFoundException e) {
+            // Running on RI. Use URLClassLoader.
+            return new java.net.URLClassLoader(
+                    new java.net.URL[] { new java.net.URL("file://" + location + "/classes-ex/") });
+        }
+    }
+
     public static void main(String[] args) {
         new Main().run();
     }
@@ -29,15 +47,18 @@
         System.out.println(new A().i);
 
         // Now run the class from the -ex file.
-
-        FancyLoader loader = new FancyLoader(getClass().getClassLoader());
-
         try {
-            Class<?> testEx = loader.loadClass("TestEx");
-            Method test = testEx.getDeclaredMethod("test");
-            test.invoke(null);
-        } catch (Exception exc) {
-            exc.printStackTrace(System.out);
+            /* this is the "alternate" DEX/Jar file */
+            ClassLoader new_loader = getClassLoaderFor(System.getenv("DEX_LOCATION"));
+            Class<?> klass = (Class<?>) new_loader.loadClass("TestEx");
+            if (klass == null) {
+                throw new AssertionError("loadClass failed");
+            }
+            Method run_test = klass.getMethod("test");
+            run_test.invoke(null);
+        } catch (Exception e) {
+            System.out.println(e.toString());
+            e.printStackTrace(System.out);
         }
     }
 }
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index 9072c8b..3cfe006 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -65,7 +65,8 @@
         String line;
         int count = 0;
         while ((line = reader.readLine()) != null) {
-            if (line.contains("@141-class-unload-ex.jar")) {
+            if (line.contains("141-class-unload-ex.odex") ||
+                line.contains("141-class-unload-ex.vdex")) {
                 System.out.println(line);
                 ++count;
             }
diff --git a/test/1900-track-alloc/alloc.cc b/test/1900-track-alloc/alloc.cc
new file mode 100644
index 0000000..db5617c
--- /dev/null
+++ b/test/1900-track-alloc/alloc.cc
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1900TrackAlloc {
+
+typedef jvmtiError (*GetGlobalState)(jvmtiEnv* env, jlong* allocated);
+
+struct AllocTrackingData {
+  GetGlobalState get_global_state;
+};
+
+template <typename T>
+static void Dealloc(T* t) {
+  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(t));
+}
+
+template <typename T, typename ...Rest>
+static void Dealloc(T* t, Rest... rs) {
+  Dealloc(t);
+  Dealloc(rs...);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1900_doDeallocate(JNIEnv* env,
+                                                                 jclass,
+                                                                 jlong jvmti_env_ptr,
+                                                                 jlong ptr) {
+  JvmtiErrorToException(env,
+                        reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr),
+                        reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr)->Deallocate(
+                            reinterpret_cast<unsigned char*>(static_cast<intptr_t>(ptr))));
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_art_Test1900_doAllocate(JNIEnv* env,
+                                                                jclass,
+                                                                jlong jvmti_env_ptr,
+                                                                jlong size) {
+  unsigned char* res = nullptr;
+  JvmtiErrorToException(env,
+                        reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr),
+                        reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr)->Allocate(size, &res));
+  return static_cast<jlong>(reinterpret_cast<intptr_t>(res));
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_art_Test1900_getAmountAllocated(JNIEnv* env, jclass) {
+  AllocTrackingData* data = nullptr;
+  if (JvmtiErrorToException(
+      env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+    return -1;
+  }
+  if (data == nullptr || data->get_global_state == nullptr) {
+    ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+    env->ThrowNew(rt_exception.get(), "Alloc tracking data not initialized.");
+    return -1;
+  }
+  jlong allocated = -1;
+  JvmtiErrorToException(env, jvmti_env, data->get_global_state(jvmti_env, &allocated));
+  return allocated;
+}
+
+static void DeallocParams(jvmtiParamInfo* params, jint n_params) {
+  for (jint i = 0; i < n_params; i++) {
+    Dealloc(params[i].name);
+  }
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_art_Test1900_getDefaultJvmtiEnv(JNIEnv*, jclass) {
+  return static_cast<jlong>(reinterpret_cast<intptr_t>(jvmti_env));
+}
+
+extern "C" JNIEXPORT void Java_art_Test1900_destroyJvmtiEnv(JNIEnv* env,
+                                                            jclass,
+                                                            jlong jvmti_env_ptr) {
+  JvmtiErrorToException(env,
+                        jvmti_env,
+                        reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr)->DisposeEnvironment());
+}
+
+extern "C" JNIEXPORT jlong Java_art_Test1900_newJvmtiEnv(JNIEnv* env, jclass) {
+  JavaVM* vm = nullptr;
+  if (env->GetJavaVM(&vm) != 0) {
+    ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+    env->ThrowNew(rt_exception.get(), "Unable to get JavaVM");
+    return -1;
+  }
+  jvmtiEnv* new_env = nullptr;
+  if (vm->GetEnv(reinterpret_cast<void**>(&new_env), JVMTI_VERSION_1_0) != 0) {
+    ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+    env->ThrowNew(rt_exception.get(), "Unable to create new jvmtiEnv");
+    return -1;
+  }
+  return static_cast<jlong>(reinterpret_cast<intptr_t>(new_env));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1900_initializeTest(JNIEnv* env, jclass) {
+  void* old_data = nullptr;
+  if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
+    return;
+  } else if (old_data != nullptr) {
+    ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+    env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
+    return;
+  }
+  AllocTrackingData* data = nullptr;
+  if (JvmtiErrorToException(env,
+                            jvmti_env,
+                            jvmti_env->Allocate(sizeof(AllocTrackingData),
+                                                reinterpret_cast<unsigned char**>(&data)))) {
+    return;
+  }
+  memset(data, 0, sizeof(AllocTrackingData));
+  // Get the extensions.
+  jint n_ext = 0;
+  jvmtiExtensionFunctionInfo* infos = nullptr;
+  if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionFunctions(&n_ext, &infos))) {
+    return;
+  }
+  for (jint i = 0; i < n_ext; i++) {
+    jvmtiExtensionFunctionInfo* cur_info = &infos[i];
+    if (strcmp("com.android.art.alloc.get_global_jvmti_allocation_state", cur_info->id) == 0) {
+      data->get_global_state = reinterpret_cast<GetGlobalState>(cur_info->func);
+    }
+    // Cleanup the cur_info
+    DeallocParams(cur_info->params, cur_info->param_count);
+    Dealloc(cur_info->id, cur_info->short_description, cur_info->params, cur_info->errors);
+  }
+  // Cleanup the array.
+  Dealloc(infos);
+  if (data->get_global_state == nullptr) {
+    ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+    env->ThrowNew(rt_exception.get(), "Unable to find memory tracking extensions.");
+    return;
+  }
+  JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data));
+  return;
+}
+
+}  // namespace Test1900TrackAlloc
+}  // namespace art
diff --git a/test/1900-track-alloc/expected.txt b/test/1900-track-alloc/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/1900-track-alloc/expected.txt
diff --git a/test/1900-track-alloc/info.txt b/test/1900-track-alloc/info.txt
new file mode 100644
index 0000000..e1d35ae
--- /dev/null
+++ b/test/1900-track-alloc/info.txt
@@ -0,0 +1 @@
+Tests the jvmti-extension to get allocated memory snapshot.
diff --git a/test/1900-track-alloc/run b/test/1900-track-alloc/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/1900-track-alloc/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1900-track-alloc/src/Main.java b/test/1900-track-alloc/src/Main.java
new file mode 100644
index 0000000..0dab4ef
--- /dev/null
+++ b/test/1900-track-alloc/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1900.run();
+  }
+}
diff --git a/test/1900-track-alloc/src/art/Main.java b/test/1900-track-alloc/src/art/Main.java
new file mode 100644
index 0000000..aa5498b
--- /dev/null
+++ b/test/1900-track-alloc/src/art/Main.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+// Binder class so the agent's C code has something that can be bound and exposed to tests.
+// In a package to separate cleanly and work around CTS reference issues (though this class
+// should be replaced in the CTS version).
+public class Main {
+  // Load the given class with the given classloader, and bind all native methods to corresponding
+  // C methods in the agent. Will abort if any of the steps fail.
+  public static native void bindAgentJNI(String className, ClassLoader classLoader);
+  // Same as above, giving the class directly.
+  public static native void bindAgentJNIForClass(Class<?> klass);
+
+  // Common infrastructure.
+  public static native void setTag(Object o, long tag);
+  public static native long getTag(Object o);
+}
diff --git a/test/1900-track-alloc/src/art/Test1900.java b/test/1900-track-alloc/src/art/Test1900.java
new file mode 100644
index 0000000..becee1b
--- /dev/null
+++ b/test/1900-track-alloc/src/art/Test1900.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+
+public class Test1900 {
+  public static void checkLE(long exp, long o) {
+    if (exp > o) {
+      throw new Error("Expected: " + exp + " Got: " + o);
+    }
+  }
+  public static void checkEq(long exp, long o) {
+    if (exp != o) {
+      throw new Error("Expected: " + exp + " Got: " + o);
+    }
+  }
+
+  public static void runConcurrent(Runnable... rs) throws Exception {
+    final CountDownLatch latch = new CountDownLatch(rs.length);
+    Thread[] thrs = new Thread[rs.length];
+    for (int i = 0; i < rs.length; i++) {
+      final Runnable r = rs[i];
+      thrs[i] = new Thread(() -> {
+        latch.countDown();
+        r.run();
+      });
+      thrs[i].start();
+    }
+    for (Thread thr : thrs) {
+      thr.join();
+    }
+  }
+  static class Holder {
+    public long val;
+  }
+
+  public static void run() throws Exception {
+    initializeTest();
+    // Get the overhead for the native part of this test.
+    final long base_state = getAmountAllocated();
+
+    // Basic alloc-dealloc
+    checkEq(base_state + 0, getAmountAllocated());
+    long abc = doAllocate(10);
+    checkLE(base_state + 10, getAmountAllocated());
+    long def = doAllocate(10);
+    checkLE(base_state + 20, getAmountAllocated());
+    doDeallocate(abc);
+    checkLE(base_state + 10, getAmountAllocated());
+
+    doDeallocate(def);
+
+    checkEq(base_state + 0, getAmountAllocated());
+
+    // Try doing it concurrently.
+    Runnable add10 = () -> { long x = doAllocate(10); doDeallocate(x); };
+    Runnable[] rs = new Runnable[100];
+    Arrays.fill(rs, add10);
+    runConcurrent(rs);
+    checkEq(base_state + 0, getAmountAllocated());
+
+    // Try doing it concurrently with different threads to allocate and deallocate.
+    final Semaphore sem = new Semaphore(0);
+    final Holder h = new Holder();
+    runConcurrent(
+        () -> {
+          try {
+            h.val = doAllocate(100);
+            checkLE(base_state + 100, getAmountAllocated());
+            sem.release();
+          } catch (Exception e) { throw new Error("exception!", e); }
+        },
+        () -> {
+          try {
+            sem.acquire();
+            long after_acq = getAmountAllocated();
+            doDeallocate(h.val);
+            checkLE(base_state + 100, after_acq);
+          } catch (Exception e) { throw new Error("exception!", e); }
+        }
+    );
+    checkEq(base_state + 0, getAmountAllocated());
+
+    // Try doing it with multiple jvmtienvs.
+    long env1 = newJvmtiEnv();
+    long env2 = newJvmtiEnv();
+
+    final long new_base_state = getAmountAllocated();
+    // new jvmtienvs shouldn't save us memory.
+    checkLE(base_state, new_base_state);
+    // Make sure we track both.
+    abc = doAllocate(env1, 10);
+    checkLE(new_base_state + 10, getAmountAllocated());
+    def = doAllocate(env2, 10);
+    checkLE(new_base_state + 20, getAmountAllocated());
+    doDeallocate(env1, abc);
+    checkLE(new_base_state + 10, getAmountAllocated());
+
+    doDeallocate(env2, def);
+
+    checkEq(new_base_state + 0, getAmountAllocated());
+
+    destroyJvmtiEnv(env1);
+    destroyJvmtiEnv(env2);
+
+    // Back to normal after getting rid of the envs.
+    checkEq(base_state + 0, getAmountAllocated());
+
+    // Try adding some tags
+    Object a = new Object();
+    Object b = new Object();
+    Main.setTag(a, 100);
+    Main.setTag(b, 200);
+
+    // tags should be counted and should have some data associated with them.
+    checkLE(base_state + 1, getAmountAllocated());
+  }
+
+  private static native long doAllocate(long jvmtienv, long size);
+  private static long doAllocate(long size) {
+    return doAllocate(getDefaultJvmtiEnv(), size);
+  }
+
+  private static native void doDeallocate(long jvmtienv, long ptr);
+  private static void doDeallocate(long size) {
+    doDeallocate(getDefaultJvmtiEnv(), size);
+  }
+
+  private static native long getDefaultJvmtiEnv();
+  private static native long newJvmtiEnv();
+  private static native void destroyJvmtiEnv(long jvmtienv);
+  private static native long getAmountAllocated();
+  private static native void initializeTest();
+}
diff --git a/test/1901-get-bytecodes/bytecodes.cc b/test/1901-get-bytecodes/bytecodes.cc
new file mode 100644
index 0000000..edcb734
--- /dev/null
+++ b/test/1901-get-bytecodes/bytecodes.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+#include <pthread.h>
+#include <stdio.h>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1901Bytecodes {
+
+extern "C" JNIEXPORT jbyteArray JNICALL Java_art_Test1901_getBytecodes(JNIEnv* env,
+                                                                       jclass,
+                                                                       jobject jmethod) {
+  jmethodID method = env->FromReflectedMethod(jmethod);
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  }
+  unsigned char* bytecodes = nullptr;
+  jint bytecodes_size = 0;
+  if (JvmtiErrorToException(env,
+                            jvmti_env,
+                            jvmti_env->GetBytecodes(method, &bytecodes_size, &bytecodes))) {
+    return nullptr;
+  }
+  jbyteArray out = env->NewByteArray(bytecodes_size);
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  } else if (bytecodes_size == 0) {
+    return out;
+  }
+  jbyte* bytes = env->GetByteArrayElements(out, /* is_copy */ nullptr);
+  memcpy(bytes, bytecodes, bytecodes_size);
+  env->ReleaseByteArrayElements(out, bytes, 0);
+  return out;
+}
+
+}  // namespace Test1901Bytecodes
+}  // namespace art
diff --git a/test/1901-get-bytecodes/expected.txt b/test/1901-get-bytecodes/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/1901-get-bytecodes/expected.txt
diff --git a/test/1901-get-bytecodes/info.txt b/test/1901-get-bytecodes/info.txt
new file mode 100644
index 0000000..c8c9189
--- /dev/null
+++ b/test/1901-get-bytecodes/info.txt
@@ -0,0 +1,3 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that the GetBytecodes function works as expected.
diff --git a/test/1901-get-bytecodes/run b/test/1901-get-bytecodes/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/1901-get-bytecodes/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1901-get-bytecodes/src/Main.java b/test/1901-get-bytecodes/src/Main.java
new file mode 100644
index 0000000..d37fcdb
--- /dev/null
+++ b/test/1901-get-bytecodes/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1901.run();
+  }
+}
diff --git a/test/1901-get-bytecodes/src/art/Test1901.java b/test/1901-get-bytecodes/src/art/Test1901.java
new file mode 100644
index 0000000..9827e3f
--- /dev/null
+++ b/test/1901-get-bytecodes/src/art/Test1901.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Base64;
+
+public class Test1901 {
+  // Class & Dex file containing the following class.
+  // Using this representation to prevent any changes to the compiler or the file formats from
+  // changing the output of this test.
+  //
+  // package art;
+  // public class Target {
+  //   public void doNothing() {
+  //     return;
+  //   }
+  //
+  //   public static void staticNothing() {
+  //     return;
+  //   }
+  //
+  //   public void sayHi() {
+  //     System.out.println("hello");
+  //   }
+  // }
+  public static byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAHgoABgAQCQARABIIABMKABQAFQcAFgcAFwEABjxpbml0PgEAAygpVgEABENvZGUB" +
+    "AA9MaW5lTnVtYmVyVGFibGUBAAlkb05vdGhpbmcBAA1zdGF0aWNOb3RoaW5nAQAFc2F5SGkBAApT" +
+    "b3VyY2VGaWxlAQALVGFyZ2V0LmphdmEMAAcACAcAGAwAGQAaAQAFaGVsbG8HABsMABwAHQEACmFy" +
+    "dC9UYXJnZXQBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxq" +
+    "YXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExq" +
+    "YXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAAEAAEABwAIAAEACQAAAB0AAQABAAAABSq3AAGx" +
+    "AAAAAQAKAAAABgABAAAAAgABAAsACAABAAkAAAAZAAAAAQAAAAGxAAAAAQAKAAAABgABAAAABAAJ" +
+    "AAwACAABAAkAAAAZAAAAAAAAAAGxAAAAAQAKAAAABgABAAAACAABAA0ACAABAAkAAAAlAAIAAQAA" +
+    "AAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAMAAgADQABAA4AAAACAA8=");
+  public static byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQAbYkxNjiZ8a+fNWF4smR2+uXbrq88/FNoYAwAAcAAAAHhWNBIAAAAAAAAAAHgCAAAP" +
+    "AAAAcAAAAAYAAACsAAAAAgAAAMQAAAABAAAA3AAAAAYAAADkAAAAAQAAABQBAADkAQAANAEAAJoB" +
+    "AACiAQAAsAEAAMcBAADbAQAA7wEAAAMCAAAQAgAAEwIAABcCAAAiAgAAKQIAAC4CAAA3AgAAPgIA" +
+    "AAEAAAACAAAAAwAAAAQAAAAFAAAABwAAAAcAAAAFAAAAAAAAAAgAAAAFAAAAlAEAAAQAAQALAAAA" +
+    "AAAAAAAAAAAAAAAACQAAAAAAAAANAAAAAAAAAA4AAAABAAEADAAAAAIAAAAAAAAAAAAAAAEAAAAC" +
+    "AAAAAAAAAAYAAAAAAAAAYgIAAAAAAAABAAEAAQAAAE0CAAAEAAAAcBAFAAAADgAAAAAAAAAAAFIC" +
+    "AAABAAAADgAAAAEAAQAAAAAAVwIAAAEAAAAOAAAAAwABAAIAAABcAgAACAAAAGIAAAAaAQoAbiAE" +
+    "ABAADgABAAAAAwAGPGluaXQ+AAxMYXJ0L1RhcmdldDsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwAS" +
+    "TGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" +
+    "OwALVGFyZ2V0LmphdmEAAVYAAlZMAAlkb05vdGhpbmcABWhlbGxvAANvdXQAB3ByaW50bG4ABXNh" +
+    "eUhpAA1zdGF0aWNOb3RoaW5nAAIABw4ACAAHDgAEAAcOAAwABw54AAAAAgIAgYAEtAIDCcwCAQHg" +
+    "AgEB9AINAAAAAAAAAAEAAAAAAAAAAQAAAA8AAABwAAAAAgAAAAYAAACsAAAAAwAAAAIAAADEAAAA" +
+    "BAAAAAEAAADcAAAABQAAAAYAAADkAAAABgAAAAEAAAAUAQAAASAAAAQAAAA0AQAAARAAAAEAAACU" +
+    "AQAAAiAAAA8AAACaAQAAAyAAAAQAAABNAgAAACAAAAEAAABiAgAAABAAAAEAAAB4AgAA");
+
+  public static byte[][] DO_NOTHING_BYTECODES = new byte[][] {
+    // Dex Bytecodes for doNothing
+    // 0e00           |0000: return-void
+    new byte[] { 14, 0 },
+    // Java bytecodes
+    // 0: return
+    new byte[] { -79 },
+  };
+
+  public static byte[][] STATIC_NOTHING_BYTECODES = new byte[][] {
+    // Dex Bytecodes for staticNothing
+    // 0e00           |0000: return-void
+    new byte[] { 14, 0 },
+    // Java bytecodes
+    // 0: return
+    new byte[] { -79 },
+  };
+
+  public static byte[][] SAY_HI_NOTHING_BYTECODES = new byte[][] {
+    // Dex Bytecodes for sayHi
+    // 6200 0000      |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000
+    // 1a01 0a00      |0002: const-string v1, "hello" // string@000a
+    // 6e20 0400 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0004
+    // 0e00           |0007: return-void
+    new byte[] { 98, 0, 0, 0, 26, 1, 10, 0, 110, 32, 4, 0, 16, 0, 14, 0 },
+    // Java bytecodes
+    // 0: getstatic     #2  // Field java/lang/System.out:Ljava/io/PrintStream;
+    // 3: ldc           #3  // String hello
+    // 5: invokevirtual #4  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
+    // 8: return
+    new byte[] { -78, 0, 2, 18, 3, -74, 0, 4, -79 },
+  };
+
+  public static ClassLoader getClassLoader() throws Exception {
+    try {
+      Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader");
+      Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class);
+      // We are on art since we got the InMemoryDexClassLoader.
+      return (ClassLoader)ctor.newInstance(
+          ByteBuffer.wrap(DEX_BYTES), Test1901.class.getClassLoader());
+    } catch (ClassNotFoundException e) {
+      // Running on RI.
+      return new ClassLoader(Test1901.class.getClassLoader()) {
+        protected Class<?> findClass(String name) throws ClassNotFoundException {
+          if (name.equals("art.Target")) {
+            return defineClass(name, CLASS_BYTES, 0, CLASS_BYTES.length);
+          } else {
+            return super.findClass(name);
+          }
+        }
+      };
+    }
+  }
+
+  public static void CheckMethodBytes(Method m, byte[][] possible_bytecodes) {
+    byte[] real_codes = getBytecodes(m);
+    for (byte[] pos : possible_bytecodes) {
+      if (Arrays.equals(pos, real_codes)) {
+        return;
+      }
+    }
+    System.out.println("Unexpected bytecodes for " + m);
+    System.out.println("Received: " + Arrays.toString(real_codes));
+    System.out.println("Expected one of:");
+    for (byte[] pos : possible_bytecodes) {
+      System.out.println("\t" + Arrays.toString(pos));
+    }
+  }
+
+  public static void run() throws Exception {
+    Class<?> target = getClassLoader().loadClass("art.Target");
+    CheckMethodBytes(target.getDeclaredMethod("doNothing"), DO_NOTHING_BYTECODES);
+    CheckMethodBytes(target.getDeclaredMethod("staticNothing"), STATIC_NOTHING_BYTECODES);
+    CheckMethodBytes(target.getDeclaredMethod("sayHi"), SAY_HI_NOTHING_BYTECODES);
+  }
+
+  public static native byte[] getBytecodes(Method m);
+}
diff --git a/test/1902-suspend/expected.txt b/test/1902-suspend/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/1902-suspend/expected.txt
diff --git a/test/1902-suspend/info.txt b/test/1902-suspend/info.txt
new file mode 100644
index 0000000..c49a20f
--- /dev/null
+++ b/test/1902-suspend/info.txt
@@ -0,0 +1,2 @@
+Test basic jvmti Suspend/ResumeThread behavior
+
diff --git a/test/1902-suspend/run b/test/1902-suspend/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/1902-suspend/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1902-suspend/src/Main.java b/test/1902-suspend/src/Main.java
new file mode 100644
index 0000000..0bc7ba1
--- /dev/null
+++ b/test/1902-suspend/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1902.run();
+  }
+}
diff --git a/test/1902-suspend/src/art/Suspension.java b/test/1902-suspend/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1902-suspend/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+  // Suspends a thread using jvmti.
+  public native static void suspend(Thread thr);
+
+  // Resumes a thread using jvmti.
+  public native static void resume(Thread thr);
+
+  public native static boolean isSuspended(Thread thr);
+
+  public native static int[] suspendList(Thread... threads);
+  public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1902-suspend/src/art/Test1902.java b/test/1902-suspend/src/art/Test1902.java
new file mode 100644
index 0000000..2bbfacf
--- /dev/null
+++ b/test/1902-suspend/src/art/Test1902.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Test1902 {
+  public static final Object lock = new Object();
+
+  public static volatile boolean OTHER_THREAD_CONTINUE = true;
+  public static volatile boolean OTHER_THREAD_DID_SOMETHING = true;
+  public static volatile boolean OTHER_THREAD_STARTED = false;
+
+  public static class OtherThread implements Runnable {
+    @Override
+    public void run() {
+      OTHER_THREAD_STARTED = true;
+      while (OTHER_THREAD_CONTINUE) {
+        OTHER_THREAD_DID_SOMETHING = true;
+      }
+    }
+  }
+
+  public static void waitFor(long millis) {
+    try {
+      lock.wait(millis);
+    } catch (Exception e) {
+      System.out.println("Unexpected error: " + e);
+      e.printStackTrace();
+    }
+  }
+
+  public static void waitForSuspension(Thread target) {
+    while (!Suspension.isSuspended(target)) {
+      waitFor(100);
+    }
+  }
+
+  public static void waitForStart() {
+    while (!OTHER_THREAD_STARTED) {
+      waitFor(100);
+    }
+  }
+
+
+  public static void run() {
+    synchronized (lock) {
+      Thread other = new Thread(new OtherThread(), "TARGET THREAD");
+      try {
+        other.start();
+
+        waitForStart();
+
+        // Try to resume ourself.
+        try {
+          Suspension.resume(Thread.currentThread());
+        } catch (Exception e) {
+          if (!e.getMessage().equals("JVMTI_ERROR_THREAD_NOT_SUSPENDED")) {
+            System.out.println("incorrect error for resuming a non-suspended thread");
+          }
+        }
+        try {
+          Suspension.resume(other);
+        } catch (Exception e) {
+          if (!e.getMessage().equals("JVMTI_ERROR_THREAD_NOT_SUSPENDED")) {
+            System.out.println("incorrect error for resuming a non-suspended thread");
+          }
+        }
+
+        Suspension.suspend(other);
+        // Wait 1 second for the other thread to suspend.
+        waitForSuspension(other);
+        OTHER_THREAD_DID_SOMETHING = false;
+        // Wait a second to see if anything happens.
+        waitFor(1000);
+
+        if (OTHER_THREAD_DID_SOMETHING) {
+          System.out.println("Looks like other thread did something while suspended!");
+        }
+        // Resume always.
+        Suspension.resume(other);
+
+        // Wait another second.
+        waitFor(1000);
+
+        if (!OTHER_THREAD_DID_SOMETHING) {
+          System.out.println("Doesn't look like the thread unsuspended!");
+        }
+
+        // Stop the other thread.
+        OTHER_THREAD_CONTINUE = false;
+        // Wait for 1 second for it to die.
+        other.join(1000);
+
+        if (other.isAlive()) {
+          System.out.println("other thread didn't terminate in a reasonable time!");
+          Runtime.getRuntime().halt(1);
+        }
+      } catch (Throwable t) {
+        System.out.println("something was thrown. Runtime might be in unrecoverable state: " + t);
+        t.printStackTrace();
+        Runtime.getRuntime().halt(2);
+      }
+    }
+  }
+}
diff --git a/test/1903-suspend-self/expected.txt b/test/1903-suspend-self/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/1903-suspend-self/expected.txt
diff --git a/test/1903-suspend-self/info.txt b/test/1903-suspend-self/info.txt
new file mode 100644
index 0000000..779becc
--- /dev/null
+++ b/test/1903-suspend-self/info.txt
@@ -0,0 +1 @@
+Test jvmti suspend/resume of the current thread.
diff --git a/test/1903-suspend-self/run b/test/1903-suspend-self/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/1903-suspend-self/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1903-suspend-self/src/Main.java b/test/1903-suspend-self/src/Main.java
new file mode 100644
index 0000000..bd2028f
--- /dev/null
+++ b/test/1903-suspend-self/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1903.run();
+  }
+}
diff --git a/test/1903-suspend-self/src/art/Suspension.java b/test/1903-suspend-self/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1903-suspend-self/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+  // Suspends a thread using jvmti.
+  public native static void suspend(Thread thr);
+
+  // Resumes a thread using jvmti.
+  public native static void resume(Thread thr);
+
+  public native static boolean isSuspended(Thread thr);
+
+  public native static int[] suspendList(Thread... threads);
+  public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1903-suspend-self/src/art/Test1903.java b/test/1903-suspend-self/src/art/Test1903.java
new file mode 100644
index 0000000..cf2a55c
--- /dev/null
+++ b/test/1903-suspend-self/src/art/Test1903.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Test1903 {
+  public static final Object lock = new Object();
+
+  public static volatile boolean OTHER_THREAD_CONTINUE = true;
+  public static volatile boolean OTHER_THREAD_DID_SOMETHING = true;
+  public static volatile boolean OTHER_THREAD_STARTED = false;
+  public static volatile boolean OTHER_THREAD_RESUMED = false;
+
+  public static class OtherThread implements Runnable {
+    @Override
+    public void run() {
+      // Wake up main thread.
+      OTHER_THREAD_STARTED = true;
+      try {
+        Suspension.suspend(Thread.currentThread());
+        OTHER_THREAD_RESUMED = true;
+      } catch (Throwable t) {
+        System.out.println("Unexpected error occurred " + t);
+        t.printStackTrace();
+        Runtime.getRuntime().halt(2);
+      }
+    }
+  }
+
+  public static void waitFor(long millis) {
+    try {
+      lock.wait(millis);
+    } catch (Exception e) {
+      System.out.println("Unexpected error: " + e);
+      e.printStackTrace();
+    }
+  }
+
+  public static void waitForSuspension(Thread target) {
+    while (!Suspension.isSuspended(target)) {
+      waitFor(100);
+    }
+  }
+
+  public static void waitForStart() {
+    while (!OTHER_THREAD_STARTED) {
+      waitFor(100);
+    }
+  }
+
+  public static void run() {
+    synchronized (lock) {
+      Thread other = new Thread(new OtherThread(), "TARGET THREAD");
+      try {
+        other.start();
+
+        // Wait for the other thread to actually start doing things.
+
+        waitForStart();
+        waitForSuspension(other);
+
+        Suspension.resume(other);
+        for (int i = 0; i < 1000; i++) {
+          waitFor(100);
+          if (OTHER_THREAD_RESUMED) {
+            return;
+          }
+        }
+        System.out.println("Failed to resume thread!");
+        Runtime.getRuntime().halt(4);
+      } catch (Throwable t) {
+        System.out.println("something was thrown. Runtime might be in unrecoverable state: " + t);
+        t.printStackTrace();
+        Runtime.getRuntime().halt(2);
+      }
+    }
+  }
+}
diff --git a/test/1904-double-suspend/expected.txt b/test/1904-double-suspend/expected.txt
new file mode 100644
index 0000000..321b8a3
--- /dev/null
+++ b/test/1904-double-suspend/expected.txt
@@ -0,0 +1 @@
+Got exception JVMTI_ERROR_THREAD_SUSPENDED
diff --git a/test/1904-double-suspend/info.txt b/test/1904-double-suspend/info.txt
new file mode 100644
index 0000000..5d2415b
--- /dev/null
+++ b/test/1904-double-suspend/info.txt
@@ -0,0 +1 @@
+Test jvmti suspending a thread more than once.
diff --git a/test/1904-double-suspend/run b/test/1904-double-suspend/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/1904-double-suspend/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1904-double-suspend/src/Main.java b/test/1904-double-suspend/src/Main.java
new file mode 100644
index 0000000..a0e71c6
--- /dev/null
+++ b/test/1904-double-suspend/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1904.run();
+  }
+}
diff --git a/test/1904-double-suspend/src/art/Suspension.java b/test/1904-double-suspend/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1904-double-suspend/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+  // Suspends a thread using jvmti.
+  public native static void suspend(Thread thr);
+
+  // Resumes a thread using jvmti.
+  public native static void resume(Thread thr);
+
+  public native static boolean isSuspended(Thread thr);
+
+  public native static int[] suspendList(Thread... threads);
+  public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1904-double-suspend/src/art/Test1904.java b/test/1904-double-suspend/src/art/Test1904.java
new file mode 100644
index 0000000..8a52aa0
--- /dev/null
+++ b/test/1904-double-suspend/src/art/Test1904.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Test1904 {
+  public static final Object lock = new Object();
+
+  public static volatile boolean OTHER_THREAD_CONTINUE = true;
+  public static volatile boolean OTHER_THREAD_DID_SOMETHING = true;
+  public static volatile boolean OTHER_THREAD_STARTED = false;
+
+  public static class OtherThread implements Runnable {
+    @Override
+    public void run() {
+      OTHER_THREAD_STARTED = true;
+      while (OTHER_THREAD_CONTINUE) {
+        OTHER_THREAD_DID_SOMETHING = true;
+      }
+    }
+  }
+
+  public static void waitFor(long millis) {
+    try {
+      lock.wait(millis);
+    } catch (Exception e) {
+      System.out.println("Unexpected error: " + e);
+      e.printStackTrace();
+    }
+  }
+
+  public static void waitForSuspension(Thread target) {
+    while (!Suspension.isSuspended(target)) {
+      waitFor(100);
+    }
+  }
+
+  public static void waitForStart() {
+    while (!OTHER_THREAD_STARTED) {
+      waitFor(100);
+    }
+  }
+
+
+  public static void run() {
+    synchronized (lock) {
+      Thread other = new Thread(new OtherThread(), "TARGET THREAD");
+      try {
+        other.start();
+
+        waitForStart();
+
+        Suspension.suspend(other);
+
+        waitForSuspension(other);
+        OTHER_THREAD_DID_SOMETHING = false;
+        // Wait a second to see if anything happens.
+        waitFor(1000);
+
+        if (OTHER_THREAD_DID_SOMETHING) {
+          System.out.println("Looks like other thread did something while suspended!");
+        }
+
+        try {
+          Suspension.suspend(other);
+        } catch (Exception e) {
+          System.out.println("Got exception " + e.getMessage());
+        }
+
+        // Resume always.
+        Suspension.resume(other);
+
+        // Wait another second.
+        waitFor(1000);
+
+        if (!OTHER_THREAD_DID_SOMETHING) {
+          System.out.println("Doesn't look like the thread unsuspended!");
+        }
+
+        // Stop the other thread.
+        OTHER_THREAD_CONTINUE = false;
+        // Wait for 1 second for it to die.
+        other.join(1000);
+
+        if (other.isAlive()) {
+          System.out.println("other thread didn't terminate in a reasonable time!");
+          Runtime.getRuntime().halt(1);
+        }
+      } catch (Throwable t) {
+        System.out.println("something was thrown. Runtime might be in unrecoverable state: " + t);
+        t.printStackTrace();
+        Runtime.getRuntime().halt(2);
+      }
+    }
+  }
+}
diff --git a/test/1905-suspend-native/expected.txt b/test/1905-suspend-native/expected.txt
new file mode 100644
index 0000000..43b2669
--- /dev/null
+++ b/test/1905-suspend-native/expected.txt
@@ -0,0 +1,8 @@
+Resumer: isNativeThreadSpinning() = true
+Resumer: isSuspended(spinner) = false
+Resumer: Suspended spinner while native spinning
+Resumer: isNativeThreadSpinning() = true
+Resumer: isSuspended(spinner) = true
+Resumer: resumed spinner while native spinning
+Resumer: isNativeThreadSpinning() = true
+Resumer: isSuspended(spinner) = false
diff --git a/test/1905-suspend-native/info.txt b/test/1905-suspend-native/info.txt
new file mode 100644
index 0000000..3545d59
--- /dev/null
+++ b/test/1905-suspend-native/info.txt
@@ -0,0 +1 @@
+Tests jvmti suspending of a thread that is spinning in native code.
diff --git a/test/1905-suspend-native/native_suspend.cc b/test/1905-suspend-native/native_suspend.cc
new file mode 100644
index 0000000..95b8da2
--- /dev/null
+++ b/test/1905-suspend-native/native_suspend.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <atomic>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1905NativeSuspend {
+
+std::atomic<bool> done(false);
+std::atomic<bool> started(false);
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1905_nativeSpin(JNIEnv*, jclass) {
+  while (!done.load()) {
+    started.store(true);
+  }
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_art_Test1905_isNativeThreadSpinning(JNIEnv*, jclass) {
+  return started.load();
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1905_nativeResume(JNIEnv*, jclass) {
+  done.store(true);
+}
+
+}  // namespace Test1905NativeSuspend
+}  // namespace art
diff --git a/test/1905-suspend-native/run b/test/1905-suspend-native/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/1905-suspend-native/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1905-suspend-native/src/Main.java b/test/1905-suspend-native/src/Main.java
new file mode 100644
index 0000000..42c02d0
--- /dev/null
+++ b/test/1905-suspend-native/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1905.run();
+  }
+}
diff --git a/test/1905-suspend-native/src/art/Suspension.java b/test/1905-suspend-native/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1905-suspend-native/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+  // Suspends a thread using jvmti.
+  public native static void suspend(Thread thr);
+
+  // Resumes a thread using jvmti.
+  public native static void resume(Thread thr);
+
+  public native static boolean isSuspended(Thread thr);
+
+  public native static int[] suspendList(Thread... threads);
+  public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1905-suspend-native/src/art/Test1905.java b/test/1905-suspend-native/src/art/Test1905.java
new file mode 100644
index 0000000..ec39019
--- /dev/null
+++ b/test/1905-suspend-native/src/art/Test1905.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Test1905 {
+  public static void run() throws Exception {
+    final Thread spinner = new Thread(() -> {
+      nativeSpin();
+    }, "Spinner");
+
+    final Thread resumer = new Thread(() -> {
+      String me = Thread.currentThread().getName();
+
+      // wait for the other thread to start spinning.
+      while (!isNativeThreadSpinning()) { }
+
+      System.out.println(me + ": isNativeThreadSpinning() = " + isNativeThreadSpinning());
+      System.out.println(me + ": isSuspended(spinner) = " + Suspension.isSuspended(spinner));
+
+      // Suspend it from java.
+      Suspension.suspend(spinner);
+
+      System.out.println(me + ": Suspended spinner while native spinning");
+      System.out.println(me + ": isNativeThreadSpinning() = " + isNativeThreadSpinning());
+      System.out.println(me + ": isSuspended(spinner) = " + Suspension.isSuspended(spinner));
+
+      // Resume it from java. It is still native spinning.
+      Suspension.resume(spinner);
+
+      System.out.println(me + ": resumed spinner while native spinning");
+      System.out.println(me + ": isNativeThreadSpinning() = " + isNativeThreadSpinning());
+      System.out.println(me + ": isSuspended(spinner) = " + Suspension.isSuspended(spinner));
+      nativeResume();
+    }, "Resumer");
+
+    spinner.start();
+    resumer.start();
+
+    spinner.join();
+    resumer.join();
+  }
+
+  public static native void nativeSpin();
+  public static native void nativeResume();
+  public static native boolean isNativeThreadSpinning();
+}
diff --git a/test/1906-suspend-list-me-first/expected.txt b/test/1906-suspend-list-me-first/expected.txt
new file mode 100644
index 0000000..503d728
--- /dev/null
+++ b/test/1906-suspend-list-me-first/expected.txt
@@ -0,0 +1 @@
+Second thread suspended before first thread suspended self!
diff --git a/test/1906-suspend-list-me-first/info.txt b/test/1906-suspend-list-me-first/info.txt
new file mode 100644
index 0000000..2b2f4e1
--- /dev/null
+++ b/test/1906-suspend-list-me-first/info.txt
@@ -0,0 +1 @@
+Test jvmti SuspendThreadList with the current thread as the first thread in the list.
diff --git a/test/1906-suspend-list-me-first/run b/test/1906-suspend-list-me-first/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/1906-suspend-list-me-first/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1906-suspend-list-me-first/src/Main.java b/test/1906-suspend-list-me-first/src/Main.java
new file mode 100644
index 0000000..1c8432c
--- /dev/null
+++ b/test/1906-suspend-list-me-first/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1906.run();
+  }
+}
diff --git a/test/1906-suspend-list-me-first/src/art/Suspension.java b/test/1906-suspend-list-me-first/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1906-suspend-list-me-first/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+  // Suspends a thread using jvmti.
+  public native static void suspend(Thread thr);
+
+  // Resumes a thread using jvmti.
+  public native static void resume(Thread thr);
+
+  public native static boolean isSuspended(Thread thr);
+
+  public native static int[] suspendList(Thread... threads);
+  public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1906-suspend-list-me-first/src/art/Test1906.java b/test/1906-suspend-list-me-first/src/art/Test1906.java
new file mode 100644
index 0000000..9bb272e
--- /dev/null
+++ b/test/1906-suspend-list-me-first/src/art/Test1906.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Test1906 {
+  public static final Object lock = new Object();
+
+  public static volatile boolean SECOND_THREAD_RUN = true;
+  public static volatile boolean SECOND_THREAD_RUNNING = false;
+
+  public static void waitFor(long millis) {
+    try {
+      lock.wait(millis);
+    } catch (Exception e) {
+      System.out.println("Unexpected error: " + e);
+      e.printStackTrace();
+    }
+  }
+
+  public static void waitForSuspension(Thread target) {
+    while (!Suspension.isSuspended(target)) {
+      waitFor(100);
+    }
+  }
+
+  public static void run() {
+    synchronized (lock) {
+      final Thread second_thread = new Thread(
+          () -> {
+            while (SECOND_THREAD_RUN) { SECOND_THREAD_RUNNING = true; }
+          },
+          "SECONDARY THREAD");
+      Thread self_thread = new Thread(
+          () -> {
+            try {
+              // Wait for second thread to start doing stuff.
+              while (!SECOND_THREAD_RUNNING) { }
+              Suspension.suspendList(Thread.currentThread(), second_thread);
+            } catch (Throwable t) {
+              System.out.println("Unexpected error occurred " + t);
+              t.printStackTrace();
+              Runtime.getRuntime().halt(2);
+            }
+          },
+          "TARGET THREAD");
+      try {
+        second_thread.start();
+        self_thread.start();
+
+        waitForSuspension(self_thread);
+
+        // Wait to see if second thread is running.
+        SECOND_THREAD_RUNNING = false;
+        waitFor(1000);
+
+        if (SECOND_THREAD_RUNNING) {
+          System.out.println("Second thread running after first thread suspended self!");
+        } else {
+          System.out.println("Second thread suspended before first thread suspended self!");
+        }
+
+        Suspension.resume(self_thread);
+        waitForSuspension(second_thread);
+        Suspension.resume(second_thread);
+        self_thread.join();
+        SECOND_THREAD_RUN = false;
+        second_thread.join();
+      } catch (Throwable t) {
+        System.out.println("something was thrown. Runtime might be in unrecoverable state: " + t);
+        t.printStackTrace();
+        Runtime.getRuntime().halt(2);
+      }
+    }
+  }
+}
diff --git a/test/1907-suspend-list-self-twice/expected.txt b/test/1907-suspend-list-self-twice/expected.txt
new file mode 100644
index 0000000..cd9b53f
--- /dev/null
+++ b/test/1907-suspend-list-self-twice/expected.txt
@@ -0,0 +1,2 @@
+Suspend self twice returned: [0, 14]
+Thread was no longer suspended after one resume.
diff --git a/test/1907-suspend-list-self-twice/info.txt b/test/1907-suspend-list-self-twice/info.txt
new file mode 100644
index 0000000..923c545
--- /dev/null
+++ b/test/1907-suspend-list-self-twice/info.txt
@@ -0,0 +1 @@
+Test jvmti SuspendThreadList with the current thread on it twice.
diff --git a/test/1907-suspend-list-self-twice/run b/test/1907-suspend-list-self-twice/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/1907-suspend-list-self-twice/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1907-suspend-list-self-twice/src/Main.java b/test/1907-suspend-list-self-twice/src/Main.java
new file mode 100644
index 0000000..910848a
--- /dev/null
+++ b/test/1907-suspend-list-self-twice/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1907.run();
+  }
+}
diff --git a/test/1907-suspend-list-self-twice/src/art/Suspension.java b/test/1907-suspend-list-self-twice/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1907-suspend-list-self-twice/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+  // Suspends a thread using jvmti.
+  public native static void suspend(Thread thr);
+
+  // Resumes a thread using jvmti.
+  public native static void resume(Thread thr);
+
+  public native static boolean isSuspended(Thread thr);
+
+  public native static int[] suspendList(Thread... threads);
+  public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1907-suspend-list-self-twice/src/art/Test1907.java b/test/1907-suspend-list-self-twice/src/art/Test1907.java
new file mode 100644
index 0000000..504f7f3
--- /dev/null
+++ b/test/1907-suspend-list-self-twice/src/art/Test1907.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.Arrays;
+
+public class Test1907 {
+  public static final Object lock = new Object();
+
+  public static void waitFor(long millis) {
+    try {
+      lock.wait(millis);
+    } catch (Exception e) {
+      System.out.println("Unexpected error: " + e);
+      e.printStackTrace();
+    }
+  }
+
+  public static void waitForSuspension(Thread target) {
+    while (!Suspension.isSuspended(target)) {
+      waitFor(100);
+    }
+  }
+
+  public static void run() {
+    synchronized (lock) {
+      Thread thrd = new Thread(
+          () -> {
+            try {
+              // Put self twice in the suspend list
+              System.out.println("Suspend self twice returned: " +
+                  Arrays.toString(
+                      Suspension.suspendList(Thread.currentThread(), Thread.currentThread())));
+            } catch (Throwable t) {
+              System.out.println("Unexpected error occurred " + t);
+              t.printStackTrace();
+              Runtime.getRuntime().halt(2);
+            }
+          },
+          "TARGET THREAD");
+      try {
+        thrd.start();
+
+        // Wait for at least one suspend to happen.
+        waitForSuspension(thrd);
+
+        // Wake it up.
+        Suspension.resume(thrd);
+        waitFor(1000);
+
+        // Is it suspended.
+        if (Suspension.isSuspended(thrd)) {
+          Suspension.resume(thrd);
+          thrd.join();
+          System.out.println("Thread was still suspended after one resume.");
+        } else {
+          thrd.join();
+          System.out.println("Thread was no longer suspended after one resume.");
+        }
+
+      } catch (Throwable t) {
+        System.out.println("something was thrown. Runtime might be in unrecoverable state: " + t);
+        t.printStackTrace();
+        Runtime.getRuntime().halt(2);
+      }
+    }
+  }
+}
diff --git a/test/1908-suspend-native-resume-self/expected.txt b/test/1908-suspend-native-resume-self/expected.txt
new file mode 100644
index 0000000..13cc517
--- /dev/null
+++ b/test/1908-suspend-native-resume-self/expected.txt
@@ -0,0 +1,10 @@
+Resumer: isNativeThreadSpinning() = true
+Resumer: isSuspended(spinner) = false
+Resumer: Suspended spinner while native spinning
+Resumer: isNativeThreadSpinning() = true
+Resumer: isSuspended(spinner) = true
+Resuming other thread
+other thread attempting self resume
+Resumer: isSuspended(spinner) = true
+real resume
+other thread resumed.
diff --git a/test/1908-suspend-native-resume-self/info.txt b/test/1908-suspend-native-resume-self/info.txt
new file mode 100644
index 0000000..3545d59
--- /dev/null
+++ b/test/1908-suspend-native-resume-self/info.txt
@@ -0,0 +1 @@
+Tests jvmti suspending of a thread that is spinning in native code.
diff --git a/test/1908-suspend-native-resume-self/native_suspend_resume.cc b/test/1908-suspend-native-resume-self/native_suspend_resume.cc
new file mode 100644
index 0000000..158b22c
--- /dev/null
+++ b/test/1908-suspend-native-resume-self/native_suspend_resume.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <atomic>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1908NativeSuspendResume {
+
+std::atomic<bool> done(false);
+std::atomic<bool> started(false);
+std::atomic<bool> resumed(false);
+std::atomic<bool> resuming(false);
+
+extern "C" JNIEXPORT jint JNICALL Java_art_Test1908_nativeSpinAndResume(JNIEnv*,
+                                                                        jclass,
+                                                                        jthread thr) {
+  while (!done.load()) {
+    started.store(true);
+  }
+  resuming.store(true);
+  jint ret = jvmti_env->ResumeThread(thr);
+  resumed.store(true);
+  return ret;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_art_Test1908_isNativeThreadSpinning(JNIEnv*, jclass) {
+  return started.load();
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1908_waitForNativeResumeStarted(JNIEnv*, jclass) {
+  while (!resuming.load()) {}
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1908_waitForNativeResumeFinished(JNIEnv*, jclass) {
+  while (!resumed.load()) {}
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1908_nativeResume(JNIEnv*, jclass) {
+  done.store(true);
+}
+
+}  // namespace Test1908NativeSuspendResume
+}  // namespace art
diff --git a/test/1908-suspend-native-resume-self/run b/test/1908-suspend-native-resume-self/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/1908-suspend-native-resume-self/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1908-suspend-native-resume-self/src/Main.java b/test/1908-suspend-native-resume-self/src/Main.java
new file mode 100644
index 0000000..312adc4
--- /dev/null
+++ b/test/1908-suspend-native-resume-self/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1908.run();
+  }
+}
diff --git a/test/1908-suspend-native-resume-self/src/art/Suspension.java b/test/1908-suspend-native-resume-self/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1908-suspend-native-resume-self/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+  // Suspends a thread using jvmti.
+  public native static void suspend(Thread thr);
+
+  // Resumes a thread using jvmti.
+  public native static void resume(Thread thr);
+
+  public native static boolean isSuspended(Thread thr);
+
+  public native static int[] suspendList(Thread... threads);
+  public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1908-suspend-native-resume-self/src/art/Test1908.java b/test/1908-suspend-native-resume-self/src/art/Test1908.java
new file mode 100644
index 0000000..9b7020a
--- /dev/null
+++ b/test/1908-suspend-native-resume-self/src/art/Test1908.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Test1908 {
+  public static void run() throws Exception {
+    final Thread spinner = new Thread(() -> {
+      int ret = nativeSpinAndResume(Thread.currentThread());
+      if (ret != 13) {
+        System.out.println("Got " + ret + " instead of JVMTI_ERROR_THREAD_NOT_SUSPENDED");
+      }
+    }, "Spinner");
+
+    final Thread resumer = new Thread(() -> {
+      String me = Thread.currentThread().getName();
+
+      // wait for the other thread to start spinning.
+      while (!isNativeThreadSpinning()) { }
+
+      System.out.println(me + ": isNativeThreadSpinning() = " + isNativeThreadSpinning());
+      System.out.println(me + ": isSuspended(spinner) = " + Suspension.isSuspended(spinner));
+
+      // Suspend it from java.
+      Suspension.suspend(spinner);
+
+      System.out.println(me + ": Suspended spinner while native spinning");
+      System.out.println(me + ": isNativeThreadSpinning() = " + isNativeThreadSpinning());
+      System.out.println(me + ": isSuspended(spinner) = " + Suspension.isSuspended(spinner));
+
+      System.out.println("Resuming other thread");
+      nativeResume();
+      waitForNativeResumeStarted();
+      // Wait for the other thread to try to resume itself
+      try { Thread.currentThread().sleep(1000); } catch (Exception e) {}
+
+      System.out.println("other thread attempting self resume");
+      System.out.println(me + ": isSuspended(spinner) = " + Suspension.isSuspended(spinner));
+
+      System.out.println("real resume");
+      Suspension.resume(spinner);
+      waitForNativeResumeFinished();
+      System.out.println("other thread resumed.");
+    }, "Resumer");
+
+    spinner.start();
+    resumer.start();
+
+    spinner.join();
+    resumer.join();
+  }
+
+  public static native int nativeSpinAndResume(Thread cur);
+  public static native void nativeResume();
+  public static native boolean isNativeThreadSpinning();
+  public static native void waitForNativeResumeFinished();
+  public static native void waitForNativeResumeStarted();
+}
diff --git a/test/1909-per-agent-tls/agent_tls.cc b/test/1909-per-agent-tls/agent_tls.cc
new file mode 100644
index 0000000..14c82e3
--- /dev/null
+++ b/test/1909-per-agent-tls/agent_tls.cc
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1909AgentTLS {
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1909_setTLS(JNIEnv* env,
+                                                           jclass,
+                                                           jlong jvmti_env_ptr,
+                                                           jthread thr,
+                                                           jlong data) {
+  JvmtiErrorToException(env,
+                        reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr),
+                        reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr)->SetThreadLocalStorage(
+                            thr, reinterpret_cast<const void*>(static_cast<intptr_t>(data))));
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_art_Test1909_getTLS(JNIEnv* env,
+                                                            jclass,
+                                                            jlong jvmti_env_ptr,
+                                                            jthread thr) {
+  void* res = nullptr;
+  JvmtiErrorToException(
+      env,
+      reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr),
+      reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr)->GetThreadLocalStorage(thr, &res));
+  return static_cast<jlong>(reinterpret_cast<intptr_t>(res));
+}
+
+extern "C" JNIEXPORT void Java_art_Test1909_destroyJvmtiEnv(JNIEnv* env,
+                                                            jclass,
+                                                            jlong jvmti_env_ptr) {
+  JvmtiErrorToException(env,
+                        jvmti_env,
+                        reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr)->DisposeEnvironment());
+}
+
+extern "C" JNIEXPORT jlong Java_art_Test1909_newJvmtiEnv(JNIEnv* env, jclass) {
+  JavaVM* vm = nullptr;
+  if (env->GetJavaVM(&vm) != 0) {
+    ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+    env->ThrowNew(rt_exception.get(), "Unable to get JavaVM");
+    return -1;
+  }
+  jvmtiEnv* new_env = nullptr;
+  if (vm->GetEnv(reinterpret_cast<void**>(&new_env), JVMTI_VERSION_1_0) != 0) {
+    ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+    env->ThrowNew(rt_exception.get(), "Unable to create new jvmtiEnv");
+    return -1;
+  }
+  return static_cast<jlong>(reinterpret_cast<intptr_t>(new_env));
+}
+
+}  // namespace Test1909AgentTLS
+}  // namespace art
diff --git a/test/1909-per-agent-tls/expected.txt b/test/1909-per-agent-tls/expected.txt
new file mode 100644
index 0000000..386f3d2
--- /dev/null
+++ b/test/1909-per-agent-tls/expected.txt
@@ -0,0 +1 @@
+Test passed
diff --git a/test/1909-per-agent-tls/info.txt b/test/1909-per-agent-tls/info.txt
new file mode 100644
index 0000000..00acefd
--- /dev/null
+++ b/test/1909-per-agent-tls/info.txt
@@ -0,0 +1 @@
+Tests jvmti behavior of GetThreadLocalStorage with multiple threads.
diff --git a/test/1909-per-agent-tls/run b/test/1909-per-agent-tls/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/1909-per-agent-tls/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1909-per-agent-tls/src/Main.java b/test/1909-per-agent-tls/src/Main.java
new file mode 100644
index 0000000..befebea
--- /dev/null
+++ b/test/1909-per-agent-tls/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1909.run();
+  }
+}
diff --git a/test/1909-per-agent-tls/src/art/Main.java b/test/1909-per-agent-tls/src/art/Main.java
new file mode 100644
index 0000000..aa5498b
--- /dev/null
+++ b/test/1909-per-agent-tls/src/art/Main.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+// Binder class so the agent's C code has something that can be bound and exposed to tests.
+// In a package to separate cleanly and work around CTS reference issues (though this class
+// should be replaced in the CTS version).
+public class Main {
+  // Load the given class with the given classloader, and bind all native methods to corresponding
+  // C methods in the agent. Will abort if any of the steps fail.
+  public static native void bindAgentJNI(String className, ClassLoader classLoader);
+  // Same as above, giving the class directly.
+  public static native void bindAgentJNIForClass(Class<?> klass);
+
+  // Common infrastructure.
+  public static native void setTag(Object o, long tag);
+  public static native long getTag(Object o);
+}
diff --git a/test/1909-per-agent-tls/src/art/Test1909.java b/test/1909-per-agent-tls/src/art/Test1909.java
new file mode 100644
index 0000000..245397d
--- /dev/null
+++ b/test/1909-per-agent-tls/src/art/Test1909.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+
+public class Test1909 {
+
+  public static class ThreadHolder {
+    public Thread thr;
+    public ThreadHolder(Thread thr) {
+      this.thr = thr;
+    }
+
+    public long getTLS(long jvmtienv) {
+      return Test1909.getTLS(jvmtienv, this.thr);
+    }
+    public void setTLS(long jvmtienv, long ptr) {
+      Test1909.setTLS(jvmtienv, this.thr, ptr);
+    }
+  }
+
+  public static class ThreadWaiter {
+    public boolean exit;
+    public Thread thr;
+    public final Object lock;
+
+    public ThreadWaiter() {
+      this.exit = false;
+      this.lock = new Object();
+      this.thr = new Thread(() -> {
+        try {
+          synchronized (lock) {
+            while (!this.exit) {
+              this.lock.wait();
+            }
+          }
+        } catch (Exception e) {
+          e.printStackTrace();
+        }
+      });
+      // Kill threads if we exit.
+      thr.setDaemon(true);
+      thr.start();
+    }
+
+    public void cleanup() throws Exception {
+      synchronized (lock) {
+        exit = true;
+        lock.notifyAll();
+      }
+      thr.join();
+    }
+    public long getTLS(long jvmtienv) {
+      return Test1909.getTLS(jvmtienv, this.thr);
+    }
+    public void setTLS(long jvmtienv, long ptr) {
+      Test1909.setTLS(jvmtienv, this.thr, ptr);
+    }
+  }
+
+  public static void checkEq(long a, long b) {
+    if (a != b) {
+      throw new Error("Expected: " + a + " got: " + b);
+    }
+  }
+
+  public static void run() throws Exception {
+    ThreadHolder tc = new ThreadHolder(Thread.currentThread());
+    ThreadWaiter t1 = new ThreadWaiter();
+    long e1 = newJvmtiEnv();
+    long e2 = newJvmtiEnv();
+
+    // Everything should be 0
+    checkEq(0, tc.getTLS(e1));
+    checkEq(0, t1.getTLS(e1));
+    checkEq(0, tc.getTLS(e2));
+    checkEq(0, t1.getTLS(e2));
+
+    // Set in one pair.
+    tc.setTLS(e1, 1);
+    checkEq(1, tc.getTLS(e1));
+    checkEq(0, t1.getTLS(e1));
+    checkEq(0, tc.getTLS(e2));
+    checkEq(0, t1.getTLS(e2));
+
+    // Set in another pair.
+    t1.setTLS(e1, 2);
+    checkEq(1, tc.getTLS(e1));
+    checkEq(2, t1.getTLS(e1));
+    checkEq(0, tc.getTLS(e2));
+    checkEq(0, t1.getTLS(e2));
+
+    // Set in third pair.
+    tc.setTLS(e2, 3);
+    checkEq(1, tc.getTLS(e1));
+    checkEq(2, t1.getTLS(e1));
+    checkEq(3, tc.getTLS(e2));
+    checkEq(0, t1.getTLS(e2));
+
+    // Set in fourth pair.
+    t1.setTLS(e2, 4);
+    checkEq(1, tc.getTLS(e1));
+    checkEq(2, t1.getTLS(e1));
+    checkEq(3, tc.getTLS(e2));
+    checkEq(4, t1.getTLS(e2));
+
+    // Create a new thread and make sure everything is 0.
+    ThreadWaiter t2 = new ThreadWaiter();
+    checkEq(1, tc.getTLS(e1));
+    checkEq(2, t1.getTLS(e1));
+    checkEq(0, t2.getTLS(e1));
+    checkEq(3, tc.getTLS(e2));
+    checkEq(4, t1.getTLS(e2));
+    checkEq(0, t2.getTLS(e2));
+
+    // Create a new jvmtienv and make sure everything is 0.
+    long e3 = newJvmtiEnv();
+    checkEq(1, tc.getTLS(e1));
+    checkEq(2, t1.getTLS(e1));
+    checkEq(0, t2.getTLS(e1));
+    checkEq(3, tc.getTLS(e2));
+    checkEq(4, t1.getTLS(e2));
+    checkEq(0, t2.getTLS(e2));
+    checkEq(0, tc.getTLS(e3));
+    checkEq(0, t1.getTLS(e3));
+    checkEq(0, t2.getTLS(e3));
+
+    // Delete an env without data and make sure everything is still there.
+    destroyJvmtiEnv(e3);
+    checkEq(1, tc.getTLS(e1));
+    checkEq(2, t1.getTLS(e1));
+    checkEq(0, t2.getTLS(e1));
+    checkEq(3, tc.getTLS(e2));
+    checkEq(4, t1.getTLS(e2));
+    checkEq(0, t2.getTLS(e2));
+
+    // Delete an env with data and make sure everything is still there.
+    destroyJvmtiEnv(e2);
+    checkEq(1, tc.getTLS(e1));
+    checkEq(2, t1.getTLS(e1));
+    checkEq(0, t2.getTLS(e1));
+
+    // Delete a thread. Make sure other thread still has data.
+    t1.cleanup();
+    checkEq(1, tc.getTLS(e1));
+    checkEq(0, t2.getTLS(e1));
+
+    t2.cleanup();
+
+    System.out.println("Test passed");
+  }
+
+  public static native long getTLS(long jvmtienv, Thread thr);
+  public static native void setTLS(long jvmtienv, Thread thr, long ptr);
+  public static native long newJvmtiEnv();
+  public static native void destroyJvmtiEnv(long jvmtienv);
+}
diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java
index 3dce23f..6d3abb1 100644
--- a/test/536-checker-intrinsic-optimization/src/Main.java
+++ b/test/536-checker-intrinsic-optimization/src/Main.java
@@ -340,7 +340,7 @@
 
   /// CHECK-START-MIPS: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after)
   /// CHECK:          InvokeVirtual {{.*\.equals.*}} intrinsic:StringEquals
-  /// CHECK-NOT:      beq r0,
+  /// CHECK-NOT:      beq zero,
   /// CHECK-NOT:      beqz
   /// CHECK-NOT:      beqzc
   // Terminate the scope for the CHECK-NOT search at the class field or length comparison,
diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc
index 0bdbade..ae3dad8 100644
--- a/test/595-profile-saving/profile-saving.cc
+++ b/test/595-profile-saving/profile-saving.cc
@@ -38,7 +38,10 @@
   CHECK(method != nullptr);
   ScopedObjectAccess soa(env);
   ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method);
-  ProfilingInfo::Create(soa.Self(), exec->GetArtMethod(), /* retry_allocation */ true);
+  ArtMethod* art_method = exec->GetArtMethod();
+  if (!ProfilingInfo::Create(soa.Self(), art_method, /* retry_allocation */ true)) {
+    LOG(ERROR) << "Failed to create profiling info for method " << art_method->PrettyMethod();
+  }
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfileProcessing(JNIEnv*, jclass) {
@@ -55,8 +58,9 @@
   ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method);
   ArtMethod* art_method = exec->GetArtMethod();
   return ProfileSaver::HasSeenMethod(std::string(filename_chars.c_str()),
-                                     art_method->GetDexFile(),
-                                     art_method->GetDexMethodIndex());
+                                     /*hot*/ true,
+                                     MethodReference(art_method->GetDexFile(),
+                                                     art_method->GetDexMethodIndex()));
 }
 
 }  // namespace
diff --git a/test/596-app-images/src/Main.java b/test/596-app-images/src/Main.java
index 8ee3c88..88d95f4 100644
--- a/test/596-app-images/src/Main.java
+++ b/test/596-app-images/src/Main.java
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
 class Main {
   static class Inner {
     final public static int abc = 10;
@@ -46,13 +50,76 @@
     if (!checkInitialized(StaticFieldsInit.class))
       System.out.println("StaticFieldsInit class is not initialized!");
 
-    if (checkInitialized(StaticInternString.class))
-      System.out.println("StaticInternString class is initialized!");
+    if (!checkInitialized(StaticInternString.class))
+      System.out.println("StaticInternString class is not initialized!");
+
+    StringBuffer sb = new StringBuffer();
+    sb.append("java.");
+    sb.append("abc.");
+    sb.append("Action");
+
+    String tmp = sb.toString();
+    String intern = tmp.intern();
+
+    assertNotEqual(tmp, intern, "Dynamically constructed String, not interned.");
+    assertEqual(intern, StaticInternString.intent, "Static encoded literal String not interned.");
+    assertEqual(BootInternedString.boot, BootInternedString.boot.intern(),
+        "Static encoded literal String not moved back to runtime intern table.");
+
+    try {
+      Field f = StaticInternString.class.getDeclaredField("intent");
+      assertEqual(intern, f.get(null), "String Literals are not interned properly.");
+
+    } catch (Exception e) {
+      System.out.println("Exception");
+    }
+
+    assertEqual(StaticInternString.getIntent(), StaticInternString2.getIntent(),
+        "String Literals are not intenred properly, App image static strings duplicated.");
+
+    // reload the class StaticInternString, check whether static strings interned properly
+    final String DEX_FILE = System.getenv("DEX_LOCATION") + "/596-app-images.jar";
+    final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path");
+
+    try {
+      Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
+      if (pathClassLoader == null) {
+        throw new AssertionError("Counldn't find path class loader class");
+      }
+      Constructor<?> ctor =
+          pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class);
+      ClassLoader loader = (ClassLoader) ctor.newInstance(
+          DEX_FILE, LIBRARY_SEARCH_PATH, null);
+
+      Class<?> staticInternString = loader.loadClass("StaticInternString");
+
+      if (!checkAppImageContains(staticInternString)) {
+        System.out.println("Not loaded again.");
+      }
+      Method getIntent = staticInternString.getDeclaredMethod("getIntent");
+
+      assertEqual(StaticInternString.getIntent(), getIntent.invoke(staticInternString),
+          "Dynamically loaded app image's literal strings not interned properly.");
+    } catch (Exception e) {
+      e.printStackTrace(System.out);
+    }
+
   }
 
   public static native boolean checkAppImageLoaded();
   public static native boolean checkAppImageContains(Class<?> klass);
   public static native boolean checkInitialized(Class<?> klass);
+
+  public static void assertEqual(Object a, Object b, String msg) {
+    if (a != b)
+      System.out.println(msg);
+  }
+
+  public static void assertNotEqual(Object a, Object b, String msg) {
+    if (a == b)
+      System.out.println(msg);
+  }
+
 }
 
 class StaticFields{
@@ -68,6 +135,21 @@
 }
 
 class StaticInternString {
-  final public static String intern = "java.abc.Action";
+  final public static String intent = "java.abc.Action";
+  static public String getIntent() {
+    return intent;
+  }
+}
+
+class BootInternedString {
+  final public static String boot = "double";
+}
+
+class StaticInternString2 {
+  final public static String intent = "java.abc.Action";
+
+  static String getIntent() {
+    return intent;
+  }
 }
 
diff --git a/test/606-erroneous-class/jasmin-multidex/ClassA.j b/test/606-erroneous-class/jasmin-multidex/ClassA.j
new file mode 100644
index 0000000..50c6755
--- /dev/null
+++ b/test/606-erroneous-class/jasmin-multidex/ClassA.j
@@ -0,0 +1,30 @@
+; Copyright (C) 2017 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class                   public final ClassA
+.super                   java/lang/Object
+
+.method                  public static foo()V
+   .limit stack          1
+   .limit locals         0
+   ; Obtain the ErrClass type from Dex cache of the first Dex file. Note that
+   ; because the first Dex file has already been verified, we know the class
+   ; is erroneous at this point.
+   getstatic             ClassB/g LErrClass;
+   ; Use the object in a way that will try to store the ErrClass type in
+   ; the Dex cache of the second Dex file.
+   invokevirtual         ErrClass/foo()V
+   return
+.end method
+
diff --git a/test/606-erroneous-class/smali-multidex/ClassA.smali b/test/606-erroneous-class/smali-multidex/ClassA.smali
deleted file mode 100644
index f87fcb2..0000000
--- a/test/606-erroneous-class/smali-multidex/ClassA.smali
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-.class public final LClassA;
-.super Ljava/lang/Object;
-
-.method public static foo()V
-    .registers 1
-    # Obtain the ErrClass type from Dex cache of the first Dex file. Note that
-    # because the first Dex file has already been verified, we know the class
-    # is erroneous at this point.
-    sget-object v0, LClassB;->g:LErrClass;
-    # Use the object in a way that will try to store the ErrClass type in
-    # the Dex cache of the second Dex file.
-    invoke-virtual {v0}, LErrClass;->foo()V
-.end method
diff --git a/test/659-unpadded-array/expected.txt b/test/659-unpadded-array/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/659-unpadded-array/expected.txt
diff --git a/test/659-unpadded-array/info.txt b/test/659-unpadded-array/info.txt
new file mode 100644
index 0000000..905c529
--- /dev/null
+++ b/test/659-unpadded-array/info.txt
@@ -0,0 +1,3 @@
+Regression test for the concurrent GC whose region space had
+a bug when the request for allocation ended up using 'usable_size'
+instead of the initially requested number of bytes.
diff --git a/test/659-unpadded-array/src-art/Main.java b/test/659-unpadded-array/src-art/Main.java
new file mode 100644
index 0000000..80fd6e2
--- /dev/null
+++ b/test/659-unpadded-array/src-art/Main.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import dalvik.system.VMRuntime;
+
+public class Main {
+  public static void main(String[] args) {
+    // Call our optimization API, we used to have a bug in the RegionSpace on large
+    // objects allocated through it.
+    Object[] o = (Object[]) VMRuntime.getRuntime().newUnpaddedArray(Object.class, 70000);
+
+    // Make the test run for 30 seconds to be less dependent on GC heuristics.
+    long time = System.currentTimeMillis();
+    int i = 1;
+    do {
+      allocateIntArray(i);
+      for (int j = 0; j < o.length; j++) {
+        if (o[j] != null) {
+          // Just print, not throw, to get into "interesting" issues (eg the first
+          // element that will not be null is the class of the object, the second is
+          // actually the first element of the int array).
+          System.out.println("Unexpected value: " + o[j]);
+        }
+      }
+      if (i < 100000) {
+        i++;
+      } else {
+        i = 0;
+      }
+    } while (System.currentTimeMillis() - time < 30000);
+  }
+
+  static void allocateIntArray(int i) {
+    int[] intArray = new int[i];
+    for (int j = 0; j < intArray.length; j++) {
+      intArray[j] = 1;
+    }
+  }
+}
diff --git a/test/912-classes/src-art/art/Test912.java b/test/912-classes/src-art/art/Test912.java
index 9896eac..fbf8794 100644
--- a/test/912-classes/src-art/art/Test912.java
+++ b/test/912-classes/src-art/art/Test912.java
@@ -228,7 +228,8 @@
     // The JIT may deeply inline and load some classes. Preload these for test determinism.
     final String PRELOAD_FOR_JIT[] = {
         "java.nio.charset.CoderMalfunctionError",
-        "java.util.NoSuchElementException"
+        "java.util.NoSuchElementException",
+        "java.io.FileNotFoundException",  // b/63581208
     };
     for (String s : PRELOAD_FOR_JIT) {
       Class.forName(s);
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.class b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.class
index 4f0f497..b8dcd55 100644
--- a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.class
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/Super.class b/test/952-invoke-custom-kinds/classes/invokecustom/Super.class
index 5990f28..7906f99 100644
--- a/test/952-invoke-custom-kinds/classes/invokecustom/Super.class
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/Super.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.class b/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.class
index 711db23..03dc233 100644
--- a/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.class
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/expected.txt b/test/952-invoke-custom-kinds/expected.txt
index c72da25..c41b5c6 100644
--- a/test/952-invoke-custom-kinds/expected.txt
+++ b/test/952-invoke-custom-kinds/expected.txt
@@ -12,7 +12,7 @@
 String
 bsmLookupStaticWithExtraArgs [1, 123456789, 123.456, 123456.789123]
 targetMethodTest3 from InvokeCustom
-bsmCreateCallSite [MethodHandle(Super)void]
+bsmCreateCallSite [MethodHandle(InvokeCustom)void]
 targetMethodTest4 from Super
 bsmLookupStatic []
 targetMethodTest5 1000 + -923 = 77
@@ -34,5 +34,7 @@
 checkStaticFieldTest9: old 0 new 1985229328 expected 1985229328 OK
 checkFieldTest9: old 0.0 new 1.99E-19 expected 1.99E-19 OK
 helperMethodTest9 in class invokecustom.InvokeCustom
+InvokeCustom.<init>(3)
 run() for Test9
+InvokeCustom.privateMethodTest9()
 targetMethodTest9()
diff --git a/test/952-invoke-custom-kinds/info.txt b/test/952-invoke-custom-kinds/info.txt
index cddb96d..33b4cff 100644
--- a/test/952-invoke-custom-kinds/info.txt
+++ b/test/952-invoke-custom-kinds/info.txt
@@ -1,7 +1,4 @@
 This test checks call sites and constant method handles in DEX files used
 by invoke-custom.
 
-It is derived from a dx test (dalvik/dx/tests/135-invoke-custom) which
-generates the invoke-custom using ASM to generate class files. The
-test here differs as it not include an instance a constant method
-handle with a constructor kind (see b/62774190).
+The class files come from dalvik/dx/tests/135-invoke-custom.
diff --git a/test/988-method-trace/check b/test/988-method-trace/check
new file mode 100644
index 0000000..4c583eb
--- /dev/null
+++ b/test/988-method-trace/check
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Jack uses @hide API which gives it wrong method trace in the expected.txt
+if [[ "$USE_JACK" == true ]]; then
+  patch -p0 expected.txt < expected_jack.diff >/dev/null
+fi
+
+./default-check "$@"
diff --git a/test/988-method-trace/expected.txt b/test/988-method-trace/expected.txt
index 8e42a48..574d5b0 100644
--- a/test/988-method-trace/expected.txt
+++ b/test/988-method-trace/expected.txt
@@ -1,4 +1,4 @@
-.<= public static native void art.Trace.enableTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
+.<= public static void art.Trace.enableTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
 <= public static void art.Trace.enableMethodTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
 => art.Test988$IterOp()
 .=> public java.lang.Object()
@@ -130,8 +130,8 @@
 ....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
 ...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
 ...=> public java.lang.String java.lang.StringBuilder.toString()
-....=> static native java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
-....<= static native java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
+....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
+....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
 ...<= public java.lang.String java.lang.StringBuilder.toString() -> <class java.lang.String: Bad argument: -19 < 0>
 ...=> public java.lang.Error(java.lang.String)
 ....=> public java.lang.Throwable(java.lang.String)
@@ -140,13 +140,13 @@
 .....=> public static final java.util.List java.util.Collections.emptyList()
 .....<= public static final java.util.List java.util.Collections.emptyList() -> <class java.util.Collections$EmptyList: []>
 .....=> public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace()
-......=> private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
-......<= private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
+......=> private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
+......<= private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
 .....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
-	art.Test988.iter_fibonacci(Test988.java:228)
-	art.Test988$IterOp.applyAsInt(Test988.java:223)
-	art.Test988.doFibTest(Test988.java:316)
-	art.Test988.run(Test988.java:286)
+	art.Test988.iter_fibonacci(Test988.java:235)
+	art.Test988$IterOp.applyAsInt(Test988.java:230)
+	art.Test988.doFibTest(Test988.java:339)
+	art.Test988.run(Test988.java:304)
 	<additional hidden frames>
 >
 ....<= public java.lang.Throwable(java.lang.String) -> <null: null>
@@ -163,10 +163,10 @@
 ...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
 ..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
 fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
-	art.Test988.iter_fibonacci(Test988.java:228)
-	art.Test988$IterOp.applyAsInt(Test988.java:223)
-	art.Test988.doFibTest(Test988.java:316)
-	art.Test988.run(Test988.java:286)
+	art.Test988.iter_fibonacci(Test988.java:235)
+	art.Test988$IterOp.applyAsInt(Test988.java:230)
+	art.Test988.doFibTest(Test988.java:339)
+	art.Test988.run(Test988.java:304)
 	<additional hidden frames>
 
 .<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
@@ -231,8 +231,8 @@
 ....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
 ...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
 ...=> public java.lang.String java.lang.StringBuilder.toString()
-....=> static native java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
-....<= static native java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
+....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
+....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
 ...<= public java.lang.String java.lang.StringBuilder.toString() -> <class java.lang.String: Bad argument: -19 < 0>
 ...=> public java.lang.Error(java.lang.String)
 ....=> public java.lang.Throwable(java.lang.String)
@@ -241,13 +241,13 @@
 .....=> public static final java.util.List java.util.Collections.emptyList()
 .....<= public static final java.util.List java.util.Collections.emptyList() -> <class java.util.Collections$EmptyList: []>
 .....=> public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace()
-......=> private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
-......<= private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
+......=> private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
+......<= private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
 .....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
-	art.Test988.fibonacci(Test988.java:250)
-	art.Test988$RecurOp.applyAsInt(Test988.java:245)
-	art.Test988.doFibTest(Test988.java:316)
-	art.Test988.run(Test988.java:287)
+	art.Test988.fibonacci(Test988.java:257)
+	art.Test988$RecurOp.applyAsInt(Test988.java:252)
+	art.Test988.doFibTest(Test988.java:339)
+	art.Test988.run(Test988.java:305)
 	<additional hidden frames>
 >
 ....<= public java.lang.Throwable(java.lang.String) -> <null: null>
@@ -264,14 +264,194 @@
 ...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
 ..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
 fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
-	art.Test988.fibonacci(Test988.java:250)
-	art.Test988$RecurOp.applyAsInt(Test988.java:245)
-	art.Test988.doFibTest(Test988.java:316)
-	art.Test988.run(Test988.java:287)
+	art.Test988.fibonacci(Test988.java:257)
+	art.Test988$RecurOp.applyAsInt(Test988.java:252)
+	art.Test988.doFibTest(Test988.java:339)
+	art.Test988.run(Test988.java:305)
 	<additional hidden frames>
 
 .<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
 <= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
-=> public static native java.lang.Thread java.lang.Thread.currentThread()
-<= public static native java.lang.Thread java.lang.Thread.currentThread() -> <<non-deterministic>: <non-deterministic>>
-=> public static native void art.Trace.disableTracing(java.lang.Thread)
+=> static void art.Test988$IntrinsicsTest.doTest()
+.=> static void art.Test988Intrinsics.test()
+..=> public static long java.lang.Double.doubleToRawLongBits(double)
+..<= public static long java.lang.Double.doubleToRawLongBits(double) -> <class java.lang.Long: 0>
+..=> public static long java.lang.Double.doubleToLongBits(double)
+..<= public static long java.lang.Double.doubleToLongBits(double) -> <class java.lang.Long: 0>
+..=> public static boolean java.lang.Double.isInfinite(double)
+..<= public static boolean java.lang.Double.isInfinite(double) -> <class java.lang.Boolean: false>
+..=> public static boolean java.lang.Double.isNaN(double)
+..<= public static boolean java.lang.Double.isNaN(double) -> <class java.lang.Boolean: false>
+..=> public static double java.lang.Double.longBitsToDouble(long)
+..<= public static double java.lang.Double.longBitsToDouble(long) -> <class java.lang.Double: 0.0>
+..=> public static int java.lang.Float.floatToRawIntBits(float)
+..<= public static int java.lang.Float.floatToRawIntBits(float) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Float.floatToIntBits(float)
+..<= public static int java.lang.Float.floatToIntBits(float) -> <class java.lang.Integer: 0>
+..=> public static boolean java.lang.Float.isInfinite(float)
+..<= public static boolean java.lang.Float.isInfinite(float) -> <class java.lang.Boolean: false>
+..=> public static boolean java.lang.Float.isNaN(float)
+..<= public static boolean java.lang.Float.isNaN(float) -> <class java.lang.Boolean: false>
+..=> public static float java.lang.Float.intBitsToFloat(int)
+..<= public static float java.lang.Float.intBitsToFloat(int) -> <class java.lang.Float: 0.0>
+..=> public static int java.lang.Integer.reverse(int)
+..<= public static int java.lang.Integer.reverse(int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.reverseBytes(int)
+..<= public static int java.lang.Integer.reverseBytes(int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.bitCount(int)
+..<= public static int java.lang.Integer.bitCount(int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.compare(int,int)
+..<= public static int java.lang.Integer.compare(int,int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.highestOneBit(int)
+..<= public static int java.lang.Integer.highestOneBit(int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.lowestOneBit(int)
+..<= public static int java.lang.Integer.lowestOneBit(int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.numberOfLeadingZeros(int)
+..<= public static int java.lang.Integer.numberOfLeadingZeros(int) -> <class java.lang.Integer: 32>
+..=> public static int java.lang.Integer.numberOfTrailingZeros(int)
+..<= public static int java.lang.Integer.numberOfTrailingZeros(int) -> <class java.lang.Integer: 32>
+..=> public static int java.lang.Integer.rotateRight(int,int)
+..<= public static int java.lang.Integer.rotateRight(int,int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.rotateLeft(int,int)
+..<= public static int java.lang.Integer.rotateLeft(int,int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.signum(int)
+..<= public static int java.lang.Integer.signum(int) -> <class java.lang.Integer: 0>
+..=> public static long java.lang.Long.reverse(long)
+..<= public static long java.lang.Long.reverse(long) -> <class java.lang.Long: 0>
+..=> public static long java.lang.Long.reverseBytes(long)
+..<= public static long java.lang.Long.reverseBytes(long) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Long.bitCount(long)
+..<= public static int java.lang.Long.bitCount(long) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Long.compare(long,long)
+..<= public static int java.lang.Long.compare(long,long) -> <class java.lang.Integer: 0>
+..=> public static long java.lang.Long.highestOneBit(long)
+..<= public static long java.lang.Long.highestOneBit(long) -> <class java.lang.Long: 0>
+..=> public static long java.lang.Long.lowestOneBit(long)
+..<= public static long java.lang.Long.lowestOneBit(long) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Long.numberOfLeadingZeros(long)
+..<= public static int java.lang.Long.numberOfLeadingZeros(long) -> <class java.lang.Integer: 64>
+..=> public static int java.lang.Long.numberOfTrailingZeros(long)
+..<= public static int java.lang.Long.numberOfTrailingZeros(long) -> <class java.lang.Integer: 64>
+..=> public static long java.lang.Long.rotateRight(long,int)
+..<= public static long java.lang.Long.rotateRight(long,int) -> <class java.lang.Long: 0>
+..=> public static long java.lang.Long.rotateLeft(long,int)
+..<= public static long java.lang.Long.rotateLeft(long,int) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Long.signum(long)
+..<= public static int java.lang.Long.signum(long) -> <class java.lang.Integer: 0>
+..=> public static short java.lang.Short.reverseBytes(short)
+..<= public static short java.lang.Short.reverseBytes(short) -> <class java.lang.Short: 0>
+..=> public static double java.lang.Math.abs(double)
+..<= public static double java.lang.Math.abs(double) -> <class java.lang.Double: 0.0>
+..=> public static float java.lang.Math.abs(float)
+..<= public static float java.lang.Math.abs(float) -> <class java.lang.Float: 0.0>
+..=> public static long java.lang.Math.abs(long)
+..<= public static long java.lang.Math.abs(long) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Math.abs(int)
+..<= public static int java.lang.Math.abs(int) -> <class java.lang.Integer: 0>
+..=> public static double java.lang.Math.min(double,double)
+..<= public static double java.lang.Math.min(double,double) -> <class java.lang.Double: 0.0>
+..=> public static float java.lang.Math.min(float,float)
+..<= public static float java.lang.Math.min(float,float) -> <class java.lang.Float: 0.0>
+..=> public static long java.lang.Math.min(long,long)
+..<= public static long java.lang.Math.min(long,long) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Math.min(int,int)
+..<= public static int java.lang.Math.min(int,int) -> <class java.lang.Integer: 0>
+..=> public static double java.lang.Math.max(double,double)
+..<= public static double java.lang.Math.max(double,double) -> <class java.lang.Double: 0.0>
+..=> public static float java.lang.Math.max(float,float)
+..<= public static float java.lang.Math.max(float,float) -> <class java.lang.Float: 0.0>
+..=> public static long java.lang.Math.max(long,long)
+..<= public static long java.lang.Math.max(long,long) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Math.max(int,int)
+..<= public static int java.lang.Math.max(int,int) -> <class java.lang.Integer: 0>
+..=> public static double java.lang.Math.cos(double)
+..<= public static double java.lang.Math.cos(double) -> <class java.lang.Double: 1.0>
+..=> public static double java.lang.Math.sin(double)
+..<= public static double java.lang.Math.sin(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.acos(double)
+..<= public static double java.lang.Math.acos(double) -> <class java.lang.Double: 1.5707963267948966>
+..=> public static double java.lang.Math.asin(double)
+..<= public static double java.lang.Math.asin(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.atan(double)
+..<= public static double java.lang.Math.atan(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.atan2(double,double)
+..<= public static double java.lang.Math.atan2(double,double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.cbrt(double)
+..<= public static double java.lang.Math.cbrt(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.cosh(double)
+..<= public static double java.lang.Math.cosh(double) -> <class java.lang.Double: 1.0>
+..=> public static double java.lang.Math.exp(double)
+..<= public static double java.lang.Math.exp(double) -> <class java.lang.Double: 1.0>
+..=> public static double java.lang.Math.expm1(double)
+..<= public static double java.lang.Math.expm1(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.hypot(double,double)
+..<= public static double java.lang.Math.hypot(double,double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.log(double)
+..<= public static double java.lang.Math.log(double) -> <class java.lang.Double: -Infinity>
+..=> public static double java.lang.Math.log10(double)
+..<= public static double java.lang.Math.log10(double) -> <class java.lang.Double: -Infinity>
+..=> public static double java.lang.Math.nextAfter(double,double)
+..<= public static double java.lang.Math.nextAfter(double,double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.sinh(double)
+..<= public static double java.lang.Math.sinh(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.tan(double)
+..<= public static double java.lang.Math.tan(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.tanh(double)
+..<= public static double java.lang.Math.tanh(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.sqrt(double)
+..<= public static double java.lang.Math.sqrt(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.ceil(double)
+..<= public static double java.lang.Math.ceil(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.floor(double)
+..<= public static double java.lang.Math.floor(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.rint(double)
+..<= public static double java.lang.Math.rint(double) -> <class java.lang.Double: 0.0>
+..=> public static long java.lang.Math.round(double)
+..<= public static long java.lang.Math.round(double) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Math.round(float)
+..<= public static int java.lang.Math.round(float) -> <class java.lang.Integer: 0>
+..=> public static java.lang.Thread java.lang.Thread.currentThread()
+..<= public static java.lang.Thread java.lang.Thread.currentThread() -> <<non-deterministic>: <non-deterministic>>
+..=> public char java.lang.String.charAt(int)
+..<= public char java.lang.String.charAt(int) -> <class java.lang.Character: s>
+..=> public int java.lang.String.compareTo(java.lang.String)
+..<= public int java.lang.String.compareTo(java.lang.String) -> <class java.lang.Integer: 11>
+..=> public boolean java.lang.String.equals(java.lang.Object)
+..<= public boolean java.lang.String.equals(java.lang.Object) -> <class java.lang.Boolean: false>
+..=> public int java.lang.String.indexOf(int)
+..<= public int java.lang.String.indexOf(int) -> <class java.lang.Integer: -1>
+..=> public int java.lang.String.indexOf(int,int)
+..<= public int java.lang.String.indexOf(int,int) -> <class java.lang.Integer: -1>
+..=> public int java.lang.String.indexOf(java.lang.String)
+..<= public int java.lang.String.indexOf(java.lang.String) -> <class java.lang.Integer: -1>
+..=> public int java.lang.String.indexOf(java.lang.String,int)
+..<= public int java.lang.String.indexOf(java.lang.String,int) -> <class java.lang.Integer: -1>
+..=> public boolean java.lang.String.isEmpty()
+..<= public boolean java.lang.String.isEmpty() -> <class java.lang.Boolean: false>
+..=> public int java.lang.String.length()
+..<= public int java.lang.String.length() -> <class java.lang.Integer: 17>
+..=> public synchronized java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.String)
+..<= public synchronized java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.String) -> <class java.lang.StringBuffer: some large string bufferhello>
+..=> public synchronized int java.lang.StringBuffer.length()
+..<= public synchronized int java.lang.StringBuffer.length() -> <class java.lang.Integer: 29>
+..=> public synchronized java.lang.String java.lang.StringBuffer.toString()
+..<= public synchronized java.lang.String java.lang.StringBuffer.toString() -> <class java.lang.String: some large string bufferhello>
+..=> public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String)
+..<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: some large string builderhello>
+..=> public int java.lang.StringBuilder.length()
+..<= public int java.lang.StringBuilder.length() -> <class java.lang.Integer: 30>
+..=> public java.lang.String java.lang.StringBuilder.toString()
+..<= public java.lang.String java.lang.StringBuilder.toString() -> <class java.lang.String: some large string builderhello>
+..=> public static java.lang.Integer java.lang.Integer.valueOf(int)
+..<= public static java.lang.Integer java.lang.Integer.valueOf(int) -> <class java.lang.Integer: 0>
+..=> public static boolean java.lang.Thread.interrupted()
+..<= public static boolean java.lang.Thread.interrupted() -> <class java.lang.Boolean: false>
+.<= static void art.Test988Intrinsics.test() -> <null: null>
+.=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+.<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
+.=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+.<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
+<= static void art.Test988$IntrinsicsTest.doTest() -> <null: null>
+=> public static java.lang.Thread java.lang.Thread.currentThread()
+<= public static java.lang.Thread java.lang.Thread.currentThread() -> <<non-deterministic>: <non-deterministic>>
+=> public static void art.Trace.disableTracing(java.lang.Thread)
diff --git a/test/988-method-trace/expected_jack.diff b/test/988-method-trace/expected_jack.diff
new file mode 100644
index 0000000..11364a0
--- /dev/null
+++ b/test/988-method-trace/expected_jack.diff
@@ -0,0 +1,10 @@
+450,453c450,453
+< .=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+< .<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
+< .=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+< .<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
+---
+> .=> public static void java.lang.System.arraycopy(int[],int,int[],int,int)
+> .<= public static void java.lang.System.arraycopy(int[],int,int[],int,int) -> <null: null>
+> .=> public static void java.lang.System.arraycopy(char[],int,char[],int,int)
+> .<= public static void java.lang.System.arraycopy(char[],int,char[],int,int) -> <null: null>
diff --git a/test/988-method-trace/gen_srcs.py b/test/988-method-trace/gen_srcs.py
new file mode 100755
index 0000000..c1ce35c
--- /dev/null
+++ b/test/988-method-trace/gen_srcs.py
@@ -0,0 +1,321 @@
+#!/usr/bin/python3
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Generates the src/art/Test988Intrinsics.java file.
+# Re-run this every time art/compiler/intrinics_list.h is modified.
+#
+# $> python3.4 gen_srcs.py > src/art/Test988Intrinsics.java
+#
+
+import argparse
+import os
+import re
+import collections
+import sys
+
+from string import Template
+
+# Relative path to art/compiler/intrinsics_list.h
+INTRINSICS_LIST_H = os.path.dirname(os.path.realpath(__file__)) + "/../../compiler/intrinsics_list.h"
+
+# Macro parameter index to V(). Negative means from the end.
+IDX_STATIC_OR_VIRTUAL = 1
+IDX_SIGNATURE = -1
+IDX_METHOD_NAME = -2
+IDX_CLASS_NAME = -3
+
+# Exclude all hidden API.
+KLASS_BLACK_LIST = ['sun.misc.Unsafe', 'libcore.io.Memory', 'java.lang.StringFactory']
+METHOD_BLACK_LIST = [('java.lang.ref.Reference', 'getReferent'),
+                     ('java.lang.String', 'getCharsNoCheck'),
+                     ('java.lang.System', 'arraycopy')]  # arraycopy has a manual test.
+
+# When testing a virtual function, it needs to operate on an instance.
+# These instances will be created with the following values,
+# otherwise a default 'new T()' is used.
+KLASS_INSTANCE_INITIALIZERS = {
+  'java.lang.String' : '"some large string"',
+  'java.lang.StringBuffer' : 'new java.lang.StringBuffer("some large string buffer")',
+  'java.lang.StringBuilder' : 'new java.lang.StringBuilder("some large string builder")',
+  'java.lang.ref.Reference' : 'new java.lang.ref.WeakReference(new Object())'
+};
+
+OUTPUT_TPL = Template("""
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// AUTO-GENENERATED by gen_srcs.py: DO NOT EDIT HERE DIRECTLY.
+//
+// $$> python3.4 gen_srcs.py > src/art/Test988Intrinsics.java
+//
+// RUN ABOVE COMMAND TO REGENERATE THIS FILE.
+
+package art;
+
+class Test988Intrinsics {
+  // Pre-initialize *all* instance variables used so that their constructors are not in the trace.
+$static_fields
+
+  static void initialize() {
+    // Ensure all static variables are initialized.
+    // In addition, pre-load classes here so that we don't see diverging class loading traces.
+$initialize_classes
+  }
+
+  static void test() {
+    // Call each intrinsic from art/compiler/intrinsics_list.h to make sure they are traced.
+$test_body
+  }
+}
+""")
+
+JNI_TYPES = {
+  'Z' : 'boolean',
+  'B' : 'byte',
+  'C' : 'char',
+  'S' : 'short',
+  'I' : 'int',
+  'J' : 'long',
+  'F' : 'float',
+  'D' : 'double',
+  'L' : 'object'
+};
+
+debug_printing_enabled = False
+
+def debug_print(x):
+  if debug_printing_enabled:
+    print(x, file=sys.stderr)
+
+# Parse JNI sig into a list, e.g. "II" -> ['I', 'I'], '[[IJ' -> ['[[I', 'J'], etc.
+def sig_to_parameter_type_list(sig):
+  sig = re.sub(r'[(](.*)[)].*', r'\1', sig)
+
+  lst = []
+  obj = ""
+  is_obj = False
+  is_array = False
+  for x in sig:
+    if is_obj:
+      obj = obj + x
+      if x == ";":
+        is_obj = False
+        lst.append(obj)
+        obj = ""
+    elif is_array:
+      obj = obj + x
+      if x != "[":
+        is_array = False
+        lst.append(obj)
+        obj = ""
+    else:
+      if x == "[":
+        obj = "["
+        is_array = True
+      elif x == "L":
+        obj = "L"
+        is_obj = True
+      else:
+        lst.append(x)
+
+  return lst
+
+# Convert a single JNI descriptor into a pretty java name, e.g. "[I" -> "int[]", etc.
+def javafy_name(kls_name):
+  if kls_name.startswith("L"):
+    kls_name = kls_name.lstrip("L").rstrip(";")
+    return kls_name.replace("/", ".")
+  elif kls_name.startswith("["):
+    array_count = kls_name.count("[")
+    non_array = javafy_name(kls_name.lstrip("["))
+    return non_array + ("[]" * array_count)
+
+  return JNI_TYPES.get(kls_name, kls_name)
+
+def extract_staticness(static_or_virtual):
+  if static_or_virtual == "kStatic":
+    return 'static'
+  return 'virtual' # kVirtual, kDirect
+
+class MethodInfo:
+  def __init__(self, staticness, pretty_params, method, kls):
+    # 'virtual' or 'static'
+    self.staticness = staticness
+    # list of e.g. ['int', 'double', 'java.lang.String'] etc
+    self.parameters = pretty_params
+    # e.g. 'toString'
+    self.method_name = method
+    # e.g. 'java.lang.String'
+    self.klass = kls
+
+  def __str__(self):
+    return "MethodInfo " + str(self.__dict__)
+
+  def dummy_parameters(self):
+    dummy_values = {
+     'boolean' : 'false',
+     'byte' : '(byte)0',
+     'char' : "'x'",
+     'short' : '(short)0',
+     'int' : '0',
+     'long' : '0L',
+     'float' : '0.0f',
+     'double' : '0.0'
+    }
+
+    def object_dummy(name):
+      if name == "java.lang.String":
+        return '"hello"'
+      else:
+        return "(%s)null" %(name)
+    return [ dummy_values.get(param, object_dummy(param)) for param in self.parameters ]
+
+  def dummy_instance_value(self):
+    return KLASS_INSTANCE_INITIALIZERS.get(self.klass, 'new %s()' %(self.klass))
+
+  def is_blacklisted(self):
+    for blk in KLASS_BLACK_LIST:
+      if self.klass.startswith(blk):
+        return True
+
+    return (self.klass, self.method_name) in METHOD_BLACK_LIST
+
+# parse the V(...) \ list of items into a MethodInfo
+def parse_method_info(items):
+  def get_item(idx):
+    return items[idx].strip().strip("\"")
+
+  staticness = get_item(IDX_STATIC_OR_VIRTUAL)
+  sig = get_item(IDX_SIGNATURE)
+  method = get_item(IDX_METHOD_NAME)
+  kls = get_item(IDX_CLASS_NAME)
+
+  debug_print ((sig, method, kls))
+
+  staticness = extract_staticness(staticness)
+  kls = javafy_name(kls)
+  param_types = sig_to_parameter_type_list(sig)
+  pretty_params = param_types
+  pretty_params = [javafy_name(i) for i in param_types]
+
+  return MethodInfo(staticness, pretty_params, method, kls)
+
+# parse a line containing '  V(...)' into a MethodInfo
+def parse_line(line):
+  line = line.strip()
+  if not line.startswith("V("):
+    return None
+
+  line = re.sub(r'V[(](.*)[)]', r'\1', line)
+  debug_print(line)
+
+  items = line.split(",")
+
+  method_info = parse_method_info(items)
+  return method_info
+
+# Generate all the MethodInfo that we parse from intrinsics_list.h
+def parse_all_method_infos():
+  with open(INTRINSICS_LIST_H) as f:
+    for line in f:
+      s = parse_line(line)
+      if s is not None:
+        yield s
+
+# Format a receiver name. For statics, it's the class name, for receivers, it's an instance variable
+def format_receiver_name(method_info):
+  receiver = method_info.klass
+  if method_info.staticness == 'virtual':
+    receiver = "instance_" + method_info.klass.replace(".", "_")
+  return receiver
+
+# Format a dummy call with dummy method parameters to the requested method.
+def format_call_to(method_info):
+  dummy_args = ", ".join(method_info.dummy_parameters())
+  receiver = format_receiver_name(method_info)
+
+  return ("%s.%s(%s);" %(receiver, method_info.method_name, dummy_args))
+
+# Format a static variable with an instance that could be used as the receiver
+# (or None for non-static methods).
+def format_instance_variable(method_info):
+  if method_info.staticness == 'static':
+    return None
+  return "static %s %s = %s;" %(method_info.klass, format_receiver_name(method_info), method_info.dummy_instance_value())
+
+def format_initialize_klass(method_info):
+  return "%s.class.toString();" %(method_info.klass)
+
+def indent_list(lst, indent):
+  return [' ' * indent + i for i in lst]
+
+def main():
+  global debug_printing_enabled
+  parser = argparse.ArgumentParser(description='Generate art/test/988-method-trace/src/art/Test988Intrinsics.java')
+  parser.add_argument('-d', '--debug', action='store_true', dest='debug', help='Print extra debugging information to stderr.')
+  parser.add_argument('output_file', nargs='?', metavar='<output-file>', default=sys.stdout, type=argparse.FileType('w'), help='Destination file to write to (default: stdout).')
+  args = parser.parse_args()
+
+  debug_printing_enabled = args.debug
+
+  #####
+
+  call_str_list = []
+  instance_variable_dict = collections.OrderedDict()
+  initialize_klass_dict = collections.OrderedDict()
+  for i in parse_all_method_infos():
+    debug_print(i)
+    if i.is_blacklisted():
+      debug_print("Blacklisted: " + str(i))
+      continue
+
+    call_str = format_call_to(i)
+    debug_print(call_str)
+
+    call_str_list.append(call_str)
+
+    instance_variable = format_instance_variable(i)
+    if instance_variable is not None:
+      debug_print(instance_variable)
+      instance_variable_dict[i.klass] = instance_variable
+
+    initialize_klass_dict[i.klass] = format_initialize_klass(i)
+
+  static_fields = indent_list([ value for (key, value) in instance_variable_dict.items() ], 2)
+  test_body = indent_list(call_str_list, 4)
+  initialize_classes = indent_list([ value for (key, value) in initialize_klass_dict.items() ], 4)
+
+  print(OUTPUT_TPL.substitute(static_fields="\n".join(static_fields),
+                              test_body="\n".join(test_body),
+                              initialize_classes="\n".join(initialize_classes)).
+                   strip("\n"), \
+        file=args.output_file)
+
+if __name__ == '__main__':
+  main()
diff --git a/test/988-method-trace/src/art/Test988.java b/test/988-method-trace/src/art/Test988.java
index e40c612..d7eda52 100644
--- a/test/988-method-trace/src/art/Test988.java
+++ b/test/988-method-trace/src/art/Test988.java
@@ -57,7 +57,7 @@
         }
         @Override
         public void Print() {
-            System.out.println(whitespace(cnt) + "=> " + m);
+            System.out.println(whitespace(cnt) + "=> " + methodToString(m));
         }
     }
 
@@ -124,6 +124,13 @@
       }
     }
 
+    static String methodToString(Object m) {
+      // Make the output more similar between ART and RI,
+      // by removing the 'native' specifier from methods.
+      String methodStr = m.toString();
+      return methodStr.replaceFirst(" native", "");
+    }
+
     static final class MethodReturn implements Printable {
         private Object m;
         private Object val;
@@ -154,7 +161,7 @@
               klass_print = klass.toString();
             }
             System.out.println(
-                whitespace(cnt) + "<= " + m + " -> <" + klass_print + ": " + print + ">");
+                whitespace(cnt) + "<= " + methodToString(m) + " -> <" + klass_print + ": " + print + ">");
         }
     }
 
@@ -167,7 +174,7 @@
         }
         @Override
         public void Print() {
-            System.out.println(whitespace(cnt) + "<= " + m + " EXCEPTION");
+            System.out.println(whitespace(cnt) + "<= " + methodToString(m) + " EXCEPTION");
         }
     }
 
@@ -255,15 +262,26 @@
         }
     }
 
+    static final int METHOD_TRACING_IGNORE_DEPTH = 2;
+    static boolean sMethodTracingIgnore = false;
+
     public static void notifyMethodEntry(Object m) {
         // Called by native code when a method is entered. This method is ignored by the native
         // entry and exit hooks.
-        results.add(new MethodEntry(m, cnt));
         cnt++;
+        if ((cnt - 1) > METHOD_TRACING_IGNORE_DEPTH && sMethodTracingIgnore) {
+          return;
+        }
+        results.add(new MethodEntry(m, cnt - 1));
     }
 
     public static void notifyMethodExit(Object m, boolean exception, Object result) {
         cnt--;
+
+        if (cnt > METHOD_TRACING_IGNORE_DEPTH && sMethodTracingIgnore) {
+          return;
+        }
+
         if (exception) {
             results.add(new MethodThrownThrough(m, cnt));
         } else {
@@ -285,6 +303,10 @@
         doFibTest(5, new RecurOp());
         doFibTest(-19, new IterOp());
         doFibTest(-19, new RecurOp());
+
+        sMethodTracingIgnore = true;
+        IntrinsicsTest.doTest();
+        sMethodTracingIgnore = false;
         // Turn off method tracing so we don't have to deal with print internals.
         Trace.disableTracing(Thread.currentThread());
         printResults();
@@ -303,6 +325,7 @@
       RecurOp.class.toString();
       IterOp.class.toString();
       StringBuilder.class.toString();
+      IntrinsicsTest.initialize();  // ensure <clinit> is executed prior to tracing.
     }
 
     public static void printResults() {
@@ -319,4 +342,30 @@
         results.add(new FibThrow("fibonacci(%d) -> %s\n", x, t));
       }
     }
+
+    static class IntrinsicsTest {
+      static int[] sSourceArray = { 0, 1, 2, 3, 4, 5 };
+      static int[] sDestArray =   { 5, 6, 7, 8, 9, 10 };
+
+      static char[] sSourceArrayChar = { '0', '1', '2', '3', '4', '5' };
+      static char[] sDestArrayChar =   { '5', '6', '7', '8', '9', 'a' };
+
+      static void initialize() {
+        Test988Intrinsics.initialize();
+
+        // Pre-load all classes used in #doTest manual intrinsics.
+        java.lang.System.class.toString();
+      }
+      static void doTest() {
+        // Ensure that the ART intrinsics in intrinsics_list.h are also being traced,
+        // since in non-tracing operation they are effectively inlined by the optimizing compiler.
+
+        // Auto-generated test file that uses null/0s as default parameters.
+        Test988Intrinsics.test();
+
+        // Manual list here for functions that require special non-null/non-zero parameters:
+        System.arraycopy(sSourceArray, 0, sDestArray, 0, 1);
+        System.arraycopy(sSourceArrayChar, 0, sDestArrayChar, 0, 1);
+      }
+    }
 }
diff --git a/test/988-method-trace/src/art/Test988Intrinsics.java b/test/988-method-trace/src/art/Test988Intrinsics.java
new file mode 100644
index 0000000..099fbf2
--- /dev/null
+++ b/test/988-method-trace/src/art/Test988Intrinsics.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// AUTO-GENENERATED by gen_srcs.py: DO NOT EDIT HERE DIRECTLY.
+//
+// $> python3.4 gen_srcs.py > src/art/Test988Intrinsics.java
+//
+// RUN ABOVE COMMAND TO REGENERATE THIS FILE.
+
+package art;
+
+class Test988Intrinsics {
+  // Pre-initialize *all* instance variables used so that their constructors are not in the trace.
+  static java.lang.String instance_java_lang_String = "some large string";
+  static java.lang.StringBuffer instance_java_lang_StringBuffer = new java.lang.StringBuffer("some large string buffer");
+  static java.lang.StringBuilder instance_java_lang_StringBuilder = new java.lang.StringBuilder("some large string builder");
+
+  static void initialize() {
+    // Ensure all static variables are initialized.
+    // In addition, pre-load classes here so that we don't see diverging class loading traces.
+    java.lang.Double.class.toString();
+    java.lang.Float.class.toString();
+    java.lang.Integer.class.toString();
+    java.lang.Long.class.toString();
+    java.lang.Short.class.toString();
+    java.lang.Math.class.toString();
+    java.lang.Thread.class.toString();
+    java.lang.String.class.toString();
+    java.lang.StringBuffer.class.toString();
+    java.lang.StringBuilder.class.toString();
+  }
+
+  static void test() {
+    // Call each intrinsic from art/compiler/intrinsics_list.h to make sure they are traced.
+    java.lang.Double.doubleToRawLongBits(0.0);
+    java.lang.Double.doubleToLongBits(0.0);
+    java.lang.Double.isInfinite(0.0);
+    java.lang.Double.isNaN(0.0);
+    java.lang.Double.longBitsToDouble(0L);
+    java.lang.Float.floatToRawIntBits(0.0f);
+    java.lang.Float.floatToIntBits(0.0f);
+    java.lang.Float.isInfinite(0.0f);
+    java.lang.Float.isNaN(0.0f);
+    java.lang.Float.intBitsToFloat(0);
+    java.lang.Integer.reverse(0);
+    java.lang.Integer.reverseBytes(0);
+    java.lang.Integer.bitCount(0);
+    java.lang.Integer.compare(0, 0);
+    java.lang.Integer.highestOneBit(0);
+    java.lang.Integer.lowestOneBit(0);
+    java.lang.Integer.numberOfLeadingZeros(0);
+    java.lang.Integer.numberOfTrailingZeros(0);
+    java.lang.Integer.rotateRight(0, 0);
+    java.lang.Integer.rotateLeft(0, 0);
+    java.lang.Integer.signum(0);
+    java.lang.Long.reverse(0L);
+    java.lang.Long.reverseBytes(0L);
+    java.lang.Long.bitCount(0L);
+    java.lang.Long.compare(0L, 0L);
+    java.lang.Long.highestOneBit(0L);
+    java.lang.Long.lowestOneBit(0L);
+    java.lang.Long.numberOfLeadingZeros(0L);
+    java.lang.Long.numberOfTrailingZeros(0L);
+    java.lang.Long.rotateRight(0L, 0);
+    java.lang.Long.rotateLeft(0L, 0);
+    java.lang.Long.signum(0L);
+    java.lang.Short.reverseBytes((short)0);
+    java.lang.Math.abs(0.0);
+    java.lang.Math.abs(0.0f);
+    java.lang.Math.abs(0L);
+    java.lang.Math.abs(0);
+    java.lang.Math.min(0.0, 0.0);
+    java.lang.Math.min(0.0f, 0.0f);
+    java.lang.Math.min(0L, 0L);
+    java.lang.Math.min(0, 0);
+    java.lang.Math.max(0.0, 0.0);
+    java.lang.Math.max(0.0f, 0.0f);
+    java.lang.Math.max(0L, 0L);
+    java.lang.Math.max(0, 0);
+    java.lang.Math.cos(0.0);
+    java.lang.Math.sin(0.0);
+    java.lang.Math.acos(0.0);
+    java.lang.Math.asin(0.0);
+    java.lang.Math.atan(0.0);
+    java.lang.Math.atan2(0.0, 0.0);
+    java.lang.Math.cbrt(0.0);
+    java.lang.Math.cosh(0.0);
+    java.lang.Math.exp(0.0);
+    java.lang.Math.expm1(0.0);
+    java.lang.Math.hypot(0.0, 0.0);
+    java.lang.Math.log(0.0);
+    java.lang.Math.log10(0.0);
+    java.lang.Math.nextAfter(0.0, 0.0);
+    java.lang.Math.sinh(0.0);
+    java.lang.Math.tan(0.0);
+    java.lang.Math.tanh(0.0);
+    java.lang.Math.sqrt(0.0);
+    java.lang.Math.ceil(0.0);
+    java.lang.Math.floor(0.0);
+    java.lang.Math.rint(0.0);
+    java.lang.Math.round(0.0);
+    java.lang.Math.round(0.0f);
+    java.lang.Thread.currentThread();
+    instance_java_lang_String.charAt(0);
+    instance_java_lang_String.compareTo("hello");
+    instance_java_lang_String.equals((java.lang.Object)null);
+    instance_java_lang_String.indexOf(0);
+    instance_java_lang_String.indexOf(0, 0);
+    instance_java_lang_String.indexOf("hello");
+    instance_java_lang_String.indexOf("hello", 0);
+    instance_java_lang_String.isEmpty();
+    instance_java_lang_String.length();
+    instance_java_lang_StringBuffer.append("hello");
+    instance_java_lang_StringBuffer.length();
+    instance_java_lang_StringBuffer.toString();
+    instance_java_lang_StringBuilder.append("hello");
+    instance_java_lang_StringBuilder.length();
+    instance_java_lang_StringBuilder.toString();
+    java.lang.Integer.valueOf(0);
+    java.lang.Thread.interrupted();
+  }
+}
diff --git a/test/990-method-handle-and-mr/build b/test/990-method-handle-and-mr/build
new file mode 100755
index 0000000..12a8e18
--- /dev/null
+++ b/test/990-method-handle-and-mr/build
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Exit on failure.
+set -e
+
+./default-build "$@" --experimental method-handles
diff --git a/test/990-method-handle-and-mr/expected.txt b/test/990-method-handle-and-mr/expected.txt
new file mode 100644
index 0000000..8483fb5
--- /dev/null
+++ b/test/990-method-handle-and-mr/expected.txt
@@ -0,0 +1,4 @@
+Test
+Test
+Test
+passed
diff --git a/test/990-method-handle-and-mr/info.txt b/test/990-method-handle-and-mr/info.txt
new file mode 100644
index 0000000..85a957c
--- /dev/null
+++ b/test/990-method-handle-and-mr/info.txt
@@ -0,0 +1,2 @@
+Test stressing code generated for invoke-polymorphic instructions with
+respect to Marking Register (on architectures supporting MR).
diff --git a/test/990-method-handle-and-mr/src/Main.java b/test/990-method-handle-and-mr/src/Main.java
new file mode 100644
index 0000000..739b8eb
--- /dev/null
+++ b/test/990-method-handle-and-mr/src/Main.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This test was inspired by benchmarks.MicroMethodHandles.java.MicroMethodHandles.
+
+import java.io.PrintStream;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+class A {
+  public Long binaryFunction(int x, double y) {
+    return 1000l;
+  }
+}
+
+class Test {
+  Test() throws Throwable {
+    this.handle = MethodHandles.lookup().findVirtual(A.class, "binaryFunction",
+                                                     MethodType.methodType(Long.class, int.class,
+                                                                           double.class));
+    this.a = new A();
+    this.x = new Integer(72);
+    this.y = new Double(-1.39e-31);
+  }
+
+  void execute() {
+    try {
+      executeFor(2000);
+      System.out.println(getName());
+    } catch (Throwable t) {
+      System.err.println("Exception during the execution of " + getName());
+      System.err.println(t);
+      t.printStackTrace(new PrintStream(System.err));
+      System.exit(1);
+    }
+  }
+
+  void executeFor(long timeMinimumMillis) throws Throwable {
+    long startTime = System.currentTimeMillis();
+    long elapsed = 0;
+    while (elapsed < timeMinimumMillis) {
+      exercise();
+      elapsed = System.currentTimeMillis() - startTime;
+    }
+  }
+
+  void exercise() throws Throwable {
+    for (int i = 0; i < EXERCISE_ITERATIONS; ++i) {
+      run();
+    }
+  }
+
+  void run() throws Throwable {
+    long result = (long) handle.invoke(a, x, y);
+  }
+
+  String getName() {
+    return getClass().getSimpleName();
+  }
+
+  private static final int EXERCISE_ITERATIONS = 500;
+
+  private MethodHandle handle;
+  private A a;
+  private Integer x;
+  private Double y;
+}
+
+public class Main {
+  public static void main(String[] args) throws Throwable {
+    Test[] tests = new Test[] { new Test(), new Test(), new Test() };
+    for (Test test : tests) {
+      test.execute();
+    }
+    System.out.println("passed");
+  }
+}
diff --git a/test/Android.bp b/test/Android.bp
index f893531..44cb4f6 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -253,6 +253,7 @@
         "ti-agent/breakpoint_helper.cc",
         "ti-agent/common_helper.cc",
         "ti-agent/redefinition_helper.cc",
+        "ti-agent/suspension_helper.cc",
         "ti-agent/trace_helper.cc",
         // This is the list of non-special OnLoad things and excludes BCI and anything that depends
         // on ART internals.
@@ -286,6 +287,11 @@
         "992-source-data/source_file.cc",
         "993-breakpoints/breakpoints.cc",
         "996-breakpoint-obsolete/obsolete_breakpoints.cc",
+        "1900-track-alloc/alloc.cc",
+        "1901-get-bytecodes/bytecodes.cc",
+        "1905-suspend-native/native_suspend.cc",
+        "1908-suspend-native-resume-self/native_suspend_resume.cc",
+        "1909-per-agent-tls/agent_tls.cc",
     ],
     shared_libs: [
         "libbase",
@@ -445,6 +451,7 @@
         "art_debug_defaults",
         "art_defaults",
     ],
+    header_libs: ["libnativebridge-dummy-headers"],
     srcs: ["115-native-bridge/nativebridge.cc"],
     target: {
         android: {
diff --git a/test/Android.run-test-jvmti-java-library.mk b/test/Android.run-test-jvmti-java-library.mk
deleted file mode 100644
index 753fe9a..0000000
--- a/test/Android.run-test-jvmti-java-library.mk
+++ /dev/null
@@ -1,171 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# shim classes. We use one that exposes the common functionality.
-LOCAL_SHIM_CLASSES := \
-  902-hello-transformation/src/art/Redefinition.java \
-  903-hello-tagging/src/art/Main.java \
-  989-method-trace-throw/src/art/Trace.java \
-
-LOCAL_SRC_FILES := $(LOCAL_SHIM_CLASSES)
-
-# Actual test classes.
-LOCAL_SRC_FILES += \
-  901-hello-ti-agent/src/art/Test901.java \
-  902-hello-transformation/src/art/Test902.java \
-  903-hello-tagging/src/art/Test903.java \
-  904-object-allocation/src/art/Test904.java \
-  905-object-free/src/art/Test905.java \
-  906-iterate-heap/src/art/Test906.java \
-  907-get-loaded-classes/src/art/Test907.java \
-    907-get-loaded-classes/src/art/Cerr.java \
-  908-gc-start-finish/src/art/Test908.java \
-  910-methods/src/art/Test910.java \
-  911-get-stack-trace/src/art/Test911.java \
-    911-get-stack-trace/src/art/AllTraces.java \
-    911-get-stack-trace/src/art/ControlData.java \
-    911-get-stack-trace/src/art/Frames.java \
-    911-get-stack-trace/src/art/OtherThread.java \
-    911-get-stack-trace/src/art/PrintThread.java \
-    911-get-stack-trace/src/art/Recurse.java \
-    911-get-stack-trace/src/art/SameThread.java \
-    911-get-stack-trace/src/art/ThreadListTraces.java \
-  912-classes/src-art/art/Test912.java \
-    912-classes/src-art/art/DexData.java \
-  913-heaps/src/art/Test913.java \
-  914-hello-obsolescence/src/art/Test914.java \
-  915-obsolete-2/src/art/Test915.java \
-  917-fields-transformation/src/art/Test917.java \
-  918-fields/src/art/Test918.java \
-  919-obsolete-fields/src/art/Test919.java \
-  920-objects/src/art/Test920.java \
-  922-properties/src/art/Test922.java \
-  923-monitors/src/art/Test923.java \
-  924-threads/src/art/Test924.java \
-  925-threadgroups/src/art/Test925.java \
-  926-multi-obsolescence/src/art/Test926.java \
-  927-timers/src/art/Test927.java \
-  928-jni-table/src/art/Test928.java \
-  930-hello-retransform/src/art/Test930.java \
-  931-agent-thread/src/art/Test931.java \
-  932-transform-saves/src/art/Test932.java \
-  933-misc-events/src/art/Test933.java \
-  940-recursive-obsolete/src/art/Test940.java \
-  942-private-recursive/src/art/Test942.java \
-  944-transform-classloaders/src/art/Test944.java \
-  945-obsolete-native/src/art/Test945.java \
-  947-reflect-method/src/art/Test947.java \
-  951-threaded-obsolete/src/art/Test951.java \
-  981-dedup-original-dex/src-art/art/Test981.java \
-  982-ok-no-retransform/src/art/Test982.java \
-  984-obsolete-invoke/src/art/Test984.java \
-  985-re-obsolete/src/art/Test985.java \
-  986-native-method-bind/src/art/Test986.java \
-  988-method-trace/src/art/Test988.java \
-  989-method-trace-throw/src/art/Test989.java \
-  990-field-trace/src/art/Test990.java \
-  991-field-trace-2/src/art/Test991.java \
-  992-source-data/src/art/Test992.java \
-    992-source-data/src/art/Target2.java \
-
-JVMTI_RUN_TEST_GENERATED_NUMBERS := \
-  901 \
-  902 \
-  903 \
-  904 \
-  905 \
-  906 \
-  907 \
-  908 \
-  910 \
-  911 \
-  912 \
-  913 \
-  914 \
-  915 \
-  917 \
-  918 \
-  919 \
-  920 \
-  922 \
-  923 \
-  924 \
-  925 \
-  926 \
-  927 \
-  928 \
-  930 \
-  931 \
-  932 \
-  933 \
-  940 \
-  942 \
-  944 \
-  945 \
-  947 \
-  951 \
-  981 \
-  982 \
-  984 \
-  985 \
-  986 \
-  988 \
-  989 \
-  990 \
-  991 \
-  992 \
-
-# Try to enforce that the directories correspond to the Java files we pull in.
-JVMTI_RUN_TEST_DIR_CHECK := $(sort $(foreach DIR,$(JVMTI_RUN_TEST_GENERATED_NUMBERS), \
-  $(filter $(DIR)%,$(LOCAL_SRC_FILES))))
-ifneq ($(sort $(LOCAL_SRC_FILES)),$(JVMTI_RUN_TEST_DIR_CHECK))
-  $(error Missing file, compare $(sort $(LOCAL_SRC_FILES)) with $(JVMTI_RUN_TEST_DIR_CHECK))
-endif
-
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_MODULE_TAGS := optional
-LOCAL_JAVA_LANGUAGE_VERSION := 1.8
-LOCAL_MODULE := run-test-jvmti-java
-
-GENERATED_SRC_DIR := $(call local-generated-sources-dir)
-JVMTI_RUN_TEST_GENERATED_FILES := \
-  $(foreach NR,$(JVMTI_RUN_TEST_GENERATED_NUMBERS),$(GENERATED_SRC_DIR)/results.$(NR).expected.txt)
-
-define GEN_JVMTI_RUN_TEST_GENERATED_FILE
-
-GEN_INPUT := $(wildcard $(LOCAL_PATH)/$(1)*/expected.txt)
-GEN_OUTPUT := $(GENERATED_SRC_DIR)/results.$(1).expected.txt
-$$(GEN_OUTPUT): $$(GEN_INPUT)
-	cp $$< $$@
-
-GEN_INPUT :=
-GEN_OUTPUT :=
-
-endef
-
-$(foreach NR,$(JVMTI_RUN_TEST_GENERATED_NUMBERS),\
-  $(eval $(call GEN_JVMTI_RUN_TEST_GENERATED_FILE,$(NR))))
-LOCAL_JAVA_RESOURCE_FILES := $(JVMTI_RUN_TEST_GENERATED_FILES)
-
-# We only want to depend on libcore.
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-all
-
-include $(BUILD_JAVA_LIBRARY)
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index afd9144..6017d28 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -19,7 +19,7 @@
 
 # Dependencies for actually running a run-test.
 TEST_ART_RUN_TEST_DEPENDENCIES := \
-  $(DX) \
+  $(HOST_OUT_EXECUTABLES)/dx \
   $(HOST_OUT_EXECUTABLES)/jasmin \
   $(HOST_OUT_EXECUTABLES)/smali \
   $(HOST_OUT_EXECUTABLES)/dexmerger \
diff --git a/test/ForClassLoaderA/Classes.java b/test/ForClassLoaderA/Classes.java
new file mode 100644
index 0000000..a65ef64
--- /dev/null
+++ b/test/ForClassLoaderA/Classes.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class DefinedInA {
+}
+
+class DefinedInAB {
+}
+
+class DefinedInABC {
+}
+
+class DefinedInAC {
+}
+
+class DefinedInAD {
+}
+
diff --git a/test/ForClassLoaderB/Classes.java b/test/ForClassLoaderB/Classes.java
new file mode 100644
index 0000000..8c85ed5
--- /dev/null
+++ b/test/ForClassLoaderB/Classes.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class DefinedInB {
+}
+
+class DefinedInAB {
+}
+
+class DefinedInABC {
+}
+
+class DefinedInBC {
+}
+
+class DefinedInBD {
+}
diff --git a/test/ForClassLoaderC/Classes.java b/test/ForClassLoaderC/Classes.java
new file mode 100644
index 0000000..7b9e83f
--- /dev/null
+++ b/test/ForClassLoaderC/Classes.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class DefinedInC {
+}
+
+class DefinedInAC {
+}
+
+class DefinedInABC {
+}
+
+class DefinedInBC {
+}
+
+class DefinedInCD {
+}
diff --git a/test/ForClassLoaderD/Classes.java b/test/ForClassLoaderD/Classes.java
new file mode 100644
index 0000000..b34177f
--- /dev/null
+++ b/test/ForClassLoaderD/Classes.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class DefinedInD {
+}
+
+class DefinedInAD {
+}
+
+class DefinedInBD {
+}
+
+class DefinedInCD {
+}
diff --git a/test/MultiDex/Second.java b/test/MultiDex/Second.java
index 540aedb..5067bcc 100644
--- a/test/MultiDex/Second.java
+++ b/test/MultiDex/Second.java
@@ -18,4 +18,10 @@
   public String getSecond() {
     return "I Second That.";
   }
+
+  // This method makes sure the second dex file has quickening
+  // instructions.
+  public String callSecond() {
+    return getSecond();
+  }
 }
diff --git a/test/README.md b/test/README.md
new file mode 100644
index 0000000..c68b40b
--- /dev/null
+++ b/test/README.md
@@ -0,0 +1,73 @@
+# VM test harness
+
+There are two suites of tests in this directory: run-tests and gtests.
+
+The run-tests are identified by directories named with with a numeric
+prefix and containing an info.txt file. For most run tests, the
+sources are in the "src" subdirectory. Sources found in the "src2"
+directory are compiled separately but to the same output directory;
+this can be used to exercise "API mismatch" situations by replacing
+class files created in the first pass. The "src-ex" directory is
+built separately, and is intended for exercising class loaders.
+
+The gtests are in named directories and contain a .java source
+file.
+
+All tests in either suite can be run using the "art/test.py"
+script. Additionally, run-tests can be run individidually. All of the
+tests can be run on the build host, on a USB-attached device, or using
+the build host "reference implementation".
+
+To see command flags run:
+
+```sh
+$ art/test.py -h
+```
+
+## Running all tests on the build host
+
+```sh
+$ art/test.py --host
+```
+
+## Running all tests on the target device
+
+```sh
+$ art/test.py --target
+```
+
+## Running all gtests on the build host
+
+```sh
+$ art/test.py --host -g
+```
+
+## Running all gtests on the target device
+
+```sh
+$ art/test.py --target -g
+```
+
+## Running all run-tests on the build host
+
+```sh
+$ art/test.py --host -r
+```
+
+## Running all run-tests on the target device
+
+```sh
+$ art/test.py --target -r
+```
+
+## Running one run-test on the build host
+
+```sh
+$ art/test.py --host -r -t 001-HelloWorld
+```
+
+## Running one run-test on the target device
+
+```sh
+$ art/test.py --target -r -t 001-HelloWorld
+```
diff --git a/test/README.txt b/test/README.txt
deleted file mode 100644
index eb1ce36..0000000
--- a/test/README.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-VM test harness.
-
-Use "./run-all-tests" to run all tests, or "./run-test <number>" to run a
-single test.  Run "./run-test" with no arguments to see command flags;
-in particular, the tests can be run on the desktop, on a USB-attached
-device, or using the desktop "reference implementation".
-
-
-For most tests, the sources are in the "src" subdirectory.  Sources found
-in the "src2" directory are compiled separately but to the same output
-directory; this can be used to exercise "API mismatch" situations by
-replacing class files created in the first pass.  The "src-ex" directory
-is built separately, and is intended for exercising class loaders.
diff --git a/test/etc/default-build b/test/etc/default-build
index 13f4301..bafd415 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -24,6 +24,13 @@
   HAS_SMALI=false
 fi
 
+# .j files in jasmin get compiled into classes.jar
+if [ -d jasmin ]; then
+  HAS_JASMIN=true
+else
+  HAS_JASMIN=false
+fi
+
 if [ -d src ]; then
   HAS_SRC=true
 else
@@ -55,6 +62,13 @@
   HAS_SMALI_MULTIDEX=false
 fi
 
+# .j files in jasmin-multidex get compiled into classes2.jar
+if [ -d jasmin-multidex ]; then
+  HAS_JASMIN_MULTIDEX=true
+else
+  HAS_JASMIN_MULTIDEX=false
+fi
+
 if [ -d src-ex ]; then
   HAS_SRC_EX=true
 else
@@ -80,7 +94,6 @@
 
 DX_FLAGS="--min-sdk-version=24"
 DX_VM_FLAGS=""
-SKIP_DX_MERGER="false"
 EXPERIMENTAL=""
 
 BUILD_MODE="target"
@@ -219,6 +232,21 @@
   fi
 }
 
+function make_jasmin() {
+  local out_directory="$1"
+  shift
+  local jasmin_sources=("$@")
+
+  mkdir -p "$out_directory"
+
+  if [[ $DEV_MODE == yes ]]; then
+    echo ${JASMIN} -d "$out_directory" "${jasmin_sources[@]}"
+    ${JASMIN} -d "$out_directory" "${jasmin_sources[@]}"
+  else
+    ${JASMIN} -d "$out_directory" "${jasmin_sources[@]}" >/dev/null
+  fi
+}
+
 function desugar() {
   local desugar_args=--mode=host
   if [[ $BUILD_MODE == target ]]; then
@@ -268,6 +296,26 @@
   ${DX} -JXmx256m ${DX_VM_FLAGS} --debug --dex --dump-to=${name}.lst --output=${name}.dex --dump-width=1000 ${DX_FLAGS} "${dx_input}"
 }
 
+# Merge all the dex files in $1..$N into $1. Skip non-existing files, but at least 1 file must exist.
+function make_dexmerge() {
+  # Dex file that acts as the destination.
+  local dst_file="$1"
+
+  # Dex files that act as the source.
+  local dex_files_to_merge=()
+
+  # Skip any non-existing files.
+  while [[ $# -gt 0 ]]; do
+    if [[ -e "$1" ]]; then
+      dex_files_to_merge+=("$1")
+    fi
+    shift
+  done
+
+  # Should have at least 1 dex_files_to_merge here, otherwise dxmerger will print the help.
+  ${DXMERGER} "$dst_file" "${dex_files_to_merge[@]}"
+}
+
 # Print the directory name only if it exists.
 function maybe_dir() {
   local dirname="$1"
@@ -281,11 +329,6 @@
   exit 0
 fi
 
-if ! [ "${HAS_SRC}" = "true" ] && ! [ "${HAS_SRC2}" = "true" ] && ! [ "${HAS_SRC_ART}" = "true" ]; then
-  # No src directory? Then forget about trying to run dx.
-  SKIP_DX_MERGER="true"
-fi
-
 if [ ${HAS_SRC_DEX2OAT_UNRESOLVED} = "true" ]; then
   mkdir classes
   mkdir classes-ex
@@ -332,7 +375,7 @@
     fi
 
     # Compile jack files into a DEX file.
-    if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ] || [ "${HAS_SRC_ART}" ]; then
+    if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ] || [ "${HAS_SRC_ART}" = "true" ]; then
       ${JACK} ${JACK_ARGS} ${jack_extra_args} --output-dex .
     fi
   else
@@ -361,22 +404,49 @@
     fi
 
     if [[ "${HAS_SRC}" == "true" || "${HAS_SRC2}" == "true" || "${HAS_SRC_ART}" == "true" ]]; then
-      if [ ${NEED_DEX} = "true" -a ${SKIP_DX_MERGER} = "false" ]; then
+      if [ ${NEED_DEX} = "true" ]; then
         make_dex classes
       fi
     fi
   fi
 fi
 
+if [[ "${HAS_JASMIN}" == true ]]; then
+  # Compile Jasmin classes as if they were part of the classes.dex file.
+  make_jasmin jasmin_classes $(find 'jasmin' -name '*.j')
+  if [[ "${NEED_DEX}" == "true" ]]; then
+    # Disable desugar because it won't handle intentional linkage errors.
+    USE_DESUGAR=false make_dex jasmin_classes
+    make_dexmerge classes.dex jasmin_classes.dex
+  else
+    # Move jasmin classes into classes directory so that they are picked up with -cp classes.
+    mkdir -p classes
+    mv jasmin_classes/* classes
+  fi
+fi
+
 if [ "${HAS_SMALI}" = "true" -a ${NEED_DEX} = "true" ]; then
   # Compile Smali classes
   ${SMALI} -JXmx512m assemble ${SMALI_ARGS} --output smali_classes.dex `find smali -name '*.smali'`
 
-  # Don't bother with dexmerger if we provide our own main function in a smali file.
-  if [ ${SKIP_DX_MERGER} = "false" ]; then
-    ${DXMERGER} classes.dex classes.dex smali_classes.dex
+  # Merge smali files into classes.dex, this takes priority over any jasmin files.
+  make_dexmerge classes.dex smali_classes.dex
+fi
+
+# Compile Jasmin classes in jasmin-multidex as if they were part of the classes2.jar
+if [[ "$HAS_JASMIN_MULTIDEX" == true ]]; then
+  make_jasmin jasmin_classes2 $(find 'jasmin-multidex' -name '*.j')
+
+  if [[ "${NEED_DEX}" == "true" ]]; then
+    # Disable desugar because it won't handle intentional linkage errors.
+    USE_DESUGAR=false make_dex jasmin_classes2
+
+    # Merge jasmin_classes2.dex into classes2.dex
+    make_dexmerge classes2.dex jasmin_classes2.dex
   else
-    mv smali_classes.dex classes.dex
+    # Move jasmin classes into classes2 directory so that they are picked up with -cp classes2.
+    mkdir -p classes2
+    mv jasmin_classes2/* classes2
   fi
 fi
 
@@ -384,12 +454,8 @@
   # Compile Smali classes
   ${SMALI} -JXmx512m assemble ${SMALI_ARGS} --output smali_classes2.dex `find smali-multidex -name '*.smali'`
 
-  # Don't bother with dexmerger if we provide our own main function in a smali file.
-  if [ ${HAS_SRC_MULTIDEX} = "true" ]; then
-    ${DXMERGER} classes2.dex classes2.dex smali_classes2.dex
-  else
-    mv smali_classes2.dex classes2.dex
-  fi
+  # Merge smali_classes2.dex into classes2.dex
+  make_dexmerge classes2.dex smali_classes2.dex
 fi
 
 
@@ -430,9 +496,9 @@
   fi
 fi
 
-# Create a single jar with two dex files for multidex.
+# Create a single dex jar with two dex files for multidex.
 if [ ${NEED_DEX} = "true" ]; then
-  if [ ${HAS_SRC_MULTIDEX} = "true" ] || [ ${HAS_SMALI_MULTIDEX} = "true" ]; then
+  if [ ${HAS_SRC_MULTIDEX} = "true" ] || [ ${HAS_JASMIN_MULTIDEX} = "true" ] || [ ${HAS_SMALI_MULTIDEX} = "true" ]; then
     zip $TEST_NAME.jar classes.dex classes2.dex
   else
     zip $TEST_NAME.jar classes.dex
diff --git a/test/knownfailures.json b/test/knownfailures.json
index d3d92c6..3edb0a8 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -100,6 +100,11 @@
         "bug": "http://b/35800768"
     },
     {
+        "tests": "163-app-image-methods",
+        "variant": "gcstress",
+        "description": ["This test sometimes runs out of memory initializing the boot classpath."]
+    },
+    {
         "tests": ["908-gc-start-finish",
                   "913-heaps"],
         "variant": "gcstress",
@@ -248,7 +253,7 @@
                   "602-deoptimizeable"],
         "description": ["Tests that should fail when the optimizing compiler ",
                         "compiles them non-debuggable."],
-        "variant": "optimizing & ndebuggable | regalloc_gc & ndebuggable | speed-profile & ndebuggable"
+        "variant": "optimizing & ndebuggable | regalloc_gc & ndebuggable | speed-profile & ndebuggable | jit & ndebuggable"
     },
     {
         "tests": "596-app-images",
@@ -538,7 +543,8 @@
             "595-profile-saving",
             "900-hello-plugin",
             "909-attach-agent",
-            "981-dedup-original-dex"
+            "981-dedup-original-dex",
+            "1900-track-alloc"
         ],
         "description": ["Tests that require exact knowledge of the number of plugins and agents."],
         "variant": "jvmti-stress | redefine-stress | trace-stress | field-stress | step-stress"
@@ -594,11 +600,12 @@
     },
     {
         "tests": [
-            "567-checker-compare"
+            "567-checker-compare",
+            "988-method-trace"
         ],
-        "description": "Checker tests failing when run with --build-with-javac-dx.",
+        "description": "Checker tests fail because desugar lowers Long.compare to lcmp",
         "env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"},
-        "bug": "b/62950048"
+        "bug": "b/63078894"
     },
     {
         "tests": [
@@ -638,6 +645,36 @@
         "env_vars": {"SANITIZE_TARGET": "address"}
     },
     {
+        "tests": [
+            "059-finalizer-throw",
+            "074-gc-thrash",
+            "911-get-stack-trace",
+            "913-heaps",
+            "980-redefine-object"
+        ],
+        "description": [
+            "Interpreter with access checks stack frames are too large and result in",
+            "StackOverFlow errors being thrown."
+        ],
+        "variant": "interp-ac & host",
+        "env_vars": {"SANITIZE_HOST": "address"}
+    },
+    {
+        "tests": [
+            "059-finalizer-throw",
+            "074-gc-thrash",
+            "911-get-stack-trace",
+            "913-heaps",
+            "980-redefine-object"
+        ],
+        "description": [
+            "Interpreter with access checks stack frames are too large and result in",
+            "StackOverFlow errors being thrown."
+        ],
+        "variant": "interp-ac & target",
+        "env_vars": {"SANITIZE_TARGET": "address"}
+    },
+    {
         "tests": "071-dexfile-map-clean",
         "description": [ "We use prebuilt zipalign on master-art-host to avoid pulling in a lot",
                          "of the framework. But a non-sanitized zipalign binary does not work with",
@@ -645,6 +682,18 @@
         "env_vars": {"SANITIZE_HOST": "address"}
     },
     {
+        "tests": "141-class-unload",
+        "description": "Segmentation fault",
+        "bug": "b/31098949",
+        "env_vars": {"SANITIZE_HOST": "address"}
+    },
+    {
+        "tests": "104-growth-limit",
+        "description": "Flake",
+        "bug": "b/63514331",
+        "env_vars": {"SANITIZE_HOST": "address"}
+    },
+    {
         "tests": ["988-method-trace"],
         "variant": "redefine-stress | jvmti-stress",
         "description": "Test disabled due to redefine-stress disabling intrinsics which changes the trace output slightly."
diff --git a/test/run-all-tests b/test/run-all-tests
deleted file mode 100755
index a0d2f23..0000000
--- a/test/run-all-tests
+++ /dev/null
@@ -1,241 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2007 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.
-
-# Set up prog to be the path of this script, including following symlinks,
-# and set up progdir to be the fully-qualified pathname of its directory.
-prog="$0"
-while [ -h "${prog}" ]; do
-    newProg=`/bin/ls -ld "${prog}"`
-    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
-    if expr "x${newProg}" : 'x/' >/dev/null; then
-        prog="${newProg}"
-    else
-        progdir=`dirname "${prog}"`
-        prog="${progdir}/${newProg}"
-    fi
-done
-oldwd=`pwd`
-progdir=`dirname "${prog}"`
-cd "${progdir}"
-progdir=`pwd`
-prog="${progdir}"/`basename "${prog}"`
-
-run_args=""
-usage="no"
-sequental="no"
-
-while true; do
-    if [ "x$1" = "x--host" ]; then
-        run_args="${run_args} --host"
-        shift
-    elif [ "x$1" = "x--use-java-home" ]; then
-        run_args="${run_args} --use-java-home"
-        shift
-    elif [ "x$1" = "x--no-image" ]; then
-        run_args="${run_args} --no-image"
-        shift
-    elif [ "x$1" = "x--optimizing" ]; then
-        run_args="${run_args} --optimizing"
-        shift
-    elif [ "x$1" = "x--image" ]; then
-        run_args="${run_args} --image"
-        shift
-    elif [ "x$1" = "x--never-clean" ]; then
-        run_args="${run_args} --never-clean"
-        shift
-    elif [ "x$1" = "x--jvm" ]; then
-        run_args="${run_args} --jvm"
-        shift
-    elif [ "x$1" = "x--debug" ]; then
-        run_args="${run_args} --debug"
-        shift
-    elif [ "x$1" = "x--build-only" ]; then
-        run_args="${run_args} --build-only"
-        shift
-    elif [ "x$1" = "x--build-with-jack" ]; then
-        run_args="${run_args} --build-with-jack"
-        shift
-    elif [ "x$1" = "x--build-with-javac-dx" ]; then
-        run_args="${run_args} --build-with-javac-dx"
-        shift
-    elif [ "x$1" = "x--dex2oat-swap" ]; then
-        run_args="${run_args} --dex2oat-swap"
-        shift
-    elif [ "x$1" = "x--dalvik" ]; then
-        run_args="${run_args} --dalvik"
-        shift
-    elif [ "x$1" = "x--debuggable" ]; then
-        run_args="${run_args} --debuggable"
-        shift
-    elif [ "x$1" = "x--zygote" ]; then
-        run_args="${run_args} --zygote"
-        shift
-    elif [ "x$1" = "x--interpreter" ]; then
-        run_args="${run_args} --interpreter"
-        shift
-    elif [ "x$1" = "x--jit" ]; then
-        run_args="${run_args} --jit"
-        shift
-    elif [ "x$1" = "x--verify-soft-fail" ]; then
-        run_args="${run_args} --verify-soft-fail"
-        shift
-    elif [ "x$1" = "x--no-verify" ]; then
-        run_args="${run_args} --no-verify"
-        shift
-    elif [ "x$1" = "x--no-optimize" ]; then
-        run_args="${run_args} --no-optimize"
-        shift
-    elif [ "x$1" = "x--dev" ]; then
-        run_args="${run_args} --dev"
-        shift
-    elif [ "x$1" = "x--update" ]; then
-        run_args="${run_args} --update"
-        shift
-    elif [ "x$1" = "x--help" ]; then
-        usage="yes"
-        shift
-    elif [ "x$1" = "x--seq" ]; then
-        sequental="yes"
-        shift
-    elif [ "x$1" = "x-O" ]; then
-        run_args="${run_args} -O"
-        shift
-    elif [ "x$1" = "x--64" ]; then
-        run_args="${run_args} --64"
-        shift
-    elif [ "x$1" = "x--gcstress" ]; then
-        run_args="${run_args} --gcstress"
-        shift
-    elif [ "x$1" = "x--gcverify" ]; then
-        run_args="${run_args} --gcverify"
-        shift
-    elif [ "x$1" = "x--trace" ]; then
-        run_args="${run_args} --trace"
-        shift
-    elif [ "x$1" = "x--relocate" ]; then
-        run_args="${run_args} --relocate"
-        shift
-    elif [ "x$1" = "x--no-relocate" ]; then
-        run_args="${run_args} --no-relocate"
-        shift
-    elif [ "x$1" = "x--no-prebuild" ]; then
-        run_args="${run_args} --no-prebuild"
-        shift;
-    elif [ "x$1" = "x--prebuild" ]; then
-        run_args="${run_args} --prebuild"
-        shift;
-    elif [ "x$1" = "x--no-dex2oat" ]; then
-        run_args="${run_args} --no-dex2oat"
-        shift;
-    elif [ "x$1" = "x--no-patchoat" ]; then
-        run_args="${run_args} --no-patchoat"
-        shift;
-    elif [ "x$1" = "x--always-clean" ]; then
-        run_args="${run_args} --always-clean"
-        shift
-    elif [ "x$1" = "x--pic-test" ]; then
-        run_args="${run_args} --pic-test"
-        shift
-    elif [ "x$1" = "x--pic-image" ]; then
-        run_args="${run_args} --pic-image"
-        shift
-    elif [ "x$1" = "x--strace" ]; then
-        run_args="${run_args} --strace"
-        shift
-    elif [ "x$1" = "x--random-profile" ]; then
-        run_args="${run_args} --random-profile"
-        shift
-    elif expr "x$1" : "x--" >/dev/null 2>&1; then
-        echo "unknown $0 option: $1" 1>&2
-        usage="yes"
-        break
-    else
-        break
-    fi
-done
-
-if [ "$usage" = "yes" ]; then
-    prog=`basename $prog`
-    (
-        echo "usage:"
-        echo "  $prog --help     Print this message."
-        echo "  $prog [options]  Run all tests with the given options."
-        echo "  Options are all passed to run-test; refer to that for " \
-             "further documentation:"
-        echo "    --debug --dev --host --interpreter --jit --jvm --no-optimize"
-        echo "    --no-verify --verify-soft-fail -O --update --zygote --64"
-        echo "    --relocate --prebuild --always-clean --gcstress --gcverify"
-        echo "    --trace --no-patchoat --no-dex2oat --use-java-home --pic-image"
-        echo "    --pic-test --strace --debuggable --dalvik --dex2oat-swap"
-        echo "    --build-only --build-with-jack --build-with-javac-dx"
-        echo "    --never-clean --image --no-image --optimizing"
-        echo "    --no-relocate --no-prebuild"
-        echo "  Specific Runtime Options:"
-        echo "    --seq                Run tests one-by-one, avoiding failures caused by busy CPU"
-    ) 1>&2
-    exit 1
-fi
-
-if [ "$sequental" == "yes" ]; then
-  i=0
-  for test_name in *; do
-    if [ -d "$test_name" -a -r "$test_name" -a -r "$test_name/info.txt" ]; then
-      ./run-test ${run_args} "$test_name"
-      RES=$?
-      test_pids[i]=i
-      test_names[test_pids[i]]="$test_name"
-      if [ "$RES" != "0" ]; then
-        let failure_count+=1
-        failed_test_names="$failed_test_names ${test_names[i]}"
-      else
-        let succeeded_count+=1
-      fi
-      let i+=1
-    fi
-  done
-else
-  # start all the tests
-  i=0
-  for test_name in *; do
-    if [ -d "$test_name" -a -r "$test_name" -a -r "$test_name/info.txt" ]; then
-      ./run-test ${run_args} "$test_name" &
-      test_pids[i]=$!
-      test_names[test_pids[i]]="$test_name"
-      let i+=1
-    fi
-  done
-
-  # wait for all the tests, collecting the failures
-  failure_count=0
-  succeeded_count=0
-  failed_test_names=""
-  for pid in ${test_pids[@]}; do
-    wait $pid
-    if [ "$?" != "0" ]; then
-      let failure_count+=1
-      failed_test_names="$failed_test_names ${test_names[$pid]}[pid=$pid]"
-    else
-      let succeeded_count+=1
-    fi
-  done
-fi
-
-echo "succeeded tests: $succeeded_count"
-echo "failed tests: $failure_count"
-
-for i in $failed_test_names; do
-  echo "failed: $i"
-done
diff --git a/test/ti-agent/suspension_helper.cc b/test/ti-agent/suspension_helper.cc
new file mode 100644
index 0000000..b685cb2
--- /dev/null
+++ b/test/ti-agent/suspension_helper.cc
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include <vector>
+
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace common_suspension {
+
+extern "C" JNIEXPORT jboolean JNICALL Java_art_Suspension_isSuspended(
+    JNIEnv* env, jclass, jthread thr) {
+  jint state;
+  if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetThreadState(thr, &state))) {
+    return false;
+  }
+  return (state & JVMTI_THREAD_STATE_SUSPENDED) != 0;
+}
+
+static std::vector<jthread> CopyToVector(JNIEnv* env, jobjectArray thrs) {
+  jsize len = env->GetArrayLength(thrs);
+  std::vector<jthread> ret;
+  for (jsize i = 0; i < len; i++) {
+    ret.push_back(reinterpret_cast<jthread>(env->GetObjectArrayElement(thrs, i)));
+  }
+  return ret;
+}
+
+extern "C" JNIEXPORT jintArray JNICALL Java_art_Suspension_resumeList(JNIEnv* env,
+                                                                      jclass,
+                                                                      jobjectArray thr) {
+  static_assert(sizeof(jvmtiError) == sizeof(jint), "cannot use jintArray as jvmtiError array");
+  std::vector<jthread> threads(CopyToVector(env, thr));
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  }
+  jintArray ret = env->NewIntArray(threads.size());
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  }
+  jint* elems = env->GetIntArrayElements(ret, nullptr);
+  JvmtiErrorToException(env, jvmti_env,
+                        jvmti_env->ResumeThreadList(threads.size(),
+                                                    threads.data(),
+                                                    reinterpret_cast<jvmtiError*>(elems)));
+  env->ReleaseIntArrayElements(ret, elems, 0);
+  return ret;
+}
+
+extern "C" JNIEXPORT jintArray JNICALL Java_art_Suspension_suspendList(JNIEnv* env,
+                                                                       jclass,
+                                                                       jobjectArray thrs) {
+  static_assert(sizeof(jvmtiError) == sizeof(jint), "cannot use jintArray as jvmtiError array");
+  std::vector<jthread> threads(CopyToVector(env, thrs));
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  }
+  jintArray ret = env->NewIntArray(threads.size());
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  }
+  jint* elems = env->GetIntArrayElements(ret, nullptr);
+  JvmtiErrorToException(env, jvmti_env,
+                        jvmti_env->SuspendThreadList(threads.size(),
+                                                     threads.data(),
+                                                     reinterpret_cast<jvmtiError*>(elems)));
+  env->ReleaseIntArrayElements(ret, elems, 0);
+  return ret;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Suspension_resume(JNIEnv* env, jclass, jthread thr) {
+  JvmtiErrorToException(env, jvmti_env, jvmti_env->ResumeThread(thr));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Suspension_suspend(JNIEnv* env, jclass, jthread thr) {
+  JvmtiErrorToException(env, jvmti_env, jvmti_env->SuspendThread(thr));
+}
+
+}  // namespace common_suspension
+}  // namespace art
+
diff --git a/tools/art b/tools/art
index 2e5df91..077dc4a 100644
--- a/tools/art
+++ b/tools/art
@@ -24,7 +24,7 @@
 LIBART=libart.so
 JIT_PROFILE="no"
 VERBOSE="no"
-EXTRA_OPTIONS=""
+EXTRA_OPTIONS=()
 
 # Follow all sym links to get the program name.
 if [ z"$BASH_SOURCE" != z ]; then
@@ -108,14 +108,48 @@
   fi
 }
 
+# Given 'VAR1=VAL VAR2=VAL2 ... cmd arg1 arg2 ... argN' run the 'cmd' with the args
+# with the modified environment {VAR1=VAL,VAL2=,...}.
+#
+# Also prints the command to be run if verbose mode is enabled.
 function verbose_run() {
   if [ "$VERBOSE" = "yes" ]; then
     echo "$@"
   fi
-  eval "$@"
+
+  env "$@"
+}
+
+# Automatically find the boot image location. It uses core.art by default.
+# On a real device, it might only have a boot.art, so use that instead when core.art does not exist.
+function detect_boot_image_location() {
+  local image_location_dir="$ANDROID_ROOT/framework"
+  local image_location_name="core.art"
+
+  local maybe_arch
+  local core_image_exists="false"
+
+  # Parse ARCHS={a,b,c,d} syntax.
+  local array
+  IFS=, read -a array <<< "${ARCHS:1:(-1)}";
+  for maybe_arch in "${array[@]}"; do
+    if [[ -e "$image_location_dir/$maybe_arch/$image_location_name" ]]; then
+      core_image_exists="true"
+      break
+    fi
+  done
+
+  if [[ "$core_image_exists" == "false" ]]; then
+    image_location_name="boot.art"
+  fi
+
+  local image_location="$image_location_dir/$image_location_name"
+  echo "$image_location"
 }
 
 function run_art() {
+  local image_location="$(detect_boot_image_location)"
+
   verbose_run ANDROID_DATA=$ANDROID_DATA               \
               ANDROID_ROOT=$ANDROID_ROOT               \
               LD_LIBRARY_PATH=$LD_LIBRARY_PATH         \
@@ -124,12 +158,12 @@
               $LAUNCH_WRAPPER $ART_BINARY_PATH $lib    \
               -XXlib:$LIBART                           \
               -Xnorelocate                             \
-              -Ximage:$ANDROID_ROOT/framework/core.art \
+              -Ximage:"$image_location"                \
               "$@"
 }
 
 while [[ "$1" = "-"* ]]; do
-  case $1 in
+  case "$1" in
   --)
     # No more arguments for this script.
     shift
@@ -149,7 +183,7 @@
   --debug)
     LIBART="libartd.so"
     # Expect that debug mode wants all checks.
-    EXTRA_OPTIONS="${EXTRA_OPTIONS} -XX:SlowDebug=true"
+    EXTRA_OPTIONS+=(-XX:SlowDebug=true)
     ;;
   --gdb)
     LIBART="libartd.so"
@@ -210,14 +244,20 @@
 # If ANDROID_DATA is the system ANDROID_DATA or is not set, use our own,
 # and ensure we delete it at the end.
 if [ "$ANDROID_DATA" = "/data" ] || [ "$ANDROID_DATA" = "" ]; then
-  ANDROID_DATA=$PWD/android-data$$
+  if [[ $PWD != / ]]; then
+    ANDROID_DATA="$PWD/android-data$$"
+  else
+    # Use /data/local/tmp when running this from adb shell, since it starts out in /
+    # by default.
+    ANDROID_DATA="$ANDROID_DATA/local/tmp/android-data$$"
+  fi
   mkdir -p $ANDROID_DATA/dalvik-cache/$ARCHS
   DELETE_ANDROID_DATA="yes"
 fi
 
 if [ "$PERF" != "" ]; then
   LAUNCH_WRAPPER="perf record -g -o $ANDROID_DATA/perf.data -e cycles:u $LAUNCH_WRAPPER"
-  EXTRA_OPTIONS="-Xcompiler-option --generate-debug-info"
+  EXTRA_OPTIONS+=(-Xcompiler-option --generate-debug-info)
 fi
 
 if [ "$JIT_PROFILE" = "yes" ]; then
@@ -251,18 +291,15 @@
   rm -rf $ANDROID_DATA/dalvik-cache/$ARCHS/*
 
   # Append arguments so next invocation of run_art uses the profile.
-  EXTRA_OPTIONS="$EXTRA_OPTIONS -Xcompiler-option --profile-file=$PROFILE_PATH"
+  EXTRA_OPTIONS+=(-Xcompiler-option --profile-file="$PROFILE_PATH")
 fi
 
-# Protect additional arguments in quotes to preserve whitespaces when evaluated.
-# This is for run-jdwp-test.sh which uses this script and has arguments with
-# whitespaces when running on device.
-while [ $# -gt 0 ]; do
-  EXTRA_OPTIONS="$EXTRA_OPTIONS \"$1\""
-  shift
-done
+# Protect additional arguments in quotes to preserve whitespaces (used by
+# run-jdwp-test.sh when running on device), '$' (may be used as part of
+# classpath) and other special characters when evaluated.
+EXTRA_OPTIONS+=("$@")
 
-run_art $EXTRA_OPTIONS
+run_art "${EXTRA_OPTIONS[@]}"
 EXIT_STATUS=$?
 
 if [ "$PERF" != "" ]; then
diff --git a/tools/generate-boot-image-profile.sh b/tools/generate-boot-image-profile.sh
new file mode 100755
index 0000000..d87123a
--- /dev/null
+++ b/tools/generate-boot-image-profile.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# This script creates a boot image profile based on input profiles.
+#
+
+if [[ "$#" -lt 2 ]]; then
+  echo "Usage $0 <output> <profman args> <profiles>+"
+  echo "Also outputs <output>.txt and <output>.preloaded-classes"
+  echo 'Example: generate-boot-image-profile.sh boot.prof --profman-arg --boot-image-sampled-method-threshold=1 profiles/0/*/primary.prof'
+  exit 1
+fi
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+TOP="$DIR/../.."
+source "${TOP}/build/envsetup.sh" >&/dev/null # import get_build_var
+
+OUT_PROFILE=$1
+shift
+
+# Read the profman args.
+profman_args=()
+while [[ "$#" -ge 2 ]] && [[ "$1" = '--profman-arg' ]]; do
+  profman_args+=("$2")
+  shift 2
+done
+
+# Remaining args are all the profiles.
+for file in "$@"; do
+  if [[ -s $file ]]; then
+    profman_args+=("--profile-file=$file")
+  fi
+done
+
+jar_args=()
+boot_jars=$("$ANDROID_BUILD_TOP"/art/tools/bootjars.sh --target)
+jar_dir=$ANDROID_BUILD_TOP/$(get_build_var TARGET_OUT_JAVA_LIBRARIES)
+for file in $boot_jars; do
+  filename="$jar_dir/$file.jar"
+  jar_args+=("--apk=$filename")
+  jar_args+=("--dex-location=$filename")
+done
+profman_args+=("${jar_args[@]}")
+
+# Generate the profile.
+"$ANDROID_HOST_OUT/bin/profman" --generate-boot-image-profile "--reference-profile-file=$OUT_PROFILE" "${profman_args[@]}"
+
+# Convert it to text.
+echo Dumping profile to $OUT_PROFILE.txt
+"$ANDROID_HOST_OUT/bin/profman" --dump-classes-and-methods "--profile-file=$OUT_PROFILE" "${jar_args[@]}" > "$OUT_PROFILE.txt"
+
+# Generate preloaded classes
+# Filter only classes by using grep -v
+# Remove first and last characters L and ;
+# Replace / with . to make dot format
+grep -v "\\->" "$OUT_PROFILE.txt" | sed 's/.\(.*\)./\1/g' | tr "/" "." > "$OUT_PROFILE.preloaded-classes"
+
+# You may need to filter some classes out since creating threads is not allowed in the zygote.
+# i.e. using: grep -v -E '(android.net.ConnectivityThread\$Singleton)'
diff --git a/tools/jfuzz/jfuzz.cc b/tools/jfuzz/jfuzz.cc
index 82683f2..4cd2335 100644
--- a/tools/jfuzz/jfuzz.cc
+++ b/tools/jfuzz/jfuzz.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <cmath>
 #include <random>
 
 #include <inttypes.h>
@@ -54,7 +55,7 @@
  * to preserve the property that a given version of JFuzz yields the same
  * fuzzed program for a deterministic random seed.
  */
-const char* VERSION = "1.2";
+const char* VERSION = "1.3";
 
 /*
  * Maximum number of array dimensions, together with corresponding maximum size
@@ -698,6 +699,72 @@
     return mayFollow;
   }
 
+  // Emit one dimension of an array initializer, where parameter dim >= 1
+  // denotes the number of remaining dimensions that should be emitted.
+  void emitArrayInitDim(int dim) {
+    if (dim == 1) {
+      // Last dimension: set of values.
+      fputs("{ ", out_);
+      for (uint32_t i = 0; i < array_size_; i++) {
+        emitExpression(array_type_);
+        fputs(", ", out_);
+      }
+      fputs("}", out_);
+
+    } else {
+      // Outer dimensions: set of sets.
+      fputs("{\n", out_);
+      indentation_ += 2;
+      emitIndentation();
+
+      for (uint32_t i = 0; i < array_size_; i++) {
+        emitArrayInitDim(dim - 1);
+        if (i != array_size_ - 1) {
+          fputs(",\n", out_);
+          emitIndentation();
+        }
+      }
+
+      fputs(",\n", out_);
+      indentation_ -= 2;
+      emitIndentation();
+      fputs("}", out_);
+    }
+  }
+
+  // Emit an array initializer of the following form.
+  //   {
+  //     type[]..[] tmp = { .. };
+  //     mArray = tmp;
+  //   }
+  bool emitArrayInit() {
+    // Avoid elaborate array initializers.
+    uint64_t p = pow(array_size_, array_dim_);
+    if (p > 20) {
+      return emitAssignment();  // fall back
+    }
+
+    fputs("{\n", out_);
+
+    indentation_ += 2;
+    emitIndentation();
+    emitType(array_type_);
+    for (uint32_t i = 0; i < array_dim_; i++) {
+      fputs("[]", out_);
+    }
+    fputs(" tmp = ", out_);
+    emitArrayInitDim(array_dim_);
+    fputs(";\n", out_);
+
+    emitIndentation();
+    fputs("mArray = tmp;\n", out_);
+
+    indentation_ -= 2;
+    emitIndentation();
+    fputs("}\n", out_);
+    return true;
+  }
+
   // Emit a for loop.
   bool emitForLoop() {
     // Continuing loop nest becomes less likely as the depth grows.
@@ -874,10 +941,11 @@
       case 2:  return emitContinue();    break;
       case 3:  return emitBreak();       break;
       case 4:  return emitScope();       break;
-      case 5:  return emitForLoop();     break;
-      case 6:  return emitDoLoop();      break;
-      case 7:  return emitIfStmt();      break;
-      case 8:  return emitSwitch();      break;
+      case 5:  return emitArrayInit();   break;
+      case 6:  return emitForLoop();     break;
+      case 7:  return emitDoLoop();      break;
+      case 8:  return emitIfStmt();      break;
+      case 9:  return emitSwitch();      break;
       default: return emitAssignment();  break;
     }
   }
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 8a4c2df..c6553f8 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -223,5 +223,10 @@
   result: EXEC_FAILED,
   bug: 62408076,
   names: ["libcore.java.lang.reflect.annotations.AnnotatedElementParameterTest#testImplicitConstructorParameters_singleAnnotation"]
+},
+{
+  description: "java.io.IOException: Error writing ASN.1 encoding",
+  result: EXEC_FAILED,
+  names: ["libcore.javax.crypto.spec.AlgorithmParametersTestGCM#testEncoding"]
 }
 ]
diff --git a/tools/runtime_memusage/README b/tools/runtime_memusage/README
index 2543df1..2af1de5 100644
--- a/tools/runtime_memusage/README
+++ b/tools/runtime_memusage/README
@@ -40,6 +40,17 @@
 
 ===========================================================================
 Usage: sanitizer_logcat_analysis.sh [options] [LOGCAT_FILE] [CATEGORIES...]
+    -a
+        Forces all pids associated with registered dex
+        files in the logcat to be processed.
+        default: only the last pid is processed
+
+    -b  [DEX_FILE_NUMBER]
+        Outputs data for the specified baksmali
+        dump if -p is provided.
+        default: first baksmali dump in order of dex
+          file registration
+
     -d  OUT_DIRECTORY
         Puts all output in specified directory.
         If not given, output will be put in a local
@@ -52,14 +63,31 @@
         the -m argument or by prune_sanitizer_output.py
 
     -f
-        forces redo of all commands even if output
-        files exist.
+        Forces redo of all commands even if output
+        files exist. Steps are skipped if their output
+        exist already and this is not enabled.
 
     -m  [MINIMUM_CALLS_PER_TRACE]
         Filters out all traces that do not have
         at least MINIMUM_CALLS_PER_TRACE lines.
         default: specified by prune_sanitizer_output.py
 
+    -o  [OFFSET],[OFFSET]
+        Filters out all Dex File offsets outside the
+        range between provided offsets. 'inf' can be
+        provided for infinity.
+        default: 0,inf
+
+    -p  [PACKAGE_NAME]
+        Using the package name, uses baksmali to get
+        a dump of the Dex File format for the package.
+
+    -t  [TIME_OFFSET],[TIME_OFFSET]
+        Filters out all time offsets outside the
+        range between provided offsets. 'inf' can be
+        provided for infinity.
+        default: 0,inf
+
     CATEGORIES are words that are expected to show in
        a large subset of symbolized traces. Splits
        output based on each word.
diff --git a/tools/runtime_memusage/prune_sanitizer_output.py b/tools/runtime_memusage/prune_sanitizer_output.py
index d95b2ce..3cc51cf 100755
--- a/tools/runtime_memusage/prune_sanitizer_output.py
+++ b/tools/runtime_memusage/prune_sanitizer_output.py
@@ -33,7 +33,7 @@
     """
     # Hard coded string are necessary since each trace must have the address
     # accessed, which is printed before trace lines.
-    if match == "use-after-poison":
+    if match == "use-after-poison" or match == "unknown-crash":
         return -2
     elif match == "READ":
         return -1
@@ -43,6 +43,9 @@
 
 def clean_trace_if_valid(trace, stack_min_size, prune_exact):
     """Cleans trace if it meets a certain standard. Returns None otherwise."""
+    # Note: Sample input may contain "unknown-crash" instead of
+    # "use-after-poison"
+    #
     # Sample input:
     #   trace:
     # "...ERROR: AddressSanitizer: use-after-poison on address 0x0071126a870a...
@@ -68,6 +71,7 @@
     trace_line_matches = [(match_to_int(match.group()), match.start())
                           for match in re.finditer("#[0-9]+ "
                                                    "|use-after-poison"
+                                                   "|unknown-crash"
                                                    "|READ", trace)
                           ]
     # Finds the first index where the line number ordering isn't in sequence or
@@ -135,16 +139,17 @@
                          ]
     trace_clean_split = [trace for trace in trace_clean_split
                          if trace is not None]
-
-    outfile = os.path.join(out_dir_name, trace_file.name + "_filtered")
+    filename = os.path.basename(trace_file.name + "_filtered")
+    outfile = os.path.join(out_dir_name, filename)
     with open(outfile, "w") as output_file:
         output_file.write(STACK_DIVIDER.join(trace_clean_split))
 
     filter_percent = 100.0 - (float(len(trace_clean_split)) /
                               len(trace_split) * 100)
     filter_amount = len(trace_split) - len(trace_clean_split)
-    print("Filtered out %d (%f%%) of %d."
-          % (filter_amount, filter_percent, len(trace_split)))
+    print("Filtered out %d (%f%%) of %d. %d (%f%%) remain."
+          % (filter_amount, filter_percent, len(trace_split),
+             len(trace_split) - filter_amount, 1 - filter_percent))
 
 
 if __name__ == "__main__":
diff --git a/tools/runtime_memusage/sanitizer_logcat_analysis.sh b/tools/runtime_memusage/sanitizer_logcat_analysis.sh
index 75cb9a9..e1a8161 100755
--- a/tools/runtime_memusage/sanitizer_logcat_analysis.sh
+++ b/tools/runtime_memusage/sanitizer_logcat_analysis.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 #
 # Copyright (C) 2017 The Android Open Source Project
 #
@@ -18,13 +18,29 @@
 #
 # This script takes in a logcat containing Sanitizer traces and outputs several
 # files, prints information regarding the traces, and plots information as well.
+ALL_PIDS=false
 USE_TEMP=true
 DO_REDO=false
+PACKAGE_NAME=""
+BAKSMALI_NUM=0
 # EXACT_ARG and MIN_ARG are passed to prune_sanitizer_output.py
 EXACT_ARG=""
-MIN_ARG=""
+MIN_ARG=()
+OFFSET_ARGS=()
+TIME_ARGS=()
 usage() {
   echo "Usage: $0 [options] [LOGCAT_FILE] [CATEGORIES...]"
+  echo "    -a"
+  echo "        Forces all pids associated with registered dex"
+  echo "        files in the logcat to be processed."
+  echo "        default: only the last pid is processed"
+  echo
+  echo "    -b  [DEX_FILE_NUMBER]"
+  echo "        Outputs data for the specified baksmali"
+  echo "        dump if -p is provided."
+  echo "        default: first baksmali dump in order of dex"
+  echo "          file registration"
+  echo
   echo "    -d  OUT_DIRECTORY"
   echo "        Puts all output in specified directory."
   echo "        If not given, output will be put in a local"
@@ -37,7 +53,7 @@
   echo "        the -m argument or by prune_sanitizer_output.py"
   echo
   echo "    -f"
-  echo "        forces redo of all commands even if output"
+  echo "        Forces redo of all commands even if output"
   echo "        files exist. Steps are skipped if their output"
   echo "        exist already and this is not enabled."
   echo
@@ -46,6 +62,22 @@
   echo "        at least MINIMUM_CALLS_PER_TRACE lines."
   echo "        default: specified by prune_sanitizer_output.py"
   echo
+  echo "    -o  [OFFSET],[OFFSET]"
+  echo "        Filters out all Dex File offsets outside the"
+  echo "        range between provided offsets. 'inf' can be"
+  echo "        provided for infinity."
+  echo "        default: 0,inf"
+  echo
+  echo "    -p  [PACKAGE_NAME]"
+  echo "        Using the package name, uses baksmali to get"
+  echo "        a dump of the Dex File format for the package."
+  echo
+  echo "    -t  [TIME_OFFSET],[TIME_OFFSET]"
+  echo "        Filters out all time offsets outside the"
+  echo "        range between provided offsets. 'inf' can be"
+  echo "        provided for infinity."
+  echo "        default: 0,inf"
+  echo
   echo "    CATEGORIES are words that are expected to show in"
   echo "       a large subset of symbolized traces. Splits"
   echo "       output based on each word."
@@ -55,35 +87,72 @@
 }
 
 
-while [[ $# -gt 1 ]]; do
-case $1 in
-  -d)
-  shift
-  USE_TEMP=false
-  OUT_DIR=$1
-  shift
-  break
-  ;;
-  -e)
-  shift
-  EXACT_ARG='-e'
-  ;;
-  -f)
-  shift
-  DO_REDO=true
-  ;;
-  -m)
-  shift
-  MIN_ARG='-m '"$1"''
-  shift
-  ;;
-  *)
-  usage
-  exit
+while getopts ":ab:d:efm:o:p:t:" opt ; do
+case ${opt} in
+  a)
+    ALL_PIDS=true
+    ;;
+  b)
+    if ! [[ "$OPTARG" -eq "$OPTARG" ]]; then
+      usage
+      exit
+    fi
+    BAKSMALI_NUM=$OPTARG
+    ;;
+  d)
+    USE_TEMP=false
+    OUT_DIR=$OPTARG
+    ;;
+  e)
+    EXACT_ARG='-e'
+    ;;
+  f)
+    DO_REDO=true
+    ;;
+  m)
+    if ! [[ "$OPTARG" -eq "$OPTARG" ]]; then
+      usage
+      exit
+    fi
+    MIN_ARG=( "-m" "$OPTARG" )
+    ;;
+  o)
+    set -f
+    old_ifs=$IFS
+    IFS=","
+    OFFSET_ARGS=( $OPTARG )
+    if [[ "${#OFFSET_ARGS[@]}" -ne 2 ]]; then
+      usage
+      exit
+    fi
+    OFFSET_ARGS=( "--offsets" "${OFFSET_ARGS[@]}" )
+    IFS=$old_ifs
+    set +f
+    ;;
+  t)
+    set -f
+    old_ifs=$IFS
+    IFS=","
+    TIME_ARGS=( $OPTARG )
+    if [[ "${#TIME_ARGS[@]}" -ne 2 ]]; then
+      usage
+      exit
+    fi
+    TIME_ARGS=( "--times" "${TIME_ARGS[@]}" )
+    IFS=$old_ifs
+    set +f
+    ;;
+  p)
+    PACKAGE_NAME=$OPTARG
+    ;;
+  \?)
+    usage
+    exit
 esac
 done
+shift $((OPTIND -1))
 
-if [ $# -lt 1 ]; then
+if [[ $# -lt 1 ]]; then
   usage
   exit
 fi
@@ -92,89 +161,214 @@
 NUM_CAT=$(($# - 1))
 
 # Use a temp directory that will be deleted
-if [ $USE_TEMP = true ]; then
-  OUT_DIR=$(mktemp -d --tmpdir=$PWD)
+if [[ $USE_TEMP = true ]]; then
+  OUT_DIR=$(mktemp -d --tmpdir="$PWD")
   DO_REDO=true
 fi
 
-if [ ! -d "$OUT_DIR" ]; then
-  mkdir $OUT_DIR
+if [[ ! -d "$OUT_DIR" ]]; then
+  mkdir "$OUT_DIR"
   DO_REDO=true
 fi
 
 # Note: Steps are skipped if their output exists until -f flag is enabled
-# Step 1 - Only output lines related to Sanitizer
-# Folder that holds all file output
 echo "Output folder: $OUT_DIR"
-ASAN_OUT=$OUT_DIR/asan_output
-if [ ! -f $ASAN_OUT ] || [ $DO_REDO = true ]; then
-  DO_REDO=true
-  echo "Extracting ASAN output"
-  grep "app_process64" $LOGCAT_FILE > $ASAN_OUT
-else
-  echo "Skipped: Extracting ASAN output"
+# Finds the lines matching pattern criteria and prints out unique instances of
+# the 3rd word (PID)
+unique_pids=( $(awk '/RegisterDexFile:/ && !/zygote/ {if(!a[$3]++) print $3}' \
+  "$LOGCAT_FILE") )
+echo "List of pids: ${unique_pids[@]}"
+if [[ $ALL_PIDS = false ]]; then
+  unique_pids=( ${unique_pids[-1]} )
 fi
 
-# Step 2 - Only output lines containing Dex File Start Addresses
-DEX_START=$OUT_DIR/dex_start
-if [ ! -f $DEX_START ] || [ $DO_REDO = true ]; then
-  DO_REDO=true
-  echo "Extracting Start of Dex File(s)"
-  grep "RegisterDexFile" $LOGCAT_FILE > $DEX_START
-else
-  echo "Skipped: Extracting Start of Dex File(s)"
-fi
+for pid in "${unique_pids[@]}"
+do
+  echo
+  echo "Current pid: $pid"
+  echo
+  pid_dir=$OUT_DIR/$pid
+  if [[ ! -d "$pid_dir" ]]; then
+    mkdir "$pid_dir"
+    DO_REDO[$pid]=true
+  fi
 
-# Step 3 - Clean Sanitizer output from Step 2 since logcat cannot
-# handle large amounts of output.
-ASAN_OUT_FILTERED=$OUT_DIR/asan_output_filtered
-if [ ! -f $ASAN_OUT_FILTERED ] || [ $DO_REDO = true ]; then
-  DO_REDO=true
-  echo "Filtering/Cleaning ASAN output"
-  python $ANDROID_BUILD_TOP/art/tools/runtime_memusage/prune_sanitizer_output.py \
-  $EXACT_ARG $MIN_ARG -d $OUT_DIR $ASAN_OUT
-else
-  echo "Skipped: Filtering/Cleaning ASAN output"
-fi
+  intermediates_dir=$pid_dir/intermediates
+  results_dir=$pid_dir/results
+  logcat_pid_file=$pid_dir/logcat
 
-# Step 4 - Retrieve symbolized stack traces from Step 3 output
-SYM_FILTERED=$OUT_DIR/sym_filtered
-if [ ! -f $SYM_FILTERED ] || [ $DO_REDO = true ]; then
-  DO_REDO=true
-  echo "Retrieving symbolized traces"
-  $ANDROID_BUILD_TOP/development/scripts/stack $ASAN_OUT_FILTERED > $SYM_FILTERED
-else
-  echo "Skipped: Retrieving symbolized traces"
-fi
+  if [[ ! -f "$logcat_pid_file" ]] || \
+     [[ "${DO_REDO[$pid]}" = true ]] || \
+     [[ $DO_REDO = true ]]; then
+    DO_REDO[$pid]=true
+    awk "{if(\$3 == $pid) print \$0}" "$LOGCAT_FILE" > "$logcat_pid_file"
+  fi
 
-# Step 5 - Using Steps 2, 3, 4 outputs in order to output graph data
-# and trace data
-# Only the category names are needed for the commands giving final output
-shift
-TIME_OUTPUT=($OUT_DIR/time_output_*.dat)
-if [ ! -e ${TIME_OUTPUT[0]} ] || [ $DO_REDO = true ]; then
-  DO_REDO=true
-  echo "Creating Categorized Time Table"
-  python $ANDROID_BUILD_TOP/art/tools/runtime_memusage/symbol_trace_info.py \
-    -d $OUT_DIR $ASAN_OUT_FILTERED $SYM_FILTERED $DEX_START $@
-else
-  echo "Skipped: Creating Categorized Time Table"
-fi
+  if [[ ! -d "$intermediates_dir" ]]; then
+    mkdir "$intermediates_dir"
+    DO_REDO[$pid]=true
+  fi
 
-# Step 6 - Use graph data from Step 5 to plot graph
-# Contains the category names used for legend of gnuplot
-PLOT_CATS=`echo \"Uncategorized $@\"`
-echo "Plotting Categorized Time Table"
-# Plots the information from logcat
-gnuplot --persist -e \
-  'filename(n) = sprintf("'"$OUT_DIR"'/time_output_%d.dat", n);
-   catnames = '"$PLOT_CATS"';
-   set title "Dex File Offset vs. Time accessed since App Start";
-   set xlabel "Time (milliseconds)";
-   set ylabel "Dex File Offset (bytes)";
-   plot for [i=0:'"$NUM_CAT"'] filename(i) using 1:2 title word(catnames, i + 1);'
+  # Step 1 - Only output lines related to Sanitizer
+  # Folder that holds all file output
+  asan_out=$intermediates_dir/asan_output
+  if [[ ! -f "$asan_out" ]] || \
+     [[ "${DO_REDO[$pid]}" = true ]] || \
+     [[ $DO_REDO = true ]]; then
+    DO_REDO[$pid]=true
+    echo "Extracting ASAN output"
+    grep "app_process64" "$logcat_pid_file" > "$asan_out"
+  else
+    echo "Skipped: Extracting ASAN output"
+  fi
 
-if [ $USE_TEMP = true ]; then
-  echo "Removing temp directory and files"
-  rm -rf $OUT_DIR
-fi
+  # Step 2 - Only output lines containing Dex File Start Addresses
+  dex_start=$intermediates_dir/dex_start
+  if [[ ! -f "$dex_start" ]] || \
+     [[ "${DO_REDO[$pid]}" = true ]] || \
+     [[ $DO_REDO = true ]]; then
+    DO_REDO[$pid]=true
+    echo "Extracting Start of Dex File(s)"
+    if [[ ! -z "$PACKAGE_NAME" ]]; then
+      awk '/RegisterDexFile:/ && /'"$PACKAGE_NAME"'/ && /\/data\/app/' \
+        "$logcat_pid_file" > "$dex_start"
+    else
+      grep "RegisterDexFile:" "$logcat_pid_file" > "$dex_start"
+    fi
+  else
+    echo "Skipped: Extracting Start of Dex File(s)"
+  fi
+
+  # Step 3 - Clean Sanitizer output from Step 2 since logcat cannot
+  # handle large amounts of output.
+  asan_out_filtered=$intermediates_dir/asan_output_filtered
+  if [[ ! -f "$asan_out_filtered" ]] || \
+     [[ "${DO_REDO[$pid]}" = true ]] || \
+     [[ $DO_REDO = true ]]; then
+    DO_REDO[$pid]=true
+    echo "Filtering/Cleaning ASAN output"
+    python "$ANDROID_BUILD_TOP"/art/tools/runtime_memusage/prune_sanitizer_output.py \
+      "$EXACT_ARG" "${MIN_ARG[@]}" -d "$intermediates_dir" "$asan_out"
+  else
+    echo "Skipped: Filtering/Cleaning ASAN output"
+  fi
+
+  # Step 4 - Retrieve symbolized stack traces from Step 3 output
+  sym_filtered=$intermediates_dir/sym_filtered
+  if [[ ! -f "$sym_filtered" ]] || \
+     [[ "${DO_REDO[$pid]}" = true ]] || \
+     [[ $DO_REDO = true ]]; then
+    DO_REDO[$pid]=true
+    echo "Retrieving symbolized traces"
+    "$ANDROID_BUILD_TOP"/development/scripts/stack "$asan_out_filtered" \
+      > "$sym_filtered"
+  else
+    echo "Skipped: Retrieving symbolized traces"
+  fi
+
+  # Step 4.5 - Obtain Dex File Format of dex file related to package
+  filtered_dex_start=$intermediates_dir/filtered_dex_start
+  baksmali_dmp_ctr=0
+  baksmali_dmp_prefix=$intermediates_dir"/baksmali_dex_file_"
+  baksmali_dmp_files=( $baksmali_dmp_prefix* )
+  baksmali_dmp_arg="--dex-file "${baksmali_dmp_files[$BAKSMALI_NUM]}
+  apk_dex_files=( )
+  if [[ ! -f "$baksmali_dmp_prefix""$BAKSMALI_NUM" ]] || \
+     [[ ! -f "$filtered_dex_start" ]] || \
+     [[ "${DO_REDO[$pid]}" = true ]] || \
+     [[ $DO_REDO = true ]]; then
+    if [[ ! -z "$PACKAGE_NAME" ]]; then
+      DO_REDO[$pid]=true
+      # Extracting Dex File path on device from Dex File related to package
+      apk_directory=$(dirname "$(tail -n1 "$dex_start" | awk "{print \$8}")")
+      for dex_file in $(awk "{print \$8}" "$dex_start"); do
+        apk_dex_files+=( $(basename "$dex_file") )
+      done
+      apk_oat_files=$(adb shell find "$apk_directory" -name "*.?dex" -type f \
+        2> /dev/null)
+      # Pulls the .odex and .vdex files associated with the package
+      for apk_file in $apk_oat_files; do
+        base_name=$(basename "$apk_file")
+        adb pull "$apk_file" "$intermediates_dir/base.${base_name#*.}"
+      done
+      oatdump --oat-file="$intermediates_dir"/base.odex \
+        --export-dex-to="$intermediates_dir" --output=/dev/null
+      for dex_file in "${apk_dex_files[@]}"; do
+        exported_dex_file=$intermediates_dir/$dex_file"_export.dex"
+        baksmali_dmp_out="$baksmali_dmp_prefix""$((baksmali_dmp_ctr++))"
+        baksmali -JXmx1024M dump "$exported_dex_file" \
+          > "$baksmali_dmp_out" 2> "$intermediates_dir"/error
+        if ! [[ -s "$baksmali_dmp_out" ]]; then
+          rm "$baksmali_dmp_prefix"*
+          baksmali_dmp_arg=""
+          echo "Failed to retrieve Dex File format"
+          break
+        fi
+      done
+      baksmali_dmp_files=( "$baksmali_dmp_prefix"* )
+      baksmali_dmp_arg="--dex-file "${baksmali_dmp_files[$BAKSMALI_NUM]}
+      # Gets the baksmali dump associated with BAKSMALI_NUM
+      awk "NR == $((BAKSMALI_NUM + 1))" "$dex_start" > "$filtered_dex_start"
+      results_dir=$results_dir"_"$BAKSMALI_NUM
+      echo "Skipped: Retrieving Dex File format from baksmali; no package given"
+    else
+      cp "$dex_start" "$filtered_dex_start"
+      baksmali_dmp_arg=""
+    fi
+  else
+    awk "NR == $((BAKSMALI_NUM + 1))" "$dex_start" > "$filtered_dex_start"
+    results_dir=$results_dir"_"$BAKSMALI_NUM
+    echo "Skipped: Retrieving Dex File format from baksmali"
+  fi
+
+  if [[ ! -d "$results_dir" ]]; then
+    mkdir "$results_dir"
+    DO_REDO[$pid]=true
+  fi
+
+  # Step 5 - Using Steps 2, 3, 4 outputs in order to output graph data
+  # and trace data
+  # Only the category names are needed for the commands giving final output
+  shift
+  time_output=($results_dir/time_output_*.dat)
+  if [[ ! -e ${time_output[0]} ]] || \
+     [[ "${DO_REDO[$pid]}" = true ]] || \
+     [[ $DO_REDO = true ]]; then
+    DO_REDO[$pid]=true
+    echo "Creating Categorized Time Table"
+    baksmali_dmp_args=( $baksmali_dmp_arg )
+    python "$ANDROID_BUILD_TOP"/art/tools/runtime_memusage/symbol_trace_info.py \
+      -d "$results_dir" "${OFFSET_ARGS[@]}" "${baksmali_dmp_args[@]}" \
+      "${TIME_ARGS[@]}" "$asan_out_filtered" "$sym_filtered" \
+      "$filtered_dex_start" "$@"
+  else
+    echo "Skipped: Creating Categorized Time Table"
+  fi
+
+  # Step 6 - Use graph data from Step 5 to plot graph
+  # Contains the category names used for legend of gnuplot
+  plot_cats="\"Uncategorized $*\""
+  package_string=""
+  dex_name=""
+  if [[ ! -z "$PACKAGE_NAME" ]]; then
+    package_string="Package name: $PACKAGE_NAME "
+  fi
+  if [[ ! -z "$baksmali_dmp_arg" ]]; then
+    dex_file_path="$(awk "{print \$8}" "$filtered_dex_start" | tail -n1)"
+    dex_name="Dex File name: $(basename "$dex_file_path") "
+  fi
+  echo "Plotting Categorized Time Table"
+  # Plots the information from logcat
+  gnuplot --persist -e \
+    'filename(n) = sprintf("'"$results_dir"'/time_output_%d.dat", n);
+     catnames = '"$plot_cats"';
+     set title "'"$package_string""$dex_name"'PID: '"$pid"'";
+     set xlabel "Time (milliseconds)";
+     set ylabel "Dex File Offset (bytes)";
+     plot for [i=0:'"$NUM_CAT"'] filename(i) using 1:2 title word(catnames, i + 1);'
+
+  if [[ $USE_TEMP = true ]]; then
+    echo "Removing temp directory and files"
+    rm -rf "$OUT_DIR"
+  fi
+done
diff --git a/tools/runtime_memusage/symbol_trace_info.py b/tools/runtime_memusage/symbol_trace_info.py
index e539be2..22f8ee9 100755
--- a/tools/runtime_memusage/symbol_trace_info.py
+++ b/tools/runtime_memusage/symbol_trace_info.py
@@ -25,7 +25,7 @@
 import argparse
 import bisect
 import os
-import sys
+import re
 
 
 def find_match(list_substrings, big_string):
@@ -36,12 +36,17 @@
     return list_substrings.index("Uncategorized")
 
 
-def absolute_to_relative(plot_list, dex_start_list, cat_list):
+def absolute_to_relative(data_lists, symbol_traces):
     """Address changed to Dex File offset and shifting time to 0 min in ms."""
+
+    offsets = data_lists["offsets"]
+    time_offsets = data_lists["times"]
+
+    # Format of time provided by logcat
     time_format_str = "%H:%M:%S.%f"
-    first_access_time = datetime.strptime(plot_list[0][0],
+    first_access_time = datetime.strptime(data_lists["plot_list"][0][0],
                                           time_format_str)
-    for ind, elem in enumerate(plot_list):
+    for ind, elem in enumerate(data_lists["plot_list"]):
         elem_date_time = datetime.strptime(elem[0], time_format_str)
         # Shift time values so that first access is at time 0 milliseconds
         elem[0] = int((elem_date_time - first_access_time).total_seconds() *
@@ -49,12 +54,23 @@
         address_access = int(elem[1], 16)
         # For each poisoned address, find highest Dex File starting address less
         # than address_access
-        dex_file_start = dex_start_list[bisect.bisect(dex_start_list,
-                                                      address_access) - 1
-                                        ]
-        elem.insert(1, address_access - dex_file_start)
-        # Category that a data point belongs to
-        elem.insert(2, cat_list[ind])
+        dex_start_list, dex_size_list = zip(*data_lists["dex_ends_list"])
+        dex_file_ind = bisect.bisect(dex_start_list, address_access) - 1
+        dex_offset = address_access - dex_start_list[dex_file_ind]
+        # Assumes that offsets is already sorted and constrains offset to be
+        # within range of the dex_file
+        max_offset = min(offsets[1], dex_size_list[dex_file_ind])
+        # Meant to nullify data that does not meet offset criteria if specified
+        if (dex_offset >= offsets[0] and dex_offset < max_offset and
+                elem[0] >= time_offsets[0] and elem[0] < time_offsets[1]):
+
+            elem.insert(1, dex_offset)
+            # Category that a data point belongs to
+            elem.insert(2, data_lists["cat_list"][ind])
+        else:
+            elem[:] = 4 * [None]
+            symbol_traces[ind] = None
+            data_lists["cat_list"][ind] = None
 
 
 def print_category_info(cat_split, outname, out_dir_name, title):
@@ -67,7 +83,7 @@
           str(len(trace_counts_list_ordered)))
     print("\tSum of trace counts: " +
           str(sum([trace[1] for trace in trace_counts_list_ordered])))
-    print("\n\tCount: How many traces appeared with count\n\t")
+    print("\n\tCount: How many traces appeared with count\n\t", end="")
     print(Counter([trace[1] for trace in trace_counts_list_ordered]))
     with open(os.path.join(out_dir_name, outname), "w") as output_file:
         for trace in trace_counts_list_ordered:
@@ -79,6 +95,8 @@
 
 def print_categories(categories, symbol_file_split, out_dir_name):
     """Prints details of all categories."""
+    symbol_file_split = [trace for trace in symbol_file_split
+                         if trace is not None]
     # Info of traces containing a call to current category
     for cat_num, cat_name in enumerate(categories[1:]):
         print("\nCategory #%d" % (cat_num + 1))
@@ -123,6 +141,26 @@
     parser.add_argument("-d", action="store",
                         default="", dest="out_dir_name", type=is_directory,
                         help="Output Directory")
+    parser.add_argument("--dex-file", action="store",
+                        default=None, dest="dex_file",
+                        type=argparse.FileType("r"),
+                        help="Baksmali Dex File Dump")
+    parser.add_argument("--offsets", action="store", nargs=2,
+                        default=[float(0), float("inf")],
+                        dest="offsets",
+                        metavar="OFFSET",
+                        type=float,
+                        help="Filters out accesses not between provided"
+                             " offsets if provided. Can provide 'inf'"
+                             " for infinity")
+    parser.add_argument("--times", action="store", nargs=2,
+                        default=[float(0), float("inf")],
+                        dest="times",
+                        metavar="TIME",
+                        type=float,
+                        help="Filters out accesses not between provided"
+                             " time offsets if provided. Can provide 'inf'"
+                             " for infinity")
     parser.add_argument("sanitizer_trace", action="store",
                         type=argparse.FileType("r"),
                         help="File containing sanitizer traces filtered by "
@@ -141,6 +179,14 @@
     return parser.parse_args(argv)
 
 
+def get_dex_offset_data(line, dex_file_item):
+    """ Returns a tuple of dex file offset, item name, and data of a line."""
+    return (int(line[:line.find(":")], 16),
+            (dex_file_item,
+             line.split("|")[1].strip())
+            )
+
+
 def read_data(parsed_argv):
     """Reads data from filepath arguments and parses them into lists."""
     # Using a dictionary to establish relation between lists added
@@ -149,22 +195,49 @@
     # Makes sure each trace maps to some category
     categories.insert(0, "Uncategorized")
 
+    data_lists["offsets"] = parsed_argv.offsets
+    data_lists["offsets"].sort()
+
+    data_lists["times"] = parsed_argv.times
+    data_lists["times"].sort()
+
     logcat_file_data = parsed_argv.sanitizer_trace.readlines()
     parsed_argv.sanitizer_trace.close()
 
-    symbol_file_split = parsed_argv.symbol_trace.read().split("Stack Trace")[
-        1:]
+    symbol_file_split = parsed_argv.symbol_trace.read().split("Stack Trace")
+    # Removes text before first trace
+    symbol_file_split = symbol_file_split[1:]
     parsed_argv.symbol_trace.close()
 
     dex_start_file_data = parsed_argv.dex_starts.readlines()
     parsed_argv.dex_starts.close()
 
+    if parsed_argv.dex_file is not None:
+        dex_file_data = parsed_argv.dex_file.read()
+        parsed_argv.dex_file.close()
+        # Splits baksmali dump by each item
+        item_split = [s.splitlines() for s in re.split(r"\|\[[0-9]+\] ",
+                                                       dex_file_data)]
+        # Splits each item by line and creates a list of offsets and a
+        # corresponding list of the data associated with that line
+        offset_list, offset_data = zip(*[get_dex_offset_data(line, item[0])
+                                         for item in item_split
+                                         for line in item[1:]
+                                         if re.search("[0-9a-f]{6}:", line)
+                                         is not None and
+                                         line.find("|") != -1])
+        data_lists["offset_list"] = offset_list
+        data_lists["offset_data"] = offset_data
+    else:
+        dex_file_data = None
+
     # Each element is a tuple of time and address accessed
     data_lists["plot_list"] = [[elem[1] for elem in enumerate(line.split())
                                 if elem[0] in (1, 11)
                                 ]
                                for line in logcat_file_data
-                               if "use-after-poison" in line
+                               if "use-after-poison" in line or
+                               "unknown-crash" in line
                                ]
     # Contains a mapping between traces and the category they belong to
     # based on arguments
@@ -173,34 +246,35 @@
 
     # Contains a list of starting address of all dex files to calculate dex
     # offsets
-    data_lists["dex_start_list"] = [int(line.split("@")[1], 16)
-                                    for line in dex_start_file_data
-                                    if "RegisterDexFile" in line
-                                    ]
+    data_lists["dex_ends_list"] = [(int(line.split()[9], 16),
+                                    int(line.split()[12])
+                                    )
+                                   for line in dex_start_file_data
+                                   if "RegisterDexFile" in line
+                                   ]
     # Dex File Starting addresses must be sorted because bisect requires sorted
     # lists.
-    data_lists["dex_start_list"].sort()
+    data_lists["dex_ends_list"].sort()
 
     return data_lists, categories, symbol_file_split
 
 
-def main(argv=None):
+def main():
     """Takes in trace information and outputs details about them."""
-    if argv is None:
-        argv = sys.argv
-    parsed_argv = parse_args(argv[1:])
-
+    parsed_argv = parse_args(None)
     data_lists, categories, symbol_file_split = read_data(parsed_argv)
+
     # Formats plot_list such that each element is a data point
-    absolute_to_relative(data_lists["plot_list"], data_lists["dex_start_list"],
-                         data_lists["cat_list"])
+    absolute_to_relative(data_lists, symbol_file_split)
     for file_ext, cat_name in enumerate(categories):
         out_file_name = os.path.join(parsed_argv.out_dir_name, "time_output_" +
                                      str(file_ext) +
                                      ".dat")
         with open(out_file_name, "w") as output_file:
             output_file.write("# Category: " + cat_name + "\n")
-            output_file.write("# Time, Dex File Offset, Address \n")
+            output_file.write("# Time, Dex File Offset_10, Dex File Offset_16,"
+                              " Address, Item Accessed, Item Member Accessed"
+                              " Unaligned\n")
             for time, dex_offset, category, address in data_lists["plot_list"]:
                 if category == cat_name:
                     output_file.write(
@@ -208,9 +282,23 @@
                         " " +
                         str(dex_offset) +
                         " #" +
-                        str(address) +
-                        "\n")
-
+                        hex(dex_offset) +
+                        " " +
+                        str(address))
+                    if "offset_list" in data_lists:
+                        dex_offset_index = bisect.bisect(
+                            data_lists["offset_list"],
+                            dex_offset) - 1
+                        aligned_dex_offset = (data_lists["offset_list"]
+                                                        [dex_offset_index])
+                        dex_offset_data = (data_lists["offset_data"]
+                                                     [dex_offset_index])
+                        output_file.write(
+                            " " +
+                            "|".join(dex_offset_data) +
+                            " " +
+                            str(aligned_dex_offset != dex_offset))
+                    output_file.write("\n")
     print_categories(categories, symbol_file_split, parsed_argv.out_dir_name)
 
 
diff --git a/tools/test_presubmit.py b/tools/test_presubmit.py
new file mode 100755
index 0000000..f6e6df9
--- /dev/null
+++ b/tools/test_presubmit.py
@@ -0,0 +1,159 @@
+#!/usr/bin/python3
+#
+# Copyright 2017, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# There are many run-tests which generate their sources automatically.
+# It is desirable to keep the checked-in source code, as we re-run generators very rarely.
+#
+# This script will re-run the generators only if their dependent files have changed and then
+# complain if the outputs no longer matched what's in the source tree.
+#
+
+import os
+import pathlib
+import subprocess
+import sys
+import tempfile
+
+THIS_PATH = os.path.dirname(os.path.realpath(__file__))
+
+TOOLS_GEN_SRCS = [
+    # tool -> path to a script to generate a file
+    # reference_files -> list of files that the script can generate
+    # args -> lambda(path) that generates arguments the 'tool' in order to output to 'path'
+    # interesting_files -> which files much change in order to re-run the tool.
+    # interesting_to_reference_files: lambda(x,reference_files)
+    #                                 given the interesting file 'x' and a list of reference_files,
+    #                                 return exactly one reference file that corresponds to it.
+    { 'tool' : 'test/988-method-trace/gen_srcs.py',
+      'reference_files' : ['test/988-method-trace/src/art/Test988Intrinsics.java'],
+      'args' : lambda output_path: [output_path],
+      'interesting_files' : ['compiler/intrinsics_list.h'],
+      'interesting_to_reference_file' : lambda interesting, references: references[0],
+    },
+]
+
+DEBUG = False
+
+def debug_print(msg):
+  if DEBUG:
+    print("[DEBUG]: " + msg, file=sys.stderr)
+
+def is_interesting(f, tool_dict):
+  """
+  Returns true if this is a file we want to run this tool before uploading. False otherwise.
+  """
+  path = pathlib.Path(f)
+  return str(path) in tool_dict['interesting_files']
+
+def get_changed_files(commit):
+  """
+  Gets the files changed in the given commit.
+  """
+  return subprocess.check_output(
+      ["git", 'diff-tree', '--no-commit-id', '--name-only', '-r', commit],
+      stderr=subprocess.STDOUT,
+      universal_newlines=True).split()
+
+def command_line_for_tool(tool_dict, output):
+  """
+  Calculate the command line for this tool when ran against the output file 'output'.
+  """
+  proc_args = [tool_dict['tool']] + tool_dict['args'](output)
+  return proc_args
+
+def run_tool(tool_dict, output):
+  """
+  Execute this tool by passing the tool args to the tool.
+  """
+  proc_args = command_line_for_tool(tool_dict, output)
+  debug_print("PROC_ARGS: %s" %(proc_args))
+  succ = subprocess.call(proc_args)
+  return succ
+
+def get_reference_file(changed_file, tool_dict):
+   """
+   Lookup the file that the tool is generating in response to changing an interesting file
+   """
+   return tool_dict['interesting_to_reference_file'](changed_file, tool_dict['reference_files'])
+
+def run_diff(changed_file, tool_dict, original_file):
+  ref_file = get_reference_file(changed_file, tool_dict)
+
+  return subprocess.call(["diff", ref_file, original_file]) != 0
+
+def run_gen_srcs(files):
+  """
+  Runs test tools only for interesting files that were changed in this commit.
+  """
+  if len(files) == 0:
+    return
+
+  success = 0  # exit code 0 = success, >0 error.
+  had_diffs = False
+
+  for tool_dict in TOOLS_GEN_SRCS:
+    tool_ran_at_least_once = False
+    for f in files:
+      if is_interesting(f, tool_dict):
+        tmp_file = tempfile.mktemp()
+        reference_file = get_reference_file(f, tool_dict)
+
+        # Generate the source code with a temporary file as the output.
+        success = run_tool(tool_dict, tmp_file)
+        if success != 0:
+          # Immediately abort if the tool fails with a non-0 exit code, do not go any further.
+          print("[FATAL] Error when running tool (return code %s)" %(success), file=sys.stderr)
+          print("$> %s" %(" ".join(command_line_for_tool(tool_dict, tmp_file))), file=sys.stderr)
+          sys.exit(success)
+        if run_diff(f, tool_dict, tmp_file):
+          # If the tool succeeded, but there was a diff, then the generated code has diverged.
+          # Output the diff information and continue to the next files/tools.
+          had_diffs = True
+          print("-----------------------------------------------------------", file=sys.stderr)
+          print("File '%s' diverged from generated file; please re-run tools:" %(reference_file), file=sys.stderr)
+          print("$> %s" %(" ".join(command_line_for_tool(tool_dict, reference_file))), file=sys.stderr)
+        else:
+          debug_print("File %s is consistent with tool %s" %(reference_file, tool_dict['tool']))
+
+        tool_ran_at_least_once = True
+
+    if not tool_ran_at_least_once:
+      debug_print("Interesting files %s unchanged, skipping tool '%s'" %(tool_dict['interesting_files'], tool_dict['tool']))
+
+  if had_diffs:
+    success = 1
+  # Always return non-0 exit code when there were diffs so that the presubmit hooks are FAILED.
+
+  return success
+
+
+def main():
+  if 'PREUPLOAD_COMMIT' in os.environ:
+    commit = os.environ['PREUPLOAD_COMMIT']
+  else:
+    print("WARNING: Not running as a pre-upload hook. Assuming commit to check = 'HEAD'", file=sys.stderr)
+    commit = "HEAD"
+
+  os.chdir(os.path.join(THIS_PATH, '..')) # run tool relative to 'art' directory
+  debug_print("CWD: %s" %(os.getcwd()))
+
+  changed_files = get_changed_files(commit)
+  debug_print("Changed files: %s" %(changed_files))
+  return run_gen_srcs(changed_files)
+
+if __name__ == '__main__':
+  sys.exit(main())