emit_x64: Refactor patching code
* Only have a single std::unordered_map for patching information * Factor patch emitters into own functions * Implement EmitX64::Unpatch
This commit is contained in:
parent
59aeed0b70
commit
b1d3e7aae9
3 changed files with 80 additions and 50 deletions
|
@ -62,8 +62,8 @@ EmitX64::BlockDescriptor EmitX64::Emit(IR::Block& block) {
|
||||||
|
|
||||||
code->align();
|
code->align();
|
||||||
const CodePtr code_ptr = code->getCurr();
|
const CodePtr code_ptr = code->getCurr();
|
||||||
basic_blocks[descriptor].code_ptr = code_ptr;
|
EmitX64::BlockDescriptor& block_desc = block_descriptors[descriptor.UniqueHash()];
|
||||||
unique_hash_to_code_ptr[descriptor.UniqueHash()] = code_ptr;
|
block_desc.code_ptr = code_ptr;
|
||||||
|
|
||||||
EmitCondPrelude(block);
|
EmitCondPrelude(block);
|
||||||
|
|
||||||
|
@ -95,13 +95,13 @@ EmitX64::BlockDescriptor EmitX64::Emit(IR::Block& block) {
|
||||||
reg_alloc.AssertNoMoreUses();
|
reg_alloc.AssertNoMoreUses();
|
||||||
|
|
||||||
Patch(descriptor, code_ptr);
|
Patch(descriptor, code_ptr);
|
||||||
basic_blocks[descriptor].size = std::intptr_t(code->getCurr()) - std::intptr_t(code_ptr);
|
block_desc.size = std::intptr_t(code->getCurr()) - std::intptr_t(code_ptr);
|
||||||
return basic_blocks[descriptor];
|
return block_desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<EmitX64::BlockDescriptor> EmitX64::GetBasicBlock(IR::LocationDescriptor descriptor) const {
|
boost::optional<EmitX64::BlockDescriptor> EmitX64::GetBasicBlock(IR::LocationDescriptor descriptor) const {
|
||||||
auto iter = basic_blocks.find(descriptor);
|
auto iter = block_descriptors.find(descriptor.UniqueHash());
|
||||||
if (iter == basic_blocks.end())
|
if (iter == block_descriptors.end())
|
||||||
return boost::none;
|
return boost::none;
|
||||||
return boost::make_optional<BlockDescriptor>(iter->second);
|
return boost::make_optional<BlockDescriptor>(iter->second);
|
||||||
}
|
}
|
||||||
|
@ -418,24 +418,25 @@ void EmitX64::EmitPushRSB(IR::Block&, IR::Inst* inst) {
|
||||||
using namespace Xbyak::util;
|
using namespace Xbyak::util;
|
||||||
|
|
||||||
ASSERT(inst->GetArg(0).IsImmediate());
|
ASSERT(inst->GetArg(0).IsImmediate());
|
||||||
u64 imm64 = inst->GetArg(0).GetU64();
|
u64 unique_hash_of_target = inst->GetArg(0).GetU64();
|
||||||
|
|
||||||
|
auto iter = block_descriptors.find(unique_hash_of_target);
|
||||||
|
CodePtr target_code_ptr = iter != block_descriptors.end()
|
||||||
|
? iter->second.code_ptr
|
||||||
|
: code->GetReturnFromRunCodeAddress();
|
||||||
|
|
||||||
Xbyak::Reg64 code_ptr_reg = reg_alloc.ScratchGpr({HostLoc::RCX});
|
Xbyak::Reg64 code_ptr_reg = reg_alloc.ScratchGpr({HostLoc::RCX});
|
||||||
Xbyak::Reg64 loc_desc_reg = reg_alloc.ScratchGpr();
|
Xbyak::Reg64 loc_desc_reg = reg_alloc.ScratchGpr();
|
||||||
Xbyak::Reg32 index_reg = reg_alloc.ScratchGpr().cvt32();
|
Xbyak::Reg32 index_reg = reg_alloc.ScratchGpr().cvt32();
|
||||||
u64 code_ptr = unique_hash_to_code_ptr.find(imm64) != unique_hash_to_code_ptr.end()
|
|
||||||
? reinterpret_cast<u64>(unique_hash_to_code_ptr[imm64])
|
|
||||||
: reinterpret_cast<u64>(code->GetReturnFromRunCodeAddress());
|
|
||||||
|
|
||||||
code->mov(index_reg, dword[r15 + offsetof(JitState, rsb_ptr)]);
|
code->mov(index_reg, dword[r15 + offsetof(JitState, rsb_ptr)]);
|
||||||
code->add(index_reg, 1);
|
code->add(index_reg, 1);
|
||||||
code->and_(index_reg, u32(JitState::RSBSize - 1));
|
code->and_(index_reg, u32(JitState::RSBSize - 1));
|
||||||
|
|
||||||
code->mov(loc_desc_reg, imm64);
|
code->mov(loc_desc_reg, unique_hash_of_target);
|
||||||
CodePtr patch_location = code->getCurr<CodePtr>();
|
|
||||||
patch_unique_hash_locations[imm64].emplace_back(patch_location);
|
patch_information[unique_hash_of_target].mov_rcx.emplace_back(code->getCurr());
|
||||||
code->mov(code_ptr_reg, code_ptr); // This line has to match up with EmitX64::Patch.
|
EmitPatchMovRcx(target_code_ptr);
|
||||||
code->EnsurePatchLocationSize(patch_location, 10);
|
|
||||||
|
|
||||||
Xbyak::Label label;
|
Xbyak::Label label;
|
||||||
for (size_t i = 0; i < JitState::RSBSize; ++i) {
|
for (size_t i = 0; i < JitState::RSBSize; ++i) {
|
||||||
|
@ -2698,12 +2699,12 @@ void EmitX64::EmitTerminalLinkBlock(IR::Term::LinkBlock terminal, IR::LocationDe
|
||||||
|
|
||||||
code->cmp(qword[r15 + offsetof(JitState, cycles_remaining)], 0);
|
code->cmp(qword[r15 + offsetof(JitState, cycles_remaining)], 0);
|
||||||
|
|
||||||
CodePtr patch_location = code->getCurr();
|
patch_information[terminal.next.UniqueHash()].jg.emplace_back(code->getCurr());
|
||||||
patch_jg_locations[terminal.next].emplace_back(patch_location);
|
|
||||||
if (auto next_bb = GetBasicBlock(terminal.next)) {
|
if (auto next_bb = GetBasicBlock(terminal.next)) {
|
||||||
code->jg(next_bb->code_ptr);
|
EmitPatchJg(next_bb->code_ptr);
|
||||||
|
} else {
|
||||||
|
EmitPatchJg();
|
||||||
}
|
}
|
||||||
code->EnsurePatchLocationSize(patch_location, 6);
|
|
||||||
|
|
||||||
code->mov(MJitStateReg(Arm::Reg::PC), terminal.next.PC());
|
code->mov(MJitStateReg(Arm::Reg::PC), terminal.next.PC());
|
||||||
code->ReturnFromRunCode(); // TODO: Check cycles, Properly do a link
|
code->ReturnFromRunCode(); // TODO: Check cycles, Properly do a link
|
||||||
|
@ -2727,15 +2728,11 @@ void EmitX64::EmitTerminalLinkBlockFast(IR::Term::LinkBlockFast terminal, IR::Lo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CodePtr patch_location = code->getCurr();
|
patch_information[terminal.next.UniqueHash()].jmp.emplace_back(code->getCurr());
|
||||||
patch_jmp_locations[terminal.next].emplace_back(patch_location);
|
|
||||||
if (auto next_bb = GetBasicBlock(terminal.next)) {
|
if (auto next_bb = GetBasicBlock(terminal.next)) {
|
||||||
code->jmp(next_bb->code_ptr);
|
EmitPatchJmp(terminal.next, next_bb->code_ptr);
|
||||||
code->EnsurePatchLocationSize(patch_location, 5);
|
|
||||||
} else {
|
} else {
|
||||||
code->mov(MJitStateReg(Arm::Reg::PC), terminal.next.PC());
|
EmitPatchJmp(terminal.next);
|
||||||
code->jmp(code->GetReturnFromRunCodeAddress());
|
|
||||||
code->nop(3);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2775,38 +2772,63 @@ void EmitX64::EmitTerminalCheckHalt(IR::Term::CheckHalt terminal, IR::LocationDe
|
||||||
EmitTerminal(terminal.else_, initial_location);
|
EmitTerminal(terminal.else_, initial_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitX64::Patch(IR::LocationDescriptor desc, CodePtr bb) {
|
void EmitX64::Patch(const IR::LocationDescriptor& desc, CodePtr bb) {
|
||||||
using namespace Xbyak::util;
|
|
||||||
|
|
||||||
const CodePtr save_code_ptr = code->getCurr();
|
const CodePtr save_code_ptr = code->getCurr();
|
||||||
|
const PatchInformation& patch_info = patch_information[desc.UniqueHash()];
|
||||||
|
|
||||||
for (CodePtr location : patch_jg_locations[desc]) {
|
for (CodePtr location : patch_info.jg) {
|
||||||
code->SetCodePtr(location);
|
code->SetCodePtr(location);
|
||||||
code->jg(bb);
|
EmitPatchJg(bb);
|
||||||
code->EnsurePatchLocationSize(location, 6);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (CodePtr location : patch_jmp_locations[desc]) {
|
for (CodePtr location : patch_info.jmp) {
|
||||||
code->SetCodePtr(location);
|
code->SetCodePtr(location);
|
||||||
code->jmp(bb);
|
EmitPatchJmp(desc, bb);
|
||||||
code->EnsurePatchLocationSize(location, 5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (CodePtr location : patch_unique_hash_locations[desc.UniqueHash()]) {
|
for (CodePtr location : patch_info.mov_rcx) {
|
||||||
code->SetCodePtr(location);
|
code->SetCodePtr(location);
|
||||||
code->mov(rcx, reinterpret_cast<u64>(bb));
|
EmitPatchMovRcx(bb);
|
||||||
code->EnsurePatchLocationSize(location, 10);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
code->SetCodePtr(save_code_ptr);
|
code->SetCodePtr(save_code_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitX64::Unpatch(const IR::LocationDescriptor& desc) {
|
||||||
|
Patch(desc, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitPatchJg(CodePtr target_code_ptr) {
|
||||||
|
const CodePtr patch_location = code->getCurr();
|
||||||
|
if (target_code_ptr) {
|
||||||
|
code->jg(target_code_ptr);
|
||||||
|
}
|
||||||
|
code->EnsurePatchLocationSize(patch_location, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitPatchJmp(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) {
|
||||||
|
const CodePtr patch_location = code->getCurr();
|
||||||
|
if (target_code_ptr) {
|
||||||
|
code->jmp(target_code_ptr);
|
||||||
|
} else {
|
||||||
|
code->mov(MJitStateReg(Arm::Reg::PC), target_desc.PC());
|
||||||
|
code->jmp(code->GetReturnFromRunCodeAddress());
|
||||||
|
}
|
||||||
|
code->EnsurePatchLocationSize(patch_location, 13);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitPatchMovRcx(CodePtr target_code_ptr) {
|
||||||
|
if (!target_code_ptr) {
|
||||||
|
target_code_ptr = code->GetReturnFromRunCodeAddress();
|
||||||
|
}
|
||||||
|
const CodePtr patch_location = code->getCurr();
|
||||||
|
code->mov(code->rcx, reinterpret_cast<u64>(target_code_ptr));
|
||||||
|
code->EnsurePatchLocationSize(patch_location, 10);
|
||||||
|
}
|
||||||
|
|
||||||
void EmitX64::ClearCache() {
|
void EmitX64::ClearCache() {
|
||||||
unique_hash_to_code_ptr.clear();
|
block_descriptors.clear();
|
||||||
patch_unique_hash_locations.clear();
|
patch_information.clear();
|
||||||
basic_blocks.clear();
|
|
||||||
patch_jg_locations.clear();
|
|
||||||
patch_jmp_locations.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace BackendX64
|
} // namespace BackendX64
|
||||||
|
|
|
@ -71,7 +71,18 @@ private:
|
||||||
void EmitTerminalPopRSBHint(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location);
|
void EmitTerminalPopRSBHint(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location);
|
||||||
void EmitTerminalIf(IR::Term::If terminal, IR::LocationDescriptor initial_location);
|
void EmitTerminalIf(IR::Term::If terminal, IR::LocationDescriptor initial_location);
|
||||||
void EmitTerminalCheckHalt(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location);
|
void EmitTerminalCheckHalt(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location);
|
||||||
void Patch(IR::LocationDescriptor desc, CodePtr bb);
|
|
||||||
|
// Patching
|
||||||
|
struct PatchInformation {
|
||||||
|
std::vector<CodePtr> jg;
|
||||||
|
std::vector<CodePtr> jmp;
|
||||||
|
std::vector<CodePtr> mov_rcx;
|
||||||
|
};
|
||||||
|
void Patch(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr);
|
||||||
|
void Unpatch(const IR::LocationDescriptor& target_desc);
|
||||||
|
void EmitPatchJg(CodePtr target_code_ptr = nullptr);
|
||||||
|
void EmitPatchJmp(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr = nullptr);
|
||||||
|
void EmitPatchMovRcx(CodePtr target_code_ptr = nullptr);
|
||||||
|
|
||||||
// Global CPU information
|
// Global CPU information
|
||||||
Xbyak::util::Cpu cpu_info;
|
Xbyak::util::Cpu cpu_info;
|
||||||
|
@ -83,11 +94,8 @@ private:
|
||||||
BlockOfCode* code;
|
BlockOfCode* code;
|
||||||
UserCallbacks cb;
|
UserCallbacks cb;
|
||||||
Jit* jit_interface;
|
Jit* jit_interface;
|
||||||
std::unordered_map<u64, CodePtr> unique_hash_to_code_ptr;
|
std::unordered_map<u64, BlockDescriptor> block_descriptors;
|
||||||
std::unordered_map<u64, std::vector<CodePtr>> patch_unique_hash_locations;
|
std::unordered_map<u64, PatchInformation> patch_information;
|
||||||
std::unordered_map<IR::LocationDescriptor, BlockDescriptor> basic_blocks;
|
|
||||||
std::unordered_map<IR::LocationDescriptor, std::vector<CodePtr>> patch_jg_locations;
|
|
||||||
std::unordered_map<IR::LocationDescriptor, std::vector<CodePtr>> patch_jmp_locations;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace BackendX64
|
} // namespace BackendX64
|
||||||
|
|
|
@ -38,6 +38,6 @@ add_executable(dynarmic_tests ${SRCS})
|
||||||
target_link_libraries(dynarmic_tests dynarmic ${llvm_libs})
|
target_link_libraries(dynarmic_tests dynarmic ${llvm_libs})
|
||||||
set_target_properties(dynarmic_tests PROPERTIES LINKER_LANGUAGE CXX)
|
set_target_properties(dynarmic_tests PROPERTIES LINKER_LANGUAGE CXX)
|
||||||
target_include_directories(dynarmic_tests PRIVATE . ../src)
|
target_include_directories(dynarmic_tests PRIVATE . ../src)
|
||||||
target_compile_options(dynarmic PRIVATE ${DYNARMIC_CXX_FLAGS})
|
target_compile_options(dynarmic_tests PRIVATE ${DYNARMIC_CXX_FLAGS})
|
||||||
|
|
||||||
add_test(dynarmic_tests dynarmic_tests)
|
add_test(dynarmic_tests dynarmic_tests)
|
||||||
|
|
Loading…
Reference in a new issue