VFPU: vsrt1-4 order NaN, denormals and signed zero like the hardware

The same ordering as vmin/vmax (sign and magnitude, denormals compare as
zero), and on a tie vsrt1/2 keep the lower lane of each pair, vsrt3/4 the
upper one. Recorded in cpu/vfpu/specials.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Henrik RydgårdandClaude Fable 5.1 committed 2026-09-22 14:10:22 -06:00
1 parent ed6d13e15f
commit 2c801b8a31
1 file changed
+42 -20
+42 -20
View File
@@ -102,6 +102,27 @@ inline float nanclamp(float f, float lower, float upper)
return nanmin(nanmax(f, lower), upper);
}
// The ordering the hardware's min/max family compares with: denormals count as zero, so ±0 and
// every denormal tie with each other, and inf/NaN sort by sign and magnitude (-NaN < -inf < finite
// < inf < NaN). A tie returns the second operand (cpu/vfpu/minmax_tie, minmax_zero, specials).
inline s32 vfpu_order_key(float f) {
u32 u;
memcpy(&u, &f, sizeof(u));
u32 mag = u & 0x7FFFFFFF;
if (mag < 0x00800000) {
mag = 0;
}
return (u & 0x80000000) ? -(s32)mag : (s32)mag;
}
inline float vfpu_min(float a, float b) {
return vfpu_order_key(a) < vfpu_order_key(b) ? a : b;
}
inline float vfpu_max(float a, float b) {
return vfpu_order_key(a) > vfpu_order_key(b) ? a : b;
}
static void ApplyPrefixST(MIPSState *mips, float *r, u32 data, VectorSize size, float invalid = 0.0f) {
// Check for no prefix.
if (data == 0xe4)
@@ -1319,11 +1340,12 @@ namespace MIPSInt
u32 tprefixAdd = VFPU_SWIZZLE(1, 0, 3, 2);
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz);
// TODO: May mishandle NAN / negative zero / etc.
d[0] = std::min(s[0], t[0]);
d[1] = std::max(s[1], t[1]);
d[2] = std::min(s[2], t[2]);
d[3] = std::max(s[3], t[3]);
// Each pair is compared as (y, x) and (w, z), so a tie keeps the lower lane; vsrt3 and
// vsrt4 compare the other way round and keep the upper one.
d[0] = vfpu_min(t[0], s[0]);
d[1] = vfpu_max(s[1], t[1]);
d[2] = vfpu_min(t[2], s[2]);
d[3] = vfpu_max(s[3], t[3]);
RetainInvalidSwizzleST(mips, d, sz);
ApplyPrefixD(mips, d, sz);
WriteVector(mips, d, sz, vd);
@@ -1345,11 +1367,11 @@ namespace MIPSInt
u32 tprefixAdd = VFPU_SWIZZLE(3, 2, 1, 0);
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz);
// TODO: May mishandle NAN / negative zero / etc.
d[0] = std::min(s[0], t[0]);
d[1] = std::min(s[1], t[1]);
d[2] = std::max(s[2], t[2]);
d[3] = std::max(s[3], t[3]);
// Compared as (w, x) and (z, y), ties keep x and y.
d[0] = vfpu_min(t[0], s[0]);
d[1] = vfpu_min(t[1], s[1]);
d[2] = vfpu_max(s[2], t[2]);
d[3] = vfpu_max(s[3], t[3]);
RetainInvalidSwizzleST(mips, d, sz);
ApplyPrefixD(mips, d, sz);
WriteVector(mips, d, sz, vd);
@@ -1371,11 +1393,11 @@ namespace MIPSInt
u32 tprefixAdd = VFPU_SWIZZLE(1, 0, 3, 2);
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz);
// TODO: May mishandle NAN / negative zero / etc.
d[0] = std::max(s[0], t[0]);
d[1] = std::min(s[1], t[1]);
d[2] = std::max(s[2], t[2]);
d[3] = std::min(s[3], t[3]);
// Compared as (x, y) and (z, w), ties keep y and w.
d[0] = vfpu_max(s[0], t[0]);
d[1] = vfpu_min(t[1], s[1]);
d[2] = vfpu_max(s[2], t[2]);
d[3] = vfpu_min(t[3], s[3]);
RetainInvalidSwizzleST(mips, d, sz);
ApplyPrefixD(mips, d, sz);
WriteVector(mips, d, sz, vd);
@@ -1397,11 +1419,11 @@ namespace MIPSInt
u32 tprefixAdd = VFPU_SWIZZLE(3, 2, 1, 0);
ApplyPrefixST(mips, t, VFPURewritePrefix(mips, VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), sz);
// TODO: May mishandle NAN / negative zero / etc.
d[0] = std::max(s[0], t[0]);
d[1] = std::max(s[1], t[1]);
d[2] = std::min(s[2], t[2]);
d[3] = std::min(s[3], t[3]);
// Compared as (x, w) and (y, z), ties keep w and z.
d[0] = vfpu_max(s[0], t[0]);
d[1] = vfpu_max(s[1], t[1]);
d[2] = vfpu_min(t[2], s[2]);
d[3] = vfpu_min(t[3], s[3]);
RetainInvalidSwizzleST(mips, d, sz);
ApplyPrefixD(mips, d, sz);
WriteVector(mips, d, sz, vd);