From 09a39fac5dccf7ee68f6a24e2578740af498789b Mon Sep 17 00:00:00 2001 From: Elad <18193363+elad335@users.noreply.github.com> Date: Tue, 30 Jun 2026 09:25:47 +0300 Subject: [PATCH] SPU LLVM: Discourage memory mirrors use on LQD/STQD --- rpcs3/Emu/Cell/SPULLVMRecompiler.cpp | 71 +++++++++++++++++----------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 73396eb08a..b0c2c9854d 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -9288,6 +9288,11 @@ public: return byteswap(data); } + static constexpr u64 make_negative_LS_offset(u32 original) + { + return original | ~u64{SPU_LS_SIZE - 1}; + } + void STQX(spu_opcode_t op) { const auto a = get_vr(op.ra); @@ -9297,17 +9302,20 @@ public: { if (auto [ok, data] = get_const_vector(pair.first.value, m_pos); ok) { - data._u32[3] %= SPU_LS_SIZE; + // "sign extend" offset addend + // Discourage the use of multiple addresses to refer to the same block of memory (due to memory mirrors use) + // Which may confuse LLVM's optimization + const u64 addend = (data._u32[3] >= SPU_LS_SIZE) ? make_negative_LS_offset(data._u32[3]) : data._u32[3]; if (const u32 remainder = data._u32[3] % 0x10; remainder == 0) { - value_t addr = eval(splat(data._u32[3]) + zext(extract(pair.second, 3) & 0x3fff0)); + value_t addr = eval(splat(addend) + zext(extract(pair.second, 3) & 0x3fff0)); make_store_ls(addr, get_vr(op.rt)); return; } else { - value_t addr = eval(splat(data._u32[3] - remainder) + zext((extract(pair.second, 3) + remainder) & 0x3fff0)); + value_t addr = eval(splat(addend - remainder) + zext((extract(pair.second, 3) + remainder) & 0x3fff0)); make_store_ls(addr, get_vr(op.rt)); return; } @@ -9327,17 +9335,20 @@ public: { if (auto [ok, data] = get_const_vector(pair.first.value, m_pos); ok) { - data._u32[3] %= SPU_LS_SIZE; + // "sign extend" offset addend + // Discourage the use of multiple addresses to refer to the same block of memory + // Which may confuse LLVM's optimization + const u64 addend = (data._u32[3] >= SPU_LS_SIZE) ? make_negative_LS_offset(data._u32[3]) : data._u32[3]; if (const u32 remainder = data._u32[3] % 0x10; remainder == 0) { - value_t addr = eval(splat(data._u32[3]) + zext(extract(pair.second, 3) & 0x3fff0)); + value_t addr = eval(splat(addend) + zext(extract(pair.second, 3) & 0x3fff0)); set_vr(op.rt, make_load_ls(addr)); return; } else { - value_t addr = eval(splat(data._u32[3] - remainder) + zext((extract(pair.second, 3) + remainder) & 0x3fff0)); + value_t addr = eval(splat(addend - remainder) + zext((extract(pair.second, 3) + remainder) & 0x3fff0)); set_vr(op.rt, make_load_ls(addr)); return; } @@ -9398,18 +9409,22 @@ public: if (auto [ok, x, y] = match_expr(a, match() + match()); ok) { - if (auto [ok1, data] = get_const_vector(x.value, m_pos + 1); ok1 && data._u32[3] % 16 == 0) + for (auto pair : std::initializer_list, llvm_match_t>>{{x, y}, {y, x}}) { - value_t addr = eval(zext(extract(y, 3) & 0x3fff0) + ((get_imm(op.si10) << 4) + splat(data._u32[3] & 0x3fff0))); - make_store_ls(addr, get_vr(op.rt)); - return; - } + if (auto [ok, data] = get_const_vector(pair.first.value, m_pos); ok) + { + // "sign extend" offset addend + // Discourage the use of multiple addresses to refer to the same block of memory + // Which may confuse LLVM's optimization + const u64 addend = (data._u32[3] >= SPU_LS_SIZE) ? make_negative_LS_offset(data._u32[3]) : data._u32[3]; - if (auto [ok2, data] = get_const_vector(y.value, m_pos + 2); ok2 && data._u32[3] % 16 == 0) - { - value_t addr = eval(zext(extract(x, 3) & 0x3fff0) + ((get_imm(op.si10) << 4) + splat(data._u32[3] & 0x3fff0))); - make_store_ls(addr, get_vr(op.rt)); - return; + if (const u32 remainder = data._u32[3] % 0x10; remainder == 0) + { + value_t addr = eval(zext(extract(pair.second, 3) & 0x3fff0) + ((get_imm(op.si10) << 4) + splat(addend))); + make_store_ls(addr, get_vr(op.rt)); + return; + } + } } } @@ -9421,20 +9436,22 @@ public: { const auto a = get_vr(op.ra); - if (auto [ok, x1, y1] = match_expr(a, match() + match()); ok) + if (auto [ok, x, y] = match_expr(a, match() + match()); ok) { - if (auto [ok1, data] = get_const_vector(x1.value, m_pos + 1); ok1 && data._u32[3] % 16 == 0) + for (auto pair : std::initializer_list, llvm_match_t>>{{x, y}, {y, x}}) { - value_t addr = eval(zext(extract(y1, 3) & 0x3fff0) + ((get_imm(op.si10) << 4) + splat(data._u32[3] & 0x3fff0))); - set_vr(op.rt, make_load_ls(addr)); - return; - } + if (auto [ok, data] = get_const_vector(pair.first.value, m_pos); ok) + { + // "sign extend" offset addend + const u64 addend = (data._u32[3] >= SPU_LS_SIZE) ? data._u32[3] | ~u64{SPU_LS_SIZE - 1} : data._u32[3]; - if (auto [ok2, data] = get_const_vector(y1.value, m_pos + 2); ok2 && data._u32[3] % 16 == 0) - { - value_t addr = eval(zext(extract(x1, 3) & 0x3fff0) + ((get_imm(op.si10) << 4) + splat(data._u32[3] & 0x3fff0))); - set_vr(op.rt, make_load_ls(addr)); - return; + if (const u32 remainder = data._u32[3] % 0x10; remainder == 0) + { + value_t addr = eval(zext(extract(pair.second, 3) & 0x3fff0) + ((get_imm(op.si10) << 4) + splat(addend))); + set_vr(op.rt, make_load_ls(addr)); + return; + } + } } }