From 9068109b3ed00f6960d1554b1532f763ca84014a Mon Sep 17 00:00:00 2001 From: Lioncash Date: Sun, 3 Jun 2018 16:45:20 -0400 Subject: [PATCH] Interpreter: Unset FPSCR.FI and FPSCR.FR for QNaN and infinity input operands This hardware behavior makes sense, as the FI bit is used to signify an inexact result. An inexact result is a form of value that results during the rounding phase of denormalization. If any bits of the significand are lost during said rounding, then the result is considered to be inexact. However NaN and infinity are not classed as subnormals and therefore don't undergo the denormalization step, making loss of precision not possible (in NaN's case, numerically rounding something that is literally Not a Number doesn't even make sense). FR is set to indicate whether or not the last arithmetic or rounding and conversion instruction that rounded the intermediate result incremented the fractional portion of the result. Given neither input types would be affected by this, this should also be unset. This corrects more of the exceptional case handling for these values to match hardware. --- .../PowerPC/Interpreter/Interpreter_FPUtils.h | 62 +++++++++---------- .../Interpreter/Interpreter_FloatingPoint.cpp | 6 ++ .../Interpreter/Interpreter_Paired.cpp | 16 ++--- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h index ed15cef6da..1311f3958a 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h @@ -103,10 +103,9 @@ inline double NI_mul(double a, double b) if (std::isnan(t)) { if (Common::IsSNAN(a) || Common::IsSNAN(b)) - { SetFPException(FPSCR_VXSNAN); - FPSCR.ClearFIFR(); - } + + FPSCR.ClearFIFR(); if (std::isnan(a)) return MakeQuiet(a); @@ -114,7 +113,6 @@ inline double NI_mul(double a, double b) return MakeQuiet(b); SetFPException(FPSCR_VXIMZ); - FPSCR.ClearFIFR(); return PPC_NAN; } return t; @@ -127,10 +125,9 @@ inline double NI_div(double a, double b) if (std::isnan(t)) { if (Common::IsSNAN(a) || Common::IsSNAN(b)) - { SetFPException(FPSCR_VXSNAN); - FPSCR.ClearFIFR(); - } + + FPSCR.ClearFIFR(); if (std::isnan(a)) return MakeQuiet(a); @@ -142,18 +139,15 @@ inline double NI_div(double a, double b) if (a == 0.0) { SetFPException(FPSCR_VXZDZ); - FPSCR.ClearFIFR(); } else { SetFPException(FPSCR_ZX); - FPSCR.ClearFIFR(); } } else if (std::isinf(a) && std::isinf(b)) { SetFPException(FPSCR_VXIDI); - FPSCR.ClearFIFR(); } return PPC_NAN; @@ -169,10 +163,9 @@ inline double NI_add(double a, double b) if (std::isnan(t)) { if (Common::IsSNAN(a) || Common::IsSNAN(b)) - { SetFPException(FPSCR_VXSNAN); - FPSCR.ClearFIFR(); - } + + FPSCR.ClearFIFR(); if (std::isnan(a)) return MakeQuiet(a); @@ -180,10 +173,12 @@ inline double NI_add(double a, double b) return MakeQuiet(b); SetFPException(FPSCR_VXISI); - FPSCR.ClearFIFR(); return PPC_NAN; } + if (std::isinf(a) || std::isinf(b)) + FPSCR.ClearFIFR(); + return t; } @@ -194,10 +189,9 @@ inline double NI_sub(double a, double b) if (std::isnan(t)) { if (Common::IsSNAN(a) || Common::IsSNAN(b)) - { SetFPException(FPSCR_VXSNAN); - FPSCR.ClearFIFR(); - } + + FPSCR.ClearFIFR(); if (std::isnan(a)) return MakeQuiet(a); @@ -205,10 +199,12 @@ inline double NI_sub(double a, double b) return MakeQuiet(b); SetFPException(FPSCR_VXISI); - FPSCR.ClearFIFR(); return PPC_NAN; } + if (std::isinf(a) || std::isinf(b)) + FPSCR.ClearFIFR(); + return t; } @@ -222,10 +218,9 @@ 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.ClearFIFR(); - } + + FPSCR.ClearFIFR(); if (std::isnan(a)) return MakeQuiet(a); @@ -235,7 +230,6 @@ inline double NI_madd(double a, double c, double b) return MakeQuiet(c); SetFPException(FPSCR_VXIMZ); - FPSCR.ClearFIFR(); return PPC_NAN; } @@ -244,19 +238,20 @@ inline double NI_madd(double a, double c, double b) if (std::isnan(t)) { if (Common::IsSNAN(b)) - { SetFPException(FPSCR_VXSNAN); - FPSCR.ClearFIFR(); - } + + FPSCR.ClearFIFR(); if (std::isnan(b)) return MakeQuiet(b); SetFPException(FPSCR_VXISI); - FPSCR.ClearFIFR(); return PPC_NAN; } + if (std::isinf(a) || std::isinf(b) || std::isinf(c)) + FPSCR.ClearFIFR(); + return t; } @@ -267,10 +262,9 @@ 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.ClearFIFR(); - } + + FPSCR.ClearFIFR(); if (std::isnan(a)) return MakeQuiet(a); @@ -280,7 +274,6 @@ inline double NI_msub(double a, double c, double b) return MakeQuiet(c); SetFPException(FPSCR_VXIMZ); - FPSCR.ClearFIFR(); return PPC_NAN; } @@ -289,19 +282,20 @@ inline double NI_msub(double a, double c, double b) if (std::isnan(t)) { if (Common::IsSNAN(b)) - { SetFPException(FPSCR_VXSNAN); - FPSCR.ClearFIFR(); - } + + FPSCR.ClearFIFR(); if (std::isnan(b)) return MakeQuiet(b); SetFPException(FPSCR_VXISI); - FPSCR.ClearFIFR(); return PPC_NAN; } + if (std::isinf(a) || std::isinf(b) || std::isinf(c)) + FPSCR.ClearFIFR(); + return t; } diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp index 32f78343e9..5fe173dcbc 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp @@ -409,6 +409,9 @@ void Interpreter::fresx(UGeckoInstruction inst) } else { + if (std::isnan(b) || std::isinf(b)) + FPSCR.ClearFIFR(); + compute_result(b); } @@ -452,6 +455,9 @@ void Interpreter::frsqrtex(UGeckoInstruction inst) } else { + if (std::isnan(b) || std::isinf(b)) + FPSCR.ClearFIFR(); + compute_result(b); } diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp index 380c9ac103..f3c55e7fc0 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp @@ -124,11 +124,11 @@ void Interpreter::ps_res(UGeckoInstruction inst) FPSCR.ClearFIFR(); } - if (Common::IsSNAN(a) || Common::IsSNAN(b)) - { - SetFPException(FPSCR_VXSNAN); + if (std::isnan(a) || std::isinf(a) || std::isnan(b) || std::isinf(b)) FPSCR.ClearFIFR(); - } + + if (Common::IsSNAN(a) || Common::IsSNAN(b)) + SetFPException(FPSCR_VXSNAN); rPS0(inst.FD) = Common::ApproximateReciprocal(a); rPS1(inst.FD) = Common::ApproximateReciprocal(b); @@ -155,11 +155,11 @@ void Interpreter::ps_rsqrte(UGeckoInstruction inst) FPSCR.ClearFIFR(); } - if (Common::IsSNAN(ps0) || Common::IsSNAN(ps1)) - { - SetFPException(FPSCR_VXSNAN); + if (std::isnan(ps0) || std::isinf(ps0) || std::isnan(ps1) || std::isinf(ps1)) FPSCR.ClearFIFR(); - } + + if (Common::IsSNAN(ps0) || Common::IsSNAN(ps1)) + SetFPException(FPSCR_VXSNAN); rPS0(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(ps0)); rPS1(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(ps1));