Submitted By:            Zeckma
Date:                    2026-07-18
Initial Package Version: 14.0.0
Upstream Status:         Applied
Origin:                  6c88c15e6ba64059231266b1ffff8801d7d06e11 and
                         8acff4e19351ada59baa61f4bbc2bc4f19377410 from
                         https://github.com/mingw-w64/mingw-w64.
Description:             This ensures exceptions on noop operations are
                         not masked and that return values of some
                         operations are fixed.

diff --git a/mingw-w64-crt/misc/mingw_setfp.c b/mingw-w64-crt/misc/mingw_setfp.c
index dcd767d9a..137a5556e 100644
--- a/mingw-w64-crt/misc/mingw_setfp.c
+++ b/mingw-w64-crt/misc/mingw_setfp.c
@@ -118,9 +118,9 @@ void __mingw_setfp( unsigned int *cw, unsigned int cw_mask,
 #if defined(__arm64ec__)
     __mingw_setfp_sse(cw, cw_mask, sw, sw_mask);
 #elif defined(__i386__) || defined(__x86_64__)
-    unsigned long oldcw = 0, newcw = 0;
-    unsigned long oldsw = 0, newsw = 0;
+    unsigned long newcw = 0, newsw = 0;
     unsigned int flags;
+    int use_fnstenv_fldenv;
     struct {
         WORD control_word;
         WORD unused1;
@@ -139,7 +139,9 @@ void __mingw_setfp( unsigned int *cw, unsigned int cw_mask,
     cw_mask &= _MCW_EM | _MCW_IC | _MCW_RC | _MCW_PC;
     sw_mask &= _MCW_EM;
 
-    if ((!sw || sw_mask == 0) && (!cw || cw_mask == 0))
+    use_fnstenv_fldenv = ((sw && sw_mask != 0) || (cw && cw_mask != 0));
+
+    if (!use_fnstenv_fldenv)
     {
         /* Fast path: when we are not going to change sw/cw which is indicated
          * by zero mask then load sw/cw via fast fnstsw/fnstcw instruction.
@@ -151,15 +153,15 @@ void __mingw_setfp( unsigned int *cw, unsigned int cw_mask,
     {
         /* Slow path: when we are going to change sw/cw or we do not know yet then
          * load whole x87 env via slow fnstenv as it is needed for changing sw/cw.
+         * Note that fnstenv masks all floating-point exceptions after storing the
+         * x87 env. And after the fnstenv call, it is always required to restore
+         * masking of previous floating-point exceptions via the fldenv call.
          */
         __asm__ __volatile__( "fnstenv %0" : "=m" (fenv) );
         newsw = fenv.status_word;
         newcw = fenv.control_word;
     }
 
-    oldsw = newsw;
-    oldcw = newcw;
-
     if (sw)
     {
         flags = 0;
@@ -229,8 +231,10 @@ void __mingw_setfp( unsigned int *cw, unsigned int cw_mask,
 
     /* For changing sw/cw always use fldenv.
      * Do not use fldcw as it can generate pending floating-point exception.
+     * When the fnstenv was called then it is required to call fldenv to
+     * restore previous floating-point exceptions.
      */
-    if (oldsw != newsw || oldcw != newcw)
+    if (use_fnstenv_fldenv)
     {
         fenv.control_word = newcw;
         fenv.status_word = newsw;

diff --git a/mingw-w64-crt/misc/fedisableexcept.c b/mingw-w64-crt/misc/fedisableexcept.c
index 29b905e3d..5c3328d02 100644
--- a/mingw-w64-crt/misc/fedisableexcept.c
+++ b/mingw-w64-crt/misc/fedisableexcept.c
@@ -4,5 +4,8 @@
 
 int __cdecl fedisableexcept(int excepts)
 {
-  return __mingw_controlfp(excepts & FE_ALL_EXCEPT, excepts & FE_ALL_EXCEPT) & FE_ALL_EXCEPT;
+  if (excepts & ~FE_ALL_EXCEPT) return -1;
+  int old_excepts = fegetexcept();
+  __mingw_controlfp(excepts, excepts);
+  return old_excepts;
 }
diff --git a/mingw-w64-crt/misc/feenableexcept.c b/mingw-w64-crt/misc/feenableexcept.c
index 8a40bd5fd..7bd6f757f 100644
--- a/mingw-w64-crt/misc/feenableexcept.c
+++ b/mingw-w64-crt/misc/feenableexcept.c
@@ -4,5 +4,8 @@
 
 int __cdecl feenableexcept(int excepts)
 {
-  return __mingw_controlfp(0, excepts & FE_ALL_EXCEPT) & FE_ALL_EXCEPT;
+  if (excepts & ~FE_ALL_EXCEPT) return -1;
+  int old_excepts = fegetexcept();
+  __mingw_controlfp(0, excepts);
+  return old_excepts;
 }
diff --git a/mingw-w64-crt/misc/fegetexcept.c b/mingw-w64-crt/misc/fegetexcept.c
index ced9f50b0..6bbf8d1bd 100644
--- a/mingw-w64-crt/misc/fegetexcept.c
+++ b/mingw-w64-crt/misc/fegetexcept.c
@@ -4,5 +4,5 @@
 
 int __cdecl fegetexcept(void)
 {
-  return __mingw_controlfp(0, 0) & FE_ALL_EXCEPT;
+  return ~__mingw_controlfp(0, 0) & FE_ALL_EXCEPT;
 }

