Merge "Visit image roots for hprof"
diff --git a/Android.mk b/Android.mk
index 216e865..9360355 100644
--- a/Android.mk
+++ b/Android.mk
@@ -42,27 +42,7 @@
 
 .PHONY: clean-oat-host
 clean-oat-host:
-	rm -f $(HOST_CORE_IMG_OUTS)
-	rm -f $(HOST_CORE_OAT_OUTS)
-	rm -f $(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/*.odex
-ifneq ($(HOST_PREFER_32_BIT),true)
-	rm -f $(HOST_OUT_JAVA_LIBRARIES)/$(2ND_ART_HOST_ARCH)/*.odex
-endif
-	rm -f $(TARGET_CORE_IMG_OUTS)
-	rm -f $(TARGET_CORE_OAT_OUTS)
-	rm -rf $(DEXPREOPT_PRODUCT_DIR_FULL_PATH)
-	rm -f $(TARGET_OUT_UNSTRIPPED)/system/framework/*.odex
-	rm -f $(TARGET_OUT_UNSTRIPPED)/system/framework/*/*.oat
-	rm -f $(TARGET_OUT_UNSTRIPPED)/system/framework/*/*.art
-	rm -f $(TARGET_OUT)/framework/*/*.oat
-	rm -f $(TARGET_OUT)/framework/*/*.art
-	rm -f $(TARGET_OUT_APPS)/*.odex
-	rm -f $(TARGET_OUT_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/javalib.odex
-	rm -f $(TARGET_OUT_INTERMEDIATES)/APPS/*_intermediates/*.odex
-ifdef TARGET_2ND_ARCH
-	rm -f $(2ND_TARGET_OUT_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/javalib.odex
-	rm -f $(2ND_TARGET_OUT_INTERMEDIATES)/APPS/*_intermediates/*.odex
-endif
+	find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" | xargs rm -f
 ifneq ($(TMPDIR),)
 	rm -rf $(TMPDIR)/$(USER)/test-*/dalvik-cache/*
 	rm -rf $(TMPDIR)/android-data/dalvik-cache/*
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c5669c0..1a4c30c 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -188,6 +188,7 @@
   compiler/dex/local_value_numbering_test.cc \
   compiler/dex/mir_graph_test.cc \
   compiler/dex/mir_optimization_test.cc \
+  compiler/dwarf/dwarf_test.cc \
   compiler/driver/compiler_driver_test.cc \
   compiler/elf_writer_test.cc \
   compiler/image_test.cc \
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 1d0aad5..96d90bb 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -24,6 +24,7 @@
 #include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "dex/verification_results.h"
 #include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
 #include "interpreter/interpreter.h"
 #include "mirror/art_method.h"
 #include "mirror/dex_cache.h"
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index d6a07f6..2386914 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -185,6 +185,7 @@
   kLinkerPatchCall,
   kLinkerPatchCallRelative,  // NOTE: Actual patching is instruction_set-dependent.
   kLinkerPatchType,
+  kLinkerPatchDexCacheArray,  // NOTE: Actual patching is instruction_set-dependent.
 };
 
 class LinkerPatch {
@@ -192,28 +193,44 @@
   static LinkerPatch MethodPatch(size_t literal_offset,
                                  const DexFile* target_dex_file,
                                  uint32_t target_method_idx) {
-    return LinkerPatch(literal_offset, kLinkerPatchMethod,
-                       target_method_idx, target_dex_file);
+    LinkerPatch patch(literal_offset, kLinkerPatchMethod, target_dex_file);
+    patch.method_idx_ = target_method_idx;
+    return patch;
   }
 
   static LinkerPatch CodePatch(size_t literal_offset,
                                const DexFile* target_dex_file,
                                uint32_t target_method_idx) {
-    return LinkerPatch(literal_offset, kLinkerPatchCall,
-                       target_method_idx, target_dex_file);
+    LinkerPatch patch(literal_offset, kLinkerPatchCall, target_dex_file);
+    patch.method_idx_ = target_method_idx;
+    return patch;
   }
 
   static LinkerPatch RelativeCodePatch(size_t literal_offset,
                                        const DexFile* target_dex_file,
                                        uint32_t target_method_idx) {
-    return LinkerPatch(literal_offset, kLinkerPatchCallRelative,
-                       target_method_idx, target_dex_file);
+    LinkerPatch patch(literal_offset, kLinkerPatchCallRelative, target_dex_file);
+    patch.method_idx_ = target_method_idx;
+    return patch;
   }
 
   static LinkerPatch TypePatch(size_t literal_offset,
                                const DexFile* target_dex_file,
                                uint32_t target_type_idx) {
-    return LinkerPatch(literal_offset, kLinkerPatchType, target_type_idx, target_dex_file);
+    LinkerPatch patch(literal_offset, kLinkerPatchType, target_dex_file);
+    patch.type_idx_ = target_type_idx;
+    return patch;
+  }
+
+  static LinkerPatch DexCacheArrayPatch(size_t literal_offset,
+                                        const DexFile* target_dex_file,
+                                        uint32_t pc_insn_offset,
+                                        size_t element_offset) {
+    DCHECK(IsUint<32>(element_offset));
+    LinkerPatch patch(literal_offset, kLinkerPatchDexCacheArray, target_dex_file);
+    patch.pc_insn_offset_ = pc_insn_offset;
+    patch.element_offset_ = element_offset;
+    return patch;
   }
 
   LinkerPatch(const LinkerPatch& other) = default;
@@ -227,10 +244,14 @@
     return patch_type_;
   }
 
+  bool IsPcRelative() const {
+    return Type() == kLinkerPatchCallRelative || Type() == kLinkerPatchDexCacheArray;
+  }
+
   MethodReference TargetMethod() const {
     DCHECK(patch_type_ == kLinkerPatchMethod ||
            patch_type_ == kLinkerPatchCall || patch_type_ == kLinkerPatchCallRelative);
-    return MethodReference(target_dex_file_, target_idx_);
+    return MethodReference(target_dex_file_, method_idx_);
   }
 
   const DexFile* TargetTypeDexFile() const {
@@ -240,22 +261,52 @@
 
   uint32_t TargetTypeIndex() const {
     DCHECK(patch_type_ == kLinkerPatchType);
-    return target_idx_;
+    return type_idx_;
+  }
+
+  const DexFile* TargetDexCacheDexFile() const {
+    DCHECK(patch_type_ == kLinkerPatchDexCacheArray);
+    return target_dex_file_;
+  }
+
+  size_t TargetDexCacheElementOffset() const {
+    DCHECK(patch_type_ == kLinkerPatchDexCacheArray);
+    return element_offset_;
+  }
+
+  uint32_t PcInsnOffset() const {
+    DCHECK(patch_type_ == kLinkerPatchDexCacheArray);
+    return pc_insn_offset_;
   }
 
  private:
-  LinkerPatch(size_t literal_offset, LinkerPatchType patch_type,
-              uint32_t target_idx, const DexFile* target_dex_file)
-      : literal_offset_(literal_offset),
-        patch_type_(patch_type),
-        target_idx_(target_idx),
-        target_dex_file_(target_dex_file) {
+  LinkerPatch(size_t literal_offset, LinkerPatchType patch_type, const DexFile* target_dex_file)
+      : target_dex_file_(target_dex_file),
+        literal_offset_(literal_offset),
+        patch_type_(patch_type) {
+    cmp1_ = 0u;
+    cmp2_ = 0u;
+    // The compiler rejects methods that are too big, so the compiled code
+    // of a single method really shouln't be anywhere close to 16MiB.
+    DCHECK(IsUint<24>(literal_offset));
   }
 
-  size_t literal_offset_;
-  LinkerPatchType patch_type_;
-  uint32_t target_idx_;  // Method index (Call/Method patches) or type index (Type patches).
   const DexFile* target_dex_file_;
+  uint32_t literal_offset_ : 24;  // Method code size up to 16MiB.
+  LinkerPatchType patch_type_ : 8;
+  union {
+    uint32_t cmp1_;             // Used for relational operators.
+    uint32_t method_idx_;       // Method index for Call/Method patches.
+    uint32_t type_idx_;         // Type index for Type patches.
+    uint32_t element_offset_;   // Element offset in the dex cache arrays.
+  };
+  union {
+    uint32_t cmp2_;             // Used for relational operators.
+    // Literal offset of the insn loading PC (same as literal_offset if it's the same insn,
+    // may be different if the PC-relative addressing needs multiple insns).
+    uint32_t pc_insn_offset_;
+    static_assert(sizeof(pc_insn_offset_) == sizeof(cmp2_), "needed by relational operators");
+  };
 
   friend bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs);
   friend bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs);
@@ -264,15 +315,17 @@
 inline bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs) {
   return lhs.literal_offset_ == rhs.literal_offset_ &&
       lhs.patch_type_ == rhs.patch_type_ &&
-      lhs.target_idx_ == rhs.target_idx_ &&
-      lhs.target_dex_file_ == rhs.target_dex_file_;
+      lhs.target_dex_file_ == rhs.target_dex_file_ &&
+      lhs.cmp1_ == rhs.cmp1_ &&
+      lhs.cmp2_ == rhs.cmp2_;
 }
 
 inline bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs) {
   return (lhs.literal_offset_ != rhs.literal_offset_) ? lhs.literal_offset_ < rhs.literal_offset_
       : (lhs.patch_type_ != rhs.patch_type_) ? lhs.patch_type_ < rhs.patch_type_
-      : (lhs.target_idx_ != rhs.target_idx_) ? lhs.target_idx_ < rhs.target_idx_
-      : lhs.target_dex_file_ < rhs.target_dex_file_;
+      : (lhs.target_dex_file_ != rhs.target_dex_file_) ? lhs.target_dex_file_ < rhs.target_dex_file_
+      : (lhs.cmp1_ != rhs.cmp1_) ? lhs.cmp1_ < rhs.cmp1_
+      : lhs.cmp2_ < rhs.cmp2_;
 }
 
 class CompiledMethod FINAL : public CompiledCode {
diff --git a/compiler/dex/mir_field_info.cc b/compiler/dex/mir_field_info.cc
index d2079a2..a9ab3bb 100644
--- a/compiler/dex/mir_field_info.cc
+++ b/compiler/dex/mir_field_info.cc
@@ -19,6 +19,7 @@
 #include <string.h>
 
 #include "base/logging.h"
+#include "dex/verified_method.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_driver-inl.h"
 #include "mirror/class_loader.h"  // Only to allow casts in Handle<ClassLoader>.
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index 3103f96..58f12c9 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -688,7 +688,7 @@
 
 /* Parse a Dex method and insert it into the MIRGraph at the current insert point. */
 void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_flags,
-                           InvokeType invoke_type, uint16_t class_def_idx,
+                           InvokeType invoke_type ATTRIBUTE_UNUSED, uint16_t class_def_idx,
                            uint32_t method_idx, jobject class_loader, const DexFile& dex_file) {
   current_code_item_ = code_item;
   method_stack_.push_back(std::make_pair(current_method_, current_offset_));
@@ -726,13 +726,6 @@
     null_block->hidden = true;
     entry_block_ = CreateNewBB(kEntryBlock);
     exit_block_ = CreateNewBB(kExitBlock);
-    // TODO: deprecate all "cu->" fields; move what's left to wherever CompilationUnit is allocated.
-    cu_->dex_file = &dex_file;
-    cu_->class_def_idx = class_def_idx;
-    cu_->method_idx = method_idx;
-    cu_->access_flags = access_flags;
-    cu_->invoke_type = invoke_type;
-    cu_->shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx));
   } else {
     UNIMPLEMENTED(FATAL) << "Nested inlining not implemented.";
     /*
diff --git a/compiler/dex/mir_method_info.cc b/compiler/dex/mir_method_info.cc
index 34fb1bf..831ad42 100644
--- a/compiler/dex/mir_method_info.cc
+++ b/compiler/dex/mir_method_info.cc
@@ -16,9 +16,11 @@
 
 # include "mir_method_info.h"
 
+#include "dex/verified_method.h"
 #include "driver/compiler_driver.h"
 #include "driver/dex_compilation_unit.h"
 #include "driver/compiler_driver-inl.h"
+#include "driver/compiler_options.h"
 #include "mirror/class_loader.h"  // Only to allow casts in Handle<ClassLoader>.
 #include "mirror/dex_cache.h"     // Only to allow casts in Handle<DexCache>.
 #include "scoped_thread_state_change.h"
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index d46c25a..3081c9e 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -23,6 +23,7 @@
 #include "dex/mir_graph.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
 #include "gc/accounting/card_table.h"
 #include "mirror/art_method.h"
 #include "mirror/object_array-inl.h"
diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h
index d15412a..f6fa938 100644
--- a/compiler/dex/quick/arm64/arm64_lir.h
+++ b/compiler/dex/quick/arm64/arm64_lir.h
@@ -236,6 +236,7 @@
   kA64Add4rrro,      // add [00001011000] rm[20-16] imm_6[15-10] rn[9-5] rd[4-0].
   kA64Add4RRre,      // add [00001011001] rm[20-16] option[15-13] imm_3[12-10] rn[9-5] rd[4-0].
   kA64Adr2xd,        // adr [0] immlo[30-29] [10000] immhi[23-5] rd[4-0].
+  kA64Adrp2xd,       // adrp [1] immlo[30-29] [10000] immhi[23-5] rd[4-0].
   kA64And3Rrl,       // and [00010010] N[22] imm_r[21-16] imm_s[15-10] rn[9-5] rd[4-0].
   kA64And4rrro,      // and [00001010] shift[23-22] [N=0] rm[20-16] imm_6[15-10] rn[9-5] rd[4-0].
   kA64Asr3rrd,       // asr [0001001100] immr[21-16] imms[15-10] rn[9-5] rd[4-0].
diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc
index 329bb1e..a59deb5 100644
--- a/compiler/dex/quick/arm64/assemble_arm64.cc
+++ b/compiler/dex/quick/arm64/assemble_arm64.cc
@@ -131,6 +131,10 @@
                  kFmtRegX, 4, 0, kFmtImm21, -1, -1, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | NEEDS_FIXUP,
                  "adr", "!0x, #!1d", kFixupAdr),
+    ENCODING_MAP(kA64Adrp2xd, NO_VARIANTS(0x90000000),
+                 kFmtRegX, 4, 0, kFmtImm21, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | NEEDS_FIXUP,
+                 "adrp", "!0x, #!1d", kFixupLabel),
     ENCODING_MAP(WIDE(kA64And3Rrl), SF_VARIANTS(0x12000000),
                  kFmtRegROrSp, 4, 0, kFmtRegR, 9, 5, kFmtBitBlt, 22, 10,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index 823cb60..3316945 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -23,10 +23,12 @@
 #include "dex/mir_graph.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
 #include "gc/accounting/card_table.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "mirror/art_method.h"
 #include "mirror/object_array-inl.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
 
 namespace art {
 
@@ -438,13 +440,13 @@
  * Bit of a hack here - in the absence of a real scheduling pass,
  * emit the next instruction in static & direct invoke sequences.
  */
-static int Arm64NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
-                               int state, const MethodReference& target_method,
-                               uint32_t unused_idx,
-                               uintptr_t direct_code, uintptr_t direct_method,
-                               InvokeType type) {
+int Arm64Mir2Lir::Arm64NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
+                                      int state, const MethodReference& target_method,
+                                      uint32_t unused_idx,
+                                      uintptr_t direct_code, uintptr_t direct_method,
+                                      InvokeType type) {
   UNUSED(info, unused_idx);
-  Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+  Arm64Mir2Lir* cg = static_cast<Arm64Mir2Lir*>(cu->cg.get());
   if (direct_code != 0 && direct_method != 0) {
     switch (state) {
     case 0:  // Get the current Method* [sets kArg0]
@@ -465,17 +467,24 @@
       return -1;
     }
   } else {
+    bool use_pc_rel = cg->CanUseOpPcRelDexCacheArrayLoad();
     RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
     switch (state) {
     case 0:  // Get the current Method* [sets kArg0]
       // TUNING: we can save a reg copy if Method* has been promoted.
-      cg->LoadCurrMethodDirect(arg0_ref);
-      break;
+      if (!use_pc_rel) {
+        cg->LoadCurrMethodDirect(arg0_ref);
+        break;
+      }
+      ++state;
+      FALLTHROUGH_INTENDED;
     case 1:  // Get method->dex_cache_resolved_methods_
-      cg->LoadRefDisp(arg0_ref,
-                      mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
-                      arg0_ref,
-                      kNotVolatile);
+      if (!use_pc_rel) {
+        cg->LoadRefDisp(arg0_ref,
+                        mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
+                        arg0_ref,
+                        kNotVolatile);
+      }
       // Set up direct code if known.
       if (direct_code != 0) {
         if (direct_code != static_cast<uintptr_t>(-1)) {
@@ -487,14 +496,23 @@
           cg->LoadCodeAddress(target_method, type, kInvokeTgt);
         }
       }
-      break;
+      if (!use_pc_rel || direct_code != 0) {
+        break;
+      }
+      ++state;
+      FALLTHROUGH_INTENDED;
     case 2:  // Grab target method*
       CHECK_EQ(cu->dex_file, target_method.dex_file);
-      cg->LoadRefDisp(arg0_ref,
-                      mirror::ObjectArray<mirror::Object>::OffsetOfElement(
-                          target_method.dex_method_index).Int32Value(),
-                      arg0_ref,
-                      kNotVolatile);
+      if (!use_pc_rel) {
+        cg->LoadRefDisp(arg0_ref,
+                        mirror::ObjectArray<mirror::Object>::OffsetOfElement(
+                            target_method.dex_method_index).Int32Value(),
+                        arg0_ref,
+                        kNotVolatile);
+      } else {
+        size_t offset = cg->dex_cache_arrays_layout_.MethodOffset(target_method.dex_method_index);
+        cg->OpPcRelDexCacheArrayLoad(cu->dex_file, offset, arg0_ref);
+      }
       break;
     case 3:  // Grab the code from the method*
       if (direct_code == 0) {
diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h
index 54fd46d..8184f02 100644
--- a/compiler/dex/quick/arm64/codegen_arm64.h
+++ b/compiler/dex/quick/arm64/codegen_arm64.h
@@ -78,6 +78,9 @@
   /// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage)
   void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE;
 
+  bool CanUseOpPcRelDexCacheArrayLoad() const OVERRIDE;
+  void OpPcRelDexCacheArrayLoad(const DexFile* dex_file, int offset, RegStorage r_dest) OVERRIDE;
+
   LIR* OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg,
                          int offset, int check_value, LIR* target, LIR** compare) OVERRIDE;
 
@@ -393,9 +396,16 @@
   void GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
                      RegLocation rl_src2, bool is_div, int flags);
 
+  static int Arm64NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
+                                 int state, const MethodReference& target_method,
+                                 uint32_t unused_idx,
+                                 uintptr_t direct_code, uintptr_t direct_method,
+                                 InvokeType type);
+
   static const A64EncodingMap EncodingMap[kA64Last];
 
   ArenaVector<LIR*> call_method_insns_;
+  ArenaVector<LIR*> dex_cache_access_insns_;
 
   int GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) OVERRIDE;
 };
diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc
index 2372ccc..a9d9f3d 100644
--- a/compiler/dex/quick/arm64/int_arm64.cc
+++ b/compiler/dex/quick/arm64/int_arm64.cc
@@ -943,6 +943,23 @@
   lir->target = target;
 }
 
+bool Arm64Mir2Lir::CanUseOpPcRelDexCacheArrayLoad() const {
+  return dex_cache_arrays_layout_.Valid();
+}
+
+void Arm64Mir2Lir::OpPcRelDexCacheArrayLoad(const DexFile* dex_file, int offset,
+                                            RegStorage r_dest) {
+  LIR* adrp = NewLIR2(kA64Adrp2xd, r_dest.GetReg(), 0);
+  adrp->operands[2] = WrapPointer(dex_file);
+  adrp->operands[3] = offset;
+  adrp->operands[4] = WrapPointer(adrp);
+  dex_cache_access_insns_.push_back(adrp);
+  LIR* ldr = LoadBaseDisp(r_dest, 0, r_dest, kReference, kNotVolatile);
+  ldr->operands[4] = adrp->operands[4];
+  ldr->flags.fixup = kFixupLabel;
+  dex_cache_access_insns_.push_back(ldr);
+}
+
 LIR* Arm64Mir2Lir::OpVldm(RegStorage r_base, int count) {
   UNUSED(r_base, count);
   LOG(FATAL) << "Unexpected use of OpVldm for Arm64";
diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc
index 09a34bf..c5c0dc5 100644
--- a/compiler/dex/quick/arm64/target_arm64.cc
+++ b/compiler/dex/quick/arm64/target_arm64.cc
@@ -606,7 +606,8 @@
 
 Arm64Mir2Lir::Arm64Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
     : Mir2Lir(cu, mir_graph, arena),
-      call_method_insns_(arena->Adapter()) {
+      call_method_insns_(arena->Adapter()),
+      dex_cache_access_insns_(arena->Adapter()) {
   // Sanity check - make sure encoding map lines up.
   for (int i = 0; i < kA64Last; i++) {
     DCHECK_EQ(UNWIDE(Arm64Mir2Lir::EncodingMap[i].opcode), i)
@@ -846,8 +847,9 @@
 }
 
 void Arm64Mir2Lir::InstallLiteralPools() {
+  patches_.reserve(call_method_insns_.size() + dex_cache_access_insns_.size());
+
   // PC-relative calls to methods.
-  patches_.reserve(call_method_insns_.size());
   for (LIR* p : call_method_insns_) {
       DCHECK_EQ(p->opcode, kA64Bl1t);
       uint32_t target_method_idx = p->operands[1];
@@ -856,6 +858,18 @@
                                                         target_dex_file, target_method_idx));
   }
 
+  // PC-relative references to dex cache arrays.
+  for (LIR* p : dex_cache_access_insns_) {
+    DCHECK(p->opcode == kA64Adrp2xd || p->opcode == kA64Ldr3rXD);
+    const LIR* adrp = UnwrapPointer<LIR>(p->operands[4]);
+    DCHECK_EQ(adrp->opcode, kA64Adrp2xd);
+    const DexFile* dex_file = UnwrapPointer<DexFile>(adrp->operands[2]);
+    uint32_t offset = adrp->operands[3];
+    DCHECK(!p->flags.is_nop);
+    DCHECK(!adrp->flags.is_nop);
+    patches_.push_back(LinkerPatch::DexCacheArrayPatch(p->offset, dex_file, adrp->offset, offset));
+  }
+
   // And do the normal processing.
   Mir2Lir::InstallLiteralPools();
 }
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index df72830..509d448 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -29,6 +29,7 @@
 #include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "dex/verification_results.h"
 #include "dex/verified_method.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
 #include "verifier/dex_gc_map.h"
 #include "verifier/method_verifier.h"
 #include "vmap_table.h"
@@ -1053,6 +1054,7 @@
       mem_ref_type_(ResourceMask::kHeapRef),
       mask_cache_(arena),
       safepoints_(arena->Adapter()),
+      dex_cache_arrays_layout_(cu->compiler_driver->GetDexCacheArraysLayout(cu->dex_file)),
       in_to_reg_storage_mapping_(arena) {
   switch_tables_.reserve(4);
   fill_array_data_.reserve(4);
@@ -1304,6 +1306,17 @@
   OpPcRelLoad(TargetReg(symbolic_reg, kRef), data_target);
 }
 
+bool Mir2Lir::CanUseOpPcRelDexCacheArrayLoad() const {
+  return false;
+}
+
+void Mir2Lir::OpPcRelDexCacheArrayLoad(const DexFile* dex_file ATTRIBUTE_UNUSED,
+                                       int offset ATTRIBUTE_UNUSED,
+                                       RegStorage r_dest ATTRIBUTE_UNUSED) {
+  LOG(FATAL) << "No generic implementation.";
+  UNREACHABLE();
+}
+
 std::vector<uint8_t>* Mir2Lir::ReturnFrameDescriptionEntry() {
   // Default case is to do nothing.
   return nullptr;
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index 2bcaaca..1813e09 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -24,12 +24,14 @@
 #include "dex/mir_graph.h"
 #include "dex/quick/arm/arm_lir.h"
 #include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "mirror/array.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_reference.h"
 #include "utils.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
 #include "verifier/method_verifier.h"
 
 namespace art {
@@ -56,6 +58,42 @@
   return (cu->enable_debug & (1 << kDebugSlowTypePath)) != 0;
 }
 
+void Mir2Lir::GenIfNullUseHelperImmMethod(
+    RegStorage r_result, QuickEntrypointEnum trampoline, int imm, RegStorage r_method) {
+  class CallHelperImmMethodSlowPath : public LIRSlowPath {
+   public:
+    CallHelperImmMethodSlowPath(Mir2Lir* m2l, LIR* fromfast, LIR* cont,
+                                QuickEntrypointEnum trampoline_in, int imm_in,
+                                RegStorage r_method_in, RegStorage r_result_in)
+        : LIRSlowPath(m2l, fromfast, cont), trampoline_(trampoline_in),
+          imm_(imm_in), r_method_(r_method_in), r_result_(r_result_in) {
+    }
+
+    void Compile() {
+      GenerateTargetLabel();
+      if (r_method_.Valid()) {
+        m2l_->CallRuntimeHelperImmReg(trampoline_, imm_, r_method_, true);
+      } else {
+        m2l_->CallRuntimeHelperImmMethod(trampoline_, imm_, true);
+      }
+      m2l_->OpRegCopy(r_result_,  m2l_->TargetReg(kRet0, kRef));
+      m2l_->OpUnconditionalBranch(cont_);
+    }
+
+   private:
+    QuickEntrypointEnum trampoline_;
+    const int imm_;
+    const RegStorage r_method_;
+    const RegStorage r_result_;
+  };
+
+  LIR* branch = OpCmpImmBranch(kCondEq, r_result, 0, NULL);
+  LIR* cont = NewLIR0(kPseudoTargetLabel);
+
+  AddSlowPath(new (arena_) CallHelperImmMethodSlowPath(this, branch, cont, trampoline, imm,
+                                                       r_method, r_result));
+}
+
 /*
  * Generate a kPseudoBarrier marker to indicate the boundary of special
  * blocks.
@@ -1022,64 +1060,41 @@
 }
 
 void Mir2Lir::GenConstClass(uint32_t type_idx, RegLocation rl_dest) {
-  RegLocation rl_method = LoadCurrMethod();
-  CheckRegLocation(rl_method);
-  RegStorage res_reg = AllocTempRef();
+  RegLocation rl_result;
   if (!cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx,
                                                         *cu_->dex_file,
                                                         type_idx)) {
     // Call out to helper which resolves type and verifies access.
     // Resolved type returned in kRet0.
-    CallRuntimeHelperImmReg(kQuickInitializeTypeAndVerifyAccess, type_idx, rl_method.reg, true);
-    RegLocation rl_result = GetReturn(kRefReg);
-    StoreValue(rl_dest, rl_result);
+    CallRuntimeHelperImmMethod(kQuickInitializeTypeAndVerifyAccess, type_idx, true);
+    rl_result = GetReturn(kRefReg);
   } else {
-    RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true);
-    // We're don't need access checks, load type from dex cache
-    int32_t dex_cache_offset =
-        mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value();
-    LoadRefDisp(rl_method.reg, dex_cache_offset, res_reg, kNotVolatile);
-    int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
-    LoadRefDisp(res_reg, offset_of_type, rl_result.reg, kNotVolatile);
+    rl_result = EvalLoc(rl_dest, kRefReg, true);
+    // We don't need access checks, load type from dex cache
+    RegStorage r_method = RegStorage::InvalidReg();
+    if (CanUseOpPcRelDexCacheArrayLoad()) {
+      size_t offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
+      OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, rl_result.reg);
+    } else {
+      RegLocation rl_method = LoadCurrMethod();
+      CheckRegLocation(rl_method);
+      r_method = rl_method.reg;
+      int32_t dex_cache_offset =
+          mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value();
+      RegStorage res_reg = AllocTempRef();
+      LoadRefDisp(r_method, dex_cache_offset, res_reg, kNotVolatile);
+      int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
+      LoadRefDisp(res_reg, offset_of_type, rl_result.reg, kNotVolatile);
+      FreeTemp(res_reg);
+    }
     if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file,
         type_idx) || ForceSlowTypePath(cu_)) {
       // Slow path, at runtime test if type is null and if so initialize
       FlushAllRegs();
-      LIR* branch = OpCmpImmBranch(kCondEq, rl_result.reg, 0, NULL);
-      LIR* cont = NewLIR0(kPseudoTargetLabel);
-
-      // Object to generate the slow path for class resolution.
-      class SlowPath : public LIRSlowPath {
-       public:
-        SlowPath(Mir2Lir* m2l, LIR* fromfast, LIR* cont_in, const int type_idx_in,
-                 const RegLocation& rl_method_in, const RegLocation& rl_result_in)
-            : LIRSlowPath(m2l, fromfast, cont_in),
-              type_idx_(type_idx_in), rl_method_(rl_method_in), rl_result_(rl_result_in) {
-        }
-
-        void Compile() {
-          GenerateTargetLabel();
-
-          m2l_->CallRuntimeHelperImmReg(kQuickInitializeType, type_idx_, rl_method_.reg, true);
-          m2l_->OpRegCopy(rl_result_.reg,  m2l_->TargetReg(kRet0, kRef));
-          m2l_->OpUnconditionalBranch(cont_);
-        }
-
-       private:
-        const int type_idx_;
-        const RegLocation rl_method_;
-        const RegLocation rl_result_;
-      };
-
-      // Add to list for future.
-      AddSlowPath(new (arena_) SlowPath(this, branch, cont, type_idx, rl_method, rl_result));
-
-      StoreValue(rl_dest, rl_result);
-     } else {
-      // Fast path, we're done - just store result
-      StoreValue(rl_dest, rl_result);
+      GenIfNullUseHelperImmMethod(rl_result.reg, kQuickInitializeType, type_idx, r_method);
     }
   }
+  StoreValue(rl_dest, rl_result);
 }
 
 void Mir2Lir::GenConstString(uint32_t string_idx, RegLocation rl_dest) {
@@ -1092,64 +1107,42 @@
     FlushAllRegs();
     LockCallTemps();  // Using explicit registers
 
-    // If the Method* is already in a register, we can save a copy.
-    RegLocation rl_method = mir_graph_->GetMethodLoc();
-    RegStorage r_method;
-    if (rl_method.location == kLocPhysReg) {
-      // A temp would conflict with register use below.
-      DCHECK(!IsTemp(rl_method.reg));
-      r_method = rl_method.reg;
-    } else {
-      r_method = TargetReg(kArg2, kRef);
-      LoadCurrMethodDirect(r_method);
-    }
-    // Method to declaring class.
-    LoadRefDisp(r_method, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
-                TargetReg(kArg0, kRef), kNotVolatile);
-    // Declaring class to dex cache strings.
-    LoadRefDisp(TargetReg(kArg0, kRef), mirror::Class::DexCacheStringsOffset().Int32Value(),
-                TargetReg(kArg0, kRef), kNotVolatile);
-
     // Might call out to helper, which will return resolved string in kRet0
-    LoadRefDisp(TargetReg(kArg0, kRef), offset_of_string, TargetReg(kRet0, kRef), kNotVolatile);
-    LIR* fromfast = OpCmpImmBranch(kCondEq, TargetReg(kRet0, kRef), 0, NULL);
-    LIR* cont = NewLIR0(kPseudoTargetLabel);
+    RegStorage ret0 = TargetReg(kRet0, kRef);
+    RegStorage r_method = RegStorage::InvalidReg();
+    if (CanUseOpPcRelDexCacheArrayLoad()) {
+      size_t offset = dex_cache_arrays_layout_.StringOffset(string_idx);
+      OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, ret0);
+    } else {
+      r_method = LoadCurrMethodWithHint(TargetReg(kArg1, kRef));
+      // Method to declaring class.
+      RegStorage arg0 = TargetReg(kArg0, kRef);
+      LoadRefDisp(r_method, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
+                  arg0, kNotVolatile);
+      // Declaring class to dex cache strings.
+      LoadRefDisp(arg0, mirror::Class::DexCacheStringsOffset().Int32Value(), arg0, kNotVolatile);
 
-    {
-      // Object to generate the slow path for string resolution.
-      class SlowPath : public LIRSlowPath {
-       public:
-        SlowPath(Mir2Lir* m2l, LIR* fromfast_in, LIR* cont_in, RegStorage r_method_in,
-                 int32_t string_idx_in)
-            : LIRSlowPath(m2l, fromfast_in, cont_in),
-              r_method_(r_method_in), string_idx_(string_idx_in) {
-        }
-
-        void Compile() {
-          GenerateTargetLabel();
-          m2l_->CallRuntimeHelperImmReg(kQuickResolveString, string_idx_, r_method_, true);
-          m2l_->OpUnconditionalBranch(cont_);
-        }
-
-       private:
-         const RegStorage r_method_;
-         const int32_t string_idx_;
-      };
-
-      AddSlowPath(new (arena_) SlowPath(this, fromfast, cont, r_method, string_idx));
+      LoadRefDisp(arg0, offset_of_string, ret0, kNotVolatile);
     }
+    GenIfNullUseHelperImmMethod(ret0, kQuickResolveString, string_idx, r_method);
 
     GenBarrier();
     StoreValue(rl_dest, GetReturn(kRefReg));
   } else {
-    RegLocation rl_method = LoadCurrMethod();
-    RegStorage res_reg = AllocTempRef();
     RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true);
-    LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), res_reg,
-                kNotVolatile);
-    LoadRefDisp(res_reg, mirror::Class::DexCacheStringsOffset().Int32Value(), res_reg,
-                kNotVolatile);
-    LoadRefDisp(res_reg, offset_of_string, rl_result.reg, kNotVolatile);
+    if (CanUseOpPcRelDexCacheArrayLoad()) {
+      size_t offset = dex_cache_arrays_layout_.StringOffset(string_idx);
+      OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, rl_result.reg);
+    } else {
+      RegLocation rl_method = LoadCurrMethod();
+      RegStorage res_reg = AllocTempRef();
+      LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), res_reg,
+                  kNotVolatile);
+      LoadRefDisp(res_reg, mirror::Class::DexCacheStringsOffset().Int32Value(), res_reg,
+                  kNotVolatile);
+      LoadRefDisp(res_reg, offset_of_string, rl_result.reg, kNotVolatile);
+      FreeTemp(res_reg);
+    }
     StoreValue(rl_dest, rl_result);
   }
 }
@@ -1224,14 +1217,20 @@
   RegStorage check_class = AllocTypedTemp(false, kRefReg);
   RegStorage object_class = AllocTypedTemp(false, kRefReg);
 
-  LoadCurrMethodDirect(check_class);
   if (use_declaring_class) {
-    LoadRefDisp(check_class, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), check_class,
+    RegStorage r_method = LoadCurrMethodWithHint(check_class);
+    LoadRefDisp(r_method, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), check_class,
                 kNotVolatile);
     LoadRefDisp(object.reg,  mirror::Object::ClassOffset().Int32Value(), object_class,
                 kNotVolatile);
+  } else if (CanUseOpPcRelDexCacheArrayLoad()) {
+    size_t offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
+    OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, check_class);
+    LoadRefDisp(object.reg,  mirror::Object::ClassOffset().Int32Value(), object_class,
+                kNotVolatile);
   } else {
-    LoadRefDisp(check_class, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
+    RegStorage r_method = LoadCurrMethodWithHint(check_class);
+    LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
                 check_class, kNotVolatile);
     LoadRefDisp(object.reg,  mirror::Object::ClassOffset().Int32Value(), object_class,
                 kNotVolatile);
@@ -1267,20 +1266,19 @@
   FlushAllRegs();
   // May generate a call - use explicit registers
   LockCallTemps();
-  RegStorage method_reg = TargetReg(kArg1, kRef);
-  LoadCurrMethodDirect(method_reg);   // kArg1 <= current Method*
   RegStorage class_reg = TargetReg(kArg2, kRef);  // kArg2 will hold the Class*
   RegStorage ref_reg = TargetReg(kArg0, kRef);  // kArg0 will hold the ref.
   RegStorage ret_reg = GetReturn(kRefReg).reg;
   if (needs_access_check) {
     // Check we have access to type_idx and if not throw IllegalAccessError,
     // returns Class* in kArg0
-    CallRuntimeHelperImm(kQuickInitializeTypeAndVerifyAccess, type_idx, true);
+    CallRuntimeHelperImmMethod(kQuickInitializeTypeAndVerifyAccess, type_idx, true);
     OpRegCopy(class_reg, ret_reg);  // Align usage with fast path
     LoadValueDirectFixed(rl_src, ref_reg);  // kArg0 <= ref
   } else if (use_declaring_class) {
+    RegStorage r_method = LoadCurrMethodWithHint(TargetReg(kArg1, kRef));
     LoadValueDirectFixed(rl_src, ref_reg);  // kArg0 <= ref
-    LoadRefDisp(method_reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
+    LoadRefDisp(r_method, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
                 class_reg, kNotVolatile);
   } else {
     if (can_assume_type_is_in_dex_cache) {
@@ -1288,42 +1286,23 @@
       LoadValueDirectFixed(rl_src, ref_reg);  // kArg0 <= ref
     }
 
-    // Load dex cache entry into class_reg (kArg2)
-    LoadRefDisp(method_reg, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
-                class_reg, kNotVolatile);
-    int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
-    LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
+    RegStorage r_method = RegStorage::InvalidReg();
+    if (CanUseOpPcRelDexCacheArrayLoad()) {
+      size_t offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
+      OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, class_reg);
+    } else {
+      r_method = LoadCurrMethodWithHint(TargetReg(kArg1, kRef));
+      // Load dex cache entry into class_reg (kArg2)
+      LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
+                  class_reg, kNotVolatile);
+      int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
+      LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
+    }
     if (!can_assume_type_is_in_dex_cache) {
-      LIR* slow_path_branch = OpCmpImmBranch(kCondEq, class_reg, 0, NULL);
-      LIR* slow_path_target = NewLIR0(kPseudoTargetLabel);
+      GenIfNullUseHelperImmMethod(class_reg, kQuickInitializeType, type_idx, r_method);
 
       // Should load value here.
       LoadValueDirectFixed(rl_src, ref_reg);  // kArg0 <= ref
-
-      class InitTypeSlowPath : public Mir2Lir::LIRSlowPath {
-       public:
-        InitTypeSlowPath(Mir2Lir* m2l, LIR* branch, LIR* cont, uint32_t type_idx_in,
-                         RegLocation rl_src_in)
-            : LIRSlowPath(m2l, branch, cont), type_idx_(type_idx_in),
-              rl_src_(rl_src_in) {
-        }
-
-        void Compile() OVERRIDE {
-          GenerateTargetLabel();
-
-          m2l_->CallRuntimeHelperImm(kQuickInitializeType, type_idx_, true);
-          m2l_->OpRegCopy(m2l_->TargetReg(kArg2, kRef),
-                          m2l_->TargetReg(kRet0, kRef));  // Align usage with fast path
-          m2l_->OpUnconditionalBranch(cont_);
-        }
-
-       private:
-        uint32_t type_idx_;
-        RegLocation rl_src_;
-      };
-
-      AddSlowPath(new (arena_) InitTypeSlowPath(this, slow_path_branch, slow_path_target,
-                                                type_idx, rl_src));
     }
   }
   /* kArg0 is ref, kArg2 is class. If ref==null, use directly as bool result */
@@ -1426,55 +1405,34 @@
   FlushAllRegs();
   // May generate a call - use explicit registers
   LockCallTemps();
-  RegStorage method_reg = TargetReg(kArg1, kRef);
-  LoadCurrMethodDirect(method_reg);  // kArg1 <= current Method*
   RegStorage class_reg = TargetReg(kArg2, kRef);  // kArg2 will hold the Class*
   if (needs_access_check) {
     // Check we have access to type_idx and if not throw IllegalAccessError,
     // returns Class* in kRet0
     // InitializeTypeAndVerifyAccess(idx, method)
-    CallRuntimeHelperImm(kQuickInitializeTypeAndVerifyAccess, type_idx, true);
+    CallRuntimeHelperImmMethod(kQuickInitializeTypeAndVerifyAccess, type_idx, true);
     OpRegCopy(class_reg, TargetReg(kRet0, kRef));  // Align usage with fast path
   } else if (use_declaring_class) {
+    RegStorage method_reg = LoadCurrMethodWithHint(TargetReg(kArg1, kRef));
     LoadRefDisp(method_reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
                 class_reg, kNotVolatile);
   } else {
     // Load dex cache entry into class_reg (kArg2)
-    LoadRefDisp(method_reg, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
-                class_reg, kNotVolatile);
-    int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
-    LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
+    RegStorage r_method = RegStorage::InvalidReg();
+    if (CanUseOpPcRelDexCacheArrayLoad()) {
+      size_t offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
+      OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, class_reg);
+    } else {
+      r_method = LoadCurrMethodWithHint(TargetReg(kArg1, kRef));
+
+      LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
+                  class_reg, kNotVolatile);
+      int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
+      LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
+    }
     if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx)) {
       // Need to test presence of type in dex cache at runtime
-      LIR* hop_branch = OpCmpImmBranch(kCondEq, class_reg, 0, NULL);
-      LIR* cont = NewLIR0(kPseudoTargetLabel);
-
-      // Slow path to initialize the type.  Executed if the type is NULL.
-      class SlowPath : public LIRSlowPath {
-       public:
-        SlowPath(Mir2Lir* m2l, LIR* fromfast, LIR* cont_in, const int type_idx_in,
-                 const RegStorage class_reg_in)
-            : LIRSlowPath(m2l, fromfast, cont_in),
-              type_idx_(type_idx_in), class_reg_(class_reg_in) {
-        }
-
-        void Compile() {
-          GenerateTargetLabel();
-
-          // Call out to helper, which will return resolved type in kArg0
-          // InitializeTypeFromCode(idx, method)
-          m2l_->CallRuntimeHelperImmReg(kQuickInitializeType, type_idx_,
-                                        m2l_->TargetReg(kArg1, kRef), true);
-          m2l_->OpRegCopy(class_reg_, m2l_->TargetReg(kRet0, kRef));  // Align usage with fast path
-          m2l_->OpUnconditionalBranch(cont_);
-        }
-
-       public:
-        const int type_idx_;
-        const RegStorage class_reg_;
-      };
-
-      AddSlowPath(new (arena_) SlowPath(this, hop_branch, cont, type_idx, class_reg));
+      GenIfNullUseHelperImmMethod(class_reg, kQuickInitializeType, type_idx, r_method);
     }
   }
   // At this point, class_reg (kArg2) has class
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 2d41ba1..e747239 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -24,6 +24,7 @@
 #include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "dex_file-inl.h"
 #include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "invoke_type.h"
 #include "mirror/array.h"
