Merge "Fixed layout for dex caches in boot image."
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..e9b9b5d 100644
--- a/compiler/dex/quick/arm64/int_arm64.cc
+++ b/compiler/dex/quick/arm64/int_arm64.cc
@@ -18,6 +18,7 @@
 
 #include "codegen_arm64.h"
 
+#include "arch/arm64/instruction_set_features_arm64.h"
 #include "arch/instruction_set_features.h"
 #include "arm64_lir.h"
 #include "base/logging.h"
@@ -943,6 +944,28 @@
   lir->target = target;
 }
 
+bool Arm64Mir2Lir::CanUseOpPcRelDexCacheArrayLoad() const {
+  if (cu_->compiler_driver->GetInstructionSetFeatures()->AsArm64InstructionSetFeatures()
+      ->NeedFixCortexA53_843419()) {
+    // TODO: Implement link-time workaround in OatWriter so that we can use ADRP on Cortex-A53.
+    return false;
+  }
+  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/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..05599e1 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -24,8 +24,11 @@
 #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,9 +46,9 @@
 
 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.
@@ -56,19 +59,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 +86,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 +117,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 +128,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),
@@ -261,18 +283,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 +324,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 +355,18 @@
   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(),
+  explicit Arm64RelativePatcher(OatWriter* writer)
+      : ArmBaseRelativePatcher(writer, kArm64, 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 & 3u, 0u);
     DCHECK_EQ(patch_offset & 3u, 0u);
@@ -345,17 +374,48 @@
     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;
+      insn = (insn & 0x9f00001fu) |
+          ((disp & 0x00003000u) << (29 - 12)) |
+          ((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.
+          ((disp & 0x80000000u) >> (31 - 23));
+      // Write the new ADRP.
+      SetInsn(code, literal_offset, insn);
+    } else {
+      DCHECK_EQ(insn & 0xfffffc00, 0xb9400000);  // LDR 32-bit with imm12 == 0 (unset).
+      DCHECK_EQ(GetInsn(code, pc_insn_offset) & 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:
@@ -374,13 +434,34 @@
     return thunk_code;
   }
 
+  uint32_t GetInsn(std::vector<uint8_t>* code, uint32_t offset) {
+    DCHECK_LE(offset + 4u, code->size());
+    DCHECK_EQ(offset & 3u, 0u);
+    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);
+  }
+
+  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);
+  DISALLOW_COPY_AND_ASSIGN(Arm64RelativePatcher);
 };
 
 #define DCHECK_OFFSET() \
@@ -445,18 +526,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 +787,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,7 +803,7 @@
 
       const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
       CHECK(quick_code != nullptr);
-      offset_ = writer_->relative_call_patcher_->ReserveSpace(offset_, compiled_method);
+      offset_ = writer_->relative_patcher_->ReserveSpace(offset_, compiled_method);
       offset_ = compiled_method->AlignCode(offset_);
       DCHECK_ALIGNED_PARAM(offset_,
                            GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
@@ -790,7 +871,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 +932,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 +1090,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;
@@ -1001,7 +1113,7 @@
         // 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_);
+        offset_ = writer_->relative_patcher_->WriteThunks(out, offset_);
         if (offset_ == 0u) {
           ReportWriteFailure("relative call thunk", it);
           return false;
@@ -1039,15 +1151,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 +1252,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
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index fd2ccae..cbf768f 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
 //
@@ -325,51 +326,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/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/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/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/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/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/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