From dc3d38c25501e84723852c52596d9d869ff9749c Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Sat, 8 Apr 2017 23:58:00 +0300 Subject: [PATCH] PPU Analyser: TOC detection Improved TOC detection logic Added "PPU Debug" option --- rpcs3/Emu/Cell/PPUAnalyser.cpp | 156 +++++++++++++++++++++++++-------- rpcs3/Emu/Cell/PPUAnalyser.h | 1 + rpcs3/Emu/Cell/PPUModule.cpp | 1 + rpcs3/Emu/Cell/PPUThread.cpp | 51 ++++++++++- 4 files changed, 171 insertions(+), 38 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index 004f04865c..42c2713ff4 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -341,23 +341,32 @@ std::vector ppu_analyse(const std::vector>& se // Function analysis workload std::vector> func_queue; + // Known references (within segs, addr and value alignment = 4) + std::set addr_heap{entry}; + // Register new function - auto add_func = [&](u32 addr, u32 toc, u32 origin) -> ppu_function& + auto add_func = [&](u32 addr, u32 toc, u32 caller) -> ppu_function& { ppu_function& func = funcs[addr]; + if (caller) + { + // Register caller + func.callers.emplace(caller); + } + if (func.addr) { - // Update TOC (TODO: this doesn't work well, must update TOC recursively) - if (func.toc == 0 || toc == -1) + if (toc && func.toc && func.toc != -1 && func.toc != toc) { - func.toc = toc; - } - else if (toc && func.toc != -1 && func.toc != toc) - { - //LOG_WARNING(PPU, "Function 0x%x: TOC mismatch (0x%x vs 0x%x)", addr, toc, func.toc); func.toc = -1; } + else if (toc && func.toc == 0) + { + // Must then update TOC recursively + func.toc = toc; + func_queue.emplace_back(func); + } return func; } @@ -365,7 +374,7 @@ std::vector ppu_analyse(const std::vector>& se func_queue.emplace_back(func); func.addr = addr; func.toc = toc; - LOG_TRACE(PPU, "Function 0x%x added (toc=0x%x, origin=0x%x)", addr, toc, origin); + LOG_TRACE(PPU, "Function 0x%x added (toc=0x%x)", addr, toc); return func; }; @@ -386,7 +395,7 @@ std::vector ppu_analyse(const std::vector>& se { // New function LOG_TRACE(PPU, "OPD*: [0x%x] 0x%x (TOC=0x%x)", ptr, ptr[0], ptr[1]); - add_func(*ptr, toc, ptr.addr()); + add_func(*ptr, addr_heap.count(ptr.addr()) ? toc : 0, 0); ptr++; } } @@ -407,6 +416,29 @@ std::vector ppu_analyse(const std::vector>& se return end; }; + // Find references indiscriminately + for (const auto& seg : segs) + { + for (vm::cptr ptr = vm::cast(seg.first); ptr.addr() < seg.first + seg.second; ptr++) + { + const u32 value = *ptr; + + if (value % 4) + { + continue; + } + + for (const auto& _seg : segs) + { + if (value >= _seg.first && value < _seg.first + _seg.second) + { + addr_heap.emplace(value); + break; + } + } + } + } + // Find OPD section for (const auto& sec : secs) { @@ -462,7 +494,7 @@ std::vector ppu_analyse(const std::vector>& se LOG_TRACE(PPU, "OPD: [0x%x] 0x%x (TOC=0x%x)", ptr, addr, toc); TOCs.emplace(toc); - auto& func = add_func(addr, toc, ptr.addr()); + auto& func = add_func(addr, addr_heap.count(ptr.addr()) ? toc : 0, 0); func.attr += ppu_attr::known_addr; } } @@ -479,6 +511,15 @@ std::vector ppu_analyse(const std::vector>& se add_toc(lib_toc); } + // Clean TOCs + for (auto&& pair : funcs) + { + if (pair.second.toc == -1) + { + pair.second.toc = 0; + } + } + // Find .eh_frame section for (const auto& sec : secs) { @@ -581,7 +622,7 @@ std::vector ppu_analyse(const std::vector>& se continue; } - //auto& func = add_func(addr, 0, ptr.addr()); + //auto& func = add_func(addr, 0, 0); //func.attr += ppu_attr::known_addr; //func.attr += ppu_attr::known_size; //func.size = size; @@ -594,6 +635,30 @@ std::vector ppu_analyse(const std::vector>& se { ppu_function& func = func_queue[i]; + // Fixup TOCs + if (func.toc && func.toc != -1) + { + for (u32 addr : func.callers) + { + ppu_function& caller = funcs[addr]; + + if (!caller.toc) + { + add_func(addr, func.toc - caller.trampoline, 0); + } + } + + for (u32 addr : func.calls) + { + ppu_function& callee = funcs[addr]; + + if (!callee.toc) + { + add_func(addr, func.toc + func.trampoline, 0); + } + } + } + if (func.blocks.empty()) { // Special function analysis @@ -618,7 +683,7 @@ std::vector ppu_analyse(const std::vector>& se if (target >= start && target < end) { - auto& new_func = add_func(target, 0, func.addr); + auto& new_func = add_func(target, func.toc, func.addr); if (new_func.blocks.empty()) { @@ -630,7 +695,7 @@ std::vector ppu_analyse(const std::vector>& se func.blocks.emplace(func.addr, func.size); func.attr += new_func.attr & ppu_attr::no_return; func.calls.emplace(target); - func.trampoline = target; + func.trampoline = 0; continue; } } @@ -646,7 +711,7 @@ std::vector ppu_analyse(const std::vector>& se if (target >= start && target < end) { - auto& new_func = add_func(target, 0, func.addr); + auto& new_func = add_func(target, func.toc, func.addr); if (new_func.blocks.empty()) { @@ -658,7 +723,7 @@ std::vector ppu_analyse(const std::vector>& se func.blocks.emplace(func.addr, func.size); func.attr += new_func.attr & ppu_attr::no_return; func.calls.emplace(target); - func.trampoline = target; + func.trampoline = 0; continue; } } @@ -670,15 +735,31 @@ std::vector ppu_analyse(const std::vector>& se (ptr[3] & 0xfc000001) == B({}, {})) { // Trampoline with TOC - const u32 new_toc = func.toc && func.toc != -1 ? func.toc + (ptr[1] << 16) + s16(ptr[2]) : 0; + const u32 toc_add = (ptr[1] << 16) + s16(ptr[2]); const u32 target = (ptr[3] & 0x2 ? 0 : (ptr + 3).addr()) + ppu_opcode_t{ptr[3]}.bt24; if (target >= start && target < end) { - add_toc(new_toc); - auto& new_func = add_func(target, 0, func.addr); + if (func.toc && func.toc != -1 && new_func.toc == 0) + { + const u32 toc = func.toc + toc_add; + add_toc(toc); + add_func(new_func.addr, toc, 0); + } + else if (new_func.toc && new_func.toc != -1 && func.toc == 0) + { + const u32 toc = new_func.toc - toc_add; + add_toc(toc); + add_func(func.addr, toc, 0); + } + else if (new_func.toc - func.toc != toc_add) + { + //func.toc = -1; + //new_func.toc = -1; + } + if (new_func.blocks.empty()) { func_queue.emplace_back(func); @@ -689,7 +770,7 @@ std::vector ppu_analyse(const std::vector>& se func.blocks.emplace(func.addr, func.size); func.attr += new_func.attr & ppu_attr::no_return; func.calls.emplace(target); - func.trampoline = target; + func.trampoline = toc_add; continue; } } @@ -813,19 +894,8 @@ std::vector ppu_analyse(const std::vector>& se // Get limit const u32 func_end2 = _next == funcs.end() ? func_end : std::min(_next->first, func_end); - // Find more block entries - for (const auto& seg : segs) - { - for (vm::cptr ptr = vm::cast(seg.first); ptr.addr() < seg.first + seg.second; ptr++) - { - const u32 value = *ptr; - - if (value % 4 == 0 && value >= func.addr && value < func_end2) - { - add_block(value); - } - } - } + // Set more block entries + std::for_each(addr_heap.lower_bound(func.addr), addr_heap.lower_bound(func_end2), add_block); } const bool was_empty = block_queue.empty(); @@ -857,7 +927,7 @@ std::vector ppu_analyse(const std::vector>& se } const bool is_call = op.lk && target != iaddr; - const auto pfunc = is_call ? &add_func(target, 0, func.addr) : nullptr; + const auto pfunc = is_call ? &add_func(target, 0, 0) : nullptr; if (pfunc && pfunc->blocks.empty()) { @@ -879,7 +949,7 @@ std::vector ppu_analyse(const std::vector>& se else if (is_call || target < func.addr || target >= func_end) { // Add function call (including obvious tail call) - add_func(target, 0, func.addr); + add_func(target, 0, 0); } else { @@ -1025,7 +1095,7 @@ std::vector ppu_analyse(const std::vector>& se if (target < func.addr || target >= func.addr + func.size) { func.calls.emplace(target); - add_func(target, 0, func.addr); + add_func(target, func.toc ? func.toc + func.trampoline : 0, func.addr); } } } @@ -1137,6 +1207,20 @@ std::vector ppu_analyse(const std::vector>& se } } } + + // Fill TOCs for trivial case + if (TOCs.size() == 1) + { + lib_toc = *TOCs.begin(); + + for (auto&& pair : funcs) + { + if (pair.second.toc == 0) + { + pair.second.toc = lib_toc; + } + } + } // Convert map to vector (destructive) std::vector result; diff --git a/rpcs3/Emu/Cell/PPUAnalyser.h b/rpcs3/Emu/Cell/PPUAnalyser.h index 8744e4f20b..aa4cce9bb0 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.h +++ b/rpcs3/Emu/Cell/PPUAnalyser.h @@ -33,6 +33,7 @@ struct ppu_function std::map blocks; // Basic blocks: addr -> size std::set calls; // Set of called functions + std::set callers; }; // PPU Module Information diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 7ee42966a3..80352c32a9 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -346,6 +346,7 @@ static void ppu_initialize_modules() // Register the HLE function directly ppu_register_function_at(addr + 0, 4, hle_funcs[index]); + ppu_register_function_at(addr + 4, 4, nullptr); } // Set memory protection to read-only diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index f7840808a0..a0ac9ed4a9 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -96,6 +96,8 @@ cfg::map_entry g_cfg_ppu_decoder(cfg::root.core, "PPU Decoder" { "Recompiler (LLVM)", ppu_decoder_type::llvm }, }); +cfg::bool_entry g_cfg_ppu_debug(cfg::root.core, "PPU Debug"); + cfg::bool_entry g_cfg_llvm_logs(cfg::root.core, "Save LLVM logs"); cfg::string_entry g_cfg_llvm_cpu(cfg::root.core, "Use LLVM CPU"); @@ -133,6 +135,38 @@ static bool ppu_fallback(ppu_thread& ppu, ppu_opcode_t op) } ppu_ref(ppu.cia) = ppu_cache(ppu.cia); + + if (g_cfg_ppu_debug) + { + LOG_ERROR(PPU, "Unregistered instruction: 0x%08x", op.opcode); + } + + return false; +} + +static std::unordered_map s_ppu_toc; + +static bool ppu_check_toc(ppu_thread& ppu, ppu_opcode_t op) +{ + // Compare TOC with expected value + const auto found = s_ppu_toc.find(ppu.cia); + + if (ppu.gpr[2] != found->second) + { + LOG_ERROR(PPU, "Unexpected TOC (0x%x, expected 0x%x)", ppu.gpr[2], found->second); + + if (!ppu.state.test_and_set(cpu_flag::dbg_pause) && ppu.check_state()) + { + return false; + } + } + + // Fallback to the interpreter function + if (reinterpret_cast(std::uintptr_t{ppu_cache(ppu.cia)})(ppu, op)) + { + ppu.cia += 4; + } + return false; } @@ -168,7 +202,11 @@ extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr) if (!size) { - LOG_ERROR(PPU, "ppu_register_function_at(0x%x): empty range", addr); + if (g_cfg_ppu_debug) + { + LOG_ERROR(PPU, "ppu_register_function_at(0x%x): empty range", addr); + } + return; } @@ -875,7 +913,16 @@ extern void ppu_initialize(const ppu_module& info) { for (const auto& func : info.funcs) { - ppu_register_function_at(func.addr, func.size, nullptr); + for (auto& block : func.blocks) + { + ppu_register_function_at(block.first, block.second, nullptr); + } + + if (g_cfg_ppu_debug && func.size && func.toc != -1) + { + s_ppu_toc.emplace(func.addr, func.toc); + ppu_ref(func.addr) = ::narrow(reinterpret_cast(&ppu_check_toc)); + } } return;