diff --git a/compiler/dex/quick/gen_loadstore.cc b/compiler/dex/quick/gen_loadstore.cc
index b71691f..54e5742 100644
--- a/compiler/dex/quick/gen_loadstore.cc
+++ b/compiler/dex/quick/gen_loadstore.cc
@@ -340,6 +340,20 @@
   LoadValueDirectFixed(mir_graph_->GetMethodLoc(), r_tgt);
 }
 
+RegStorage Mir2Lir::LoadCurrMethodWithHint(RegStorage r_hint) {
+  // If the method is promoted to a register, return that register, otherwise load it to r_hint.
+  // (Replacement for LoadCurrMethod() usually used when LockCallTemps() is in effect.)
+  DCHECK(r_hint.Valid());
+  RegLocation rl_method = mir_graph_->GetMethodLoc();
+  if (rl_method.location == kLocPhysReg) {
+    DCHECK(!IsTemp(rl_method.reg));
+    return rl_method.reg;
+  } else {
+    LoadCurrMethodDirect(r_hint);
+    return r_hint;
+  }
+}
+
 RegLocation Mir2Lir::LoadCurrMethod() {
   return LoadValue(mir_graph_->GetMethodLoc(), kRefReg);
 }
diff --git a/compiler/dex/quick/local_optimizations.cc b/compiler/dex/quick/local_optimizations.cc
index e573899..6cdf567 100644
--- a/compiler/dex/quick/local_optimizations.cc
+++ b/compiler/dex/quick/local_optimizations.cc
@@ -493,15 +493,14 @@
       /* Found a slot to hoist to */
       if (slot >= 0) {
         LIR* cur_lir = prev_inst_list[slot];
-        LIR* new_load_lir =
-          static_cast<LIR*>(arena_->Alloc(sizeof(LIR), kArenaAllocLIR));
-        *new_load_lir = *this_lir;
+        LIR* prev_lir = PREV_LIR(this_lir);
+        UnlinkLIR(this_lir);
         /*
          * Insertion is guaranteed to succeed since check_lir
          * is never the first LIR on the list
          */
-        InsertLIRBefore(cur_lir, new_load_lir);
-        NopLIR(this_lir);
+        InsertLIRBefore(cur_lir, this_lir);
+        this_lir = prev_lir;  // Continue the loop with the next LIR.
       }
     }
   }
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index cca4e5a..bb8fbae 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -32,6 +32,7 @@
 #include "leb128.h"
 #include "safe_map.h"
 #include "utils/array_ref.h"
+#include "utils/dex_cache_arrays_layout.h"
 #include "utils/stack_checks.h"
 
 namespace art {
@@ -956,6 +957,7 @@
     // Shared by all targets - implemented in gen_loadstore.cc.
     RegLocation LoadCurrMethod();
     void LoadCurrMethodDirect(RegStorage r_tgt);
+    RegStorage LoadCurrMethodWithHint(RegStorage r_hint);
     virtual LIR* LoadConstant(RegStorage r_dest, int value);
     // Natural word size.
     LIR* LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest) {
@@ -1093,6 +1095,18 @@
     virtual void LoadClassType(const DexFile& dex_file, uint32_t type_idx,
                                SpecialTargetRegister symbolic_reg);
 
+    // TODO: Support PC-relative dex cache array loads on all platforms and
+    // replace CanUseOpPcRelDexCacheArrayLoad() with dex_cache_arrays_layout_.Valid().
+    virtual bool CanUseOpPcRelDexCacheArrayLoad() const;
+
+    /*
+     * @brief Load an element of one of the dex cache arrays.
+     * @param dex_file the dex file associated with the target dex cache.
+     * @param offset the offset of the element in the fixed dex cache arrays' layout.
+     * @param r_dest the register where to load the element.
+     */
+    virtual void OpPcRelDexCacheArrayLoad(const DexFile* dex_file, int offset, RegStorage r_dest);
+
     // Routines that work for the generic case, but may be overriden by target.
     /*
      * @brief Compare memory to immediate, and branch if condition true.
@@ -1596,7 +1610,6 @@
      */
     virtual bool GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special);
 
-  protected:
     void ClobberBody(RegisterInfo* p);
     void SetCurrentDexPc(DexOffset dexpc) {
       current_dalvik_offset_ = dexpc;
@@ -1669,6 +1682,16 @@
      */
     bool GenSpecialIdentity(MIR* mir, const InlineMethod& special);
 
+    /**
+     * @brief Generate code to check if result is null and, if it is, call helper to load it.
+     * @param r_result the result register.
+     * @param trampoline the helper to call in slow path.
+     * @param imm the immediate passed to the helper.
+     * @param r_method the register with ArtMethod* if available, otherwise RegStorage::Invalid().
+     */
+    void GenIfNullUseHelperImmMethod(
+        RegStorage r_result, QuickEntrypointEnum trampoline, int imm, RegStorage r_method);
+
     void AddDivZeroCheckSlowPath(LIR* branch);
 
     // Copy arg0 and arg1 to kArg0 and kArg1 safely, possibly using
@@ -1815,7 +1838,9 @@
     // Record the MIR that generated a given safepoint (nullptr for prologue safepoints).
     ArenaVector<std::pair<LIR*, MIR*>> safepoints_;
 
-  protected:
+    // The layout of the cu_->dex_file's dex cache arrays for PC-relative addressing.
+    const DexCacheArraysLayout dex_cache_arrays_layout_;
+
     // ABI support
     class ShortyArg {
       public:
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
index 6d28984..8baafc7 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -634,6 +634,12 @@
     instruction_set = kThumb2;
   }
   CompilationUnit cu(runtime->GetArenaPool(), instruction_set, driver, class_linker);
+  cu.dex_file = &dex_file;
+  cu.class_def_idx = class_def_idx;
+  cu.method_idx = method_idx;
+  cu.access_flags = access_flags;
+  cu.invoke_type = invoke_type;
+  cu.shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx));
 
   CHECK((cu.instruction_set == kThumb2) ||
         (cu.instruction_set == kArm64) ||
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index abee872..e81228a 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -21,6 +21,7 @@
 #include "base/logging.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
 #include "gc/accounting/card_table.h"
 #include "mirror/art_method.h"
 #include "mirror/object_array-inl.h"
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 100d49a..670efee 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -31,6 +31,7 @@
 #include "base/timing_logger.h"
 #include "class_linker.h"
 #include "compiled_class.h"
+#include "compiled_method.h"
 #include "compiler.h"
 #include "compiler_driver-inl.h"
 #include "dex_compilation_unit.h"
@@ -62,6 +63,7 @@
 #include "thread_pool.h"
 #include "trampolines/trampoline_compiler.h"
 #include "transaction.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
 #include "utils/swap_space.h"
 #include "verifier/method_verifier.h"
 #include "verifier/method_verifier-inl.h"
@@ -1173,6 +1175,13 @@
   return klass->GetDisableIntrinsicFlagOffset().Uint32Value();
 }
 
