mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 15:00:30 +02:00
VFPU: vsgn of a denormal is zero
In the interpreter, the IR interpreter, every backend's FSign and the x86 JIT's own vsgn. Recorded in cpu/vfpu/specials. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
2c801b8a31
commit
b16cd9a734
7 files changed
+37
-18
No files matched your search
@@ -20,6 +20,7 @@
|
||||
#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
|
||||
|
||||
#ifndef offsetof
|
||||
#include <cfloat>
|
||||
#include <cstddef>
|
||||
#endif
|
||||
|
||||
@@ -99,8 +100,11 @@ void Arm64JitBackend::CompIR_FAssign(IRInst inst) {
|
||||
|
||||
case IROp::FSign:
|
||||
regs_.Map(inst);
|
||||
// We'll need this flag later. Vector could use a temp and FCMEQ.
|
||||
fp_.FCMP(regs_.F(inst.src1));
|
||||
// Zero and denormals both sign as zero on the hardware, so compare the magnitude against
|
||||
// the smallest normal. We'll need this flag later.
|
||||
fp_.FABS(SCRATCHF1, regs_.F(inst.src1));
|
||||
fp_.MOVI2FDUP(EncodeRegToDouble(SCRATCHF2), FLT_MIN);
|
||||
fp_.FCMP(SCRATCHF1, SCRATCHF2);
|
||||
|
||||
fp_.MOVI2FDUP(EncodeRegToDouble(SCRATCHF1), 1.0f);
|
||||
// Invert 0x80000000 -> 0x7FFFFFFF as a mask for sign.
|
||||
@@ -110,9 +114,9 @@ void Arm64JitBackend::CompIR_FAssign(IRInst inst) {
|
||||
fp_.FMOV(regs_.FD(inst.dest), regs_.FD(inst.src1));
|
||||
fp_.BIT(regs_.FD(inst.dest), EncodeRegToDouble(SCRATCHF1), EncodeRegToDouble(SCRATCHF2));
|
||||
|
||||
// It's later now, let's replace with zero if that FCmp was EQ to zero.
|
||||
// It's later now, let's replace with zero if that FCmp said below (MI is false for a NaN.)
|
||||
fp_.MOVI2FDUP(EncodeRegToDouble(SCRATCHF1), 0.0f);
|
||||
fp_.FCSEL(regs_.F(inst.dest), SCRATCHF1, regs_.F(inst.dest), CC_EQ);
|
||||
fp_.FCSEL(regs_.F(inst.dest), SCRATCHF1, regs_.F(inst.dest), CC_MI);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -894,10 +894,10 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) {
|
||||
|
||||
case IROp::FSign:
|
||||
{
|
||||
// Bitwise trickery
|
||||
// Bitwise trickery. Denormals give zero, as on the hardware.
|
||||
u32 val;
|
||||
memcpy(&val, &mips->f[inst->src1], sizeof(u32));
|
||||
if (val == 0 || val == 0x80000000)
|
||||
if ((val & 0x7F800000) == 0)
|
||||
mips->f[inst->dest] = 0.0f;
|
||||
else if ((val >> 31) == 0)
|
||||
mips->f[inst->dest] = 1.0f;
|
||||
|
||||
@@ -800,10 +800,11 @@ namespace MIPSInt
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
float diff = s[i] - t[i];
|
||||
// To handle NaNs correctly, we do this with integer hackery
|
||||
// To handle NaNs correctly, we do this with integer hackery. A denormal difference
|
||||
// is zero, like everywhere else on the VFPU.
|
||||
u32 val;
|
||||
memcpy(&val, &diff, sizeof(u32));
|
||||
if (val == 0 || val == 0x80000000)
|
||||
if ((val & 0x7F800000) == 0)
|
||||
d[i] = 0.0f;
|
||||
else if ((val >> 31) == 0)
|
||||
d[i] = 1.0f;
|
||||
|
||||
@@ -158,10 +158,10 @@ void LoongArch64JitBackend::CompIR_FAssign(IRInst inst) {
|
||||
case IROp::FSign:
|
||||
{
|
||||
regs_.Map(inst);
|
||||
// Check if it's negative zero, either 0x20/0x200 is zero.
|
||||
// Zero or a denormal signs as zero: 0x20/0x200 are the zeros, 0x10/0x100 the subnormals.
|
||||
FCLASS_S(SCRATCHF1, regs_.F(inst.src1));
|
||||
MOVFR2GR_S(SCRATCH1, SCRATCHF1);
|
||||
ANDI(SCRATCH1, SCRATCH1, 0x220);
|
||||
ANDI(SCRATCH1, SCRATCH1, 0x330);
|
||||
SLTUI(SCRATCH1, SCRATCH1, 1);
|
||||
// Okay, it's zero if zero, 1 otherwise. Convert 1 to a constant 1.0.
|
||||
// Probably non-zero is the common case, so we make that the straight line.
|
||||
|
||||
@@ -164,9 +164,9 @@ void RiscVJitBackend::CompIR_FAssign(IRInst inst) {
|
||||
case IROp::FSign:
|
||||
{
|
||||
regs_.Map(inst);
|
||||
// Check if it's negative zero, either 0x10/0x08 is zero.
|
||||
// Zero or a denormal signs as zero: 0x10/0x08 are the zeros, 0x20/0x04 the subnormals.
|
||||
FCLASS(32, SCRATCH1, regs_.F(inst.src1));
|
||||
ANDI(SCRATCH1, SCRATCH1, 0x18);
|
||||
ANDI(SCRATCH1, SCRATCH1, 0x3C);
|
||||
SEQZ(SCRATCH1, SCRATCH1);
|
||||
// Okay, it's zero if zero, 1 otherwise. Convert 1 to a constant 1.0.
|
||||
// Probably non-zero is the common case, so we make that the straight line.
|
||||
|
||||
@@ -2072,9 +2072,17 @@ void Jit::Comp_Vsgn(MIPSOpcode op) {
|
||||
|
||||
// Would be nice with more temp regs here so we could put signBitLower and oneOneOneOne into regs...
|
||||
for (int i = 0; i < n; ++i) {
|
||||
XORPS(XMM0, R(XMM0));
|
||||
CMPEQSS(XMM0, fpr.V(sregs[i])); // XMM0 = s[i] == 0.0f
|
||||
// A denormal signs as zero like on the hardware, so test the exponent field, not the value.
|
||||
MOVSS(XMM1, fpr.V(sregs[i]));
|
||||
MOVAPS(tempxregs[i], R(XMM1));
|
||||
if (RipAccessible(fourinfnan)) {
|
||||
ANDPS(tempxregs[i], M(&fourinfnan)); // rip accessible
|
||||
} else {
|
||||
MOV(PTRBITS, R(TEMPREG), ImmPtr(&fourinfnan));
|
||||
ANDPS(tempxregs[i], MatR(TEMPREG));
|
||||
}
|
||||
XORPS(XMM0, R(XMM0));
|
||||
CMPEQSS(XMM0, R(tempxregs[i])); // XMM0 = exponent of s[i] == 0
|
||||
// Preserve sign bit, replace rest with ones
|
||||
if (RipAccessible(signBitLower)) {
|
||||
ANDPS(XMM1, M(&signBitLower)); // rip accessible
|
||||
|
||||
@@ -241,13 +241,19 @@ void X64JitBackend::CompIR_FAssign(IRInst inst) {
|
||||
}
|
||||
ORPS(tempReg, M(constants.positiveOnes)); // rip accessible
|
||||
|
||||
// Set dest = 0xFFFFFFFF if +0.0 or -0.0.
|
||||
// Set dest = 0xFFFFFFFF if the exponent is zero: +0.0, -0.0 or a denormal, which the
|
||||
// hardware also signs as zero.
|
||||
if (inst.dest != inst.src1) {
|
||||
XORPS(regs_.FX(inst.dest), regs_.F(inst.dest));
|
||||
CMPPS(regs_.FX(inst.dest), regs_.F(inst.src1), CMP_EQ);
|
||||
if (cpu_info.bAVX) {
|
||||
VANDPS(128, regs_.FX(inst.dest), regs_.FX(inst.src1), M(constants.positiveInfinity)); // rip accessible
|
||||
} else {
|
||||
MOVAPS(regs_.FX(inst.dest), regs_.F(inst.src1));
|
||||
ANDPS(regs_.FX(inst.dest), M(constants.positiveInfinity)); // rip accessible
|
||||
}
|
||||
} else {
|
||||
CMPPS(regs_.FX(inst.dest), M(constants.positiveZeroes), CMP_EQ); // rip accessible
|
||||
ANDPS(regs_.FX(inst.dest), M(constants.positiveInfinity)); // rip accessible
|
||||
}
|
||||
CMPPS(regs_.FX(inst.dest), M(constants.positiveZeroes), CMP_EQ); // rip accessible
|
||||
|
||||
// Now not the mask to keep zero if it was zero.
|
||||
ANDNPS(regs_.FX(inst.dest), R(tempReg));
|
||||
|
||||
Reference in new issue
Block a user