diff --git a/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp b/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp index 8bc19d7f7e..f7e84f82f3 100644 --- a/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp @@ -1024,7 +1024,7 @@ void spu_cache::initialize(bool build_existing_cache) } } - if (new_entry != umax && !spu_thread::is_exec_code(new_entry, { reinterpret_cast(ls.data()), SPU_LS_SIZE })) + if (new_entry != umax && !spu_thread::is_exec_code(new_entry, { reinterpret_cast(ls.data()), SPU_LS_SIZE }, 0, true)) { new_entry = umax; } @@ -1033,7 +1033,7 @@ void spu_cache::initialize(bool build_existing_cache) { new_entry = start_new; - while (new_entry < next_func && (ls[start_new / 4] < 0x3fffc || !spu_thread::is_exec_code(new_entry, { reinterpret_cast(ls.data()), SPU_LS_SIZE }))) + while (new_entry < next_func && (ls[start_new / 4] < 0x3fffc || !spu_thread::is_exec_code(new_entry, { reinterpret_cast(ls.data()), SPU_LS_SIZE }, 0, true))) { new_entry += 4; } @@ -2281,13 +2281,13 @@ std::vector spu_thread::discover_functions(u32 base_addr, std::span addrs; diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index ae8206ad1d..d366000072 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -4119,9 +4119,11 @@ bool spu_thread::check_mfc_interrupts(u32 next_pc) return false; } -bool spu_thread::is_exec_code(u32 addr, std::span ls_ptr, u32 base_addr) +bool spu_thread::is_exec_code(u32 addr, std::span ls_ptr, u32 base_addr, bool avoid_dead_code) { - for (u32 i = 0; i < 30; i++) + bool had_conditional = false; + + for (u32 i = 0; i < 40; i++) { if (addr & ~0x3FFFC) { @@ -4179,6 +4181,51 @@ bool spu_thread::is_exec_code(u32 addr, std::span ls_ptr, u32 base_add break; } } + else + { + switch (type) + { + case spu_itype::BR: + case spu_itype::BRNZ: + case spu_itype::BRZ: + case spu_itype::BRHNZ: + case spu_itype::BRHZ: + case spu_itype::BRSL: + { + const s32 rel = bf_t::extract(static_cast(u32{op.i16} << 2)); + + if (rel == 0 && !had_conditional && avoid_dead_code) + { + // Infinite loop 100%, detect that as invalid code + return false; + } + + // Detect "invalid" relative branches + // Branch offsets that, although are the only way to get X code address using relative address + // Rely on overflow/underflow of SPU memory bounds + // Thus they would behave differently if SPU LS memory size was to increase (evolving the CELL architecture was the original plan) + // Making them highly unlikely to be valid code + + if (rel < 0) + { + if (addr < 0u - rel) + { + return false; + } + } + else if (SPU_LS_SIZE - addr <= rel + 0u) + { + return false; + } + + break; + } + default: + { + break; + } + } + } for (usz res_i = 1; res_i < results.size(); res_i++) { @@ -4203,6 +4250,8 @@ bool spu_thread::is_exec_code(u32 addr, std::span ls_ptr, u32 base_add { return false; } + + had_conditional = true; } addr = spu_branch_target(results[0]); diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 0eb8cae5d9..8c22d4471c 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -835,7 +835,7 @@ public: void set_events(u32 bits); void set_interrupt_status(bool enable); bool check_mfc_interrupts(u32 next_pc); - static bool is_exec_code(u32 addr, std::span ls_ptr, u32 base_addr = 0); // Only a hint, do not rely on it other than debugging purposes + static bool is_exec_code(u32 addr, std::span ls_ptr, u32 base_addr = 0, bool avoid_dead_code = false); // Only a hint, do not rely on it other than debugging purposes static std::vector discover_functions(u32 base_addr, std::span ls, bool is_known_addr, u32 /*entry*/); u32 get_ch_count(u32 ch); s64 get_ch_value(u32 ch);