+DexCacheArraysLayout CompilerDriver::GetDexCacheArraysLayout(const DexFile* dex_file) {
+  // Currently only image dex caches have fixed array layout.
+  return IsImage() && GetSupportBootImageFixup()
+      ? DexCacheArraysLayout(dex_file)
+      : DexCacheArraysLayout();
+}
+
 void CompilerDriver::ProcessedInstanceField(bool resolved) {
   if (!resolved) {
     stats_->UnresolvedInstanceField();
@@ -2246,7 +2255,7 @@
     // Count non-relative linker patches.
     size_t non_relative_linker_patch_count = 0u;
     for (const LinkerPatch& patch : compiled_method->GetPatches()) {
-      if (patch.Type() != kLinkerPatchCallRelative) {
+      if (!patch.IsPcRelative()) {
         ++non_relative_linker_patch_count;
       }
     }
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index b825293..efcaae4 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -26,11 +26,8 @@
 #include "base/mutex.h"
 #include "base/timing_logger.h"
 #include "class_reference.h"
-#include "compiled_method.h"
 #include "compiler.h"
 #include "dex_file.h"
-#include "dex/verified_method.h"
-#include "driver/compiler_options.h"
 #include "invoke_type.h"
 #include "method_reference.h"
 #include "mirror/class.h"  // For mirror::Class::Status.
@@ -39,7 +36,9 @@
 #include "runtime.h"
 #include "safe_map.h"
 #include "thread_pool.h"
+#include "utils/array_ref.h"
 #include "utils/dedupe_set.h"
+#include "utils/dex_cache_arrays_layout.h"
 #include "utils/swap_space.h"
 #include "utils.h"
 
@@ -54,6 +53,7 @@
 }  // namespace verifier
 
 class CompiledClass;
+class CompiledMethod;
 class CompilerOptions;
 class DexCompilationUnit;
 class DexFileToMethodInlinerMap;
@@ -62,6 +62,9 @@
 class OatWriter;
 class ParallelCompilationManager;
 class ScopedObjectAccess;
+template <class Allocator> class SrcMap;
+class SrcMapElem;
+using SwapSrcMap = SrcMap<SwapAllocator<SrcMapElem>>;
 template<class T> class Handle;
 class TimingLogger;
 class VerificationResults;
@@ -318,6 +321,10 @@
   bool IsMethodsClassInitialized(mirror::Class* referrer_class, mirror::ArtMethod* resolved_method)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Get the layout of dex cache arrays for a dex file. Returns invalid layout if the
+  // dex cache arrays don't have a fixed layout.
+  DexCacheArraysLayout GetDexCacheArraysLayout(const DexFile* dex_file);
+
   void ProcessedInstanceField(bool resolved);
   void ProcessedStaticField(bool resolved, bool local);
   void ProcessedInvoke(InvokeType invoke_type, int flags);
diff --git a/compiler/dwarf/debug_frame_opcode_writer.h b/compiler/dwarf/debug_frame_opcode_writer.h
new file mode 100644
index 0000000..cc4ef8f
--- /dev/null
+++ b/compiler/dwarf/debug_frame_opcode_writer.h
@@ -0,0 +1,282 @@
+/*
+ * 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_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
+#define ART_COMPILER_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
+
+#include "dwarf.h"
+#include "register.h"
+#include "writer.h"
+
+namespace art {
+namespace dwarf {
+
+// Writer for .debug_frame opcodes (DWARF-3).
+// See the DWARF specification for the precise meaning of the opcodes.
+// The writer is very light-weight, however it will do the following for you:
+//  * Choose the most compact encoding of a given opcode.
+//  * Keep track of current state and convert absolute values to deltas.
+//  * Divide by header-defined factors as appropriate.
+template<typename Allocator = std::allocator<uint8_t> >
+class DebugFrameOpCodeWriter : private Writer<Allocator> {
+ public:
+  // To save space, DWARF divides most offsets by header-defined factors.
+  // They are used in integer divisions, so we make them constants.
+  // We usually subtract from stack base pointer, so making the factor
+  // negative makes the encoded values positive and thus easier to encode.
+  static constexpr int kDataAlignmentFactor = -4;
+  static constexpr int kCodeAlignmentFactor = 1;
+
+  // Explicitely advance the program counter to given location.
+  void AdvancePC(int absolute_pc) {
+    DCHECK_GE(absolute_pc, current_pc_);
+    int delta = FactorCodeOffset(absolute_pc - current_pc_);
+    if (delta != 0) {
+      if (delta <= 0x3F) {
+        this->PushUint8(DW_CFA_advance_loc | delta);
+      } else if (delta <= UINT8_MAX) {
+        this->PushUint8(DW_CFA_advance_loc1);
+        this->PushUint8(delta);
+      } else if (delta <= UINT16_MAX) {
+        this->PushUint8(DW_CFA_advance_loc2);
+        this->PushUint16(delta);
+      } else {
+        this->PushUint8(DW_CFA_advance_loc4);
+        this->PushUint32(delta);
+      }
+    }
+    current_pc_ = absolute_pc;
+  }
+
+  // Override this method to automatically advance the PC before each opcode.
+  virtual void ImplicitlyAdvancePC() { }
+
+  // Common alias in assemblers - spill relative to current stack pointer.
+  void RelOffset(Reg reg, int offset) {
+    Offset(reg, offset - current_cfa_offset_);
+  }
+
+  // Common alias in assemblers - increase stack frame size.
+  void AdjustCFAOffset(int delta) {
+    DefCFAOffset(current_cfa_offset_ + delta);
+  }
+
+  // Custom alias - spill many registers based on bitmask.
+  void RelOffsetForMany(Reg reg_base, int offset, uint32_t reg_mask,
+                        int reg_size) {
+    DCHECK(reg_size == 4 || reg_size == 8);
+    for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) {
+      if ((reg_mask & 1) != 0u) {
+        RelOffset(Reg(reg_base.num() + i), offset);
+        offset += reg_size;
+      }
+    }
+  }
+
+  // Custom alias - unspill many registers based on bitmask.
+  void RestoreMany(Reg reg_base, uint32_t reg_mask) {
+    for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) {
+      if ((reg_mask & 1) != 0u) {
+        Restore(Reg(reg_base.num() + i));
+      }
+    }
+  }
+
+  void Nop() {
+    this->PushUint8(DW_CFA_nop);
+  }
+
+  void Offset(Reg reg, int offset) {
+    ImplicitlyAdvancePC();
+    int factored_offset = FactorDataOffset(offset);  // May change sign.
+    if (factored_offset >= 0) {
+      if (0 <= reg.num() && reg.num() <= 0x3F) {
+        this->PushUint8(DW_CFA_offset | reg.num());
+        this->PushUleb128(factored_offset);
+      } else {
+        this->PushUint8(DW_CFA_offset_extended);
+        this->PushUleb128(reg.num());
+        this->PushUleb128(factored_offset);
+      }
+    } else {
+      uses_dwarf3_features_ = true;
+      this->PushUint8(DW_CFA_offset_extended_sf);
+      this->PushUleb128(reg.num());
+      this->PushSleb128(factored_offset);
+    }
+  }
+
+  void Restore(Reg reg) {
+    ImplicitlyAdvancePC();
+    if (0 <= reg.num() && reg.num() <= 0x3F) {
+      this->PushUint8(DW_CFA_restore | reg.num());
+    } else {
+      this->PushUint8(DW_CFA_restore_extended);
+      this->PushUleb128(reg.num());
+    }
+  }
+
+  void Undefined(Reg reg) {
+    ImplicitlyAdvancePC();
+    this->PushUint8(DW_CFA_undefined);
+    this->PushUleb128(reg.num());
+  }
+
+  void SameValue(Reg reg) {
+    ImplicitlyAdvancePC();
+    this->PushUint8(DW_CFA_same_value);
+    this->PushUleb128(reg.num());
+  }
+
+  // The previous value of "reg" is stored in register "new_reg".
+  void Register(Reg reg, Reg new_reg) {
+    ImplicitlyAdvancePC();
+    this->PushUint8(DW_CFA_register);
+    this->PushUleb128(reg.num());
+    this->PushUleb128(new_reg.num());
+  }
+
+  void RememberState() {
+    // Note that we do not need to advance the PC.
+    this->PushUint8(DW_CFA_remember_state);
+  }
+
+  void RestoreState() {
+    ImplicitlyAdvancePC();
+    this->PushUint8(DW_CFA_restore_state);
+  }
+
+  void DefCFA(Reg reg, int offset) {
+    ImplicitlyAdvancePC();
+    if (offset >= 0) {
+      this->PushUint8(DW_CFA_def_cfa);
+      this->PushUleb128(reg.num());
+      this->PushUleb128(offset);  // Non-factored.
+    } else {
+      uses_dwarf3_features_ = true;
+      this->PushUint8(DW_CFA_def_cfa_sf);
+      this->PushUleb128(reg.num());
+      this->PushSleb128(FactorDataOffset(offset));
+    }
+    current_cfa_offset_ = offset;
+  }
+
+  void DefCFARegister(Reg reg) {
+    ImplicitlyAdvancePC();
+    this->PushUint8(DW_CFA_def_cfa_register);
+    this->PushUleb128(reg.num());
+  }
+
+  void DefCFAOffset(int offset) {
+    if (current_cfa_offset_ != offset) {
+      ImplicitlyAdvancePC();
+      if (offset >= 0) {
+        this->PushUint8(DW_CFA_def_cfa_offset);
+        this->PushUleb128(offset);  // Non-factored.
+      } else {
+        uses_dwarf3_features_ = true;
+        this->PushUint8(DW_CFA_def_cfa_offset_sf);
+        this->PushSleb128(FactorDataOffset(offset));
+      }
+      current_cfa_offset_ = offset;
+    }
+  }
+
+  void ValOffset(Reg reg, int offset) {
+    ImplicitlyAdvancePC();
+    uses_dwarf3_features_ = true;
+    int factored_offset = FactorDataOffset(offset);  // May change sign.
+    if (factored_offset >= 0) {
+      this->PushUint8(DW_CFA_val_offset);
+      this->PushUleb128(reg.num());
+      this->PushUleb128(factored_offset);
+    } else {
+      this->PushUint8(DW_CFA_val_offset_sf);
+      this->PushUleb128(reg.num());
+      this->PushSleb128(factored_offset);
+    }
+  }
+
+  void DefCFAExpression(void* expr, int expr_size) {
+    ImplicitlyAdvancePC();
+    uses_dwarf3_features_ = true;
+    this->PushUint8(DW_CFA_def_cfa_expression);
+    this->PushUleb128(expr_size);
+    this->PushData(expr, expr_size);
+  }
+
+  void Expression(Reg reg, void* expr, int expr_size) {
+    ImplicitlyAdvancePC();
+    uses_dwarf3_features_ = true;
+    this->PushUint8(DW_CFA_expression);
+    this->PushUleb128(reg.num());
+    this->PushUleb128(expr_size);
+    this->PushData(expr, expr_size);
+  }
+
+  void ValExpression(Reg reg, void* expr, int expr_size) {
+    ImplicitlyAdvancePC();
+    uses_dwarf3_features_ = true;
+    this->PushUint8(DW_CFA_val_expression);
+    this->PushUleb128(reg.num());
+    this->PushUleb128(expr_size);
+    this->PushData(expr, expr_size);
+  }
+
+  int GetCurrentCFAOffset() const {
+    return current_cfa_offset_;
+  }
+
+  void SetCurrentCFAOffset(int offset) {
+    current_cfa_offset_ = offset;
+  }
+
+  using Writer<Allocator>::data;
+
+  DebugFrameOpCodeWriter(const Allocator& alloc = Allocator())
+      : Writer<Allocator>(&opcodes_),
+        opcodes_(alloc),
+        current_cfa_offset_(0),
+        current_pc_(0),
+        uses_dwarf3_features_(false) {
+  }
+
+  virtual ~DebugFrameOpCodeWriter() { }
+
+ protected:
+  int FactorDataOffset(int offset) const {
+    DCHECK_EQ(offset % kDataAlignmentFactor, 0);
+    return offset / kDataAlignmentFactor;
+  }
+
+  int FactorCodeOffset(int offset) const {
+    DCHECK_EQ(offset % kCodeAlignmentFactor, 0);
+    return offset / kCodeAlignmentFactor;
+  }
+
+  std::vector<uint8_t, Allocator> opcodes_;
+  int current_cfa_offset_;
+  int current_pc_;
+  bool uses_dwarf3_features_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DebugFrameOpCodeWriter);
+};
+
+}  // namespace dwarf
+}  // namespace art
+
+#endif  // ART_COMPILER_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
diff --git a/compiler/dwarf/debug_frame_writer.h b/compiler/dwarf/debug_frame_writer.h
new file mode 100644
index 0000000..6de45f5
--- /dev/null
+++ b/compiler/dwarf/debug_frame_writer.h
@@ -0,0 +1,96 @@
+/*
+ * 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_DWARF_DEBUG_FRAME_WRITER_H_
+#define ART_COMPILER_DWARF_DEBUG_FRAME_WRITER_H_
+
+#include "debug_frame_opcode_writer.h"
+#include "dwarf.h"
+#include "writer.h"
+
+namespace art {
+namespace dwarf {
+
+// Writer for the .eh_frame section (which extends .debug_frame specification).
+template<typename Allocator = std::allocator<uint8_t>>
+class DebugFrameWriter FINAL : private Writer<Allocator> {
+ public:
+  void WriteCIE(Reg return_address_register,
+                const uint8_t* initial_opcodes,
+                int initial_opcodes_size) {
+    DCHECK(cie_header_start_ == ~0u);
+    cie_header_start_ = this->data()->size();
+    this->PushUint32(0);  // Length placeholder.
+    this->PushUint32(0);  // CIE id.
+    this->PushUint8(1);   // Version.
+    this->PushString("zR");
+    this->PushUleb128(DebugFrameOpCodeWriter<Allocator>::kCodeAlignmentFactor);
+    this->PushSleb128(DebugFrameOpCodeWriter<Allocator>::kDataAlignmentFactor);
+    this->PushUleb128(return_address_register.num());  // ubyte in DWARF2.
+    this->PushUleb128(1);  // z: Augmentation data size.
+    if (use_64bit_address_) {
+      this->PushUint8(0x04);  // R: ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata8).
+    } else {
+      this->PushUint8(0x03);  // R: ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata4).
+    }
+    this->PushData(initial_opcodes, initial_opcodes_size);
+    this->Pad(use_64bit_address_ ? 8 : 4);
+    this->UpdateUint32(cie_header_start_, this->data()->size() - cie_header_start_ - 4);
+  }
+
+  void WriteCIE(Reg return_address_register,
+                const DebugFrameOpCodeWriter<Allocator>& opcodes) {
+    WriteCIE(return_address_register, opcodes.data()->data(), opcodes.data()->size());
+  }
+
+  void WriteFDE(uint64_t initial_address,
+                uint64_t address_range,
+                const uint8_t* unwind_opcodes,
+                int unwind_opcodes_size) {
+    DCHECK(cie_header_start_ != ~0u);
+    size_t fde_header_start = this->data()->size();
+    this->PushUint32(0);  // Length placeholder.
+    this->PushUint32(this->data()->size() - cie_header_start_);  // 'CIE_pointer'
+    if (use_64bit_address_) {
+      this->PushUint64(initial_address);
+      this->PushUint64(address_range);
+    } else {
+      this->PushUint32(initial_address);
+      this->PushUint32(address_range);
+    }
+    this->PushUleb128(0);  // Augmentation data size.
+    this->PushData(unwind_opcodes, unwind_opcodes_size);
+    this->Pad(use_64bit_address_ ? 8 : 4);
+    this->UpdateUint32(fde_header_start, this->data()->size() - fde_header_start - 4);
+  }
+
+  DebugFrameWriter(std::vector<uint8_t, Allocator>* buffer, bool use_64bit_address)
+      : Writer<Allocator>(buffer),
+        use_64bit_address_(use_64bit_address),
+        cie_header_start_(~0u) {
+  }
+
+ private:
+  bool use_64bit_address_;
+  size_t cie_header_start_;
+
+  DISALLOW_COPY_AND_ASSIGN(DebugFrameWriter);
+};
+
+}  // namespace dwarf
+}  // namespace art
+
+#endif  // ART_COMPILER_DWARF_DEBUG_FRAME_WRITER_H_
diff --git a/compiler/dwarf/debug_line_opcode_writer.h b/compiler/dwarf/debug_line_opcode_writer.h
new file mode 100644
index 0000000..f34acee
--- /dev/null
+++ b/compiler/dwarf/debug_line_opcode_writer.h
@@ -0,0 +1,243 @@
+/*
+ * 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_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
+#define ART_COMPILER_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
+
+#include "dwarf.h"
+#include "writer.h"
+
+namespace art {
+namespace dwarf {
+
+// Writer for the .debug_line opcodes (DWARF-3).
+// The writer is very light-weight, however it will do the following for you:
+//  * Choose the most compact encoding of a given opcode.
+//  * Keep track of current state and convert absolute values to deltas.
+//  * Divide by header-defined factors as appropriate.
+template<typename Allocator = std::allocator<uint8_t>>
+class DebugLineOpCodeWriter FINAL : private Writer<Allocator> {
+ public:
+  static constexpr int kOpcodeBase = 13;
+  static constexpr bool kDefaultIsStmt = true;
+  static constexpr int kLineBase = -5;
+  static constexpr int kLineRange = 14;
+
+  void AddRow() {
+    this->PushUint8(DW_LNS_copy);
+  }
+
+  void AdvancePC(uint64_t absolute_address) {
+    DCHECK_NE(current_address_, 0u);  // Use SetAddress for the first advance.
+    DCHECK_GE(absolute_address, current_address_);
+    if (absolute_address != current_address_) {
+      uint64_t delta = FactorCodeOffset(absolute_address - current_address_);
+      if (delta <= INT32_MAX) {
+        this->PushUint8(DW_LNS_advance_pc);
+        this->PushUleb128(static_cast<int>(delta));
+        current_address_ = absolute_address;
+      } else {
+        SetAddress(absolute_address);
+      }
+    }
+  }
+
+  void AdvanceLine(int absolute_line) {
+    int delta = absolute_line - current_line_;
+    if (delta != 0) {
+      this->PushUint8(DW_LNS_advance_line);
+      this->PushSleb128(delta);
+      current_line_ = absolute_line;
+    }
+  }
+
+  void SetFile(int file) {
+    if (current_file_ != file) {
+      this->PushUint8(DW_LNS_set_file);
+      this->PushUleb128(file);
+      current_file_ = file;
+    }
+  }
+
+  void SetColumn(int column) {
+    this->PushUint8(DW_LNS_set_column);
+    this->PushUleb128(column);
+  }
+
+  void NegateStmt() {
+    this->PushUint8(DW_LNS_negate_stmt);
+  }
+
+  void SetBasicBlock() {
+    this->PushUint8(DW_LNS_set_basic_block);
+  }
+
+  void SetPrologueEnd() {
+    uses_dwarf3_features_ = true;
+    this->PushUint8(DW_LNS_set_prologue_end);
+  }
+
+  void SetEpilogueBegin() {
+    uses_dwarf3_features_ = true;
+    this->PushUint8(DW_LNS_set_epilogue_begin);
+  }
+
+  void SetISA(int isa) {
+    uses_dwarf3_features_ = true;
+    this->PushUint8(DW_LNS_set_isa);
+    this->PushUleb128(isa);
+  }
+
+  void EndSequence() {
+    this->PushUint8(0);
+    this->PushUleb128(1);
+    this->PushUint8(DW_LNE_end_sequence);
+    current_address_ = 0;
+    current_file_ = 1;
+    current_line_ = 1;
+  }
+
+  // Uncoditionally set address using the long encoding.
+  // This gives the linker opportunity to relocate the address.
+  void SetAddress(uint64_t absolute_address) {
+    DCHECK_GE(absolute_address, current_address_);
+    FactorCodeOffset(absolute_address);  // Check if it is factorable.
+    this->PushUint8(0);
+    if (use_64bit_address_) {
+      this->PushUleb128(1 + 8);
+      this->PushUint8(DW_LNE_set_address);
+      this->PushUint64(absolute_address);
+    } else {
+      this->PushUleb128(1 + 4);
+      this->PushUint8(DW_LNE_set_address);
+      this->PushUint32(absolute_address);
+    }
+    current_address_ = absolute_address;
+  }
+
+  void DefineFile(const char* filename,
+                  int directory_index,
+                  int modification_time,
+                  int file_size) {
+    int size = 1 +
+               strlen(filename) + 1 +
+               UnsignedLeb128Size(directory_index) +
+               UnsignedLeb128Size(modification_time) +
+               UnsignedLeb128Size(file_size);
+    this->PushUint8(0);
+    this->PushUleb128(size);
+    size_t start = data()->size();
+    this->PushUint8(DW_LNE_define_file);
+    this->PushString(filename);
+    this->PushUleb128(directory_index);
+    this->PushUleb128(modification_time);
+    this->PushUleb128(file_size);
+    DCHECK_EQ(start + size, data()->size());
+  }
+
+  // Compact address and line opcode.
+  void AddRow(uint64_t absolute_address, int absolute_line) {
+    DCHECK_GE(absolute_address, current_address_);
+
+    // If the address is definitely too far, use the long encoding.
+    uint64_t delta_address = FactorCodeOffset(absolute_address - current_address_);
+    if (delta_address > UINT8_MAX) {
+      AdvancePC(absolute_address);
+      delta_address = 0;
+    }
+
+    // If the line is definitely too far, use the long encoding.
+    int delta_line = absolute_line - current_line_;
+    if (!(kLineBase <= delta_line && delta_line < kLineBase + kLineRange)) {
+      AdvanceLine(absolute_line);
+      delta_line = 0;
+    }
+
+    // Both address and line should be reasonable now.  Use the short encoding.
+    int opcode = kOpcodeBase + (delta_line - kLineBase) +
+                 (static_cast<int>(delta_address) * kLineRange);
+    if (opcode > UINT8_MAX) {
+      // If the address is still too far, try to increment it by const amount.
+      int const_advance = (0xFF - kOpcodeBase) / kLineRange;
+      opcode -= (kLineRange * const_advance);
+      if (opcode <= UINT8_MAX) {
+        this->PushUint8(DW_LNS_const_add_pc);
+      } else {
+        // Give up and use long encoding for address.
+        AdvancePC(absolute_address);
+        // Still use the opcode to do line advance and copy.
+        opcode = kOpcodeBase + (delta_line - kLineBase);
+      }
+    }
+    DCHECK(kOpcodeBase <= opcode && opcode <= 0xFF);
+    this->PushUint8(opcode);  // Special opcode.
+    current_line_ = absolute_line;
+    current_address_ = absolute_address;
+  }
+
+  int GetCodeFactorBits() const {
+    return code_factor_bits_;
+  }
+
+  uint64_t CurrentAddress() const {
+    return current_address_;
+  }
+
+  int CurrentFile() const {
+    return current_file_;
+  }
+
+  int CurrentLine() const {
+    return current_line_;
+  }
+
+  using Writer<Allocator>::data;
+
+  DebugLineOpCodeWriter(bool use64bitAddress,
+                        int codeFactorBits,
+                        const Allocator& alloc = Allocator())
+      : Writer<Allocator>(&opcodes_),
+        opcodes_(alloc),
+        uses_dwarf3_features_(false),
+        use_64bit_address_(use64bitAddress),
+        code_factor_bits_(codeFactorBits),
+        current_address_(0),
+        current_file_(1),
+        current_line_(1) {
+  }
+
+ private:
+  uint64_t FactorCodeOffset(uint64_t offset) const {
+    DCHECK_GE(code_factor_bits_, 0);
+    DCHECK_EQ((offset >> code_factor_bits_) << code_factor_bits_, offset);
+    return offset >> code_factor_bits_;
+  }
+
+  std::vector<uint8_t, Allocator> opcodes_;
+  bool uses_dwarf3_features_;
+  bool use_64bit_address_;
+  int code_factor_bits_;
+  uint64_t current_address_;
+  int current_file_;
+  int current_line_;
+
+  DISALLOW_COPY_AND_ASSIGN(DebugLineOpCodeWriter);
+};
+
+}  // namespace dwarf
+}  // namespace art
+
+#endif  // ART_COMPILER_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
diff --git a/compiler/dwarf/debug_line_writer.h b/compiler/dwarf/debug_line_writer.h
new file mode 100644
index 0000000..4b7d8d9
--- /dev/null
+++ b/compiler/dwarf/debug_line_writer.h
@@ -0,0 +1,87 @@
+/*
+ * 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_DWARF_DEBUG_LINE_WRITER_H_
+#define ART_COMPILER_DWARF_DEBUG_LINE_WRITER_H_
+
+#include "debug_line_opcode_writer.h"
+#include "dwarf.h"
+#include "writer.h"
+#include <string>
+
+namespace art {
+namespace dwarf {
+
+// Writer for the .debug_line section (DWARF-3).
+template<typename Allocator = std::allocator<uint8_t>>
+class DebugLineWriter FINAL : private Writer<Allocator> {
+ public:
+  struct FileEntry {
+    std::string file_name;
+    int directory_index;
+    int modification_time;
+    int file_size;
+  };
+
+  void WriteTable(const std::vector<std::string>& include_directories,
+                  const std::vector<FileEntry>& files,
+                  const DebugLineOpCodeWriter<Allocator>& opcodes) {
+    size_t header_start = this->data()->size();
+    this->PushUint32(0);  // Section-length placeholder.
+    // Claim DWARF-2 version even though we use some DWARF-3 features.
+    // DWARF-2 consumers will ignore the unknown opcodes.
+    // This is what clang currently does.
+    this->PushUint16(2);  // .debug_line version.
+    size_t header_length_pos = this->data()->size();
+    this->PushUint32(0);  // Header-length placeholder.
+    this->PushUint8(1 << opcodes.GetCodeFactorBits());
+    this->PushUint8(DebugLineOpCodeWriter<Allocator>::kDefaultIsStmt ? 1 : 0);
+    this->PushInt8(DebugLineOpCodeWriter<Allocator>::kLineBase);
+    this->PushUint8(DebugLineOpCodeWriter<Allocator>::kLineRange);
+    this->PushUint8(DebugLineOpCodeWriter<Allocator>::kOpcodeBase);
+    static const int opcode_lengths[DebugLineOpCodeWriter<Allocator>::kOpcodeBase] = {
+        0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 };
+    for (int i = 1; i < DebugLineOpCodeWriter<Allocator>::kOpcodeBase; i++) {
+      this->PushUint8(opcode_lengths[i]);
+    }
+    for (const std::string& directory : include_directories) {
+      this->PushData(directory.data(), directory.size() + 1);
+    }
+    this->PushUint8(0);  // Terminate include_directories list.
+    for (const FileEntry& file : files) {
+      this->PushData(file.file_name.data(), file.file_name.size() + 1);
+      this->PushUleb128(file.directory_index);
+      this->PushUleb128(file.modification_time);
+      this->PushUleb128(file.file_size);
+    }
+    this->PushUint8(0);  // Terminate file list.
+    this->UpdateUint32(header_length_pos, this->data()->size() - header_length_pos - 4);
+    this->PushData(opcodes.data()->data(), opcodes.data()->size());
+    this->UpdateUint32(header_start, this->data()->size() - header_start - 4);
+  }
+
+  explicit DebugLineWriter(std::vector<uint8_t, Allocator>* buffer)
+    : Writer<Allocator>(buffer) {
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DebugLineWriter);
+};
+
+}  // namespace dwarf
+}  // namespace art
+
+#endif  // ART_COMPILER_DWARF_DEBUG_LINE_WRITER_H_
diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc
new file mode 100644
index 0000000..f3553bc
--- /dev/null
+++ b/compiler/dwarf/dwarf_test.cc
@@ -0,0 +1,235 @@
+/*
+ * 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 "dwarf_test.h"
+
+#include "dwarf/debug_frame_opcode_writer.h"
+#include "dwarf/debug_frame_writer.h"
+#include "dwarf/debug_line_opcode_writer.h"
+#include "dwarf/debug_line_writer.h"
+#include "gtest/gtest.h"
+
+namespace art {
+namespace dwarf {
+
+// Run the tests only on host since we need objdump.
+#ifndef HAVE_ANDROID_OS
+
+TEST_F(DwarfTest, DebugFrame) {
+  const bool is64bit = false;
+
+  // Pick offset value which would catch Uleb vs Sleb errors.
+  const int offset = 40000;
+  ASSERT_EQ(UnsignedLeb128Size(offset / 4), 2u);
+  ASSERT_EQ(SignedLeb128Size(offset / 4), 3u);
+  DW_CHECK("Data alignment factor: -4");
+  const Reg reg(6);
+
+  // Test the opcodes in the order mentioned in the spec.
+  // There are usually several encoding variations of each opcode.
+  DebugFrameOpCodeWriter<> opcodes;
+  DW_CHECK("FDE");
+  int pc = 0;
+  for (int i : {0, 1, 0x3F, 0x40, 0xFF, 0x100, 0xFFFF, 0x10000}) {
+    pc += i;
+    opcodes.AdvancePC(pc);
+  }
+  DW_CHECK_NEXT("DW_CFA_advance_loc: 1 to 01000001");
+  DW_CHECK_NEXT("DW_CFA_advance_loc: 63 to 01000040");
+  DW_CHECK_NEXT("DW_CFA_advance_loc1: 64 to 01000080");
+  DW_CHECK_NEXT("DW_CFA_advance_loc1: 255 to 0100017f");
+  DW_CHECK_NEXT("DW_CFA_advance_loc2: 256 to 0100027f");
+  DW_CHECK_NEXT("DW_CFA_advance_loc2: 65535 to 0101027e");
+  DW_CHECK_NEXT("DW_CFA_advance_loc4: 65536 to 0102027e");
+  opcodes.DefCFA(reg, offset);
+  DW_CHECK_NEXT("DW_CFA_def_cfa: r6 (esi) ofs 40000");
+  opcodes.DefCFA(reg, -offset);
+  DW_CHECK_NEXT("DW_CFA_def_cfa_sf: r6 (esi) ofs -40000");
+  opcodes.DefCFARegister(reg);
+  DW_CHECK_NEXT("DW_CFA_def_cfa_register: r6 (esi)");
+  opcodes.DefCFAOffset(offset);
+  DW_CHECK_NEXT("DW_CFA_def_cfa_offset: 40000");
+  opcodes.DefCFAOffset(-offset);
+  DW_CHECK_NEXT("DW_CFA_def_cfa_offset_sf: -40000");
+  uint8_t expr[] = { 0 };
+  opcodes.DefCFAExpression(expr, arraysize(expr));
+  DW_CHECK_NEXT("DW_CFA_def_cfa_expression");
+  opcodes.Undefined(reg);
+  DW_CHECK_NEXT("DW_CFA_undefined: r6 (esi)");
+  opcodes.SameValue(reg);
+  DW_CHECK_NEXT("DW_CFA_same_value: r6 (esi)");
+  opcodes.Offset(Reg(0x3F), -offset);
+  // Bad register likely means that it does not exist on x86,
+  // but we want to test high register numbers anyway.
+  DW_CHECK_NEXT("DW_CFA_offset: bad register: r63 at cfa-40000");
+  opcodes.Offset(Reg(0x40), -offset);
+  DW_CHECK_NEXT("DW_CFA_offset_extended: bad register: r64 at cfa-40000");
+  opcodes.Offset(Reg(0x40), offset);
+  DW_CHECK_NEXT("DW_CFA_offset_extended_sf: bad register: r64 at cfa+40000");
+  opcodes.ValOffset(reg, -offset);
+  DW_CHECK_NEXT("DW_CFA_val_offset: r6 (esi) at cfa-40000");
+  opcodes.ValOffset(reg, offset);
+  DW_CHECK_NEXT("DW_CFA_val_offset_sf: r6 (esi) at cfa+40000");
+  opcodes.Register(reg, Reg(1));
+  DW_CHECK_NEXT("DW_CFA_register: r6 (esi) in r1 (ecx)");
+  opcodes.Expression(reg, expr, arraysize(expr));
+  DW_CHECK_NEXT("DW_CFA_expression: r6 (esi)");
+  opcodes.ValExpression(reg, expr, arraysize(expr));
+  DW_CHECK_NEXT("DW_CFA_val_expression: r6 (esi)");
+  opcodes.Restore(Reg(0x3F));
+  DW_CHECK_NEXT("DW_CFA_restore: bad register: r63");
+  opcodes.Restore(Reg(0x40));
+  DW_CHECK_NEXT("DW_CFA_restore_extended: bad register: r64");
+  opcodes.Restore(reg);
+  DW_CHECK_NEXT("DW_CFA_restore: r6 (esi)");
+  opcodes.RememberState();
+  DW_CHECK_NEXT("DW_CFA_remember_state");
+  opcodes.RestoreState();
+  DW_CHECK_NEXT("DW_CFA_restore_state");
+  opcodes.Nop();
+  DW_CHECK_NEXT("DW_CFA_nop");
+
+  // Also test helpers.
+  opcodes.DefCFA(Reg(4), 100);  // ESP
+  DW_CHECK_NEXT("DW_CFA_def_cfa: r4 (esp) ofs 100");
+  opcodes.AdjustCFAOffset(8);
+  DW_CHECK_NEXT("DW_CFA_def_cfa_offset: 108");
+  opcodes.RelOffset(Reg(0), 0);  // push R0
+  DW_CHECK_NEXT("DW_CFA_offset: r0 (eax) at cfa-108");
+  opcodes.RelOffset(Reg(1), 4);  // push R1
+  DW_CHECK_NEXT("DW_CFA_offset: r1 (ecx) at cfa-104");
+  opcodes.RelOffsetForMany(Reg(2), 8, 1 | (1 << 3), 4);  // push R2 and R5
+  DW_CHECK_NEXT("DW_CFA_offset: r2 (edx) at cfa-100");
+  DW_CHECK_NEXT("DW_CFA_offset: r5 (ebp) at cfa-96");
+  opcodes.RestoreMany(Reg(2), 1 | (1 << 3));  // pop R2 and R5
+  DW_CHECK_NEXT("DW_CFA_restore: r2 (edx)");
+  DW_CHECK_NEXT("DW_CFA_restore: r5 (ebp)");
+
+  DebugFrameWriter<> eh_frame(&eh_frame_data_, is64bit);
+  DebugFrameOpCodeWriter<> initial_opcodes;
+  eh_frame.WriteCIE(Reg(is64bit ? 16 : 8),  // Return address register.
+                    initial_opcodes);  // Initial opcodes.
+  eh_frame.WriteFDE(0x01000000, 0x01000000,
+                    opcodes.data()->data(), opcodes.data()->size());
+  CheckObjdumpOutput(is64bit, "-W");
+}
+
+TEST_F(DwarfTest, DebugFrame64) {
+  const bool is64bit = true;
+  DebugFrameWriter<> eh_frame(&eh_frame_data_, is64bit);
+  DebugFrameOpCodeWriter<> no_opcodes;
+  eh_frame.WriteCIE(Reg(16), no_opcodes);
+  eh_frame.WriteFDE(0x0100000000000000, 0x0200000000000000,
+                    no_opcodes.data()->data(), no_opcodes.data()->size());
+  DW_CHECK("FDE cie=00000000 pc=100000000000000..300000000000000");
+  CheckObjdumpOutput(is64bit, "-W");
+}
+
+TEST_F(DwarfTest, DebugLine) {
+  const bool is64bit = false;
+  const int code_factor_bits = 1;
+  DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits);
+
+  std::vector<std::string> include_directories;
+  include_directories.push_back("/path/to/source");
+  DW_CHECK("/path/to/source");
+
+  std::vector<DebugLineWriter<>::FileEntry> files {
+    { "file0.c", 0, 1000, 2000 },
+    { "file1.c", 1, 1000, 2000 },
+    { "file2.c", 1, 1000, 2000 },
+  };
+  DW_CHECK("1\t0\t1000\t2000\tfile0.c");
+  DW_CHECK_NEXT("2\t1\t1000\t2000\tfile1.c");
+  DW_CHECK_NEXT("3\t1\t1000\t2000\tfile2.c");
+
+  DW_CHECK("Line Number Statements");
+  opcodes.SetAddress(0x01000000);
+  DW_CHECK_NEXT("Extended opcode 2: set Address to 0x1000000");
+  opcodes.AddRow();
+  DW_CHECK_NEXT("Copy");
+  opcodes.AdvancePC(0x01000100);
+  DW_CHECK_NEXT("Advance PC by 256 to 0x1000100");
+  opcodes.SetFile(2);
+  DW_CHECK_NEXT("Set File Name to entry 2 in the File Name Table");
+  opcodes.AdvanceLine(3);
+  DW_CHECK_NEXT("Advance Line by 2 to 3");
+  opcodes.SetColumn(4);
+  DW_CHECK_NEXT("Set column to 4");
+  opcodes.NegateStmt();
+  DW_CHECK_NEXT("Set is_stmt to 0");
+  opcodes.SetBasicBlock();
+  DW_CHECK_NEXT("Set basic block");
+  opcodes.SetPrologueEnd();
+  DW_CHECK_NEXT("Set prologue_end to true");
+  opcodes.SetEpilogueBegin();
+  DW_CHECK_NEXT("Set epilogue_begin to true");
+  opcodes.SetISA(5);
+  DW_CHECK_NEXT("Set ISA to 5");
+  opcodes.EndSequence();
+  DW_CHECK_NEXT("Extended opcode 1: End of Sequence");
+  opcodes.DefineFile("file.c", 0, 1000, 2000);
+  DW_CHECK_NEXT("Extended opcode 3: define new File Table entry");
+  DW_CHECK_NEXT("Entry\tDir\tTime\tSize\tName");
+  DW_CHECK_NEXT("1\t0\t1000\t2000\tfile.c");
+
+  DebugLineWriter<> debug_line(&debug_line_data_);
+  debug_line.WriteTable(include_directories, files, opcodes);
+  CheckObjdumpOutput(is64bit, "-W");
+}
+
+// DWARF has special one byte codes which advance PC and line at the same time.
+TEST_F(DwarfTest, DebugLineSpecialOpcodes) {
+  const bool is64bit = false;
+  const int code_factor_bits = 1;
+  uint32_t pc = 0x01000000;
+  int line = 1;
+  DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits);
+  opcodes.SetAddress(pc);
+  size_t num_rows = 0;
+  DW_CHECK("Line Number Statements:");
+  DW_CHECK("Special opcode");
+  DW_CHECK("Advance PC by constant");
+  DW_CHECK("Decoded dump of debug contents of section .debug_line:");
+  DW_CHECK("Line number    Starting address");
+  for (int addr_delta = 0; addr_delta < 80; addr_delta += 2) {
+    for (int line_delta = 16; line_delta >= -16; --line_delta) {
+      pc += addr_delta;
+      line += line_delta;
+      opcodes.AddRow(pc, line);
+      num_rows++;
+      ASSERT_EQ(opcodes.CurrentAddress(), pc);
+      ASSERT_EQ(opcodes.CurrentLine(), line);
+      char expected[1024];
+      sprintf(expected, "%i           0x%x", line, pc);
+      DW_CHECK_NEXT(expected);
+    }
+  }
+  EXPECT_LT(opcodes.data()->size(), num_rows * 3);
+
+  std::vector<std::string> directories;
+  std::vector<DebugLineWriter<>::FileEntry> files {
+    { "file.c", 0, 1000, 2000 },
+  };
+  DebugLineWriter<> debug_line(&debug_line_data_);
+  debug_line.WriteTable(directories, files, opcodes);
+  CheckObjdumpOutput(is64bit, "-W -WL");
+}
+
+#endif  // HAVE_ANDROID_OS
+
+}  // namespace dwarf
+}  // namespace art
diff --git a/compiler/dwarf/dwarf_test.h b/compiler/dwarf/dwarf_test.h
new file mode 100644
index 0000000..dd5e0c2
--- /dev/null
+++ b/compiler/dwarf/dwarf_test.h
@@ -0,0 +1,220 @@
+/*
+ * 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_DWARF_DWARF_TEST_H_
+#define ART_COMPILER_DWARF_DWARF_TEST_H_
+
+#include <cstring>
+#include <dirent.h>
+#include <memory>
+#include <set>
+#include <stdio.h>
+#include <string>
+#include <sys/types.h>
+
+#include "utils.h"
+#include "base/unix_file/fd_file.h"
+#include "common_runtime_test.h"
+#include "elf_builder.h"
+#include "gtest/gtest.h"
+#include "os.h"
+
+namespace art {
+namespace dwarf {
+
+#define DW_CHECK(substring) Check(substring, false, __FILE__, __LINE__)
+#define DW_CHECK_NEXT(substring) Check(substring, true, __FILE__, __LINE__)
+
+class DwarfTest : public CommonRuntimeTest {
+ public:
+  static constexpr bool kPrintObjdumpOutput = false;  // debugging.
+
+  struct ExpectedLine {
+    std::string substring;
+    bool next;
+    const char* at_file;
+    int at_line;
+  };
+
+  // Check that the objdump output contains given output.
+  // If next is true, it must be the next line.  Otherwise lines are skipped.
+  void Check(const char* substr, bool next, const char* at_file, int at_line) {
+    expected_lines_.push_back(ExpectedLine {substr, next, at_file, at_line});
+  }
+
+  static std::string GetObjdumpPath() {
+    const char* android_build_top = getenv("ANDROID_BUILD_TOP");
+    if (android_build_top != nullptr) {
+      std::string host_prebuilts = std::string(android_build_top) +
+                                   "/prebuilts/gcc/linux-x86/host/";
+      // Read the content of the directory.
+      std::set<std::string> entries;
+      DIR* dir = opendir(host_prebuilts.c_str());
+      if (dir != nullptr) {
+        struct dirent* entry;
+        while ((entry = readdir(dir)) != nullptr) {
+          if (strstr(entry->d_name, "linux-glibc")) {
+            entries.insert(host_prebuilts + entry->d_name);
+          }
+        }
+        closedir(dir);
+      }
+      // Strings are sorted so the last one should be the most recent version.
+      if (!entries.empty()) {
+        std::string path = *entries.rbegin() + "/x86_64-linux/bin/objdump";
+        struct stat st;
+        if (stat(path.c_str(), &st) == 0) {
+          return path;  // File exists.
+        }
+      }
+    }
+    ADD_FAILURE() << "Can not find prebuild objdump.";
+    return "objdump";  // Use the system objdump as fallback.
+  }
+
+  // Pretty-print the generated DWARF data using objdump.
+  template<typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Dyn,
+           typename Elf_Sym, typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr>
+  std::vector<std::string> Objdump(bool is64bit, const char* args) {
+    // Write simple elf file with just the DWARF sections.
+    class NoCode : public CodeOutput {
+      virtual void SetCodeOffset(size_t) { }
+      virtual bool Write(OutputStream*) { return true; }
+    } code;
+    ScratchFile file;
+    InstructionSet isa = is64bit ? kX86_64 : kX86;
+    ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+               Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr> builder(
+        &code, file.GetFile(), isa, 0, 0, 0, 0, 0, 0, false, false);
+    typedef ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> Section;
+    if (!debug_info_data_.empty()) {
+      Section debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
+      debug_info.SetBuffer(debug_info_data_);
+      builder.RegisterRawSection(debug_info);
+    }
+    if (!debug_abbrev_data_.empty()) {
+      Section debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
+      debug_abbrev.SetBuffer(debug_abbrev_data_);
+      builder.RegisterRawSection(debug_abbrev);
+    }
+    if (!debug_str_data_.empty()) {
+      Section debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
+      debug_str.SetBuffer(debug_str_data_);
+      builder.RegisterRawSection(debug_str);
+    }
+    if (!debug_line_data_.empty()) {
+      Section debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
+      debug_line.SetBuffer(debug_line_data_);
+      builder.RegisterRawSection(debug_line);
+    }
+    if (!eh_frame_data_.empty()) {
+      Section eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0);
+      eh_frame.SetBuffer(eh_frame_data_);
+      builder.RegisterRawSection(eh_frame);
+    }
+    builder.Init();
+    builder.Write();
+
+    // Read the elf file back using objdump.
+    std::vector<std::string> lines;
+    std::string cmd = GetObjdumpPath();
+    cmd = cmd + " " + args + " " + file.GetFilename() + " 2>&1";
+    FILE* output = popen(cmd.data(), "r");
+    char buffer[1024];
+    const char* line;
+    while ((line = fgets(buffer, sizeof(buffer), output)) != nullptr) {
+      if (kPrintObjdumpOutput) {
+        printf("%s", line);
+      }
+      if (line[0] != '\0' && line[0] != '\n') {
+        EXPECT_TRUE(strstr(line, "objdump: Error:") == nullptr) << line;
+        EXPECT_TRUE(strstr(line, "objdump: Warning:") == nullptr) << line;
+        std::string str(line);
+        if (str.back() == '\n') {
+          str.pop_back();
+        }
+        lines.push_back(str);
+      }
+    }
+    pclose(output);
+    return lines;
+  }
+
+  std::vector<std::string> Objdump(bool is64bit, const char* args) {
+    if (is64bit) {
+      return Objdump<Elf64_Word, Elf64_Sword, Elf64_Addr, Elf64_Dyn,
+          Elf64_Sym, Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(is64bit, args);
+    } else {
+      return Objdump<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
+          Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(is64bit, args);
+    }
+  }
+
+  // Compare objdump output to the recorded checks.
+  void CheckObjdumpOutput(bool is64bit, const char* args) {
+    std::vector<std::string> actual_lines = Objdump(is64bit, args);
+    auto actual_line = actual_lines.begin();
+    for (const ExpectedLine& expected_line : expected_lines_) {
+      const std::string& substring = expected_line.substring;
+      if (actual_line == actual_lines.end()) {
+        ADD_FAILURE_AT(expected_line.at_file, expected_line.at_line) <<
+            "Expected '" << substring << "'.\n" <<
+            "Seen end of output.";
+      } else if (expected_line.next) {
+        if (actual_line->find(substring) == std::string::npos) {
+          ADD_FAILURE_AT(expected_line.at_file, expected_line.at_line) <<
+            "Expected '" << substring << "'.\n" <<
+            "Seen '" << actual_line->data() << "'.";
+        } else {
+          // printf("Found '%s' in '%s'.\n", substring.data(), actual_line->data());
+        }
+        actual_line++;
+      } else {
+        bool found = false;
+        for (auto it = actual_line; it < actual_lines.end(); it++) {
+          if (it->find(substring) != std::string::npos) {
+            actual_line = it;
+            found = true;
+            break;
+          }
+        }
+        if (!found) {
+          ADD_FAILURE_AT(expected_line.at_file, expected_line.at_line) <<
+            "Expected '" << substring << "'.\n" <<
+            "Not found anywhere in the rest of the output.";
+        } else {
+          // printf("Found '%s' in '%s'.\n", substring.data(), actual_line->data());
+          actual_line++;
+        }
+      }
+    }
+  }
+
+  // Buffers which are going to assembled into ELF file and passed to objdump.
+  std::vector<uint8_t> eh_frame_data_;
+  std::vector<uint8_t> debug_info_data_;
+  std::vector<uint8_t> debug_abbrev_data_;
+  std::vector<uint8_t> debug_str_data_;
+  std::vector<uint8_t> debug_line_data_;
+
+  // The expected output of objdump.
+  std::vector<ExpectedLine> expected_lines_;
+};
+
+}  // namespace dwarf
+}  // namespace art
+
+#endif  // ART_COMPILER_DWARF_DWARF_TEST_H_
diff --git a/compiler/dwarf/register.h b/compiler/dwarf/register.h
new file mode 100644
index 0000000..fa666df
--- /dev/null
+++ b/compiler/dwarf/register.h
@@ -0,0 +1,58 @@
+/*
+ * 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_DWARF_REGISTER_H_
+#define ART_COMPILER_DWARF_REGISTER_H_
+
+namespace art {
+namespace dwarf {
+
+// Represents DWARF register.
+class Reg {
+ public:
+  explicit Reg(int reg_num) : num_(reg_num) { }
+  int num() const { return num_; }
+
+  // TODO: Arm S0–S31 register mapping is obsolescent.
+  //   We should use VFP-v3/Neon D0-D31 mapping instead.
+  //   However, D0 is aliased to pair of S0 and S1, so using that
+  //   mapping we can not easily say S0 is spilled and S1 is not.
+  //   There are ways around this in DWARF but they are complex.
+  //   It would be much simpler to always spill whole D registers.
+  //   Arm64 mapping is correct since we already do this there.
+
+  static Reg ArmCore(int num) { return Reg(num); }
+  static Reg ArmFp(int num) { return Reg(64 + num); }  // S0–S31.
+  static Reg Arm64Core(int num) { return Reg(num); }
+  static Reg Arm64Fp(int num) { return Reg(64 + num); }  // V0-V31.
+  static Reg MipsCore(int num) { return Reg(num); }
+  static Reg Mips64Core(int num) { return Reg(num); }
+  static Reg X86Core(int num) { return Reg(num); }
+  static Reg X86Fp(int num) { return Reg(21 + num); }
+  static Reg X86_64Core(int num) {
+    static const int map[8] = {0, 2, 1, 3, 7, 6, 4, 5};
+    return Reg(num < 8 ? map[num] : num);
+  }
+  static Reg X86_64Fp(int num) { return Reg(17 + num); }
+
+ private:
+  int num_;
+};
+
+}  // namespace dwarf
+}  // namespace art
+
+#endif  // ART_COMPILER_DWARF_REGISTER_H_
diff --git a/compiler/dwarf/writer.h b/compiler/dwarf/writer.h
new file mode 100644
index 0000000..d8e29f0
--- /dev/null
+++ b/compiler/dwarf/writer.h
@@ -0,0 +1,159 @@
+/*
+ * 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_DWARF_WRITER_H_
+#define ART_COMPILER_DWARF_WRITER_H_
+
+#include <vector>
+#include "leb128.h"
+#include "base/logging.h"
+#include "utils.h"
+
+namespace art {
+namespace dwarf {
+
+// The base class for all DWARF writers.
+template<typename Allocator = std::allocator<uint8_t>>
+class Writer {
+ public:
+  void PushUint8(int value) {
+    DCHECK_GE(value, 0);
+    DCHECK_LE(value, UINT8_MAX);
+    data_->push_back(value & 0xff);
+  }
+
+  void PushUint16(int value) {
+    DCHECK_GE(value, 0);
+    DCHECK_LE(value, UINT16_MAX);
+    data_->push_back((value >> 0) & 0xff);
+    data_->push_back((value >> 8) & 0xff);
+  }
+
+  void PushUint32(uint32_t value) {
+    data_->push_back((value >> 0) & 0xff);
+    data_->push_back((value >> 8) & 0xff);
+    data_->push_back((value >> 16) & 0xff);
+    data_->push_back((value >> 24) & 0xff);
+  }
+
+  void PushUint32(int value) {
+    DCHECK_GE(value, 0);
+    PushUint32(static_cast<uint32_t>(value));
+  }
+
+  void PushUint32(uint64_t value) {
+    DCHECK_LE(value, UINT32_MAX);
+    PushUint32(static_cast<uint32_t>(value));
+  }
+
+  void PushUint64(uint64_t value) {
+    data_->push_back((value >> 0) & 0xff);
+    data_->push_back((value >> 8) & 0xff);
+    data_->push_back((value >> 16) & 0xff);
+    data_->push_back((value >> 24) & 0xff);
+    data_->push_back((value >> 32) & 0xff);
+    data_->push_back((value >> 40) & 0xff);
+    data_->push_back((value >> 48) & 0xff);
+    data_->push_back((value >> 56) & 0xff);
+  }
+
+  void PushInt8(int value) {
+    DCHECK_GE(value, INT8_MIN);
+    DCHECK_LE(value, INT8_MAX);
+    PushUint8(static_cast<uint8_t>(value));
+  }
+
+  void PushInt16(int value) {
+    DCHECK_GE(value, INT16_MIN);
+    DCHECK_LE(value, INT16_MAX);
+    PushUint16(static_cast<uint16_t>(value));
+  }
+
+  void PushInt32(int value) {
+    PushUint32(static_cast<uint32_t>(value));
+  }
+
+  void PushInt64(int64_t value) {
+    PushUint64(static_cast<uint64_t>(value));
+  }
+
+  // Variable-length encoders.
+
+  void PushUleb128(uint32_t value) {
+    EncodeUnsignedLeb128(data_, value);
+  }
+
+  void PushUleb128(int value) {
+    DCHECK_GE(value, 0);
+    EncodeUnsignedLeb128(data_, value);
+  }
+
+  void PushSleb128(int value) {
+    EncodeSignedLeb128(data_, value);
+  }
+
+  // Miscellaneous functions.
+
+  void PushString(const char* value) {
+    data_->insert(data_->end(), value, value + strlen(value) + 1);
+  }
+
+  void PushData(const void* ptr, size_t size) {
+    const char* p = reinterpret_cast<const char*>(ptr);
+    data_->insert(data_->end(), p, p + size);
+  }
+
+  void UpdateUint32(size_t offset, uint32_t value) {
+    DCHECK_LT(offset + 3, data_->size());
+    (*data_)[offset + 0] = (value >> 0) & 0xFF;
+    (*data_)[offset + 1] = (value >> 8) & 0xFF;
+    (*data_)[offset + 2] = (value >> 16) & 0xFF;
+    (*data_)[offset + 3] = (value >> 24) & 0xFF;
+  }
+
+  void UpdateUint64(size_t offset, uint64_t value) {
+    DCHECK_LT(offset + 7, data_->size());
+    (*data_)[offset + 0] = (value >> 0) & 0xFF;
+    (*data_)[offset + 1] = (value >> 8) & 0xFF;
+    (*data_)[offset + 2] = (value >> 16) & 0xFF;
+    (*data_)[offset + 3] = (value >> 24) & 0xFF;
+    (*data_)[offset + 4] = (value >> 32) & 0xFF;
+    (*data_)[offset + 5] = (value >> 40) & 0xFF;
+    (*data_)[offset + 6] = (value >> 48) & 0xFF;
+    (*data_)[offset + 7] = (value >> 56) & 0xFF;
+  }
+
+  void Pad(int alignment) {
+    DCHECK_NE(alignment, 0);
+    data_->resize(RoundUp(data_->size(), alignment), 0);
+  }
+
+  const std::vector<uint8_t, Allocator>* data() const {
+    return data_;
+  }
+
+  explicit Writer(std::vector<uint8_t, Allocator>* buffer) : data_(buffer) { }
+
+ private:
+  std::vector<uint8_t, Allocator>* data_;
+
+  DISALLOW_COPY_AND_ASSIGN(Writer);
+};
+
+}  // namespace dwarf
+}  // namespace art
+
+#endif  // ART_COMPILER_DWARF_WRITER_H_
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index a822b24..ca5ec66 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -21,7 +21,9 @@
 #include "base/logging.h"
 #include "base/unix_file/fd_file.h"
 #include "buffered_output_stream.h"
+#include "compiled_method.h"
 #include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
 #include "dwarf.h"
 #include "elf_builder.h"
 #include "elf_file.h"
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index c1555aa..1ede228 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -19,6 +19,7 @@
 #include <sys/stat.h>
 
 #include <memory>
+#include <numeric>
 #include <vector>
 
 #include "base/logging.h"
@@ -54,8 +55,7 @@
 #include "runtime.h"
 #include "scoped_thread_state_change.h"
 #include "handle_scope-inl.h"
-
-#include <numeric>
+#include "utils/dex_cache_arrays_layout-inl.h"
 
 using ::art::mirror::ArtField;
 using ::art::mirror::ArtMethod;
@@ -238,7 +238,7 @@
   DCHECK(object != nullptr);
   DCHECK_NE(image_objects_offset_begin_, 0u);
 
-  size_t previous_bin_sizes = GetBinSizeSum(bin_slot.GetBin());  // sum sizes in [0..bin#)
+  size_t previous_bin_sizes = bin_slot_previous_sizes_[bin_slot.GetBin()];
   size_t new_offset = image_objects_offset_begin_ + previous_bin_sizes + bin_slot.GetIndex();
   DCHECK_ALIGNED(new_offset, kObjectAlignment);
 
@@ -293,6 +293,28 @@
   DCHECK(IsImageBinSlotAssigned(object));
 }
 
+void ImageWriter::PrepareDexCacheArraySlots() {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  ReaderMutexLock mu(Thread::Current(), *class_linker->DexLock());
+  size_t dex_cache_count = class_linker->GetDexCacheCount();
+  uint32_t size = 0u;
+  for (size_t idx = 0; idx < dex_cache_count; ++idx) {
+    DexCache* dex_cache = class_linker->GetDexCache(idx);
+    const DexFile* dex_file = dex_cache->GetDexFile();
+    dex_cache_array_starts_.Put(dex_file, size);
+    DexCacheArraysLayout layout(dex_file);
+    DCHECK(layout.Valid());
+    dex_cache_array_indexes_.Put(dex_cache->GetResolvedTypes(), size + layout.TypesOffset());
+    dex_cache_array_indexes_.Put(dex_cache->GetResolvedMethods(), size + layout.MethodsOffset());
+    dex_cache_array_indexes_.Put(dex_cache->GetResolvedFields(), size + layout.FieldsOffset());
+    dex_cache_array_indexes_.Put(dex_cache->GetStrings(), size + layout.StringsOffset());
+    size += layout.Size();
+  }
+  // Set the slot size early to avoid DCHECK() failures in IsImageBinSlotAssigned()
+  // when AssignImageBinSlot() assigns their indexes out or order.
+  bin_slot_sizes_[kBinDexCacheArray] = size;
+}
+
 void ImageWriter::AssignImageBinSlot(mirror::Object* object) {
   DCHECK(object != nullptr);
   size_t object_size = object->SizeOf();
@@ -307,6 +329,7 @@
   // This means more pages will stay either clean or shared dirty (with zygote) and
   // the app will use less of its own (private) memory.
   Bin bin = kBinRegular;
+  size_t current_offset = 0u;
 
   if (kBinObjects) {
     //
@@ -316,6 +339,12 @@
     // Memory analysis has determined that the following types of objects get dirtied
     // the most:
     //
+    // * Dex cache arrays are stored in a special bin. The arrays for each dex cache have
+    //   a fixed layout which helps improve generated code (using PC-relative addressing),
+    //   so we pre-calculate their offsets separately in PrepareDexCacheArraySlots().
+    //   Since these arrays are huge, most pages do not overlap other objects and it's not
+    //   really important where they are for the clean/dirty separation. Due to their
+    //   special PC-relative addressing, we arbitrarily keep them at the beginning.
     // * Class'es which are verified [their clinit runs only at runtime]
     //   - classes in general [because their static fields get overwritten]
     //   - initialized classes with all-final statics are unlikely to be ever dirty,
@@ -376,13 +405,21 @@
       }
     } else if (object->GetClass<kVerifyNone>()->IsStringClass()) {
       bin = kBinString;  // Strings are almost always immutable (except for object header).
+    } else if (object->IsObjectArray()) {
+      auto it = dex_cache_array_indexes_.find(object);
+      if (it != dex_cache_array_indexes_.end()) {
+        bin = kBinDexCacheArray;
+        current_offset = it->second;  // Use prepared offset defined by the DexCacheLayout.
+      }  // else bin = kBinRegular
     }  // else bin = kBinRegular
   }
 
-  size_t current_offset = bin_slot_sizes_[bin];  // How many bytes the current bin is at (aligned).
-  // Move the current bin size up to accomodate the object we just assigned a bin slot.
   size_t offset_delta = RoundUp(object_size, kObjectAlignment);  // 64-bit alignment
-  bin_slot_sizes_[bin] += offset_delta;
+  if (bin != kBinDexCacheArray) {
+    current_offset = bin_slot_sizes_[bin];  // How many bytes the current bin is at (aligned).
+    // Move the current bin size up to accomodate the object we just assigned a bin slot.
+    bin_slot_sizes_[bin] += offset_delta;
+  }
 
   BinSlot new_bin_slot(bin, current_offset);
   SetImageBinSlot(object, new_bin_slot);
@@ -887,8 +924,17 @@
   // TODO: Image spaces only?
   DCHECK_LT(image_end_, image_->Size());
   image_objects_offset_begin_ = image_end_;
+  // Prepare bin slots for dex cache arrays.
+  PrepareDexCacheArraySlots();
   // Clear any pre-existing monitors which may have been in the monitor words, assign bin slots.
   heap->VisitObjects(WalkFieldsCallback, this);
+  // Calculate cumulative bin slot sizes.
+  size_t previous_sizes = 0u;
+  for (size_t i = 0; i != kBinSize; ++i) {
+    bin_slot_previous_sizes_[i] = previous_sizes;
+    previous_sizes += bin_slot_sizes_[i];
+  }
+  DCHECK_EQ(previous_sizes, GetBinSizeSum());
   // Transform each object's bin slot into an offset which will be used to do the final copy.
   heap->VisitObjects(UnbinObjectsIntoOffsetCallback, this);
   DCHECK(saved_hashes_map_.empty());  // All binslot hashes should've been put into vector by now.
@@ -1187,8 +1233,8 @@
 
 ImageWriter::BinSlot::BinSlot(uint32_t lockword) : lockword_(lockword) {
   // These values may need to get updated if more bins are added to the enum Bin
-  static_assert(kBinBits == 3, "wrong number of bin bits");
-  static_assert(kBinShift == 29, "wrong number of shift");
+  static_assert(kBinBits == 4, "wrong number of bin bits");
+  static_assert(kBinShift == 28, "wrong number of shift");
   static_assert(sizeof(BinSlot) == sizeof(LockWord), "BinSlot/LockWord must have equal sizes");
 
   DCHECK_LT(GetBin(), kBinSize);
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 53f5ce4..71044f7 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -52,7 +52,8 @@
         quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0),
         quick_to_interpreter_bridge_offset_(0), compile_pic_(compile_pic),
         target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())),
-        bin_slot_sizes_(), bin_slot_count_() {
+        bin_slot_sizes_(), bin_slot_previous_sizes_(), bin_slot_count_(),
+        string_data_array_(nullptr) {
     CHECK_NE(image_begin, 0U);
   }
 
@@ -80,6 +81,14 @@
     return reinterpret_cast<mirror::Object*>(image_begin_ + GetImageOffset(object));
   }
 
+  mirror::HeapReference<mirror::Object>* GetDexCacheArrayElementImageAddress(
+      const DexFile* dex_file, uint32_t offset) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    auto it = dex_cache_array_starts_.find(dex_file);
+    DCHECK(it != dex_cache_array_starts_.end());
+    return reinterpret_cast<mirror::HeapReference<mirror::Object>*>(
+        image_begin_ + RoundUp(sizeof(ImageHeader), kObjectAlignment) + it->second + offset);
+  }
+
   uint8_t* GetOatFileBegin() const {
     return image_begin_ + RoundUp(image_end_, kPageSize);
   }
@@ -101,6 +110,10 @@
 
   // Classify different kinds of bins that objects end up getting packed into during image writing.
   enum Bin {
+    // Dex cache arrays have a special slot for PC-relative addressing. Since they are
+    // huge, and as such their dirtiness is not important for the clean/dirty separation,
+    // we arbitrarily keep them at the beginning.
+    kBinDexCacheArray,            // Object arrays belonging to dex cache.
     // Likely-clean:
     kBinString,                        // [String] Almost always immutable (except for obj header).
     kBinArtMethodsManagedInitialized,  // [ArtMethod] Not-native, and initialized. Unlikely to dirty
@@ -113,7 +126,6 @@
     kBinClassVerified,            // Class verified, but initializers haven't been run
     kBinArtMethodNative,          // Art method that is actually native
     kBinArtMethodNotInitialized,  // Art method with a declaring class that wasn't initialized
-    // Don't care about other art methods since they don't dirty
     // Add more bins here if we add more segregation code.
     kBinSize,
   };
@@ -157,6 +169,7 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   size_t GetImageOffset(mirror::Object* object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  void PrepareDexCacheArraySlots() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void AssignImageBinSlot(mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void SetImageBinSlot(mirror::Object* object, BinSlot bin_slot)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -282,6 +295,12 @@
   // Memory mapped for generating the image.
   std::unique_ptr<MemMap> image_;
 
+  // Indexes for dex cache arrays (objects are inside of the image so that they don't move).
+  SafeMap<mirror::Object*, size_t> dex_cache_array_indexes_;
+
+  // The start offsets of the dex cache arrays.
+  SafeMap<const DexFile*, size_t> dex_cache_array_starts_;
+
   // Saved hashes (objects are inside of the image so that they don't move).
   std::vector<std::pair<mirror::Object*, uint32_t>> saved_hashes_;
 
@@ -309,6 +328,7 @@
 
   // Bin slot tracking for dirty object packing
   size_t bin_slot_sizes_[kBinSize];  // Number of bytes in a bin
+  size_t bin_slot_previous_sizes_[kBinSize];  // Number of bytes in previous bins.
   size_t bin_slot_count_[kBinSize];  // Number of objects in a bin
 
   void* string_data_array_;  // The backing for the interned strings.
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index afd39e8..503068c 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -17,11 +17,14 @@
 #include "arch/instruction_set_features.h"
 #include "class_linker.h"
 #include "common_compiler_test.h"
+#include "compiled_method.h"
 #include "compiler.h"
 #include "dex/pass_manager.h"
 #include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "dex/quick_compiler_callbacks.h"
 #include "dex/verification_results.h"
+#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/class-inl.h"
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index b3bb438..04f0db6 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -18,14 +18,18 @@
 
 #include <zlib.h>
 
+#include "arch/arm64/instruction_set_features_arm64.h"
 #include "base/allocator.h"
 #include "base/bit_vector.h"
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
 #include "compiled_class.h"
+#include "compiled_method.h"
 #include "dex_file-inl.h"
 #include "dex/verification_results.h"
+#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
 #include "gc/space/space.h"
 #include "image_writer.h"
 #include "mirror/art_method-inl.h"
@@ -43,12 +47,12 @@
 
 namespace art {
 
-class OatWriter::RelativeCallPatcher {
+class OatWriter::RelativePatcher {
  public:
-  virtual ~RelativeCallPatcher() { }
+  virtual ~RelativePatcher() { }
 
-  // Reserve space for relative call thunks if needed, return adjusted offset.
-  // After all methods have been processed it's call one last time with compiled_method == nullptr.
+  // Reserve space for relative call thunks if needed, return adjusted offset. After all methods
+  // of a class have been processed it's called one last time with compiled_method == nullptr.
   virtual uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method) = 0;
 
   // Write relative call thunks if needed, return adjusted offset.
@@ -56,19 +60,23 @@
 
   // Patch method code. The input displacement is relative to the patched location,
   // the patcher may need to adjust it if the correct base is different.
-  virtual void Patch(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t patch_offset,
-                     uint32_t target_offset) = 0;
+  virtual void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
+                         uint32_t patch_offset, uint32_t target_offset) = 0;
+
+  // Patch a reference to a dex cache location.
+  virtual void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch,
+                                      uint32_t patch_offset, uint32_t target_offset) = 0;
 
  protected:
-  RelativeCallPatcher() { }
+  RelativePatcher() { }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(RelativeCallPatcher);
+  DISALLOW_COPY_AND_ASSIGN(RelativePatcher);
 };
 
-class OatWriter::NoRelativeCallPatcher FINAL : public RelativeCallPatcher {
+class OatWriter::NoRelativePatcher FINAL : public RelativePatcher {
  public:
-  NoRelativeCallPatcher() { }
+  NoRelativePatcher() { }
 
   uint32_t ReserveSpace(uint32_t offset,
                         const CompiledMethod* compiled_method ATTRIBUTE_UNUSED) OVERRIDE {
@@ -79,19 +87,27 @@
     return offset;  // No thunks added; no patches expected.
   }
 
-  void Patch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, uint32_t literal_offset ATTRIBUTE_UNUSED,
-             uint32_t patch_offset ATTRIBUTE_UNUSED,
-             uint32_t target_offset ATTRIBUTE_UNUSED) OVERRIDE {
-    LOG(FATAL) << "Unexpected relative patch.";
+  void PatchCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+                 uint32_t literal_offset ATTRIBUTE_UNUSED,
+                 uint32_t patch_offset ATTRIBUTE_UNUSED,
+                 uint32_t target_offset ATTRIBUTE_UNUSED) OVERRIDE {
+    LOG(FATAL) << "Unexpected relative call patch.";
+  }
+
+  virtual void PatchDexCacheReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+                                      const LinkerPatch& patch ATTRIBUTE_UNUSED,
+                                      uint32_t patch_offset ATTRIBUTE_UNUSED,
+                                      uint32_t target_offset ATTRIBUTE_UNUSED) {
+    LOG(FATAL) << "Unexpected relative dex cache array patch.";
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(NoRelativeCallPatcher);
+  DISALLOW_COPY_AND_ASSIGN(NoRelativePatcher);
 };
 
-class OatWriter::X86RelativeCallPatcher FINAL : public RelativeCallPatcher {
+class OatWriter::X86RelativePatcher FINAL : public RelativePatcher {
  public:
-  X86RelativeCallPatcher() { }
+  X86RelativePatcher() { }
 
   uint32_t ReserveSpace(uint32_t offset,
                         const CompiledMethod* compiled_method ATTRIBUTE_UNUSED) OVERRIDE {
@@ -102,8 +118,8 @@
     return offset;  // No thunks added; no limit on relative call distance.
   }
 
-  void Patch(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t patch_offset,
-             uint32_t target_offset) OVERRIDE {
+  void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
+                 uint32_t patch_offset, uint32_t target_offset) OVERRIDE {
     DCHECK_LE(literal_offset + 4u, code->size());
     // Unsigned arithmetic with its well-defined overflow behavior is just fine here.
     uint32_t displacement = target_offset - patch_offset;
@@ -113,17 +129,24 @@
     reinterpret_cast<unaligned_int32_t*>(&(*code)[literal_offset])[0] = displacement;
   }
 
+  virtual void PatchDexCacheReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+                                      const LinkerPatch& patch ATTRIBUTE_UNUSED,
+                                      uint32_t patch_offset ATTRIBUTE_UNUSED,
+                                      uint32_t target_offset ATTRIBUTE_UNUSED) {
+    LOG(FATAL) << "Unexpected relative dex cache array patch.";
+  }
+
  private:
   // PC displacement from patch location; x86 PC for relative calls points to the next
   // instruction and the patch location is 4 bytes earlier.
   static constexpr int32_t kPcDisplacement = 4;
 
-  DISALLOW_COPY_AND_ASSIGN(X86RelativeCallPatcher);
+  DISALLOW_COPY_AND_ASSIGN(X86RelativePatcher);
 };
 
-class OatWriter::ArmBaseRelativeCallPatcher : public RelativeCallPatcher {
+class OatWriter::ArmBaseRelativePatcher : public RelativePatcher {
  public:
-  ArmBaseRelativeCallPatcher(OatWriter* writer,
+  ArmBaseRelativePatcher(OatWriter* writer,
                              InstructionSet instruction_set, std::vector<uint8_t> thunk_code,
                              uint32_t max_positive_displacement, uint32_t max_negative_displacement)
       : writer_(writer), instruction_set_(instruction_set), thunk_code_(thunk_code),
@@ -133,41 +156,7 @@
   }
 
   uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method) OVERRIDE {
-    // NOTE: The final thunk can be reserved from InitCodeMethodVisitor::EndClass() while it
-    // may be written early by WriteCodeMethodVisitor::VisitMethod() for a deduplicated chunk
-    // of code. To avoid any alignment discrepancies for the final chunk, we always align the
-    // offset after reserving of writing any chunk.
-    if (UNLIKELY(compiled_method == nullptr)) {
-      uint32_t aligned_offset = CompiledMethod::AlignCode(offset, instruction_set_);
-      bool needs_thunk = ReserveSpaceProcessPatches(aligned_offset);
-      if (needs_thunk) {
-        thunk_locations_.push_back(aligned_offset);
-        offset = CompiledMethod::AlignCode(aligned_offset + thunk_code_.size(), instruction_set_);
-      }
-      return offset;
-    }
-    DCHECK(compiled_method->GetQuickCode() != nullptr);
-    uint32_t quick_code_size = compiled_method->GetQuickCode()->size();
-    uint32_t quick_code_offset = compiled_method->AlignCode(offset) + sizeof(OatQuickMethodHeader);
-    uint32_t next_aligned_offset = compiled_method->AlignCode(quick_code_offset + quick_code_size);
-    if (!unprocessed_patches_.empty() &&
-        next_aligned_offset - unprocessed_patches_.front().second > max_positive_displacement_) {
-      bool needs_thunk = ReserveSpaceProcessPatches(next_aligned_offset);
-      if (needs_thunk) {
-        // A single thunk will cover all pending patches.
-        unprocessed_patches_.clear();
-        uint32_t thunk_location = compiled_method->AlignCode(offset);
-        thunk_locations_.push_back(thunk_location);
-        offset = CompiledMethod::AlignCode(thunk_location + thunk_code_.size(), instruction_set_);
-      }
-    }
-    for (const LinkerPatch& patch : compiled_method->GetPatches()) {
-      if (patch.Type() == kLinkerPatchCallRelative) {
-        unprocessed_patches_.emplace_back(patch.TargetMethod(),
-                                          quick_code_offset + patch.LiteralOffset());
-      }
-    }
-    return offset;
+    return ReserveSpaceInternal(offset, compiled_method, 0u);
   }
 
   uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE {
@@ -197,6 +186,47 @@
   }
 
  protected:
+  uint32_t ReserveSpaceInternal(uint32_t offset, const CompiledMethod* compiled_method,
+                                uint32_t max_extra_space) {
+    // NOTE: The final thunk can be reserved from InitCodeMethodVisitor::EndClass() while it
+    // may be written early by WriteCodeMethodVisitor::VisitMethod() for a deduplicated chunk
+    // of code. To avoid any alignment discrepancies for the final chunk, we always align the
+    // offset after reserving of writing any chunk.
+    if (UNLIKELY(compiled_method == nullptr)) {
+      uint32_t aligned_offset = CompiledMethod::AlignCode(offset, instruction_set_);
+      bool needs_thunk = ReserveSpaceProcessPatches(aligned_offset);
+      if (needs_thunk) {
+        thunk_locations_.push_back(aligned_offset);
+        offset = CompiledMethod::AlignCode(aligned_offset + thunk_code_.size(), instruction_set_);
+      }
+      return offset;
+    }
+    DCHECK(compiled_method->GetQuickCode() != nullptr);
+    uint32_t quick_code_size = compiled_method->GetQuickCode()->size();
+    uint32_t quick_code_offset = compiled_method->AlignCode(offset) + sizeof(OatQuickMethodHeader);
+    uint32_t next_aligned_offset = compiled_method->AlignCode(quick_code_offset + quick_code_size);
+    // Adjust for extra space required by the subclass.
+    next_aligned_offset = compiled_method->AlignCode(next_aligned_offset + max_extra_space);
+    if (!unprocessed_patches_.empty() &&
+        next_aligned_offset - unprocessed_patches_.front().second > max_positive_displacement_) {
+      bool needs_thunk = ReserveSpaceProcessPatches(next_aligned_offset);
+      if (needs_thunk) {
+        // A single thunk will cover all pending patches.
+        unprocessed_patches_.clear();
+        uint32_t thunk_location = compiled_method->AlignCode(offset);
+        thunk_locations_.push_back(thunk_location);
+        offset = CompiledMethod::AlignCode(thunk_location + thunk_code_.size(), instruction_set_);
+      }
+    }
+    for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+      if (patch.Type() == kLinkerPatchCallRelative) {
+        unprocessed_patches_.emplace_back(patch.TargetMethod(),
+                                          quick_code_offset + patch.LiteralOffset());
+      }
+    }
+    return offset;
+  }
+
   uint32_t CalculateDisplacement(uint32_t patch_offset, uint32_t target_offset) {
     // Unsigned arithmetic with its well-defined overflow behavior is just fine here.
     uint32_t displacement = target_offset - patch_offset;
@@ -219,6 +249,10 @@
     return displacement;
   }
 
+  OatWriter* Writer() const {
+    return writer_;
+  }
+
  private:
   bool ReserveSpaceProcessPatches(uint32_t next_aligned_offset) {
     // Process as many patches as possible, stop only on unresolved targets or calls too far back.
@@ -261,18 +295,18 @@
   typedef std::pair<MethodReference, uint32_t> UnprocessedPatch;
   std::deque<UnprocessedPatch> unprocessed_patches_;
 
-  DISALLOW_COPY_AND_ASSIGN(ArmBaseRelativeCallPatcher);
+  DISALLOW_COPY_AND_ASSIGN(ArmBaseRelativePatcher);
 };
 
-class OatWriter::Thumb2RelativeCallPatcher FINAL : public ArmBaseRelativeCallPatcher {
+class OatWriter::Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher {
  public:
-  explicit Thumb2RelativeCallPatcher(OatWriter* writer)
-      : ArmBaseRelativeCallPatcher(writer, kThumb2, CompileThunkCode(),
+  explicit Thumb2RelativePatcher(OatWriter* writer)
+      : ArmBaseRelativePatcher(writer, kThumb2, CompileThunkCode(),
                                    kMaxPositiveDisplacement, kMaxNegativeDisplacement) {
   }
 
-  void Patch(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t patch_offset,
-             uint32_t target_offset) OVERRIDE {
+  void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
+                 uint32_t patch_offset, uint32_t target_offset) OVERRIDE {
     DCHECK_LE(literal_offset + 4u, code->size());
     DCHECK_EQ(literal_offset & 1u, 0u);
     DCHECK_EQ(patch_offset & 1u, 0u);
@@ -302,6 +336,13 @@
     addr[3] = (value >> 8) & 0xff;
   }
 
+  virtual void PatchDexCacheReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+                                      const LinkerPatch& patch ATTRIBUTE_UNUSED,
+                                      uint32_t patch_offset ATTRIBUTE_UNUSED,
+                                      uint32_t target_offset ATTRIBUTE_UNUSED) {
+    LOG(FATAL) << "Unexpected relative dex cache array patch.";
+  }
+
  private:
   static std::vector<uint8_t> CompileThunkCode() {
     // The thunk just uses the entry point in the ArtMethod. This works even for calls
@@ -326,18 +367,102 @@
   static constexpr uint32_t kMaxPositiveDisplacement = (1u << 24) - 2 + kPcDisplacement;
   static constexpr uint32_t kMaxNegativeDisplacement = (1u << 24) - kPcDisplacement;
 
-  DISALLOW_COPY_AND_ASSIGN(Thumb2RelativeCallPatcher);
+  DISALLOW_COPY_AND_ASSIGN(Thumb2RelativePatcher);
 };
 
-class OatWriter::Arm64RelativeCallPatcher FINAL : public ArmBaseRelativeCallPatcher {
+class OatWriter::Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher {
  public:
-  explicit Arm64RelativeCallPatcher(OatWriter* writer)
-      : ArmBaseRelativeCallPatcher(writer, kArm64, CompileThunkCode(),
-                                   kMaxPositiveDisplacement, kMaxNegativeDisplacement) {
+  explicit Arm64RelativePatcher(OatWriter* writer)
+      : ArmBaseRelativePatcher(writer, kArm64, CompileThunkCode(),
+                                   kMaxPositiveDisplacement, kMaxNegativeDisplacement),
+        fix_cortex_a53_843419_(writer->compiler_driver_->GetInstructionSetFeatures()
+                               ->AsArm64InstructionSetFeatures()->NeedFixCortexA53_835769()),
+        reserved_adrp_thunks_(0u),
+        processed_adrp_thunks_(0u) {
+    if (fix_cortex_a53_843419_) {
+      adrp_thunk_locations_.reserve(16u);
+      current_method_thunks_.reserve(16u * kAdrpThunkSize);
+    }
   }
 
-  void Patch(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t patch_offset,
-             uint32_t target_offset) OVERRIDE {
+  uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method) OVERRIDE {
+    if (!fix_cortex_a53_843419_) {
+      DCHECK(adrp_thunk_locations_.empty());
+      return ReserveSpaceInternal(offset, compiled_method, 0u);
+    }
+
+    // Add thunks for previous method if any.
+    if (reserved_adrp_thunks_ != adrp_thunk_locations_.size()) {
+      size_t num_adrp_thunks = adrp_thunk_locations_.size() - reserved_adrp_thunks_;
+      offset = CompiledMethod::AlignCode(offset, kArm64) + kAdrpThunkSize * num_adrp_thunks;
+      reserved_adrp_thunks_ = adrp_thunk_locations_.size();
+    }
+
+    // Count the number of ADRP insns as the upper bound on the number of thunks needed
+    // and use it to reserve space for other linker patches.
+    size_t num_adrp = 0u;
+    if (LIKELY(compiled_method != nullptr)) {
+      for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+        if (patch.Type() == kLinkerPatchDexCacheArray &&
+            patch.LiteralOffset() == patch.PcInsnOffset()) {  // ADRP patch
+          ++num_adrp;
+        }
+      }
+    }
+    offset = ReserveSpaceInternal(offset, compiled_method, kAdrpThunkSize * num_adrp);
+    if (num_adrp == 0u) {
+      return offset;
+    }
+
+    // Now that we have the actual offset where the code will be placed, locate the ADRP insns
+    // that actually require the thunk.
+    uint32_t quick_code_offset = compiled_method->AlignCode(offset) + sizeof(OatQuickMethodHeader);
+    ArrayRef<const uint8_t> code(*compiled_method->GetQuickCode());
+    uint32_t thunk_offset = compiled_method->AlignCode(quick_code_offset + code.size());
+    DCHECK(compiled_method != nullptr);
+    for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+      if (patch.Type() == kLinkerPatchDexCacheArray &&
+          patch.LiteralOffset() == patch.PcInsnOffset()) {  // ADRP patch
+        uint32_t patch_offset = quick_code_offset + patch.LiteralOffset();
+        if (NeedsErratum843419Thunk(code, patch.LiteralOffset(), patch_offset)) {
+          adrp_thunk_locations_.emplace_back(patch_offset, thunk_offset);
+          thunk_offset += kAdrpThunkSize;
+        }
+      }
+    }
+    return offset;
+  }
+
+  uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE {
+    if (fix_cortex_a53_843419_) {
+      if (!current_method_thunks_.empty()) {
+        uint32_t aligned_offset = CompiledMethod::AlignCode(offset, kArm64);
+        if (kIsDebugBuild) {
+          CHECK(IsAligned<kAdrpThunkSize>(current_method_thunks_.size()));
+          size_t num_thunks = current_method_thunks_.size() / kAdrpThunkSize;
+          CHECK_LE(num_thunks, processed_adrp_thunks_);
+          for (size_t i = 0u; i != num_thunks; ++i) {
+            const auto& entry = adrp_thunk_locations_[processed_adrp_thunks_ - num_thunks + i];
+            CHECK_EQ(entry.second, aligned_offset + i * kAdrpThunkSize);
+          }
+        }
+        uint32_t aligned_code_delta = aligned_offset - offset;
+        if (aligned_code_delta != 0u && !Writer()->WriteCodeAlignment(out, aligned_code_delta)) {
+          return 0u;
+        }
+        if (!out->WriteFully(&current_method_thunks_[0], current_method_thunks_.size())) {
+          return 0u;
+        }
+        Writer()->size_misc_thunks_ += current_method_thunks_.size();
+        offset = aligned_offset + current_method_thunks_.size();
+        current_method_thunks_.clear();
+      }
+    }
+    return ArmBaseRelativePatcher::WriteThunks(out, offset);
+  }
+
+  void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
+                 uint32_t patch_offset, uint32_t target_offset) OVERRIDE {
     DCHECK_LE(literal_offset + 4u, code->size());
     DCHECK_EQ(literal_offset & 3u, 0u);
     DCHECK_EQ(patch_offset & 3u, 0u);
@@ -345,20 +470,104 @@
     uint32_t displacement = CalculateDisplacement(patch_offset, target_offset & ~1u);
     DCHECK_EQ(displacement & 3u, 0u);
     DCHECK((displacement >> 27) == 0u || (displacement >> 27) == 31u);  // 28-bit signed.
-    uint32_t value = (displacement & 0x0fffffffu) >> 2;
-    value |= 0x94000000;  // BL
+    uint32_t insn = (displacement & 0x0fffffffu) >> 2;
+    insn |= 0x94000000;  // BL
 
-    uint8_t* addr = &(*code)[literal_offset];
     // Check that we're just overwriting an existing BL.
-    DCHECK_EQ(addr[3] & 0xfc, 0x94);
+    DCHECK_EQ(GetInsn(code, literal_offset) & 0xfc000000u, 0x94000000u);
     // Write the new BL.
-    addr[0] = (value >> 0) & 0xff;
-    addr[1] = (value >> 8) & 0xff;
-    addr[2] = (value >> 16) & 0xff;
-    addr[3] = (value >> 24) & 0xff;
+    SetInsn(code, literal_offset, insn);
+  }
+
+  virtual void PatchDexCacheReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+                                      const LinkerPatch& patch ATTRIBUTE_UNUSED,
+                                      uint32_t patch_offset ATTRIBUTE_UNUSED,
+                                      uint32_t target_offset ATTRIBUTE_UNUSED) {
+    DCHECK_EQ(patch_offset & 3u, 0u);
+    DCHECK_EQ(target_offset & 3u, 0u);
+    uint32_t literal_offset = patch.LiteralOffset();
+    uint32_t insn = GetInsn(code, literal_offset);
+    uint32_t pc_insn_offset = patch.PcInsnOffset();
+    uint32_t disp = target_offset - ((patch_offset - literal_offset + pc_insn_offset) & ~0xfffu);
+    if (literal_offset == pc_insn_offset) {
+      // Check it's an ADRP with imm == 0 (unset).
+      DCHECK_EQ((insn & 0xffffffe0u), 0x90000000u)
+          << literal_offset << ", " << pc_insn_offset << ", 0x" << std::hex << insn;
+      if (fix_cortex_a53_843419_ && processed_adrp_thunks_ != adrp_thunk_locations_.size() &&
+          adrp_thunk_locations_[processed_adrp_thunks_].first == patch_offset) {
+        DCHECK(NeedsErratum843419Thunk(ArrayRef<const uint8_t>(*code),
+                                       literal_offset, patch_offset));
+        uint32_t thunk_offset = adrp_thunk_locations_[processed_adrp_thunks_].second;
+        uint32_t adrp_disp = target_offset - (thunk_offset & ~0xfffu);
+        uint32_t adrp = PatchAdrp(insn, adrp_disp);
+
+        uint32_t out_disp = thunk_offset - patch_offset;
+        DCHECK_EQ(out_disp & 3u, 0u);
+        DCHECK((out_disp >> 27) == 0u || (out_disp >> 27) == 31u);  // 28-bit signed.
+        insn = (out_disp & 0x0fffffffu) >> 2;
+        insn |= 0x14000000;  // B <thunk>
+
+        uint32_t back_disp = -out_disp;
+        DCHECK_EQ(back_disp & 3u, 0u);
+        DCHECK((back_disp >> 27) == 0u || (back_disp >> 27) == 31u);  // 28-bit signed.
+        uint32_t b_back = (back_disp & 0x0fffffffu) >> 2;
+        b_back |= 0x14000000;  // B <back>
+        size_t thunks_code_offset = current_method_thunks_.size();
+        current_method_thunks_.resize(thunks_code_offset + kAdrpThunkSize);
+        SetInsn(&current_method_thunks_, thunks_code_offset, adrp);
+        SetInsn(&current_method_thunks_, thunks_code_offset + 4u, b_back);
+        static_assert(kAdrpThunkSize == 2 * 4u, "thunk has 2 instructions");
+
+        processed_adrp_thunks_ += 1u;
+      } else {
+        insn = PatchAdrp(insn, disp);
+      }
+      // Write the new ADRP (or B to the erratum 843419 thunk).
+      SetInsn(code, literal_offset, insn);
+    } else {
+      DCHECK_EQ(insn & 0xfffffc00, 0xb9400000);  // LDR 32-bit with imm12 == 0 (unset).
+      if (kIsDebugBuild) {
+        uint32_t adrp = GetInsn(code, pc_insn_offset);
+        if ((adrp & 0x9f000000u) != 0x90000000u) {
+          CHECK(fix_cortex_a53_843419_);
+          CHECK_EQ(adrp & 0xfc000000u, 0x14000000u);  // B <thunk>
+          CHECK(IsAligned<kAdrpThunkSize>(current_method_thunks_.size()));
+          size_t num_thunks = current_method_thunks_.size() / kAdrpThunkSize;
+          CHECK_LE(num_thunks, processed_adrp_thunks_);
+          uint32_t b_offset = patch_offset - literal_offset + pc_insn_offset;
+          for (size_t i = processed_adrp_thunks_ - num_thunks; ; ++i) {
+            CHECK_NE(i, processed_adrp_thunks_);
+            if (adrp_thunk_locations_[i].first == b_offset) {
+              size_t idx = num_thunks - (processed_adrp_thunks_ - i);
+              adrp = GetInsn(&current_method_thunks_, idx * kAdrpThunkSize);
+              break;
+            }
+          }
+        }
+        CHECK_EQ(adrp & 0x9f00001fu,                    // Check that pc_insn_offset points
+                 0x90000000 | ((insn >> 5) & 0x1fu));   // to ADRP with matching register.
+      }
+      uint32_t imm12 = (disp & 0xfffu) >> 2;
+      insn = (insn & ~(0xfffu << 10)) | (imm12 << 10);
+      SetInsn(code, literal_offset, insn);
+    }
   }
 
  private:
+  static uint32_t PatchAdrp(uint32_t adrp, uint32_t disp) {
+    return (adrp & 0x9f00001fu) |  // Clear offset bits, keep ADRP with destination reg.
+        // Bottom 12 bits are ignored, the next 2 lowest bits are encoded in bits 29-30.
+        ((disp & 0x00003000u) << (29 - 12)) |
+        // The next 16 bits are encoded in bits 5-22.
+        ((disp & 0xffffc000u) >> (12 + 2 - 5)) |
+        // Since the target_offset is based on the beginning of the oat file and the
+        // image space precedes the oat file, the target_offset into image space will
+        // be negative yet passed as uint32_t. Therefore we limit the displacement
+        // to +-2GiB (rather than the maximim +-4GiB) and determine the sign bit from
+        // the highest bit of the displacement. This is encoded in bit 23.
+        ((disp & 0x80000000u) >> (31 - 23));
+  }
+
   static std::vector<uint8_t> CompileThunkCode() {
     // The thunk just uses the entry point in the ArtMethod. This works even for calls
     // to the generic JNI and interpreter trampolines.
@@ -374,13 +583,65 @@
     return thunk_code;
   }
 
+  static bool NeedsErratum843419Thunk(ArrayRef<const uint8_t> code, uint32_t literal_offset,
+                                      uint32_t patch_offset) {
+    DCHECK_EQ(patch_offset & 0x3u, 0u);
+    if ((patch_offset & 0xff8) == 0xff8) {  // ...ff8 or ...ffc
+      uint32_t adrp = GetInsn(code, literal_offset);
+      DCHECK_EQ(adrp & 0xff000000, 0x90000000);
+      // TODO: Improve the check. For now, we're just checking if the next insn is
+      // the LDR using the result of the ADRP, otherwise we implement the workaround.
+      uint32_t next_insn = GetInsn(code, literal_offset + 4u);
+      bool ok = (next_insn & 0xffc00000) == 0xb9400000 &&  // LDR <Wt>, [<Xn>, #pimm]
+          (((next_insn >> 5) ^ adrp) & 0x1f) == 0;         // <Xn> == ADRP destination reg
+      return !ok;
+    }
+    return false;
+  }
+
+  static uint32_t GetInsn(ArrayRef<const uint8_t> code, uint32_t offset) {
+    DCHECK_LE(offset + 4u, code.size());
+    DCHECK_EQ(offset & 3u, 0u);
+    const uint8_t* addr = &code[offset];
+    return
+        (static_cast<uint32_t>(addr[0]) << 0) +
+        (static_cast<uint32_t>(addr[1]) << 8) +
+        (static_cast<uint32_t>(addr[2]) << 16)+
+        (static_cast<uint32_t>(addr[3]) << 24);
+  }
+
+  template <typename Alloc>
+  static uint32_t GetInsn(std::vector<uint8_t, Alloc>* code, uint32_t offset) {
+    return GetInsn(ArrayRef<const uint8_t>(*code), offset);
+  }
+
+  void SetInsn(std::vector<uint8_t>* code, uint32_t offset, uint32_t value) {
+    DCHECK_LE(offset + 4u, code->size());
+    DCHECK_EQ(offset & 3u, 0u);
+    uint8_t* addr = &(*code)[offset];
+    addr[0] = (value >> 0) & 0xff;
+    addr[1] = (value >> 8) & 0xff;
+    addr[2] = (value >> 16) & 0xff;
+    addr[3] = (value >> 24) & 0xff;
+  }
+
   // Maximum positive and negative displacement measured from the patch location.
   // (Signed 28 bit displacement with the last bit 0 has range [-2^27, 2^27-4] measured from
   // the ARM64 PC pointing to the BL.)
   static constexpr uint32_t kMaxPositiveDisplacement = (1u << 27) - 4u;
   static constexpr uint32_t kMaxNegativeDisplacement = (1u << 27);
 
-  DISALLOW_COPY_AND_ASSIGN(Arm64RelativeCallPatcher);
+  // The ADRP thunk for erratum 843419 is 2 instructions, i.e. 8 bytes.
+  static constexpr uint32_t kAdrpThunkSize = 8u;
+
+  const bool fix_cortex_a53_843419_;
+  // Map original patch_offset to thunk offset.
+  std::vector<std::pair<uint32_t, uint32_t>> adrp_thunk_locations_;
+  size_t reserved_adrp_thunks_;
+  size_t processed_adrp_thunks_;
+  std::vector<uint8_t> current_method_thunks_;
+
+  DISALLOW_COPY_AND_ASSIGN(Arm64RelativePatcher);
 };
 
 #define DCHECK_OFFSET() \
@@ -427,6 +688,7 @@
     size_code_(0),
     size_code_alignment_(0),
     size_relative_call_thunks_(0),
+    size_misc_thunks_(0),
     size_mapping_table_(0),
     size_vmap_table_(0),
     size_gc_map_(0),
@@ -445,18 +707,18 @@
   switch (compiler_driver_->GetInstructionSet()) {
     case kX86:
     case kX86_64:
-      relative_call_patcher_.reset(new X86RelativeCallPatcher);
+      relative_patcher_.reset(new X86RelativePatcher);
       break;
     case kArm:
       // Fall through: we generate Thumb2 code for "arm".
     case kThumb2:
-      relative_call_patcher_.reset(new Thumb2RelativeCallPatcher(this));
+      relative_patcher_.reset(new Thumb2RelativePatcher(this));
       break;
     case kArm64:
-      relative_call_patcher_.reset(new Arm64RelativeCallPatcher(this));
+      relative_patcher_.reset(new Arm64RelativePatcher(this));
       break;
     default:
-      relative_call_patcher_.reset(new NoRelativeCallPatcher);
+      relative_patcher_.reset(new NoRelativePatcher);
       break;
   }
 
@@ -706,7 +968,7 @@
   bool EndClass() {
     OatDexMethodVisitor::EndClass();
     if (oat_class_index_ == writer_->oat_classes_.size()) {
-      offset_ = writer_->relative_call_patcher_->ReserveSpace(offset_, nullptr);
+      offset_ = writer_->relative_patcher_->ReserveSpace(offset_, nullptr);
     }
     return true;
   }
@@ -722,23 +984,22 @@
 
       const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
       CHECK(quick_code != nullptr);
-      offset_ = writer_->relative_call_patcher_->ReserveSpace(offset_, compiled_method);
-      offset_ = compiled_method->AlignCode(offset_);
-      DCHECK_ALIGNED_PARAM(offset_,
-                           GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
       uint32_t code_size = quick_code->size() * sizeof(uint8_t);
       CHECK_NE(code_size, 0U);
       uint32_t thumb_offset = compiled_method->CodeDelta();
-      quick_code_offset = offset_ + sizeof(OatQuickMethodHeader) + thumb_offset;
-
-      bool deduped = false;
 
       // Deduplicate code arrays.
+      bool deduped = false;
       auto lb = dedupe_map_.lower_bound(compiled_method);
       if (lb != dedupe_map_.end() && !dedupe_map_.key_comp()(compiled_method, lb->first)) {
         quick_code_offset = lb->second;
         deduped = true;
       } else {
+        offset_ = writer_->relative_patcher_->ReserveSpace(offset_, compiled_method);
+        offset_ = compiled_method->AlignCode(offset_);
+        DCHECK_ALIGNED_PARAM(offset_,
+                             GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
+        quick_code_offset = offset_ + sizeof(OatQuickMethodHeader) + thumb_offset;
         dedupe_map_.PutBefore(lb, compiled_method, quick_code_offset);
       }
 
@@ -790,7 +1051,7 @@
         if (!compiled_method->GetPatches().empty()) {
           uintptr_t base_loc = offset_ - code_size - writer_->oat_header_->GetExecutableOffset();
           for (const LinkerPatch& patch : compiled_method->GetPatches()) {
-            if (patch.Type() != kLinkerPatchCallRelative) {
+            if (!patch.IsPcRelative()) {
               writer_->absolute_patch_locations_.push_back(base_loc + patch.LiteralOffset());
             }
           }
@@ -851,6 +1112,37 @@
   }
 
  private:
+  struct CodeOffsetsKeyComparator {
+    bool operator()(const CompiledMethod* lhs, const CompiledMethod* rhs) const {
+      if (lhs->GetQuickCode() != rhs->GetQuickCode()) {
+        return lhs->GetQuickCode() < rhs->GetQuickCode();
+      }
+      // If the code is the same, all other fields are likely to be the same as well.
+      if (UNLIKELY(lhs->GetMappingTable() != rhs->GetMappingTable())) {
+        return lhs->GetMappingTable() < rhs->GetMappingTable();
+      }
+      if (UNLIKELY(lhs->GetVmapTable() != rhs->GetVmapTable())) {
+        return lhs->GetVmapTable() < rhs->GetVmapTable();
+      }
+      if (UNLIKELY(lhs->GetGcMap() != rhs->GetGcMap())) {
+        return lhs->GetGcMap() < rhs->GetGcMap();
+      }
+      const auto& lhs_patches = lhs->GetPatches();
+      const auto& rhs_patches = rhs->GetPatches();
+      if (UNLIKELY(lhs_patches.size() != rhs_patches.size())) {
+        return lhs_patches.size() < rhs_patches.size();
+      }
+      auto rit = rhs_patches.begin();
+      for (const LinkerPatch& lpatch : lhs_patches) {
+        if (UNLIKELY(!(lpatch == *rit))) {
+          return lpatch < *rit;
+        }
+        ++rit;
+      }
+      return false;
+    }
+  };
+
   // 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.
   SafeMap<const CompiledMethod*, uint32_t, CodeOffsetsKeyComparator> dedupe_map_;
@@ -978,7 +1270,7 @@
     bool result = OatDexMethodVisitor::EndClass();
     if (oat_class_index_ == writer_->oat_classes_.size()) {
       DCHECK(result);  // OatDexMethodVisitor::EndClass() never fails.
-      offset_ = writer_->relative_call_patcher_->WriteThunks(out_, offset_);
+      offset_ = writer_->relative_patcher_->WriteThunks(out_, offset_);
       if (UNLIKELY(offset_ == 0u)) {
         PLOG(ERROR) << "Failed to write final relative call thunks";
         result = false;
@@ -1000,33 +1292,32 @@
       if (quick_code != nullptr) {
         // Need a wrapper if we create a copy for patching.
         ArrayRef<const uint8_t> wrapped(*quick_code);
-
-        offset_ = writer_->relative_call_patcher_->WriteThunks(out, offset_);
-        if (offset_ == 0u) {
-          ReportWriteFailure("relative call thunk", it);
-          return false;
-        }
-        uint32_t aligned_offset = compiled_method->AlignCode(offset_);
-        uint32_t aligned_code_delta = aligned_offset - offset_;
-        if (aligned_code_delta != 0) {
-          if (!writer_->WriteCodeAlignment(out, aligned_code_delta)) {
-            ReportWriteFailure("code alignment padding", it);
-            return false;
-          }
-          offset_ += aligned_code_delta;
-          DCHECK_OFFSET_();
-        }
-        DCHECK_ALIGNED_PARAM(offset_,
-                             GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
         uint32_t code_size = quick_code->size() * sizeof(uint8_t);
         CHECK_NE(code_size, 0U);
 
         // Deduplicate code arrays.
         const OatMethodOffsets& method_offsets = oat_class->method_offsets_[method_offsets_index_];
-        DCHECK(method_offsets.code_offset_ < offset_ || method_offsets.code_offset_ ==
-                   offset_ + sizeof(OatQuickMethodHeader) + compiled_method->CodeDelta())
-            << PrettyMethod(it.GetMemberIndex(), *dex_file_);
         if (method_offsets.code_offset_ >= offset_) {
+          offset_ = writer_->relative_patcher_->WriteThunks(out, offset_);
+          if (offset_ == 0u) {
+            ReportWriteFailure("relative call thunk", it);
+            return false;
+          }
+          uint32_t aligned_offset = compiled_method->AlignCode(offset_);
+          uint32_t aligned_code_delta = aligned_offset - offset_;
+          if (aligned_code_delta != 0) {
+            if (!writer_->WriteCodeAlignment(out, aligned_code_delta)) {
+              ReportWriteFailure("code alignment padding", it);
+              return false;
+            }
+            offset_ += aligned_code_delta;
+            DCHECK_OFFSET_();
+          }
+          DCHECK_ALIGNED_PARAM(offset_,
+                               GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
+          DCHECK_EQ(method_offsets.code_offset_,
+                    offset_ + sizeof(OatQuickMethodHeader) + compiled_method->CodeDelta())
+              << PrettyMethod(it.GetMemberIndex(), *dex_file_);
           const OatQuickMethodHeader& method_header =
               oat_class->method_headers_[method_offsets_index_];
           writer_->oat_header_->UpdateChecksum(&method_header, sizeof(method_header));
@@ -1039,15 +1330,21 @@
           DCHECK_OFFSET_();
 
           if (!compiled_method->GetPatches().empty()) {
-            patched_code_ = std::vector<uint8_t>(quick_code->begin(), quick_code->end());
+            patched_code_.assign(quick_code->begin(), quick_code->end());
             wrapped = ArrayRef<const uint8_t>(patched_code_);
             for (const LinkerPatch& patch : compiled_method->GetPatches()) {
               if (patch.Type() == kLinkerPatchCallRelative) {
                 // NOTE: Relative calls across oat files are not supported.
                 uint32_t target_offset = GetTargetOffset(patch);
                 uint32_t literal_offset = patch.LiteralOffset();
-                writer_->relative_call_patcher_->Patch(&patched_code_, literal_offset,
+                writer_->relative_patcher_->PatchCall(&patched_code_, literal_offset,
                                                        offset_ + literal_offset, target_offset);
+              } else if (patch.Type() == kLinkerPatchDexCacheArray) {
+                uint32_t target_offset = GetDexCacheOffset(patch);
+                uint32_t literal_offset = patch.LiteralOffset();
+                writer_->relative_patcher_->PatchDexCacheReference(&patched_code_, patch,
+                                                                   offset_ + literal_offset,
+                                                                   target_offset);
               } else if (patch.Type() == kLinkerPatchCall) {
                 uint32_t target_offset = GetTargetOffset(patch);
                 PatchCodeAddress(&patched_code_, patch.LiteralOffset(), target_offset);
@@ -1134,6 +1431,18 @@
     return type;
   }
 
+  uint32_t GetDexCacheOffset(const LinkerPatch& patch) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    if (writer_->image_writer_ != nullptr) {
+      auto* element = writer_->image_writer_->GetDexCacheArrayElementImageAddress(
+              patch.TargetDexCacheDexFile(), patch.TargetDexCacheElementOffset());
+      const uint8_t* oat_data = writer_->image_writer_->GetOatFileBegin() + file_offset_;
+      return reinterpret_cast<const uint8_t*>(element) - oat_data;
+    } else {
+      LOG(FATAL) << "Unimplemented.";
+      UNREACHABLE();
+    }
+  }
+
   void PatchObjectAddress(std::vector<uint8_t>* code, uint32_t offset, mirror::Object* object)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // NOTE: Direct method pointers across oat files don't use linker patches. However, direct
@@ -1472,6 +1781,7 @@
     DO_STAT(size_code_);
     DO_STAT(size_code_alignment_);
     DO_STAT(size_relative_call_thunks_);
+    DO_STAT(size_misc_thunks_);
     DO_STAT(size_mapping_table_);
     DO_STAT(size_vmap_table_);
     DO_STAT(size_gc_map_);
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index fd2ccae..676d628 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -21,7 +21,6 @@
 #include <cstddef>
 #include <memory>
 
-#include "driver/compiler_driver.h"
 #include "mem_map.h"
 #include "method_reference.h"
 #include "oat.h"
@@ -32,8 +31,10 @@
 
 class BitVector;
 class CompiledMethod;
+class CompilerDriver;
 class ImageWriter;
 class OutputStream;
+class TimingLogger;
 
 // OatHeader         variable length with count of D OatDexFiles
 //
@@ -312,6 +313,7 @@
   uint32_t size_code_;
   uint32_t size_code_alignment_;
   uint32_t size_relative_call_thunks_;
+  uint32_t size_misc_thunks_;
   uint32_t size_mapping_table_;
   uint32_t size_vmap_table_;
   uint32_t size_gc_map_;
@@ -325,51 +327,20 @@
   uint32_t size_oat_class_method_bitmaps_;
   uint32_t size_oat_class_method_offsets_;
 
-  class RelativeCallPatcher;
-  class NoRelativeCallPatcher;
-  class X86RelativeCallPatcher;
-  class ArmBaseRelativeCallPatcher;
-  class Thumb2RelativeCallPatcher;
-  class Arm64RelativeCallPatcher;
+  class RelativePatcher;
+  class NoRelativePatcher;
+  class X86RelativePatcher;
+  class ArmBaseRelativePatcher;
+  class Thumb2RelativePatcher;
+  class Arm64RelativePatcher;
 
-  std::unique_ptr<RelativeCallPatcher> relative_call_patcher_;
+  std::unique_ptr<RelativePatcher> relative_patcher_;
 
   // The locations of absolute patches relative to the start of the executable section.
   std::vector<uintptr_t> absolute_patch_locations_;
 
   SafeMap<MethodReference, uint32_t, MethodReferenceComparator> method_offset_map_;
 
-  struct CodeOffsetsKeyComparator {
-    bool operator()(const CompiledMethod* lhs, const CompiledMethod* rhs) const {
-      if (lhs->GetQuickCode() != rhs->GetQuickCode()) {
-        return lhs->GetQuickCode() < rhs->GetQuickCode();
-      }
-      // If the code is the same, all other fields are likely to be the same as well.
-      if (UNLIKELY(lhs->GetMappingTable() != rhs->GetMappingTable())) {
-        return lhs->GetMappingTable() < rhs->GetMappingTable();
-      }
-      if (UNLIKELY(lhs->GetVmapTable() != rhs->GetVmapTable())) {
-        return lhs->GetVmapTable() < rhs->GetVmapTable();
-      }
-      if (UNLIKELY(lhs->GetGcMap() != rhs->GetGcMap())) {
-        return lhs->GetGcMap() < rhs->GetGcMap();
-      }
-      const auto& lhs_patches = lhs->GetPatches();
-      const auto& rhs_patches = rhs->GetPatches();
-      if (UNLIKELY(lhs_patches.size() != rhs_patches.size())) {
-        return lhs_patches.size() < rhs_patches.size();
-      }
-      auto rit = rhs_patches.begin();
-      for (const LinkerPatch& lpatch : lhs_patches) {
-        if (UNLIKELY(!(lpatch == *rit))) {
-          return lpatch < *rit;
-        }
-        ++rit;
-      }
-      return false;
-    }
-  };
-
   DISALLOW_COPY_AND_ASSIGN(OatWriter);
 };
 
diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc
index ab77505..be432c5 100644
--- a/compiler/optimizing/boolean_simplifier.cc
+++ b/compiler/optimizing/boolean_simplifier.cc
@@ -59,7 +59,8 @@
       return new (allocator) HGreaterThan(lhs, rhs);
     } else if (cond->IsGreaterThan()) {
       return new (allocator) HLessThanOrEqual(lhs, rhs);
-    } else if (cond->IsGreaterThanOrEqual()) {
+    } else {
+      DCHECK(cond->IsGreaterThanOrEqual());
       return new (allocator) HLessThan(lhs, rhs);
     }
   } else if (cond->IsIntConstant()) {
@@ -70,10 +71,11 @@
       DCHECK(int_const->IsOne());
       return graph->GetIntConstant(0);
     }
+  } else {
+    // General case when 'cond' is another instruction of type boolean.
+    // Negate with 'cond == 0'.
+    return new (allocator) HEqual(cond, graph->GetIntConstant(0));
   }
-
-  // TODO: b/19992954
-  return nullptr;
 }
 
 void HBooleanSimplifier::Run() {
@@ -105,10 +107,6 @@
     HInstruction* replacement;
     if (NegatesCondition(true_value, false_value)) {
       replacement = GetOppositeCondition(if_condition);
-      if (replacement == nullptr) {
-        // Something we could not handle.
-        continue;
-      }
       if (replacement->GetBlock() == nullptr) {
         block->InsertInstructionBefore(replacement, if_instruction);
       }
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 2cdd5af..2da3176 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -23,6 +23,7 @@
 #include "dex_instruction.h"
 #include "dex_instruction-inl.h"
 #include "driver/compiler_driver-inl.h"
+#include "driver/compiler_options.h"
 #include "mirror/art_field.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/class_loader.h"
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 8d0ca0b..0d5fe49 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -3146,7 +3146,7 @@
   Location obj = locations->InAt(0);
 
   if (obj.IsRegister()) {
-    __ cmpl(obj.AsRegister<Register>(), Immediate(0));
+    __ testl(obj.AsRegister<Register>(), obj.AsRegister<Register>());
   } else if (obj.IsStackSlot()) {
     __ cmpl(Address(ESP, obj.GetStackIndex()), Immediate(0));
   } else {
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index b2f9c65..e474c49 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -26,11 +26,13 @@
 #include "bounds_check_elimination.h"
 #include "builder.h"
 #include "code_generator.h"
+#include "compiled_method.h"
 #include "compiler.h"
 #include "constant_folding.h"
 #include "dead_code_elimination.h"
 #include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
 #include "driver/dex_compilation_unit.h"
 #include "elf_writer_quick.h"
 #include "graph_visualizer.h"
diff --git a/compiler/optimizing/parallel_move_resolver.cc b/compiler/optimizing/parallel_move_resolver.cc
index 7d0641e..9df8f56 100644
--- a/compiler/optimizing/parallel_move_resolver.cc
+++ b/compiler/optimizing/parallel_move_resolver.cc
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <iostream>
 
 #include "parallel_move_resolver.h"
 #include "nodes.h"
@@ -63,39 +64,42 @@
   }
 }
 
+Location LowOf(Location location) {
+  if (location.IsRegisterPair()) {
+    return Location::RegisterLocation(location.low());
+  } else if (location.IsFpuRegisterPair()) {
+    return Location::FpuRegisterLocation(location.low());
+  } else if (location.IsDoubleStackSlot()) {
+    return Location::StackSlot(location.GetStackIndex());
+  } else {
+    return Location::NoLocation();
+  }
+}
+
+Location HighOf(Location location) {
+  if (location.IsRegisterPair()) {
+    return Location::RegisterLocation(location.high());
+  } else if (location.IsFpuRegisterPair()) {
+    return Location::FpuRegisterLocation(location.high());
+  } else if (location.IsDoubleStackSlot()) {
+    return Location::StackSlot(location.GetHighStackIndex(4));
+  } else {
+    return Location::NoLocation();
+  }
+}
+
 // Update the source of `move`, knowing that `updated_location` has been swapped
 // with `new_source`. Note that `updated_location` can be a pair, therefore if
 // `move` is non-pair, we need to extract which register to use.
 static void UpdateSourceOf(MoveOperands* move, Location updated_location, Location new_source) {
   Location source = move->GetSource();
-  if (new_source.GetKind() == source.GetKind()) {
-    DCHECK(updated_location.Equals(source));
+  if (LowOf(updated_location).Equals(source)) {
+    move->SetSource(LowOf(new_source));
+  } else if (HighOf(updated_location).Equals(source)) {
+    move->SetSource(HighOf(new_source));
+  } else {
+    DCHECK(updated_location.Equals(source)) << updated_location << " " << source;
     move->SetSource(new_source);
-  } else if (new_source.IsStackSlot()
-             || new_source.IsDoubleStackSlot()
-             || source.IsStackSlot()
-             || source.IsDoubleStackSlot()) {
-    // Stack slots never take part of a pair/non-pair swap.
-    DCHECK(updated_location.Equals(source));
-    move->SetSource(new_source);
-  } else if (source.IsRegister()) {
-    DCHECK(new_source.IsRegisterPair()) << new_source;
-    DCHECK(updated_location.IsRegisterPair()) << updated_location;
-    if (updated_location.low() == source.reg()) {
-      move->SetSource(Location::RegisterLocation(new_source.low()));
-    } else {
-      DCHECK_EQ(updated_location.high(), source.reg());
-      move->SetSource(Location::RegisterLocation(new_source.high()));
-    }
-  } else if (source.IsFpuRegister()) {
-    DCHECK(new_source.IsFpuRegisterPair()) << new_source;
-    DCHECK(updated_location.IsFpuRegisterPair()) << updated_location;
-    if (updated_location.low() == source.reg()) {
-      move->SetSource(Location::FpuRegisterLocation(new_source.low()));
-    } else {
-      DCHECK_EQ(updated_location.high(), source.reg());
-      move->SetSource(Location::FpuRegisterLocation(new_source.high()));
-    }
   }
 }
 
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index 817a44b..5c502f7 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -31,8 +31,13 @@
       message_ << "C";
     } else if (location.IsPair()) {
       message_ << location.low() << "," << location.high();
-    } else {
+    } else if (location.IsRegister()) {
       message_ << location.reg();
+    } else if (location.IsStackSlot()) {
+      message_ << location.GetStackIndex() << "(sp)";
+    } else {
+      message_ << "2x" << location.GetStackIndex() << "(sp)";
+      DCHECK(location.IsDoubleStackSlot()) << location;
     }
   }
 
@@ -279,6 +284,26 @@
     resolver.EmitNativeCode(moves);
     ASSERT_STREQ("(0,1 <-> 2,3)", resolver.GetMessage().c_str());
   }
+
+  {
+    // Test involving registers used in single context and pair context.
+    TestParallelMoveResolver resolver(&allocator);
+    HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+    moves->AddMove(
+        Location::RegisterLocation(10),
+        Location::RegisterLocation(5),
+        nullptr);
+    moves->AddMove(
+        Location::RegisterPairLocation(4, 5),
+        Location::DoubleStackSlot(32),
+        nullptr);
+    moves->AddMove(
+        Location::DoubleStackSlot(32),
+        Location::RegisterPairLocation(10, 11),
+        nullptr);
+    resolver.EmitNativeCode(moves);
+    ASSERT_STREQ("(2x32(sp) <-> 10,11) (4,5 <-> 2x32(sp)) (4 -> 5)", resolver.GetMessage().c_str());
+  }
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index cf38bd3..4bca434 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -1408,26 +1408,36 @@
 
     // Walk over all uses covered by this interval, and update the location
     // information.
-    while (use != nullptr && use->GetPosition() <= current->GetEnd()) {
-      LocationSummary* locations = use->GetUser()->GetLocations();
-      if (use->GetIsEnvironment()) {
-        locations->SetEnvironmentAt(use->GetInputIndex(), source);
-      } else {
-        Location expected_location = locations->InAt(use->GetInputIndex());
-        // The expected (actual) location may be invalid in case the input is unused. Currently
-        // this only happens for intrinsics.
-        if (expected_location.IsValid()) {
-          if (expected_location.IsUnallocated()) {
-            locations->SetInAt(use->GetInputIndex(), source);
-          } else if (!expected_location.IsConstant()) {
-            AddInputMoveFor(interval->GetDefinedBy(), use->GetUser(), source, expected_location);
-          }
-        } else {
-          DCHECK(use->GetUser()->IsInvoke());
-          DCHECK(use->GetUser()->AsInvoke()->GetIntrinsic() != Intrinsics::kNone);
-        }
+
+    LiveRange* range = current->GetFirstRange();
+    while (range != nullptr) {
+      while (use != nullptr && use->GetPosition() < range->GetStart()) {
+        DCHECK(use->GetIsEnvironment());
+        use = use->GetNext();
       }
-      use = use->GetNext();
+      while (use != nullptr && use->GetPosition() <= range->GetEnd()) {
+        DCHECK(current->Covers(use->GetPosition()) || (use->GetPosition() == range->GetEnd()));
+        LocationSummary* locations = use->GetUser()->GetLocations();
+        if (use->GetIsEnvironment()) {
+          locations->SetEnvironmentAt(use->GetInputIndex(), source);
+        } else {
+          Location expected_location = locations->InAt(use->GetInputIndex());
+          // The expected (actual) location may be invalid in case the input is unused. Currently
+          // this only happens for intrinsics.
+          if (expected_location.IsValid()) {
+            if (expected_location.IsUnallocated()) {
+              locations->SetInAt(use->GetInputIndex(), source);
+            } else if (!expected_location.IsConstant()) {
+              AddInputMoveFor(interval->GetDefinedBy(), use->GetUser(), source, expected_location);
+            }
+          } else {
+            DCHECK(use->GetUser()->IsInvoke());
+            DCHECK(use->GetUser()->AsInvoke()->GetIntrinsic() != Intrinsics::kNone);
+          }
+        }
+        use = use->GetNext();
+      }
+      range = range->GetNext();
     }
 
     // If the next interval starts just after this one, and has a register,
@@ -1503,7 +1513,15 @@
     }
     current = next_sibling;
   } while (current != nullptr);
-  DCHECK(use == nullptr);
+
+  if (kIsDebugBuild) {
+    // Following uses can only be environment uses. The location for
+    // these environments will be none.
+    while (use != nullptr) {
+      DCHECK(use->GetIsEnvironment());
+      use = use->GetNext();
+    }
+  }
 }
 
 void RegisterAllocator::ConnectSplitSiblings(LiveInterval* interval,
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 0f3973e..95da6ef 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -218,6 +218,26 @@
         current->GetLiveInterval()->SetFrom(current->GetLifetimePosition());
       }
 
+      // Process the environment first, because we know their uses come after
+      // or at the same liveness position of inputs.
+      if (current->HasEnvironment()) {
+        // Handle environment uses. See statements (b) and (c) of the
+        // SsaLivenessAnalysis.
+        HEnvironment* environment = current->GetEnvironment();
+        for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+          HInstruction* instruction = environment->GetInstructionAt(i);
+          bool should_be_live = ShouldBeLiveForEnvironment(instruction);
+          if (should_be_live) {
+            DCHECK(instruction->HasSsaIndex());
+            live_in->SetBit(instruction->GetSsaIndex());
+          }
+          if (instruction != nullptr) {
+            instruction->GetLiveInterval()->AddUse(
+                current, i, /* is_environment */ true, should_be_live);
+          }
+        }
+      }
+
       // All inputs of an instruction must be live.
       for (size_t i = 0, e = current->InputCount(); i < e; ++i) {
         HInstruction* input = current->InputAt(i);
@@ -225,21 +245,7 @@
         // to be materialized.
         if (input->HasSsaIndex()) {
           live_in->SetBit(input->GetSsaIndex());
-          input->GetLiveInterval()->AddUse(current, i, false);
-        }
-      }
-
-      if (current->HasEnvironment()) {
-        // Handle environment uses. See statements (b) and (c) of the
-        // SsaLivenessAnalysis.
-        HEnvironment* environment = current->GetEnvironment();
-        for (size_t i = 0, e = environment->Size(); i < e; ++i) {
-          HInstruction* instruction = environment->GetInstructionAt(i);
-          if (ShouldBeLiveForEnvironment(instruction)) {
-            DCHECK(instruction->HasSsaIndex());
-            live_in->SetBit(instruction->GetSsaIndex());
-            instruction->GetLiveInterval()->AddUse(current, i, true);
-          }
+          input->GetLiveInterval()->AddUse(current, i, /* is_environment */ false);
         }
       }
     }
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index bc78dc2..d2da84c 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -189,7 +189,10 @@
     AddRange(position, position + 1);
   }
 
