mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
EE/FPU: narrow SQRT's root with a plain Fcvt
ToPS2FPU_Full's saturating and flushing arms are for results that leave the PS2's range, and a root does not, so both sat dead behind their compares. The body goes from 54 host instructions to 23, under the 26 the single-precision path spends.
This commit is contained in:
@@ -1034,8 +1034,8 @@ void recDIV_S_xmm(int info)
|
||||
|
||||
void recSQRT_S_xmm(int info)
|
||||
{
|
||||
// Round-to-nearest for the double Fsqrt + the ToPS2FPU narrowing, like
|
||||
// x86's roundmode_nearest swap (FPUDivFPCR is the nearest-mode FPCR).
|
||||
// Round-to-nearest for the double Fsqrt and the narrowing Fcvt, like x86's
|
||||
// roundmode_nearest swap (FPUDivFPCR is the nearest-mode FPCR).
|
||||
const bool swapFpcr = EmuConfig.Cpu.FPUFPCR.bitmask != EmuConfig.Cpu.FPUDivFPCR.bitmask;
|
||||
if (swapFpcr)
|
||||
emitLoadFPCRImm(EmuConfig.Cpu.FPUDivFPCR.bitmask);
|
||||
@@ -1056,7 +1056,13 @@ void recSQRT_S_xmm(int info)
|
||||
armAsm->Bind(&tPositive);
|
||||
|
||||
armAsm->Fsqrt(armDRegister(treg), armDRegister(treg));
|
||||
ToPS2FPU_Full(treg, false, treg, false, false);
|
||||
// A root cannot leave the in-range band, so the narrowing is the plain
|
||||
// Fcvt with none of ToPS2FPU_Full's arms around it. The largest operand is
|
||||
// a shade under 2^129 and roots to under 2^65; the smallest one FZ does
|
||||
// not flush is 2^-126 and roots to 2^-63. Both sit inside [2^-126, 2^128),
|
||||
// and the only result outside it is the zero the underflow arm would have
|
||||
// flushed to the same zero, |t| having already made its sign positive.
|
||||
armAsm->Fcvt(armSRegister(treg), armDRegister(treg));
|
||||
SingleToSlot(EEREC_D, treg);
|
||||
_freeNEONreg(treg);
|
||||
|
||||
|
||||
@@ -588,6 +588,35 @@ TEST(EeRecFpuFull, SqrtNegativeSetsIFlagAndUsesAbs)
|
||||
EXPECT_EQ(h.GetGpr64Jit(reg::v0) & 0x00020040u, 0x00020040u) << "I|SI not set";
|
||||
}
|
||||
|
||||
// recSQRT_S_xmm narrows with a plain Fcvt because a root cannot leave the band
|
||||
// ToPS2FPU_Full's saturating and flushing arms exist for. These are the four
|
||||
// operands nearest the ends of that band.
|
||||
TEST(EeRecFpuFull, SqrtStaysInsideTheNarrowingBand)
|
||||
{
|
||||
struct Case
|
||||
{
|
||||
u32 ft, want;
|
||||
const char* what;
|
||||
};
|
||||
static constexpr Case kCases[] = {
|
||||
{0x7FFFFFFFu, 0x5FB504F3u, "EEMAX: the largest root there is, 2^64.5"},
|
||||
{0x00800000u, 0x20000000u, "2^-126: the smallest operand FZ keeps, root 2^-63"},
|
||||
{0x007FFFFFu, 0x00000000u, "the largest denormal, flushed ahead of the root"},
|
||||
{0x80000000u, 0x00000000u, "-0.0"},
|
||||
};
|
||||
for (const Case& c : kCases)
|
||||
{
|
||||
SCOPED_TRACE(c.what);
|
||||
EeRecTestHarness h;
|
||||
h.EnableCop1();
|
||||
h.EnableFpuFullMode();
|
||||
h.SetFprBits(1, c.ft);
|
||||
h.LoadProgram({SQRT_S(2, 1)});
|
||||
h.RunJitNoDiff();
|
||||
EXPECT_EQ(h.GetFprBitsJit(2), c.want) << std::hex << "ft=" << c.ft;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EeRecFpuFull, RsqrtPseudoInfExact)
|
||||
{
|
||||
// 1.0 / sqrt(2^128) = 2^-64 = 0x1f800000 exactly. The current interp
|
||||
|
||||
Reference in New Issue
Block a user