mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-21 20:15:27 +00:00
PPU Analyser: TOC detection
Improved TOC detection logic Added "PPU Debug" option
This commit is contained in:
parent
fb9b09d004
commit
dc3d38c255
4 changed files with 171 additions and 38 deletions
|
@ -341,23 +341,32 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
|
|||
// Function analysis workload
|
||||
std::vector<std::reference_wrapper<ppu_function>> func_queue;
|
||||
|
||||
// Known references (within segs, addr and value alignment = 4)
|
||||
std::set<u32> 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
|
|||
return end;
|
||||
};
|
||||
|
||||
// Find references indiscriminately
|
||||
for (const auto& seg : segs)
|
||||
{
|
||||
for (vm::cptr<u32> 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
|
|||
// Get limit
|
||||
const u32 func_end2 = _next == funcs.end() ? func_end : std::min<u32>(_next->first, func_end);
|
||||
|
||||
// Find more block entries
|
||||
for (const auto& seg : segs)
|
||||
{
|
||||
for (vm::cptr<u32> 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& 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_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& 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<ppu_function> result;
|
||||
|
|
|
@ -33,6 +33,7 @@ struct ppu_function
|
|||
|
||||
std::map<u32, u32> blocks; // Basic blocks: addr -> size
|
||||
std::set<u32> calls; // Set of called functions
|
||||
std::set<u32> callers;
|
||||
};
|
||||
|
||||
// PPU Module Information
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -96,6 +96,8 @@ cfg::map_entry<ppu_decoder_type> 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<u32, u32> 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<decltype(&ppu_interpreter::UNK)>(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<u32>(reinterpret_cast<std::uintptr_t>(&ppu_check_toc));
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
Loading…
Add table
Reference in a new issue