-  void AddUse(HInstruction* instruction, size_t input_index, bool is_environment) {
+  void AddUse(HInstruction* instruction,
+              size_t input_index,
+              bool is_environment,
+              bool keep_alive = false) {
     // Set the use within the instruction.
     size_t position = instruction->GetLifetimePosition() + 1;
     LocationSummary* locations = instruction->GetLocations();
@@ -211,6 +214,7 @@
         && (first_use_->GetPosition() < position)) {
       // The user uses the instruction multiple times, and one use dies before the other.
       // We update the use list so that the latter is first.
+      DCHECK(!is_environment);
       UsePosition* cursor = first_use_;
       while ((cursor->GetNext() != nullptr) && (cursor->GetNext()->GetPosition() < position)) {
         cursor = cursor->GetNext();
@@ -225,6 +229,15 @@
       return;
     }
 
+    first_use_ = new (allocator_) UsePosition(
+        instruction, input_index, is_environment, position, first_use_);
+
+    if (is_environment && !keep_alive) {
+      // If this environment use does not keep the instruction live, it does not
+      // affect the live range of that instruction.
+      return;
+    }
+
     size_t start_block_position = instruction->GetBlock()->GetLifetimeStart();
     if (first_range_ == nullptr) {
       // First time we see a use of that interval.
@@ -246,8 +259,6 @@
       // and the check line 205 would succeed.
       first_range_ = new (allocator_) LiveRange(start_block_position, position, first_range_);
     }
-    first_use_ = new (allocator_) UsePosition(
-        instruction, input_index, is_environment, position, first_use_);
   }
 
   void AddPhiUse(HInstruction* instruction, size_t input_index, HBasicBlock* block) {
@@ -425,9 +436,11 @@
     UsePosition* use = first_use_;
     size_t end = GetEnd();
     while (use != nullptr && use->GetPosition() <= end) {
-      size_t use_position = use->GetPosition();
-      if (use_position > position) {
-        return use_position;
+      if (!use->GetIsEnvironment()) {
+        size_t use_position = use->GetPosition();
+        if (use_position > position) {
+          return use_position;
+        }
       }
       use = use->GetNext();
     }
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 5818a37..a73c8d7 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -27,6 +27,32 @@
 
 namespace art {
 
+// Helper to build art::StackMapStream::LocationCatalogEntriesIndices.
+class LocationCatalogEntriesIndicesEmptyFn {
+ public:
+  void MakeEmpty(std::pair<DexRegisterLocation, size_t>& item) const {
+    item.first = DexRegisterLocation::None();
+  }
+  bool IsEmpty(const std::pair<DexRegisterLocation, size_t>& item) const {
+    return item.first == DexRegisterLocation::None();
+  }
+};
+
+// Hash function for art::StackMapStream::LocationCatalogEntriesIndices.
+// This hash function does not create collisions.
+class DexRegisterLocationHashFn {
+ public:
+  size_t operator()(DexRegisterLocation key) const {
+    // Concatenate `key`s fields to create a 64-bit value to be hashed.
+    int64_t kind_and_value =
+        (static_cast<int64_t>(key.kind_) << 32) | static_cast<int64_t>(key.value_);
+    return inner_hash_fn_(kind_and_value);
+  }
+ private:
+  std::hash<int64_t> inner_hash_fn_;
+};
+
+
 /**
  * Collects and builds stack maps for a method. All the stack maps
  * for a method are placed in a CodeInfo object.
@@ -36,11 +62,13 @@
   explicit StackMapStream(ArenaAllocator* allocator)
       : allocator_(allocator),
         stack_maps_(allocator, 10),
+        location_catalog_entries_(allocator, 4),
         dex_register_locations_(allocator, 10 * 4),
         inline_infos_(allocator, 2),
         stack_mask_max_(-1),
         dex_pc_max_(0),
         native_pc_offset_max_(0),
+        register_mask_max_(0),
         number_of_stack_maps_with_inline_info_(0),
         dex_map_hash_to_stack_map_indices_(std::less<uint32_t>(), allocator->Adapter()) {}
 
@@ -101,6 +129,7 @@
 
     dex_pc_max_ = std::max(dex_pc_max_, dex_pc);
     native_pc_offset_max_ = std::max(native_pc_offset_max_, native_pc_offset);
+    register_mask_max_ = std::max(register_mask_max_, register_mask);
   }
 
   void AddInlineInfoEntry(uint32_t method_index) {
@@ -111,6 +140,7 @@
 
   size_t ComputeNeededSize() {
     size_t size = CodeInfo::kFixedSize
+        + ComputeDexRegisterLocationCatalogSize()
         + ComputeStackMapsSize()
         + ComputeDexRegisterMapsSize()
         + ComputeInlineInfoSize();
@@ -128,24 +158,43 @@
         ComputeInlineInfoSize(),
         ComputeDexRegisterMapsSize(),
         dex_pc_max_,
-        native_pc_offset_max_);
+        native_pc_offset_max_,
+        register_mask_max_);
   }
 
-  // Compute the size of the Dex register map of `entry`.
+  // Compute the size of the Dex register location catalog of `entry`.
+  size_t ComputeDexRegisterLocationCatalogSize() const {
+    size_t size = DexRegisterLocationCatalog::kFixedSize;
+    for (size_t location_catalog_entry_index = 0;
+         location_catalog_entry_index < location_catalog_entries_.Size();
+         ++location_catalog_entry_index) {
+      DexRegisterLocation dex_register_location =
+          location_catalog_entries_.Get(location_catalog_entry_index);
+      size += DexRegisterLocationCatalog::EntrySize(dex_register_location);
+    }
+    return size;
+  }
+
   size_t ComputeDexRegisterMapSize(const StackMapEntry& entry) const {
+    // Size of the map in bytes.
     size_t size = DexRegisterMap::kFixedSize;
-    // Add the bit mask for the dex register liveness.
-    size += DexRegisterMap::LiveBitMaskSize(entry.num_dex_registers);
-    for (size_t dex_register_number = 0, index_in_dex_register_locations = 0;
+    // Add the live bit mask for the Dex register liveness.
+    size += DexRegisterMap::GetLiveBitMaskSize(entry.num_dex_registers);
+    // Compute the size of the set of live Dex register entries.
+    size_t number_of_live_dex_registers = 0;
+    for (size_t dex_register_number = 0;
          dex_register_number < entry.num_dex_registers;
          ++dex_register_number) {
       if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) {
-        DexRegisterLocation dex_register_location = dex_register_locations_.Get(
-            entry.dex_register_locations_start_index + index_in_dex_register_locations);
-        size += DexRegisterMap::EntrySize(dex_register_location);
-        index_in_dex_register_locations++;
+        ++number_of_live_dex_registers;
       }
     }
+    size_t map_entries_size_in_bits =
+        DexRegisterMap::SingleEntrySizeInBits(location_catalog_entries_.Size())
+        * number_of_live_dex_registers;
+    size_t map_entries_size_in_bytes =
+        RoundUp(map_entries_size_in_bits, kBitsPerByte) / kBitsPerByte;
+    size += map_entries_size_in_bytes;
     return size;
   }
 
@@ -168,8 +217,16 @@
       + (number_of_stack_maps_with_inline_info_ * InlineInfo::kFixedSize);
   }
 
+  size_t ComputeDexRegisterLocationCatalogStart() const {
+    return CodeInfo::kFixedSize;
+  }
+
+  size_t ComputeStackMapsStart() const {
+    return ComputeDexRegisterLocationCatalogStart() + ComputeDexRegisterLocationCatalogSize();
+  }
+
   size_t ComputeDexRegisterMapsStart() {
-    return CodeInfo::kFixedSize + ComputeStackMapsSize();
+    return ComputeStackMapsStart() + ComputeStackMapsSize();
   }
 
   size_t ComputeInlineInfoStart() {
@@ -194,11 +251,32 @@
       ComputeInlineInfoStart(),
       inline_info_size);
 
-    code_info.SetEncoding(
-        inline_info_size, dex_register_map_size, dex_pc_max_, native_pc_offset_max_);
+    code_info.SetEncoding(inline_info_size,
+                          dex_register_map_size,
+                          dex_pc_max_,
+                          native_pc_offset_max_,
+                          register_mask_max_);
     code_info.SetNumberOfStackMaps(stack_maps_.Size());
     code_info.SetStackMaskSize(stack_mask_size);
-    DCHECK_EQ(code_info.StackMapsSize(), ComputeStackMapsSize());
+    DCHECK_EQ(code_info.GetStackMapsSize(), ComputeStackMapsSize());
+
+    // Set the Dex register location catalog.
+    code_info.SetNumberOfDexRegisterLocationCatalogEntries(
+        location_catalog_entries_.Size());
+    MemoryRegion dex_register_location_catalog_region = region.Subregion(
+        ComputeDexRegisterLocationCatalogStart(),
+        ComputeDexRegisterLocationCatalogSize());
+    DexRegisterLocationCatalog dex_register_location_catalog(dex_register_location_catalog_region);
+    // Offset in `dex_register_location_catalog` where to store the next
+    // register location.
+    size_t location_catalog_offset = DexRegisterLocationCatalog::kFixedSize;
+    for (size_t i = 0, e = location_catalog_entries_.Size(); i < e; ++i) {
+      DexRegisterLocation dex_register_location = location_catalog_entries_.Get(i);
+      dex_register_location_catalog.SetRegisterInfo(location_catalog_offset, dex_register_location);
+      location_catalog_offset += DexRegisterLocationCatalog::EntrySize(dex_register_location);
+    }
+    // Ensure we reached the end of the Dex registers location_catalog.
+    DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size());
 
     uintptr_t next_dex_register_map_offset = 0;
     uintptr_t next_inline_info_offset = 0;
@@ -234,25 +312,25 @@
           stack_map.SetDexRegisterMapOffset(
             code_info, register_region.start() - dex_register_locations_region.start());
 
-          // Offset in `dex_register_map` where to store the next register entry.
-          size_t offset = DexRegisterMap::kFixedSize;
-          dex_register_map.SetLiveBitMask(offset,
-                                          entry.num_dex_registers,
-                                          *entry.live_dex_registers_mask);
-          offset += DexRegisterMap::LiveBitMaskSize(entry.num_dex_registers);
+          // Set the live bit mask.
+          dex_register_map.SetLiveBitMask(entry.num_dex_registers, *entry.live_dex_registers_mask);
+
+          // Set the dex register location mapping data.
           for (size_t dex_register_number = 0, index_in_dex_register_locations = 0;
                dex_register_number < entry.num_dex_registers;
                ++dex_register_number) {
             if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) {
-              DexRegisterLocation dex_register_location = dex_register_locations_.Get(
-                  entry.dex_register_locations_start_index + index_in_dex_register_locations);
-              dex_register_map.SetRegisterInfo(offset, dex_register_location);
-              offset += DexRegisterMap::EntrySize(dex_register_location);
+              size_t location_catalog_entry_index =
+                  dex_register_locations_.Get(entry.dex_register_locations_start_index
+                                              + index_in_dex_register_locations);
+              dex_register_map.SetLocationCatalogEntryIndex(
+                  index_in_dex_register_locations,
+                  location_catalog_entry_index,
+                  entry.num_dex_registers,
+                  location_catalog_entries_.Size());
               ++index_in_dex_register_locations;
             }
           }
-          // Ensure we reached the end of the Dex registers region.
-          DCHECK_EQ(offset, register_region.size());
         }
       }
 
@@ -282,12 +360,31 @@
   }
 
   void AddDexRegisterEntry(uint16_t dex_register, DexRegisterLocation::Kind kind, int32_t value) {
+    StackMapEntry entry = stack_maps_.Get(stack_maps_.Size() - 1);
+    DCHECK_LT(dex_register, entry.num_dex_registers);
+
     if (kind != DexRegisterLocation::Kind::kNone) {
       // Ensure we only use non-compressed location kind at this stage.
       DCHECK(DexRegisterLocation::IsShortLocationKind(kind))
           << DexRegisterLocation::PrettyDescriptor(kind);
-      dex_register_locations_.Add(DexRegisterLocation(kind, value));
-      StackMapEntry entry = stack_maps_.Get(stack_maps_.Size() - 1);
+      DexRegisterLocation location(kind, value);
+
+      // Look for Dex register `location` in the location catalog (using the
+      // companion hash map of locations to indices).  Use its index if it
+      // is already in the location catalog.  If not, insert it (in the
+      // location catalog and the hash map) and use the newly created index.
+      auto it = location_catalog_entries_indices_.Find(location);
+      if (it != location_catalog_entries_indices_.end()) {
+        // Retrieve the index from the hash map.
+        dex_register_locations_.Add(it->second);
+      } else {
+        // Create a new entry in the location catalog and the hash map.
+        size_t index = location_catalog_entries_.Size();
+        location_catalog_entries_.Add(location);
+        dex_register_locations_.Add(index);
+        location_catalog_entries_indices_.Insert(std::make_pair(location, index));
+      }
+
       entry.live_dex_registers_mask->SetBit(dex_register);
       entry.dex_register_map_hash += (1 << dex_register);
       entry.dex_register_map_hash += static_cast<uint32_t>(value);
@@ -354,9 +451,9 @@
         return false;
       }
       if (a.live_dex_registers_mask->IsBitSet(i)) {
-        DexRegisterLocation a_loc = dex_register_locations_.Get(
+        size_t a_loc = dex_register_locations_.Get(
             a.dex_register_locations_start_index + index_in_dex_register_locations);
-        DexRegisterLocation b_loc = dex_register_locations_.Get(
+        size_t b_loc = dex_register_locations_.Get(
             b.dex_register_locations_start_index + index_in_dex_register_locations);
         if (a_loc != b_loc) {
           return false;
@@ -369,21 +466,29 @@
 
   ArenaAllocator* allocator_;
   GrowableArray<StackMapEntry> stack_maps_;
-  GrowableArray<DexRegisterLocation> dex_register_locations_;
+
+  // A catalog of unique [location_kind, register_value] pairs (per method).
+  GrowableArray<DexRegisterLocation> location_catalog_entries_;
+  // Map from Dex register location catalog entries to their indices in the
+  // location catalog.
+  typedef HashMap<DexRegisterLocation, size_t, LocationCatalogEntriesIndicesEmptyFn,
+                  DexRegisterLocationHashFn> LocationCatalogEntriesIndices;
+  LocationCatalogEntriesIndices location_catalog_entries_indices_;
+
+  // A set of concatenated maps of Dex register locations indices to
+  // `location_catalog_entries_`.
+  GrowableArray<size_t> dex_register_locations_;
   GrowableArray<InlineInfoEntry> inline_infos_;
   int stack_mask_max_;
   uint32_t dex_pc_max_;
   uint32_t native_pc_offset_max_;
+  uint32_t register_mask_max_;
   size_t number_of_stack_maps_with_inline_info_;
 
   ArenaSafeMap<uint32_t, GrowableArray<uint32_t>> dex_map_hash_to_stack_map_indices_;
 
   static constexpr uint32_t kNoSameDexMapFound = -1;
 
-  ART_FRIEND_TEST(StackMapTest, Test1);
-  ART_FRIEND_TEST(StackMapTest, Test2);
-  ART_FRIEND_TEST(StackMapTest, TestNonLiveDexRegisters);
-
   DISALLOW_COPY_AND_ASSIGN(StackMapStream);
 };
 
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index e5a9790..8d160bc 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -31,6 +31,8 @@
   return true;
 }
 
+using Kind = DexRegisterLocation::Kind;
+
 TEST(StackMapTest, Test1) {
   ArenaPool pool;
   ArenaAllocator arena(&pool);
@@ -39,8 +41,8 @@
   ArenaBitVector sp_mask(&arena, 0, false);
   size_t number_of_dex_registers = 2;
   stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
-  stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInStack, 0);
-  stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2);
+  stream.AddDexRegisterEntry(0, Kind::kInStack, 0);         // Short location.
+  stream.AddDexRegisterEntry(1, Kind::kConstant, -2);       // Short location.
 
   size_t size = stream.ComputeNeededSize();
   void* memory = arena.Alloc(size, kArenaAllocMisc);
@@ -51,6 +53,16 @@
   ASSERT_EQ(0u, code_info.GetStackMaskSize());
   ASSERT_EQ(1u, code_info.GetNumberOfStackMaps());
 
+  uint32_t number_of_location_catalog_entries =
+      code_info.GetNumberOfDexRegisterLocationCatalogEntries();
+  ASSERT_EQ(2u, number_of_location_catalog_entries);
+  DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog();
+  // The Dex register location catalog contains:
+  // - one 1-byte short Dex register location, and
+  // - one 5-byte large Dex register location.
+  size_t expected_location_catalog_size = 1u + 5u;
+  ASSERT_EQ(expected_location_catalog_size, location_catalog.Size());
+
   StackMap stack_map = code_info.GetStackMapAt(0);
   ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
   ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
@@ -62,14 +74,40 @@
   ASSERT_TRUE(SameBits(stack_mask, sp_mask));
 
   ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info));
-  DexRegisterMap dex_registers = code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
-  ASSERT_EQ(7u, dex_registers.Size());
-  DexRegisterLocation location0 = dex_registers.GetLocationKindAndValue(0, number_of_dex_registers);
-  DexRegisterLocation location1 = dex_registers.GetLocationKindAndValue(1, number_of_dex_registers);
-  ASSERT_EQ(DexRegisterLocation::Kind::kInStack, location0.GetKind());
-  ASSERT_EQ(DexRegisterLocation::Kind::kConstant, location1.GetKind());
-  ASSERT_EQ(DexRegisterLocation::Kind::kInStack, location0.GetInternalKind());
-  ASSERT_EQ(DexRegisterLocation::Kind::kConstantLargeValue, location1.GetInternalKind());
+  DexRegisterMap dex_register_map =
+      code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
+  ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0));
+  ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
+  ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
+  // The Dex register map contains:
+  // - one 1-byte live bit mask, and
+  // - one 1-byte set of location catalog entry indices composed of two 2-bit values.
+  size_t expected_dex_register_map_size = 1u + 1u;
+  ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size());
+
+  ASSERT_EQ(Kind::kInStack,
+            dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info));
+  ASSERT_EQ(Kind::kConstant,
+            dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info));
+  ASSERT_EQ(Kind::kInStack,
+            dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info));
+  ASSERT_EQ(Kind::kConstantLargeValue,
+            dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info));
+  ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info));
+  ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info));
+
+  size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(
+      0, number_of_dex_registers, number_of_location_catalog_entries);
+  size_t index1 = dex_register_map.GetLocationCatalogEntryIndex(
+      1, number_of_dex_registers, number_of_location_catalog_entries);
+  ASSERT_EQ(0u, index0);
+  ASSERT_EQ(1u, index1);
+  DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0);
+  DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1);
+  ASSERT_EQ(Kind::kInStack, location0.GetKind());
+  ASSERT_EQ(Kind::kConstant, location1.GetKind());
+  ASSERT_EQ(Kind::kInStack, location0.GetInternalKind());
+  ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind());
   ASSERT_EQ(0, location0.GetValue());
   ASSERT_EQ(-2, location1.GetValue());
 
@@ -86,8 +124,8 @@
   sp_mask1.SetBit(4);
   size_t number_of_dex_registers = 2;
   stream.AddStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 2);
-  stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInStack, 0);
-  stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2);
+  stream.AddDexRegisterEntry(0, Kind::kInStack, 0);         // Short location.
+  stream.AddDexRegisterEntry(1, Kind::kConstant, -2);       // Large location.
   stream.AddInlineInfoEntry(42);
   stream.AddInlineInfoEntry(82);
 
@@ -95,8 +133,8 @@
   sp_mask2.SetBit(3);
   sp_mask1.SetBit(8);
   stream.AddStackMapEntry(1, 128, 0xFF, &sp_mask2, number_of_dex_registers, 0);
-  stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 18);
-  stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kInFpuRegister, 3);
+  stream.AddDexRegisterEntry(0, Kind::kInRegister, 18);     // Short location.
+  stream.AddDexRegisterEntry(1, Kind::kInFpuRegister, 3);   // Short location.
 
   size_t size = stream.ComputeNeededSize();
   void* memory = arena.Alloc(size, kArenaAllocMisc);
@@ -107,6 +145,16 @@
   ASSERT_EQ(1u, code_info.GetStackMaskSize());
   ASSERT_EQ(2u, code_info.GetNumberOfStackMaps());
 
+  uint32_t number_of_location_catalog_entries =
+      code_info.GetNumberOfDexRegisterLocationCatalogEntries();
+  ASSERT_EQ(4u, number_of_location_catalog_entries);
+  DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog();
+  // The Dex register location catalog contains:
+  // - three 1-byte short Dex register locations, and
+  // - one 5-byte large Dex register location.
+  size_t expected_location_catalog_size = 3u * 1u + 5u;
+  ASSERT_EQ(expected_location_catalog_size, location_catalog.Size());
+
   // First stack map.
   {
     StackMap stack_map = code_info.GetStackMapAt(0);
@@ -120,17 +168,40 @@
     ASSERT_TRUE(SameBits(stack_mask, sp_mask1));
 
     ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info));
-    DexRegisterMap dex_registers =
+    DexRegisterMap dex_register_map =
         code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
-    ASSERT_EQ(7u, dex_registers.Size());
-    DexRegisterLocation location0 =
-        dex_registers.GetLocationKindAndValue(0, number_of_dex_registers);
-    DexRegisterLocation location1 =
-        dex_registers.GetLocationKindAndValue(1, number_of_dex_registers);
-    ASSERT_EQ(DexRegisterLocation::Kind::kInStack, location0.GetKind());
-    ASSERT_EQ(DexRegisterLocation::Kind::kConstant, location1.GetKind());
-    ASSERT_EQ(DexRegisterLocation::Kind::kInStack, location0.GetInternalKind());
-    ASSERT_EQ(DexRegisterLocation::Kind::kConstantLargeValue, location1.GetInternalKind());
+    ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0));
+    ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
+    ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
+    // The Dex register map contains:
+    // - one 1-byte live bit mask, and
+    // - one 1-byte set of location catalog entry indices composed of two 2-bit values.
+    size_t expected_dex_register_map_size = 1u + 1u;
+    ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size());
+
+    ASSERT_EQ(Kind::kInStack,
+              dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info));
+    ASSERT_EQ(Kind::kConstant,
+              dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info));
+    ASSERT_EQ(Kind::kInStack,
+              dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info));
+    ASSERT_EQ(Kind::kConstantLargeValue,
+              dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info));
+    ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info));
+    ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info));
+
+    size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(
+        0, number_of_dex_registers, number_of_location_catalog_entries);
+    size_t index1 = dex_register_map.GetLocationCatalogEntryIndex(
+        1, number_of_dex_registers, number_of_location_catalog_entries);
+    ASSERT_EQ(0u, index0);
+    ASSERT_EQ(1u, index1);
+    DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0);
+    DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1);
+    ASSERT_EQ(Kind::kInStack, location0.GetKind());
+    ASSERT_EQ(Kind::kConstant, location1.GetKind());
+    ASSERT_EQ(Kind::kInStack, location0.GetInternalKind());
+    ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind());
     ASSERT_EQ(0, location0.GetValue());
     ASSERT_EQ(-2, location1.GetValue());
 
@@ -154,17 +225,40 @@
     ASSERT_TRUE(SameBits(stack_mask, sp_mask2));
 
     ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info));
-    DexRegisterMap dex_registers =
+    DexRegisterMap dex_register_map =
         code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
-    ASSERT_EQ(3u, dex_registers.Size());
-    DexRegisterLocation location0 =
-        dex_registers.GetLocationKindAndValue(0, number_of_dex_registers);
-    DexRegisterLocation location1 =
-        dex_registers.GetLocationKindAndValue(1, number_of_dex_registers);
-    ASSERT_EQ(DexRegisterLocation::Kind::kInRegister, location0.GetKind());
-    ASSERT_EQ(DexRegisterLocation::Kind::kInFpuRegister, location1.GetKind());
-    ASSERT_EQ(DexRegisterLocation::Kind::kInRegister, location0.GetInternalKind());
-    ASSERT_EQ(DexRegisterLocation::Kind::kInFpuRegister, location1.GetInternalKind());
+    ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0));
+    ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
+    ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
+    // The Dex register map contains:
+    // - one 1-byte live bit mask, and
+    // - one 1-byte set of location catalog entry indices composed of two 2-bit values.
+    size_t expected_dex_register_map_size = 1u + 1u;
+    ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size());
+
+    ASSERT_EQ(Kind::kInRegister,
+              dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info));
+    ASSERT_EQ(Kind::kInFpuRegister,
+              dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info));
+    ASSERT_EQ(Kind::kInRegister,
+              dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info));
+    ASSERT_EQ(Kind::kInFpuRegister,
+              dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info));
+    ASSERT_EQ(18, dex_register_map.GetMachineRegister(0, number_of_dex_registers, code_info));
+    ASSERT_EQ(3, dex_register_map.GetMachineRegister(1, number_of_dex_registers, code_info));
+
+    size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(
+        0, number_of_dex_registers, number_of_location_catalog_entries);
+    size_t index1 = dex_register_map.GetLocationCatalogEntryIndex(
+        1, number_of_dex_registers, number_of_location_catalog_entries);
+    ASSERT_EQ(2u, index0);
+    ASSERT_EQ(3u, index1);
+    DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0);
+    DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1);
+    ASSERT_EQ(Kind::kInRegister, location0.GetKind());
+    ASSERT_EQ(Kind::kInFpuRegister, location1.GetKind());
+    ASSERT_EQ(Kind::kInRegister, location0.GetInternalKind());
+    ASSERT_EQ(Kind::kInFpuRegister, location1.GetInternalKind());
     ASSERT_EQ(18, location0.GetValue());
     ASSERT_EQ(3, location1.GetValue());
 
@@ -180,8 +274,8 @@
   ArenaBitVector sp_mask(&arena, 0, false);
   uint32_t number_of_dex_registers = 2;
   stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
-  stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kNone, 0);
-  stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2);
+  stream.AddDexRegisterEntry(0, Kind::kNone, 0);            // No location.
+  stream.AddDexRegisterEntry(1, Kind::kConstant, -2);       // Large location.
 
   size_t size = stream.ComputeNeededSize();
   void* memory = arena.Alloc(size, kArenaAllocMisc);
@@ -189,14 +283,62 @@
   stream.FillIn(region);
 
   CodeInfo code_info(region);
+  ASSERT_EQ(0u, code_info.GetStackMaskSize());
+  ASSERT_EQ(1u, code_info.GetNumberOfStackMaps());
+
+  uint32_t number_of_location_catalog_entries =
+      code_info.GetNumberOfDexRegisterLocationCatalogEntries();
+  ASSERT_EQ(1u, number_of_location_catalog_entries);
+  DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog();
+  // The Dex register location catalog contains:
+  // - one 5-byte large Dex register location.
+  size_t expected_location_catalog_size = 5u;
+  ASSERT_EQ(expected_location_catalog_size, location_catalog.Size());
+
   StackMap stack_map = code_info.GetStackMapAt(0);
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
+  ASSERT_EQ(0u, stack_map.GetDexPc(code_info));
+  ASSERT_EQ(64u, stack_map.GetNativePcOffset(code_info));
+  ASSERT_EQ(0x3u, stack_map.GetRegisterMask(code_info));
+
   ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info));
-  DexRegisterMap dex_registers = code_info.GetDexRegisterMapOf(stack_map, 2);
-  ASSERT_EQ(DexRegisterLocation::Kind::kNone,
-            dex_registers.GetLocationKind(0, number_of_dex_registers));
-  ASSERT_EQ(DexRegisterLocation::Kind::kConstant,
-            dex_registers.GetLocationKind(1, number_of_dex_registers));
-  ASSERT_EQ(-2, dex_registers.GetConstant(1, number_of_dex_registers));
+  DexRegisterMap dex_register_map =
+      code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
+  ASSERT_FALSE(dex_register_map.IsDexRegisterLive(0));
+  ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
+  ASSERT_EQ(1u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
+  // The Dex register map contains:
+  // - one 1-byte live bit mask.
+  // No space is allocated for the sole location catalog entry index, as it is useless.
+  size_t expected_dex_register_map_size = 1u + 0u;
+  ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size());
+
+  ASSERT_EQ(Kind::kNone,
+            dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info));
+  ASSERT_EQ(Kind::kConstant,
+            dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info));
+  ASSERT_EQ(Kind::kNone,
+            dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info));
+  ASSERT_EQ(Kind::kConstantLargeValue,
+            dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info));
+  ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info));
+
+  size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(
+      0, number_of_dex_registers, number_of_location_catalog_entries);
+  size_t index1 =  dex_register_map.GetLocationCatalogEntryIndex(
+      1, number_of_dex_registers, number_of_location_catalog_entries);
+  ASSERT_EQ(DexRegisterLocationCatalog::kNoLocationEntryIndex, index0);
+  ASSERT_EQ(0u, index1);
+  DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0);
+  DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1);
+  ASSERT_EQ(Kind::kNone, location0.GetKind());
+  ASSERT_EQ(Kind::kConstant, location1.GetKind());
+  ASSERT_EQ(Kind::kNone, location0.GetInternalKind());
+  ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind());
+  ASSERT_EQ(0, location0.GetValue());
+  ASSERT_EQ(-2, location1.GetValue());
+
   ASSERT_FALSE(stack_map.HasInlineInfo(code_info));
 }
 
@@ -209,14 +351,21 @@
   StackMapStream stream(&arena);
 
   ArenaBitVector sp_mask(&arena, 0, false);
-  uint32_t number_of_dex_registers = 0xEA;
+  uint32_t number_of_dex_registers = 1024;
+  // Create the first stack map (and its Dex register map).
   stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
-  for (uint32_t i = 0; i < number_of_dex_registers - 9; ++i) {
-    stream.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kConstant, 0);
+  uint32_t number_of_dex_live_registers_in_dex_register_map_0 = number_of_dex_registers - 8;
+  for (uint32_t i = 0; i < number_of_dex_live_registers_in_dex_register_map_0; ++i) {
+    // Use two different Dex register locations to populate this map,
+    // as using a single value (in the whole CodeInfo object) would
+    // make this Dex register mapping data empty (see
+    // art::DexRegisterMap::SingleEntrySizeInBits).
+    stream.AddDexRegisterEntry(i, Kind::kConstant, i % 2);  // Short location.
   }
+  // Create the second stack map (and its Dex register map).
   stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
   for (uint32_t i = 0; i < number_of_dex_registers; ++i) {
-    stream.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kConstant, 0);
+    stream.AddDexRegisterEntry(i, Kind::kConstant, 0);  // Short location.
   }
 
   size_t size = stream.ComputeNeededSize();
@@ -225,10 +374,35 @@
   stream.FillIn(region);
 
   CodeInfo code_info(region);
-  StackMap stack_map = code_info.GetStackMapAt(1);
-  ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info));
-  ASSERT_NE(stack_map.GetDexRegisterMapOffset(code_info), StackMap::kNoDexRegisterMap);
-  ASSERT_EQ(stack_map.GetDexRegisterMapOffset(code_info), StackMap::kNoDexRegisterMapSmallEncoding);
+  // The location catalog contains two entries (DexRegisterLocation(kConstant, 0)
+  // and DexRegisterLocation(kConstant, 1)), therefore the location catalog index
+  // has a size of 1 bit.
+  uint32_t number_of_location_catalog_entries =
+      code_info.GetNumberOfDexRegisterLocationCatalogEntries();
+  ASSERT_EQ(2u, number_of_location_catalog_entries);
+  ASSERT_EQ(1u, DexRegisterMap::SingleEntrySizeInBits(number_of_location_catalog_entries));
+
+  // The first Dex register map contains:
+  // - a live register bit mask for 1024 registers (that is, 128 bytes of
+  //   data); and
+  // - Dex register mapping information for 1016 1-bit Dex (live) register
+  //   locations (that is, 127 bytes of data).
+  // Hence it has a size of 255 bytes, and therefore...
+  ASSERT_EQ(128u, DexRegisterMap::GetLiveBitMaskSize(number_of_dex_registers));
+  StackMap stack_map0 = code_info.GetStackMapAt(0);
+  DexRegisterMap dex_register_map0 =
+      code_info.GetDexRegisterMapOf(stack_map0, number_of_dex_registers);
+  ASSERT_EQ(127u, dex_register_map0.GetLocationMappingDataSize(number_of_dex_registers,
+                                                               number_of_location_catalog_entries));
+  ASSERT_EQ(255u, dex_register_map0.Size());
+
+  StackMap stack_map1 = code_info.GetStackMapAt(1);
+  ASSERT_TRUE(stack_map1.HasDexRegisterMap(code_info));
+  // ...the offset of the second Dex register map (relative to the
+  // beginning of the Dex register maps region) is 255 (i.e.,
+  // kNoDexRegisterMapSmallEncoding).
+  ASSERT_NE(stack_map1.GetDexRegisterMapOffset(code_info), StackMap::kNoDexRegisterMap);
+  ASSERT_EQ(stack_map1.GetDexRegisterMapOffset(code_info), 0xFFu);
 }
 
 TEST(StackMapTest, TestShareDexRegisterMap) {
@@ -240,16 +414,16 @@
   uint32_t number_of_dex_registers = 2;
   // First stack map.
   stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
-  stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 0);
-  stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2);
+  stream.AddDexRegisterEntry(0, Kind::kInRegister, 0);  // Short location.
+  stream.AddDexRegisterEntry(1, Kind::kConstant, -2);   // Large location.
   // Second stack map, which should share the same dex register map.
   stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
-  stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 0);
-  stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2);
+  stream.AddDexRegisterEntry(0, Kind::kInRegister, 0);  // Short location.
+  stream.AddDexRegisterEntry(1, Kind::kConstant, -2);   // Large location.
   // Third stack map (doesn't share the dex register map).
   stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
-  stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 2);
-  stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2);
+  stream.AddDexRegisterEntry(0, Kind::kInRegister, 2);  // Short location.
+  stream.AddDexRegisterEntry(1, Kind::kConstant, -2);   // Large location.
 
   size_t size = stream.ComputeNeededSize();
   void* memory = arena.Alloc(size, kArenaAllocMisc);
@@ -260,20 +434,20 @@
   // Verify first stack map.
   StackMap sm0 = ci.GetStackMapAt(0);
   DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, number_of_dex_registers);
-  ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers));
-  ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers));
+  ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers, ci));
+  ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers, ci));
 
   // Verify second stack map.
   StackMap sm1 = ci.GetStackMapAt(1);
   DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, number_of_dex_registers);
-  ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers));
-  ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers));
+  ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers, ci));
+  ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers, ci));
 
   // Verify third stack map.
   StackMap sm2 = ci.GetStackMapAt(2);
   DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, number_of_dex_registers);
-  ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers));
-  ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers));
+  ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers, ci));
+  ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers, ci));
 
   // Verify dex register map offsets.
   ASSERT_EQ(sm0.GetDexRegisterMapOffset(ci), sm1.GetDexRegisterMapOffset(ci));
@@ -281,4 +455,39 @@
   ASSERT_NE(sm1.GetDexRegisterMapOffset(ci), sm2.GetDexRegisterMapOffset(ci));
 }
 
+TEST(StackMapTest, TestNoDexRegisterMap) {
+  ArenaPool pool;
+  ArenaAllocator arena(&pool);
+  StackMapStream stream(&arena);
+
+  ArenaBitVector sp_mask(&arena, 0, false);
+  uint32_t number_of_dex_registers = 0;
+  stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
+
+  size_t size = stream.ComputeNeededSize();
+  void* memory = arena.Alloc(size, kArenaAllocMisc);
+  MemoryRegion region(memory, size);
+  stream.FillIn(region);
+
+  CodeInfo code_info(region);
+  ASSERT_EQ(0u, code_info.GetStackMaskSize());
+  ASSERT_EQ(1u, code_info.GetNumberOfStackMaps());
+
+  uint32_t number_of_location_catalog_entries =
+      code_info.GetNumberOfDexRegisterLocationCatalogEntries();
+  ASSERT_EQ(0u, number_of_location_catalog_entries);
+  DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog();
+  ASSERT_EQ(0u, location_catalog.Size());
+
+  StackMap stack_map = code_info.GetStackMapAt(0);
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
+  ASSERT_EQ(0u, stack_map.GetDexPc(code_info));
+  ASSERT_EQ(64u, stack_map.GetNativePcOffset(code_info));
+  ASSERT_EQ(0x3u, stack_map.GetRegisterMask(code_info));
+
+  ASSERT_FALSE(stack_map.HasDexRegisterMap(code_info));
+  ASSERT_FALSE(stack_map.HasInlineInfo(code_info));
+}
+
 }  // namespace art
diff --git a/compiler/utils/dex_cache_arrays_layout-inl.h b/compiler/utils/dex_cache_arrays_layout-inl.h
new file mode 100644
index 0000000..7d02ce3
--- /dev/null
+++ b/compiler/utils/dex_cache_arrays_layout-inl.h
@@ -0,0 +1,77 @@
+/*
+ * 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_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
+#define ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
+
+#include "dex_cache_arrays_layout.h"
+
+#include "base/logging.h"
+#include "globals.h"
+#include "mirror/array-inl.h"
+#include "primitive.h"
+#include "utils.h"
+
+namespace mirror {
+class ArtField;
+class ArtMethod;
+class Class;
+class String;
+}  // namespace mirror
+
+namespace art {
+
+inline DexCacheArraysLayout::DexCacheArraysLayout(const DexFile* dex_file)
+    : /* types_offset_ is always 0u */
+      methods_offset_(types_offset_ + ArraySize<mirror::Class>(dex_file->NumTypeIds())),
+      strings_offset_(methods_offset_ + ArraySize<mirror::ArtMethod>(dex_file->NumMethodIds())),
+      fields_offset_(strings_offset_ + ArraySize<mirror::String>(dex_file->NumStringIds())),
+      size_(fields_offset_ + ArraySize<mirror::ArtField>(dex_file->NumFieldIds())) {
+}
+
+inline size_t DexCacheArraysLayout::TypeOffset(uint32_t type_idx) const {
+  return types_offset_ + ElementOffset<mirror::Class>(type_idx);
+}
+
+inline size_t DexCacheArraysLayout::MethodOffset(uint32_t method_idx) const {
+  return methods_offset_ + ElementOffset<mirror::ArtMethod>(method_idx);
+}
+
+inline size_t DexCacheArraysLayout::StringOffset(uint32_t string_idx) const {
+  return strings_offset_ + ElementOffset<mirror::String>(string_idx);
+}
+
+inline size_t DexCacheArraysLayout::FieldOffset(uint32_t field_idx) const {
+  return fields_offset_ + ElementOffset<mirror::ArtField>(field_idx);
+}
+
+template <typename MirrorType>
+inline size_t DexCacheArraysLayout::ElementOffset(uint32_t idx) {
+  return mirror::Array::DataOffset(sizeof(mirror::HeapReference<MirrorType>)).Uint32Value() +
+      sizeof(mirror::HeapReference<MirrorType>) * idx;
+}
+
+template <typename MirrorType>
+inline size_t DexCacheArraysLayout::ArraySize(uint32_t num_elements) {
+  size_t array_size = mirror::ComputeArraySize(
+      num_elements, ComponentSizeShiftWidth<sizeof(mirror::HeapReference<MirrorType>)>());
+  DCHECK_NE(array_size, 0u);  // No overflow expected for dex cache arrays.
+  return RoundUp(array_size, kObjectAlignment);
+}
+
+}  // namespace art
+
+#endif  // ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
diff --git a/compiler/utils/dex_cache_arrays_layout.h b/compiler/utils/dex_cache_arrays_layout.h
new file mode 100644
index 0000000..b461256
--- /dev/null
+++ b/compiler/utils/dex_cache_arrays_layout.h
@@ -0,0 +1,89 @@
+/*
+ * 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_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
+#define ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
+
+namespace art {
+
+/**
+ * @class DexCacheArraysLayout
+ * @details This class provides the layout information for the type, method, field and
+ * string arrays for a DexCache with a fixed arrays' layout (such as in the boot image),
+ */
+class DexCacheArraysLayout {
+ public:
+  // Construct an invalid layout.
+  DexCacheArraysLayout()
+      : /* types_offset_ is always 0u */
+        methods_offset_(0u),
+        strings_offset_(0u),
+        fields_offset_(0u),
+        size_(0u) {
+  }
+
+  // Construct a layout for a particular dex file.
+  explicit DexCacheArraysLayout(const DexFile* dex_file);
+
+  bool Valid() const {
+    return Size() != 0u;
+  }
+
+  size_t Size() const {
+    return size_;
+  }
+
+  size_t TypesOffset() const {
+    return types_offset_;
+  }
+
+  size_t TypeOffset(uint32_t type_idx) const;
+
+  size_t MethodsOffset() const {
+    return methods_offset_;
+  }
+
+  size_t MethodOffset(uint32_t method_idx) const;
+
+  size_t StringsOffset() const {
+    return strings_offset_;
+  }
+
+  size_t StringOffset(uint32_t string_idx) const;
+
+  size_t FieldsOffset() const {
+    return fields_offset_;
+  }
+
+  size_t FieldOffset(uint32_t field_idx) const;
+
+ private:
+  static constexpr size_t types_offset_ = 0u;
+  const size_t methods_offset_;
+  const size_t strings_offset_;
+  const size_t fields_offset_;
+  const size_t size_;
+
+  template <typename MirrorType>
+  static size_t ElementOffset(uint32_t idx);
+
+  template <typename MirrorType>
+  static size_t ArraySize(uint32_t num_elements);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk
index 3f15964..321cd75 100644
--- a/dex2oat/Android.mk
+++ b/dex2oat/Android.mk
@@ -46,8 +46,8 @@
 
 # We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
 ifeq ($(ART_BUILD_HOST_NDEBUG),true)
-  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart-compiler libziparchive-host,art/compiler,host,ndebug,$(dex2oat_host_arch)))
+  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler libziparchive-host,art/compiler,host,ndebug,$(dex2oat_host_arch)))
 endif
 ifeq ($(ART_BUILD_HOST_DEBUG),true)
