From 33123bff044396473cedd41b6110231a5fda9da5 Mon Sep 17 00:00:00 2001 From: Walter <90877047+WalterKruger@users.noreply.github.com> Date: Sat, 20 Jun 2026 01:02:20 +1000 Subject: [PATCH] [SPU] Calculate `FRSQEST` exponent using arithmetic (#18905) The `FRSQEST` instruction uses a lookup-table to calculate both the exponent and mantissa. The exponent follows a simple linear pattern and thus can be calculated using integer arithmetic: `(exponent==0)? 0xFF : 190 - (exponent + 1) / 2`. This patch replaces the LUT with this method, vectorizing its calculating and saving on 1kB. The mantissa still relies on a LUT, which bottlenecks this instruction's emulation despite the improvement. --- rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp | 25 +++++++++-------- rpcs3/Emu/Cell/SPULLVMRecompiler.cpp | 37 ++++++++++++++------------ 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp index 89694affd4..2addc37017 100644 --- a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp @@ -2799,32 +2799,35 @@ void spu_recompiler::FREST(spu_opcode_t op) void spu_recompiler::FRSQEST(spu_opcode_t op) { const XmmLink& va = XmmGet(op.ra, XmmType::Float); + const XmmLink& vz = XmmAlloc(); + const XmmLink& v1 = XmmAlloc(); const XmmLink& v_fraction = XmmAlloc(); const XmmLink& v_exponent = XmmAlloc(); c->movdqa(v_fraction, va); c->movdqa(v_exponent, va); + // (exponent==0)? 0xFF : 190 - (exponent + 1) / 2 + c->paddd(v_exponent, v_exponent); + c->pxor(vz, vz); + c->pavgb(v_exponent, vz); + c->movdqa(v1, XmmConst(v128::from32p(190 << 24))); + c->psubb(v1, v_exponent); + c->pcmpeqb(v_exponent, vz); + c->por(v_exponent, v1); + c->psrld(v_exponent, 1); + c->pand(v_exponent, XmmConst(v128::from32p(0xFF << 23))); + c->psrld(v_fraction, 18); - c->psrld(v_exponent, 23); - - c->andps(v_fraction, XmmConst(v128::from32p(0x3F))); - c->andps(v_exponent, XmmConst(v128::from32p(0xFF))); - + c->pand(v_fraction, XmmConst(v128::from32p(0x3F))); const u64 fraction_lut_addr = reinterpret_cast(spu_frsqest_fraction_lut); - const u64 exponent_lut_addr = reinterpret_cast(spu_frsqest_exponent_lut); c->movabs(*arg0, fraction_lut_addr); - c->movabs(*arg1, exponent_lut_addr); for (u32 index = 0; index < 4; index++) { c->pextrd(*qw0, v_fraction, index); c->mov(*qw1, asmjit::x86::dword_ptr(*arg0, *qw0, 2)); c->pinsrd(v_fraction, *qw1, index); - - c->pextrd(*qw0, v_exponent, index); - c->mov(*qw1, asmjit::x86::dword_ptr(*arg1, *qw0, 2)); - c->pinsrd(v_exponent, *qw1, index); } c->orps(v_fraction, v_exponent); diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 80d1100754..5f5c90f6f4 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -128,7 +128,6 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator // Global LUTs llvm::GlobalVariable* m_spu_frest_fraction_lut{}; llvm::GlobalVariable* m_spu_frsqest_fraction_lut{}; - llvm::GlobalVariable* m_spu_frsqest_exponent_lut{}; // Helpers (interpreter) llvm::GlobalVariable* m_scale_float_to{}; @@ -1623,7 +1622,6 @@ public: // LUTs for some instructions m_spu_frest_fraction_lut = new llvm::GlobalVariable(*m_module, llvm::ArrayType::get(GetType(), 32), true, llvm::GlobalValue::PrivateLinkage, llvm::ConstantDataArray::get(m_context, spu_frest_fraction_lut)); m_spu_frsqest_fraction_lut = new llvm::GlobalVariable(*m_module, llvm::ArrayType::get(GetType(), 64), true, llvm::GlobalValue::PrivateLinkage, llvm::ConstantDataArray::get(m_context, spu_frsqest_fraction_lut)); - m_spu_frsqest_exponent_lut = new llvm::GlobalVariable(*m_module, llvm::ArrayType::get(GetType(), 256), true, llvm::GlobalValue::PrivateLinkage, llvm::ConstantDataArray::get(m_context, spu_frsqest_exponent_lut)); } virtual spu_function_t compile(spu_program&& _func) override @@ -7529,22 +7527,23 @@ public: { const auto a = bitcast(value(ci->getOperand(0))); + // (exponent==0)? 0xFF : 190 - (exponent + 1) / 2 + const auto a_exponent = a & splat(0xFF << 23); + const auto h_exponent = (a_exponent + (a_exponent & splat(1 << 23))) >> splat(1); + const auto r_exponent = splat(190 << 23) - h_exponent; + const auto final_exponent = select(a_exponent == 0, splat(0xFF << 23), r_exponent); + const auto a_fraction = (a >> splat(18)) & splat(0x3F); - const auto a_exponent = (a >> splat(23)) & splat(0xFF); - value_t final_result = eval(splat(0)); + value_t final_fraction = eval(splat(0)); for (u32 i = 0; i < 4; i++) { const auto eval_fraction = eval(extract(a_fraction, i)); - const auto eval_exponent = eval(extract(a_exponent, i)); - value_t r_fraction = load_const(m_spu_frsqest_fraction_lut, eval_fraction); - value_t r_exponent = load_const(m_spu_frsqest_exponent_lut, eval_exponent); - - final_result = eval(insert(final_result, i, eval(r_fraction | r_exponent))); + final_fraction = eval(insert(final_fraction, i, r_fraction)); } - return bitcast(final_result); + return bitcast(final_fraction | final_exponent); }); set_vr(op.rt, frsqest(get_vr(op.ra))); @@ -8575,21 +8574,25 @@ public: register_intrinsic("spu_rsqrte", [&](llvm::CallInst* ci) { const auto a = bitcast(value(ci->getOperand(0))); + + // (exponent==0)? 0xFF : 190 - (exponent + 1) / 2 + const auto a_exponent = a & splat(0xFF << 23); + const auto h_exponent = (a_exponent + (a_exponent & splat(1 << 23))) >> splat(1); + const auto r_exponent = splat(190 << 23) - h_exponent; + const auto final_exponent = select(a_exponent == 0, splat(0xFF << 23), r_exponent); + const auto a_fraction = (a >> splat(18)) & splat(0x3F); - const auto a_exponent = (a >> splat(23)) & splat(0xFF); - value_t b = eval(splat(0)); + value_t final_fraction = eval(splat(0)); for (u32 i = 0; i < 4; i++) { const auto eval_fraction = eval(extract(a_fraction, i)); - const auto eval_exponent = eval(extract(a_exponent, i)); - value_t r_fraction = load_const(m_spu_frsqest_fraction_lut, eval_fraction); - value_t r_exponent = load_const(m_spu_frsqest_exponent_lut, eval_exponent); - - b = eval(insert(b, i, eval(r_fraction | r_exponent))); + final_fraction = eval(insert(final_fraction, i, r_fraction)); } + const auto b = eval(final_fraction | final_exponent); + const auto base = (b & 0x007ffc00u) << 9; // Base fraction const auto ymul = (b & 0x3ff) * (a & 0x7ffff); // Step fraction * Y fraction (fixed point at 2^-32) const auto comparison = (ymul > base); // Should exponent be adjusted?