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.
This commit is contained in:
Walter
2026-06-16 17:51:14 +10:00
committed by Elad
parent 4d38e69fd9
commit e429f8cf05
2 changed files with 27 additions and 6 deletions
+4 -6
View File
@@ -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;
}
+23
View File
@@ -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<f32[4]> flt;
const auto a = get_vr(op.ra);
flt.value = m_ir->CreateSIToFP(a.value, get_type<f32[4]>()); // only correct with round-towards-zero!
const auto exp = bitcast<u32[4]>(flt) >> 23;
// "Negative" values cause saturation due to float's sign bit
const auto offset = select(a == 0, splat<u32[4]>(32), splat<u32[4]>(exp_bias + 31));
const auto lzcnt = sub_sat(bitcast<u16[8]>(offset), bitcast<u16[8]>(exp));
set_vr(op.rt, bitcast<u32[4]>(lzcnt));
#endif
}
void XSWD(spu_opcode_t op)