-  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd-compiler libziparchive-host,art/compiler,host,debug,$(dex2oat_host_arch)))
+  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler libziparchive-host,art/compiler,host,debug,$(dex2oat_host_arch)))
 endif
diff --git a/patchoat/Android.mk b/patchoat/Android.mk
index 1e16096..68ca923 100644
--- a/patchoat/Android.mk
+++ b/patchoat/Android.mk
@@ -38,8 +38,8 @@
 
 # We always build patchoat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
 ifeq ($(ART_BUILD_HOST_NDEBUG),true)
-  $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),,art/compiler,host,ndebug))
+  $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,host,ndebug))
 endif
 ifeq ($(ART_BUILD_HOST_DEBUG),true)
-  $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),,art/compiler,host,debug))
+  $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,host,debug))
 endif
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index a1270dc..f8a9f9d 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -48,19 +48,23 @@
       return nullptr;
     }
   }
-  return new Arm64InstructionSetFeatures(smp, needs_a53_835769_fix);
+
+  // The variants that need a fix for 843419 are the same that need a fix for 835769.
+  bool needs_a53_843419_fix = needs_a53_835769_fix;
+
+  return new Arm64InstructionSetFeatures(smp, needs_a53_835769_fix, needs_a53_843419_fix);
 }
 
 const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromBitmap(uint32_t bitmap) {
   bool smp = (bitmap & kSmpBitfield) != 0;
   bool is_a53 = (bitmap & kA53Bitfield) != 0;
-  return new Arm64InstructionSetFeatures(smp, is_a53);
+  return new Arm64InstructionSetFeatures(smp, is_a53, is_a53);
 }
 
 const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromCppDefines() {
   const bool smp = true;
   const bool is_a53 = true;  // Pessimistically assume all ARM64s are A53s.
-  return new Arm64InstructionSetFeatures(smp, is_a53);
+  return new Arm64InstructionSetFeatures(smp, is_a53, is_a53);
 }
 
 const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromCpuInfo() {
@@ -85,13 +89,13 @@
   } else {
     LOG(ERROR) << "Failed to open /proc/cpuinfo";
   }
