diff options
| -rw-r--r-- | compiler/dex/quick/arm/call_arm.cc | 53 | ||||
| -rw-r--r-- | compiler/dex/quick/arm/codegen_arm.h | 12 | ||||
| -rw-r--r-- | compiler/dex/quick/arm/int_arm.cc | 21 | ||||
| -rw-r--r-- | compiler/dex/quick/arm/target_arm.cc | 29 | ||||
| -rw-r--r-- | compiler/linker/arm/relative_patcher_thumb2.cc | 58 | ||||
| -rw-r--r-- | compiler/linker/arm/relative_patcher_thumb2.h | 6 | ||||
| -rw-r--r-- | compiler/linker/arm/relative_patcher_thumb2_test.cc | 62 |
7 files changed, 204 insertions, 37 deletions
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index e6158c3200..3f4f1fef91 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -29,6 +29,7 @@ #include "mirror/object_array-inl.h" #include "entrypoints/quick/quick_entrypoints.h" #include "utils.h" +#include "utils/dex_cache_arrays_layout-inl.h" namespace art { @@ -571,12 +572,12 @@ static bool ArmUseRelativeCall(CompilationUnit* cu, const MethodReference& targe * Bit of a hack here - in the absence of a real scheduling pass, * emit the next instruction in static & direct invoke sequences. */ -static int ArmNextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSED, - int state, const MethodReference& target_method, - uint32_t unused_idx ATTRIBUTE_UNUSED, - uintptr_t direct_code, uintptr_t direct_method, - InvokeType type) { - Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); +int ArmMir2Lir::ArmNextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSED, + int state, const MethodReference& target_method, + uint32_t unused_idx ATTRIBUTE_UNUSED, + uintptr_t direct_code, uintptr_t direct_method, + InvokeType type) { + ArmMir2Lir* cg = static_cast<ArmMir2Lir*>(cu->cg.get()); if (direct_code != 0 && direct_method != 0) { switch (state) { case 0: // Get the current Method* [sets kArg0] @@ -597,17 +598,24 @@ static int ArmNextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSE 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)) { @@ -619,14 +627,23 @@ static int ArmNextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSE 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/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index 4141bcfe98..619c11fb94 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -82,6 +82,9 @@ class ArmMir2Lir FINAL : public Mir2Lir { /// @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; + // Required for target - register utilities. RegStorage TargetReg(SpecialTargetRegister reg) OVERRIDE; RegStorage TargetReg(SpecialTargetRegister reg, WideKind wide_kind) OVERRIDE { @@ -300,6 +303,9 @@ class ArmMir2Lir FINAL : public Mir2Lir { ArenaVector<LIR*> call_method_insns_; + // Instructions needing patching with PC relative code addresses. + ArenaVector<LIR*> dex_cache_access_insns_; + /** * @brief Given float register pair, returns Solo64 float register. * @param reg #RegStorage containing a float register pair (e.g. @c s2 and @c s3). @@ -329,6 +335,12 @@ class ArmMir2Lir FINAL : public Mir2Lir { } int GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) OVERRIDE; + + static int ArmNextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSED, + int state, const MethodReference& target_method, + uint32_t unused_idx ATTRIBUTE_UNUSED, + uintptr_t direct_code, uintptr_t direct_method, + InvokeType type); }; } // namespace art diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 9193e1b23c..c788401cfb 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -1087,6 +1087,27 @@ void ArmMir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) { lir->target = target; } +bool ArmMir2Lir::CanUseOpPcRelDexCacheArrayLoad() const { + return dex_cache_arrays_layout_.Valid(); +} + +void ArmMir2Lir::OpPcRelDexCacheArrayLoad(const DexFile* dex_file, int offset, RegStorage r_dest) { + LIR* movw = NewLIR2(kThumb2MovImm16, r_dest.GetReg(), 0); + LIR* movt = NewLIR2(kThumb2MovImm16H, r_dest.GetReg(), 0); + ArmOpcode add_pc_opcode = (r_dest.GetRegNum() < 8) ? kThumbAddRRLH : kThumbAddRRHH; + LIR* add_pc = NewLIR2(add_pc_opcode, r_dest.GetReg(), rs_rARM_PC.GetReg()); + add_pc->flags.fixup = kFixupLabel; + movw->operands[2] = WrapPointer(dex_file); + movw->operands[3] = offset; + movw->operands[4] = WrapPointer(add_pc); + movt->operands[2] = movw->operands[2]; + movt->operands[3] = movw->operands[3]; + movt->operands[4] = movw->operands[4]; + dex_cache_access_insns_.push_back(movw); + dex_cache_access_insns_.push_back(movt); + LoadRefDisp(r_dest, 0, r_dest, kNotVolatile); +} + LIR* ArmMir2Lir::OpVldm(RegStorage r_base, int count) { return NewLIR3(kThumb2Vldms, r_base.GetReg(), rs_fr0.GetReg(), count); } diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index 9812d9ff99..580dcb789f 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -575,7 +575,8 @@ RegisterClass ArmMir2Lir::RegClassForFieldLoadStore(OpSize size, bool is_volatil ArmMir2Lir::ArmMir2Lir(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()) { call_method_insns_.reserve(100); // Sanity check - make sure encoding map lines up. for (int i = 0; i < kArmLast; i++) { @@ -901,14 +902,28 @@ RegStorage ArmMir2Lir::AllocPreservedSingle(int s_reg) { } void ArmMir2Lir::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, kThumb2Bl); - uint32_t target_method_idx = p->operands[1]; - const DexFile* target_dex_file = UnwrapPointer<DexFile>(p->operands[2]); - patches_.push_back(LinkerPatch::RelativeCodePatch(p->offset, - target_dex_file, target_method_idx)); + DCHECK_EQ(p->opcode, kThumb2Bl); + uint32_t target_method_idx = p->operands[1]; + const DexFile* target_dex_file = UnwrapPointer<DexFile>(p->operands[2]); + patches_.push_back(LinkerPatch::RelativeCodePatch(p->offset, + target_dex_file, target_method_idx)); + } + + // PC-relative dex cache array accesses. + for (LIR* p : dex_cache_access_insns_) { + DCHECK(p->opcode = kThumb2MovImm16 || p->opcode == kThumb2MovImm16H); + const LIR* add_pc = UnwrapPointer<LIR>(p->operands[4]); + DCHECK(add_pc->opcode == kThumbAddRRLH || add_pc->opcode == kThumbAddRRHH); + const DexFile* dex_file = UnwrapPointer<DexFile>(p->operands[2]); + uint32_t offset = p->operands[3]; + DCHECK(!p->flags.is_nop); + DCHECK(!add_pc->flags.is_nop); + patches_.push_back(LinkerPatch::DexCacheArrayPatch(p->offset, + dex_file, add_pc->offset, offset)); } // And do the normal processing. diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc index 4267743097..b17cbca2d2 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.cc +++ b/compiler/linker/arm/relative_patcher_thumb2.cc @@ -48,22 +48,30 @@ void Thumb2RelativePatcher::PatchCall(std::vector<uint8_t>* code, uint32_t liter uint32_t value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) | imm11; value |= 0xf000d000; // BL - uint8_t* addr = &(*code)[literal_offset]; // Check that we're just overwriting an existing BL. - DCHECK_EQ(addr[1] & 0xf8, 0xf0); - DCHECK_EQ(addr[3] & 0xd0, 0xd0); + DCHECK_EQ(GetInsn32(code, literal_offset) & 0xf800d000, 0xf000d000); // Write the new BL. - addr[0] = (value >> 16) & 0xff; - addr[1] = (value >> 24) & 0xff; - addr[2] = (value >> 0) & 0xff; - addr[3] = (value >> 8) & 0xff; + SetInsn32(code, literal_offset, value); } -void Thumb2RelativePatcher::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 Thumb2RelativePatcher::PatchDexCacheReference(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) { + uint32_t literal_offset = patch.LiteralOffset(); + uint32_t pc_literal_offset = patch.PcInsnOffset(); + uint32_t pc_base = patch_offset + (pc_literal_offset - literal_offset) + 4u /* PC adjustment */; + uint32_t diff = target_offset - pc_base; + + uint32_t insn = GetInsn32(code, literal_offset); + DCHECK_EQ(insn & 0xff7ff0ffu, 0xf2400000u); // MOVW/MOVT, unpatched (imm16 == 0). + uint32_t diff16 = ((insn & 0x00800000u) != 0u) ? (diff >> 16) : (diff & 0xffffu); + uint32_t imm4 = (diff16 >> 12) & 0xfu; + uint32_t imm = (diff16 >> 11) & 0x1u; + uint32_t imm3 = (diff16 >> 8) & 0x7u; + uint32_t imm8 = diff16 & 0xffu; + insn = (insn & 0xfbf08f00u) | (imm << 26) | (imm4 << 16) | (imm3 << 12) | imm8; + SetInsn32(code, literal_offset, insn); } std::vector<uint8_t> Thumb2RelativePatcher::CompileThunkCode() { @@ -80,5 +88,31 @@ std::vector<uint8_t> Thumb2RelativePatcher::CompileThunkCode() { return thunk_code; } +void Thumb2RelativePatcher::SetInsn32(std::vector<uint8_t>* code, uint32_t offset, uint32_t value) { + DCHECK_LE(offset + 4u, code->size()); + DCHECK_EQ(offset & 1u, 0u); + uint8_t* addr = &(*code)[offset]; + addr[0] = (value >> 16) & 0xff; + addr[1] = (value >> 24) & 0xff; + addr[2] = (value >> 0) & 0xff; + addr[3] = (value >> 8) & 0xff; +} + +uint32_t Thumb2RelativePatcher::GetInsn32(ArrayRef<const uint8_t> code, uint32_t offset) { + DCHECK_LE(offset + 4u, code.size()); + DCHECK_EQ(offset & 1u, 0u); + const uint8_t* addr = &code[offset]; + return + (static_cast<uint32_t>(addr[0]) << 16) + + (static_cast<uint32_t>(addr[1]) << 24) + + (static_cast<uint32_t>(addr[2]) << 0)+ + (static_cast<uint32_t>(addr[3]) << 8); +} + +template <typename Alloc> +uint32_t Thumb2RelativePatcher::GetInsn32(std::vector<uint8_t, Alloc>* code, uint32_t offset) { + return GetInsn32(ArrayRef<const uint8_t>(*code), offset); +} + } // namespace linker } // namespace art diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h index 561130305e..2d474c2db0 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.h +++ b/compiler/linker/arm/relative_patcher_thumb2.h @@ -34,6 +34,12 @@ class Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher { private: static std::vector<uint8_t> CompileThunkCode(); + void SetInsn32(std::vector<uint8_t>* code, uint32_t offset, uint32_t value); + static uint32_t GetInsn32(ArrayRef<const uint8_t> code, uint32_t offset); + + template <typename Alloc> + static uint32_t GetInsn32(std::vector<uint8_t, Alloc>* code, uint32_t offset); + // PC displacement from patch location; Thumb2 PC is always at instruction address + 4. static constexpr int32_t kPcDisplacement = 4; diff --git a/compiler/linker/arm/relative_patcher_thumb2_test.cc b/compiler/linker/arm/relative_patcher_thumb2_test.cc index 3b397cc5fb..a057a4cf16 100644 --- a/compiler/linker/arm/relative_patcher_thumb2_test.cc +++ b/compiler/linker/arm/relative_patcher_thumb2_test.cc @@ -121,6 +121,48 @@ class Thumb2RelativePatcherTest : public RelativePatcherTest { result.push_back(static_cast<uint8_t>(bl >> 8)); return result; } + + void TestDexCachereference(uint32_t dex_cache_arrays_begin, uint32_t element_offset) { + dex_cache_arrays_begin_ = dex_cache_arrays_begin; + static const uint8_t raw_code[] = { + 0x40, 0xf2, 0x00, 0x00, // MOVW r0, #0 (placeholder) + 0xc0, 0xf2, 0x00, 0x00, // MOVT r0, #0 (placeholder) + 0x78, 0x44, // ADD r0, pc + }; + constexpr uint32_t pc_insn_offset = 8u; + const ArrayRef<const uint8_t> code(raw_code); + LinkerPatch patches[] = { + LinkerPatch::DexCacheArrayPatch(0u, nullptr, pc_insn_offset, element_offset), + LinkerPatch::DexCacheArrayPatch(4u, nullptr, pc_insn_offset, element_offset), + }; + AddCompiledMethod(MethodRef(1u), code, ArrayRef<const LinkerPatch>(patches)); + Link(); + + uint32_t method1_offset = GetMethodOffset(1u); + uint32_t pc_base_offset = method1_offset + pc_insn_offset + 4u /* PC adjustment */; + uint32_t diff = dex_cache_arrays_begin_ + element_offset - pc_base_offset; + // Distribute the bits of the diff between the MOVW and MOVT: + uint32_t diffw = diff & 0xffffu; + uint32_t difft = diff >> 16; + uint32_t movw = 0xf2400000u | // MOVW r0, #0 (placeholder), + ((diffw & 0xf000u) << (16 - 12)) | // move imm4 from bits 12-15 to bits 16-19, + ((diffw & 0x0800u) << (26 - 11)) | // move imm from bit 11 to bit 26, + ((diffw & 0x0700u) << (12 - 8)) | // move imm3 from bits 8-10 to bits 12-14, + ((diffw & 0x00ffu)); // keep imm8 at bits 0-7. + uint32_t movt = 0xf2c00000u | // MOVT r0, #0 (placeholder), + ((difft & 0xf000u) << (16 - 12)) | // move imm4 from bits 12-15 to bits 16-19, + ((difft & 0x0800u) << (26 - 11)) | // move imm from bit 11 to bit 26, + ((difft & 0x0700u) << (12 - 8)) | // move imm3 from bits 8-10 to bits 12-14, + ((difft & 0x00ffu)); // keep imm8 at bits 0-7. + const uint8_t expected_code[] = { + static_cast<uint8_t>(movw >> 16), static_cast<uint8_t>(movw >> 24), + static_cast<uint8_t>(movw >> 0), static_cast<uint8_t>(movw >> 8), + static_cast<uint8_t>(movt >> 16), static_cast<uint8_t>(movt >> 24), + static_cast<uint8_t>(movt >> 0), static_cast<uint8_t>(movt >> 8), + 0x78, 0x44, + }; + EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); + } }; const uint8_t Thumb2RelativePatcherTest::kCallRawCode[] = { @@ -285,5 +327,25 @@ TEST_F(Thumb2RelativePatcherTest, CallOtherJustTooFarBefore) { EXPECT_TRUE(CheckThunk(thunk_offset)); } +TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceImm8) { + TestDexCachereference(0x00ff0000u, 0x00fcu); + ASSERT_LT(GetMethodOffset(1u), 0xfcu); +} + +TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceImm3) { + TestDexCachereference(0x02ff0000u, 0x05fcu); + ASSERT_LT(GetMethodOffset(1u), 0xfcu); +} + +TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceImm) { + TestDexCachereference(0x08ff0000u, 0x08fcu); + ASSERT_LT(GetMethodOffset(1u), 0xfcu); +} + +TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceimm4) { + TestDexCachereference(0xd0ff0000u, 0x60fcu); + ASSERT_LT(GetMethodOffset(1u), 0xfcu); +} + } // namespace linker } // namespace art |