Interpreter: Fix FPRF handling of denormal singles

This commit is contained in:
JosJuice 2021-06-13 14:35:04 +02:00
parent def5666419
commit d56721ebb9
6 changed files with 47 additions and 42 deletions

View File

@ -87,7 +87,6 @@ enum PPCFpClass
// Uses PowerPC conventions for the return value, so it can be easily // Uses PowerPC conventions for the return value, so it can be easily
// used directly in CPU emulation. // used directly in CPU emulation.
u32 ClassifyDouble(double dvalue); u32 ClassifyDouble(double dvalue);
// More efficient float version.
u32 ClassifyFloat(float fvalue); u32 ClassifyFloat(float fvalue);
struct BaseAndDec struct BaseAndDec

View File

@ -302,7 +302,7 @@ void Interpreter::frspx(UGeckoInstruction inst) // round to single
if (!is_snan || FPSCR.VE == 0) if (!is_snan || FPSCR.VE == 0)
{ {
rPS(inst.FD).Fill(rounded); rPS(inst.FD).Fill(rounded);
PowerPC::UpdateFPRF(b); PowerPC::UpdateFPRFSingle(rounded);
} }
FPSCR.ClearFIFR(); FPSCR.ClearFIFR();
@ -311,7 +311,7 @@ void Interpreter::frspx(UGeckoInstruction inst) // round to single
{ {
SetFI(&FPSCR, b != rounded); SetFI(&FPSCR, b != rounded);
FPSCR.FR = fabs(rounded) > fabs(b); FPSCR.FR = fabs(rounded) > fabs(b);
PowerPC::UpdateFPRF(rounded); PowerPC::UpdateFPRFSingle(rounded);
rPS(inst.FD).Fill(rounded); rPS(inst.FD).Fill(rounded);
} }
@ -333,7 +333,7 @@ void Interpreter::fmulx(UGeckoInstruction inst)
rPS(inst.FD).SetPS0(result); rPS(inst.FD).SetPS0(result);
FPSCR.FI = 0; // are these flags important? FPSCR.FI = 0; // are these flags important?
FPSCR.FR = 0; FPSCR.FR = 0;
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFDouble(result);
} }
if (inst.Rc) if (inst.Rc)
@ -354,7 +354,7 @@ void Interpreter::fmulsx(UGeckoInstruction inst)
rPS(inst.FD).Fill(result); rPS(inst.FD).Fill(result);
FPSCR.FI = 0; FPSCR.FI = 0;
FPSCR.FR = 0; FPSCR.FR = 0;
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFSingle(result);
} }
if (inst.Rc) if (inst.Rc)
@ -372,7 +372,7 @@ void Interpreter::fmaddx(UGeckoInstruction inst)
{ {
const double result = ForceDouble(FPSCR, product.value); const double result = ForceDouble(FPSCR, product.value);
rPS(inst.FD).SetPS0(result); rPS(inst.FD).SetPS0(result);
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFDouble(result);
} }
if (inst.Rc) if (inst.Rc)
@ -395,7 +395,7 @@ void Interpreter::fmaddsx(UGeckoInstruction inst)
rPS(inst.FD).Fill(result); rPS(inst.FD).Fill(result);
FPSCR.FI = d_value.value != result; FPSCR.FI = d_value.value != result;
FPSCR.FR = 0; FPSCR.FR = 0;
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFSingle(result);
} }
if (inst.Rc) if (inst.Rc)
@ -413,7 +413,7 @@ void Interpreter::faddx(UGeckoInstruction inst)
{ {
const double result = ForceDouble(FPSCR, sum.value); const double result = ForceDouble(FPSCR, sum.value);
rPS(inst.FD).SetPS0(result); rPS(inst.FD).SetPS0(result);
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFDouble(result);
} }
if (inst.Rc) if (inst.Rc)
@ -430,7 +430,7 @@ void Interpreter::faddsx(UGeckoInstruction inst)
{ {
const double result = ForceSingle(FPSCR, sum.value); const double result = ForceSingle(FPSCR, sum.value);
rPS(inst.FD).Fill(result); rPS(inst.FD).Fill(result);
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFSingle(result);
} }
if (inst.Rc) if (inst.Rc)
@ -450,7 +450,7 @@ void Interpreter::fdivx(UGeckoInstruction inst)
{ {
const double result = ForceDouble(FPSCR, quotient.value); const double result = ForceDouble(FPSCR, quotient.value);
rPS(inst.FD).SetPS0(result); rPS(inst.FD).SetPS0(result);
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFDouble(result);
} }
// FR,FI,OX,UX??? // FR,FI,OX,UX???
@ -470,7 +470,7 @@ void Interpreter::fdivsx(UGeckoInstruction inst)
{ {
const double result = ForceSingle(FPSCR, quotient.value); const double result = ForceSingle(FPSCR, quotient.value);
rPS(inst.FD).Fill(result); rPS(inst.FD).Fill(result);
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFSingle(result);
} }
if (inst.Rc) if (inst.Rc)
@ -485,7 +485,7 @@ void Interpreter::fresx(UGeckoInstruction inst)
const auto compute_result = [inst](double value) { const auto compute_result = [inst](double value) {
const double result = Common::ApproximateReciprocal(value); const double result = Common::ApproximateReciprocal(value);
rPS(inst.FD).Fill(result); rPS(inst.FD).Fill(result);
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFSingle(result);
}; };
if (b == 0.0) if (b == 0.0)
@ -523,7 +523,7 @@ void Interpreter::frsqrtex(UGeckoInstruction inst)
const auto compute_result = [inst](double value) { const auto compute_result = [inst](double value) {
const double result = Common::ApproximateReciprocalSquareRoot(value); const double result = Common::ApproximateReciprocalSquareRoot(value);
rPS(inst.FD).SetPS0(result); rPS(inst.FD).SetPS0(result);
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFDouble(result);
}; };
if (b < 0.0) if (b < 0.0)
@ -574,7 +574,7 @@ void Interpreter::fmsubx(UGeckoInstruction inst)
{ {
const double result = ForceDouble(FPSCR, product.value); const double result = ForceDouble(FPSCR, product.value);
rPS(inst.FD).SetPS0(result); rPS(inst.FD).SetPS0(result);
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFDouble(result);
} }
if (inst.Rc) if (inst.Rc)
@ -594,7 +594,7 @@ void Interpreter::fmsubsx(UGeckoInstruction inst)
{ {
const double result = ForceSingle(FPSCR, product.value); const double result = ForceSingle(FPSCR, product.value);
rPS(inst.FD).Fill(result); rPS(inst.FD).Fill(result);
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFSingle(result);
} }
if (inst.Rc) if (inst.Rc)
@ -615,7 +615,7 @@ void Interpreter::fnmaddx(UGeckoInstruction inst)
const double result = std::isnan(tmp) ? tmp : -tmp; const double result = std::isnan(tmp) ? tmp : -tmp;
rPS(inst.FD).SetPS0(result); rPS(inst.FD).SetPS0(result);
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFDouble(result);
} }
if (inst.Rc) if (inst.Rc)
@ -637,7 +637,7 @@ void Interpreter::fnmaddsx(UGeckoInstruction inst)
const double result = std::isnan(tmp) ? tmp : -tmp; const double result = std::isnan(tmp) ? tmp : -tmp;
rPS(inst.FD).Fill(result); rPS(inst.FD).Fill(result);
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFSingle(result);
} }
if (inst.Rc) if (inst.Rc)
@ -658,7 +658,7 @@ void Interpreter::fnmsubx(UGeckoInstruction inst)
const double result = std::isnan(tmp) ? tmp : -tmp; const double result = std::isnan(tmp) ? tmp : -tmp;
rPS(inst.FD).SetPS0(result); rPS(inst.FD).SetPS0(result);
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFDouble(result);
} }
if (inst.Rc) if (inst.Rc)
@ -680,7 +680,7 @@ void Interpreter::fnmsubsx(UGeckoInstruction inst)
const double result = std::isnan(tmp) ? tmp : -tmp; const double result = std::isnan(tmp) ? tmp : -tmp;
rPS(inst.FD).Fill(result); rPS(inst.FD).Fill(result);
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFSingle(result);
} }
if (inst.Rc) if (inst.Rc)
@ -698,7 +698,7 @@ void Interpreter::fsubx(UGeckoInstruction inst)
{ {
const double result = ForceDouble(FPSCR, difference.value); const double result = ForceDouble(FPSCR, difference.value);
rPS(inst.FD).SetPS0(result); rPS(inst.FD).SetPS0(result);
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFDouble(result);
} }
if (inst.Rc) if (inst.Rc)
@ -716,7 +716,7 @@ void Interpreter::fsubsx(UGeckoInstruction inst)
{ {
const double result = ForceSingle(FPSCR, difference.value); const double result = ForceSingle(FPSCR, difference.value);
rPS(inst.FD).Fill(result); rPS(inst.FD).Fill(result);
PowerPC::UpdateFPRF(result); PowerPC::UpdateFPRFSingle(result);
} }
if (inst.Rc) if (inst.Rc)

