Quick: PC-relative loads from dex cache arrays on x86.

Rewrite all PC-relative addressing on x86 and implement
PC-relative loads from dex cache arrays. Don't adjust the
base to point to the start of the method, let it point to
the anchor, i.e. the target of the "call +0" insn.

Change-Id: Ic22544a8bc0c5e49eb00a75154dc8f3ead816989
diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc
index c3371cf..25ea694 100644
--- a/compiler/dex/quick/arm/utility_arm.cc
+++ b/compiler/dex/quick/arm/utility_arm.cc
@@ -1273,13 +1273,14 @@
 
   if (pc_rel_temp_ != nullptr) {
     // Now, if the dex cache array base temp is used only once outside any loops (weight = 1),
-    // avoid the promotion, otherwise boost the weight by factor 4 because the full PC-relative
-    // load sequence is 4 instructions long.
+    // avoid the promotion, otherwise boost the weight by factor 3 because the full PC-relative
+    // load sequence is 4 instructions long and by promoting the PC base we save up to 3
+    // instructions per use.
     int p_map_idx = SRegToPMap(pc_rel_temp_->s_reg_low);
     if (core_counts[p_map_idx].count == 1) {
       core_counts[p_map_idx].count = 0;
     } else {
-      core_counts[p_map_idx].count *= 4;
+      core_counts[p_map_idx].count *= 3;
     }
   }
 }
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 232a228..f8594a2 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -541,13 +541,11 @@
         DCHECK(tab_rec->anchor->flags.fixup != kFixupNone);
         bx_offset = tab_rec->anchor->offset + 4;
         break;
-      case kX86:
-        bx_offset = 0;
-        break;
       case kX86_64:
         // RIP relative to switch table.
         bx_offset = tab_rec->offset;
         break;
+      case kX86:
       case kArm64:
       case kMips:
       case kMips64:
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 1624c84..e9752fd 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -635,7 +635,7 @@
     RegisterClass ShortyToRegClass(char shorty_type);
     RegisterClass LocToRegClass(RegLocation loc);
     int ComputeFrameSize();
-    virtual void Materialize();
+    void Materialize();
     virtual CompiledMethod* GetCompiledMethod();
     void MarkSafepointPC(LIR* inst);
     void MarkSafepointPCAfter(LIR* after);
@@ -776,7 +776,7 @@
      */
     virtual RegLocation EvalLoc(RegLocation loc, int reg_class, bool update);
 
-    void AnalyzeMIR(RefCounts* core_counts, MIR* mir, uint32_t weight);
+    virtual void AnalyzeMIR(RefCounts* core_counts, MIR* mir, uint32_t weight);
     virtual void CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t num_regs);
     void DumpCounts(const RefCounts* arr, int size, const char* msg);
     virtual void DoPromotion();
diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc
index 118ab1d..af19f5e 100644
--- a/compiler/dex/quick/x86/assemble_x86.cc
+++ b/compiler/dex/quick/x86/assemble_x86.cc
@@ -544,7 +544,6 @@
   { kX86CallI, kCall, IS_UNARY_OP  | IS_BRANCH,                             { 0,             0, 0xE8, 0,    0, 0, 0, 4, false }, "CallI", "!0d" },
   { kX86Ret,   kNullary, NO_OPERAND | IS_BRANCH,                            { 0,             0, 0xC3, 0,    0, 0, 0, 0, false }, "Ret", "" },
 
-  { kX86StartOfMethod, kMacro,  IS_UNARY_OP | REG_DEF0 | SETS_CCODES,  { 0, 0, 0,    0, 0, 0, 0, 0, false }, "StartOfMethod", "!0r" },
   { kX86PcRelLoadRA,   kPcRel,  IS_LOAD | IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8B, 0, 0, 0, 0, 0, false }, "PcRelLoadRA",   "!0r,[!1r+!2r<<!3d+!4p]" },
   { kX86PcRelAdr,      kPcRel,  IS_LOAD | IS_BINARY_OP | REG_DEF0,     { 0, 0, 0xB8, 0, 0, 0, 0, 4, false }, "PcRelAdr",      "!0r,!1p" },
   { kX86RepneScasw,    kNullary, NO_OPERAND | REG_USEA | REG_USEC | SETS_CCODES, { 0x66, 0xF2, 0xAF, 0, 0, 0, 0, 0, false }, "RepNE ScasW", "" },
@@ -865,13 +864,6 @@
         DCHECK_EQ(entry->opcode, kX86PcRelAdr);
         return 5;  // opcode with reg + 4 byte immediate
       }
-    case kMacro:  // lir operands - 0: reg
-      DCHECK_EQ(lir->opcode, static_cast<int>(kX86StartOfMethod));
-      return 5 /* call opcode + 4 byte displacement */ + 1 /* pop reg */ +
-          ComputeSize(&X86Mir2Lir::EncodingMap[cu_->target64 ? kX86Sub64RI : kX86Sub32RI],
-                      lir->operands[0], NO_REG, NO_REG, 0) -
-              // Shorter ax encoding.
-              (RegStorage::RegNum(lir->operands[0]) == rs_rAX.GetRegNum()  ? 1 : 0);
     case kUnimplemented:
       break;
   }
