From e429f8cf05926852ed63949cad652bf1de5869a4 Mon Sep 17 00:00:00 2001 From: Walter Date: Tue, 16 Jun 2026 17:51:14 +1000 Subject: [PATCH] SPU LLVM + zero with saturation subtract - Added a LLVM SPU recompiler implementation as LLVM can't take advantage of the rounding mode. - Further optimized the method by replacing MSB check with saturation subtraction. Negative values are saturated to zero due to the float's sign bit. --- rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp | 10 ++++------ rpcs3/Emu/Cell/SPULLVMRecompiler.cpp | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp index 14cd7314dc..89694affd4 100644 --- a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp @@ -3354,8 +3354,8 @@ void spu_recompiler::CLZ(spu_opcode_t op) } // Use signed conversion to float, as exponent is ilog2 - // Fixup "negative" cases by overwriting with zero - const u32 exp_bias = 127; + // "Negative" values are zeroed due to saturation subtract + constexpr u32 exp_bias = 127; const XmmLink& vf = XmmAlloc(); const XmmLink& v1 = XmmAlloc(); @@ -3365,10 +3365,8 @@ void spu_recompiler::CLZ(spu_opcode_t op) c->pcmpeqd(v1, va); c->pand(v1, XmmConst(v128::from32p(32 ^ (31 + exp_bias)))); c->pxor(v1, XmmConst(v128::from32p(31 + exp_bias))); - c->psubd(v1, vf); // (x==0)? 32 : 31 - (exponent - exp_bias) - c->psrad(va, 31); - c->pandn(va, v1); - c->movdqa(SPU_OFF_128(gpr, op.rt), va); + c->psubusw(v1, vf); // (x==0)? 32 : 31 - (exponent - exp_bias) + c->movdqa(SPU_OFF_128(gpr, op.rt), v1); return; } diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 40e473d365..80d1100754 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -6329,7 +6329,30 @@ public: void CLZ(spu_opcode_t op) { +#ifdef ARCH_ARM64 set_vr(op.rt, ctlz(get_vr(op.ra))); +#else + if (m_use_avx512) + { + set_vr(op.rt, ctlz(get_vr(op.ra))); + return; + } + + // Implement manually since LLVM can't take advantage of round-towards-zero. + // Helpful as when converting to a float the exponent is always floor(ilog2) + + constexpr u32 exp_bias = 127; + value_t flt; + + const auto a = get_vr(op.ra); + flt.value = m_ir->CreateSIToFP(a.value, get_type()); // only correct with round-towards-zero! + const auto exp = bitcast(flt) >> 23; + + // "Negative" values cause saturation due to float's sign bit + const auto offset = select(a == 0, splat(32), splat(exp_bias + 31)); + const auto lzcnt = sub_sat(bitcast(offset), bitcast(exp)); + set_vr(op.rt, bitcast(lzcnt)); +#endif } void XSWD(spu_opcode_t op)