From acfe22e5bc7cca305bf4a1488b49c833ea6e1f6b Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Fri, 4 May 2018 23:51:35 +0300 Subject: [PATCH] SPU: improve jumptable detection --- rpcs3/Emu/Cell/SPURecompiler.cpp | 56 ++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 25aaab0b88..2d0e91b3c3 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -198,7 +198,7 @@ std::vector spu_recompiler_base::block(SPUThread& spu, u32 lsa, std::bitset case spu_itype::STOP: case spu_itype::STOPD: { - if (data == 0 || data == 0x80) + if (data == 0 || data == 3) { // Stop before null data blocks[pos / 4] = true; @@ -267,12 +267,14 @@ std::vector spu_recompiler_base::block(SPUThread& spu, u32 lsa, std::bitset std::basic_string jt_rel; const u32 start = pos + 4; const u32 limit = 0x40000; + u64 dabs = 0; + u64 drel = 0; for (u32 i = start; i < limit; i += 4) { const u32 target = spu._ref(i); - if (target % 4) + if (target == 0 || target % 4) { // Address cannot be misaligned: abort break; @@ -297,22 +299,62 @@ std::vector spu_recompiler_base::block(SPUThread& spu, u32 lsa, std::bitset } } - // Add detected jump table blocks (TODO: avoid adding both) + // Choose position after the jt as an anchor and compute the average distance + for (u32 target : jt_abs) + { + dabs += std::abs(static_cast(target - start - jt_abs.size() * 4)); + } + + for (u32 target : jt_rel) + { + drel += std::abs(static_cast(target - start - jt_rel.size() * 4)); + } + + // Add detected jump table blocks if (jt_abs.size() >= 3 || jt_rel.size() >= 3) { + if (jt_abs.size() == jt_rel.size()) + { + if (dabs < drel) + { + jt_rel.clear(); + } + + if (dabs > drel) + { + jt_abs.clear(); + } + } + if (jt_abs.size() >= jt_rel.size()) { - for (u32 target : jt_abs) + const u32 new_size = (start - lsa) / 4 + 1 + jt_abs.size(); + + if (result.size() < new_size) { - add_block(target); + result.resize(new_size); + } + + for (u32 i = 0; i < jt_abs.size(); i++) + { + add_block(jt_abs[i]); + result[(start - lsa) / 4 + 1 + i] = se_storage::swap(jt_abs[i]); } } if (jt_rel.size() >= jt_abs.size()) { - for (u32 target : jt_rel) + const u32 new_size = (start - lsa) / 4 + 1 + jt_rel.size(); + + if (result.size() < new_size) { - add_block(target); + result.resize(new_size); + } + + for (u32 i = 0; i < jt_rel.size(); i++) + { + add_block(jt_rel[i]); + result[(start - lsa) / 4 + 1 + i] = se_storage::swap(jt_rel[i] - start); } } }