mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
FPU: saturate float-to-int in the interpreters
The C cast is undefined past the int32 range, and x86 makes it INT_MIN, so round/trunc/ceil/floor/cvt.w.s of anything from 2^31 up gave 0x80000000 on x86 hosts while the PSP saturates to 0x7fffffff (cpu/fpu/roundmode). Route all of them through SaturatedFloatToInt, which also covers NaN and inf, and drop the special cases that did. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
dc983bb1c5
commit
fc9dbf5ff5
3 files changed
+29
-82
No files matched your search
@@ -127,6 +127,19 @@ inline int is_even(float d) {
|
||||
return 2.0f * int_part == d;
|
||||
}
|
||||
|
||||
// Float to int the way the PSP's FPU does it: at or past the int32 range the result is the nearest
|
||||
// limit, and a NaN gives INT_MAX whatever its sign (cpu/fpu/roundmode). The plain cast is undefined
|
||||
// there, and x86 makes it INT_MIN. Takes a value that's already been rounded.
|
||||
inline int32_t SaturatedFloatToInt(double d) {
|
||||
if (d >= 2147483648.0)
|
||||
return 0x7FFFFFFF;
|
||||
if (d <= -2147483648.0)
|
||||
return (int32_t)0x80000000;
|
||||
if (d != d)
|
||||
return 0x7FFFFFFF;
|
||||
return (int32_t)d;
|
||||
}
|
||||
|
||||
// Rounds *.5 to closest even number
|
||||
inline double round_ieee_754(double d) {
|
||||
float i = (float)floor(d);
|
||||
|
||||
@@ -928,58 +928,17 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) {
|
||||
mips->r[inst->dest] = mips->vfpuCtrl[inst->src1];
|
||||
break;
|
||||
case IROp::FRound:
|
||||
{
|
||||
float value = mips->f[inst->src1];
|
||||
if (my_isnanorinf(value)) {
|
||||
mips->fi[inst->dest] = my_isinf(value) && value < 0.0f ? -2147483648LL : 2147483647LL;
|
||||
mips->fs[inst->dest] = SaturatedFloatToInt(round_ieee_754(mips->f[inst->src1]));
|
||||
break;
|
||||
} else {
|
||||
mips->fs[inst->dest] = (int)round_ieee_754(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IROp::FTrunc:
|
||||
{
|
||||
float value = mips->f[inst->src1];
|
||||
if (my_isnanorinf(value)) {
|
||||
mips->fi[inst->dest] = my_isinf(value) && value < 0.0f ? -2147483648LL : 2147483647LL;
|
||||
mips->fs[inst->dest] = SaturatedFloatToInt(truncf(mips->f[inst->src1]));
|
||||
break;
|
||||
} else {
|
||||
if (value >= 0.0f) {
|
||||
mips->fs[inst->dest] = (int)floorf(value);
|
||||
// Overflow, but it was positive.
|
||||
if (mips->fs[inst->dest] == -2147483648LL) {
|
||||
mips->fs[inst->dest] = 2147483647LL;
|
||||
}
|
||||
} else {
|
||||
// Overflow happens to be the right value anyway.
|
||||
mips->fs[inst->dest] = (int)ceilf(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
case IROp::FCeil:
|
||||
{
|
||||
float value = mips->f[inst->src1];
|
||||
if (my_isnanorinf(value)) {
|
||||
mips->fi[inst->dest] = my_isinf(value) && value < 0.0f ? -2147483648LL : 2147483647LL;
|
||||
mips->fs[inst->dest] = SaturatedFloatToInt(ceilf(mips->f[inst->src1]));
|
||||
break;
|
||||
} else {
|
||||
mips->fs[inst->dest] = (int)ceilf(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IROp::FFloor:
|
||||
{
|
||||
float value = mips->f[inst->src1];
|
||||
if (my_isnanorinf(value)) {
|
||||
mips->fi[inst->dest] = my_isinf(value) && value < 0.0f ? -2147483648LL : 2147483647LL;
|
||||
mips->fs[inst->dest] = SaturatedFloatToInt(floorf(mips->f[inst->src1]));
|
||||
break;
|
||||
} else {
|
||||
mips->fs[inst->dest] = (int)floorf(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IROp::FCmp:
|
||||
switch (inst->dest) {
|
||||
case IRFpCompareMode::False:
|
||||
@@ -1019,16 +978,12 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) {
|
||||
case IROp::FCvtWS:
|
||||
{
|
||||
float src = mips->f[inst->src1];
|
||||
if (my_isnanorinf(src)) {
|
||||
mips->fs[inst->dest] = my_isinf(src) && src < 0.0f ? -2147483648LL : 2147483647LL;
|
||||
break;
|
||||
}
|
||||
// TODO: Inline assembly to use here would be better.
|
||||
switch (IRRoundMode(mips->fcr31 & 3)) {
|
||||
case IRRoundMode::RINT_0: mips->fs[inst->dest] = (int)round_ieee_754(src); break;
|
||||
case IRRoundMode::CAST_1: mips->fs[inst->dest] = (int)src; break;
|
||||
case IRRoundMode::CEIL_2: mips->fs[inst->dest] = (int)ceilf(src); break;
|
||||
case IRRoundMode::FLOOR_3: mips->fs[inst->dest] = (int)floorf(src); break;
|
||||
case IRRoundMode::RINT_0: mips->fs[inst->dest] = SaturatedFloatToInt(round_ieee_754(src)); break;
|
||||
case IRRoundMode::CAST_1: mips->fs[inst->dest] = SaturatedFloatToInt(truncf(src)); break;
|
||||
case IRRoundMode::CEIL_2: mips->fs[inst->dest] = SaturatedFloatToInt(ceilf(src)); break;
|
||||
case IRRoundMode::FLOOR_3: mips->fs[inst->dest] = SaturatedFloatToInt(floorf(src)); break;
|
||||
}
|
||||
break; //cvt.w.s
|
||||
}
|
||||
|
||||
@@ -1130,46 +1130,25 @@ namespace MIPSInt {
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
if (my_isnanorinf(F(fs)))
|
||||
{
|
||||
FsI(fd) = my_isinf(F(fs)) && F(fs) < 0.0f ? -2147483648LL : 2147483647LL;
|
||||
break;
|
||||
}
|
||||
switch (op & 0x3f)
|
||||
{
|
||||
// round.w.s is round-half-to-even, not half-away-from-zero - and its mode is fixed,
|
||||
// so unlike cvt.w.s below it must not follow fcr31. round_ieee_754 is both.
|
||||
case 12: FsI(fd) = (int)round_ieee_754(F(fs)); break; //round.w.s
|
||||
case 13: //trunc.w.s
|
||||
if (F(fs) >= 0.0f) {
|
||||
FsI(fd) = (int)floorf(F(fs));
|
||||
// Overflow, but it was positive.
|
||||
if (FsI(fd) == -2147483648LL) {
|
||||
FsI(fd) = 2147483647LL;
|
||||
}
|
||||
} else {
|
||||
// Overflow happens to be the right value anyway.
|
||||
FsI(fd) = (int)ceilf(F(fs));
|
||||
}
|
||||
break;
|
||||
case 14: FsI(fd) = (int)ceilf (F(fs)); break; //ceil.w.s
|
||||
case 15: FsI(fd) = (int)floorf(F(fs)); break; //floor.w.s
|
||||
case 12: FsI(fd) = SaturatedFloatToInt(round_ieee_754(F(fs))); break; //round.w.s
|
||||
case 13: FsI(fd) = SaturatedFloatToInt(truncf(F(fs))); break; //trunc.w.s
|
||||
case 14: FsI(fd) = SaturatedFloatToInt(ceilf(F(fs))); break; //ceil.w.s
|
||||
case 15: FsI(fd) = SaturatedFloatToInt(floorf(F(fs))); break; //floor.w.s
|
||||
}
|
||||
break;
|
||||
case 32: F(fd) = (float)FsI(fs); break; //cvt.s.w
|
||||
|
||||
case 36:
|
||||
if (my_isnanorinf(F(fs)))
|
||||
{
|
||||
FsI(fd) = my_isinf(F(fs)) && F(fs) < 0.0f ? -2147483648LL : 2147483647LL;
|
||||
break;
|
||||
}
|
||||
switch (mips->fcr31 & 3)
|
||||
{
|
||||
case 0: FsI(fd) = (int)round_ieee_754(F(fs)); break; // RINT_0
|
||||
case 1: FsI(fd) = (int)F(fs); break; // CAST_1
|
||||
case 2: FsI(fd) = (int)ceilf(F(fs)); break; // CEIL_2
|
||||
case 3: FsI(fd) = (int)floorf(F(fs)); break; // FLOOR_3
|
||||
case 0: FsI(fd) = SaturatedFloatToInt(round_ieee_754(F(fs))); break; // RINT_0
|
||||
case 1: FsI(fd) = SaturatedFloatToInt(truncf(F(fs))); break; // CAST_1
|
||||
case 2: FsI(fd) = SaturatedFloatToInt(ceilf(F(fs))); break; // CEIL_2
|
||||
case 3: FsI(fd) = SaturatedFloatToInt(floorf(F(fs))); break; // FLOOR_3
|
||||
}
|
||||
break; //cvt.w.s
|
||||
default:
|
||||
|
||||
Reference in new issue
Block a user