diff options
author | Vladimir Marko <vmarko@google.com> | 2015-04-08 20:51:48 +0100 |
---|---|---|
committer | Vladimir Marko <vmarko@google.com> | 2015-04-09 10:10:39 +0100 |
commit | 1961b609bfefaedb71cee3651c4f931cc3e7393d (patch) | |
tree | 16e98169664ea3cca7f317c1cece67d440dacb6f | |
parent | 1576be32be4a99a1cffdaaf209a3cd67e8b2f88a (diff) | |
download | android_art-1961b609bfefaedb71cee3651c4f931cc3e7393d.tar.gz android_art-1961b609bfefaedb71cee3651c4f931cc3e7393d.tar.bz2 android_art-1961b609bfefaedb71cee3651c4f931cc3e7393d.zip |
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
-rw-r--r-- | compiler/dex/quick/arm/utility_arm.cc | 7 | ||||
-rw-r--r-- | compiler/dex/quick/codegen_util.cc | 4 | ||||
-rw-r--r-- | compiler/dex/quick/mir_to_lir.h | 4 | ||||
-rw-r--r-- | compiler/dex/quick/x86/assemble_x86.cc | 45 | ||||
-rw-r--r-- | compiler/dex/quick/x86/call_x86.cc | 40 | ||||
-rw-r--r-- | compiler/dex/quick/x86/codegen_x86.h | 77 | ||||
-rwxr-xr-x | compiler/dex/quick/x86/fp_x86.cc | 18 | ||||
-rwxr-xr-x | compiler/dex/quick/x86/int_x86.cc | 65 | ||||
-rwxr-xr-x | compiler/dex/quick/x86/target_x86.cc | 47 | ||||
-rw-r--r-- | compiler/dex/quick/x86/utility_x86.cc | 242 | ||||
-rw-r--r-- | compiler/dex/quick/x86/x86_lir.h | 3 | ||||
-rw-r--r-- | compiler/linker/x86/relative_patcher_x86.cc | 39 | ||||
-rw-r--r-- | compiler/linker/x86/relative_patcher_x86_test.cc | 30 |
13 files changed, 307 insertions, 314 deletions
diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index c3371cf329..25ea6941c0 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -1273,13 +1273,14 @@ void ArmMir2Lir::CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t 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 232a2286e2..f8594a2510 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -541,13 +541,11 @@ void Mir2Lir::InstallSwitchTables() { 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 1624c84437..e9752fd928 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -635,7 +635,7 @@ class Mir2Lir { 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 @@ class Mir2Lir { */ 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 118ab1d843..af19f5eaed 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -544,7 +544,6 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { 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 @@ size_t X86Mir2Lir::GetInsnSize(LIR* lir) { 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 @@ void X86Mir2Lir::EmitPcRel(const X86EncodingMap* entry, int32_t raw_reg, int32_t 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 @@ void X86Mir2Lir::EmitPcRel(const X86EncodingMap* entry, int32_t raw_reg, int32_t 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 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) { // 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 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) { 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 @@ void X86Mir2Lir::AssembleLIR() { 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 18fae17d70..3041458d6d 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -97,29 +97,23 @@ void X86Mir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocat // 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 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { 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 a98a99ec4e..72580a3e39 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 @@ class X86Mir2Lir : public Mir2Lir { */ 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 @@ class X86Mir2Lir : public Mir2Lir { 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 @@ class X86Mir2Lir : public Mir2Lir { 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 @@ class X86Mir2Lir : public Mir2Lir { 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_; - - // Have we decided to compute a ptr to code and store in temporary VR? - bool store_method_addr_; + // The base register for PC-relative addressing if promoted (32-bit only). + RegStorage pc_rel_base_reg_; - // Have we used the stored method address? - bool store_method_addr_used_; + // Have we actually used the pc_rel_base_reg_? + bool pc_rel_base_reg_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 @@ class X86Mir2Lir : public Mir2Lir { 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 d8616a7bf3..cfe0480c54 100755 --- a/compiler/dex/quick/x86/fp_x86.cc +++ b/compiler/dex/quick/x86/fp_x86.cc @@ -756,24 +756,6 @@ bool X86Mir2Lir::GenInlinedMinMaxFP(CallInfo* info, bool is_min, bool is_double) 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 5def5c8bb0..075f721633 100755 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -1324,11 +1324,6 @@ bool X86Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) { 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 @@ void X86Mir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) { 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 @@ void X86Mir2Lir::OpPcRelDexCacheArrayLoad(const DexFile* dex_file, int offset, 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 081f80fed6..1def88e7e7 100755 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -824,7 +824,9 @@ RegisterClass X86Mir2Lir::RegClassForFieldLoadStore(OpSize size, bool is_volatil 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 @@ X86Mir2Lir::X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* 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 @@ void X86Mir2Lir::DumpRegLocation(RegLocation loc) { << ", 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 @@ void X86Mir2Lir::InstallLiteralPools() { // 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 @@ void X86Mir2Lir::AppendOpcodeWithConst(X86OpCode opcode, int reg, MIR* mir) { 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 893b98a49d..efcb9eefb5 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 @@ LIR* X86Mir2Lir::LoadConstantWide(RegStorage r_dest, int64_t value) { 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 @@ LIR* X86Mir2Lir::LoadConstantWide(RegStorage r_dest, int64_t value) { 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 @@ LIR* X86Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegS 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 @@ void X86Mir2Lir::AnalyzeMIR(int opcode, BasicBlock* bb, MIR* mir) { 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 @@ void X86Mir2Lir::AnalyzeFPInstruction(int opcode, BasicBlock* bb, MIR* mir) { } 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 @@ void X86Mir2Lir::AnalyzeFPInstruction(int opcode, BasicBlock* bb, MIR* mir) { } 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,37 +1114,47 @@ RegLocation X86Mir2Lir::UpdateLocWideTyped(RegLocation loc) { 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. +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) { - return; + return OpThreadMem(op, GetThreadOffset<8>(trampoline)); + } else { + return OpThreadMem(op, GetThreadOffset<4>(trampoline)); } +} - 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; +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; } } } -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) { - return OpThreadMem(op, GetThreadOffset<8>(trampoline)); - } else { - return OpThreadMem(op, GetThreadOffset<4>(trampoline)); +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; } } diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h index 7dea09a579..57db0158e4 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -635,8 +635,6 @@ enum X86OpCode { 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 @@ enum X86EncodingKind { 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 246cf11dae..315585d9e7 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 15ac47e7a4..7acc33004a 100644 --- a/compiler/linker/x86/relative_patcher_x86_test.cc +++ b/compiler/linker/x86/relative_patcher_x86_test.cc @@ -101,5 +101,35 @@ TEST_F(X86RelativePatcherTest, CallTrampoline) { 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 |