mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-07-10 01:54:46 -06:00
[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.
This commit is contained in:
parent
c0598f61e7
commit
33123bff04
@ -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<u64>(spu_frsqest_fraction_lut);
|
||||
const u64 exponent_lut_addr = reinterpret_cast<u64>(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);
|
||||
|
||||
@ -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<u32>(), 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<u32>(), 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<u32>(), 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<u32[4]>(value<f32[4]>(ci->getOperand(0)));
|
||||
|
||||
// (exponent==0)? 0xFF : 190 - (exponent + 1) / 2
|
||||
const auto a_exponent = a & splat<u32[4]>(0xFF << 23);
|
||||
const auto h_exponent = (a_exponent + (a_exponent & splat<u32[4]>(1 << 23))) >> splat<u32[4]>(1);
|
||||
const auto r_exponent = splat<u32[4]>(190 << 23) - h_exponent;
|
||||
const auto final_exponent = select(a_exponent == 0, splat<u32[4]>(0xFF << 23), r_exponent);
|
||||
|
||||
const auto a_fraction = (a >> splat<u32[4]>(18)) & splat<u32[4]>(0x3F);
|
||||
const auto a_exponent = (a >> splat<u32[4]>(23)) & splat<u32[4]>(0xFF);
|
||||
value_t<u32[4]> final_result = eval(splat<u32[4]>(0));
|
||||
value_t<u32[4]> final_fraction = eval(splat<u32[4]>(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<u32> r_fraction = load_const<u32>(m_spu_frsqest_fraction_lut, eval_fraction);
|
||||
value_t<u32> r_exponent = load_const<u32>(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<f32[4]>(final_result);
|
||||
return bitcast<f32[4]>(final_fraction | final_exponent);
|
||||
});
|
||||
|
||||
set_vr(op.rt, frsqest(get_vr<f32[4]>(op.ra)));
|
||||
@ -8575,21 +8574,25 @@ public:
|
||||
register_intrinsic("spu_rsqrte", [&](llvm::CallInst* ci)
|
||||
{
|
||||
const auto a = bitcast<u32[4]>(value<f32[4]>(ci->getOperand(0)));
|
||||
|
||||
// (exponent==0)? 0xFF : 190 - (exponent + 1) / 2
|
||||
const auto a_exponent = a & splat<u32[4]>(0xFF << 23);
|
||||
const auto h_exponent = (a_exponent + (a_exponent & splat<u32[4]>(1 << 23))) >> splat<u32[4]>(1);
|
||||
const auto r_exponent = splat<u32[4]>(190 << 23) - h_exponent;
|
||||
const auto final_exponent = select(a_exponent == 0, splat<u32[4]>(0xFF << 23), r_exponent);
|
||||
|
||||
const auto a_fraction = (a >> splat<u32[4]>(18)) & splat<u32[4]>(0x3F);
|
||||
const auto a_exponent = (a >> splat<u32[4]>(23)) & splat<u32[4]>(0xFF);
|
||||
value_t<u32[4]> b = eval(splat<u32[4]>(0));
|
||||
value_t<u32[4]> final_fraction = eval(splat<u32[4]>(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<u32> r_fraction = load_const<u32>(m_spu_frsqest_fraction_lut, eval_fraction);
|
||||
value_t<u32> r_exponent = load_const<u32>(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?
|
||||
|
||||
Loading…
Reference in New Issue
Block a user