diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index 9e6ae11906..c8390b6075 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -696,7 +696,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b const vm::cptr seg_end = vm::cast(seg.addr + seg.size - 4); auto ptr = get_ptr(_ptr); - for (vm::cptr _ptr = vm::cast(seg.addr); _ptr <= seg_end; advance(_ptr, ptr, 1)) + for (; _ptr <= seg_end; advance(_ptr, ptr, 1)) { const u32 value = *ptr; @@ -929,6 +929,38 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b } } + if (func_queue.empty() && segs[0].size >= 4u) + { + // Fallback, identify functions using callers (no jumptable detection, tail calls etc) + ppu_log.warning("Looking for PPU functions using callers. ('%s')", name); + + vm::cptr _ptr = vm::cast(start); + const vm::cptr seg_end = vm::cast(end - 4); + + for (auto ptr = get_ptr(_ptr); _ptr <= seg_end; advance(_ptr, ptr, 1)) + { + const u32 iaddr = _ptr.addr(); + + const ppu_opcode_t op{*ptr}; + const ppu_itype::type type = s_ppu_itype.decode(op.opcode); + + if (type == ppu_itype::B && op.lk && !op.aa) + { + const u32 target = iaddr + op.bt24; + + if (target >= start && target < end && target != iaddr && target != iaddr + 4) + { + // TODO: Check full executability + if (s_ppu_itype.decode(get_ref(target)) != ppu_itype::UNK) + { + ppu_log.trace("Enqueued PPU function 0x%x using a caller at 0x%x", target, iaddr); + add_func(target, 0, 0); + } + } + } + } + } + // Main loop (func_queue may grow) for (usz i = 0; i < func_queue.size(); i++) { @@ -1631,7 +1663,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b } } - ppu_log.notice("Function analysis: %zu functions (%zu enqueued)", fmap.size(), func_queue.size()); + (fmap.empty() ? ppu_log.error : ppu_log.notice)("Function analysis: %zu functions (%zu enqueued)", fmap.size(), func_queue.size()); // Decompose functions to basic blocks if (!entry && !sec_end)