From 7df093c94bb65e9b3fa23528d800c4e622ab4123 Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 9 Dec 2022 20:06:50 +0200 Subject: [PATCH] Make it work with LLVM Allow multiple entries for a single code. --- Utilities/bin_patch.cpp | 21 ++++--- rpcs3/Emu/Cell/PPUThread.cpp | 114 +++++++++++++++++++++++++---------- 2 files changed, 95 insertions(+), 40 deletions(-) diff --git a/Utilities/bin_patch.cpp b/Utilities/bin_patch.cpp index b2aae1f0ad..9212165ef1 100644 --- a/Utilities/bin_patch.cpp +++ b/Utilities/bin_patch.cpp @@ -703,8 +703,6 @@ static usz apply_modification(std::basic_string& applied, const patch_engin } case patch_type::code_alloc: { - relocate_instructions_at = 0; - const u32 out_branch = vm::try_get_addr(dst + (offset & -4)).first; // Allow only if points to a PPU executable instruction @@ -715,6 +713,13 @@ static usz apply_modification(std::basic_string& applied, const patch_engin const u32 alloc_size = utils::align(static_cast(p.value.long_value + 1) * 4, 0x10000); + // Check if should maybe reuse previous code cave allocation (0 size) + if (alloc_size - 4 != 0) + { + // Nope + relocate_instructions_at = 0; + } + // Always executable u64 flags = vm::alloc_executable | vm::alloc_unwritable; @@ -738,7 +743,7 @@ static usz apply_modification(std::basic_string& applied, const patch_engin // Range allowed for absolute branches to operate at // It takes into account that we need to put a branch for return at the end of memory space - const u32 addr = p.alloc_addr = alloc_map->alloc(alloc_size, nullptr, 0x10000, flags); + const u32 addr = p.alloc_addr = (relocate_instructions_at ? relocate_instructions_at : alloc_map->alloc(alloc_size, nullptr, 0x10000, flags)); if (!addr) { @@ -751,8 +756,12 @@ static usz apply_modification(std::basic_string& applied, const patch_engin // NOP filled std::fill_n(vm::get_super_ptr(addr), p.value.long_value, 0x60000000); - // Register code - ppu_register_range(addr, alloc_size); + // Check if already registered by previous code allocation + if (relocate_instructions_at != addr) + { + // Register code + ppu_register_range(addr, alloc_size); + } resval = out_branch & -4; @@ -772,8 +781,6 @@ static usz apply_modification(std::basic_string& applied, const patch_engin continue; } - // Write address of the allocated memory to the code entry - *vm::get_super_ptr(resval) = addr; relocate_instructions_at = addr; break; } diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 44ebbbd49c..812efcecd6 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -617,34 +617,20 @@ struct ppu_far_jumps_t bool with_toc; std::string module_name; ppu_intrp_func_t func; - }; - - ppu_far_jumps_t(int) noexcept {} - - std::unordered_map vals; - ::jit_runtime rt; - - mutable shared_mutex mutex; - - // Get target address, 'ppu' is used in ppu_far_jump in order to modify registers - u32 get_target(const u32 pc, ppu_thread* ppu = nullptr) - { - reader_lock lock(mutex); - - if (auto it = vals.find(pc); it != vals.end()) + + u32 get_target(u32 pc, ppu_thread* ppu = nullptr) const { - all_info_t& all_info = it->second; - u32 target = all_info.target; + u32 direct_target = this->target; - bool link = all_info.link; - bool from_opd = all_info.with_toc; + bool to_link = this->link; + bool from_opd = this->with_toc; - if (!all_info.module_name.empty()) + if (!this->module_name.empty()) { - target = ppu_get_exported_func_addr(target, all_info.module_name); + direct_target = ppu_get_exported_func_addr(direct_target, this->module_name); } - if (from_opd && !vm::check_addr(target)) + if (from_opd && !vm::check_addr(direct_target)) { // Avoid reading unmapped memory under mutex from_opd = false; @@ -652,11 +638,11 @@ struct ppu_far_jumps_t if (from_opd) { - auto& opd = vm::_ref(target); - target = opd.addr; + auto& opd = vm::_ref(direct_target); + direct_target = opd.addr; // We modify LR to custom values here - link = false; + to_link = false; if (ppu) { @@ -670,20 +656,71 @@ struct ppu_far_jumps_t saved_info.saved_lr = std::exchange(ppu->lr, g_fxo->get().func_addr(FIND_FUNC(ppu_return_from_far_jump), true)); saved_info.saved_r2 = std::exchange(ppu->gpr[2], opd.rtoc); } - } - if (link && ppu) + if (to_link && ppu) { ppu->lr = pc + 4; } - return target; + return direct_target; + } + }; + + ppu_far_jumps_t(int) noexcept {} + + std::map vals; + ::jit_runtime rt; + + mutable shared_mutex mutex; + + // Get target address, 'ppu' is used in ppu_far_jump in order to modify registers + u32 get_target(u32 pc, ppu_thread* ppu = nullptr) + { + reader_lock lock(mutex); + + if (auto it = vals.find(pc); it != vals.end()) + { + all_info_t& all_info = it->second; + return all_info.get_target(pc, ppu); } return {}; } + // Get function patches in range (entry -> target) + std::vector> get_targets(u32 pc, u32 size) + { + std::vector> targets; + + reader_lock lock(mutex); + + auto it = vals.lower_bound(pc); + + if (it == vals.end()) + { + return targets; + } + + if (it->first >= pc + size) + { + return targets; + } + + for (auto end = vals.lower_bound(pc + size); it != end; it++) + { + all_info_t& all_info = it->second; + + if (u32 target = all_info.get_target(it->first)) + { + targets.emplace_back(it->first, target); + } + } + + return targets; + } + + // Generate a mini-function which updates PC (for LLVM) and jumps to ppu_far_jump to handle redirections template ppu_intrp_func_t gen_jump(u32 pc) { @@ -1019,7 +1056,7 @@ void ppu_thread::dump_regs(std::string& ret) const if (const_value != reg) { - // Expectation of pretictable code path has not been met (such as a branch directly to the instruction) + // Expectation of predictable code path has not been met (such as a branch directly to the instruction) is_const = false; } @@ -3478,14 +3515,25 @@ bool ppu_initialize(const ppu_module& info, bool check_only) } } - if (jit) + if (g_fxo->is_init()) { - const auto far_jump = ppu_get_far_jump(func.addr) ? g_fxo->get().gen_jump(func.addr) : nullptr; + auto targets = g_fxo->get().get_targets(func.addr, func.size); - if (far_jump) + for (auto [source, target] : targets) + { + auto far_jump = ensure(g_fxo->get().gen_jump(source)); + + if (source == func.addr && jit) + { + jit->update_global_mapping(fmt::format("__0x%x", func.addr - reloc), reinterpret_cast(far_jump)); + } + + ppu_register_function_at(source, 4, far_jump); + } + + if (!targets.empty()) { // Replace the function with ppu_far_jump - jit->update_global_mapping(fmt::format("__0x%x", func.addr - reloc), reinterpret_cast(far_jump)); fpos++; continue; }