View File

@ -117,7 +117,7 @@ void Interpreter::ps_div(UGeckoInstruction inst)
const double ps1 = ForceSingle(FPSCR, NI_div(&FPSCR, a.PS1AsDouble(), b.PS1AsDouble()).value); const double ps1 = ForceSingle(FPSCR, NI_div(&FPSCR, a.PS1AsDouble(), b.PS1AsDouble()).value);
rPS(inst.FD).SetBoth(ps0, ps1); rPS(inst.FD).SetBoth(ps0, ps1);
PowerPC::UpdateFPRF(ps0); PowerPC::UpdateFPRFSingle(ps0);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -145,7 +145,7 @@ void Interpreter::ps_res(UGeckoInstruction inst)
const double ps1 = Common::ApproximateReciprocal(b); const double ps1 = Common::ApproximateReciprocal(b);
rPS(inst.FD).SetBoth(ps0, ps1); rPS(inst.FD).SetBoth(ps0, ps1);
PowerPC::UpdateFPRF(ps0); PowerPC::UpdateFPRFSingle(ps0);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -178,7 +178,7 @@ void Interpreter::ps_rsqrte(UGeckoInstruction inst)
const double dst_ps1 = ForceSingle(FPSCR, Common::ApproximateReciprocalSquareRoot(ps1)); const double dst_ps1 = ForceSingle(FPSCR, Common::ApproximateReciprocalSquareRoot(ps1));
rPS(inst.FD).SetBoth(dst_ps0, dst_ps1); rPS(inst.FD).SetBoth(dst_ps0, dst_ps1);
PowerPC::UpdateFPRF(dst_ps0); PowerPC::UpdateFPRFSingle(dst_ps0);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -193,7 +193,7 @@ void Interpreter::ps_sub(UGeckoInstruction inst)
const double ps1 = ForceSingle(FPSCR, NI_sub(&FPSCR, a.PS1AsDouble(), b.PS1AsDouble()).value); const double ps1 = ForceSingle(FPSCR, NI_sub(&FPSCR, a.PS1AsDouble(), b.PS1AsDouble()).value);
rPS(inst.FD).SetBoth(ps0, ps1); rPS(inst.FD).SetBoth(ps0, ps1);
PowerPC::UpdateFPRF(ps0); PowerPC::UpdateFPRFSingle(ps0);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -208,7 +208,7 @@ void Interpreter::ps_add(UGeckoInstruction inst)
const double ps1 = ForceSingle(FPSCR, NI_add(&FPSCR, a.PS1AsDouble(), b.PS1AsDouble()).value); const double ps1 = ForceSingle(FPSCR, NI_add(&FPSCR, a.PS1AsDouble(), b.PS1AsDouble()).value);
rPS(inst.FD).SetBoth(ps0, ps1); rPS(inst.FD).SetBoth(ps0, ps1);
PowerPC::UpdateFPRF(ps0); PowerPC::UpdateFPRFSingle(ps0);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -226,7 +226,7 @@ void Interpreter::ps_mul(UGeckoInstruction inst)
const double ps1 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS1AsDouble(), c1).value); const double ps1 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS1AsDouble(), c1).value);
rPS(inst.FD).SetBoth(ps0, ps1); rPS(inst.FD).SetBoth(ps0, ps1);
PowerPC::UpdateFPRF(ps0); PowerPC::UpdateFPRFSingle(ps0);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -247,7 +247,7 @@ void Interpreter::ps_msub(UGeckoInstruction inst)
ForceSingle(FPSCR, NI_msub(&FPSCR, a.PS1AsDouble(), c1, b.PS1AsDouble()).value); ForceSingle(FPSCR, NI_msub(&FPSCR, a.PS1AsDouble(), c1, b.PS1AsDouble()).value);
rPS(inst.FD).SetBoth(ps0, ps1); rPS(inst.FD).SetBoth(ps0, ps1);
PowerPC::UpdateFPRF(ps0); PowerPC::UpdateFPRFSingle(ps0);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -268,7 +268,7 @@ void Interpreter::ps_madd(UGeckoInstruction inst)
ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS1AsDouble(), c1, b.PS1AsDouble()).value); ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS1AsDouble(), c1, b.PS1AsDouble()).value);
rPS(inst.FD).SetBoth(ps0, ps1); rPS(inst.FD).SetBoth(ps0, ps1);
PowerPC::UpdateFPRF(ps0); PowerPC::UpdateFPRFSingle(ps0);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -292,7 +292,7 @@ void Interpreter::ps_nmsub(UGeckoInstruction inst)
const double ps1 = std::isnan(tmp1) ? tmp1 : -tmp1; const double ps1 = std::isnan(tmp1) ? tmp1 : -tmp1;
rPS(inst.FD).SetBoth(ps0, ps1); rPS(inst.FD).SetBoth(ps0, ps1);
PowerPC::UpdateFPRF(ps0); PowerPC::UpdateFPRFSingle(ps0);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -316,7 +316,7 @@ void Interpreter::ps_nmadd(UGeckoInstruction inst)
const double ps1 = std::isnan(tmp1) ? tmp1 : -tmp1; const double ps1 = std::isnan(tmp1) ? tmp1 : -tmp1;
rPS(inst.FD).SetBoth(ps0, ps1); rPS(inst.FD).SetBoth(ps0, ps1);
PowerPC::UpdateFPRF(ps0); PowerPC::UpdateFPRFSingle(ps0);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -332,7 +332,7 @@ void Interpreter::ps_sum0(UGeckoInstruction inst)
const double ps1 = ForceSingle(FPSCR, c.PS1AsDouble()); const double ps1 = ForceSingle(FPSCR, c.PS1AsDouble());
rPS(inst.FD).SetBoth(ps0, ps1); rPS(inst.FD).SetBoth(ps0, ps1);
PowerPC::UpdateFPRF(ps0); PowerPC::UpdateFPRFSingle(ps0);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -348,7 +348,7 @@ void Interpreter::ps_sum1(UGeckoInstruction inst)
const double ps1 = ForceSingle(FPSCR, NI_add(&FPSCR, a.PS0AsDouble(), b.PS1AsDouble()).value); const double ps1 = ForceSingle(FPSCR, NI_add(&FPSCR, a.PS0AsDouble(), b.PS1AsDouble()).value);
rPS(inst.FD).SetBoth(ps0, ps1); rPS(inst.FD).SetBoth(ps0, ps1);
PowerPC::UpdateFPRF(ps1); PowerPC::UpdateFPRFSingle(ps1);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -364,7 +364,7 @@ void Interpreter::ps_muls0(UGeckoInstruction inst)
const double ps1 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS1AsDouble(), c0).value); const double ps1 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS1AsDouble(), c0).value);
rPS(inst.FD).SetBoth(ps0, ps1); rPS(inst.FD).SetBoth(ps0, ps1);
PowerPC::UpdateFPRF(ps0); PowerPC::UpdateFPRFSingle(ps0);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -380,7 +380,7 @@ void Interpreter::ps_muls1(UGeckoInstruction inst)
const double ps1 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS1AsDouble(), c1).value); const double ps1 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS1AsDouble(), c1).value);
rPS(inst.FD).SetBoth(ps0, ps1); rPS(inst.FD).SetBoth(ps0, ps1);
PowerPC::UpdateFPRF(ps0); PowerPC::UpdateFPRFSingle(ps0);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -399,7 +399,7 @@ void Interpreter::ps_madds0(UGeckoInstruction inst)
ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS1AsDouble(), c0, b.PS1AsDouble()).value); ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS1AsDouble(), c0, b.PS1AsDouble()).value);
rPS(inst.FD).SetBoth(ps0, ps1); rPS(inst.FD).SetBoth(ps0, ps1);
PowerPC::UpdateFPRF(ps0); PowerPC::UpdateFPRFSingle(ps0);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -418,7 +418,7 @@ void Interpreter::ps_madds1(UGeckoInstruction inst)
ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS1AsDouble(), c1, b.PS1AsDouble()).value); ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS1AsDouble(), c1, b.PS1AsDouble()).value);
rPS(inst.FD).SetBoth(ps0, ps1); rPS(inst.FD).SetBoth(ps0, ps1);
PowerPC::UpdateFPRF(ps0); PowerPC::UpdateFPRFSingle(ps0);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();