-  return new Arm64InstructionSetFeatures(smp, is_a53);
+  return new Arm64InstructionSetFeatures(smp, is_a53, is_a53);
 }
 
 const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromHwcap() {
   bool smp = sysconf(_SC_NPROCESSORS_CONF) > 1;
   const bool is_a53 = true;  // Pessimistically assume all ARM64s are A53s.
-  return new Arm64InstructionSetFeatures(smp, is_a53);
+  return new Arm64InstructionSetFeatures(smp, is_a53, is_a53);
 }
 
 const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromAssembly() {
@@ -140,7 +144,7 @@
       return nullptr;
     }
   }
-  return new Arm64InstructionSetFeatures(smp, is_a53);
+  return new Arm64InstructionSetFeatures(smp, is_a53, is_a53);
 }
 
 }  // namespace art
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.h b/runtime/arch/arm64/instruction_set_features_arm64.h
index f6bfee7..3b3e2c9 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.h
+++ b/runtime/arch/arm64/instruction_set_features_arm64.h
@@ -61,6 +61,11 @@
       return fix_cortex_a53_835769_;
   }
 
+  // Generate code addressing Cortex-A53 erratum 843419?
+  bool NeedFixCortexA53_843419() const {
+      return fix_cortex_a53_843419_;
+  }
+
   // TODO: Tune this on a per CPU basis. For now, we pessimistically assume
   // that all ARM64 CPUs prefer explicit memory barriers over acquire-release.
   //
@@ -79,8 +84,12 @@
                                  std::string* error_msg) const OVERRIDE;
 
  private:
-  explicit Arm64InstructionSetFeatures(bool smp, bool needs_a53_835769_fix)
-      : InstructionSetFeatures(smp), fix_cortex_a53_835769_(needs_a53_835769_fix) {
+  explicit Arm64InstructionSetFeatures(bool smp,
+                                       bool needs_a53_835769_fix,
+                                       bool needs_a53_843419_fix)
+      : InstructionSetFeatures(smp),
+        fix_cortex_a53_835769_(needs_a53_835769_fix),
+        fix_cortex_a53_843419_(needs_a53_843419_fix) {
   }
 
   // Bitmap positions for encoding features as a bitmap.
@@ -90,6 +99,7 @@
   };
 
   const bool fix_cortex_a53_835769_;
+  const bool fix_cortex_a53_843419_;
 
   DISALLOW_COPY_AND_ASSIGN(Arm64InstructionSetFeatures);
 };
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 16f0e70..0c2250e 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1094,7 +1094,7 @@
     lw      $a0, ARG_SLOT_SIZE($sp)       # load resolved method to $a0
     RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     move    $t9, $v0               # code pointer must be in $t9 to generate the global pointer
-    jalr    $zero, $v0             # tail call to method
+    jalr    $zero, $t9             # tail call to method
     nop
 1:
     RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
@@ -1203,29 +1203,28 @@
     .cpload  $t9
     move     $ra, $zero     # link register is to here, so clobber with 0 for later checks
 
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
     addiu    $sp, $sp, -16  # allocate temp storage on the stack
     .cfi_adjust_cfa_offset 16
-    sw       $v0, 12($sp)
-    .cfi_rel_offset 2, 32
-    sw       $v1, 8($sp)
-    .cfi_rel_offset 3, 36
-    s.d      $f0, 0($sp)
-    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
+    sw       $v0, ARG_SLOT_SIZE+12($sp)
+    .cfi_rel_offset 2, ARG_SLOT_SIZE+12
+    sw       $v1, ARG_SLOT_SIZE+8($sp)
+    .cfi_rel_offset 3, ARG_SLOT_SIZE+8
+    s.d      $f0, ARG_SLOT_SIZE($sp)
     s.d      $f0, 16($sp)   # pass fpr result
     move     $a2, $v0       # pass gpr result
     move     $a3, $v1
-    addiu    $a1, $sp, ARG_SLOT_SIZE   # pass $sp (remove arg slots)
+    addiu    $a1, $sp, ARG_SLOT_SIZE+16   # pass $sp (remove arg slots and temp storage)
     jal      artInstrumentationMethodExitFromCode  # (Thread*, SP, gpr_res, fpr_res)
     move     $a0, rSELF     # pass Thread::Current
-    move     $t0, $v0       # set aside returned link register
+    move     $t9, $v0       # set aside returned link register
     move     $ra, $v1       # set link register for deoptimization
