From 468efb7243a50652bb17f68cd384523faa90f2ef Mon Sep 17 00:00:00 2001 From: Lioncash Date: Sat, 2 Jun 2018 20:13:30 -0400 Subject: [PATCH 1/5] Interpreter_FPUtils: Unset FPSCR.FI and FPSCR.FR if an invalid operation occurs in NI_* functions If an invalid operation occurs, FI and FR bits are defined to be cleared to zero for arithmetic operations. --- .../PowerPC/Interpreter/Interpreter_FPUtils.h | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h index abc798d220..666ca2bffb 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h @@ -103,7 +103,11 @@ inline double NI_mul(double a, double b) if (std::isnan(t)) { if (Common::IsSNAN(a) || Common::IsSNAN(b)) + { SetFPException(FPSCR_VXSNAN); + FPSCR.FI = 0; + FPSCR.FR = 0; + } if (std::isnan(a)) return MakeQuiet(a); @@ -111,6 +115,8 @@ inline double NI_mul(double a, double b) return MakeQuiet(b); SetFPException(FPSCR_VXIMZ); + FPSCR.FI = 0; + FPSCR.FR = 0; return PPC_NAN; } return t; @@ -123,7 +129,11 @@ inline double NI_div(double a, double b) if (std::isnan(t)) { if (Common::IsSNAN(a) || Common::IsSNAN(b)) + { SetFPException(FPSCR_VXSNAN); + FPSCR.FI = 0; + FPSCR.FR = 0; + } if (std::isnan(a)) return MakeQuiet(a); @@ -135,6 +145,8 @@ inline double NI_div(double a, double b) if (a == 0.0) { SetFPException(FPSCR_VXZDZ); + FPSCR.FI = 0; + FPSCR.FR = 0; } else { @@ -146,6 +158,8 @@ inline double NI_div(double a, double b) else if (std::isinf(a) && std::isinf(b)) { SetFPException(FPSCR_VXIDI); + FPSCR.FI = 0; + FPSCR.FR = 0; } return PPC_NAN; @@ -161,7 +175,11 @@ inline double NI_add(double a, double b) if (std::isnan(t)) { if (Common::IsSNAN(a) || Common::IsSNAN(b)) + { SetFPException(FPSCR_VXSNAN); + FPSCR.FI = 0; + FPSCR.FR = 0; + } if (std::isnan(a)) return MakeQuiet(a); @@ -169,6 +187,8 @@ inline double NI_add(double a, double b) return MakeQuiet(b); SetFPException(FPSCR_VXISI); + FPSCR.FI = 0; + FPSCR.FR = 0; return PPC_NAN; } @@ -182,7 +202,11 @@ inline double NI_sub(double a, double b) if (std::isnan(t)) { if (Common::IsSNAN(a) || Common::IsSNAN(b)) + { SetFPException(FPSCR_VXSNAN); + FPSCR.FI = 0; + FPSCR.FR = 0; + } if (std::isnan(a)) return MakeQuiet(a); @@ -190,6 +214,8 @@ inline double NI_sub(double a, double b) return MakeQuiet(b); SetFPException(FPSCR_VXISI); + FPSCR.FI = 0; + FPSCR.FR = 0; return PPC_NAN; } @@ -206,7 +232,11 @@ inline double NI_madd(double a, double c, double b) if (std::isnan(t)) { if (Common::IsSNAN(a) || Common::IsSNAN(b) || Common::IsSNAN(c)) + { SetFPException(FPSCR_VXSNAN); + FPSCR.FI = 0; + FPSCR.FR = 0; + } if (std::isnan(a)) return MakeQuiet(a); @@ -216,6 +246,8 @@ inline double NI_madd(double a, double c, double b) return MakeQuiet(c); SetFPException(FPSCR_VXIMZ); + FPSCR.FI = 0; + FPSCR.FR = 0; return PPC_NAN; } @@ -224,12 +256,18 @@ inline double NI_madd(double a, double c, double b) if (std::isnan(t)) { if (Common::IsSNAN(b)) + { SetFPException(FPSCR_VXSNAN); + FPSCR.FI = 0; + FPSCR.FR = 0; + } if (std::isnan(b)) return MakeQuiet(b); SetFPException(FPSCR_VXISI); + FPSCR.FI = 0; + FPSCR.FR = 0; return PPC_NAN; } @@ -243,7 +281,11 @@ inline double NI_msub(double a, double c, double b) if (std::isnan(t)) { if (Common::IsSNAN(a) || Common::IsSNAN(b) || Common::IsSNAN(c)) + { SetFPException(FPSCR_VXSNAN); + FPSCR.FI = 0; + FPSCR.FR = 0; + } if (std::isnan(a)) return MakeQuiet(a); @@ -253,6 +295,8 @@ inline double NI_msub(double a, double c, double b) return MakeQuiet(c); SetFPException(FPSCR_VXIMZ); + FPSCR.FI = 0; + FPSCR.FR = 0; return PPC_NAN; } @@ -261,12 +305,18 @@ inline double NI_msub(double a, double c, double b) if (std::isnan(t)) { if (Common::IsSNAN(b)) + { SetFPException(FPSCR_VXSNAN); + FPSCR.FI = 0; + FPSCR.FR = 0; + } if (std::isnan(b)) return MakeQuiet(b); SetFPException(FPSCR_VXISI); + FPSCR.FI = 0; + FPSCR.FR = 0; return PPC_NAN; } From 83774f72adbdf48f51afe1891ea2c6c4d4c5a65c Mon Sep 17 00:00:00 2001 From: Lioncash Date: Sat, 2 Jun 2018 20:26:18 -0400 Subject: [PATCH 2/5] Interpreter_FloatingPoint: Unset FPSCR.FI and FPSCR.FR if a division by zero exception occurs in fres and frsqrte Within the programming environments manual, part of the behavior of a zero divide exception condition is that FI and FR be cleared. --- .../Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp index 3782c748a5..acf7c93afb 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp @@ -397,6 +397,8 @@ void Interpreter::fresx(UGeckoInstruction inst) if (b == 0.0) { SetFPException(FPSCR_ZX); + FPSCR.FI = 0; + FPSCR.FR = 0; if (FPSCR.ZE == 0) compute_result(b); @@ -441,6 +443,8 @@ void Interpreter::frsqrtex(UGeckoInstruction inst) else if (b == 0.0) { SetFPException(FPSCR_ZX); + FPSCR.FI = 0; + FPSCR.FR = 0; if (FPSCR.ZE == 0) compute_result(b); From d05c2ef90dc2ae94d21aac5d61960dcb50160c35 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Sat, 2 Jun 2018 20:42:45 -0400 Subject: [PATCH 3/5] Interpreter_Paired: Unset FPSCR.FI and FR in ps_res and ps_frsqrte in exceptional cases If invalid operation exceptions or zero divide exceptions occur in either of these instructions, FI and FR are supposed to be unset. --- Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp index 6a84132c96..ce4dfd9568 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp @@ -121,6 +121,8 @@ void Interpreter::ps_res(UGeckoInstruction inst) if (a == 0.0 || b == 0.0) { SetFPException(FPSCR_ZX); + FPSCR.FI = 0; + FPSCR.FR = 0; } rPS0(inst.FD) = Common::ApproximateReciprocal(a); @@ -136,11 +138,15 @@ void Interpreter::ps_rsqrte(UGeckoInstruction inst) if (rPS0(inst.FB) == 0.0 || rPS1(inst.FB) == 0.0) { SetFPException(FPSCR_ZX); + FPSCR.FI = 0; + FPSCR.FR = 0; } if (rPS0(inst.FB) < 0.0 || rPS1(inst.FB) < 0.0) { SetFPException(FPSCR_VXSQRT); + FPSCR.FI = 0; + FPSCR.FR = 0; } rPS0(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(rPS0(inst.FB))); From d6bafbfaaf0cfb62222fad52b4e64c419c61ec42 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Sat, 2 Jun 2018 20:47:16 -0400 Subject: [PATCH 4/5] Interpreter_Paired: Handle signaling NaNs within ps_res and ps_rsqrte Like regular fres and frsqrte, these also signal whether or not either of the inputs are signaling NaNs. --- .../Interpreter/Interpreter_Paired.cpp | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp index ce4dfd9568..44145195e5 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp @@ -115,8 +115,8 @@ void Interpreter::ps_div(UGeckoInstruction inst) void Interpreter::ps_res(UGeckoInstruction inst) { // this code is based on the real hardware tests - double a = rPS0(inst.FB); - double b = rPS1(inst.FB); + const double a = rPS0(inst.FB); + const double b = rPS1(inst.FB); if (a == 0.0 || b == 0.0) { @@ -125,6 +125,13 @@ void Interpreter::ps_res(UGeckoInstruction inst) FPSCR.FR = 0; } + if (Common::IsSNAN(a) || Common::IsSNAN(b)) + { + SetFPException(FPSCR_VXSNAN); + FPSCR.FI = 0; + FPSCR.FR = 0; + } + rPS0(inst.FD) = Common::ApproximateReciprocal(a); rPS1(inst.FD) = Common::ApproximateReciprocal(b); PowerPC::UpdateFPRF(rPS0(inst.FD)); @@ -135,22 +142,32 @@ void Interpreter::ps_res(UGeckoInstruction inst) void Interpreter::ps_rsqrte(UGeckoInstruction inst) { - if (rPS0(inst.FB) == 0.0 || rPS1(inst.FB) == 0.0) + const double ps0 = rPS0(inst.FB); + const double ps1 = rPS1(inst.FB); + + if (ps0 == 0.0 || ps1 == 0.0) { SetFPException(FPSCR_ZX); FPSCR.FI = 0; FPSCR.FR = 0; } - if (rPS0(inst.FB) < 0.0 || rPS1(inst.FB) < 0.0) + if (ps0 < 0.0 || ps1 < 0.0) { SetFPException(FPSCR_VXSQRT); FPSCR.FI = 0; FPSCR.FR = 0; } - rPS0(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(rPS0(inst.FB))); - rPS1(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(rPS1(inst.FB))); + if (Common::IsSNAN(ps0) || Common::IsSNAN(ps1)) + { + SetFPException(FPSCR_VXSNAN); + FPSCR.FI = 0; + FPSCR.FR = 0; + } + + rPS0(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(ps0)); + rPS1(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(ps1)); PowerPC::UpdateFPRF(rPS0(inst.FD)); From 3e63d7104634c719956e1f518cd64ded622a1eb5 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Sun, 3 Jun 2018 07:51:03 -0400 Subject: [PATCH 5/5] Gekko: Add helper function for clearing both FPSCR.FI and FPSCR.FR --- Source/Core/Core/PowerPC/Gekko.h | 6 +++ .../PowerPC/Interpreter/Interpreter_FPUtils.h | 54 +++++++------------ .../Interpreter/Interpreter_FloatingPoint.cpp | 24 +++------ .../Interpreter/Interpreter_Paired.cpp | 15 ++---- 4 files changed, 37 insertions(+), 62 deletions(-) diff --git a/Source/Core/Core/PowerPC/Gekko.h b/Source/Core/Core/PowerPC/Gekko.h index a046b883c2..ca083b1219 100644 --- a/Source/Core/Core/PowerPC/Gekko.h +++ b/Source/Core/Core/PowerPC/Gekko.h @@ -476,6 +476,12 @@ union UReg_FPSCR UReg_FPSCR() = default; explicit UReg_FPSCR(u32 hex_) : Hex{hex_} {} + + void ClearFIFR() + { + FI = 0; + FR = 0; + } }; // Hardware Implementation-Dependent Register 0 diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h index 666ca2bffb..ed15cef6da 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h @@ -105,8 +105,7 @@ inline double NI_mul(double a, double b) if (Common::IsSNAN(a) || Common::IsSNAN(b)) { SetFPException(FPSCR_VXSNAN); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } if (std::isnan(a)) @@ -115,8 +114,7 @@ inline double NI_mul(double a, double b) return MakeQuiet(b); SetFPException(FPSCR_VXIMZ); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); return PPC_NAN; } return t; @@ -131,8 +129,7 @@ inline double NI_div(double a, double b) if (Common::IsSNAN(a) || Common::IsSNAN(b)) { SetFPException(FPSCR_VXSNAN); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } if (std::isnan(a)) @@ -145,21 +142,18 @@ inline double NI_div(double a, double b) if (a == 0.0) { SetFPException(FPSCR_VXZDZ); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } else { SetFPException(FPSCR_ZX); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } } else if (std::isinf(a) && std::isinf(b)) { SetFPException(FPSCR_VXIDI); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } return PPC_NAN; @@ -177,8 +171,7 @@ inline double NI_add(double a, double b) if (Common::IsSNAN(a) || Common::IsSNAN(b)) { SetFPException(FPSCR_VXSNAN); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } if (std::isnan(a)) @@ -187,8 +180,7 @@ inline double NI_add(double a, double b) return MakeQuiet(b); SetFPException(FPSCR_VXISI); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); return PPC_NAN; } @@ -204,8 +196,7 @@ inline double NI_sub(double a, double b) if (Common::IsSNAN(a) || Common::IsSNAN(b)) { SetFPException(FPSCR_VXSNAN); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } if (std::isnan(a)) @@ -214,8 +205,7 @@ inline double NI_sub(double a, double b) return MakeQuiet(b); SetFPException(FPSCR_VXISI); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); return PPC_NAN; } @@ -234,8 +224,7 @@ inline double NI_madd(double a, double c, double b) if (Common::IsSNAN(a) || Common::IsSNAN(b) || Common::IsSNAN(c)) { SetFPException(FPSCR_VXSNAN); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } if (std::isnan(a)) @@ -246,8 +235,7 @@ inline double NI_madd(double a, double c, double b) return MakeQuiet(c); SetFPException(FPSCR_VXIMZ); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); return PPC_NAN; } @@ -258,16 +246,14 @@ inline double NI_madd(double a, double c, double b) if (Common::IsSNAN(b)) { SetFPException(FPSCR_VXSNAN); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } if (std::isnan(b)) return MakeQuiet(b); SetFPException(FPSCR_VXISI); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); return PPC_NAN; } @@ -283,8 +269,7 @@ inline double NI_msub(double a, double c, double b) if (Common::IsSNAN(a) || Common::IsSNAN(b) || Common::IsSNAN(c)) { SetFPException(FPSCR_VXSNAN); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } if (std::isnan(a)) @@ -295,8 +280,7 @@ inline double NI_msub(double a, double c, double b) return MakeQuiet(c); SetFPException(FPSCR_VXIMZ); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); return PPC_NAN; } @@ -307,16 +291,14 @@ inline double NI_msub(double a, double c, double b) if (Common::IsSNAN(b)) { SetFPException(FPSCR_VXSNAN); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } if (std::isnan(b)) return MakeQuiet(b); SetFPException(FPSCR_VXISI); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); return PPC_NAN; } diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp index acf7c93afb..32f78343e9 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp @@ -92,8 +92,7 @@ void ConvertToInteger(UGeckoInstruction inst, RoundingMode rounding_mode) const double di = i; if (di == b) { - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } else { @@ -105,8 +104,7 @@ void ConvertToInteger(UGeckoInstruction inst, RoundingMode rounding_mode) if (exception_occurred) { - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } if (!exception_occurred || FPSCR.VE == 0) @@ -285,8 +283,7 @@ void Interpreter::frspx(UGeckoInstruction inst) // round to single PowerPC::UpdateFPRF(b); } - SetFI(0); - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } else { @@ -397,8 +394,7 @@ void Interpreter::fresx(UGeckoInstruction inst) if (b == 0.0) { SetFPException(FPSCR_ZX); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); if (FPSCR.ZE == 0) compute_result(b); @@ -406,8 +402,7 @@ void Interpreter::fresx(UGeckoInstruction inst) else if (Common::IsSNAN(b)) { SetFPException(FPSCR_VXSNAN); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); if (FPSCR.VE == 0) compute_result(b); @@ -434,8 +429,7 @@ void Interpreter::frsqrtex(UGeckoInstruction inst) if (b < 0.0) { SetFPException(FPSCR_VXSQRT); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); if (FPSCR.VE == 0) compute_result(b); @@ -443,8 +437,7 @@ void Interpreter::frsqrtex(UGeckoInstruction inst) else if (b == 0.0) { SetFPException(FPSCR_ZX); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); if (FPSCR.ZE == 0) compute_result(b); @@ -452,8 +445,7 @@ void Interpreter::frsqrtex(UGeckoInstruction inst) else if (Common::IsSNAN(b)) { SetFPException(FPSCR_VXSNAN); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); if (FPSCR.VE == 0) compute_result(b); diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp index 44145195e5..380c9ac103 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp @@ -121,15 +121,13 @@ void Interpreter::ps_res(UGeckoInstruction inst) if (a == 0.0 || b == 0.0) { SetFPException(FPSCR_ZX); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } if (Common::IsSNAN(a) || Common::IsSNAN(b)) { SetFPException(FPSCR_VXSNAN); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } rPS0(inst.FD) = Common::ApproximateReciprocal(a); @@ -148,22 +146,19 @@ void Interpreter::ps_rsqrte(UGeckoInstruction inst) if (ps0 == 0.0 || ps1 == 0.0) { SetFPException(FPSCR_ZX); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } if (ps0 < 0.0 || ps1 < 0.0) { SetFPException(FPSCR_VXSQRT); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } if (Common::IsSNAN(ps0) || Common::IsSNAN(ps1)) { SetFPException(FPSCR_VXSNAN); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } rPS0(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(ps0));