1
0
Fork 0

Dyncom/VFP: Perform flush-to-zero on the second operand of vsub before sending it to vadd.

Previously we were letting vadd flush the value to positive 0, but there are cases where this behavior is wrong, for example,

vsub: -0 - +0 = -0
vadd: -0 + +0 = +0

Now we'll flush the value to +0 inside vsub, and then negate it.
This commit is contained in:
Subv 2017-05-08 16:24:08 -05:00
parent e33558c6ce
commit a0874a7a68
1 changed files with 12 additions and 2 deletions

View File

@ -1062,12 +1062,22 @@ static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
static u32 vfp_single_fsub(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) { static u32 vfp_single_fsub(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) {
LOG_TRACE(Core_ARM11, "s%u = %08x", sn, sd); LOG_TRACE(Core_ARM11, "s%u = %08x", sn, sd);
/* /*
* Subtraction is addition with one sign inverted. * Subtraction is addition with one sign inverted. Unpack the second operand to perform FTZ if
* necessary, we can't let fadd do this because a denormal in m might get flushed to +0 in FTZ
* mode, and the resulting sign of 0 OP +0 differs between fadd and fsub. We do not need to do
* this for n because +0 OP 0 is always +0 for both fadd and fsub.
*/ */
struct vfp_single vsm;
u32 exceptions = vfp_single_unpack(&vsm, m, fpscr);
if (exceptions & FPSCR_IDC) {
// The value was flushed to zero, re-pack it.
m = vfp_single_pack(&vsm);
}
if (m != 0x7FC00000) // Only negate if m isn't NaN. if (m != 0x7FC00000) // Only negate if m isn't NaN.
m = vfp_single_packed_negate(m); m = vfp_single_packed_negate(m);
return vfp_single_fadd(state, sd, sn, m, fpscr); return vfp_single_fadd(state, sd, sn, m, fpscr) | exceptions;
} }
/* /*