@@ -1586,8 +1578,8 @@
                            int32_t raw_index, int scale, int32_t table_or_disp) {
   int disp;
   if (entry->opcode == kX86PcRelLoadRA) {
-    const EmbeddedData* tab_rec = UnwrapPointer<EmbeddedData>(table_or_disp);
-    disp = tab_rec->offset;
+    const SwitchTable* tab_rec = UnwrapPointer<SwitchTable>(table_or_disp);
+    disp = tab_rec->offset - tab_rec->anchor->offset;
   } else {
     DCHECK(entry->opcode == kX86PcRelAdr);
     const EmbeddedData* tab_rec = UnwrapPointer<EmbeddedData>(raw_base_or_table);
@@ -1621,23 +1613,6 @@
   DCHECK_EQ(0, entry->skeleton.ax_opcode);
 }
 
-void X86Mir2Lir::EmitMacro(const X86EncodingMap* entry, int32_t raw_reg, int32_t offset) {
-  DCHECK_EQ(entry->opcode, kX86StartOfMethod) << entry->name;
-  DCHECK_EQ(false, entry->skeleton.r8_form);
-  EmitPrefix(entry, raw_reg, NO_REG, NO_REG);
-  code_buffer_.push_back(0xE8);  // call +0
-  code_buffer_.push_back(0);
-  code_buffer_.push_back(0);
-  code_buffer_.push_back(0);
-  code_buffer_.push_back(0);
-
-  uint8_t low_reg = LowRegisterBits(raw_reg);
-  code_buffer_.push_back(0x58 + low_reg);  // pop reg
-
-  EmitRegImm(&X86Mir2Lir::EncodingMap[cu_->target64 ? kX86Sub64RI : kX86Sub32RI],
-             raw_reg, offset + 5 /* size of call +0 */);
-}
-
 void X86Mir2Lir::EmitUnimplemented(const X86EncodingMap* entry, LIR* lir) {
   UNIMPLEMENTED(WARNING) << "encoding kind for " << entry->name << " "
                          << BuildInsnString(entry->fmt, lir, 0);
@@ -1780,7 +1755,8 @@
               // Offset is relative to next instruction.
               lir->operands[2] = target - (lir->offset + lir->flags.size);
             } else {
-              lir->operands[2] = target;
+              const LIR* anchor = UnwrapPointer<LIR>(lir->operands[4]);
+              lir->operands[2] = target - anchor->offset;
               int newSize = GetInsnSize(lir);
               if (newSize != lir->flags.size) {
                 lir->flags.size = newSize;
@@ -1951,9 +1927,6 @@
         EmitPcRel(entry, lir->operands[0], lir->operands[1], lir->operands[2],
                   lir->operands[3], lir->operands[4]);
         break;
-      case kMacro:  // lir operands - 0: reg
-        EmitMacro(entry, lir->operands[0], lir->offset);
-        break;
       case kNop:  // TODO: these instruction kinds are missing implementations.
       case kThreadReg:
       case kRegArrayImm:
@@ -2044,9 +2017,13 @@
   cu_->NewTimingSplit("Assemble");
 
   // We will remove the method address if we never ended up using it
-  if (store_method_addr_ && !store_method_addr_used_) {
-    setup_method_address_[0]->flags.is_nop = true;
-    setup_method_address_[1]->flags.is_nop = true;
+  if (pc_rel_base_reg_.Valid() && !pc_rel_base_reg_used_) {
+    if (kIsDebugBuild) {
+      LOG(WARNING) << "PC-relative addressing base promoted but unused in "
+          << PrettyMethod(cu_->method_idx, *cu_->dex_file);
+    }
+    setup_pc_rel_base_reg_->flags.is_nop = true;
+    NEXT_LIR(setup_pc_rel_base_reg_)->flags.is_nop = true;
   }
 
   AssignOffsets();
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index 18fae17..3041458 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -97,29 +97,23 @@
 
     // Add the offset from the table to the table base.
     OpRegReg(kOpAdd, addr_for_jump, table_base);
+    tab_rec->anchor = nullptr;  // Unused for x86-64.
   } else {
-    // Materialize a pointer to the switch table.
-    RegStorage start_of_method_reg;
-    if (base_of_code_ != nullptr) {
-      // We can use the saved value.
-      RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
-      rl_method = LoadValue(rl_method, kCoreReg);
-      start_of_method_reg = rl_method.reg;
-      store_method_addr_used_ = true;
-    } else {
-      start_of_method_reg = AllocTempRef();
-      NewLIR1(kX86StartOfMethod, start_of_method_reg.GetReg());
-    }
+    // Get the PC to a register and get the anchor.
+    LIR* anchor;
+    RegStorage r_pc = GetPcAndAnchor(&anchor);
+
     // Load the displacement from the switch table.
     addr_for_jump = AllocTemp();
-    NewLIR5(kX86PcRelLoadRA, addr_for_jump.GetReg(), start_of_method_reg.GetReg(), keyReg.GetReg(),
+    NewLIR5(kX86PcRelLoadRA, addr_for_jump.GetReg(), r_pc.GetReg(), keyReg.GetReg(),
             2, WrapPointer(tab_rec));
-    // Add displacement to start of method.
-    OpRegReg(kOpAdd, addr_for_jump, start_of_method_reg);
+    // Add displacement and r_pc to get the address.
+    OpRegReg(kOpAdd, addr_for_jump, r_pc);
+    tab_rec->anchor = anchor;
   }
 
   // ..and go!
-  tab_rec->anchor = NewLIR1(kX86JmpR, addr_for_jump.GetReg());
+  NewLIR1(kX86JmpR, addr_for_jump.GetReg());
 
   /* branch_over target here */
   LIR* target = NewLIR0(kPseudoTargetLabel);
@@ -235,14 +229,12 @@
 
   FlushIns(ArgLocs, rl_method);
 
-  if (base_of_code_ != nullptr) {
-    RegStorage method_start = TargetPtrReg(kArg0);
-    // We have been asked to save the address of the method start for later use.
-    setup_method_address_[0] = NewLIR1(kX86StartOfMethod, method_start.GetReg());
-    int displacement = SRegOffset(base_of_code_->s_reg_low);
-    // Native pointer - must be natural word size.
-    setup_method_address_[1] = StoreBaseDisp(rs_rSP, displacement, method_start,
-                                             cu_->target64 ? k64 : k32, kNotVolatile);
+  // We can promote the PC of an anchor for PC-relative addressing to a register
+  // if it's used at least twice. Without investigating where we should lazily
+  // load the reference, we conveniently load it after flushing inputs.
+  if (pc_rel_base_reg_.Valid()) {
+    DCHECK(!cu_->target64);
+    setup_pc_rel_base_reg_ = OpLoadPc(pc_rel_base_reg_);
   }
 
   FreeTemp(arg0);
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index a98a99e..72580a3 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -28,7 +28,7 @@
 
 namespace art {
 
-class X86Mir2Lir : public Mir2Lir {
+class X86Mir2Lir FINAL : public Mir2Lir {
  protected:
   class InToRegStorageX86_64Mapper : public InToRegStorageMapper {
    public:
@@ -375,6 +375,10 @@
    */
   LIR* GenCallInsn(const MirMethodLoweringInfo& method_info) OVERRIDE;
 
+  void AnalyzeMIR(RefCounts* core_counts, MIR* mir, uint32_t weight) OVERRIDE;
+  void CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t num_regs) OVERRIDE;
+  void DoPromotion() OVERRIDE;
+
   /*
    * @brief Handle x86 specific literals
    */
@@ -488,7 +492,6 @@
   void EmitCallThread(const X86EncodingMap* entry, int32_t disp);
   void EmitPcRel(const X86EncodingMap* entry, int32_t raw_reg, int32_t raw_base_or_table,
                  int32_t raw_index, int scale, int32_t table_or_disp);
-  void EmitMacro(const X86EncodingMap* entry, int32_t raw_reg, int32_t offset);
   void EmitUnimplemented(const X86EncodingMap* entry, LIR* lir);
   void GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1,
                                 int64_t val, ConditionCode ccode);
@@ -859,12 +862,6 @@
   void SpillFPRegs();
 
   /*
-   * @brief Perform MIR analysis before compiling method.
-   * @note Invokes Mir2LiR::Materialize after analysis.
-   */
-  void Materialize();
-
-  /*
    * Mir2Lir's UpdateLoc() looks to see if the Dalvik value is currently live in any temp register
    * without regard to data type.  In practice, this can result in UpdateLoc returning a
    * location record for a Dalvik float value in a core register, and vis-versa.  For targets
@@ -878,67 +875,39 @@
   RegLocation UpdateLocWideTyped(RegLocation loc);
 
   /*
-   * @brief Analyze MIR before generating code, to prepare for the code generation.
-   */
-  void AnalyzeMIR();
-
-  /*
-   * @brief Analyze one basic block.
-   * @param bb Basic block to analyze.
-   */
-  void AnalyzeBB(BasicBlock* bb);
-
-  /*
-   * @brief Analyze one extended MIR instruction
-   * @param opcode MIR instruction opcode.
-   * @param bb Basic block containing instruction.
-   * @param mir Extended instruction to analyze.
-   */
-  void AnalyzeExtendedMIR(int opcode, BasicBlock* bb, MIR* mir);
-
-  /*
-   * @brief Analyze one MIR instruction
-   * @param opcode MIR instruction opcode.
-   * @param bb Basic block containing instruction.
-   * @param mir Instruction to analyze.
-   */
-  virtual void AnalyzeMIR(int opcode, BasicBlock* bb, MIR* mir);
-
-  /*
    * @brief Analyze one MIR float/double instruction
    * @param opcode MIR instruction opcode.
-   * @param bb Basic block containing instruction.
    * @param mir Instruction to analyze.
+   * @return true iff the instruction needs to load a literal using PC-relative addressing.
    */
-  virtual void AnalyzeFPInstruction(int opcode, BasicBlock* bb, MIR* mir);
+  bool AnalyzeFPInstruction(int opcode, MIR* mir);
 
   /*
    * @brief Analyze one use of a double operand.
    * @param rl_use Double RegLocation for the operand.
+   * @return true iff the instruction needs to load a literal using PC-relative addressing.
    */
-  void AnalyzeDoubleUse(RegLocation rl_use);
+  bool AnalyzeDoubleUse(RegLocation rl_use);
 
   /*
    * @brief Analyze one invoke-static MIR instruction
-   * @param opcode MIR instruction opcode.
-   * @param bb Basic block containing instruction.
    * @param mir Instruction to analyze.
+   * @return true iff the instruction needs to load a literal using PC-relative addressing.
    */
-  void AnalyzeInvokeStatic(int opcode, BasicBlock* bb, MIR* mir);
+  bool AnalyzeInvokeStaticIntrinsic(MIR* mir);
 
   // Information derived from analysis of MIR
 
-  // The compiler temporary for the code address of the method.
-  CompilerTemp *base_of_code_;
+  // The base register for PC-relative addressing if promoted (32-bit only).
+  RegStorage pc_rel_base_reg_;
 
-  // Have we decided to compute a ptr to code and store in temporary VR?
-  bool store_method_addr_;
+  // Have we actually used the pc_rel_base_reg_?
+  bool pc_rel_base_reg_used_;
 
-  // Have we used the stored method address?
-  bool store_method_addr_used_;
-
-  // Instructions to remove if we didn't use the stored method address.
-  LIR* setup_method_address_[2];
+  // Pointer to the "call +0" insn that sets up the promoted register for PC-relative addressing.
+  // The anchor "pop" insn is NEXT_LIR(setup_pc_rel_base_reg_). The whole "call +0; pop <reg>"
+  // sequence will be removed in AssembleLIR() if we do not actually use PC-relative addressing.
+  LIR* setup_pc_rel_base_reg_;  // There are 2 chained insns (no reordering allowed).
 
   // Instructions needing patching with Method* values.
   ArenaVector<LIR*> method_address_insns_;
@@ -992,6 +961,14 @@
                                uintptr_t direct_code, uintptr_t direct_method,
                                InvokeType type);
 
+  LIR* OpLoadPc(RegStorage r_dest);
+  RegStorage GetPcAndAnchor(LIR** anchor, RegStorage r_tmp = RegStorage::InvalidReg());
+
+  // When we don't know the proper offset for the value, pick one that will force
+  // 4 byte offset.  We will fix this up in the assembler or linker later to have
+  // the right value.
+  static constexpr int kDummy32BitOffset = 256;
+
   static const X86EncodingMap EncodingMap[kX86Last];
 
   friend std::ostream& operator<<(std::ostream& os, const X86OpCode& rhs);
diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc
index d8616a7..cfe0480 100755
--- a/compiler/dex/quick/x86/fp_x86.cc
+++ b/compiler/dex/quick/x86/fp_x86.cc
@@ -756,24 +756,6 @@
     branch_nan->target = NewLIR0(kPseudoTargetLabel);
     LoadConstantWide(rl_result.reg, INT64_C(0x7ff8000000000000));
 
-    // The base_of_code_ compiler temp is non-null when it is reserved
-    // for being able to do data accesses relative to method start.
-    if (base_of_code_ != nullptr) {
-      // Loading from the constant pool may have used base of code register.
-      // However, the code here generates logic in diamond shape and not all
-      // paths load base of code register. Therefore, we ensure it is clobbered so
-      // that the temp caching system does not believe it is live at merge point.
-      RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
-      if (rl_method.wide) {
-        rl_method = UpdateLocWide(rl_method);
-      } else {
-        rl_method = UpdateLoc(rl_method);
-      }
-      if (rl_method.location == kLocPhysReg) {
-        Clobber(rl_method.reg);
-      }
-    }
-
     LIR* branch_exit_nan = NewLIR1(kX86Jmp8, 0);
     // Handle Min/Max. Copy greater/lesser value from src2.
     branch_cond1->target = NewLIR0(kPseudoTargetLabel);
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 5def5c8..075f721 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -1324,11 +1324,6 @@
   return true;
 }
 
-// When we don't know the proper offset for the value, pick one that will force
-// 4 byte offset.  We will fix this up in the assembler or linker later to have
-// the right value.
-static constexpr int kDummy32BitOffset = 256;
-
 void X86Mir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
   if (cu_->target64) {
     // We can do this directly using RIP addressing.
@@ -1339,27 +1334,48 @@
     return;
   }
 
-  CHECK(base_of_code_ != nullptr);
-
-  // Address the start of the method
-  RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
-  if (rl_method.wide) {
-    LoadValueDirectWideFixed(rl_method, reg);
-  } else {
-    LoadValueDirectFixed(rl_method, reg);
-  }
-  store_method_addr_used_ = true;
+  // Get the PC to a register and get the anchor.
+  LIR* anchor;
+  RegStorage r_pc = GetPcAndAnchor(&anchor);
 
   // Load the proper value from the literal area.
   ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
-  LIR* res = NewLIR3(kX86Mov32RM, reg.GetReg(), reg.GetReg(), kDummy32BitOffset);
+  LIR* res = NewLIR3(kX86Mov32RM, reg.GetReg(), r_pc.GetReg(), kDummy32BitOffset);
+  res->operands[4] = WrapPointer(anchor);
   res->target = target;
   res->flags.fixup = kFixupLoad;
 }
 
 bool X86Mir2Lir::CanUseOpPcRelDexCacheArrayLoad() const {
-  // TODO: Implement for 32-bit.
-  return cu_->target64 && dex_cache_arrays_layout_.Valid();
+  return dex_cache_arrays_layout_.Valid();
+}
+
+LIR* X86Mir2Lir::OpLoadPc(RegStorage r_dest) {
+  DCHECK(!cu_->target64);
+  LIR* call = NewLIR1(kX86CallI, 0);
+  call->flags.fixup = kFixupLabel;
+  LIR* pop = NewLIR1(kX86Pop32R, r_dest.GetReg());
+  pop->flags.fixup = kFixupLabel;
+  DCHECK(NEXT_LIR(call) == pop);
+  return call;
+}
+
+RegStorage X86Mir2Lir::GetPcAndAnchor(LIR** anchor, RegStorage r_tmp) {
+  if (pc_rel_base_reg_.Valid()) {
+    DCHECK(setup_pc_rel_base_reg_ != nullptr);
+    *anchor = NEXT_LIR(setup_pc_rel_base_reg_);
+    DCHECK(*anchor != nullptr);
+    DCHECK_EQ((*anchor)->opcode, kX86Pop32R);
+    pc_rel_base_reg_used_ = true;
+    return pc_rel_base_reg_;
+  } else {
+    RegStorage r_pc = r_tmp.Valid() ? r_tmp : AllocTempRef();
+    LIR* load_pc = OpLoadPc(r_pc);
+    *anchor = NEXT_LIR(load_pc);
+    DCHECK(*anchor != nullptr);
+    DCHECK_EQ((*anchor)->opcode, kX86Pop32R);
+    return r_pc;
+  }
 }
 
 void X86Mir2Lir::OpPcRelDexCacheArrayLoad(const DexFile* dex_file, int offset,
@@ -1369,11 +1385,18 @@
     mov->flags.fixup = kFixupLabel;
     mov->operands[3] = WrapPointer(dex_file);
     mov->operands[4] = offset;
+    mov->target = mov;  // Used for pc_insn_offset (not used by x86-64 relative patcher).
     dex_cache_access_insns_.push_back(mov);
   } else {
-    // TODO: Implement for 32-bit.
-    LOG(FATAL) << "Unimplemented.";
-    UNREACHABLE();
+    // Get the PC to a register and get the anchor. Use r_dest for the temp if needed.
+    LIR* anchor;
+    RegStorage r_pc = GetPcAndAnchor(&anchor, r_dest);
+    LIR* mov = NewLIR3(kX86Mov32RM, r_dest.GetReg(), r_pc.GetReg(), kDummy32BitOffset);
+    mov->flags.fixup = kFixupLabel;
+    mov->operands[3] = WrapPointer(dex_file);
+    mov->operands[4] = offset;
+    mov->target = anchor;  // Used for pc_insn_offset.
+    dex_cache_access_insns_.push_back(mov);
   }
 }
 
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index 081f80f..1def88e 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -824,7 +824,9 @@
 X86Mir2Lir::X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
     : Mir2Lir(cu, mir_graph, arena),
       in_to_reg_storage_x86_64_mapper_(this), in_to_reg_storage_x86_mapper_(this),
-      base_of_code_(nullptr), store_method_addr_(false), store_method_addr_used_(false),
+      pc_rel_base_reg_(RegStorage::InvalidReg()),
+      pc_rel_base_reg_used_(false),
+      setup_pc_rel_base_reg_(nullptr),
       method_address_insns_(arena->Adapter()),
       class_type_address_insns_(arena->Adapter()),
       call_method_insns_(arena->Adapter()),
@@ -833,12 +835,11 @@
   method_address_insns_.reserve(100);
   class_type_address_insns_.reserve(100);
   call_method_insns_.reserve(100);
-  store_method_addr_used_ = false;
-    for (int i = 0; i < kX86Last; i++) {
-      DCHECK_EQ(X86Mir2Lir::EncodingMap[i].opcode, i)
-          << "Encoding order for " << X86Mir2Lir::EncodingMap[i].name
-          << " is wrong: expecting " << i << ", seeing "
-          << static_cast<int>(X86Mir2Lir::EncodingMap[i].opcode);
+  for (int i = 0; i < kX86Last; i++) {
+    DCHECK_EQ(X86Mir2Lir::EncodingMap[i].opcode, i)
+        << "Encoding order for " << X86Mir2Lir::EncodingMap[i].name
+        << " is wrong: expecting " << i << ", seeing "
+        << static_cast<int>(X86Mir2Lir::EncodingMap[i].opcode);
   }
 }
 
@@ -923,14 +924,6 @@
              << ", orig: " << loc.orig_sreg;
 }
 
-void X86Mir2Lir::Materialize() {
-  // A good place to put the analysis before starting.
-  AnalyzeMIR();
-
-  // Now continue with regular code generation.
-  Mir2Lir::Materialize();
-}
-
 void X86Mir2Lir::LoadMethodAddress(const MethodReference& target_method, InvokeType type,
                                    SpecialTargetRegister symbolic_reg) {
   /*
@@ -1105,7 +1098,8 @@
     // The offset to patch is the last 4 bytes of the instruction.
     int patch_offset = p->offset + p->flags.size - 4;
     DCHECK(!p->flags.is_nop);
-    patches_.push_back(LinkerPatch::DexCacheArrayPatch(patch_offset, dex_file, p->offset, offset));
+    patches_.push_back(LinkerPatch::DexCacheArrayPatch(patch_offset, dex_file,
+                                                       p->target->offset, offset));
   }
 
   // And do the normal processing.
@@ -1560,20 +1554,17 @@
   LIR* load;
   ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
   if (cu_->target64) {
-    load = NewLIR3(opcode, reg, kRIPReg, 256 /* bogus */);
+    load = NewLIR3(opcode, reg, kRIPReg, kDummy32BitOffset);
   } else {
-    // Address the start of the method.
-    RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
-    if (rl_method.wide) {
-      rl_method = LoadValueWide(rl_method, kCoreReg);
-    } else {
-      rl_method = LoadValue(rl_method, kCoreReg);
+    // Get the PC to a register and get the anchor.
+    LIR* anchor;
+    RegStorage r_pc = GetPcAndAnchor(&anchor);
+
+    load = NewLIR3(opcode, reg, r_pc.GetReg(), kDummy32BitOffset);
+    load->operands[4] = WrapPointer(anchor);
+    if (IsTemp(r_pc)) {
+      FreeTemp(r_pc);
     }
-
-    load = NewLIR3(opcode, reg, rl_method.reg.GetReg(), 256 /* bogus */);
-
-    // The literal pool needs position independent logic.
-    store_method_addr_used_ = true;
   }
   load->flags.fixup = kFixupLoad;
   load->target = data_target;
diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc
index 893b98a..efcb9ee 100644
--- a/compiler/dex/quick/x86/utility_x86.cc
+++ b/compiler/dex/quick/x86/utility_x86.cc
@@ -17,6 +17,7 @@
 #include "codegen_x86.h"
 
 #include "base/logging.h"
+#include "dex/mir_graph.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/dataflow_iterator-inl.h"
 #include "dex/quick/dex_file_method_inliner.h"
@@ -574,7 +575,7 @@
       DCHECK(r_dest.IsDouble());
       if (value == 0) {
         return NewLIR2(kX86XorpdRR, low_reg_val, low_reg_val);
-      } else if (base_of_code_ != nullptr || cu_->target64) {
+      } else if (pc_rel_base_reg_.Valid() || cu_->target64) {
         // We will load the value from the literal area.
         LIR* data_target = ScanLiteralPoolWide(literal_list_, val_lo, val_hi);
         if (data_target == NULL) {
@@ -589,17 +590,16 @@
         if (cu_->target64) {
           res = NewLIR3(kX86MovsdRM, low_reg_val, kRIPReg, 256 /* bogus */);
         } else {
-          // Address the start of the method.
-          RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
-          if (rl_method.wide) {
-            rl_method = LoadValueWide(rl_method, kCoreReg);
-          } else {
-            rl_method = LoadValue(rl_method, kCoreReg);
-          }
+          // Get the PC to a register and get the anchor.
+          LIR* anchor;
+          RegStorage r_pc = GetPcAndAnchor(&anchor);
 
-          res = LoadBaseDisp(rl_method.reg, 256 /* bogus */, RegStorage::FloatSolo64(low_reg_val),
+          res = LoadBaseDisp(r_pc, kDummy32BitOffset, RegStorage::FloatSolo64(low_reg_val),
                              kDouble, kNotVolatile);
-          store_method_addr_used_ = true;
+          res->operands[4] = WrapPointer(anchor);
+          if (IsTemp(r_pc)) {
+            FreeTemp(r_pc);
+          }
         }
         res->target = data_target;
         res->flags.fixup = kFixupLoad;
@@ -954,82 +954,14 @@
   return branch;
 }
 
-void X86Mir2Lir::AnalyzeMIR() {
-  // Assume we don't need a pointer to the base of the code.
-  cu_->NewTimingSplit("X86 MIR Analysis");
-  store_method_addr_ = false;
-
-  // Walk the MIR looking for interesting items.
-  PreOrderDfsIterator iter(mir_graph_);
-  BasicBlock* curr_bb = iter.Next();
-  while (curr_bb != NULL) {
-    AnalyzeBB(curr_bb);
-    curr_bb = iter.Next();
-  }
-
-  // Did we need a pointer to the method code?  Not in 64 bit mode.
-  base_of_code_ = nullptr;
-
-  // store_method_addr_ must be false for x86_64, since RIP addressing is used.
-  CHECK(!(cu_->target64 && store_method_addr_));
-  if (store_method_addr_) {
-    base_of_code_ = mir_graph_->GetNewCompilerTemp(kCompilerTempBackend, false);
-    DCHECK(base_of_code_ != nullptr);
-  }
-}
-
-void X86Mir2Lir::AnalyzeBB(BasicBlock* bb) {
-  if (bb->block_type == kDead) {
-    // Ignore dead blocks
+void X86Mir2Lir::AnalyzeMIR(RefCounts* core_counts, MIR* mir, uint32_t weight) {
+  if (cu_->target64) {
+    Mir2Lir::AnalyzeMIR(core_counts, mir, weight);
     return;
   }
 
-  for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
-    int opcode = mir->dalvikInsn.opcode;
-    if (MIR::DecodedInstruction::IsPseudoMirOp(opcode)) {
-      AnalyzeExtendedMIR(opcode, bb, mir);
-    } else {
-      AnalyzeMIR(opcode, bb, mir);
-    }
-  }
-}
-
-
-void X86Mir2Lir::AnalyzeExtendedMIR(int opcode, BasicBlock* bb, MIR* mir) {
-  switch (opcode) {
-    // Instructions referencing doubles.
-    case kMirOpFusedCmplDouble:
-    case kMirOpFusedCmpgDouble:
-      AnalyzeFPInstruction(opcode, bb, mir);
-      break;
-    case kMirOpConstVector:
-      if (!cu_->target64) {
-        store_method_addr_ = true;
-      }
-      break;
-    case kMirOpPackedMultiply:
-    case kMirOpPackedShiftLeft:
-    case kMirOpPackedSignedShiftRight:
-    case kMirOpPackedUnsignedShiftRight:
-      if (!cu_->target64) {
-        // Byte emulation requires constants from the literal pool.
-        OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
-        if (opsize == kSignedByte || opsize == kUnsignedByte) {
-          store_method_addr_ = true;
-        }
-      }
-      break;
-    default:
-      // Ignore the rest.
-      break;
-  }
-}
-
-void X86Mir2Lir::AnalyzeMIR(int opcode, BasicBlock* bb, MIR* mir) {
-  // Looking for
-  // - Do we need a pointer to the code (used for packed switches and double lits)?
-  // 64 bit uses RIP addressing instead.
-
+  int opcode = mir->dalvikInsn.opcode;
+  bool uses_pc_rel_load = false;
   switch (opcode) {
     // Instructions referencing doubles.
     case Instruction::CMPL_DOUBLE:
@@ -1045,34 +977,62 @@
     case Instruction::MUL_DOUBLE_2ADDR:
     case Instruction::DIV_DOUBLE_2ADDR:
     case Instruction::REM_DOUBLE_2ADDR:
-      AnalyzeFPInstruction(opcode, bb, mir);
+    case kMirOpFusedCmplDouble:
+    case kMirOpFusedCmpgDouble:
+      uses_pc_rel_load = AnalyzeFPInstruction(opcode, mir);
       break;
 
-    // Packed switches and array fills need a pointer to the base of the method.
-    case Instruction::FILL_ARRAY_DATA:
+    // Packed switch needs the PC-relative pointer if it's large.
     case Instruction::PACKED_SWITCH:
-      if (!cu_->target64) {
-        store_method_addr_ = true;
+      if (mir_graph_->GetTable(mir, mir->dalvikInsn.vB)[1] > kSmallSwitchThreshold) {
+        uses_pc_rel_load = true;
       }
       break;
+
+    case kMirOpConstVector:
+      uses_pc_rel_load = true;
+      break;
+    case kMirOpPackedMultiply:
+    case kMirOpPackedShiftLeft:
+    case kMirOpPackedSignedShiftRight:
+    case kMirOpPackedUnsignedShiftRight:
+      {
+        // Byte emulation requires constants from the literal pool.
+        OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
+        if (opsize == kSignedByte || opsize == kUnsignedByte) {
+          uses_pc_rel_load = true;
+        }
+      }
+      break;
+
     case Instruction::INVOKE_STATIC:
     case Instruction::INVOKE_STATIC_RANGE:
-      AnalyzeInvokeStatic(opcode, bb, mir);
-      break;
+      if (mir_graph_->GetMethodLoweringInfo(mir).IsIntrinsic()) {
+        uses_pc_rel_load = AnalyzeInvokeStaticIntrinsic(mir);
+        break;
+      }
+      FALLTHROUGH_INTENDED;
     default:
-      // Other instructions are not interesting yet.
+      Mir2Lir::AnalyzeMIR(core_counts, mir, weight);
       break;
   }
+
+  if (uses_pc_rel_load) {
+    DCHECK(pc_rel_temp_ != nullptr);
+    core_counts[SRegToPMap(pc_rel_temp_->s_reg_low)].count += weight;
+  }
 }
 
-void X86Mir2Lir::AnalyzeFPInstruction(int opcode, BasicBlock* bb, MIR* mir) {
-  UNUSED(bb);
+bool X86Mir2Lir::AnalyzeFPInstruction(int opcode, MIR* mir) {
+  DCHECK(!cu_->target64);
   // Look at all the uses, and see if they are double constants.
   uint64_t attrs = MIRGraph::GetDataFlowAttributes(static_cast<Instruction::Code>(opcode));
   int next_sreg = 0;
   if (attrs & DF_UA) {
     if (attrs & DF_A_WIDE) {
-      AnalyzeDoubleUse(mir_graph_->GetSrcWide(mir, next_sreg));
+      if (AnalyzeDoubleUse(mir_graph_->GetSrcWide(mir, next_sreg))) {
+        return true;
+      }
       next_sreg += 2;
     } else {
       next_sreg++;
@@ -1080,7 +1040,9 @@
   }
   if (attrs & DF_UB) {
     if (attrs & DF_B_WIDE) {
-      AnalyzeDoubleUse(mir_graph_->GetSrcWide(mir, next_sreg));
+      if (AnalyzeDoubleUse(mir_graph_->GetSrcWide(mir, next_sreg))) {
+        return true;
+      }
       next_sreg += 2;
     } else {
       next_sreg++;
@@ -1088,15 +1050,39 @@
   }
   if (attrs & DF_UC) {
     if (attrs & DF_C_WIDE) {
-      AnalyzeDoubleUse(mir_graph_->GetSrcWide(mir, next_sreg));
+      if (AnalyzeDoubleUse(mir_graph_->GetSrcWide(mir, next_sreg))) {
+        return true;
+      }
     }
   }
+  return false;
 }
 
-void X86Mir2Lir::AnalyzeDoubleUse(RegLocation use) {
+inline bool X86Mir2Lir::AnalyzeDoubleUse(RegLocation use) {
   // If this is a double literal, we will want it in the literal pool on 32b platforms.
-  if (use.is_const && !cu_->target64) {
-    store_method_addr_ = true;
+  DCHECK(!cu_->target64);
+  return use.is_const;
+}
+
+bool X86Mir2Lir::AnalyzeInvokeStaticIntrinsic(MIR* mir) {
+  // 64 bit RIP addressing doesn't need this analysis.
+  DCHECK(!cu_->target64);
+
+  // Retrieve the type of the intrinsic.
+  MethodReference method_ref = mir_graph_->GetMethodLoweringInfo(mir).GetTargetMethod();
+  DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr);
+  DexFileMethodInliner* method_inliner =
+    cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(method_ref.dex_file);
+  InlineMethod method;
+  bool is_intrinsic = method_inliner->IsIntrinsic(method_ref.dex_method_index, &method);
+  DCHECK(is_intrinsic);
+
+  switch (method.opcode) {
+    case kIntrinsicAbsDouble:
+    case kIntrinsicMinMaxDouble:
+      return true;
+    default:
+      return false;
   }
 }
 
@@ -1128,31 +1114,6 @@
   return loc;
 }
 
-void X86Mir2Lir::AnalyzeInvokeStatic(int opcode, BasicBlock* bb, MIR* mir) {
-  UNUSED(opcode, bb);
-
-  // 64 bit RIP addressing doesn't need store_method_addr_ set.
-  if (cu_->target64) {
-    return;
-  }
-
-  uint32_t index = mir->dalvikInsn.vB;
-  DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr);
-  DexFileMethodInliner* method_inliner =
-    cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file);
-  InlineMethod method;
-  if (method_inliner->IsIntrinsic(index, &method)) {
-    switch (method.opcode) {
-      case kIntrinsicAbsDouble:
-      case kIntrinsicMinMaxDouble:
-        store_method_addr_ = true;
-        break;
-      default:
-        break;
-    }
-  }
-}
-
 LIR* X86Mir2Lir::InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) {
   UNUSED(r_tgt);  // Call to absolute memory location doesn't need a temporary target register.
   if (cu_->target64) {
@@ -1162,4 +1123,39 @@
   }
 }
 
+void X86Mir2Lir::CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t num_regs) {
+  // Start with the default counts.
+  Mir2Lir::CountRefs(core_counts, fp_counts, num_regs);
+
+  if (pc_rel_temp_ != nullptr) {
+    // Now, if the dex cache array base temp is used only once outside any loops (weight = 1),
+    // avoid the promotion, otherwise boost the weight by factor 2 because the full PC-relative
+    // load sequence is 3 instructions long and by promoting the PC base we save 2 instructions
+    // per use.
+    int p_map_idx = SRegToPMap(pc_rel_temp_->s_reg_low);
+    if (core_counts[p_map_idx].count == 1) {
+      core_counts[p_map_idx].count = 0;
+    } else {
+      core_counts[p_map_idx].count *= 2;
+    }
+  }
+}
+
+void X86Mir2Lir::DoPromotion() {
+  if (!cu_->target64) {
+    pc_rel_temp_ = mir_graph_->GetNewCompilerTemp(kCompilerTempBackend, false);
+  }
+
+  Mir2Lir::DoPromotion();
+
+  if (pc_rel_temp_ != nullptr) {
+    // Now, if the dex cache array base temp is promoted, remember the register but
+    // always remove the temp's stack location to avoid unnecessarily bloating the stack.
+    pc_rel_base_reg_ = mir_graph_->reg_location_[pc_rel_temp_->s_reg_low].reg;
+    DCHECK(!pc_rel_base_reg_.Valid() || !pc_rel_base_reg_.IsFloat());
+    mir_graph_->RemoveLastCompilerTemp(kCompilerTempBackend, false, pc_rel_temp_);
+    pc_rel_temp_ = nullptr;
+  }
+}
+
 }  // namespace art
diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h
index 7dea09a..57db015 100644
--- a/compiler/dex/quick/x86/x86_lir.h
+++ b/compiler/dex/quick/x86/x86_lir.h
@@ -635,8 +635,6 @@
   kX86CallT,            // call fs:[disp]; fs: is equal to Thread::Current(); lir operands - 0: disp
   kX86CallI,            // call <relative> - 0: disp; Used for core.oat linking only
   kX86Ret,              // ret; no lir operands
-  kX86StartOfMethod,    // call 0; pop reg; sub reg, # - generate start of method into reg
-                        // lir operands - 0: reg
   kX86PcRelLoadRA,      // mov reg, [base + index * scale + PC relative displacement]
                         // lir operands - 0: reg, 1: base, 2: index, 3: scale, 4: table
   kX86PcRelAdr,         // mov reg, PC relative displacement; lir operands - 0: reg, 1: table
@@ -670,7 +668,6 @@
   kRegMemCond,                             // RM instruction kind followed by a condition.
   kJmp, kJcc, kCall,                       // Branch instruction kinds.
   kPcRel,                                  // Operation with displacement that is PC relative
-  kMacro,                                  // An instruction composing multiple others
   kUnimplemented                           // Encoding used when an instruction isn't yet implemented.
 };
 
diff --git a/compiler/linker/x86/relative_patcher_x86.cc b/compiler/linker/x86/relative_patcher_x86.cc
index 246cf11..315585d 100644
--- a/compiler/linker/x86/relative_patcher_x86.cc
+++ b/compiler/linker/x86/relative_patcher_x86.cc
@@ -16,14 +16,43 @@
 
 #include "linker/x86/relative_patcher_x86.h"
 
+#include "compiled_method.h"
+
 namespace art {
 namespace linker {
 
-void X86RelativePatcher::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.";
+void X86RelativePatcher::PatchDexCacheReference(std::vector<uint8_t>* code,
+                                                const LinkerPatch& patch,
+                                                uint32_t patch_offset,
+                                                uint32_t target_offset) {
+  uint32_t anchor_literal_offset = patch.PcInsnOffset();
+  uint32_t literal_offset = patch.LiteralOffset();
+
+  // Check that the anchor points to pop in a "call +0; pop <reg>" sequence.
+  DCHECK_GE(anchor_literal_offset, 5u);
+  DCHECK_LT(anchor_literal_offset, code->size());
+  DCHECK_EQ((*code)[anchor_literal_offset - 5u], 0xe8u);
+  DCHECK_EQ((*code)[anchor_literal_offset - 4u], 0x00u);
+  DCHECK_EQ((*code)[anchor_literal_offset - 3u], 0x00u);
+  DCHECK_EQ((*code)[anchor_literal_offset - 2u], 0x00u);
+  DCHECK_EQ((*code)[anchor_literal_offset - 1u], 0x00u);
+  DCHECK_EQ((*code)[anchor_literal_offset] & 0xf8u, 0x58u);
+
+  // Check that the patched data contains kDummy32BitOffset.
+  constexpr int kDummy32BitOffset = 256;  // Must match X86Mir2Lir::kDummy32BitOffset.
+  DCHECK_LE(literal_offset, code->size());
+  DCHECK_EQ((*code)[literal_offset + 0u], static_cast<uint8_t>(kDummy32BitOffset >> 0));
+  DCHECK_EQ((*code)[literal_offset + 1u], static_cast<uint8_t>(kDummy32BitOffset >> 8));
+  DCHECK_EQ((*code)[literal_offset + 2u], static_cast<uint8_t>(kDummy32BitOffset >> 16));
+  DCHECK_EQ((*code)[literal_offset + 3u], static_cast<uint8_t>(kDummy32BitOffset >> 24));
+
+  // Apply patch.
+  uint32_t anchor_offset = patch_offset - literal_offset + anchor_literal_offset;
+  uint32_t diff = target_offset - anchor_offset;
+  (*code)[literal_offset + 0u] = static_cast<uint8_t>(diff >> 0);
+  (*code)[literal_offset + 1u] = static_cast<uint8_t>(diff >> 8);
+  (*code)[literal_offset + 2u] = static_cast<uint8_t>(diff >> 16);
+  (*code)[literal_offset + 3u] = static_cast<uint8_t>(diff >> 24);
 }
 
 }  // namespace linker
diff --git a/compiler/linker/x86/relative_patcher_x86_test.cc b/compiler/linker/x86/relative_patcher_x86_test.cc
index 15ac47e..7acc330 100644
--- a/compiler/linker/x86/relative_patcher_x86_test.cc
+++ b/compiler/linker/x86/relative_patcher_x86_test.cc
@@ -101,5 +101,35 @@
   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
 }
 
+TEST_F(X86RelativePatcherTest, DexCacheReference) {
+  dex_cache_arrays_begin_ = 0x12345678;
+  constexpr size_t kElementOffset = 0x1234;
+  static const uint8_t raw_code[] = {
+      0xe8, 0x00, 0x00, 0x00, 0x00,         // call +0
+      0x5b,                                 // pop ebx
+      0x8b, 0x83, 0x00, 0x01, 0x00, 0x00,   // mov eax, [ebx + 256 (kDummy32BitValue)]
+  };
+  constexpr uint32_t anchor_offset = 5u;  // After call +0.
+  ArrayRef<const uint8_t> code(raw_code);
+  LinkerPatch patches[] = {
+      LinkerPatch::DexCacheArrayPatch(code.size() - 4u, nullptr, anchor_offset, kElementOffset),
+  };
+  AddCompiledMethod(MethodRef(1u), code, ArrayRef<const LinkerPatch>(patches));
+  Link();
+
+  auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
+  ASSERT_TRUE(result.first);
+  uint32_t diff =
+      dex_cache_arrays_begin_ + kElementOffset - (result.second + anchor_offset);
+  static const uint8_t expected_code[] = {
+      0xe8, 0x00, 0x00, 0x00, 0x00,         // call +0
+      0x5b,                                 // pop ebx
+      0x8b, 0x83,                           // mov eax, [ebx + diff]
+      static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8),
+      static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24)
+  };
+  EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
+}
+
 }  // namespace linker
 }  // namespace art