-    addiu    $sp, $sp, ARG_SLOT_SIZE+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE  # args slot + refs_only callee save frame
-    lw       $v0, 12($sp)   # restore return values
-    lw       $v1, 8($sp)
-    l.d      $f0, 0($sp)
-    jalr     $zero, $t0     # return
-    addiu    $sp, $sp, 16   # remove temp storage from stack
-    .cfi_adjust_cfa_offset -16
+    lw       $v0, ARG_SLOT_SIZE+12($sp)   # restore return values
+    lw       $v1, ARG_SLOT_SIZE+8($sp)
+    l.d      $f0, ARG_SLOT_SIZE($sp)
+    jalr     $zero, $t9     # return
+    addiu    $sp, $sp, ARG_SLOT_SIZE+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE+16  # restore stack 
+    .cfi_adjust_cfa_offset -(ARG_SLOT_SIZE+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE+16)
 END art_quick_instrumentation_exit
 
     /*
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 8cb95f1..697bf00 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1504,11 +1504,11 @@
     move     $a1, $t0          # pass $sp
     jal      artInstrumentationMethodExitFromCode  # (Thread*, SP, gpr_res, fpr_res)
     move     $a0, rSELF        # pass Thread::Current
-    move     $t0, $v0          # set aside returned link register
+    move     $t9, $v0          # set aside returned link register
     move     $ra, $v1          # set link register for deoptimization
     ld       $v0, 0($sp)       # restore return values
     l.d      $f0, 8($sp)
-    jalr     $zero, $t0        # return
+    jalr     $zero, $t9        # return
     daddiu   $sp, $sp, 16+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE  # 16 bytes of saved values + ref_only callee save frame
     .cfi_adjust_cfa_offset -(16+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE)
 END art_quick_instrumentation_exit
diff --git a/runtime/base/hash_map.h b/runtime/base/hash_map.h
index c0f903f..eab80ff 100644
--- a/runtime/base/hash_map.h
+++ b/runtime/base/hash_map.h
@@ -48,7 +48,7 @@
   Fn fn_;
 };
 
-template <class Key, class Value, class EmptyFn = DefaultEmptyFn<Key>,
+template <class Key, class Value, class EmptyFn,
     class HashFn = std::hash<Key>, class Pred = std::equal_to<Key>,
     class Alloc = std::allocator<std::pair<Key, Value>>>
 class HashMap : public HashSet<std::pair<Key, Value>, EmptyFn, HashMapWrapper<HashFn>,
diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h
index 204546d..5d9cd35 100644
--- a/runtime/check_reference_map_visitor.h
+++ b/runtime/check_reference_map_visitor.h
@@ -75,7 +75,7 @@
       int reg = registers[i];
       CHECK(reg < m->GetCodeItem()->registers_size_);
       DexRegisterLocation location =
-          dex_register_map.GetLocationKindAndValue(reg, number_of_dex_registers);
+          dex_register_map.GetDexRegisterLocation(reg, number_of_dex_registers, code_info);
       switch (location.GetKind()) {
         case DexRegisterLocation::Kind::kNone:
           // Not set, should not be a reference.
diff --git a/runtime/gc/accounting/mod_union_table_test.cc b/runtime/gc/accounting/mod_union_table_test.cc
index 7780935..94bb3f5 100644
--- a/runtime/gc/accounting/mod_union_table_test.cc
+++ b/runtime/gc/accounting/mod_union_table_test.cc
@@ -47,7 +47,7 @@
       Thread* self, space::ContinuousMemMapAllocSpace* space, size_t component_count)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     auto* klass = GetObjectArrayClass(self, space);
-    const size_t size = ComputeArraySize(self, klass, component_count, 2);
+    const size_t size = mirror::ComputeArraySize(component_count, 2);
     size_t bytes_allocated = 0, bytes_tl_bulk_allocated;
     auto* obj = down_cast<mirror::ObjectArray<mirror::Object>*>(
         space->Alloc(self, size, &bytes_allocated, nullptr, &bytes_tl_bulk_allocated));
diff --git a/runtime/leb128.h b/runtime/leb128.h
index dfb42b8..d36b690 100644
--- a/runtime/leb128.h
+++ b/runtime/leb128.h
@@ -124,6 +124,18 @@
   return dest;
 }
 
+template<typename Allocator>
+static inline void EncodeUnsignedLeb128(std::vector<uint8_t, Allocator>* dest, uint32_t value) {
+  uint8_t out = value & 0x7f;
+  value >>= 7;
+  while (value != 0) {
+    dest->push_back(out | 0x80);
+    out = value & 0x7f;
+    value >>= 7;
+  }
+  dest->push_back(out);
+}
+
 static inline uint8_t* EncodeSignedLeb128(uint8_t* dest, int32_t value) {
   uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6;
   uint8_t out = value & 0x7f;
@@ -137,6 +149,19 @@
   return dest;
 }
 
+template<typename Allocator>
+static inline void EncodeSignedLeb128(std::vector<uint8_t, Allocator>* dest, int32_t value) {
+  uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6;
+  uint8_t out = value & 0x7f;
+  while (extra_bits != 0u) {
+    dest->push_back(out | 0x80);
+    value >>= 7;
+    out = value & 0x7f;
+    extra_bits >>= 7;
+  }
+  dest->push_back(out);
+}
+
 // An encoder that pushed uint32_t data onto the given std::vector.
 class Leb128Encoder {
  public:
@@ -149,14 +174,7 @@
   }
 
   void PushBackUnsigned(uint32_t value) {
-    uint8_t out = value & 0x7f;
-    value >>= 7;
-    while (value != 0) {
-      data_->push_back(out | 0x80);
-      out = value & 0x7f;
-      value >>= 7;
-    }
-    data_->push_back(out);
+    EncodeUnsignedLeb128(data_, value);
   }
 
   template<typename It>
@@ -167,15 +185,7 @@
   }
 
   void PushBackSigned(int32_t value) {
-    uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6;
-    uint8_t out = value & 0x7f;
-    while (extra_bits != 0u) {
-      data_->push_back(out | 0x80);
-      value >>= 7;
-      out = value & 0x7f;
-      extra_bits >>= 7;
-    }
-    data_->push_back(out);
+    EncodeSignedLeb128(data_, value);
   }
 
   template<typename It>
diff --git a/runtime/memory_region.h b/runtime/memory_region.h
index f867f6a..6a784eb 100644
--- a/runtime/memory_region.h
+++ b/runtime/memory_region.h
@@ -48,22 +48,28 @@
   uint8_t* end() const { return start() + size_; }
 
   // Load value of type `T` at `offset`.  The memory address corresponding
-  // to `offset` should be word-aligned.
-  template<typename T> T Load(uintptr_t offset) const {
-    // TODO: DCHECK that the address is word-aligned.
-    return *ComputeInternalPointer<T>(offset);
+  // to `offset` should be word-aligned (on ARM, this is a requirement).
+  template<typename T>
+  ALWAYS_INLINE T Load(uintptr_t offset) const {
+    T* address = ComputeInternalPointer<T>(offset);
+    DCHECK(IsWordAligned(address));
+    return *address;
   }
 
   // Store `value` (of type `T`) at `offset`.  The memory address
-  // corresponding to `offset` should be word-aligned.
-  template<typename T> void Store(uintptr_t offset, T value) const {
-    // TODO: DCHECK that the address is word-aligned.
-    *ComputeInternalPointer<T>(offset) = value;
+  // corresponding to `offset` should be word-aligned (on ARM, this is
+  // a requirement).
+  template<typename T>
+  ALWAYS_INLINE void Store(uintptr_t offset, T value) const {
+    T* address = ComputeInternalPointer<T>(offset);
+    DCHECK(IsWordAligned(address));
+    *address = value;
   }
 
   // Load value of type `T` at `offset`.  The memory address corresponding
   // to `offset` does not need to be word-aligned.
-  template<typename T> T LoadUnaligned(uintptr_t offset) const {
+  template<typename T>
+  ALWAYS_INLINE T LoadUnaligned(uintptr_t offset) const {
     // Equivalent unsigned integer type corresponding to T.
     typedef typename UnsignedIntegerType<sizeof(T)>::type U;
     U equivalent_unsigned_integer_value = 0;
@@ -77,7 +83,8 @@
 
   // Store `value` (of type `T`) at `offset`.  The memory address
   // corresponding to `offset` does not need to be word-aligned.
-  template<typename T> void StoreUnaligned(uintptr_t offset, T value) const {
+  template<typename T>
+  ALWAYS_INLINE void StoreUnaligned(uintptr_t offset, T value) const {
     // Equivalent unsigned integer type corresponding to T.
     typedef typename UnsignedIntegerType<sizeof(T)>::type U;
     U equivalent_unsigned_integer_value = bit_cast<U, T>(value);
@@ -88,19 +95,20 @@
     }
   }
 
-  template<typename T> T* PointerTo(uintptr_t offset) const {
+  template<typename T>
+  ALWAYS_INLINE T* PointerTo(uintptr_t offset) const {
     return ComputeInternalPointer<T>(offset);
   }
 
   // Load a single bit in the region. The bit at offset 0 is the least
   // significant bit in the first byte.
-  bool LoadBit(uintptr_t bit_offset) const {
+  ALWAYS_INLINE bool LoadBit(uintptr_t bit_offset) const {
     uint8_t bit_mask;
     uint8_t byte = *ComputeBitPointer(bit_offset, &bit_mask);
     return byte & bit_mask;
   }
 
-  void StoreBit(uintptr_t bit_offset, bool value) const {
+  ALWAYS_INLINE void StoreBit(uintptr_t bit_offset, bool value) const {
     uint8_t bit_mask;
     uint8_t* byte = ComputeBitPointer(bit_offset, &bit_mask);
     if (value) {
@@ -110,6 +118,31 @@
     }
   }
 
+  // Load `length` bits from the region starting at bit offset `bit_offset`.
+  // The bit at the smallest offset is the least significant bit in the
+  // loaded value.  `length` must not be larger than the number of bits
+  // contained in the return value (32).
+  uint32_t LoadBits(uintptr_t bit_offset, size_t length) const {
+    CHECK_LE(length, sizeof(uint32_t) * kBitsPerByte);
+    uint32_t value = 0u;
+    for (size_t i = 0; i < length; ++i) {
+      value |= LoadBit(bit_offset + i) << i;
+    }
+    return value;
+  }
+
+  // Store `value` on `length` bits in the region starting at bit offset
+  // `bit_offset`.  The bit at the smallest offset is the least significant
+  // bit of the stored `value`.  `value` must not be larger than `length`
+  // bits.
+  void StoreBits(uintptr_t bit_offset, uint32_t value, size_t length) {
+    CHECK_LT(value, 2u << length);
+    for (size_t i = 0; i < length; ++i) {
+      bool ith_bit = value & (1 << i);
+      StoreBit(bit_offset + i, ith_bit);
+    }
+  }
+
   void CopyFrom(size_t offset, const MemoryRegion& from) const;
 
   // Compute a sub memory region based on an existing one.
@@ -126,7 +159,8 @@
   }
 
  private:
-  template<typename T> T* ComputeInternalPointer(size_t offset) const {
+  template<typename T>
+  ALWAYS_INLINE T* ComputeInternalPointer(size_t offset) const {
     CHECK_GE(size(), sizeof(T));
     CHECK_LE(offset, size() - sizeof(T));
     return reinterpret_cast<T*>(start() + offset);
@@ -134,13 +168,20 @@
 
   // Locate the bit with the given offset. Returns a pointer to the byte
   // containing the bit, and sets bit_mask to the bit within that byte.
-  uint8_t* ComputeBitPointer(uintptr_t bit_offset, uint8_t* bit_mask) const {
+  ALWAYS_INLINE uint8_t* ComputeBitPointer(uintptr_t bit_offset, uint8_t* bit_mask) const {
     uintptr_t bit_remainder = (bit_offset & (kBitsPerByte - 1));
     *bit_mask = (1U << bit_remainder);
     uintptr_t byte_offset = (bit_offset >> kBitsPerByteLog2);
     return ComputeInternalPointer<uint8_t>(byte_offset);
   }
 
+  // Is `address` aligned on a machine word?
+  template<typename T> static bool IsWordAligned(const T* address) {
+    // Word alignment in bytes.
+    size_t kWordAlignment = GetInstructionSetPointerSize(kRuntimeISA);
+    return IsAlignedParam(address, kWordAlignment);
+  }
+
   void* pointer_;
   size_t size_;
 };
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 048d8ba..7f04992 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -64,25 +64,20 @@
   return true;
 }
 
-static inline size_t ComputeArraySize(Thread* self, Class* array_class, int32_t component_count,
-                                      size_t component_size_shift)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  DCHECK(array_class != NULL);
+static inline size_t ComputeArraySize(int32_t component_count, size_t component_size_shift) {
   DCHECK_GE(component_count, 0);
-  DCHECK(array_class->IsArrayClass());
 
   size_t component_size = 1U << component_size_shift;
   size_t header_size = Array::DataOffset(component_size).SizeValue();
   size_t data_size = static_cast<size_t>(component_count) << component_size_shift;
   size_t size = header_size + data_size;
 
-  // Check for size_t overflow and throw OutOfMemoryError if this was
-  // an unreasonable request.
+  // Check for size_t overflow if this was an unreasonable request
+  // but let the caller throw OutOfMemoryError.
 #ifdef __LP64__
   // 64-bit. No overflow as component_count is 32-bit and the maximum
   // component size is 8.
   DCHECK_LE((1U << component_size_shift), 8U);
-  UNUSED(self);
 #else
   // 32-bit.
   DCHECK_NE(header_size, 0U);
@@ -90,9 +85,6 @@
   // The array length limit (exclusive).
   const size_t length_limit = (0U - header_size) >> component_size_shift;
   if (UNLIKELY(length_limit <= static_cast<size_t>(component_count))) {
-    self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
-                                             PrettyDescriptor(array_class).c_str(),
-                                             component_count).c_str());
     return 0;  // failure
   }
 #endif
@@ -159,15 +151,20 @@
 inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count,
                            size_t component_size_shift, gc::AllocatorType allocator_type) {
   DCHECK(allocator_type != gc::kAllocatorTypeLOS);
+  DCHECK(array_class != nullptr);
+  DCHECK(array_class->IsArrayClass());
   DCHECK_EQ(array_class->GetComponentSizeShift(), component_size_shift);
   DCHECK_EQ(array_class->GetComponentSize(), (1U << component_size_shift));
-  size_t size = ComputeArraySize(self, array_class, component_count, component_size_shift);
+  size_t size = ComputeArraySize(component_count, component_size_shift);
 #ifdef __LP64__
   // 64-bit. No size_t overflow.
   DCHECK_NE(size, 0U);
 #else
   // 32-bit.
   if (UNLIKELY(size == 0)) {
+    self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
+                                             PrettyDescriptor(array_class).c_str(),
+                                             component_count).c_str());
     return nullptr;
   }
 #endif
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 0422fcd..a8b0876 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -210,29 +210,29 @@
         // image in case of the GSS collector.
         + 384 * MB;
 
-    std::string error_msg;
     std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true));
     ASSERT_TRUE(map.get() != nullptr) << "Failed to build process map";
     for (BacktraceMap::const_iterator it = map->begin();
         reservation_start < reservation_end && it != map->end(); ++it) {
-      if (it->end <= reservation_start) {
-        continue;
-      }
+      ReserveImageSpaceChunk(reservation_start, std::min(it->start, reservation_end));
+      reservation_start = std::max(reservation_start, it->end);
+    }
+    ReserveImageSpaceChunk(reservation_start, reservation_end);
+  }
 
-      if (it->start < reservation_start) {
-        reservation_start = std::min(reservation_end, it->end);
-      }
-
+  // Reserve a chunk of memory for the image space in the given range.
+  // Only has effect for chunks with a positive number of bytes.
+  void ReserveImageSpaceChunk(uintptr_t start, uintptr_t end) {
+    if (start < end) {
+      std::string error_msg;
       image_reservation_.push_back(std::unique_ptr<MemMap>(
           MemMap::MapAnonymous("image reservation",
-              reinterpret_cast<uint8_t*>(reservation_start),
-              std::min(it->start, reservation_end) - reservation_start,
+              reinterpret_cast<uint8_t*>(start), end - start,
               PROT_NONE, false, false, &error_msg)));
       ASSERT_TRUE(image_reservation_.back().get() != nullptr) << error_msg;
       LOG(INFO) << "Reserved space for image " <<
         reinterpret_cast<void*>(image_reservation_.back()->Begin()) << "-" <<
         reinterpret_cast<void*>(image_reservation_.back()->End());
-      reservation_start = it->end;
     }
   }
 
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 2d688ee..4ae49dd 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -206,21 +206,22 @@
   DexRegisterMap dex_register_map =
       code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
   DexRegisterLocation::Kind location_kind =
-      dex_register_map.GetLocationKind(vreg, number_of_dex_registers);
+      dex_register_map.GetLocationKind(vreg, number_of_dex_registers, code_info);
   switch (location_kind) {
     case DexRegisterLocation::Kind::kInStack: {
-      const int32_t offset = dex_register_map.GetStackOffsetInBytes(vreg, number_of_dex_registers);
+      const int32_t offset =
+          dex_register_map.GetStackOffsetInBytes(vreg, number_of_dex_registers, code_info);
       const uint8_t* addr = reinterpret_cast<const uint8_t*>(cur_quick_frame_) + offset;
       *val = *reinterpret_cast<const uint32_t*>(addr);
       return true;
     }
     case DexRegisterLocation::Kind::kInRegister:
     case DexRegisterLocation::Kind::kInFpuRegister: {
-      uint32_t reg = dex_register_map.GetMachineRegister(vreg, number_of_dex_registers);
+      uint32_t reg = dex_register_map.GetMachineRegister(vreg, number_of_dex_registers, code_info);
       return GetRegisterIfAccessible(reg, kind, val);
     }
     case DexRegisterLocation::Kind::kConstant:
-      *val = dex_register_map.GetConstant(vreg, number_of_dex_registers);
+      *val = dex_register_map.GetConstant(vreg, number_of_dex_registers, code_info);
       return true;
     case DexRegisterLocation::Kind::kNone:
       return false;
@@ -228,7 +229,7 @@
       LOG(FATAL)
           << "Unexpected location kind"
           << DexRegisterLocation::PrettyDescriptor(
-                dex_register_map.GetLocationInternalKind(vreg, number_of_dex_registers));
+                dex_register_map.GetLocationInternalKind(vreg, number_of_dex_registers, code_info));
       UNREACHABLE();
   }
 }
@@ -396,18 +397,19 @@
   DexRegisterMap dex_register_map =
       code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
   DexRegisterLocation::Kind location_kind =
-      dex_register_map.GetLocationKind(vreg, number_of_dex_registers);
+      dex_register_map.GetLocationKind(vreg, number_of_dex_registers, code_info);
   uint32_t dex_pc = m->ToDexPc(cur_quick_frame_pc_, false);
   switch (location_kind) {
     case DexRegisterLocation::Kind::kInStack: {
-      const int32_t offset = dex_register_map.GetStackOffsetInBytes(vreg, number_of_dex_registers);
+      const int32_t offset =
+          dex_register_map.GetStackOffsetInBytes(vreg, number_of_dex_registers, code_info);
       uint8_t* addr = reinterpret_cast<uint8_t*>(cur_quick_frame_) + offset;
       *reinterpret_cast<uint32_t*>(addr) = new_value;
       return true;
     }
     case DexRegisterLocation::Kind::kInRegister:
     case DexRegisterLocation::Kind::kInFpuRegister: {
-      uint32_t reg = dex_register_map.GetMachineRegister(vreg, number_of_dex_registers);
+      uint32_t reg = dex_register_map.GetMachineRegister(vreg, number_of_dex_registers, code_info);
       return SetRegisterIfAccessible(reg, new_value, kind);
     }
     case DexRegisterLocation::Kind::kConstant:
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index 020a6e6..11e7e44 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -16,133 +16,198 @@
 
 #include "stack_map.h"
 
+#include <stdint.h>
+
 namespace art {
 
-constexpr uint32_t StackMap::kNoDexRegisterMapSmallEncoding;
-constexpr uint32_t StackMap::kNoInlineInfoSmallEncoding;
+constexpr size_t DexRegisterLocationCatalog::kNoLocationEntryIndex;
 constexpr uint32_t StackMap::kNoDexRegisterMap;
 constexpr uint32_t StackMap::kNoInlineInfo;
 
-uint32_t StackMap::GetDexPc(const CodeInfo& info) const {
-  return info.HasSmallDexPc()
-      ? region_.LoadUnaligned<kSmallEncoding>(info.ComputeStackMapDexPcOffset())
-      : region_.LoadUnaligned<kLargeEncoding>(info.ComputeStackMapDexPcOffset());
+DexRegisterLocation::Kind DexRegisterMap::GetLocationInternalKind(uint16_t dex_register_number,
+                                                                  uint16_t number_of_dex_registers,
+                                                                  const CodeInfo& code_info) const {
+  DexRegisterLocationCatalog dex_register_location_catalog =
+      code_info.GetDexRegisterLocationCatalog();
+  size_t location_catalog_entry_index = GetLocationCatalogEntryIndex(
+      dex_register_number,
+      number_of_dex_registers,
+      code_info.GetNumberOfDexRegisterLocationCatalogEntries());
+  return dex_register_location_catalog.GetLocationInternalKind(location_catalog_entry_index);
 }
 
-void StackMap::SetDexPc(const CodeInfo& info, uint32_t dex_pc) {
-  DCHECK(!info.HasSmallDexPc() || IsUint<kBitsForSmallEncoding>(dex_pc)) << dex_pc;
-  info.HasSmallDexPc()
-      ? region_.StoreUnaligned<kSmallEncoding>(info.ComputeStackMapDexPcOffset(), dex_pc)
-      : region_.StoreUnaligned<kLargeEncoding>(info.ComputeStackMapDexPcOffset(), dex_pc);
+DexRegisterLocation DexRegisterMap::GetDexRegisterLocation(uint16_t dex_register_number,
+                                                           uint16_t number_of_dex_registers,
+                                                           const CodeInfo& code_info) const {
+  DexRegisterLocationCatalog dex_register_location_catalog =
+      code_info.GetDexRegisterLocationCatalog();
+  size_t location_catalog_entry_index = GetLocationCatalogEntryIndex(
+      dex_register_number,
+      number_of_dex_registers,
+      code_info.GetNumberOfDexRegisterLocationCatalogEntries());
+  return dex_register_location_catalog.GetDexRegisterLocation(location_catalog_entry_index);
 }
 
-uint32_t StackMap::GetNativePcOffset(const CodeInfo& info) const {
-  return info.HasSmallNativePc()
-      ? region_.LoadUnaligned<kSmallEncoding>(info.ComputeStackMapNativePcOffset())
-      : region_.LoadUnaligned<kLargeEncoding>(info.ComputeStackMapNativePcOffset());
-}
-
-void StackMap::SetNativePcOffset(const CodeInfo& info, uint32_t native_pc_offset) {
-  DCHECK(!info.HasSmallNativePc()
-         || IsUint<kBitsForSmallEncoding>(native_pc_offset)) << native_pc_offset;
-  uint32_t entry = info.ComputeStackMapNativePcOffset();
-  info.HasSmallNativePc()
-      ? region_.StoreUnaligned<kSmallEncoding>(entry, native_pc_offset)
-      : region_.StoreUnaligned<kLargeEncoding>(entry, native_pc_offset);
-}
-
-uint32_t StackMap::GetDexRegisterMapOffset(const CodeInfo& info) const {
-  if (info.HasSmallDexRegisterMap()) {
-    uint8_t value = region_.LoadUnaligned<kSmallEncoding>(
-        info.ComputeStackMapDexRegisterMapOffset());
-    if (value == kNoDexRegisterMapSmallEncoding) {
-      return kNoDexRegisterMap;
+// Loads `number_of_bytes` at the given `offset` and assemble a uint32_t. If `check_max` is true,
+// this method converts a maximum value of size `number_of_bytes` into a uint32_t 0xFFFFFFFF.
+static uint32_t LoadAt(MemoryRegion region,
+                       size_t number_of_bytes,
+                       size_t offset,
+                       bool check_max = false) {
+  if (number_of_bytes == 0u) {
+    DCHECK(!check_max);
+    return 0;
+  } else if (number_of_bytes == 1u) {
+    uint8_t value = region.LoadUnaligned<uint8_t>(offset);
+    if (check_max && value == 0xFF) {
+      return -1;
+    } else {
+      return value;
+    }
+  } else if (number_of_bytes == 2u) {
+    uint16_t value = region.LoadUnaligned<uint16_t>(offset);
+    if (check_max && value == 0xFFFF) {
+      return -1;
+    } else {
+      return value;
+    }
+  } else if (number_of_bytes == 3u) {
+    uint16_t low = region.LoadUnaligned<uint16_t>(offset);
+    uint16_t high = region.LoadUnaligned<uint8_t>(offset + sizeof(uint16_t));
+    uint32_t value = (high << 16) + low;
+    if (check_max && value == 0xFFFFFF) {
+      return -1;
     } else {
       return value;
     }
   } else {
-    return region_.LoadUnaligned<kLargeEncoding>(info.ComputeStackMapDexRegisterMapOffset());
+    DCHECK_EQ(number_of_bytes, 4u);
+    return region.LoadUnaligned<uint32_t>(offset);
   }
 }
 
+static void StoreAt(MemoryRegion region, size_t number_of_bytes, size_t offset, uint32_t value) {
+  if (number_of_bytes == 0u) {
+    DCHECK_EQ(value, 0u);
+  } else if (number_of_bytes == 1u) {
+    region.StoreUnaligned<uint8_t>(offset, value);
+  } else if (number_of_bytes == 2u) {
+    region.StoreUnaligned<uint16_t>(offset, value);
+  } else if (number_of_bytes == 3u) {
+    region.StoreUnaligned<uint16_t>(offset, Low16Bits(value));
+    region.StoreUnaligned<uint8_t>(offset + sizeof(uint16_t), High16Bits(value));
+  } else {
+    region.StoreUnaligned<uint32_t>(offset, value);
+    DCHECK_EQ(number_of_bytes, 4u);
+  }
+}
+
+uint32_t StackMap::GetDexPc(const CodeInfo& info) const {
+  return LoadAt(region_, info.NumberOfBytesForDexPc(), info.ComputeStackMapDexPcOffset());
+}
+
+void StackMap::SetDexPc(const CodeInfo& info, uint32_t dex_pc) {
+  StoreAt(region_, info.NumberOfBytesForDexPc(), info.ComputeStackMapDexPcOffset(), dex_pc);
+}
+
+uint32_t StackMap::GetNativePcOffset(const CodeInfo& info) const {
+  return LoadAt(region_, info.NumberOfBytesForNativePc(), info.ComputeStackMapNativePcOffset());
+}
+
+void StackMap::SetNativePcOffset(const CodeInfo& info, uint32_t native_pc_offset) {
+  StoreAt(region_, info.NumberOfBytesForNativePc(), info.ComputeStackMapNativePcOffset(), native_pc_offset);
+}
+
+uint32_t StackMap::GetDexRegisterMapOffset(const CodeInfo& info) const {
+  return LoadAt(region_,
+                info.NumberOfBytesForDexRegisterMap(),
+                info.ComputeStackMapDexRegisterMapOffset(),
+                /* check_max */ true);
+}
+
 void StackMap::SetDexRegisterMapOffset(const CodeInfo& info, uint32_t offset) {
-  DCHECK(!info.HasSmallDexRegisterMap()
-         || (IsUint<kBitsForSmallEncoding>(offset)
-             || (offset == kNoDexRegisterMap))) << offset;
-  size_t dex_register_map_entry = info.ComputeStackMapDexRegisterMapOffset();
-  info.HasSmallDexRegisterMap()
-      ? region_.StoreUnaligned<kSmallEncoding>(dex_register_map_entry, offset)
-      : region_.StoreUnaligned<kLargeEncoding>(dex_register_map_entry, offset);
+  StoreAt(region_,
+          info.NumberOfBytesForDexRegisterMap(),
+          info.ComputeStackMapDexRegisterMapOffset(),
+          offset);
 }
 
 uint32_t StackMap::GetInlineDescriptorOffset(const CodeInfo& info) const {
   if (!info.HasInlineInfo()) return kNoInlineInfo;
-  if (info.HasSmallInlineInfo()) {
-    uint8_t value = region_.LoadUnaligned<kSmallEncoding>(
-        info.ComputeStackMapInlineInfoOffset());
-    if (value == kNoInlineInfoSmallEncoding) {
-      return kNoInlineInfo;
-    } else {
-      return value;
-    }
-  } else {
-    return region_.LoadUnaligned<kLargeEncoding>(info.ComputeStackMapInlineInfoOffset());
-  }
+  return LoadAt(region_,
+                info.NumberOfBytesForInlineInfo(),
+                info.ComputeStackMapInlineInfoOffset(),
+                /* check_max */ true);
 }
 
 void StackMap::SetInlineDescriptorOffset(const CodeInfo& info, uint32_t offset) {
   DCHECK(info.HasInlineInfo());
-  DCHECK(!info.HasSmallInlineInfo()
-         || (IsUint<kBitsForSmallEncoding>(offset)
-             || (offset == kNoInlineInfo))) << offset;
-  size_t inline_entry = info.ComputeStackMapInlineInfoOffset();
-  info.HasSmallInlineInfo()
-      ? region_.StoreUnaligned<kSmallEncoding>(inline_entry, offset)
-      : region_.StoreUnaligned<kLargeEncoding>(inline_entry, offset);
+  StoreAt(region_,
+          info.NumberOfBytesForInlineInfo(),
+          info.ComputeStackMapInlineInfoOffset(),
+          offset);
 }
 
 uint32_t StackMap::GetRegisterMask(const CodeInfo& info) const {
-  return region_.LoadUnaligned<kLargeEncoding>(info.ComputeStackMapRegisterMaskOffset());
+  return LoadAt(region_,
+                info.NumberOfBytesForRegisterMask(),
+                info.ComputeStackMapRegisterMaskOffset());
 }
 
 void StackMap::SetRegisterMask(const CodeInfo& info, uint32_t mask) {
-  region_.StoreUnaligned<kLargeEncoding>(info.ComputeStackMapRegisterMaskOffset(), mask);
+  StoreAt(region_,
+          info.NumberOfBytesForRegisterMask(),
+          info.ComputeStackMapRegisterMaskOffset(),
+          mask);
 }
 
-size_t StackMap::ComputeStackMapSize(size_t stack_mask_size,
-                                     bool has_inline_info,
-                                     bool is_small_inline_info,
-                                     bool is_small_dex_map,
-                                     bool is_small_dex_pc,
-                                     bool is_small_native_pc) {
-  return StackMap::kFixedSize
-      + stack_mask_size
-      + (has_inline_info ? NumberOfBytesForEntry(is_small_inline_info) : 0)
-      + NumberOfBytesForEntry(is_small_dex_map)
-      + NumberOfBytesForEntry(is_small_dex_pc)
-      + NumberOfBytesForEntry(is_small_native_pc);
+size_t StackMap::ComputeStackMapSizeInternal(size_t stack_mask_size,
+                                             size_t number_of_bytes_for_inline_info,
+                                             size_t number_of_bytes_for_dex_map,
+                                             size_t number_of_bytes_for_dex_pc,
+                                             size_t number_of_bytes_for_native_pc,
+                                             size_t number_of_bytes_for_register_mask) {
+  return stack_mask_size
+      + number_of_bytes_for_inline_info
+      + number_of_bytes_for_dex_map
+      + number_of_bytes_for_dex_pc
+      + number_of_bytes_for_native_pc
+      + number_of_bytes_for_register_mask;
 }
 
 size_t StackMap::ComputeStackMapSize(size_t stack_mask_size,
                                      size_t inline_info_size,
                                      size_t dex_register_map_size,
                                      size_t dex_pc_max,
-                                     size_t native_pc_max) {
-  return ComputeStackMapSize(
+                                     size_t native_pc_max,
+                                     size_t register_mask_max) {
+  return ComputeStackMapSizeInternal(
       stack_mask_size,
-      inline_info_size != 0,
-      // + 1 to also encode kNoInlineInfo.
-      IsUint<kBitsForSmallEncoding>(inline_info_size + dex_register_map_size + 1),
+      inline_info_size == 0
+          ? 0
+            // + 1 to also encode kNoInlineInfo.
+          :  CodeInfo::EncodingSizeInBytes(inline_info_size + dex_register_map_size + 1),
       // + 1 to also encode kNoDexRegisterMap.
-      IsUint<kBitsForSmallEncoding>(dex_register_map_size + 1),
-      IsUint<kBitsForSmallEncoding>(dex_pc_max),
-      IsUint<kBitsForSmallEncoding>(native_pc_max));
+      CodeInfo::EncodingSizeInBytes(dex_register_map_size + 1),
+      CodeInfo::EncodingSizeInBytes(dex_pc_max),
+      CodeInfo::EncodingSizeInBytes(native_pc_max),
+      CodeInfo::EncodingSizeInBytes(register_mask_max));
 }
 
 MemoryRegion StackMap::GetStackMask(const CodeInfo& info) const {
   return region_.Subregion(info.ComputeStackMapStackMaskOffset(), info.GetStackMaskSize());
 }
 
+static void DumpRegisterMapping(std::ostream& os,
+                                size_t dex_register_num,
+                                DexRegisterLocation location,
+                                const std::string& prefix = "v",
+                                const std::string& suffix = "") {
+  os << "      " << prefix << dex_register_num << ": "
+     << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind())
+     << " (" << location.GetValue() << ")" << suffix << '\n';
+}
+
 void CodeInfo::DumpStackMapHeader(std::ostream& os, size_t stack_map_num) const {
   StackMap stack_map = GetStackMapAt(stack_map_num);
   os << "    StackMap " << stack_map_num
@@ -168,13 +233,25 @@
      << ", number_of_dex_registers=" << number_of_dex_registers
      << ", number_of_stack_maps=" << number_of_stack_maps
      << ", has_inline_info=" << HasInlineInfo()
-     << ", has_small_inline_info=" << HasSmallInlineInfo()
-     << ", has_small_dex_register_map=" << HasSmallDexRegisterMap()
-     << ", has_small_dex_pc=" << HasSmallDexPc()
-     << ", has_small_native_pc=" << HasSmallNativePc()
+     << ", number_of_bytes_for_inline_info=" << NumberOfBytesForInlineInfo()
+     << ", number_of_bytes_for_dex_register_map=" << NumberOfBytesForDexRegisterMap()
+     << ", number_of_bytes_for_dex_pc=" << NumberOfBytesForDexPc()
+     << ", number_of_bytes_for_native_pc=" << NumberOfBytesForNativePc()
+     << ", number_of_bytes_for_register_mask=" << NumberOfBytesForRegisterMask()
      << ")\n";
 
-  // Display stack maps along with Dex register maps.
+  // Display the Dex register location catalog.
+  size_t number_of_location_catalog_entries = GetNumberOfDexRegisterLocationCatalogEntries();
+  size_t location_catalog_size_in_bytes = GetDexRegisterLocationCatalogSize();
+  os << "  DexRegisterLocationCatalog (number_of_entries=" << number_of_location_catalog_entries
+     << ", size_in_bytes=" << location_catalog_size_in_bytes << ")\n";
+  DexRegisterLocationCatalog dex_register_location_catalog = GetDexRegisterLocationCatalog();
+  for (size_t i = 0; i < number_of_location_catalog_entries; ++i) {
+    DexRegisterLocation location = dex_register_location_catalog.GetDexRegisterLocation(i);
+    DumpRegisterMapping(os, i, location, "entry ");
+  }
+
+  // Display stack maps along with (live) Dex register maps.
   for (size_t i = 0; i < number_of_stack_maps; ++i) {
     StackMap stack_map = GetStackMapAt(i);
     DumpStackMapHeader(os, i);
@@ -183,11 +260,13 @@
       // TODO: Display the bit mask of live Dex registers.
       for (size_t j = 0; j < number_of_dex_registers; ++j) {
         if (dex_register_map.IsDexRegisterLive(j)) {
+          size_t location_catalog_entry_index = dex_register_map.GetLocationCatalogEntryIndex(
+              j, number_of_dex_registers, number_of_location_catalog_entries);
           DexRegisterLocation location =
-              dex_register_map.GetLocationKindAndValue(j, number_of_dex_registers);
-           os << "      " << "v" << j << ": "
-              << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind())
-              << " (" << location.GetValue() << ")" << '\n';
+              dex_register_map.GetDexRegisterLocation(j, number_of_dex_registers, *this);
+          DumpRegisterMapping(
+              os, j, location, "v",
+              "\t[entry " + std::to_string(static_cast<int>(location_catalog_entry_index)) + "]");
         }
       }
     }
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 6ec7cc8..f68cafe 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -26,13 +26,10 @@
 // Size of a frame slot, in bytes.  This constant is a signed value,
 // to please the compiler in arithmetic operations involving int32_t
 // (signed) values.
-static ssize_t constexpr kFrameSlotSize = 4;
-
-// Word alignment required on ARM, in bytes.
-static constexpr size_t kWordAlignment = 4;
+static constexpr ssize_t kFrameSlotSize = 4;
 
 // Size of Dex virtual registers.
-static size_t constexpr kVRegSize = 4;
+static constexpr size_t kVRegSize = 4;
 
 class CodeInfo;
 
@@ -97,9 +94,9 @@
    *
    * In addition, DexRegisterMap also uses these values:
    * - kInStackLargeOffset: value holds a "large" stack offset (greater than
-   *   128 bytes);
-   * - kConstantLargeValue: value holds a "large" constant (lower than or
-   *   equal to -16, or greater than 16).
+   *   or equal to 128 bytes);
+   * - kConstantLargeValue: value holds a "large" constant (lower than 0, or
+   *   or greater than or equal to 32).
    */
   enum class Kind : uint8_t {
     // Short location kinds, for entries fitting on one byte (3 bits
@@ -120,8 +117,7 @@
     kInStackLargeOffset = 5,  // 0b101
 
     // Large constant, that cannot fit on a 5-bit signed integer (i.e.,
-    // lower than -2^(5-1) = -16, or greater than or equal to
-    // 2^(5-1) - 1 = 15).
+    // lower than 0, or greater than or equal to 2^5 = 32).
     kConstantLargeValue = 6,  // 0b110
 
     kLastLocationKind = kConstantLargeValue
@@ -193,8 +189,10 @@
     }
   }
 
-  DexRegisterLocation(Kind kind, int32_t value)
-      : kind_(kind), value_(value) {}
+  // Required by art::StackMapStream::LocationCatalogEntriesIndices.
+  DexRegisterLocation() : kind_(Kind::kNone), value_(0) {}
+
+  DexRegisterLocation(Kind kind, int32_t value) : kind_(kind), value_(value) {}
 
   static DexRegisterLocation None() {
     return DexRegisterLocation(Kind::kNone, 0);
@@ -223,33 +221,23 @@
  private:
   Kind kind_;
   int32_t value_;
+
+  friend class DexRegisterLocationHashFn;
 };
 
 /**
- * Information on dex register values for a specific PC. The information is
- * of the form:
- * [live_bit_mask, DexRegisterLocation+].
+ * Store information on unique Dex register locations used in a method.
+ * The information is of the form:
+ * [DexRegisterLocation+].
  * DexRegisterLocations are either 1- or 5-byte wide (see art::DexRegisterLocation::Kind).
  */
