Merge pull request #9811 from JosJuice/fprf-denormal-singles
Fix FPRF handling of denormal singles
This commit is contained in:
commit
901a4fb5f4
|
@ -87,7 +87,6 @@ enum PPCFpClass
|
|||
// Uses PowerPC conventions for the return value, so it can be easily
|
||||
// used directly in CPU emulation.
|
||||
u32 ClassifyDouble(double dvalue);
|
||||
// More efficient float version.
|
||||
u32 ClassifyFloat(float fvalue);
|
||||
|
||||
struct BaseAndDec
|
||||
|
|
|
@ -36,15 +36,13 @@ inline void SetFPException(UReg_FPSCR* fpscr, u32 mask)
|
|||
fpscr->VX = (fpscr->Hex & FPSCR_VX_ANY) != 0;
|
||||
}
|
||||
|
||||
inline double ForceSingle(const UReg_FPSCR& fpscr, double value)
|
||||
inline float ForceSingle(const UReg_FPSCR& fpscr, double value)
|
||||
{
|
||||
// convert to float...
|
||||
float x = (float)value;
|
||||
float x = static_cast<float>(value);
|
||||
if (!cpu_info.bFlushToZero && fpscr.NI)
|
||||
{
|
||||
x = Common::FlushToZero(x);
|
||||
}
|
||||
// ...and back to double:
|
||||
return x;
|
||||
}
|
||||
|
||||
|
|
|
@ -290,7 +290,7 @@ void Interpreter::fselx(UGeckoInstruction inst)
|
|||
void Interpreter::frspx(UGeckoInstruction inst) // round to single
|
||||
{
|
||||
const double b = rPS(inst.FB).PS0AsDouble();
|
||||
const double rounded = ForceSingle(FPSCR, b);
|
||||
const float rounded = ForceSingle(FPSCR, b);
|
||||
|
||||
if (std::isnan(b))
|
||||
{
|
||||
|
@ -302,7 +302,7 @@ void Interpreter::frspx(UGeckoInstruction inst) // round to single
|
|||
if (!is_snan || FPSCR.VE == 0)
|
||||
{
|
||||
rPS(inst.FD).Fill(rounded);
|
||||
PowerPC::UpdateFPRF(b);
|
||||
PowerPC::UpdateFPRFSingle(rounded);
|
||||
}
|
||||
|
||||
FPSCR.ClearFIFR();
|
||||
|
@ -311,7 +311,7 @@ void Interpreter::frspx(UGeckoInstruction inst) // round to single
|
|||
{
|
||||
SetFI(&FPSCR, b != rounded);
|
||||
FPSCR.FR = fabs(rounded) > fabs(b);
|
||||
PowerPC::UpdateFPRF(rounded);
|
||||
PowerPC::UpdateFPRFSingle(rounded);
|
||||
rPS(inst.FD).Fill(rounded);
|
||||
}
|
||||
|
||||
|
@ -333,7 +333,7 @@ void Interpreter::fmulx(UGeckoInstruction inst)
|
|||
rPS(inst.FD).SetPS0(result);
|
||||
FPSCR.FI = 0; // are these flags important?
|
||||
FPSCR.FR = 0;
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFDouble(result);
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
@ -349,12 +349,12 @@ void Interpreter::fmulsx(UGeckoInstruction inst)
|
|||
|
||||
if (FPSCR.VE == 0 || d_value.HasNoInvalidExceptions())
|
||||
{
|
||||
const double result = ForceSingle(FPSCR, d_value.value);
|
||||
const float result = ForceSingle(FPSCR, d_value.value);
|
||||
|
||||
rPS(inst.FD).Fill(result);
|
||||
FPSCR.FI = 0;
|
||||
FPSCR.FR = 0;
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFSingle(result);
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
@ -372,7 +372,7 @@ void Interpreter::fmaddx(UGeckoInstruction inst)
|
|||
{
|
||||
const double result = ForceDouble(FPSCR, product.value);
|
||||
rPS(inst.FD).SetPS0(result);
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFDouble(result);
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
@ -390,12 +390,12 @@ void Interpreter::fmaddsx(UGeckoInstruction inst)
|
|||
|
||||
if (FPSCR.VE == 0 || d_value.HasNoInvalidExceptions())
|
||||
{
|
||||
const double result = ForceSingle(FPSCR, d_value.value);
|
||||
const float result = ForceSingle(FPSCR, d_value.value);
|
||||
|
||||
rPS(inst.FD).Fill(result);
|
||||
FPSCR.FI = d_value.value != result;
|
||||
FPSCR.FR = 0;
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFSingle(result);
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
@ -413,7 +413,7 @@ void Interpreter::faddx(UGeckoInstruction inst)
|
|||
{
|
||||
const double result = ForceDouble(FPSCR, sum.value);
|
||||
rPS(inst.FD).SetPS0(result);
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFDouble(result);
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
@ -428,9 +428,9 @@ void Interpreter::faddsx(UGeckoInstruction inst)
|
|||
|
||||
if (FPSCR.VE == 0 || sum.HasNoInvalidExceptions())
|
||||
{
|
||||
const double result = ForceSingle(FPSCR, sum.value);
|
||||
const float result = ForceSingle(FPSCR, sum.value);
|
||||
rPS(inst.FD).Fill(result);
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFSingle(result);
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
@ -450,7 +450,7 @@ void Interpreter::fdivx(UGeckoInstruction inst)
|
|||
{
|
||||
const double result = ForceDouble(FPSCR, quotient.value);
|
||||
rPS(inst.FD).SetPS0(result);
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFDouble(result);
|
||||
}
|
||||
|
||||
// FR,FI,OX,UX???
|
||||
|
@ -468,9 +468,9 @@ void Interpreter::fdivsx(UGeckoInstruction inst)
|
|||
|
||||
if (not_divide_by_zero && not_invalid)
|
||||
{
|
||||
const double result = ForceSingle(FPSCR, quotient.value);
|
||||
const float result = ForceSingle(FPSCR, quotient.value);
|
||||
rPS(inst.FD).Fill(result);
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFSingle(result);
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
@ -485,7 +485,7 @@ void Interpreter::fresx(UGeckoInstruction inst)
|
|||
const auto compute_result = [inst](double value) {
|
||||
const double result = Common::ApproximateReciprocal(value);
|
||||
rPS(inst.FD).Fill(result);
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFSingle(result);
|
||||
};
|
||||
|
||||
if (b == 0.0)
|
||||
|
@ -523,7 +523,7 @@ void Interpreter::frsqrtex(UGeckoInstruction inst)
|
|||
const auto compute_result = [inst](double value) {
|
||||
const double result = Common::ApproximateReciprocalSquareRoot(value);
|
||||
rPS(inst.FD).SetPS0(result);
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFDouble(result);
|
||||
};
|
||||
|
||||
if (b < 0.0)
|
||||
|
@ -574,7 +574,7 @@ void Interpreter::fmsubx(UGeckoInstruction inst)
|
|||
{
|
||||
const double result = ForceDouble(FPSCR, product.value);
|
||||
rPS(inst.FD).SetPS0(result);
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFDouble(result);
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
@ -592,9 +592,9 @@ void Interpreter::fmsubsx(UGeckoInstruction inst)
|
|||
|
||||
if (FPSCR.VE == 0 || product.HasNoInvalidExceptions())
|
||||
{
|
||||
const double result = ForceSingle(FPSCR, product.value);
|
||||
const float result = ForceSingle(FPSCR, product.value);
|
||||
rPS(inst.FD).Fill(result);
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFSingle(result);
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
@ -615,7 +615,7 @@ void Interpreter::fnmaddx(UGeckoInstruction inst)
|
|||
const double result = std::isnan(tmp) ? tmp : -tmp;
|
||||
|
||||
rPS(inst.FD).SetPS0(result);
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFDouble(result);
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
@ -633,11 +633,11 @@ void Interpreter::fnmaddsx(UGeckoInstruction inst)
|
|||
|
||||
if (FPSCR.VE == 0 || product.HasNoInvalidExceptions())
|
||||
{
|
||||
const double tmp = ForceSingle(FPSCR, product.value);
|
||||
const double result = std::isnan(tmp) ? tmp : -tmp;
|
||||
const float tmp = ForceSingle(FPSCR, product.value);
|
||||
const float result = std::isnan(tmp) ? tmp : -tmp;
|
||||
|
||||
rPS(inst.FD).Fill(result);
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFSingle(result);
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
@ -658,7 +658,7 @@ void Interpreter::fnmsubx(UGeckoInstruction inst)
|
|||
const double result = std::isnan(tmp) ? tmp : -tmp;
|
||||
|
||||
rPS(inst.FD).SetPS0(result);
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFDouble(result);
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
@ -676,11 +676,11 @@ void Interpreter::fnmsubsx(UGeckoInstruction inst)
|
|||
|
||||
if (FPSCR.VE == 0 || product.HasNoInvalidExceptions())
|
||||
{
|
||||
const double tmp = ForceSingle(FPSCR, product.value);
|
||||
const double result = std::isnan(tmp) ? tmp : -tmp;
|
||||
const float tmp = ForceSingle(FPSCR, product.value);
|
||||
const float result = std::isnan(tmp) ? tmp : -tmp;
|
||||
|
||||
rPS(inst.FD).Fill(result);
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFSingle(result);
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
@ -698,7 +698,7 @@ void Interpreter::fsubx(UGeckoInstruction inst)
|
|||
{
|
||||
const double result = ForceDouble(FPSCR, difference.value);
|
||||
rPS(inst.FD).SetPS0(result);
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFDouble(result);
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
@ -714,9 +714,9 @@ void Interpreter::fsubsx(UGeckoInstruction inst)
|
|||
|
||||
if (FPSCR.VE == 0 || difference.HasNoInvalidExceptions())
|
||||
{
|
||||
const double result = ForceSingle(FPSCR, difference.value);
|
||||
const float result = ForceSingle(FPSCR, difference.value);
|
||||
rPS(inst.FD).Fill(result);
|
||||
PowerPC::UpdateFPRF(result);
|
||||
PowerPC::UpdateFPRFSingle(result);
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
|
|
@ -113,11 +113,11 @@ void Interpreter::ps_div(UGeckoInstruction inst)
|
|||
const auto& a = rPS(inst.FA);
|
||||
const auto& b = rPS(inst.FB);
|
||||
|
||||
const double ps0 = ForceSingle(FPSCR, NI_div(&FPSCR, a.PS0AsDouble(), b.PS0AsDouble()).value);
|
||||
const double ps1 = ForceSingle(FPSCR, NI_div(&FPSCR, a.PS1AsDouble(), b.PS1AsDouble()).value);
|
||||
const float ps0 = ForceSingle(FPSCR, NI_div(&FPSCR, a.PS0AsDouble(), b.PS0AsDouble()).value);
|
||||
const float ps1 = ForceSingle(FPSCR, NI_div(&FPSCR, a.PS1AsDouble(), b.PS1AsDouble()).value);
|
||||
|
||||
rPS(inst.FD).SetBoth(ps0, ps1);
|
||||
PowerPC::UpdateFPRF(ps0);
|
||||
PowerPC::UpdateFPRFSingle(ps0);
|
||||
|
||||
if (inst.Rc)
|
||||
PowerPC::ppcState.UpdateCR1();
|
||||
|
@ -145,7 +145,7 @@ void Interpreter::ps_res(UGeckoInstruction inst)
|
|||
const double ps1 = Common::ApproximateReciprocal(b);
|
||||
|
||||
rPS(inst.FD).SetBoth(ps0, ps1);
|
||||
PowerPC::UpdateFPRF(ps0);
|
||||
PowerPC::UpdateFPRFSingle(ps0);
|
||||
|
||||
if (inst.Rc)
|
||||
PowerPC::ppcState.UpdateCR1();
|
||||
|
@ -174,11 +174,11 @@ void Interpreter::ps_rsqrte(UGeckoInstruction inst)
|
|||
if (Common::IsSNAN(ps0) || Common::IsSNAN(ps1))
|
||||
SetFPException(&FPSCR, FPSCR_VXSNAN);
|
||||
|
||||
const double dst_ps0 = ForceSingle(FPSCR, Common::ApproximateReciprocalSquareRoot(ps0));
|
||||
const double dst_ps1 = ForceSingle(FPSCR, Common::ApproximateReciprocalSquareRoot(ps1));
|
||||
const float dst_ps0 = ForceSingle(FPSCR, Common::ApproximateReciprocalSquareRoot(ps0));
|
||||
const float dst_ps1 = ForceSingle(FPSCR, Common::ApproximateReciprocalSquareRoot(ps1));
|
||||
|
||||
rPS(inst.FD).SetBoth(dst_ps0, dst_ps1);
|
||||
PowerPC::UpdateFPRF(dst_ps0);
|
||||
PowerPC::UpdateFPRFSingle(dst_ps0);
|
||||
|
||||
if (inst.Rc)
|
||||
PowerPC::ppcState.UpdateCR1();
|
||||
|
@ -189,11 +189,11 @@ void Interpreter::ps_sub(UGeckoInstruction inst)
|
|||
const auto& a = rPS(inst.FA);
|
||||
const auto& b = rPS(inst.FB);
|
||||
|
||||
const double ps0 = ForceSingle(FPSCR, NI_sub(&FPSCR, a.PS0AsDouble(), b.PS0AsDouble()).value);
|
||||
const double ps1 = ForceSingle(FPSCR, NI_sub(&FPSCR, a.PS1AsDouble(), b.PS1AsDouble()).value);
|
||||
const float ps0 = ForceSingle(FPSCR, NI_sub(&FPSCR, a.PS0AsDouble(), b.PS0AsDouble()).value);
|
||||
const float ps1 = ForceSingle(FPSCR, NI_sub(&FPSCR, a.PS1AsDouble(), b.PS1AsDouble()).value);
|
||||
|
||||
rPS(inst.FD).SetBoth(ps0, ps1);
|
||||
PowerPC::UpdateFPRF(ps0);
|
||||
PowerPC::UpdateFPRFSingle(ps0);
|
||||
|
||||
if (inst.Rc)
|
||||
PowerPC::ppcState.UpdateCR1();
|
||||
|
@ -204,11 +204,11 @@ void Interpreter::ps_add(UGeckoInstruction inst)
|
|||
const auto& a = rPS(inst.FA);
|
||||
const auto& b = rPS(inst.FB);
|
||||
|
||||
const double ps0 = ForceSingle(FPSCR, NI_add(&FPSCR, a.PS0AsDouble(), b.PS0AsDouble()).value);
|
||||
const double ps1 = ForceSingle(FPSCR, NI_add(&FPSCR, a.PS1AsDouble(), b.PS1AsDouble()).value);
|
||||
const float ps0 = ForceSingle(FPSCR, NI_add(&FPSCR, a.PS0AsDouble(), b.PS0AsDouble()).value);
|
||||
const float ps1 = ForceSingle(FPSCR, NI_add(&FPSCR, a.PS1AsDouble(), b.PS1AsDouble()).value);
|
||||
|
||||
rPS(inst.FD).SetBoth(ps0, ps1);
|
||||
PowerPC::UpdateFPRF(ps0);
|
||||
PowerPC::UpdateFPRFSingle(ps0);
|
||||
|
||||
if (inst.Rc)
|
||||
PowerPC::ppcState.UpdateCR1();
|
||||
|
@ -222,11 +222,11 @@ void Interpreter::ps_mul(UGeckoInstruction inst)
|
|||
const double c0 = Force25Bit(c.PS0AsDouble());
|
||||
const double c1 = Force25Bit(c.PS1AsDouble());
|
||||
|
||||
const double ps0 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS0AsDouble(), c0).value);
|
||||
const double ps1 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS1AsDouble(), c1).value);
|
||||
const float ps0 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS0AsDouble(), c0).value);
|
||||
const float ps1 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS1AsDouble(), c1).value);
|
||||
|
||||
rPS(inst.FD).SetBoth(ps0, ps1);
|
||||
PowerPC::UpdateFPRF(ps0);
|
||||
PowerPC::UpdateFPRFSingle(ps0);
|
||||
|
||||
if (inst.Rc)
|
||||
PowerPC::ppcState.UpdateCR1();
|
||||
|
@ -241,13 +241,11 @@ void Interpreter::ps_msub(UGeckoInstruction inst)
|
|||
const double c0 = Force25Bit(c.PS0AsDouble());
|
||||
const double c1 = Force25Bit(c.PS1AsDouble());
|
||||
|
||||
const double ps0 =
|
||||
ForceSingle(FPSCR, NI_msub(&FPSCR, a.PS0AsDouble(), c0, b.PS0AsDouble()).value);
|
||||
const double ps1 =
|
||||
ForceSingle(FPSCR, NI_msub(&FPSCR, a.PS1AsDouble(), c1, b.PS1AsDouble()).value);
|
||||
const float ps0 = ForceSingle(FPSCR, NI_msub(&FPSCR, a.PS0AsDouble(), c0, b.PS0AsDouble()).value);
|
||||
const float ps1 = ForceSingle(FPSCR, NI_msub(&FPSCR, a.PS1AsDouble(), c1, b.PS1AsDouble()).value);
|
||||
|
||||
rPS(inst.FD).SetBoth(ps0, ps1);
|
||||
PowerPC::UpdateFPRF(ps0);
|
||||
PowerPC::UpdateFPRFSingle(ps0);
|
||||
|
||||
if (inst.Rc)
|
||||
PowerPC::ppcState.UpdateCR1();
|
||||
|
@ -262,13 +260,11 @@ void Interpreter::ps_madd(UGeckoInstruction inst)
|
|||
const double c0 = Force25Bit(c.PS0AsDouble());
|
||||
const double c1 = Force25Bit(c.PS1AsDouble());
|
||||
|
||||
const double ps0 =
|
||||
ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS0AsDouble(), c0, b.PS0AsDouble()).value);
|
||||
const double ps1 =
|
||||
ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS1AsDouble(), c1, b.PS1AsDouble()).value);
|
||||
const float ps0 = ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS0AsDouble(), c0, b.PS0AsDouble()).value);
|
||||
const float ps1 = ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS1AsDouble(), c1, b.PS1AsDouble()).value);
|
||||
|
||||
rPS(inst.FD).SetBoth(ps0, ps1);
|
||||
PowerPC::UpdateFPRF(ps0);
|
||||
PowerPC::UpdateFPRFSingle(ps0);
|
||||
|
||||
if (inst.Rc)
|
||||
PowerPC::ppcState.UpdateCR1();
|
||||
|
@ -283,16 +279,16 @@ void Interpreter::ps_nmsub(UGeckoInstruction inst)
|
|||
const double c0 = Force25Bit(c.PS0AsDouble());
|
||||
const double c1 = Force25Bit(c.PS1AsDouble());
|
||||
|
||||
const double tmp0 =
|
||||
const float tmp0 =
|
||||
ForceSingle(FPSCR, NI_msub(&FPSCR, a.PS0AsDouble(), c0, b.PS0AsDouble()).value);
|
||||
const double tmp1 =
|
||||
const float tmp1 =
|
||||
ForceSingle(FPSCR, NI_msub(&FPSCR, a.PS1AsDouble(), c1, b.PS1AsDouble()).value);
|
||||
|
||||
const double ps0 = std::isnan(tmp0) ? tmp0 : -tmp0;
|
||||
const double ps1 = std::isnan(tmp1) ? tmp1 : -tmp1;
|
||||
const float ps0 = std::isnan(tmp0) ? tmp0 : -tmp0;
|
||||
const float ps1 = std::isnan(tmp1) ? tmp1 : -tmp1;
|
||||
|
||||
rPS(inst.FD).SetBoth(ps0, ps1);
|
||||
PowerPC::UpdateFPRF(ps0);
|
||||
PowerPC::UpdateFPRFSingle(ps0);
|
||||
|
||||
if (inst.Rc)
|
||||
PowerPC::ppcState.UpdateCR1();
|
||||
|
@ -307,16 +303,16 @@ void Interpreter::ps_nmadd(UGeckoInstruction inst)
|
|||
const double c0 = Force25Bit(c.PS0AsDouble());
|
||||
const double c1 = Force25Bit(c.PS1AsDouble());
|
||||
|
||||
const double tmp0 =
|
||||
const float tmp0 =
|
||||
ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS0AsDouble(), c0, b.PS0AsDouble()).value);
|
||||
const double tmp1 =
|
||||
const float tmp1 =
|
||||
ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS1AsDouble(), c1, b.PS1AsDouble()).value);
|
||||
|
||||
const double ps0 = std::isnan(tmp0) ? tmp0 : -tmp0;
|
||||
const double ps1 = std::isnan(tmp1) ? tmp1 : -tmp1;
|
||||
const float ps0 = std::isnan(tmp0) ? tmp0 : -tmp0;
|
||||
const float ps1 = std::isnan(tmp1) ? tmp1 : -tmp1;
|
||||
|
||||
rPS(inst.FD).SetBoth(ps0, ps1);
|
||||
PowerPC::UpdateFPRF(ps0);
|
||||
PowerPC::UpdateFPRFSingle(ps0);
|
||||
|
||||
if (inst.Rc)
|
||||
PowerPC::ppcState.UpdateCR1();
|
||||
|
@ -328,11 +324,11 @@ void Interpreter::ps_sum0(UGeckoInstruction inst)
|
|||
const auto& b = rPS(inst.FB);
|
||||
const auto& c = rPS(inst.FC);
|
||||
|
||||
const double ps0 = ForceSingle(FPSCR, NI_add(&FPSCR, a.PS0AsDouble(), b.PS1AsDouble()).value);
|
||||
const double ps1 = ForceSingle(FPSCR, c.PS1AsDouble());
|
||||
const float ps0 = ForceSingle(FPSCR, NI_add(&FPSCR, a.PS0AsDouble(), b.PS1AsDouble()).value);
|
||||
const float ps1 = ForceSingle(FPSCR, c.PS1AsDouble());
|
||||
|
||||
rPS(inst.FD).SetBoth(ps0, ps1);
|
||||
PowerPC::UpdateFPRF(ps0);
|
||||
PowerPC::UpdateFPRFSingle(ps0);
|
||||
|
||||
if (inst.Rc)
|
||||
PowerPC::ppcState.UpdateCR1();
|
||||
|
@ -344,11 +340,11 @@ void Interpreter::ps_sum1(UGeckoInstruction inst)
|
|||
const auto& b = rPS(inst.FB);
|
||||
const auto& c = rPS(inst.FC);
|
||||
|
||||
const double ps0 = ForceSingle(FPSCR, c.PS0AsDouble());
|
||||
const double ps1 = ForceSingle(FPSCR, NI_add(&FPSCR, a.PS0AsDouble(), b.PS1AsDouble()).value);
|
||||
const float ps0 = ForceSingle(FPSCR, c.PS0AsDouble());
|
||||
const float ps1 = ForceSingle(FPSCR, NI_add(&FPSCR, a.PS0AsDouble(), b.PS1AsDouble()).value);
|
||||
|
||||
rPS(inst.FD).SetBoth(ps0, ps1);
|
||||
PowerPC::UpdateFPRF(ps1);
|
||||
PowerPC::UpdateFPRFSingle(ps1);
|
||||
|
||||
if (inst.Rc)
|
||||
PowerPC::ppcState.UpdateCR1();
|
||||
|
@ -360,11 +356,11 @@ void Interpreter::ps_muls0(UGeckoInstruction inst)
|
|||
const auto& c = rPS(inst.FC);
|
||||
|
||||
const double c0 = Force25Bit(c.PS0AsDouble());
|
||||
const double ps0 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS0AsDouble(), c0).value);
|
||||
const double ps1 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS1AsDouble(), c0).value);
|
||||
const float ps0 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS0AsDouble(), c0).value);
|
||||
const float ps1 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS1AsDouble(), c0).value);
|
||||
|
||||
rPS(inst.FD).SetBoth(ps0, ps1);
|
||||
PowerPC::UpdateFPRF(ps0);
|
||||
PowerPC::UpdateFPRFSingle(ps0);
|
||||
|
||||
if (inst.Rc)
|
||||
PowerPC::ppcState.UpdateCR1();
|
||||
|
@ -376,11 +372,11 @@ void Interpreter::ps_muls1(UGeckoInstruction inst)
|
|||
const auto& c = rPS(inst.FC);
|
||||
|
||||
const double c1 = Force25Bit(c.PS1AsDouble());
|
||||
const double ps0 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS0AsDouble(), c1).value);
|
||||
const double ps1 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS1AsDouble(), c1).value);
|
||||
const float ps0 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS0AsDouble(), c1).value);
|
||||
const float ps1 = ForceSingle(FPSCR, NI_mul(&FPSCR, a.PS1AsDouble(), c1).value);
|
||||
|
||||
rPS(inst.FD).SetBoth(ps0, ps1);
|
||||
PowerPC::UpdateFPRF(ps0);
|
||||
PowerPC::UpdateFPRFSingle(ps0);
|
||||
|
||||
if (inst.Rc)
|
||||
PowerPC::ppcState.UpdateCR1();
|
||||
|
@ -393,13 +389,11 @@ void Interpreter::ps_madds0(UGeckoInstruction inst)
|
|||
const auto& c = rPS(inst.FC);
|
||||
|
||||
const double c0 = Force25Bit(c.PS0AsDouble());
|
||||
const double ps0 =
|
||||
ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS0AsDouble(), c0, b.PS0AsDouble()).value);
|
||||
const double ps1 =
|
||||
ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS1AsDouble(), c0, b.PS1AsDouble()).value);
|
||||
const float ps0 = ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS0AsDouble(), c0, b.PS0AsDouble()).value);
|
||||
const float ps1 = ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS1AsDouble(), c0, b.PS1AsDouble()).value);
|
||||
|
||||
rPS(inst.FD).SetBoth(ps0, ps1);
|
||||
PowerPC::UpdateFPRF(ps0);
|
||||
PowerPC::UpdateFPRFSingle(ps0);
|
||||
|
||||
if (inst.Rc)
|
||||
PowerPC::ppcState.UpdateCR1();
|
||||
|
@ -412,13 +406,11 @@ void Interpreter::ps_madds1(UGeckoInstruction inst)
|
|||
const auto& c = rPS(inst.FC);
|
||||
|
||||
const double c1 = Force25Bit(c.PS1AsDouble());
|
||||
const double ps0 =
|
||||
ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS0AsDouble(), c1, b.PS0AsDouble()).value);
|
||||
const double ps1 =
|
||||
ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS1AsDouble(), c1, b.PS1AsDouble()).value);
|
||||
const float ps0 = ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS0AsDouble(), c1, b.PS0AsDouble()).value);
|
||||
const float ps1 = ForceSingle(FPSCR, NI_madd(&FPSCR, a.PS1AsDouble(), c1, b.PS1AsDouble()).value);
|
||||
|
||||
rPS(inst.FD).SetBoth(ps0, ps1);
|
||||
PowerPC::UpdateFPRF(ps0);
|
||||
PowerPC::UpdateFPRFSingle(ps0);
|
||||
|
||||
if (inst.Rc)
|
||||
PowerPC::ppcState.UpdateCR1();
|
||||
|
|
|
@ -121,8 +121,11 @@ public:
|
|||
// Generates a branch that will check if a given bit of a CR register part
|
||||
// is set or not.
|
||||
Gen::FixupBranch JumpIfCRFieldBit(int field, int bit, bool jump_if_set = true);
|
||||
void SetFPRFIfNeeded(Gen::X64Reg xmm);
|
||||
|
||||
void SetFPRFIfNeeded(const Gen::OpArg& xmm, bool single);
|
||||
void FinalizeSingleResult(Gen::X64Reg output, const Gen::OpArg& input, bool packed = true,
|
||||
bool duplicate = false);
|
||||
void FinalizeDoubleResult(Gen::X64Reg output, const Gen::OpArg& input);
|
||||
void HandleNaNs(UGeckoInstruction inst, Gen::X64Reg xmm_out, Gen::X64Reg xmm_in,
|
||||
Gen::X64Reg clobber = Gen::XMM0);
|
||||
|
||||
|
|
|
@ -33,13 +33,63 @@ alignas(16) static const double half_qnan_and_s32_max[2] = {0x7FFFFFFF, -0x80000
|
|||
// We can avoid calculating FPRF if it's not needed; every float operation resets it, so
|
||||
// if it's going to be clobbered in a future instruction before being read, we can just
|
||||
// not calculate it.
|
||||
void Jit64::SetFPRFIfNeeded(X64Reg xmm)
|
||||
void Jit64::SetFPRFIfNeeded(const OpArg& input, bool single)
|
||||
{
|
||||
// As far as we know, the games that use this flag only need FPRF for fmul and fmadd, but
|
||||
// FPRF is fast enough in JIT that we might as well just enable it for every float instruction
|
||||
// if the FPRF flag is set.
|
||||
if (SConfig::GetInstance().bFPRF && js.op->wantsFPRF)
|
||||
SetFPRF(xmm);
|
||||
if (!SConfig::GetInstance().bFPRF || !js.op->wantsFPRF)
|
||||
return;
|
||||
|
||||
X64Reg xmm = XMM0;
|
||||
if (input.IsSimpleReg())
|
||||
xmm = input.GetSimpleReg();
|
||||
else
|
||||
MOVSD(xmm, input);
|
||||
|
||||
SetFPRF(xmm, single);
|
||||
}
|
||||
|
||||
void Jit64::FinalizeSingleResult(X64Reg output, const OpArg& input, bool packed, bool duplicate)
|
||||
{
|
||||
// Most games don't need these. Zelda requires it though - some platforms get stuck without them.
|
||||
if (jo.accurateSinglePrecision)
|
||||
{
|
||||
if (packed)
|
||||
{
|
||||
CVTPD2PS(output, input);
|
||||
SetFPRFIfNeeded(R(output), true);
|
||||
CVTPS2PD(output, R(output));
|
||||
}
|
||||
else
|
||||
{
|
||||
CVTSD2SS(output, input);
|
||||
SetFPRFIfNeeded(R(output), true);
|
||||
CVTSS2SD(output, R(output));
|
||||
if (duplicate)
|
||||
MOVDDUP(output, R(output));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!input.IsSimpleReg(output))
|
||||
{
|
||||
if (duplicate)
|
||||
MOVDDUP(output, input);
|
||||
else
|
||||
MOVAPD(output, input);
|
||||
}
|
||||
|
||||
SetFPRFIfNeeded(input, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Jit64::FinalizeDoubleResult(X64Reg output, const OpArg& input)
|
||||
{
|
||||
if (!input.IsSimpleReg(output))
|
||||
MOVSD(output, input);
|
||||
|
||||
SetFPRFIfNeeded(input, false);
|
||||
}
|
||||
|
||||
void Jit64::HandleNaNs(UGeckoInstruction inst, X64Reg xmm_out, X64Reg xmm, X64Reg clobber)
|
||||
|
@ -210,8 +260,9 @@ void Jit64::fp_arith(UGeckoInstruction inst)
|
|||
|
||||
HandleNaNs(inst, Rd, dest);
|
||||
if (single)
|
||||
ForceSinglePrecision(Rd, Rd, packed, true);
|
||||
SetFPRFIfNeeded(Rd);
|
||||
FinalizeSingleResult(Rd, Rd, packed, true);
|
||||
else
|
||||
FinalizeDoubleResult(Rd, Rd);
|
||||
};
|
||||
|
||||
switch (inst.SUBOP5)
|
||||
|
@ -452,14 +503,13 @@ void Jit64::fmaddXX(UGeckoInstruction inst)
|
|||
if (single)
|
||||
{
|
||||
HandleNaNs(inst, result_reg, result_reg, result_reg == XMM1 ? XMM0 : XMM1);
|
||||
ForceSinglePrecision(Rd, R(result_reg), packed, true);
|
||||
FinalizeSingleResult(Rd, R(result_reg), packed, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleNaNs(inst, result_reg, result_reg, XMM1);
|
||||
MOVSD(Rd, R(result_reg));
|
||||
FinalizeDoubleResult(Rd, R(result_reg));
|
||||
}
|
||||
SetFPRFIfNeeded(Rd);
|
||||
}
|
||||
|
||||
void Jit64::fsign(UGeckoInstruction inst)
|
||||
|
@ -763,12 +813,11 @@ void Jit64::frspx(UGeckoInstruction inst)
|
|||
int d = inst.FD;
|
||||
bool packed = js.op->fprIsDuplicated[b] && !cpu_info.bAtom;
|
||||
|
||||
RCOpArg Rb = fpr.Use(b, RCMode::Read);
|
||||
RCOpArg Rb = fpr.Bind(b, RCMode::Read);
|
||||
RCX64Reg Rd = fpr.Bind(d, RCMode::Write);
|
||||
RegCache::Realize(Rb, Rd);
|
||||
|
||||
ForceSinglePrecision(Rd, Rb, packed, true);
|
||||
SetFPRFIfNeeded(Rd);
|
||||
FinalizeSingleResult(Rd, Rb, packed, true);
|
||||
}
|
||||
|
||||
void Jit64::frsqrtex(UGeckoInstruction inst)
|
||||
|
@ -786,8 +835,7 @@ void Jit64::frsqrtex(UGeckoInstruction inst)
|
|||
|
||||
MOVAPD(XMM0, Rb);
|
||||
CALL(asm_routines.frsqrte);
|
||||
MOVSD(Rd, XMM0);
|
||||
SetFPRFIfNeeded(Rd);
|
||||
FinalizeDoubleResult(Rd, R(XMM0));
|
||||
}
|
||||
|
||||
void Jit64::fresx(UGeckoInstruction inst)
|
||||
|
@ -806,5 +854,5 @@ void Jit64::fresx(UGeckoInstruction inst)
|
|||
MOVAPD(XMM0, Rb);
|
||||
CALL(asm_routines.fres);
|
||||
MOVDDUP(Rd, R(XMM0));
|
||||
SetFPRFIfNeeded(Rd);
|
||||
SetFPRFIfNeeded(R(XMM0), true);
|
||||
}
|
||||
|
|
|
@ -77,8 +77,7 @@ void Jit64::ps_sum(UGeckoInstruction inst)
|
|||
PanicAlertFmt("ps_sum WTF!!!");
|
||||
}
|
||||
HandleNaNs(inst, Rd, tmp, tmp == XMM1 ? XMM0 : XMM1);
|
||||
ForceSinglePrecision(Rd, Rd);
|
||||
SetFPRFIfNeeded(Rd);
|
||||
FinalizeSingleResult(Rd, Rd);
|
||||
}
|
||||
|
||||
void Jit64::ps_muls(UGeckoInstruction inst)
|
||||
|
@ -112,8 +111,7 @@ void Jit64::ps_muls(UGeckoInstruction inst)
|
|||
Force25BitPrecision(XMM1, R(XMM1), XMM0);
|
||||
MULPD(XMM1, Ra);
|
||||
HandleNaNs(inst, Rd, XMM1);
|
||||
ForceSinglePrecision(Rd, Rd);
|
||||
SetFPRFIfNeeded(Rd);
|
||||
FinalizeSingleResult(Rd, Rd);
|
||||
}
|
||||
|
||||
void Jit64::ps_mergeXX(UGeckoInstruction inst)
|
||||
|
@ -171,8 +169,7 @@ void Jit64::ps_rsqrte(UGeckoInstruction inst)
|
|||
CALL(asm_routines.frsqrte);
|
||||
MOVLHPS(Rd, XMM0);
|
||||
|
||||
ForceSinglePrecision(Rd, Rd);
|
||||
SetFPRFIfNeeded(Rd);
|
||||
FinalizeSingleResult(Rd, Rd);
|
||||
}
|
||||
|
||||
void Jit64::ps_res(UGeckoInstruction inst)
|
||||
|
@ -196,8 +193,7 @@ void Jit64::ps_res(UGeckoInstruction inst)
|
|||
CALL(asm_routines.fres);
|
||||
MOVLHPS(Rd, XMM0);
|
||||
|
||||
ForceSinglePrecision(Rd, Rd);
|
||||
SetFPRFIfNeeded(Rd);
|
||||
FinalizeSingleResult(Rd, Rd);
|
||||
}
|
||||
|
||||
void Jit64::ps_cmpXX(UGeckoInstruction inst)
|
||||
|
|
|
@ -727,34 +727,6 @@ void EmuCodeBlock::JitClearCA()
|
|||
MOV(8, PPCSTATE(xer_ca), Imm8(0));
|
||||
}
|
||||
|
||||
void EmuCodeBlock::ForceSinglePrecision(X64Reg output, const OpArg& input, bool packed,
|
||||
bool duplicate)
|
||||
{
|
||||
// Most games don't need these. Zelda requires it though - some platforms get stuck without them.
|
||||
if (m_jit.jo.accurateSinglePrecision)
|
||||
{
|
||||
if (packed)
|
||||
{
|
||||
CVTPD2PS(output, input);
|
||||
CVTPS2PD(output, R(output));
|
||||
}
|
||||
else
|
||||
{
|
||||
CVTSD2SS(output, input);
|
||||
CVTSS2SD(output, R(output));
|
||||
if (duplicate)
|
||||
MOVDDUP(output, R(output));
|
||||
}
|
||||
}
|
||||
else if (!input.IsSimpleReg(output))
|
||||
{
|
||||
if (duplicate)
|
||||
MOVDDUP(output, input);
|
||||
else
|
||||
MOVAPD(output, input);
|
||||
}
|
||||
}
|
||||
|
||||
// Abstract between AVX and SSE: automatically handle 3-operand instructions
|
||||
void EmuCodeBlock::avx_op(void (XEmitter::*avxOp)(X64Reg, X64Reg, const OpArg&),
|
||||
void (XEmitter::*sseOp)(X64Reg, const OpArg&), X64Reg regOp,
|
||||
|
@ -907,30 +879,35 @@ void EmuCodeBlock::ConvertSingleToDouble(X64Reg dst, X64Reg src, bool src_is_gpr
|
|||
MOVDDUP(dst, R(dst));
|
||||
}
|
||||
|
||||
alignas(16) static const u64 psDoubleExp[2] = {0x7FF0000000000000ULL, 0};
|
||||
alignas(16) static const u64 psDoubleFrac[2] = {0x000FFFFFFFFFFFFFULL, 0};
|
||||
alignas(16) static const u64 psDoubleNoSign[2] = {0x7FFFFFFFFFFFFFFFULL, 0};
|
||||
alignas(16) static const u64 psDoubleExp[2] = {Common::DOUBLE_EXP, 0};
|
||||
alignas(16) static const u64 psDoubleFrac[2] = {Common::DOUBLE_FRAC, 0};
|
||||
alignas(16) static const u64 psDoubleNoSign[2] = {~Common::DOUBLE_SIGN, 0};
|
||||
|
||||
alignas(16) static const u32 psFloatExp[4] = {Common::FLOAT_EXP, 0, 0, 0};
|
||||
alignas(16) static const u32 psFloatFrac[4] = {Common::FLOAT_FRAC, 0, 0, 0};
|
||||
alignas(16) static const u32 psFloatNoSign[4] = {~Common::FLOAT_SIGN, 0, 0, 0};
|
||||
|
||||
// TODO: it might be faster to handle FPRF in the same way as CR is currently handled for integer,
|
||||
// storing
|
||||
// the result of each floating point op and calculating it when needed. This is trickier than for
|
||||
// integers
|
||||
// though, because there's 32 possible FPRF bit combinations but only 9 categories of floating point
|
||||
// values,
|
||||
// which makes the whole thing rather trickier.
|
||||
// Fortunately, PPCAnalyzer can optimize out a large portion of FPRF calculations, so maybe this
|
||||
// isn't
|
||||
// quite that necessary.
|
||||
void EmuCodeBlock::SetFPRF(Gen::X64Reg xmm)
|
||||
// storing the result of each floating point op and calculating it when needed. This is trickier
|
||||
// than for integers though, because there's 32 possible FPRF bit combinations but only 9 categories
|
||||
// of floating point values. Fortunately, PPCAnalyzer can optimize out a large portion of FPRF
|
||||
// calculations, so maybe this isn't quite that necessary.
|
||||
void EmuCodeBlock::SetFPRF(Gen::X64Reg xmm, bool single)
|
||||
{
|
||||
const int input_size = single ? 32 : 64;
|
||||
|
||||
AND(32, PPCSTATE(fpscr), Imm32(~FPRF_MASK));
|
||||
|
||||
FixupBranch continue1, continue2, continue3, continue4;
|
||||
if (cpu_info.bSSE4_1)
|
||||
{
|
||||
MOVQ_xmm(R(RSCRATCH), xmm);
|
||||
SHR(64, R(RSCRATCH), Imm8(63)); // Get the sign bit; almost all the branches need it.
|
||||
PTEST(xmm, MConst(psDoubleExp));
|
||||
// Get the sign bit; almost all the branches need it.
|
||||
SHR(input_size, R(RSCRATCH), Imm8(input_size - 1));
|
||||
if (single)
|
||||
PTEST(xmm, MConst(psFloatExp));
|
||||
else
|
||||
PTEST(xmm, MConst(psDoubleExp));
|
||||
FixupBranch maxExponent = J_CC(CC_C);
|
||||
FixupBranch zeroExponent = J_CC(CC_Z);
|
||||
|
||||
|
@ -940,7 +917,10 @@ void EmuCodeBlock::SetFPRF(Gen::X64Reg xmm)
|
|||
continue1 = J();
|
||||
|
||||
SetJumpTarget(maxExponent);
|
||||
PTEST(xmm, MConst(psDoubleFrac));
|
||||
if (single)
|
||||
PTEST(xmm, MConst(psFloatFrac));
|
||||
else
|
||||
PTEST(xmm, MConst(psDoubleFrac));
|
||||
FixupBranch notNAN = J_CC(CC_Z);
|
||||
|
||||
// Max exponent + mantissa: PPC_FPCLASS_QNAN
|
||||
|
@ -955,7 +935,10 @@ void EmuCodeBlock::SetFPRF(Gen::X64Reg xmm)
|
|||
continue3 = J();
|
||||
|
||||
SetJumpTarget(zeroExponent);
|
||||
PTEST(xmm, MConst(psDoubleNoSign));
|
||||
if (single)
|
||||
PTEST(xmm, MConst(psFloatNoSign));
|
||||
else
|
||||
PTEST(xmm, MConst(psDoubleNoSign));
|
||||
FixupBranch zero = J_CC(CC_Z);
|
||||
|
||||
// No exponent + mantissa: sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD;
|
||||
|
@ -971,37 +954,58 @@ void EmuCodeBlock::SetFPRF(Gen::X64Reg xmm)
|
|||
else
|
||||
{
|
||||
MOVQ_xmm(R(RSCRATCH), xmm);
|
||||
TEST(64, R(RSCRATCH), MConst(psDoubleExp));
|
||||
if (single)
|
||||
TEST(32, R(RSCRATCH), Imm32(Common::FLOAT_EXP));
|
||||
else
|
||||
TEST(64, R(RSCRATCH), MConst(psDoubleExp));
|
||||
FixupBranch zeroExponent = J_CC(CC_Z);
|
||||
AND(64, R(RSCRATCH), MConst(psDoubleNoSign));
|
||||
CMP(64, R(RSCRATCH), MConst(psDoubleExp));
|
||||
|
||||
if (single)
|
||||
{
|
||||
AND(32, R(RSCRATCH), Imm32(~Common::FLOAT_SIGN));
|
||||
CMP(32, R(RSCRATCH), Imm32(Common::FLOAT_EXP));
|
||||
}
|
||||
else
|
||||
{
|
||||
AND(64, R(RSCRATCH), MConst(psDoubleNoSign));
|
||||
CMP(64, R(RSCRATCH), MConst(psDoubleExp));
|
||||
}
|
||||
FixupBranch nan =
|
||||
J_CC(CC_G); // This works because if the sign bit is set, RSCRATCH is negative
|
||||
FixupBranch infinity = J_CC(CC_E);
|
||||
|
||||
MOVQ_xmm(R(RSCRATCH), xmm);
|
||||
SHR(64, R(RSCRATCH), Imm8(63));
|
||||
SHR(input_size, R(RSCRATCH), Imm8(input_size - 1));
|
||||
LEA(32, RSCRATCH,
|
||||
MScaled(RSCRATCH, Common::PPC_FPCLASS_NN - Common::PPC_FPCLASS_PN, Common::PPC_FPCLASS_PN));
|
||||
continue1 = J();
|
||||
|
||||
SetJumpTarget(nan);
|
||||
MOV(32, R(RSCRATCH), Imm32(Common::PPC_FPCLASS_QNAN));
|
||||
continue2 = J();
|
||||
|
||||
SetJumpTarget(infinity);
|
||||
MOVQ_xmm(R(RSCRATCH), xmm);
|
||||
SHR(64, R(RSCRATCH), Imm8(63));
|
||||
SHR(input_size, R(RSCRATCH), Imm8(input_size - 1));
|
||||
LEA(32, RSCRATCH,
|
||||
MScaled(RSCRATCH, Common::PPC_FPCLASS_NINF - Common::PPC_FPCLASS_PINF,
|
||||
Common::PPC_FPCLASS_PINF));
|
||||
continue3 = J();
|
||||
|
||||
SetJumpTarget(zeroExponent);
|
||||
TEST(64, R(RSCRATCH), MConst(psDoubleNoSign));
|
||||
if (single)
|
||||
TEST(input_size, R(RSCRATCH), Imm32(~Common::FLOAT_SIGN));
|
||||
else
|
||||
TEST(input_size, R(RSCRATCH), MConst(psDoubleNoSign));
|
||||
FixupBranch zero = J_CC(CC_Z);
|
||||
SHR(64, R(RSCRATCH), Imm8(63));
|
||||
|
||||
SHR(input_size, R(RSCRATCH), Imm8(input_size - 1));
|
||||
LEA(32, RSCRATCH,
|
||||
MScaled(RSCRATCH, Common::PPC_FPCLASS_ND - Common::PPC_FPCLASS_PD, Common::PPC_FPCLASS_PD));
|
||||
continue4 = J();
|
||||
|
||||
SetJumpTarget(zero);
|
||||
SHR(64, R(RSCRATCH), Imm8(63));
|
||||
SHR(input_size, R(RSCRATCH), Imm8(input_size - 1));
|
||||
SHL(32, R(RSCRATCH), Imm8(4));
|
||||
ADD(32, R(RSCRATCH), Imm8(Common::PPC_FPCLASS_PZ));
|
||||
}
|
||||
|
|
|
@ -117,14 +117,12 @@ public:
|
|||
void (Gen::XEmitter::*sseOp)(Gen::X64Reg, const Gen::OpArg&, u8), Gen::X64Reg regOp,
|
||||
const Gen::OpArg& arg1, const Gen::OpArg& arg2, u8 imm);
|
||||
|
||||
void ForceSinglePrecision(Gen::X64Reg output, const Gen::OpArg& input, bool packed = true,
|
||||
bool duplicate = false);
|
||||
void Force25BitPrecision(Gen::X64Reg output, const Gen::OpArg& input, Gen::X64Reg tmp);
|
||||
|
||||
// RSCRATCH might get trashed
|
||||
void ConvertSingleToDouble(Gen::X64Reg dst, Gen::X64Reg src, bool src_is_gpr = false);
|
||||
void ConvertDoubleToSingle(Gen::X64Reg dst, Gen::X64Reg src);
|
||||
void SetFPRF(Gen::X64Reg xmm);
|
||||
void SetFPRF(Gen::X64Reg xmm, bool single);
|
||||
void Clear();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -455,25 +455,12 @@ void JitArm64::GenerateFPRF(bool single)
|
|||
FixupBranch nan_or_inf = B(CCFlags::CC_EQ);
|
||||
|
||||
// exp != 0 && exp != EXP_MASK
|
||||
const u8* normal = GetCodePtr();
|
||||
emit_write_fprf_and_ret();
|
||||
|
||||
// exp == 0
|
||||
SetJumpTarget(zero_or_denormal);
|
||||
TSTI2R(input_reg, INPUT_FRAC_MASK);
|
||||
FixupBranch denormal;
|
||||
if (single)
|
||||
{
|
||||
// To match the interpreter, what we output should be based on how the input would be classified
|
||||
// after conversion to double. Converting a denormal single to a double always results in a
|
||||
// normal double, so for denormal singles we need to output PPC_FPCLASS_PN/PPC_FPCLASS_NN.
|
||||
// TODO: Hardware test that the interpreter actually is correct.
|
||||
B(CCFlags::CC_NEQ, normal);
|
||||
}
|
||||
else
|
||||
{
|
||||
denormal = B(CCFlags::CC_NEQ);
|
||||
}
|
||||
FixupBranch denormal = B(CCFlags::CC_NEQ);
|
||||
|
||||
// exp == 0 && frac == 0
|
||||
LSR(ARM64Reg::W1, fprf_reg, 3);
|
||||
|
@ -483,8 +470,7 @@ void JitArm64::GenerateFPRF(bool single)
|
|||
emit_write_fprf_and_ret();
|
||||
|
||||
// exp == 0 && frac != 0
|
||||
if (!single)
|
||||
SetJumpTarget(denormal);
|
||||
SetJumpTarget(denormal);
|
||||
ORRI2R(fprf_reg, fprf_reg, Common::PPC_FPCLASS_PD & ~OUTPUT_SIGN_MASK);
|
||||
B(write_fprf_and_ret);
|
||||
|
||||
|
|
|
@ -626,11 +626,16 @@ void PowerPCState::SetSR(u32 index, u32 value)
|
|||
|
||||
// FPSCR update functions
|
||||
|
||||
void UpdateFPRF(double dvalue)
|
||||
void UpdateFPRFDouble(double dvalue)
|
||||
{
|
||||
FPSCR.FPRF = Common::ClassifyDouble(dvalue);
|
||||
}
|
||||
|
||||
void UpdateFPRFSingle(float fvalue)
|
||||
{
|
||||
FPSCR.FPRF = Common::ClassifyFloat(fvalue);
|
||||
}
|
||||
|
||||
void RoundingModeUpdated()
|
||||
{
|
||||
// The rounding mode is separate for each thread, so this must run on the CPU thread
|
||||
|
|
|
@ -304,7 +304,8 @@ inline void SetXER_OV(bool value)
|
|||
SetXER_SO(value);
|
||||
}
|
||||
|
||||
void UpdateFPRF(double dvalue);
|
||||
void UpdateFPRFDouble(double dvalue);
|
||||
void UpdateFPRFSingle(float fvalue);
|
||||
|
||||
void RoundingModeUpdated();
|
||||
|
||||
|
|
|
@ -74,14 +74,14 @@ TEST(JitArm64, FPRF)
|
|||
for (const u64 double_input : double_test_values)
|
||||
{
|
||||
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); });
|
||||
EXPECT_EQ(expected_double, actual_double);
|
||||
|
||||
const u32 single_input = ConvertToSingle(double_input);
|
||||
|
||||
const u32 expected_single = RunUpdateFPRF(
|
||||
[&] { PowerPC::UpdateFPRF(Common::BitCast<double>(ConvertToDouble(single_input))); });
|
||||
const u32 expected_single =
|
||||
RunUpdateFPRF([&] { PowerPC::UpdateFPRFSingle(Common::BitCast<float>(single_input)); });
|
||||
const u32 actual_single = RunUpdateFPRF([&] { test.fprf_single(single_input); });
|
||||
EXPECT_EQ(expected_single, actual_single);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue