diff options
author | 2017-07-31 18:43:18 -0700 | |
---|---|---|
committer | 2017-08-18 15:29:31 -0700 | |
commit | a663d9d5b32a525794a2b98fa43da54dd7c79e3b (patch) | |
tree | 88c643ca5ebfb0dfe11f45a9b232f9a2592fb043 | |
parent | b9463674919ba91fe131e65785ad67b4202e86b9 (diff) |
MIPS32: Allow some patched instructions in delay slots
Test: test-art-host-gtest
Test: booted MIPS64 (with 2nd arch MIPS32R6) in QEMU
Test: test-art-target-gtest32
Test: testrunner.py --target --optimizing --32
Test: same tests as above on CI20
Test: booted MIPS32R2 in QEMU
Change-Id: I7e1ba59993008014d0115ae20c56e0a71fef0fb0
-rw-r--r-- | compiler/linker/mips/relative_patcher_mips.cc | 4 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_mips.cc | 74 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_mips.h | 3 | ||||
-rw-r--r-- | compiler/utils/label.h | 4 | ||||
-rw-r--r-- | compiler/utils/mips/assembler_mips.cc | 141 | ||||
-rw-r--r-- | compiler/utils/mips/assembler_mips.h | 31 | ||||
-rw-r--r-- | compiler/utils/mips/assembler_mips32r6_test.cc | 64 | ||||
-rw-r--r-- | compiler/utils/mips/assembler_mips_test.cc | 144 |
8 files changed, 367 insertions, 98 deletions
diff --git a/compiler/linker/mips/relative_patcher_mips.cc b/compiler/linker/mips/relative_patcher_mips.cc index 3bec30f1e8..6c974c308f 100644 --- a/compiler/linker/mips/relative_patcher_mips.cc +++ b/compiler/linker/mips/relative_patcher_mips.cc @@ -61,10 +61,6 @@ void MipsRelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, // lui reg, offset_high DCHECK_EQ(((*code)[literal_offset + 2] & 0xE0), 0x00); DCHECK_EQ((*code)[literal_offset + 3], 0x3C); - // addu reg, reg, reg2 - DCHECK_EQ((*code)[literal_offset + 4], 0x21); - DCHECK_EQ(((*code)[literal_offset + 5] & 0x07), 0x00); - DCHECK_EQ(((*code)[literal_offset + 7] & 0xFC), 0x00); } } else { // instr reg(s), offset_low diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 2e78af5d7c..51f5b969d5 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -267,13 +267,10 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { DCHECK(bss_info_high_); CodeGeneratorMIPS::PcRelativePatchInfo* info_low = mips_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index, bss_info_high_); - bool reordering = __ SetReorder(false); - __ Bind(&info_low->label); - __ StoreToOffset(kStoreWord, - calling_convention.GetRegisterAt(0), - entry_address, - /* placeholder */ 0x5678); - __ SetReorder(reordering); + __ Sw(calling_convention.GetRegisterAt(0), + entry_address, + /* placeholder */ 0x5678, + &info_low->label); } // Move the class to the desired location. @@ -296,10 +293,8 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { mips_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index); CodeGeneratorMIPS::PcRelativePatchInfo* info_low = mips_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index, info_high); - bool reordering = __ SetReorder(false); - mips_codegen->EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base, info_low); - __ StoreToOffset(kStoreWord, out.AsRegister<Register>(), TMP, /* placeholder */ 0x5678); - __ SetReorder(reordering); + mips_codegen->EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base); + __ Sw(out.AsRegister<Register>(), TMP, /* placeholder */ 0x5678, &info_low->label); } __ B(GetExitLabel()); } @@ -366,13 +361,10 @@ class LoadStringSlowPathMIPS : public SlowPathCodeMIPS { DCHECK(bss_info_high_); CodeGeneratorMIPS::PcRelativePatchInfo* info_low = mips_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index, bss_info_high_); - bool reordering = __ SetReorder(false); - __ Bind(&info_low->label); - __ StoreToOffset(kStoreWord, - calling_convention.GetRegisterAt(0), - entry_address, - /* placeholder */ 0x5678); - __ SetReorder(reordering); + __ Sw(calling_convention.GetRegisterAt(0), + entry_address, + /* placeholder */ 0x5678, + &info_low->label); } Primitive::Type type = instruction_->GetType(); @@ -391,10 +383,8 @@ class LoadStringSlowPathMIPS : public SlowPathCodeMIPS { mips_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index); CodeGeneratorMIPS::PcRelativePatchInfo* info_low = mips_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index, info_high); - bool reordering = __ SetReorder(false); - mips_codegen->EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base, info_low); - __ StoreToOffset(kStoreWord, out, TMP, /* placeholder */ 0x5678); - __ SetReorder(reordering); + mips_codegen->EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base); + __ Sw(out, TMP, /* placeholder */ 0x5678, &info_low->label); } __ B(GetExitLabel()); } @@ -1743,16 +1733,17 @@ Literal* CodeGeneratorMIPS::DeduplicateBootImageAddressLiteral(uint32_t address) void CodeGeneratorMIPS::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info_high, Register out, - Register base, - PcRelativePatchInfo* info_low) { + Register base) { DCHECK(!info_high->patch_info_high); DCHECK_NE(out, base); + bool reordering = __ SetReorder(false); if (GetInstructionSetFeatures().IsR6()) { DCHECK_EQ(base, ZERO); __ Bind(&info_high->label); __ Bind(&info_high->pc_rel_label); // Add the high half of a 32-bit offset to PC. __ Auipc(out, /* placeholder */ 0x1234); + __ SetReorder(reordering); } else { // If base is ZERO, emit NAL to obtain the actual base. if (base == ZERO) { @@ -1766,15 +1757,12 @@ void CodeGeneratorMIPS::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo if (base == ZERO) { __ Bind(&info_high->pc_rel_label); } + __ SetReorder(reordering); // Add the high half of a 32-bit offset to PC. __ Addu(out, out, (base == ZERO) ? RA : base); } // A following instruction will add the sign-extended low half of the 32-bit // offset to `out` (e.g. lw, jialc, addiu). - if (info_low != nullptr) { - DCHECK_EQ(info_low->patch_info_high, info_high); - __ Bind(&info_low->label); - } } CodeGeneratorMIPS::JitPatchInfo* CodeGeneratorMIPS::NewJitRootStringPatch( @@ -7515,11 +7503,9 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall( PcRelativePatchInfo* info_high = NewPcRelativeMethodPatch(invoke->GetTargetMethod()); PcRelativePatchInfo* info_low = NewPcRelativeMethodPatch(invoke->GetTargetMethod(), info_high); - bool reordering = __ SetReorder(false); Register temp_reg = temp.AsRegister<Register>(); - EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base_reg, info_low); - __ Addiu(temp_reg, TMP, /* placeholder */ 0x5678); - __ SetReorder(reordering); + EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base_reg); + __ Addiu(temp_reg, TMP, /* placeholder */ 0x5678, &info_low->label); break; } case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: @@ -7531,10 +7517,8 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall( PcRelativePatchInfo* info_low = NewMethodBssEntryPatch( MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()), info_high); Register temp_reg = temp.AsRegister<Register>(); - bool reordering = __ SetReorder(false); - EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base_reg, info_low); - __ Lw(temp_reg, TMP, /* placeholder */ 0x5678); - __ SetReorder(reordering); + EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base_reg); + __ Lw(temp_reg, TMP, /* placeholder */ 0x5678, &info_low->label); break; } case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: { @@ -7729,13 +7713,10 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); CodeGeneratorMIPS::PcRelativePatchInfo* info_low = codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high); - bool reordering = __ SetReorder(false); codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, out, - base_or_current_method_reg, - info_low); - __ Addiu(out, out, /* placeholder */ 0x5678); - __ SetReorder(reordering); + base_or_current_method_reg); + __ Addiu(out, out, /* placeholder */ 0x5678, &info_low->label); break; } case HLoadClass::LoadKind::kBootImageAddress: { @@ -7754,11 +7735,9 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex(), bss_info_high); constexpr bool non_baker_read_barrier = kUseReadBarrier && !kUseBakerReadBarrier; Register temp = non_baker_read_barrier ? out : locations->GetTemp(0).AsRegister<Register>(); - bool reordering = __ SetReorder(false); codegen_->EmitPcRelativeAddressPlaceholderHigh(bss_info_high, temp, base_or_current_method_reg); - __ SetReorder(reordering); GenerateGcRootFieldLoad(cls, out_loc, temp, @@ -7899,13 +7878,10 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); CodeGeneratorMIPS::PcRelativePatchInfo* info_low = codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high); - bool reordering = __ SetReorder(false); codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, out, - base_or_current_method_reg, - info_low); - __ Addiu(out, out, /* placeholder */ 0x5678); - __ SetReorder(reordering); + base_or_current_method_reg); + __ Addiu(out, out, /* placeholder */ 0x5678, &info_low->label); return; // No dex cache slow path. } case HLoadString::LoadKind::kBootImageAddress: { @@ -7925,11 +7901,9 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high); constexpr bool non_baker_read_barrier = kUseReadBarrier && !kUseBakerReadBarrier; Register temp = non_baker_read_barrier ? out : locations->GetTemp(0).AsRegister<Register>(); - bool reordering = __ SetReorder(false); codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, temp, base_or_current_method_reg); - __ SetReorder(reordering); GenerateGcRootFieldLoad(load, out_loc, temp, diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 7195b9d89d..c0e1ec0fa2 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -637,8 +637,7 @@ class CodeGeneratorMIPS : public CodeGenerator { void EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info_high, Register out, - Register base, - PcRelativePatchInfo* info_low = nullptr); + Register base); // The JitPatchInfo is used for JIT string and class loads. struct JitPatchInfo { diff --git a/compiler/utils/label.h b/compiler/utils/label.h index 85710d0811..d835c63443 100644 --- a/compiler/utils/label.h +++ b/compiler/utils/label.h @@ -31,9 +31,11 @@ namespace arm64 { } // namespace arm64 namespace mips { class MipsAssembler; + class MipsLabel; } // namespace mips namespace mips64 { class Mips64Assembler; + class Mips64Label; } // namespace mips64 namespace x86 { class X86Assembler; @@ -114,7 +116,9 @@ class Label { friend class arm64::Arm64Assembler; friend class mips::MipsAssembler; + friend class mips::MipsLabel; friend class mips64::Mips64Assembler; + friend class mips64::Mips64Label; friend class x86::X86Assembler; friend class x86::NearLabel; friend class x86_64::X86_64Assembler; diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index 18099d8e13..b300cc597f 100644 --- a/compiler/utils/mips/assembler_mips.cc +++ b/compiler/utils/mips/assembler_mips.cc @@ -47,7 +47,8 @@ MipsAssembler::DelaySlot::DelaySlot() fpr_outs_mask_(0), fpr_ins_mask_(0), cc_outs_mask_(0), - cc_ins_mask_(0) {} + cc_ins_mask_(0), + patcher_label_(nullptr) {} void MipsAssembler::DsFsmInstr(uint32_t instruction, uint32_t gpr_outs_mask, @@ -55,7 +56,8 @@ void MipsAssembler::DsFsmInstr(uint32_t instruction, uint32_t fpr_outs_mask, uint32_t fpr_ins_mask, uint32_t cc_outs_mask, - uint32_t cc_ins_mask) { + uint32_t cc_ins_mask, + MipsLabel* patcher_label) { if (!reordering_) { CHECK_EQ(ds_fsm_state_, kExpectingLabel); CHECK_EQ(delay_slot_.instruction_, 0u); @@ -96,6 +98,7 @@ void MipsAssembler::DsFsmInstr(uint32_t instruction, delay_slot_.fpr_ins_mask_ = fpr_ins_mask; delay_slot_.cc_outs_mask_ = cc_outs_mask; delay_slot_.cc_ins_mask_ = cc_ins_mask; + delay_slot_.patcher_label_ = patcher_label; } void MipsAssembler::DsFsmLabel() { @@ -167,8 +170,12 @@ void MipsAssembler::DsFsmInstrNop(uint32_t instruction ATTRIBUTE_UNUSED) { DsFsmInstr(0, 0, 0, 0, 0, 0, 0); } -void MipsAssembler::DsFsmInstrRrr(uint32_t instruction, Register out, Register in1, Register in2) { - DsFsmInstr(instruction, (1u << out), (1u << in1) | (1u << in2), 0, 0, 0, 0); +void MipsAssembler::DsFsmInstrRrr(uint32_t instruction, + Register out, + Register in1, + Register in2, + MipsLabel* patcher_label) { + DsFsmInstr(instruction, (1u << out), (1u << in1) | (1u << in2), 0, 0, 0, 0, patcher_label); } void MipsAssembler::DsFsmInstrRrrr(uint32_t instruction, @@ -310,8 +317,8 @@ void MipsAssembler::EmitBranches() { // Switch from appending instructions at the end of the buffer to overwriting // existing instructions (branch placeholders) in the buffer. overwriting_ = true; - for (auto& branch : branches_) { - EmitBranch(&branch); + for (size_t id = 0; id < branches_.size(); id++) { + EmitBranch(id); } overwriting_ = false; } @@ -531,8 +538,15 @@ void MipsAssembler::Addu(Register rd, Register rs, Register rt) { DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x21), rd, rs, rt); } +void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16, MipsLabel* patcher_label) { + if (patcher_label != nullptr) { + Bind(patcher_label); + } + DsFsmInstrRrr(EmitI(0x9, rs, rt, imm16), rt, rs, rs, patcher_label); +} + void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0x9, rs, rt, imm16), rt, rs, rs); + Addiu(rt, rs, imm16, /* patcher_label */ nullptr); } void MipsAssembler::Subu(Register rd, Register rs, Register rt) { @@ -791,8 +805,15 @@ void MipsAssembler::Lh(Register rt, Register rs, uint16_t imm16) { DsFsmInstrRrr(EmitI(0x21, rs, rt, imm16), rt, rs, rs); } +void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16, MipsLabel* patcher_label) { + if (patcher_label != nullptr) { + Bind(patcher_label); + } + DsFsmInstrRrr(EmitI(0x23, rs, rt, imm16), rt, rs, rs, patcher_label); +} + void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0x23, rs, rt, imm16), rt, rs, rs); + Lw(rt, rs, imm16, /* patcher_label */ nullptr); } void MipsAssembler::Lwl(Register rt, Register rs, uint16_t imm16) { @@ -866,8 +887,15 @@ void MipsAssembler::Sh(Register rt, Register rs, uint16_t imm16) { DsFsmInstrRrr(EmitI(0x29, rs, rt, imm16), ZERO, rt, rs); } +void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16, MipsLabel* patcher_label) { + if (patcher_label != nullptr) { + Bind(patcher_label); + } + DsFsmInstrRrr(EmitI(0x2b, rs, rt, imm16), ZERO, rt, rs, patcher_label); +} + void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0x2b, rs, rt, imm16), ZERO, rt, rs); + Sw(rt, rs, imm16, /* patcher_label */ nullptr); } void MipsAssembler::Swl(Register rt, Register rs, uint16_t imm16) { @@ -991,6 +1019,7 @@ void MipsAssembler::Jal(uint32_t addr26) { void MipsAssembler::Jalr(Register rd, Register rs) { uint32_t last_instruction = delay_slot_.instruction_; + MipsLabel* patcher_label = delay_slot_.patcher_label_; bool exchange = (last_instruction != 0 && (delay_slot_.gpr_outs_mask_ & (1u << rs)) == 0 && ((delay_slot_.gpr_ins_mask_ | delay_slot_.gpr_outs_mask_) & (1u << rd)) == 0); @@ -1011,6 +1040,10 @@ void MipsAssembler::Jalr(Register rd, Register rs) { CHECK_EQ(instr1, last_instruction); buffer_.Store<uint32_t>(pos1, instr2); buffer_.Store<uint32_t>(pos2, instr1); + // Move the patcher label along with the patched instruction. + if (patcher_label != nullptr) { + patcher_label->AdjustBoundPosition(sizeof(uint32_t)); + } } else if (reordering_) { Nop(); } @@ -3237,7 +3270,8 @@ MipsAssembler::Branch::Branch(bool is_r6, lhs_reg_(0), rhs_reg_(0), condition_(kUncond), - delayed_instruction_(kUnfilledDelaySlot) { + delayed_instruction_(kUnfilledDelaySlot), + patcher_label_(nullptr) { InitializeType( (is_call ? (is_bare ? kBareCall : kCall) : (is_bare ? kBareCondBranch : kCondBranch)), is_r6); @@ -3256,7 +3290,8 @@ MipsAssembler::Branch::Branch(bool is_r6, lhs_reg_(lhs_reg), rhs_reg_(rhs_reg), condition_(condition), - delayed_instruction_(kUnfilledDelaySlot) { + delayed_instruction_(kUnfilledDelaySlot), + patcher_label_(nullptr) { CHECK_NE(condition, kUncond); switch (condition) { case kCondLT: @@ -3313,7 +3348,8 @@ MipsAssembler::Branch::Branch(bool is_r6, lhs_reg_(dest_reg), rhs_reg_(base_reg), condition_(kUncond), - delayed_instruction_(kUnfilledDelaySlot) { + delayed_instruction_(kUnfilledDelaySlot), + patcher_label_(nullptr) { CHECK_NE(dest_reg, ZERO); if (is_r6) { CHECK_EQ(base_reg, ZERO); @@ -3690,6 +3726,17 @@ const MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) const return &branches_[branch_id]; } +void MipsAssembler::BindRelativeToPrecedingBranch(MipsLabel* label, + uint32_t prev_branch_id_plus_one, + uint32_t position) { + if (prev_branch_id_plus_one != 0) { + const Branch* branch = GetBranch(prev_branch_id_plus_one - 1); + position -= branch->GetEndLocation(); + } + label->prev_branch_id_plus_one_ = prev_branch_id_plus_one; + label->BindTo(position); +} + void MipsAssembler::Bind(MipsLabel* label) { CHECK(!label->IsBound()); uint32_t bound_pc = buffer_.Size(); @@ -3715,22 +3762,15 @@ void MipsAssembler::Bind(MipsLabel* label) { // Now make the label object contain its own location (relative to the end of the preceding // branch, if any; it will be used by the branches referring to and following this label). - label->prev_branch_id_plus_one_ = branches_.size(); - if (label->prev_branch_id_plus_one_) { - uint32_t branch_id = label->prev_branch_id_plus_one_ - 1; - const Branch* branch = GetBranch(branch_id); - bound_pc -= branch->GetEndLocation(); - } - label->BindTo(bound_pc); + BindRelativeToPrecedingBranch(label, branches_.size(), bound_pc); } uint32_t MipsAssembler::GetLabelLocation(const MipsLabel* label) const { CHECK(label->IsBound()); uint32_t target = label->Position(); - if (label->prev_branch_id_plus_one_) { + if (label->prev_branch_id_plus_one_ != 0) { // Get label location based on the branch preceding it. - uint32_t branch_id = label->prev_branch_id_plus_one_ - 1; - const Branch* branch = GetBranch(branch_id); + const Branch* branch = GetBranch(label->prev_branch_id_plus_one_ - 1); target += branch->GetEndLocation(); } return target; @@ -3872,10 +3912,15 @@ uint32_t MipsAssembler::Branch::GetDelayedInstruction() const { return delayed_instruction_; } -void MipsAssembler::Branch::SetDelayedInstruction(uint32_t instruction) { +MipsLabel* MipsAssembler::Branch::GetPatcherLabel() const { + return patcher_label_; +} + +void MipsAssembler::Branch::SetDelayedInstruction(uint32_t instruction, MipsLabel* patcher_label) { CHECK_NE(instruction, kUnfilledDelaySlot); CHECK_EQ(delayed_instruction_, kUnfilledDelaySlot); delayed_instruction_ = instruction; + patcher_label_ = patcher_label; } void MipsAssembler::Branch::DecrementLocations() { @@ -3916,7 +3961,7 @@ void MipsAssembler::MoveInstructionToDelaySlot(Branch& branch) { buffer_.Resize(size); // Attach it to the branch and adjust the branch locations. branch.DecrementLocations(); - branch.SetDelayedInstruction(delay_slot_.instruction_); + branch.SetDelayedInstruction(delay_slot_.instruction_, delay_slot_.patcher_label_); } else if (!reordering_ && branch.GetType() == Branch::kUncondBranch) { // If reordefing is disabled, prevent absorption of the target instruction. branch.SetDelayedInstruction(Branch::kUnfillableDelaySlot); @@ -4140,15 +4185,49 @@ const MipsAssembler::Branch::BranchInfo MipsAssembler::Branch::branch_info_[] = { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6FarLiteral }; +static inline bool IsAbsorbableInstruction(uint32_t instruction) { + // The relative patcher patches addiu, lw and sw with an immediate operand of 0x5678. + // We want to make sure that these instructions do not get absorbed into delay slots + // of unconditional branches on R2. Absorption would otherwise make copies of + // unpatched instructions. + if ((instruction & 0xFFFF) != 0x5678) { + return true; + } + switch (instruction >> kOpcodeShift) { + case 0x09: // Addiu. + case 0x23: // Lw. + case 0x2B: // Sw. + return false; + default: + return true; + } +} + // Note: make sure branch_info_[] and EmitBranch() are kept synchronized. -void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) { +void MipsAssembler::EmitBranch(uint32_t branch_id) { CHECK_EQ(overwriting_, true); + Branch* branch = GetBranch(branch_id); overwrite_location_ = branch->GetLocation(); uint32_t offset = branch->GetOffset(GetBranchOrPcRelBaseForEncoding(branch)); BranchCondition condition = branch->GetCondition(); Register lhs = branch->GetLeftRegister(); Register rhs = branch->GetRightRegister(); uint32_t delayed_instruction = branch->GetDelayedInstruction(); + MipsLabel* patcher_label = branch->GetPatcherLabel(); + if (patcher_label != nullptr) { + // Update the patcher label location to account for branch promotion and + // delay slot filling. + CHECK(patcher_label->IsBound()); + uint32_t bound_pc = branch->GetLocation(); + if (!branch->IsLong()) { + // Short branches precede delay slots. + // Long branches follow "delay slots". + bound_pc += sizeof(uint32_t); + } + // Rebind the label. + patcher_label->Reinitialize(); + BindRelativeToPrecedingBranch(patcher_label, branch_id, bound_pc); + } switch (branch->GetType()) { // R2 short branches. case Branch::kUncondBranch: @@ -4164,8 +4243,11 @@ void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) { if (offset != 0x7FFF) { uint32_t target = branch->GetTarget(); if (std::binary_search(ds_fsm_target_pcs_.begin(), ds_fsm_target_pcs_.end(), target)) { - delayed_instruction = buffer_.Load<uint32_t>(target); - offset++; + uint32_t target_instruction = buffer_.Load<uint32_t>(target); + if (IsAbsorbableInstruction(target_instruction)) { + delayed_instruction = target_instruction; + offset++; + } } } } @@ -4406,6 +4488,11 @@ void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) { } CHECK_EQ(overwrite_location_, branch->GetEndLocation()); CHECK_LT(branch->GetSize(), static_cast<uint32_t>(Branch::kMaxBranchSize)); + if (patcher_label != nullptr) { + // The patched instruction should look like one. + uint32_t patched_instruction = buffer_.Load<uint32_t>(GetLabelLocation(patcher_label)); + CHECK(!IsAbsorbableInstruction(patched_instruction)); + } } void MipsAssembler::B(MipsLabel* label, bool is_bare) { diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index 7f9d5763f1..0f163ac83f 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -80,6 +80,12 @@ class MipsLabel : public Label { MipsLabel(MipsLabel&& src) : Label(std::move(src)), prev_branch_id_plus_one_(src.prev_branch_id_plus_one_) {} + void AdjustBoundPosition(int delta) { + CHECK(IsBound()); + // Bound label's position is negative, hence decrementing it. + position_ -= delta; + } + private: uint32_t prev_branch_id_plus_one_; // To get distance from preceding branch, if any. @@ -215,6 +221,7 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi // Emit Machine Instructions. void Addu(Register rd, Register rs, Register rt); + void Addiu(Register rt, Register rs, uint16_t imm16, MipsLabel* patcher_label); void Addiu(Register rt, Register rs, uint16_t imm16); void Subu(Register rd, Register rs, Register rt); @@ -272,6 +279,7 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi void Lb(Register rt, Register rs, uint16_t imm16); void Lh(Register rt, Register rs, uint16_t imm16); + void Lw(Register rt, Register rs, uint16_t imm16, MipsLabel* patcher_label); void Lw(Register rt, Register rs, uint16_t imm16); void Lwl(Register rt, Register rs, uint16_t imm16); void Lwr(Register rt, Register rs, uint16_t imm16); @@ -287,6 +295,7 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi void Sb(Register rt, Register rs, uint16_t imm16); void Sh(Register rt, Register rs, uint16_t imm16); + void Sw(Register rt, Register rs, uint16_t imm16, MipsLabel* patcher_label); void Sw(Register rt, Register rs, uint16_t imm16); void Swl(Register rt, Register rs, uint16_t imm16); void Swr(Register rt, Register rs, uint16_t imm16); @@ -1288,6 +1297,9 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi uint32_t cc_ins_mask_; // Branches never operate on the LO and HI registers, hence there's // no mask for LO and HI. + + // Label for patchable instructions to allow moving them into delay slots. + MipsLabel* patcher_label_; }; // Delay slot finite state machine's (DS FSM's) state. The FSM state is updated @@ -1440,8 +1452,9 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi // Various helpers for branch delay slot management. bool CanHaveDelayedInstruction(const DelaySlot& delay_slot) const; - void SetDelayedInstruction(uint32_t instruction); + void SetDelayedInstruction(uint32_t instruction, MipsLabel* patcher_label = nullptr); uint32_t GetDelayedInstruction() const; + MipsLabel* GetPatcherLabel() const; void DecrementLocations(); // Returns the bit size of the signed offset that the branch instruction can handle. @@ -1526,6 +1539,8 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi // kUnfillableDelaySlot if none and unfillable // (the latter is only used for unconditional R2 // branches). + + MipsLabel* patcher_label_; // Patcher label for the instruction in the delay slot. }; friend std::ostream& operator<<(std::ostream& os, const Branch::Type& rhs); friend std::ostream& operator<<(std::ostream& os, const Branch::OffsetBits& rhs); @@ -1580,9 +1595,14 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi uint32_t fpr_outs_mask, uint32_t fpr_ins_mask, uint32_t cc_outs_mask, - uint32_t cc_ins_mask); + uint32_t cc_ins_mask, + MipsLabel* patcher_label = nullptr); void DsFsmInstrNop(uint32_t instruction); - void DsFsmInstrRrr(uint32_t instruction, Register out, Register in1, Register in2); + void DsFsmInstrRrr(uint32_t instruction, + Register out, + Register in1, + Register in2, + MipsLabel* patcher_label = nullptr); void DsFsmInstrRrrr(uint32_t instruction, Register in1_out, Register in2, Register in3); void DsFsmInstrFff(uint32_t instruction, FRegister out, FRegister in1, FRegister in2); void DsFsmInstrFfff(uint32_t instruction, FRegister in1_out, FRegister in2, FRegister in3); @@ -1605,12 +1625,15 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi const Branch* GetBranch(uint32_t branch_id) const; uint32_t GetBranchLocationOrPcRelBase(const MipsAssembler::Branch* branch) const; uint32_t GetBranchOrPcRelBaseForEncoding(const MipsAssembler::Branch* branch) const; + void BindRelativeToPrecedingBranch(MipsLabel* label, + uint32_t prev_branch_id_plus_one, + uint32_t position); void EmitLiterals(); void ReserveJumpTableSpace(); void EmitJumpTables(); void PromoteBranches(); - void EmitBranch(Branch* branch); + void EmitBranch(uint32_t branch_id); void EmitBranches(); void PatchCFI(size_t number_of_delayed_adjust_pcs); diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc index 6e52b17000..a5cd5a7c65 100644 --- a/compiler/utils/mips/assembler_mips32r6_test.cc +++ b/compiler/utils/mips/assembler_mips32r6_test.cc @@ -1529,10 +1529,62 @@ TEST_F(AssemblerMIPS32r6Test, SetReorder) { DriverStr(expected, "SetReorder"); } +TEST_F(AssemblerMIPS32r6Test, ReorderPatchedInstruction) { + __ SetReorder(true); + mips::MipsLabel label1, label2; + mips::MipsLabel patcher_label1, patcher_label2, patcher_label3, patcher_label4, patcher_label5; + __ Lw(mips::V0, mips::A0, 0x5678, &patcher_label1); + __ Bc1eqz(mips::F0, &label1); + constexpr uint32_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label1); + __ Sw(mips::V0, mips::A0, 0x5678, &patcher_label2); + __ Bc1nez(mips::F2, &label2); + constexpr uint32_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label2); + __ Addiu(mips::V0, mips::A0, 0x5678, &patcher_label3); + __ Bc1eqz(mips::F4, &label1); + __ Lw(mips::V0, mips::A0, 0x5678, &patcher_label4); + __ Jalr(mips::T9); + __ Sw(mips::V0, mips::A0, 0x5678, &patcher_label5); + __ Bltc(mips::V0, mips::V1, &label2); + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + + std::string expected = + ".set noreorder\n" + "bc1eqz $f0, 1f\n" + "lw $v0, 0x5678($a0)\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + "bc1nez $f2, 2f\n" + "sw $v0, 0x5678($a0)\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "2:\n" + "bc1eqz $f4, 1b\n" + "addiu $v0, $a0, 0x5678\n" + "jalr $t9\n" + "lw $v0, 0x5678($a0)\n" + "sw $v0, 0x5678($a0)\n" + "bltc $v0, $v1, 2b\n" + "nop\n" + "addu $zero, $zero, $zero\n"; + DriverStr(expected, "ReorderPatchedInstruction"); + EXPECT_EQ(__ GetLabelLocation(&patcher_label1), 1 * 4u); + EXPECT_EQ(__ GetLabelLocation(&patcher_label2), (kAdduCount1 + 3) * 4u); + EXPECT_EQ(__ GetLabelLocation(&patcher_label3), (kAdduCount1 + kAdduCount2 + 5) * 4u); + EXPECT_EQ(__ GetLabelLocation(&patcher_label4), (kAdduCount1 + kAdduCount2 + 7) * 4u); + EXPECT_EQ(__ GetLabelLocation(&patcher_label5), (kAdduCount1 + kAdduCount2 + 8) * 4u); +} + TEST_F(AssemblerMIPS32r6Test, LongBranchReorder) { - mips::MipsLabel label; + mips::MipsLabel label, patcher_label1, patcher_label2; __ SetReorder(true); - __ Subu(mips::T0, mips::T1, mips::T2); + __ Addiu(mips::T0, mips::T1, 0x5678, &patcher_label1); __ Bc1nez(mips::F0, &label); constexpr uint32_t kAdduCount1 = (1u << 15) + 1; for (uint32_t i = 0; i != kAdduCount1; ++i) { @@ -1543,7 +1595,7 @@ TEST_F(AssemblerMIPS32r6Test, LongBranchReorder) { for (uint32_t i = 0; i != kAdduCount2; ++i) { __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); } - __ Subu(mips::T0, mips::T1, mips::T2); + __ Addiu(mips::T0, mips::T1, 0x5678, &patcher_label2); __ Bc1eqz(mips::F0, &label); uint32_t offset_forward = 2 + kAdduCount1; // 2: account for auipc and jic. @@ -1557,7 +1609,7 @@ TEST_F(AssemblerMIPS32r6Test, LongBranchReorder) { std::ostringstream oss; oss << ".set noreorder\n" - "subu $t0, $t1, $t2\n" + "addiu $t0, $t1, 0x5678\n" "bc1eqz $f0, 1f\n" "auipc $at, 0x" << std::hex << High16Bits(offset_forward) << "\n" "jic $at, 0x" << std::hex << Low16Bits(offset_forward) << "\n" @@ -1565,13 +1617,15 @@ TEST_F(AssemblerMIPS32r6Test, LongBranchReorder) { RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") << "2:\n" << RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") << - "subu $t0, $t1, $t2\n" + "addiu $t0, $t1, 0x5678\n" "bc1nez $f0, 3f\n" "auipc $at, 0x" << std::hex << High16Bits(offset_back) << "\n" "jic $at, 0x" << std::hex << Low16Bits(offset_back) << "\n" "3:\n"; std::string expected = oss.str(); DriverStr(expected, "LongBranchReorder"); + EXPECT_EQ(__ GetLabelLocation(&patcher_label1), 0 * 4u); + EXPECT_EQ(__ GetLabelLocation(&patcher_label2), (kAdduCount1 + kAdduCount2 + 4) * 4u); } /////////////////////// diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc index d9bf0b82f4..680c347fef 100644 --- a/compiler/utils/mips/assembler_mips_test.cc +++ b/compiler/utils/mips/assembler_mips_test.cc @@ -2506,6 +2506,7 @@ TEST_F(AssemblerMIPSTest, Reordering) { TEST_F(AssemblerMIPSTest, AbsorbTargetInstruction) { mips::MipsLabel label1, label2, label3, label4, label5, label6; + mips::MipsLabel label7, label8, label9, label10, label11, label12, label13; __ SetReorder(true); __ B(&label1); @@ -2529,6 +2530,41 @@ TEST_F(AssemblerMIPSTest, AbsorbTargetInstruction) { __ Bind(&label6); __ CodePosition(); // Even across Bind(), CodePosition() prevents absorbing the ADDU above. + __ Nop(); + __ B(&label7); + __ Bind(&label7); + __ Lw(mips::V0, mips::A0, 0x5678); // Possibly patchable instruction, not absorbed. + + __ Nop(); + __ B(&label8); + __ Bind(&label8); + __ Sw(mips::V0, mips::A0, 0x5678); // Possibly patchable instruction, not absorbed. + + __ Nop(); + __ B(&label9); + __ Bind(&label9); + __ Addiu(mips::V0, mips::A0, 0x5678); // Possibly patchable instruction, not absorbed. + + __ Nop(); + __ B(&label10); + __ Bind(&label10); + __ Lw(mips::V0, mips::A0, 0x5680); // Immediate isn't 0x5678, absorbed. + + __ Nop(); + __ B(&label11); + __ Bind(&label11); + __ Sw(mips::V0, mips::A0, 0x5680); // Immediate isn't 0x5678, absorbed. + + __ Nop(); + __ B(&label12); + __ Bind(&label12); + __ Addiu(mips::V0, mips::A0, 0x5680); // Immediate isn't 0x5678, absorbed. + + __ Nop(); + __ B(&label13); + __ Bind(&label13); + __ Andi(mips::V0, mips::A0, 0x5678); // Not one of patchable instructions, absorbed. + std::string expected = ".set noreorder\n" "b 1f\n" @@ -2550,7 +2586,49 @@ TEST_F(AssemblerMIPSTest, AbsorbTargetInstruction) { "b 5f\n" "nop\n" "5:\n" - "addu $t0, $t1, $t2\n"; + "addu $t0, $t1, $t2\n" + + "nop\n" + "b 7f\n" + "nop\n" + "7:\n" + "lw $v0, 0x5678($a0)\n" + + "nop\n" + "b 8f\n" + "nop\n" + "8:\n" + "sw $v0, 0x5678($a0)\n" + + "nop\n" + "b 9f\n" + "nop\n" + "9:\n" + "addiu $v0, $a0, 0x5678\n" + + "nop\n" + "b 10f\n" + "lw $v0, 0x5680($a0)\n" + "lw $v0, 0x5680($a0)\n" + "10:\n" + + "nop\n" + "b 11f\n" + "sw $v0, 0x5680($a0)\n" + "sw $v0, 0x5680($a0)\n" + "11:\n" + + "nop\n" + "b 12f\n" + "addiu $v0, $a0, 0x5680\n" + "addiu $v0, $a0, 0x5680\n" + "12:\n" + + "nop\n" + "b 13f\n" + "andi $v0, $a0, 0x5678\n" + "andi $v0, $a0, 0x5678\n" + "13:\n"; DriverStr(expected, "AbsorbTargetInstruction"); } @@ -2637,10 +2715,62 @@ TEST_F(AssemblerMIPSTest, SetReorder) { DriverStr(expected, "SetReorder"); } +TEST_F(AssemblerMIPSTest, ReorderPatchedInstruction) { + __ SetReorder(true); + mips::MipsLabel label1, label2; + mips::MipsLabel patcher_label1, patcher_label2, patcher_label3, patcher_label4, patcher_label5; + __ Lw(mips::V0, mips::A0, 0x5678, &patcher_label1); + __ Beq(mips::A0, mips::A1, &label1); + constexpr uint32_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label1); + __ Sw(mips::V0, mips::A0, 0x5678, &patcher_label2); + __ Bltz(mips::V1, &label2); + constexpr uint32_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label2); + __ Addiu(mips::V0, mips::A0, 0x5678, &patcher_label3); + __ B(&label1); + __ Lw(mips::V0, mips::A0, 0x5678, &patcher_label4); + __ Jalr(mips::T9); + __ Sw(mips::V0, mips::A0, 0x5678, &patcher_label5); + __ Blt(mips::V0, mips::V1, &label2); + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + + std::string expected = + ".set noreorder\n" + "beq $a0, $a1, 1f\n" + "lw $v0, 0x5678($a0)\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + "bltz $v1, 2f\n" + "sw $v0, 0x5678($a0)\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "2:\n" + "b 1b\n" + "addiu $v0, $a0, 0x5678\n" + "jalr $t9\n" + "lw $v0, 0x5678($a0)\n" + "slt $at, $v0, $v1\n" + "bnez $at, 2b\n" + "sw $v0, 0x5678($a0)\n" + "addu $zero, $zero, $zero\n"; + DriverStr(expected, "ReorderPatchedInstruction"); + EXPECT_EQ(__ GetLabelLocation(&patcher_label1), 1 * 4u); + EXPECT_EQ(__ GetLabelLocation(&patcher_label2), (kAdduCount1 + 3) * 4u); + EXPECT_EQ(__ GetLabelLocation(&patcher_label3), (kAdduCount1 + kAdduCount2 + 5) * 4u); + EXPECT_EQ(__ GetLabelLocation(&patcher_label4), (kAdduCount1 + kAdduCount2 + 7) * 4u); + EXPECT_EQ(__ GetLabelLocation(&patcher_label5), (kAdduCount1 + kAdduCount2 + 10) * 4u); +} + TEST_F(AssemblerMIPSTest, LongBranchReorder) { - mips::MipsLabel label; + mips::MipsLabel label, patcher_label1, patcher_label2; __ SetReorder(true); - __ Subu(mips::T0, mips::T1, mips::T2); + __ Addiu(mips::T0, mips::T1, 0x5678, &patcher_label1); __ B(&label); constexpr uint32_t kAdduCount1 = (1u << 15) + 1; for (size_t i = 0; i != kAdduCount1; ++i) { @@ -2651,7 +2781,7 @@ TEST_F(AssemblerMIPSTest, LongBranchReorder) { for (size_t i = 0; i != kAdduCount2; ++i) { __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); } - __ Subu(mips::T0, mips::T1, mips::T2); + __ Addiu(mips::T0, mips::T1, 0x5678, &patcher_label2); __ B(&label); // Account for 5 extra instructions: ori, addu, lw, jalr, addiu. @@ -2662,7 +2792,7 @@ TEST_F(AssemblerMIPSTest, LongBranchReorder) { std::ostringstream oss; oss << ".set noreorder\n" - "subu $t0, $t1, $t2\n" + "addiu $t0, $t1, 0x5678\n" "addiu $sp, $sp, -4\n" "sw $ra, 0($sp)\n" "bltzal $zero, .+4\n" @@ -2674,7 +2804,7 @@ TEST_F(AssemblerMIPSTest, LongBranchReorder) { "addiu $sp, $sp, 4\n" << RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") << RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") << - "subu $t0, $t1, $t2\n" + "addiu $t0, $t1, 0x5678\n" "addiu $sp, $sp, -4\n" "sw $ra, 0($sp)\n" "bltzal $zero, .+4\n" @@ -2686,6 +2816,8 @@ TEST_F(AssemblerMIPSTest, LongBranchReorder) { "addiu $sp, $sp, 4\n"; std::string expected = oss.str(); DriverStr(expected, "LongBranchReorder"); + EXPECT_EQ(__ GetLabelLocation(&patcher_label1), 0 * 4u); + EXPECT_EQ(__ GetLabelLocation(&patcher_label2), (kAdduCount1 + kAdduCount2 + 10) * 4u); } /////////////////////// |