-class DexRegisterMap {
+class DexRegisterLocationCatalog {
  public:
-  explicit DexRegisterMap(MemoryRegion region) : region_(region) {}
+  explicit DexRegisterLocationCatalog(MemoryRegion region) : region_(region) {}
 
   // Short (compressed) location, fitting on one byte.
   typedef uint8_t ShortLocation;
 
-  static size_t LiveBitMaskSize(uint16_t number_of_dex_registers) {
-    return RoundUp(number_of_dex_registers, kBitsPerByte) / kBitsPerByte;
-  }
-
-  void SetLiveBitMask(size_t offset,
-                      uint16_t number_of_dex_registers,
-                      const BitVector& live_dex_registers_mask) {
-    for (uint16_t i = 0; i < number_of_dex_registers; i++) {
-      region_.StoreBit(offset + i, live_dex_registers_mask.IsBitSet(i));
-    }
-  }
-
   void SetRegisterInfo(size_t offset, const DexRegisterLocation& dex_register_location) {
     DexRegisterLocation::Kind kind = ComputeCompressedKind(dex_register_location);
     int32_t value = dex_register_location.GetValue();
@@ -265,12 +253,12 @@
         DCHECK_EQ(value % kFrameSlotSize, 0);
         value /= kFrameSlotSize;
       }
-      DCHECK(IsUint<kValueBits>(value)) << value;
+      DCHECK(IsShortValue(value)) << value;
       region_.StoreUnaligned<ShortLocation>(offset, MakeShortLocation(kind, value));
     } else {
       // Large location.  Write the location on one byte and the value
       // on 4 bytes.
-      DCHECK(!IsUint<kValueBits>(value)) << value;
+      DCHECK(!IsShortValue(value)) << value;
       if (kind == DexRegisterLocation::Kind::kInStackLargeOffset) {
         // Also divide large stack offsets by 4 for the sake of consistency.
         DCHECK_EQ(value % kFrameSlotSize, 0);
@@ -285,63 +273,39 @@
     }
   }
 
-  bool IsDexRegisterLive(uint16_t dex_register_index) const {
+  // Find the offset of the location catalog entry number `location_catalog_entry_index`.
+  size_t FindLocationOffset(size_t location_catalog_entry_index) const {
     size_t offset = kFixedSize;
-    return region_.LoadBit(offset + dex_register_index);
-  }
-
-  static constexpr size_t kNoDexRegisterLocationOffset = -1;
-
-  static size_t GetDexRegisterMapLocationsOffset(uint16_t number_of_dex_registers) {
-    return kLiveBitMaskOffset + LiveBitMaskSize(number_of_dex_registers);
-  }
-
-  // Find the offset of the Dex register location number `dex_register_index`.
-  size_t FindLocationOffset(uint16_t dex_register_index, uint16_t number_of_dex_registers) const {
-    if (!IsDexRegisterLive(dex_register_index)) return kNoDexRegisterLocationOffset;
-    size_t offset = GetDexRegisterMapLocationsOffset(number_of_dex_registers);
-    // Skip the first `dex_register_index - 1` entries.
-    for (uint16_t i = 0; i < dex_register_index; ++i) {
-      if (IsDexRegisterLive(i)) {
-        // Read the first next byte and inspect its first 3 bits to decide
-        // whether it is a short or a large location.
-        DexRegisterLocation::Kind kind = ExtractKindAtOffset(offset);
-        if (DexRegisterLocation::IsShortLocationKind(kind)) {
-          // Short location.  Skip the current byte.
-          offset += SingleShortEntrySize();
-        } else {
-          // Large location.  Skip the 5 next bytes.
-          offset += SingleLargeEntrySize();
-        }
+    // Skip the first `location_catalog_entry_index - 1` entries.
+    for (uint16_t i = 0; i < location_catalog_entry_index; ++i) {
+      // Read the first next byte and inspect its first 3 bits to decide
+      // whether it is a short or a large location.
+      DexRegisterLocation::Kind kind = ExtractKindAtOffset(offset);
+      if (DexRegisterLocation::IsShortLocationKind(kind)) {
+        // Short location.  Skip the current byte.
+        offset += SingleShortEntrySize();
+      } else {
+        // Large location.  Skip the 5 next bytes.
+        offset += SingleLargeEntrySize();
       }
     }
     return offset;
   }
 
-  // Get the surface kind.
-  DexRegisterLocation::Kind GetLocationKind(uint16_t dex_register_index,
-                                            uint16_t number_of_dex_registers) const {
-    return IsDexRegisterLive(dex_register_index)
-        ? DexRegisterLocation::ConvertToSurfaceKind(
-              GetLocationInternalKind(dex_register_index, number_of_dex_registers))
-        : DexRegisterLocation::Kind::kNone;
+  // Get the internal kind of entry at `location_catalog_entry_index`.
+  DexRegisterLocation::Kind GetLocationInternalKind(size_t location_catalog_entry_index) const {
+    if (location_catalog_entry_index == kNoLocationEntryIndex) {
+      return DexRegisterLocation::Kind::kNone;
+    }
+    return ExtractKindAtOffset(FindLocationOffset(location_catalog_entry_index));
   }
 
-  // Get the internal kind.
-  DexRegisterLocation::Kind GetLocationInternalKind(uint16_t dex_register_index,
-                                                    uint16_t number_of_dex_registers) const {
-    return IsDexRegisterLive(dex_register_index)
-        ? ExtractKindAtOffset(FindLocationOffset(dex_register_index, number_of_dex_registers))
-        : DexRegisterLocation::Kind::kNone;
-  }
-
-  // TODO: Rename as GetDexRegisterLocation?
-  DexRegisterLocation GetLocationKindAndValue(uint16_t dex_register_index,
-                                              uint16_t number_of_dex_registers) const {
-    if (!IsDexRegisterLive(dex_register_index)) {
+  // Get the (surface) kind and value of entry at `location_catalog_entry_index`.
+  DexRegisterLocation GetDexRegisterLocation(size_t location_catalog_entry_index) const {
+    if (location_catalog_entry_index == kNoLocationEntryIndex) {
       return DexRegisterLocation::None();
     }
-    size_t offset = FindLocationOffset(dex_register_index, number_of_dex_registers);
+    size_t offset = FindLocationOffset(location_catalog_entry_index);
     // Read the first byte and inspect its first 3 bits to get the location.
     ShortLocation first_byte = region_.LoadUnaligned<ShortLocation>(offset);
     DexRegisterLocation::Kind kind = ExtractKindFromShortLocation(first_byte);
@@ -364,31 +328,6 @@
     }
   }
 
-  int32_t GetStackOffsetInBytes(uint16_t dex_register_index,
-                                uint16_t number_of_dex_registers) const {
-    DexRegisterLocation location =
-        GetLocationKindAndValue(dex_register_index, number_of_dex_registers);
-    DCHECK(location.GetKind() == DexRegisterLocation::Kind::kInStack);
-    // GetLocationKindAndValue returns the offset in bytes.
-    return location.GetValue();
-  }
-
-  int32_t GetConstant(uint16_t dex_register_index, uint16_t number_of_dex_registers) const {
-    DexRegisterLocation location =
-        GetLocationKindAndValue(dex_register_index, number_of_dex_registers);
-    DCHECK(location.GetKind() == DexRegisterLocation::Kind::kConstant);
-    return location.GetValue();
-  }
-
-  int32_t GetMachineRegister(uint16_t dex_register_index, uint16_t number_of_dex_registers) const {
-    DexRegisterLocation location =
-        GetLocationKindAndValue(dex_register_index, number_of_dex_registers);
-    DCHECK(location.GetInternalKind() == DexRegisterLocation::Kind::kInRegister
-           || location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegister)
-        << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind());
-    return location.GetValue();
-  }
-
   // Compute the compressed kind of `location`.
   static DexRegisterLocation::Kind ComputeCompressedKind(const DexRegisterLocation& location) {
     switch (location.GetInternalKind()) {
@@ -398,22 +337,21 @@
 
       case DexRegisterLocation::Kind::kInRegister:
         DCHECK_GE(location.GetValue(), 0);
-        DCHECK_LT(location.GetValue(), 1 << DexRegisterMap::kValueBits);
+        DCHECK_LT(location.GetValue(), 1 << kValueBits);
         return DexRegisterLocation::Kind::kInRegister;
 
       case DexRegisterLocation::Kind::kInFpuRegister:
         DCHECK_GE(location.GetValue(), 0);
-        DCHECK_LT(location.GetValue(), 1 << DexRegisterMap::kValueBits);
+        DCHECK_LT(location.GetValue(), 1 << kValueBits);
         return DexRegisterLocation::Kind::kInFpuRegister;
 
       case DexRegisterLocation::Kind::kInStack:
-        DCHECK_EQ(location.GetValue() % kFrameSlotSize, 0);
-        return IsUint<DexRegisterMap::kValueBits>(location.GetValue() / kFrameSlotSize)
+        return IsShortStackOffsetValue(location.GetValue())
             ? DexRegisterLocation::Kind::kInStack
             : DexRegisterLocation::Kind::kInStackLargeOffset;
 
       case DexRegisterLocation::Kind::kConstant:
-        return IsUint<DexRegisterMap::kValueBits>(location.GetValue())
+        return IsShortConstantValue(location.GetValue())
             ? DexRegisterLocation::Kind::kConstant
             : DexRegisterLocation::Kind::kConstantLargeValue;
 
@@ -433,11 +371,10 @@
         return true;
 
       case DexRegisterLocation::Kind::kInStack:
-        DCHECK_EQ(location.GetValue() % kFrameSlotSize, 0);
-        return IsUint<kValueBits>(location.GetValue() / kFrameSlotSize);
+        return IsShortStackOffsetValue(location.GetValue());
 
       case DexRegisterLocation::Kind::kConstant:
-        return IsUint<kValueBits>(location.GetValue());
+        return IsShortConstantValue(location.GetValue());
 
       default:
         UNREACHABLE();
@@ -445,9 +382,7 @@
   }
 
   static size_t EntrySize(const DexRegisterLocation& location) {
-    return CanBeEncodedAsShortLocation(location)
-        ? DexRegisterMap::SingleShortEntrySize()
-        : DexRegisterMap::SingleLargeEntrySize();
+    return CanBeEncodedAsShortLocation(location) ? SingleShortEntrySize() : SingleLargeEntrySize();
   }
 
   static size_t SingleShortEntrySize() {
@@ -462,10 +397,14 @@
     return region_.size();
   }
 
-  static constexpr int kLiveBitMaskOffset = 0;
-  static constexpr int kFixedSize = kLiveBitMaskOffset;
+  // Special (invalid) Dex register location catalog entry index meaning
+  // that there is no location for a given Dex register (i.e., it is
+  // mapped to a DexRegisterLocation::Kind::kNone location).
+  static constexpr size_t kNoLocationEntryIndex = -1;
 
  private:
+  static constexpr int kFixedSize = 0;
+
   // Width of the kind "field" in a short location, in bits.
   static constexpr size_t kKindBits = 3;
   // Width of the value "field" in a short location, in bits.
@@ -476,10 +415,24 @@
   static constexpr size_t kKindOffset = 0;
   static constexpr size_t kValueOffset = kKindBits;
 
+  static bool IsShortStackOffsetValue(int32_t value) {
+    DCHECK_EQ(value % kFrameSlotSize, 0);
+    return IsShortValue(value / kFrameSlotSize);
+  }
+
+  static bool IsShortConstantValue(int32_t value) {
+    return IsShortValue(value);
+  }
+
+  static bool IsShortValue(int32_t value) {
+    return IsUint<kValueBits>(value);
+  }
+
   static ShortLocation MakeShortLocation(DexRegisterLocation::Kind kind, int32_t value) {
-    DCHECK(IsUint<kKindBits>(static_cast<uint8_t>(kind))) << static_cast<uint8_t>(kind);
-    DCHECK(IsUint<kValueBits>(value)) << value;
-    return (static_cast<uint8_t>(kind) & kKindMask) << kKindOffset
+    uint8_t kind_integer_value = static_cast<uint8_t>(kind);
+    DCHECK(IsUint<kKindBits>(kind_integer_value)) << kind_integer_value;
+    DCHECK(IsShortValue(value)) << value;
+    return (kind_integer_value & kKindMask) << kKindOffset
         | (value & kValueMask) << kValueOffset;
   }
 
@@ -507,6 +460,210 @@
   friend class StackMapStream;
 };
 
+/* Information on Dex register locations for a specific PC, mapping a
+ * stack map's Dex register to a location entry in a DexRegisterLocationCatalog.
+ * The information is of the form:
+ * [live_bit_mask, entries*]
+ * where entries are concatenated unsigned integer values encoded on a number
+ * of bits (fixed per DexRegisterMap instances of a CodeInfo object) depending
+ * on the number of entries in the Dex register location catalog
+ * (see DexRegisterMap::SingleEntrySizeInBits).  The map is 1-byte aligned.
+ */
+class DexRegisterMap {
+ public:
+  explicit DexRegisterMap(MemoryRegion region) : region_(region) {}
+
+  // Get the surface kind of Dex register `dex_register_number`.
+  DexRegisterLocation::Kind GetLocationKind(uint16_t dex_register_number,
+                                            uint16_t number_of_dex_registers,
+                                            const CodeInfo& code_info) const {
+    return DexRegisterLocation::ConvertToSurfaceKind(
+        GetLocationInternalKind(dex_register_number, number_of_dex_registers, code_info));
+  }
+
+  // Get the internal kind of Dex register `dex_register_number`.
+  DexRegisterLocation::Kind GetLocationInternalKind(uint16_t dex_register_number,
+                                                    uint16_t number_of_dex_registers,
+                                                    const CodeInfo& code_info) const;
+
+  // Get the Dex register location `dex_register_number`.
+  DexRegisterLocation GetDexRegisterLocation(uint16_t dex_register_number,
+                                             uint16_t number_of_dex_registers,
+                                             const CodeInfo& code_info) const;
+
+  int32_t GetStackOffsetInBytes(uint16_t dex_register_number,
+                                uint16_t number_of_dex_registers,
+                                const CodeInfo& code_info) const {
+    DexRegisterLocation location =
+        GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info);
+    DCHECK(location.GetKind() == DexRegisterLocation::Kind::kInStack);
+    // GetDexRegisterLocation returns the offset in bytes.
+    return location.GetValue();
+  }
+
+  int32_t GetConstant(uint16_t dex_register_number,
+                      uint16_t number_of_dex_registers,
+                      const CodeInfo& code_info) const {
+    DexRegisterLocation location =
+        GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info);
+    DCHECK(location.GetKind() == DexRegisterLocation::Kind::kConstant);
+    return location.GetValue();
+  }
+
+  int32_t GetMachineRegister(uint16_t dex_register_number,
+                             uint16_t number_of_dex_registers,
+                             const CodeInfo& code_info) const {
+    DexRegisterLocation location =
+        GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info);
+    DCHECK(location.GetInternalKind() == DexRegisterLocation::Kind::kInRegister
+           || location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegister)
+        << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind());
+    return location.GetValue();
+  }
+
+  // Get the index of the entry in the Dex register location catalog
+  // corresponding to `dex_register_number`.
+  size_t GetLocationCatalogEntryIndex(uint16_t dex_register_number,
+                                      uint16_t number_of_dex_registers,
+                                      size_t number_of_location_catalog_entries) const {
+    if (!IsDexRegisterLive(dex_register_number)) {
+      return DexRegisterLocationCatalog::kNoLocationEntryIndex;
+    }
+
+    if (number_of_location_catalog_entries == 1) {
+      // We do not allocate space for location maps in the case of a
+      // single-entry location catalog, as it is useless.  The only valid
+      // entry index is 0;
+      return 0;
+    }
+
+    // The bit offset of the beginning of the map locations.
+    size_t map_locations_offset_in_bits =
+        GetLocationMappingDataOffset(number_of_dex_registers) * kBitsPerByte;
+    size_t index_in_dex_register_map = GetIndexInDexRegisterMap(dex_register_number);
+    DCHECK_LT(index_in_dex_register_map, GetNumberOfLiveDexRegisters(number_of_dex_registers));
+    // The bit size of an entry.
+    size_t map_entry_size_in_bits = SingleEntrySizeInBits(number_of_location_catalog_entries);
+    // The bit offset where `index_in_dex_register_map` is located.
+    size_t entry_offset_in_bits =
+        map_locations_offset_in_bits + index_in_dex_register_map * map_entry_size_in_bits;
+    size_t location_catalog_entry_index =
+        region_.LoadBits(entry_offset_in_bits, map_entry_size_in_bits);
+    DCHECK_LT(location_catalog_entry_index, number_of_location_catalog_entries);
+    return location_catalog_entry_index;
+  }
+
+  // Map entry at `index_in_dex_register_map` to `location_catalog_entry_index`.
+  void SetLocationCatalogEntryIndex(size_t index_in_dex_register_map,
+                                    size_t location_catalog_entry_index,
+                                    uint16_t number_of_dex_registers,
+                                    size_t number_of_location_catalog_entries) {
+    DCHECK_LT(index_in_dex_register_map, GetNumberOfLiveDexRegisters(number_of_dex_registers));
+    DCHECK_LT(location_catalog_entry_index, number_of_location_catalog_entries);
+
+    if (number_of_location_catalog_entries == 1) {
+      // We do not allocate space for location maps in the case of a
+      // single-entry location catalog, as it is useless.
+      return;
+    }
+
+    // The bit offset of the beginning of the map locations.
+    size_t map_locations_offset_in_bits =
+        GetLocationMappingDataOffset(number_of_dex_registers) * kBitsPerByte;
+    // The bit size of an entry.
+    size_t map_entry_size_in_bits = SingleEntrySizeInBits(number_of_location_catalog_entries);
+    // The bit offset where `index_in_dex_register_map` is located.
+    size_t entry_offset_in_bits =
+        map_locations_offset_in_bits + index_in_dex_register_map * map_entry_size_in_bits;
+    region_.StoreBits(entry_offset_in_bits, location_catalog_entry_index, map_entry_size_in_bits);
+  }
+
+  void SetLiveBitMask(uint16_t number_of_dex_registers,
+                      const BitVector& live_dex_registers_mask) {
+    size_t live_bit_mask_offset_in_bits = GetLiveBitMaskOffset() * kBitsPerByte;
+    for (uint16_t i = 0; i < number_of_dex_registers; ++i) {
+      region_.StoreBit(live_bit_mask_offset_in_bits + i, live_dex_registers_mask.IsBitSet(i));
+    }
+  }
+
+  bool IsDexRegisterLive(uint16_t dex_register_number) const {
+    size_t live_bit_mask_offset_in_bits = GetLiveBitMaskOffset() * kBitsPerByte;
+    return region_.LoadBit(live_bit_mask_offset_in_bits + dex_register_number);
+  }
+
+  size_t GetNumberOfLiveDexRegisters(uint16_t number_of_dex_registers) const {
+    size_t number_of_live_dex_registers = 0;
+    for (size_t i = 0; i < number_of_dex_registers; ++i) {
+      if (IsDexRegisterLive(i)) {
+        ++number_of_live_dex_registers;
+      }
+    }
+    return number_of_live_dex_registers;
+  }
+
+  static size_t GetLiveBitMaskOffset() {
+    return kFixedSize;
+  }
+
+  // Compute the size of the live register bit mask (in bytes), for a
+  // method having `number_of_dex_registers` Dex registers.
+  static size_t GetLiveBitMaskSize(uint16_t number_of_dex_registers) {
+    return RoundUp(number_of_dex_registers, kBitsPerByte) / kBitsPerByte;
+  }
+
+  static size_t GetLocationMappingDataOffset(uint16_t number_of_dex_registers) {
+    return GetLiveBitMaskOffset() + GetLiveBitMaskSize(number_of_dex_registers);
+  }
+
+  size_t GetLocationMappingDataSize(uint16_t number_of_dex_registers,
+                                    size_t number_of_location_catalog_entries) const {
+    size_t location_mapping_data_size_in_bits =
+        GetNumberOfLiveDexRegisters(number_of_dex_registers)
+        * SingleEntrySizeInBits(number_of_location_catalog_entries);
+    return RoundUp(location_mapping_data_size_in_bits, kBitsPerByte) / kBitsPerByte;
+  }
+
+  // Return the size of a map entry in bits.  Note that if
+  // `number_of_location_catalog_entries` equals 1, this function returns 0,
+  // which is fine, as there is no need to allocate a map for a
+  // single-entry location catalog; the only valid location catalog entry index
+  // for a live register in this case is 0 and there is no need to
+  // store it.
+  static size_t SingleEntrySizeInBits(size_t number_of_location_catalog_entries) {
+    // Handle the case of 0, as we cannot pass 0 to art::WhichPowerOf2.
+    return number_of_location_catalog_entries == 0
+        ? 0u
+        : WhichPowerOf2(RoundUpToPowerOfTwo(number_of_location_catalog_entries));
+  }
+
+  // Return the size of the DexRegisterMap object, in bytes.
+  size_t Size() const {
+    return region_.size();
+  }
+
+ private:
+  // Return the index in the Dex register map corresponding to the Dex
+  // register number `dex_register_number`.
+  size_t GetIndexInDexRegisterMap(uint16_t dex_register_number) const {
+    if (!IsDexRegisterLive(dex_register_number)) {
+      return kInvalidIndexInDexRegisterMap;
+    }
+    return GetNumberOfLiveDexRegisters(dex_register_number);
+  }
+
+  // Special (invalid) Dex register map entry index meaning that there
+  // is no index in the map for a given Dex register (i.e., it must
+  // have been mapped to a DexRegisterLocation::Kind::kNone location).
+  static constexpr size_t kInvalidIndexInDexRegisterMap = -1;
+
+  static constexpr int kFixedSize = 0;
+
+  MemoryRegion region_;
+
+  friend class CodeInfo;
+  friend class StackMapStream;
+};
+
 /**
  * A Stack Map holds compilation information for a specific PC necessary for:
  * - Mapping it to a dex PC,
@@ -516,7 +673,8 @@
  * - Knowing the values of dex registers.
  *
  * The information is of the form:
- * [dex_pc, native_pc_offset, dex_register_map_offset, inlining_info_offset, register_mask, stack_mask].
+ * [dex_pc, native_pc_offset, dex_register_map_offset, inlining_info_offset, register_mask,
+ * stack_mask].
  *
  * Note that register_mask is fixed size, but stack_mask is variable size, depending on the
  * stack size of a method.
@@ -568,49 +726,32 @@
   }
 
   static size_t ComputeStackMapSize(size_t stack_mask_size,
-                                    bool has_inline_info,
-                                    bool is_small_inline_info,
-                                    bool is_small_dex_map,
-                                    bool is_small_dex_pc,
-                                    bool is_small_native_pc);
-
-  static size_t ComputeStackMapSize(size_t stack_mask_size,
                                     size_t inline_info_size,
                                     size_t dex_register_map_size,
                                     size_t dex_pc_max,
-                                    size_t native_pc_max);
-
-  // TODO: Revisit this abstraction if we allow 3 bytes encoding.
-  typedef uint8_t kSmallEncoding;
-  typedef uint32_t kLargeEncoding;
-  static constexpr size_t kBytesForSmallEncoding = sizeof(kSmallEncoding);
-  static constexpr size_t kBitsForSmallEncoding = kBitsPerByte * kBytesForSmallEncoding;
-  static constexpr size_t kBytesForLargeEncoding = sizeof(kLargeEncoding);
-  static constexpr size_t kBitsForLargeEncoding = kBitsPerByte * kBytesForLargeEncoding;
+                                    size_t native_pc_max,
+                                    size_t register_mask_max);
 
   // Special (invalid) offset for the DexRegisterMapOffset field meaning
   // that there is no Dex register map for this stack map.
   static constexpr uint32_t kNoDexRegisterMap = -1;
-  static constexpr uint32_t kNoDexRegisterMapSmallEncoding =
-      std::numeric_limits<kSmallEncoding>::max();
 
   // Special (invalid) offset for the InlineDescriptorOffset field meaning
   // that there is no inline info for this stack map.
   static constexpr uint32_t kNoInlineInfo = -1;
-  static constexpr uint32_t kNoInlineInfoSmallEncoding =
-    std::numeric_limits<kSmallEncoding>::max();
-
-  // Returns the number of bytes needed for an entry in the StackMap.
-  static size_t NumberOfBytesForEntry(bool small_encoding) {
-    return small_encoding ? kBytesForSmallEncoding : kBytesForLargeEncoding;
-  }
 
  private:
+  static size_t ComputeStackMapSizeInternal(size_t stack_mask_size,
+                                            size_t number_of_bytes_for_inline_info,
+                                            size_t number_of_bytes_for_dex_map,
+                                            size_t number_of_bytes_for_dex_pc,
+                                            size_t number_of_bytes_for_native_pc,
+                                            size_t number_of_bytes_for_register_mask);
+
   // TODO: Instead of plain types such as "uint32_t", introduce
   // typedefs (and document the memory layout of StackMap).
   static constexpr int kRegisterMaskOffset = 0;
-  static constexpr int kFixedSize = kRegisterMaskOffset + sizeof(uint32_t);
-  static constexpr int kStackMaskOffset = kFixedSize;
+  static constexpr int kFixedSize = 0;
 
   MemoryRegion region_;
 
@@ -622,7 +763,8 @@
 /**
  * Wrapper around all compiler information collected for a method.
  * The information is of the form:
- * [overall_size, number_of_stack_maps, stack_mask_size, StackMap+, DexRegisterInfo+, InlineInfo*].
+ * [overall_size, number_of_location_catalog_entries, number_of_stack_maps, stack_mask_size,
+ * DexRegisterLocationCatalog+, StackMap+, DexRegisterMap+, InlineInfo*].
  */
 class CodeInfo {
  public:
@@ -633,50 +775,77 @@
     region_ = MemoryRegion(const_cast<void*>(data), size);
   }
 
+  static size_t EncodingSizeInBytes(size_t max_element) {
+    DCHECK(IsUint<32>(max_element));
+    return (max_element == 0) ? 0
+         : IsUint<8>(max_element) ? 1
+         : IsUint<16>(max_element) ? 2
+         : IsUint<24>(max_element) ? 3
+         : 4;
+  }
+
   void SetEncoding(size_t inline_info_size,
                    size_t dex_register_map_size,
                    size_t dex_pc_max,
-                   size_t native_pc_max) {
+                   size_t native_pc_max,
+                   size_t register_mask_max) {
     if (inline_info_size != 0) {
       region_.StoreBit(kHasInlineInfoBitOffset, 1);
-      region_.StoreBit(kHasSmallInlineInfoBitOffset, IsUint<StackMap::kBitsForSmallEncoding>(
-          // + 1 to also encode kNoInlineInfo: if an inline info offset
-          // is at 0xFF, we want to overflow to a larger encoding, because it will
-          // conflict with kNoInlineInfo.
-          // The offset is relative to the dex register map. TODO: Change this.
-          inline_info_size + dex_register_map_size + 1));
+      // + 1 to also encode kNoInlineInfo: if an inline info offset
+      // is at 0xFF, we want to overflow to a larger encoding, because it will
+      // conflict with kNoInlineInfo.
+      // The offset is relative to the dex register map. TODO: Change this.
+      SetEncodingAt(kInlineInfoBitOffset,
+                    EncodingSizeInBytes(dex_register_map_size + inline_info_size + 1));
     } else {
       region_.StoreBit(kHasInlineInfoBitOffset, 0);
-      region_.StoreBit(kHasSmallInlineInfoBitOffset, 0);
+      SetEncodingAt(kInlineInfoBitOffset, 0);
     }
-    region_.StoreBit(kHasSmallDexRegisterMapBitOffset,
-                     // + 1 to also encode kNoDexRegisterMap: if a dex register map offset
-                     // is at 0xFF, we want to overflow to a larger encoding, because it will
-                     // conflict with kNoDexRegisterMap.
-                     IsUint<StackMap::kBitsForSmallEncoding>(dex_register_map_size + 1));
-    region_.StoreBit(kHasSmallDexPcBitOffset, IsUint<StackMap::kBitsForSmallEncoding>(dex_pc_max));
-    region_.StoreBit(kHasSmallNativePcBitOffset,
-                     IsUint<StackMap::kBitsForSmallEncoding>(native_pc_max));
+    // + 1 to also encode kNoDexRegisterMap: if a dex register map offset
+    // is at 0xFF, we want to overflow to a larger encoding, because it will
+    // conflict with kNoDexRegisterMap.
+    SetEncodingAt(kDexRegisterMapBitOffset, EncodingSizeInBytes(dex_register_map_size + 1));
+    SetEncodingAt(kDexPcBitOffset, EncodingSizeInBytes(dex_pc_max));
+    SetEncodingAt(kNativePcBitOffset, EncodingSizeInBytes(native_pc_max));
+    SetEncodingAt(kRegisterMaskBitOffset, EncodingSizeInBytes(register_mask_max));
+  }
+
+  void SetEncodingAt(size_t bit_offset, size_t number_of_bytes) {
+    // We encode the number of bytes needed for writing a value on 3 bits,
+    // for values that we know are maximum 32bits.
+    region_.StoreBit(bit_offset, (number_of_bytes & 1));
+    region_.StoreBit(bit_offset + 1, (number_of_bytes & 2));
+    region_.StoreBit(bit_offset + 2, (number_of_bytes & 4));
+  }
+
+  size_t GetNumberOfBytesForEncoding(size_t bit_offset) const {
+    return region_.LoadBit(bit_offset)
+        + (region_.LoadBit(bit_offset + 1) << 1)
+        + (region_.LoadBit(bit_offset + 2) << 2);
   }
 
   bool HasInlineInfo() const {
     return region_.LoadBit(kHasInlineInfoBitOffset);
   }
 
-  bool HasSmallInlineInfo() const {
-    return region_.LoadBit(kHasSmallInlineInfoBitOffset);
+  size_t NumberOfBytesForInlineInfo() const {
+    return GetNumberOfBytesForEncoding(kInlineInfoBitOffset);
   }
 
-  bool HasSmallDexRegisterMap() const {
-    return region_.LoadBit(kHasSmallDexRegisterMapBitOffset);
+  size_t NumberOfBytesForDexRegisterMap() const {
+    return GetNumberOfBytesForEncoding(kDexRegisterMapBitOffset);
   }
 
-  bool HasSmallNativePc() const {
-    return region_.LoadBit(kHasSmallNativePcBitOffset);
+  size_t NumberOfBytesForRegisterMask() const {
+    return GetNumberOfBytesForEncoding(kRegisterMaskBitOffset);
   }
 
-  bool HasSmallDexPc() const {
-    return region_.LoadBit(kHasSmallDexPcBitOffset);
+  size_t NumberOfBytesForNativePc() const {
+    return GetNumberOfBytesForEncoding(kNativePcBitOffset);
+  }
+
+  size_t NumberOfBytesForDexPc() const {
+    return GetNumberOfBytesForEncoding(kDexPcBitOffset);
   }
 
   size_t ComputeStackMapRegisterMaskOffset() const {
@@ -684,7 +853,8 @@
   }
 
   size_t ComputeStackMapStackMaskOffset() const {
-    return StackMap::kStackMaskOffset;
+    return ComputeStackMapRegisterMaskOffset()
+        + (NumberOfBytesForRegisterMask() * sizeof(uint8_t));
   }
 
   size_t ComputeStackMapDexPcOffset() const {
@@ -693,18 +863,28 @@
 
   size_t ComputeStackMapNativePcOffset() const {
     return ComputeStackMapDexPcOffset()
-        + (HasSmallDexPc() ? sizeof(uint8_t) : sizeof(uint32_t));
+        + (NumberOfBytesForDexPc() * sizeof(uint8_t));
   }
 
   size_t ComputeStackMapDexRegisterMapOffset() const {
     return ComputeStackMapNativePcOffset()
-        + (HasSmallNativePc() ? sizeof(uint8_t) : sizeof(uint32_t));
+        + (NumberOfBytesForNativePc() * sizeof(uint8_t));
   }
 
   size_t ComputeStackMapInlineInfoOffset() const {
     CHECK(HasInlineInfo());
     return ComputeStackMapDexRegisterMapOffset()
-        + (HasSmallDexRegisterMap() ? sizeof(uint8_t) : sizeof(uint32_t));
+        + (NumberOfBytesForDexRegisterMap() * sizeof(uint8_t));
+  }
+
+  uint32_t GetDexRegisterLocationCatalogOffset() const {
+    return kFixedSize;
+  }
+
+  DexRegisterLocationCatalog GetDexRegisterLocationCatalog() const {
+    return DexRegisterLocationCatalog(region_.Subregion(
+        GetDexRegisterLocationCatalogOffset(),
+        GetDexRegisterLocationCatalogSize()));
   }
 
   StackMap GetStackMapAt(size_t i) const {
@@ -720,6 +900,19 @@
     region_.StoreUnaligned<uint32_t>(kOverallSizeOffset, size);
   }
 
+  uint32_t GetNumberOfDexRegisterLocationCatalogEntries() const {
+    return region_.LoadUnaligned<uint32_t>(kNumberOfDexRegisterLocationCatalogEntriesOffset);
+  }
+
+  void SetNumberOfDexRegisterLocationCatalogEntries(uint32_t num_entries) {
+    region_.StoreUnaligned<uint32_t>(kNumberOfDexRegisterLocationCatalogEntriesOffset, num_entries);
+  }
+
+  uint32_t GetDexRegisterLocationCatalogSize() const {
+    return ComputeDexRegisterLocationCatalogSize(GetDexRegisterLocationCatalogOffset(),
+                                                 GetNumberOfDexRegisterLocationCatalogEntries());
+  }
+
   uint32_t GetStackMaskSize() const {
     return region_.LoadUnaligned<uint32_t>(kStackMaskSizeOffset);
   }
@@ -739,31 +932,31 @@
   // Get the size of one stack map of this CodeInfo object, in bytes.
   // All stack maps of a CodeInfo have the same size.
   size_t StackMapSize() const {
-    return StackMap::ComputeStackMapSize(GetStackMaskSize(),
-                                         HasInlineInfo(),
-                                         HasSmallInlineInfo(),
-                                         HasSmallDexRegisterMap(),
-                                         HasSmallDexPc(),
-                                         HasSmallNativePc());
+    return StackMap::ComputeStackMapSizeInternal(GetStackMaskSize(),
+                                                 NumberOfBytesForInlineInfo(),
+                                                 NumberOfBytesForDexRegisterMap(),
+                                                 NumberOfBytesForDexPc(),
+                                                 NumberOfBytesForNativePc(),
+                                                 NumberOfBytesForRegisterMask());
   }
 
   // Get the size all the stack maps of this CodeInfo object, in bytes.
-  size_t StackMapsSize() const {
+  size_t GetStackMapsSize() const {
     return StackMapSize() * GetNumberOfStackMaps();
   }
 
   size_t GetDexRegisterMapsOffset() const {
-    return CodeInfo::kFixedSize + StackMapsSize();
+    return GetStackMapsOffset() + GetStackMapsSize();
   }
 
   uint32_t GetStackMapsOffset() const {
-    return kFixedSize;
+    return GetDexRegisterLocationCatalogOffset() + GetDexRegisterLocationCatalogSize();
   }
 
   DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, uint32_t number_of_dex_registers) const {
     DCHECK(stack_map.HasDexRegisterMap(*this));
-    uint32_t offset = stack_map.GetDexRegisterMapOffset(*this) + GetDexRegisterMapsOffset();
-    size_t size = ComputeDexRegisterMapSize(offset, number_of_dex_registers);
+    uint32_t offset = GetDexRegisterMapsOffset() + stack_map.GetDexRegisterMapOffset(*this);
+    size_t size = ComputeDexRegisterMapSizeOf(offset, number_of_dex_registers);
     return DexRegisterMap(region_.Subregion(offset, size));
   }
 
@@ -806,50 +999,73 @@
   // typedefs (and document the memory layout of CodeInfo).
   static constexpr int kOverallSizeOffset = 0;
   static constexpr int kEncodingInfoOffset = kOverallSizeOffset + sizeof(uint32_t);
-  static constexpr int kNumberOfStackMapsOffset = kEncodingInfoOffset + sizeof(uint8_t);
+  static constexpr int kNumberOfDexRegisterLocationCatalogEntriesOffset =
+      kEncodingInfoOffset + sizeof(uint16_t);
+  static constexpr int kNumberOfStackMapsOffset =
+      kNumberOfDexRegisterLocationCatalogEntriesOffset + sizeof(uint32_t);
   static constexpr int kStackMaskSizeOffset = kNumberOfStackMapsOffset + sizeof(uint32_t);
   static constexpr int kFixedSize = kStackMaskSizeOffset + sizeof(uint32_t);
 
   static constexpr int kHasInlineInfoBitOffset = (kEncodingInfoOffset * kBitsPerByte);
-  static constexpr int kHasSmallInlineInfoBitOffset = kHasInlineInfoBitOffset + 1;
-  static constexpr int kHasSmallDexRegisterMapBitOffset = kHasSmallInlineInfoBitOffset + 1;
-  static constexpr int kHasSmallDexPcBitOffset = kHasSmallDexRegisterMapBitOffset + 1;
-  static constexpr int kHasSmallNativePcBitOffset = kHasSmallDexPcBitOffset + 1;
+  static constexpr int kInlineInfoBitOffset = kHasInlineInfoBitOffset + 1;
+  static constexpr int kDexRegisterMapBitOffset = kInlineInfoBitOffset + 3;
+  static constexpr int kDexPcBitOffset = kDexRegisterMapBitOffset + 3;
+  static constexpr int kNativePcBitOffset = kDexPcBitOffset + 3;
+  static constexpr int kRegisterMaskBitOffset = kNativePcBitOffset + 3;
 
   MemoryRegion GetStackMaps() const {
     return region_.size() == 0
         ? MemoryRegion()
-        : region_.Subregion(kFixedSize, StackMapsSize());
+        : region_.Subregion(GetStackMapsOffset(), GetStackMapsSize());
   }
 
-  // Compute the size of a Dex register map starting at offset `origin` in
-  // `region_` and containing `number_of_dex_registers` locations.
-  size_t ComputeDexRegisterMapSize(uint32_t origin, uint32_t number_of_dex_registers) const {
-    // TODO: Ideally, we would like to use art::DexRegisterMap::Size or
-    // art::DexRegisterMap::FindLocationOffset, but the DexRegisterMap is not
-    // yet built.  Try to factor common code.
-    size_t offset =
-        origin + DexRegisterMap::GetDexRegisterMapLocationsOffset(number_of_dex_registers);
+  // Compute the size of the Dex register map associated to the stack map at
+  // `dex_register_map_offset_in_code_info`.
+  size_t ComputeDexRegisterMapSizeOf(uint32_t dex_register_map_offset_in_code_info,
+                                     uint16_t number_of_dex_registers) const {
+    // Offset where the actual mapping data starts within art::DexRegisterMap.
+    size_t location_mapping_data_offset_in_dex_register_map =
+        DexRegisterMap::GetLocationMappingDataOffset(number_of_dex_registers);
+    // Create a temporary art::DexRegisterMap to be able to call
+    // art::DexRegisterMap::GetNumberOfLiveDexRegisters and
+    DexRegisterMap dex_register_map_without_locations(
+        MemoryRegion(region_.Subregion(dex_register_map_offset_in_code_info,
+                                       location_mapping_data_offset_in_dex_register_map)));
+    size_t number_of_live_dex_registers =
+        dex_register_map_without_locations.GetNumberOfLiveDexRegisters(number_of_dex_registers);
+    size_t location_mapping_data_size_in_bits =
+        DexRegisterMap::SingleEntrySizeInBits(GetNumberOfDexRegisterLocationCatalogEntries())
+        * number_of_live_dex_registers;
+    size_t location_mapping_data_size_in_bytes =
+        RoundUp(location_mapping_data_size_in_bits, kBitsPerByte) / kBitsPerByte;
+    size_t dex_register_map_size =
+        location_mapping_data_offset_in_dex_register_map + location_mapping_data_size_in_bytes;
+    return dex_register_map_size;
+  }
 
-    // Create a temporary DexRegisterMap to be able to call DexRegisterMap.IsDexRegisterLive.
-    DexRegisterMap only_live_mask(MemoryRegion(region_.Subregion(origin, offset - origin)));
+  // Compute the size of a Dex register location catalog starting at offset `origin`
+  // in `region_` and containing `number_of_dex_locations` entries.
+  size_t ComputeDexRegisterLocationCatalogSize(uint32_t origin,
+                                               uint32_t number_of_dex_locations) const {
+    // TODO: Ideally, we would like to use art::DexRegisterLocationCatalog::Size or
+    // art::DexRegisterLocationCatalog::FindLocationOffset, but the
+    // DexRegisterLocationCatalog is not yet built.  Try to factor common code.
+    size_t offset = origin + DexRegisterLocationCatalog::kFixedSize;
 
-    // Skip the first `number_of_dex_registers - 1` entries.
-    for (uint16_t i = 0; i < number_of_dex_registers; ++i) {
-      if (only_live_mask.IsDexRegisterLive(i)) {
-        // Read the first next byte and inspect its first 3 bits to decide
-        // whether it is a short or a large location.
-        DexRegisterMap::ShortLocation first_byte =
-            region_.LoadUnaligned<DexRegisterMap::ShortLocation>(offset);
-        DexRegisterLocation::Kind kind =
-            DexRegisterMap::ExtractKindFromShortLocation(first_byte);
-        if (DexRegisterLocation::IsShortLocationKind(kind)) {
-          // Short location.  Skip the current byte.
-          offset += DexRegisterMap::SingleShortEntrySize();
-        } else {
-          // Large location.  Skip the 5 next bytes.
-          offset += DexRegisterMap::SingleLargeEntrySize();
-        }
+    // Skip the first `number_of_dex_locations - 1` entries.
+    for (uint16_t i = 0; i < number_of_dex_locations; ++i) {
+      // Read the first next byte and inspect its first 3 bits to decide
+      // whether it is a short or a large location.
+      DexRegisterLocationCatalog::ShortLocation first_byte =
+          region_.LoadUnaligned<DexRegisterLocationCatalog::ShortLocation>(offset);
+      DexRegisterLocation::Kind kind =
+          DexRegisterLocationCatalog::ExtractKindFromShortLocation(first_byte);
+      if (DexRegisterLocation::IsShortLocationKind(kind)) {
+        // Short location.  Skip the current byte.
+        offset += DexRegisterLocationCatalog::SingleShortEntrySize();
+      } else {
+        // Large location.  Skip the 5 next bytes.
+        offset += DexRegisterLocationCatalog::SingleLargeEntrySize();
       }
     }
     size_t size = offset - origin;
diff --git a/test/466-get-live-vreg/expected.txt b/test/466-get-live-vreg/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/466-get-live-vreg/expected.txt
diff --git a/test/466-get-live-vreg/get_live_vreg_jni.cc b/test/466-get-live-vreg/get_live_vreg_jni.cc
new file mode 100644
index 0000000..6715ba1
--- /dev/null
+++ b/test/466-get-live-vreg/get_live_vreg_jni.cc
@@ -0,0 +1,71 @@
+/*
+ * 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 "arch/context.h"
+#include "jni.h"
+#include "mirror/art_method-inl.h"
+#include "scoped_thread_state_change.h"
+#include "stack.h"
+#include "thread.h"
+
+namespace art {
+
+namespace {
+
+class TestVisitor : public StackVisitor {
+ public:
+  TestVisitor(Thread* thread, Context* context) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : StackVisitor(thread, context) {}
+
+  bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::ArtMethod* m = GetMethod();
+    std::string m_name(m->GetName());
+
+    if (m_name.compare("testLiveArgument") == 0) {
+      found_method_ = true;
+      uint32_t value = 0;
+      CHECK(GetVReg(m, 0, kIntVReg, &value));
+      CHECK_EQ(value, 42u);
+    } else if (m_name.compare("testIntervalHole") == 0) {
+      found_method_ = true;
+      uint32_t value = 0;
+      if (m->IsOptimized(sizeof(void*))) {
+        CHECK_EQ(GetVReg(m, 0, kIntVReg, &value), false);
+      } else {
+        CHECK(GetVReg(m, 0, kIntVReg, &value));
+        CHECK_EQ(value, 1u);
+      }
+    }
+
+    return true;
+  }
+
+  // Value returned to Java to ensure the methods testSimpleVReg and testPairVReg
+  // have been found and tested.
+  bool found_method_ = false;
+};
+
+extern "C" JNIEXPORT void JNICALL Java_Main_doStaticNativeCallLiveVreg(JNIEnv*, jclass) {
+  ScopedObjectAccess soa(Thread::Current());
+  std::unique_ptr<Context> context(Context::Create());
+  TestVisitor visitor(soa.Self(), context.get());
+  visitor.WalkStack();
+  CHECK(visitor.found_method_);
+}
+
+}  // namespace
+
+}  // namespace art
diff --git a/test/466-get-live-vreg/info.txt b/test/466-get-live-vreg/info.txt
new file mode 100644
index 0000000..cbe0c9a
--- /dev/null
+++ b/test/466-get-live-vreg/info.txt
@@ -0,0 +1,3 @@
+Tests for inspecting live DEX registers. The test must
+also pass for non-debuggable, as we need to know live DEX registers
+when de-opting.
diff --git a/test/466-get-live-vreg/src/Main.java b/test/466-get-live-vreg/src/Main.java
new file mode 100644
index 0000000..3118085
--- /dev/null
+++ b/test/466-get-live-vreg/src/Main.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+public class Main {
+  public Main() {
+  }
+
+  static int testLiveArgument(int arg) {
+    doStaticNativeCallLiveVreg();
+    return arg;
+  }
+
+  static void moveArgToCalleeSave() {
+    try {
+      Thread.sleep(0);
+    } catch (Exception e) {
+      throw new Error(e);
+    }
+  }
+
+  static void testIntervalHole(int arg, boolean test) {
+    // Move the argument to callee save to ensure it is in
+    // a readable register.
+    moveArgToCalleeSave();
+    if (test) {
+      staticField1 = arg;
+      // The environment use of `arg` should not make it live.
+      doStaticNativeCallLiveVreg();
+    } else {
+      staticField2 = arg;
+      // The environment use of `arg` should not make it live.
+      doStaticNativeCallLiveVreg();
+    }
+  }
+
+  static native void doStaticNativeCallLiveVreg();
+
+  static {
+    System.loadLibrary("arttest");
+  }
+
+  public static void main(String[] args) {
+    if (testLiveArgument(42) != 42) {
+      throw new Error("Expected 42");
+    }
+
+    if (testLiveArgument(42) != 42) {
+      throw new Error("Expected 42");
+    }
+
+    testIntervalHole(1, true);
+    testIntervalHole(1, false);
+  }
+
+  static int staticField1;
+  static int staticField2;
+}
diff --git a/test/468-bool-simplifier-regression/expected.txt b/test/468-bool-simplifier-regression/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/468-bool-simplifier-regression/expected.txt
diff --git a/test/468-bool-simplifier-regression/info.txt b/test/468-bool-simplifier-regression/info.txt
new file mode 100644
index 0000000..0a46584
--- /dev/null
+++ b/test/468-bool-simplifier-regression/info.txt
@@ -0,0 +1,2 @@
+Regression test for optimizing's boolean simplifier
+that used to trip when a boolean value was the input of an If.
diff --git a/test/468-bool-simplifier-regression/smali/TestCase.smali b/test/468-bool-simplifier-regression/smali/TestCase.smali
new file mode 100644
index 0000000..f36304d
--- /dev/null
+++ b/test/468-bool-simplifier-regression/smali/TestCase.smali
@@ -0,0 +1,32 @@
+# 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.
+
+.class public LTestCase;
+
+.super Ljava/lang/Object;
+
+.field public static value:Z
+
+.method public static testCase()Z
+    .registers 2
+    sget-boolean v0, LTestCase;->value:Z
+    const/4 v1, 1
+    if-eq v0, v1, :label1
+    const/4 v1, 1
+    goto :label2
+    :label1
+    const/4 v1, 0
+    :label2
+    return v1
+.end method
diff --git a/test/468-bool-simplifier-regression/src/Main.java b/test/468-bool-simplifier-regression/src/Main.java
new file mode 100644
index 0000000..1dd27c9
--- /dev/null
+++ b/test/468-bool-simplifier-regression/src/Main.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.*;
+
+public class Main {
+  public static boolean runTest(boolean input) throws Exception {
+    Class<?> c = Class.forName("TestCase");
+    Method m = c.getMethod("testCase");
+    Field f = c.getField("value");
+    f.set(null, (Boolean) input);
+    return (Boolean) m.invoke(null);
+  }
+
+  public static void main(String[] args) throws Exception {
+    if (runTest(true) != false) {
+      throw new Error("Expected false, got true");
+    }
+
+    if (runTest(false) != true) {
+      throw new Error("Expected true, got false");
+    }
+  }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 0cafb06..5e768ee 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -31,7 +31,8 @@
   454-get-vreg/get_vreg_jni.cc \
   455-set-vreg/set_vreg_jni.cc \
   457-regs/regs_jni.cc \
-  461-get-reference-vreg/get_reference_vreg_jni.cc
+  461-get-reference-vreg/get_reference_vreg_jni.cc \
+  466-get-live-vreg/get_live_vreg_jni.cc
 
 ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
 ifdef TARGET_2ND_ARCH
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 28fbc3e..39afc67 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -313,6 +313,7 @@
   455-set-vreg \
   457-regs \
   461-get-reference-vreg \
+  466-get-live-vreg \
 
 ifneq (,$(filter ndebug,$(RUN_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),ndebug,$(PREBUILD_TYPES), \
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index e52fc3f..90c01f5 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -43,11 +43,10 @@
 # We use the art script on target to ensure the runner and the debuggee share the same
 # image.
 vm_command="--vm-command=$art"
+image_compiler_option=""
 
 while true; do
   if [[ "$1" == "--mode=host" ]]; then
-    echo "Host testing currently disabled because of buildbot. Investigating..."
-    exit 0
     # Specify bash explicitly since the art script cannot, since it has to run on the device
     # with mksh.
     art="bash out/host/linux-x86/bin/art"
@@ -57,6 +56,9 @@
     device_dir=""
     # Vogar knows which VM to use on host.
     vm_command=""
+    # We only compile the image on the host. Note that not providing this option
+    # puts us below the adb command limit for vogar.
+    image_compiler_option="--vm-arg -Ximage-compiler-option --vm-arg --debuggable"
     shift
   elif [[ $1 == -Ximage:* ]]; then
     image="$1"
@@ -74,11 +76,11 @@
       --verbose \
       $args \
       $device_dir \
-      --vm-arg -Ximage-compiler-option \
-      --vm-arg --debuggable \
+      $image_compiler_option \
       --timeout 600 \
       --vm-arg -Djpda.settings.verbose=true \
       --vm-arg -Djpda.settings.syncPort=34016 \
+      --vm-arg -Djpda.settings.transportAddress=127.0.0.1:55107 \
       --vm-arg -Djpda.settings.debuggeeJavaPath="$art $image $debuggee_args" \
       --classpath $test_jar \
       --classpath $junit_jar \