View File

@ -626,11 +626,16 @@ void PowerPCState::SetSR(u32 index, u32 value)
// FPSCR update functions // FPSCR update functions
void UpdateFPRF(double dvalue) void UpdateFPRFDouble(double dvalue)
{ {
FPSCR.FPRF = Common::ClassifyDouble(dvalue); FPSCR.FPRF = Common::ClassifyDouble(dvalue);
} }
void UpdateFPRFSingle(float fvalue)
{
FPSCR.FPRF = Common::ClassifyFloat(fvalue);
}
void RoundingModeUpdated() void RoundingModeUpdated()
{ {
// The rounding mode is separate for each thread, so this must run on the CPU thread // The rounding mode is separate for each thread, so this must run on the CPU thread

View File

@ -304,7 +304,8 @@ inline void SetXER_OV(bool value)
SetXER_SO(value); SetXER_SO(value);
} }
void UpdateFPRF(double dvalue); void UpdateFPRFDouble(double dvalue);
void UpdateFPRFSingle(float fvalue);
void RoundingModeUpdated(); void RoundingModeUpdated();

View File

@ -74,14 +74,14 @@ TEST(JitArm64, FPRF)
for (const u64 double_input : double_test_values) for (const u64 double_input : double_test_values)
{ {
const u32 expected_double = const u32 expected_double =
RunUpdateFPRF([&] { PowerPC::UpdateFPRF(Common::BitCast<double>(double_input)); }); RunUpdateFPRF([&] { PowerPC::UpdateFPRFDouble(Common::BitCast<double>(double_input)); });
const u32 actual_double = RunUpdateFPRF([&] { test.fprf_double(double_input); }); const u32 actual_double = RunUpdateFPRF([&] { test.fprf_double(double_input); });
EXPECT_EQ(expected_double, actual_double); EXPECT_EQ(expected_double, actual_double);
const u32 single_input = ConvertToSingle(double_input); const u32 single_input = ConvertToSingle(double_input);
const u32 expected_single = RunUpdateFPRF( const u32 expected_single =
[&] { PowerPC::UpdateFPRF(Common::BitCast<double>(ConvertToDouble(single_input))); }); RunUpdateFPRF([&] { PowerPC::UpdateFPRFSingle(Common::BitCast<float>(single_input)); });
const u32 actual_single = RunUpdateFPRF([&] { test.fprf_single(single_input); }); const u32 actual_single = RunUpdateFPRF([&] { test.fprf_single(single_input); });
EXPECT_EQ(expected_single, actual_single); EXPECT_EQ(expected_single, actual_single);
} }