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; }