Since the current mingw-w64 uses a different layout for fenv_t and constant
values, this changes the ABI.

Based on Wine code by Piotr Caban Alexandre Julliard and Martin Storsjö, who
granted permission to use it under the mingw-w64 license.

Unlike earlier versions, msvcr120 and UCRT implement fenv.h, but their
representation differs from what mingw-w64 has used so far. fenv_t has a
target-independent layout and flags, requiring translation to and from
machine-specific flags. Generic helpers handle this conversion.

On Windows x86_64, these functions operate only on SSE registers, ignoring x87.
Since mingw long doubles use x87, they would be unaffected by these calls. This
patch does not follow that behavior; instead, it modifies x87 registers as
well, effectively following the 32-bit x86 code path.

Additionally, in 32-bit versions older than msvcr71, functions like _control87,
__control87_2, and _statusfp did not support SSE, even though they would
otherwise be useful as helpers. This patch provides variants that operate on
both SSE and x87 for x86 and x86_64 (though we could still consider a different
approach for ARM targets).

I left FE_*_ENV handling unchanged, but we could consider making them proper
pointers to structs filled with the appropriate values instead.

This patch also adds support for ARM64EC. The ARM64EC version currently ignores
x87, as 80-bit long doubles are not supported anyway.
---
 mingw-w64-crt/Makefile.am            |   2 +
 mingw-w64-crt/include/internal.h     | 133 ++++++++++
 mingw-w64-crt/misc/feclearexcept.c   |  40 +--
 mingw-w64-crt/misc/fegetenv.c        |  31 +--
 mingw-w64-crt/misc/fegetexceptflag.c |  29 +--
 mingw-w64-crt/misc/fegetround.c      |  19 +-
 mingw-w64-crt/misc/feholdexcept.c    |  23 +-
 mingw-w64-crt/misc/feraiseexcept.c   |  32 +--
 mingw-w64-crt/misc/fesetenv.c        | 103 ++++----
 mingw-w64-crt/misc/fesetexceptflag.c |  42 +--
 mingw-w64-crt/misc/fesetround.c      |  43 +--
 mingw-w64-crt/misc/fetestexcept.c    |  23 +-
 mingw-w64-crt/misc/feupdateenv.c     |  10 +-
 mingw-w64-crt/misc/mingw_controlfp.c |  53 ++++
 mingw-w64-crt/misc/mingw_setfp.c     | 373 +++++++++++++++++++++++++++
 mingw-w64-headers/crt/fenv.h         |  72 ++----
 16 files changed, 692 insertions(+), 336 deletions(-)
 create mode 100644 mingw-w64-crt/misc/mingw_controlfp.c
 create mode 100644 mingw-w64-crt/misc/mingw_setfp.c

diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index 992de1f0c..bc498b888 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -979,6 +979,8 @@ src_libmingwex=\
   misc/dirname.c \
   misc/feclearexcept.c   misc/fegetenv.c            misc/fegetexceptflag.c  
misc/fegetround.c            misc/feholdexcept.c    \
   misc/feraiseexcept.c   misc/fesetenv.c            misc/fesetexceptflag.c  
misc/fesetround.c            misc/fetestexcept.c    \
+  misc/mingw_controlfp.c \
+  misc/mingw_setfp.c \
   misc/feupdateenv.c     misc/ftruncate.c           misc/fwide.c            
misc/getlogin.c              misc/getopt.c          \
   misc/gettimeofday.c    misc/__mingw_has_sse.c      \
   misc/mempcpy.c         misc/mingw-aligned-malloc.c \
diff --git a/mingw-w64-crt/include/internal.h b/mingw-w64-crt/include/internal.h
index b3f1186e1..84d52c991 100644
--- a/mingw-w64-crt/include/internal.h
+++ b/mingw-w64-crt/include/internal.h
@@ -14,6 +14,7 @@ extern "C" {
 #endif
 
 #include <limits.h>
+#include <fenv.h>
 #include <windows.h>
 
 #pragma pack(push,_CRT_PACKING)
@@ -154,6 +155,138 @@ extern "C" {
 # define __mingw_has_sse()  0
 #endif
 
+#if defined(__i386__) || defined(__x86_64__)
+enum fenv_masks
+{
+    /* x87 encoding constants */
+    FENV_X_INVALID = 0x00100010,
+    FENV_X_DENORMAL = 0x00200020,
+    FENV_X_ZERODIVIDE = 0x00080008,
+    FENV_X_OVERFLOW = 0x00040004,
+    FENV_X_UNDERFLOW = 0x00020002,
+    FENV_X_INEXACT = 0x00010001,
+    FENV_X_AFFINE = 0x00004000,
+    FENV_X_UP = 0x00800200,
+    FENV_X_DOWN = 0x00400100,
+    FENV_X_24 = 0x00002000,
+    FENV_X_53 = 0x00001000,
+    /* SSE encoding constants: they share the same lower word as their x87 
counterparts
+     * but differ in the upper word */
+    FENV_Y_INVALID = 0x10000010,
+    FENV_Y_DENORMAL = 0x20000020,
+    FENV_Y_ZERODIVIDE = 0x08000008,
+    FENV_Y_OVERFLOW = 0x04000004,
+    FENV_Y_UNDERFLOW = 0x02000002,
+    FENV_Y_INEXACT = 0x01000001,
+    FENV_Y_UP = 0x80000200,
+    FENV_Y_DOWN = 0x40000100,
+    FENV_Y_FLUSH = 0x00000400,
+    FENV_Y_FLUSH_SAVE = 0x00000800
+};
+
+/* encodes the x87 (represented as x) or SSE (represented as y) control/status 
word in a ulong */
+static inline unsigned long fenv_encode(unsigned int x, unsigned int y)
+{
+    unsigned long ret = 0;
+
+    if (x & _EM_INVALID) ret |= FENV_X_INVALID;
+    if (x & _EM_DENORMAL) ret |= FENV_X_DENORMAL;
+    if (x & _EM_ZERODIVIDE) ret |= FENV_X_ZERODIVIDE;
+    if (x & _EM_OVERFLOW) ret |= FENV_X_OVERFLOW;
+    if (x & _EM_UNDERFLOW) ret |= FENV_X_UNDERFLOW;
+    if (x & _EM_INEXACT) ret |= FENV_X_INEXACT;
+    if (x & _IC_AFFINE) ret |= FENV_X_AFFINE;
+    if (x & _RC_UP) ret |= FENV_X_UP;
+    if (x & _RC_DOWN) ret |= FENV_X_DOWN;
+    if (x & _PC_24) ret |= FENV_X_24;
+    if (x & _PC_53) ret |= FENV_X_53;
+
+    if (y & _EM_INVALID) ret |= FENV_Y_INVALID;
+    if (y & _EM_DENORMAL) ret |= FENV_Y_DENORMAL;
+    if (y & _EM_ZERODIVIDE) ret |= FENV_Y_ZERODIVIDE;
+    if (y & _EM_OVERFLOW) ret |= FENV_Y_OVERFLOW;
+    if (y & _EM_UNDERFLOW) ret |= FENV_Y_UNDERFLOW;
+    if (y & _EM_INEXACT) ret |= FENV_Y_INEXACT;
+    if (y & _RC_UP) ret |= FENV_Y_UP;
+    if (y & _RC_DOWN) ret |= FENV_Y_DOWN;
+    if (y & _DN_FLUSH) ret |= FENV_Y_FLUSH;
+    if (y & _DN_FLUSH_OPERANDS_SAVE_RESULTS) ret |= FENV_Y_FLUSH_SAVE;
+
+    return ret;
+}
+
+/* decodes the x87 (represented as x) or SSE (represented as y) control/status 
word in a ulong */
+static inline BOOL fenv_decode(unsigned long enc, unsigned int *x, unsigned 
int *y)
+{
+    *x = *y = 0;
+    if ((enc & FENV_X_INVALID) == FENV_X_INVALID) *x |= _EM_INVALID;
+    if ((enc & FENV_X_DENORMAL) == FENV_X_DENORMAL) *x |= _EM_DENORMAL;
+    if ((enc & FENV_X_ZERODIVIDE) == FENV_X_ZERODIVIDE) *x |= _EM_ZERODIVIDE;
+    if ((enc & FENV_X_OVERFLOW) == FENV_X_OVERFLOW) *x |= _EM_OVERFLOW;
+    if ((enc & FENV_X_UNDERFLOW) == FENV_X_UNDERFLOW) *x |= _EM_UNDERFLOW;
+    if ((enc & FENV_X_INEXACT) == FENV_X_INEXACT) *x |= _EM_INEXACT;
+    if ((enc & FENV_X_AFFINE) == FENV_X_AFFINE) *x |= _IC_AFFINE;
+    if ((enc & FENV_X_UP) == FENV_X_UP) *x |= _RC_UP;
+    if ((enc & FENV_X_DOWN) == FENV_X_DOWN) *x |= _RC_DOWN;
+    if ((enc & FENV_X_24) == FENV_X_24) *x |= _PC_24;
+    if ((enc & FENV_X_53) == FENV_X_53) *x |= _PC_53;
+
+    if ((enc & FENV_Y_INVALID) == FENV_Y_INVALID) *y |= _EM_INVALID;
+    if ((enc & FENV_Y_DENORMAL) == FENV_Y_DENORMAL) *y |= _EM_DENORMAL;
+    if ((enc & FENV_Y_ZERODIVIDE) == FENV_Y_ZERODIVIDE) *y |= _EM_ZERODIVIDE;
+    if ((enc & FENV_Y_OVERFLOW) == FENV_Y_OVERFLOW) *y |= _EM_OVERFLOW;
+    if ((enc & FENV_Y_UNDERFLOW) == FENV_Y_UNDERFLOW) *y |= _EM_UNDERFLOW;
+    if ((enc & FENV_Y_INEXACT) == FENV_Y_INEXACT) *y |= _EM_INEXACT;
+    if ((enc & FENV_Y_UP) == FENV_Y_UP) *y |= _RC_UP;
+    if ((enc & FENV_Y_DOWN) == FENV_Y_DOWN) *y |= _RC_DOWN;
+    if ((enc & FENV_Y_FLUSH) == FENV_Y_FLUSH) *y |= _DN_FLUSH;
+    if ((enc & FENV_Y_FLUSH_SAVE) == FENV_Y_FLUSH_SAVE) *y |= 
_DN_FLUSH_OPERANDS_SAVE_RESULTS;
+
+    return fenv_encode(*x, *y) == enc;
+}
+#else
+static inline unsigned long fenv_encode(unsigned int x, unsigned int y)
+{
+    /* Encode _EM_DENORMAL as 0x20 for Windows compatibility. */
+    if (y & _EM_DENORMAL)
+        y = (y & ~_EM_DENORMAL) | 0x20;
+
+    return x | y;
+}
+
+static inline BOOL fenv_decode(unsigned long enc, unsigned int *x, unsigned 
int *y)
+{
+    /* Decode 0x20 as _EM_DENORMAL. */
+    if (enc & 0x20)
+        enc = (enc & ~0x20) | _EM_DENORMAL;
+
+    *x = *y = enc;
+    return TRUE;
+}
+#endif
+
+void __mingw_setfp( unsigned int *cw, unsigned int cw_mask, unsigned int *sw, 
unsigned int sw_mask );
+void __mingw_setfp_sse( unsigned int *cw, unsigned int cw_mask, unsigned int 
*sw, unsigned int sw_mask );
+unsigned int __mingw_controlfp(unsigned int newval, unsigned int mask);
+#if defined(__i386__) || (defined(__x86_64__) && !defined(__arm64ec__))
+int __mingw_control87_2(unsigned int, unsigned int, unsigned int *, unsigned 
int *);
+#endif
+
+static inline unsigned int __mingw_statusfp(void)
+{
+    unsigned int flags = 0;
+#if defined(__i386__) || (defined(__x86_64__) && !defined(__arm64ec__))
+    unsigned int x86_sw, sse2_sw = 0;
+    __mingw_setfp(NULL, 0, &x86_sw, 0);
+    if (__mingw_has_sse())
+        __mingw_setfp_sse(NULL, 0, &sse2_sw, 0);
+    flags = x86_sw | sse2_sw;
+#else
+    __mingw_setfp(NULL, 0, &flags, 0);
+#endif
+    return flags;
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/mingw-w64-crt/misc/feclearexcept.c 
b/mingw-w64-crt/misc/feclearexcept.c
index a5ab0f095..5fa811fbe 100644
--- a/mingw-w64-crt/misc/feclearexcept.c
+++ b/mingw-w64-crt/misc/feclearexcept.c
@@ -4,44 +4,18 @@
  * No warranty is given; refer to the file DISCLAIMER.PD within this package.
  */
 
-#include <fenv.h>
 #include <internal.h>
 
 /* 7.6.2.1
    The feclearexcept function clears the supported exceptions
    represented by its argument.  */
 
-int feclearexcept (int excepts)
+int feclearexcept(int flags)
 {
-  fenv_t _env;
-#if defined(_ARM_) || defined(__arm__)
-  __asm__ volatile ("fmrx %0, FPSCR" : "=r" (_env));
-  _env.__cw &= ~(excepts & FE_ALL_EXCEPT);
-  __asm__ volatile ("fmxr FPSCR, %0" : : "r" (_env));
-#elif defined(_ARM64_) || defined(__aarch64__)
-  unsigned __int64 fpcr;
-  (void) _env;
-  __asm__ volatile ("mrs %0, fpcr" : "=r" (fpcr));
-  fpcr &= ~(excepts & FE_ALL_EXCEPT);
-  __asm__ volatile ("msr fpcr, %0" : : "r" (fpcr));
-#else
-  int _mxcsr;
-  if (excepts == FE_ALL_EXCEPT)
-    {
-      __asm__ volatile ("fnclex");
-    }
-  else
-    {
-      __asm__ volatile ("fnstenv %0" : "=m" (_env));
-      _env.__status_word &= ~(excepts & FE_ALL_EXCEPT);
-      __asm__ volatile ("fldenv %0" : : "m" (_env));
-    }
-  if (__mingw_has_sse ())
-    {
-      __asm__ volatile ("stmxcsr %0" : "=m" (_mxcsr));
-      _mxcsr &= ~(((excepts & FE_ALL_EXCEPT)));
-      __asm__ volatile ("ldmxcsr %0" : : "m" (_mxcsr));
-    }
-#endif /* defined(_ARM_) || defined(__arm__) || defined(_ARM64_) || 
defined(__aarch64__) */
-  return (0);
+    fenv_t env;
+
+    fegetenv(&env);
+    flags &= FE_ALL_EXCEPT;
+    env._Fe_stat &= ~fenv_encode(flags, flags);
+    return fesetenv(&env);
 }
diff --git a/mingw-w64-crt/misc/fegetenv.c b/mingw-w64-crt/misc/fegetenv.c
index e17fd491c..c1169cda7 100644
--- a/mingw-w64-crt/misc/fegetenv.c
+++ b/mingw-w64-crt/misc/fegetenv.c
@@ -4,34 +4,25 @@
  * No warranty is given; refer to the file DISCLAIMER.PD within this package.
  */
 
-#include <fenv.h>
 #include <internal.h>
 
 /* 7.6.4.1
    The fegetenv function stores the current floating-point environment
    in the object pointed to by envp.  */
 
-int fegetenv (fenv_t * envp)
+int fegetenv(fenv_t *env)
 {
-#if defined(_ARM_) || defined(__arm__)
-  __asm__ volatile ("fmrx %0, FPSCR" : "=r" (*envp));
-#elif defined(_ARM64_) || defined(__aarch64__)
-  unsigned __int64 fpcr;
-  __asm__ volatile ("mrs %0, fpcr" : "=r" (fpcr));
-  envp->__cw = fpcr;
+#if defined(__i386__) || (defined(__x86_64__) && !defined(__arm64ec__))
+    unsigned int x87, sse;
+    __mingw_control87_2(0, 0, &x87, &sse);
+    env->_Fe_ctl = fenv_encode(x87, sse);
+    __mingw_setfp(NULL, 0, &x87, 0);
+    __mingw_setfp_sse(NULL, 0, &sse, 0);
+    env->_Fe_stat = fenv_encode(x87, sse);
 #else
-  __asm__ __volatile__ ("fnstenv %0;": "=m" (*envp));
- /* fnstenv sets control word to non-stop for all exceptions, so we
-    need to reload our env to restore the original mask.  */
-  __asm__ __volatile__ ("fldenv %0" : : "m" (*envp));
-  if (__mingw_has_sse ())
-    {
-      int _mxcsr;
-      __asm__ __volatile__ ("stmxcsr %0" : "=m" (_mxcsr));
-      envp->__unused0 = (((unsigned int) _mxcsr) >> 16);
-      envp->__unused1 = (((unsigned int) _mxcsr) & 0xffff);
-    }
-#endif /* defined(_ARM_) || defined(__arm__) || defined(_ARM64_) || 
defined(__aarch64__) */
+    env->_Fe_ctl = fenv_encode(0, __mingw_controlfp(0, 0));
+    env->_Fe_stat = fenv_encode(0, __mingw_statusfp());
+#endif
   return 0;
 }
 
diff --git a/mingw-w64-crt/misc/fegetexceptflag.c 
b/mingw-w64-crt/misc/fegetexceptflag.c
index b50ca6fc1..cc9bdde4b 100644
--- a/mingw-w64-crt/misc/fegetexceptflag.c
+++ b/mingw-w64-crt/misc/fegetexceptflag.c
@@ -12,26 +12,15 @@
    representation of the exception flags indicated by the argument
    excepts in the object pointed to by the argument flagp.  */
 
-int fegetexceptflag (fexcept_t * flagp, int excepts)
+int fegetexceptflag(fexcept_t *status, int excepts)
 {
-#if defined(_ARM_) || defined(__arm__)
-  fenv_t _env;
-  __asm__ volatile ("fmrx %0, FPSCR" : "=r" (_env));
-  *flagp = _env.__cw & excepts & FE_ALL_EXCEPT;
-#elif defined(_ARM64_) || defined(__aarch64__)
-  unsigned __int64 fpcr;
-  __asm__ volatile ("mrs %0, fpcr" : "=r" (fpcr));
-  *flagp = fpcr & excepts & FE_ALL_EXCEPT;
+#if defined(__i386__) || (defined(__x86_64__) && !defined(__arm64ec__))
+    unsigned int x87, sse;
+    __mingw_setfp(NULL, 0, &x87, 0);
+    __mingw_setfp_sse(NULL, 0, &sse, 0);
+    *status = fenv_encode(x87 & excepts, sse & excepts);
 #else
-  int _mxcsr;
-  unsigned short _status;
-
-  __asm__ volatile ("fnstsw %0" : "=am" (_status));
-  _mxcsr = 0;
-  if (__mingw_has_sse ())
-    __asm__ volatile ("stmxcsr %0" : "=m" (_mxcsr));
-
-  *flagp = (_mxcsr | _status) & excepts & FE_ALL_EXCEPT;
-#endif /* defined(_ARM_) || defined(__arm__) || defined(_ARM64_) || 
defined(__aarch64__) */
-  return 0;
+    *status = fenv_encode(0, __mingw_statusfp() & excepts);
+#endif
+    return 0;
 }
diff --git a/mingw-w64-crt/misc/fegetround.c b/mingw-w64-crt/misc/fegetround.c
index d2fdb7725..22d392ef9 100644
--- a/mingw-w64-crt/misc/fegetround.c
+++ b/mingw-w64-crt/misc/fegetround.c
@@ -3,26 +3,13 @@
  * This file is part of the mingw-w64 runtime package.
  * No warranty is given; refer to the file DISCLAIMER.PD within this package.
  */
-#include <fenv.h>
+#include <internal.h>
 
 /* 7.6.3.1
    The fegetround function returns the value of the rounding direction
    macro representing the current rounding direction.  */
 
-int
-fegetround (void)
+int fegetround(void)
 {
-#if defined(_ARM_) || defined(__arm__)
-  fenv_t _env;
-  __asm__ volatile ("fmrx %0, FPSCR" : "=r" (_env));
-  return (_env.__cw & (FE_TONEAREST | FE_DOWNWARD |  FE_UPWARD | 
FE_TOWARDZERO));
-#elif defined(_ARM64_) || defined(__aarch64__)
-  unsigned __int64 fpcr;
-  __asm__ volatile ("mrs %0, fpcr" : "=r" (fpcr));
-  return (fpcr & (FE_TONEAREST | FE_DOWNWARD |  FE_UPWARD | FE_TOWARDZERO));
-#else
-  int _control;
-  __asm__ volatile ("fnstcw %0" : "=m" (*&_control));
-  return (_control & (FE_TONEAREST | FE_DOWNWARD |  FE_UPWARD | 
FE_TOWARDZERO));
-#endif /* defined(_ARM_) || defined(__arm__) || defined(_ARM64_) || 
defined(__aarch64__) */
+    return __mingw_controlfp(0, 0) & _MCW_RC;
 }
diff --git a/mingw-w64-crt/misc/feholdexcept.c 
b/mingw-w64-crt/misc/feholdexcept.c
index 8736faadf..8048430b7 100644
--- a/mingw-w64-crt/misc/feholdexcept.c
+++ b/mingw-w64-crt/misc/feholdexcept.c
@@ -11,25 +11,8 @@
    flags, and then installs a non-stop (continue on exceptions) mode,
    if available, for all exceptions.  */
 
-int feholdexcept (fenv_t * envp)
+int feholdexcept(fenv_t *env)
 {
-#if defined(_ARM_) || defined(__arm__)
-  fenv_t _env;
-  __asm__ volatile ("fmrx %0, FPSCR" : "=r" (_env));
-  envp->__cw = _env.__cw;
-  _env.__cw &= ~(FE_ALL_EXCEPT);
-  __asm__ volatile ("fmxr FPSCR, %0" : : "r" (_env));
-#elif defined(_ARM64_) || defined(__aarch64__)
-  unsigned __int64 fpcr;
-  __asm__ volatile ("mrs %0, fpcr" : "=r" (fpcr));
-  envp->__cw = fpcr;
-  fpcr &= ~(FE_ALL_EXCEPT);
-  __asm__ volatile ("msr fpcr, %0" : : "r" (fpcr));
-#else
-  __asm__ __volatile__ ("fnstenv %0;" : "=m" (* envp)); /* save current into 
envp */
- /* fnstenv sets control word to non-stop for all exceptions, so all we
-    need to do is clear the exception flags.  */
-  __asm__ __volatile__ ("fnclex");
-#endif /* defined(_ARM_) || defined(__arm__) || defined(_ARM64_) || 
defined(__aarch64__) */
-  return 0;
+    fegetenv(env);
+    return feclearexcept(FE_ALL_EXCEPT);
 }
diff --git a/mingw-w64-crt/misc/feraiseexcept.c 
b/mingw-w64-crt/misc/feraiseexcept.c
index 8ec36f732..041b8a772 100644
--- a/mingw-w64-crt/misc/feraiseexcept.c
+++ b/mingw-w64-crt/misc/feraiseexcept.c
@@ -3,7 +3,7 @@
  * This file is part of the mingw-w64 runtime package.
  * No warranty is given; refer to the file DISCLAIMER.PD within this package.
  */
-#include <fenv.h> 
+#include <internal.h>
 
 /* 7.6.2.3
    The feraiseexcept function raises the supported exceptions
@@ -13,24 +13,16 @@
    the inexact exception whenever it raises the overflow
    or underflow exception is implementation-defined. */
 
-int feraiseexcept (int excepts)
+int feraiseexcept(int flags)
 {
-  fenv_t _env;
-#if defined(_ARM_) || defined(__arm__)
-  __asm__ volatile ("fmrx %0, FPSCR" : "=r" (_env));
-  _env.__cw |= excepts & FE_ALL_EXCEPT;
-  __asm__ volatile ("fmxr FPSCR, %0" : : "r" (_env));
-#elif defined(_ARM64_) || defined(__aarch64__)
-  unsigned __int64 fpcr;
-  (void) _env;
-  __asm__ volatile ("mrs %0, fpcr" : "=r" (fpcr));
-  fpcr |= excepts & FE_ALL_EXCEPT;
-  __asm__ volatile ("msr fpcr, %0" : : "r" (fpcr));
-#else
-  __asm__ volatile ("fnstenv %0;" : "=m" (_env));
-  _env.__status_word |= excepts & FE_ALL_EXCEPT;
-  __asm__ volatile ("fldenv %0;"
-                   "fwait;" : : "m" (_env));
-#endif /* defined(_ARM_) || defined(__arm__) || defined(_ARM64_) || 
defined(__aarch64__) */
-  return 0;
+    fenv_t env;
+
+    flags &= FE_ALL_EXCEPT;
+    fegetenv(&env);
+    env._Fe_stat |= fenv_encode(flags, flags);
+    fesetenv(&env);
+#if defined(__i386__) || (defined(__x86_64__) && !defined(__arm64ec__))
+    __asm__ volatile ("fwait\n\t");
+#endif
+    return 0;
 }
diff --git a/mingw-w64-crt/misc/fesetenv.c b/mingw-w64-crt/misc/fesetenv.c
index 76325ce37..ac0dffe46 100644
--- a/mingw-w64-crt/misc/fesetenv.c
+++ b/mingw-w64-crt/misc/fesetenv.c
@@ -4,9 +4,6 @@
  * No warranty is given; refer to the file DISCLAIMER.PD within this package.
  */
 
-#include <_mingw.h>
-#include <fenv.h>
-#include <float.h>
 #include <internal.h>
 
 /* 7.6.4.3
@@ -22,61 +19,59 @@
 extern void (* __MINGW_IMP_SYMBOL(_fpreset))(void);
 extern void _fpreset(void);
 
-int fesetenv (const fenv_t * envp)
+int fesetenv(const fenv_t *env)
 {
-#if defined(_ARM_) || defined(__arm__)
-  if (envp == FE_DFL_ENV)
-    /* Use the choice made at app startup */
-    _fpreset();
-  else
-    __asm__ volatile ("fmxr FPSCR, %0" : : "r" (*envp));
-#elif defined(_ARM64_) || defined(__aarch64__)
-  if (envp == FE_DFL_ENV) {
-    /* Use the choice made at app startup */
-    _fpreset();
-  } else {
-    unsigned __int64 fpcr = envp->__cw;
-    __asm__ volatile ("msr fpcr, %0" : : "r" (fpcr));
-  }
-#else
-  if (envp == FE_PC64_ENV)
-   /*
-    *  fninit initializes the control register to 0x37f,
-    *  the status register to zero and the tag word to 0FFFFh.
-    *  The other registers are unaffected.
-    */
-    __asm__ __volatile__ ("fninit");
-
-  else if (envp == FE_PC53_ENV)
-   /*
-    * MS _fpreset() does same *except* it sets control word
-    * to 0x27f (53-bit precision).
-    * We force calling _fpreset in msvcrt.dll
-    */
-
-   (* __MINGW_IMP_SYMBOL(_fpreset))();
+    unsigned int x87_cw, cw, x87_stat, stat;
+    unsigned int mask = ~0u;
 
-  else if (envp == FE_DFL_ENV)
-    /* Use the choice made at app startup */
-    _fpreset();
+#if defined(__i386__) || defined(__x86_64__)
+# if !defined(__arm64ec__)
+    if (env == FE_PC64_ENV)
+    {
+        /*
+         *  fninit initializes the control register to 0x37f,
+         *  the status register to zero and the tag word to 0FFFFh.
+         *  The other registers are unaffected.
+         */
+        __asm__ __volatile__ ("fninit");
+        return 0;
+    }
+# endif /* __arm64ec__ */
+    if (env == FE_PC53_ENV)
+    {
+        /*
+         * MS _fpreset() does same *except* it sets control word
+         * to 0x27f (53-bit precision).
+         * We force calling _fpreset in msvcrt.dll
+         */
 
-  else
+        (* __MINGW_IMP_SYMBOL(_fpreset))();
+        return 0;
+    }
+#endif /* defined(__i386__) || defined(__x86_64__) */
+    if (env == FE_DFL_ENV)
     {
-      fenv_t env = *envp;
-      int has_sse = __mingw_has_sse ();
-      int _mxcsr;
-      /*_mxcsr = ((int)envp->__unused0 << 16) | (int)envp->__unused1; *//* 
mxcsr low and high */
-      if (has_sse)
-        __asm__ ("stmxcsr %0" : "=m" (*&_mxcsr));
-      env.__unused0 = 0xffff;
-      env.__unused1 = 0xffff;
-      __asm__ volatile ("fldenv %0" : : "m" (env)
-                       : "st", "st(1)", "st(2)", "st(3)", "st(4)",
-                       "st(5)", "st(6)", "st(7)");
-      if (has_sse)
-        __asm__ volatile ("ldmxcsr %0" : : "m" (*&_mxcsr));
+        /* Use the choice made at app startup */
+        _fpreset();
+        return 0;
+    }
+
+    if (!env->_Fe_ctl && !env->_Fe_stat) {
+        _fpreset();
+        return 0;
     }
 
-#endif /* defined(_ARM_) || defined(__arm__) || defined(_ARM64_) || 
defined(__aarch64__) */
-  return 0;
+    if (!fenv_decode(env->_Fe_ctl, &x87_cw, &cw))
+        return 1;
+    if (!fenv_decode(env->_Fe_stat, &x87_stat, &stat))
+        return 1;
+
+#if defined(__i386__) || (defined(__x86_64__) && !defined(__arm64ec__))
+    __mingw_setfp(&x87_cw, mask, &x87_stat, ~0);
+    if (__mingw_has_sse())
+        __mingw_setfp_sse(&cw, mask, &stat, ~0);
+#else
+    __mingw_setfp(&cw, mask, &stat, ~0);
+#endif
+    return 0;
 }
diff --git a/mingw-w64-crt/misc/fesetexceptflag.c 
b/mingw-w64-crt/misc/fesetexceptflag.c
index 3e6012c76..9faedbb53 100644
--- a/mingw-w64-crt/misc/fesetexceptflag.c
+++ b/mingw-w64-crt/misc/fesetexceptflag.c
@@ -4,7 +4,6 @@
  * No warranty is given; refer to the file DISCLAIMER.PD within this package.
  */
 
-#include <fenv.h>
 #include <internal.h>
 
 /* 7.6.2.4
@@ -16,39 +15,16 @@
    represented by the argument excepts. This function does not raise
    exceptions, but only sets the state of the flags. */
 
-int fesetexceptflag (const fexcept_t * flagp, int excepts)
+int fesetexceptflag(const fexcept_t *status, int excepts)
 {
-  fenv_t _env;
+    fenv_t env;
 
-  excepts &= FE_ALL_EXCEPT;
+    excepts &= FE_ALL_EXCEPT;
+    if(!excepts)
+        return 0;
 
-#if defined(_ARM_) || defined(__arm__)
-  __asm__ volatile ("fmrx %0, FPSCR" : "=r" (_env));
-  _env.__cw &= ~excepts;
-  _env.__cw |= (*flagp & excepts);
-  __asm__ volatile ("fmxr FPSCR, %0" : : "r" (_env));
-#elif defined(_ARM64_) || defined(__aarch64__)
-  unsigned __int64 fpcr;
-  (void) _env;
-  __asm__ volatile ("mrs %0, fpcr" : "=r" (fpcr));
-  fpcr &= ~excepts;
-  fpcr |= (*flagp & excepts);
-  __asm__ volatile ("msr fpcr, %0" : : "r" (fpcr));
-#else
-  __asm__ volatile ("fnstenv %0;" : "=m" (_env));
-  _env.__status_word &= ~excepts;
-  _env.__status_word |= (*flagp & excepts);
-  __asm__ volatile ("fldenv %0;" : : "m" (_env));
-
-  if (__mingw_has_sse ())
-    {
-      int sse_cw;
-      __asm__ volatile ("stmxcsr %0;" : "=m" (sse_cw));
-      sse_cw &= ~excepts;
-      sse_cw |= (*flagp & excepts);
-      __asm__ volatile ("ldmxcsr %0" : : "m" (sse_cw));
-    }
-
-#endif /* defined(_ARM_) || defined(__arm__) || defined(_ARM64_) || 
defined(__aarch64__) */
-  return 0;
+    fegetenv(&env);
+    env._Fe_stat &= ~fenv_encode(excepts, excepts);
+    env._Fe_stat |= *status & fenv_encode(excepts, excepts);
+    return fesetenv(&env);
 }
diff --git a/mingw-w64-crt/misc/fesetround.c b/mingw-w64-crt/misc/fesetround.c
index e51f46cd7..9bf7c3599 100644
--- a/mingw-w64-crt/misc/fesetround.c
+++ b/mingw-w64-crt/misc/fesetround.c
@@ -13,43 +13,10 @@
     to the value of a rounding direction macro, the rounding direction
     is not changed.  */
 
-int fesetround (int mode)
+int fesetround(int round_mode)
 {
-#if defined(_ARM_) || defined(__arm__)
-  fenv_t _env;
-  if ((mode & ~(FE_TONEAREST | FE_DOWNWARD | FE_UPWARD | FE_TOWARDZERO)) != 0)
-    return -1;
-  __asm__ volatile ("fmrx %0, FPSCR" : "=r" (_env));
-  _env.__cw &= ~(FE_TONEAREST | FE_DOWNWARD |  FE_UPWARD | FE_TOWARDZERO);
-  _env.__cw |= mode;
-  __asm__ volatile ("fmxr FPSCR, %0" : : "r" (_env));
-#elif defined(_ARM64_) || defined(__aarch64__)
-  unsigned __int64 fpcr;
-  if ((mode & ~(FE_TONEAREST | FE_DOWNWARD | FE_UPWARD | FE_TOWARDZERO)) != 0)
-    return -1;
-  __asm__ volatile ("mrs %0, fpcr" : "=r" (fpcr));
-  fpcr &= ~(FE_TONEAREST | FE_DOWNWARD |  FE_UPWARD | FE_TOWARDZERO);
-  fpcr |= mode;
-  __asm__ volatile ("msr fpcr, %0" : : "r" (fpcr));
-#else
-  unsigned short _cw;
-  if ((mode & ~(FE_TONEAREST | FE_DOWNWARD | FE_UPWARD | FE_TOWARDZERO))
-      != 0)
-    return -1;
-  __asm__ volatile ("fnstcw %0;": "=m" (*&_cw));
-  _cw &= ~0xc00;
-  _cw |= mode;
-  __asm__ volatile ("fldcw %0;" : : "m" (*&_cw));
-
-  if (__mingw_has_sse ())
-    {
-      int mxcsr;
-
-      __asm__ volatile ("stmxcsr %0" : "=m" (*&mxcsr));
-      mxcsr &= ~0x6000;
-      mxcsr |= mode << 3;
-      __asm__ volatile ("ldmxcsr %0" : : "m" (*&mxcsr));
-    }
-#endif /* defined(_ARM_) || defined(__arm__) || defined(_ARM64_) || 
defined(__aarch64__) */
-  return 0;
+    if (round_mode & (~_MCW_RC))
+        return 1;
+    __mingw_controlfp(round_mode, _MCW_RC);
+    return 0;
 }
diff --git a/mingw-w64-crt/misc/fetestexcept.c 
b/mingw-w64-crt/misc/fetestexcept.c
index 04e75970f..4c9abfb5a 100644
--- a/mingw-w64-crt/misc/fetestexcept.c
+++ b/mingw-w64-crt/misc/fetestexcept.c
@@ -15,26 +15,7 @@
    exception macros corresponding to the currently set exceptions
    included in excepts. */
 
-int fetestexcept (int excepts)
+int fetestexcept(int flags)
 {
-#if defined(_ARM_) || defined(__arm__)
-  fenv_t _env;
-  __asm__ volatile ("fmrx %0, FPSCR" : "=r" (_env));
-  return _env.__cw & excepts & FE_ALL_EXCEPT;
-#elif defined(_ARM64_) || defined(__aarch64__)
-  unsigned __int64 fpcr;
-  __asm__ volatile ("mrs %0, fpcr" : "=r" (fpcr));
-  return fpcr & excepts & FE_ALL_EXCEPT;
-#else
-  unsigned short _sw;
-  __asm__ __volatile__ ("fnstsw %%ax" : "=a" (_sw));
-
-  if (__mingw_has_sse ())
-    {
-      int sse_sw;
-      __asm__ __volatile__ ("stmxcsr %0;" : "=m" (sse_sw));
-      _sw |= sse_sw;
-    }
-  return _sw & excepts & FE_ALL_EXCEPT;
-#endif /* defined(_ARM_) || defined(__arm__) || defined(_ARM64_) || 
defined(__aarch64__) */
+    return __mingw_statusfp() & flags;
 }
diff --git a/mingw-w64-crt/misc/feupdateenv.c b/mingw-w64-crt/misc/feupdateenv.c
index aea95902b..8774d7402 100644
--- a/mingw-w64-crt/misc/feupdateenv.c
+++ b/mingw-w64-crt/misc/feupdateenv.c
@@ -13,13 +13,9 @@
    set by a call to feholdexcept or fegetenv, or equal the macro
    FE_DFL_ENV or an implementation-defined environment macro. */
 
-/* FIXME: this works but surely there must be a better way.  */
-
-int feupdateenv (const fenv_t * envp)
+int feupdateenv(const fenv_t *env)
 {
-  unsigned int _fexcept = fetestexcept (FE_ALL_EXCEPT); /*save excepts */
-  fesetenv (envp); /* install the env  */
-  feraiseexcept (_fexcept); /* raise the except */
-  return 0;
+    int except = fetestexcept(FE_ALL_EXCEPT);
+    return fesetenv(env) || feraiseexcept(except);
 }
 
diff --git a/mingw-w64-crt/misc/mingw_controlfp.c 
b/mingw-w64-crt/misc/mingw_controlfp.c
new file mode 100644
index 000000000..c7f553a4d
--- /dev/null
+++ b/mingw-w64-crt/misc/mingw_controlfp.c
@@ -0,0 +1,53 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#include "internal.h"
+
+#if defined(__i386__) || (defined(__x86_64__) && !defined(__arm64ec__))
+/* Internal MinGW version of _control87_2 */
+int __mingw_control87_2( unsigned int newval, unsigned int mask,
+                         unsigned int *x86_cw, unsigned int *sse2_cw )
+{
+    if (x86_cw)
+    {
+        *x86_cw = newval;
+        __mingw_setfp(x86_cw, mask, NULL, 0);
+    }
+
+    if (!sse2_cw) return 1;
+
+    if (__mingw_has_sse())
+    {
+        *sse2_cw = newval;
+        __mingw_setfp_sse(sse2_cw, mask, NULL, 0);
+    }
+    else *sse2_cw = 0;
+
+    return 1;
+}
+#endif
+
+/* Internal MinGW version of _control87 */
+unsigned int __mingw_controlfp(unsigned int newval, unsigned int mask)
+{
+    unsigned int flags = 0;
+#if defined(__i386__) || (defined(__x86_64__) && !defined(__arm64ec__))
+    unsigned int sse2_cw;
+
+    __mingw_control87_2( newval, mask, &flags, &sse2_cw );
+
+    if (__mingw_has_sse())
+    {
+        if ((flags ^ sse2_cw) & (_MCW_EM | _MCW_RC)) flags |= _EM_AMBIGUOUS;
+        flags |= sse2_cw;
+    }
+#else
+    flags = newval;
+    __mingw_setfp(&flags, mask, NULL, 0);
+#endif
+    return flags;
+}
+
diff --git a/mingw-w64-crt/misc/mingw_setfp.c b/mingw-w64-crt/misc/mingw_setfp.c
new file mode 100644
index 000000000..14fda6e24
--- /dev/null
+++ b/mingw-w64-crt/misc/mingw_setfp.c
@@ -0,0 +1,373 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#include "internal.h"
+
+#if defined(__i386__) || defined(__x86_64__)
+
+static unsigned int get_mxcsr(void)
+{
+    unsigned int ret;
+#ifdef __arm64ec__
+    extern NTSTATUS (*__os_arm64x_get_x64_information)(ULONG,void*,void*);
+    __os_arm64x_get_x64_information( 0, &ret, NULL );
+#else
+    __asm__ __volatile__( "stmxcsr %0" : "=m" (ret) );
+#endif
+    return ret;
+}
+
+static void set_mxcsr( unsigned int val )
+{
+#ifdef __arm64ec__
+    extern NTSTATUS (*__os_arm64x_set_x64_information)(ULONG,ULONG_PTR,void*);
+    __os_arm64x_set_x64_information( 0, val, NULL );
+#else
+    __asm__ __volatile__( "ldmxcsr %0" : : "m" (val) );
+#endif
+}
+
+void __mingw_setfp_sse( unsigned int *cw, unsigned int cw_mask, unsigned int 
*sw, unsigned int sw_mask )
+{
+    unsigned int old_fpword, fpword = get_mxcsr();
+    unsigned int flags;
+
+    old_fpword = fpword;
+
+    cw_mask &= _MCW_EM | _MCW_RC | _MCW_DN;
+    sw_mask &= _MCW_EM;
+
+    if (sw)
+    {
+        flags = 0;
+        if (fpword & 0x1) flags |= _SW_INVALID;
+        if (fpword & 0x2) flags |= _SW_DENORMAL;
+        if (fpword & 0x4) flags |= _SW_ZERODIVIDE;
+        if (fpword & 0x8) flags |= _SW_OVERFLOW;
+        if (fpword & 0x10) flags |= _SW_UNDERFLOW;
+        if (fpword & 0x20) flags |= _SW_INEXACT;
+
+        *sw = (flags & ~sw_mask) | (*sw & sw_mask);
+        fpword &= ~0x3f;
+        if (*sw & _SW_INVALID) fpword |= 0x1;
+        if (*sw & _SW_DENORMAL) fpword |= 0x2;
+        if (*sw & _SW_ZERODIVIDE) fpword |= 0x4;
+        if (*sw & _SW_OVERFLOW) fpword |= 0x8;
+        if (*sw & _SW_UNDERFLOW) fpword |= 0x10;
+        if (*sw & _SW_INEXACT) fpword |= 0x20;
+        *sw = flags;
+    }
+
+    if (cw)
+    {
+        flags = 0;
+        if (fpword & 0x80) flags |= _EM_INVALID;
+        if (fpword & 0x100) flags |= _EM_DENORMAL;
+        if (fpword & 0x200) flags |= _EM_ZERODIVIDE;
+        if (fpword & 0x400) flags |= _EM_OVERFLOW;
+        if (fpword & 0x800) flags |= _EM_UNDERFLOW;
+        if (fpword & 0x1000) flags |= _EM_INEXACT;
+        switch (fpword & 0x6000)
+        {
+        case 0x6000: flags |= _RC_UP|_RC_DOWN; break;
+        case 0x4000: flags |= _RC_UP; break;
+        case 0x2000: flags |= _RC_DOWN; break;
+        }
+        switch (fpword & 0x8040)
+        {
+        case 0x0040: flags |= _DN_FLUSH_OPERANDS_SAVE_RESULTS; break;
+        case 0x8000: flags |= _DN_SAVE_OPERANDS_FLUSH_RESULTS; break;
+        case 0x8040: flags |= _DN_FLUSH; break;
+        }
+
+        *cw = (flags & ~cw_mask) | (*cw & cw_mask);
+        fpword &= ~0xffc0;
+        if (*cw & _EM_INVALID) fpword |= 0x80;
+        if (*cw & _EM_DENORMAL) fpword |= 0x100;
+        if (*cw & _EM_ZERODIVIDE) fpword |= 0x200;
+        if (*cw & _EM_OVERFLOW) fpword |= 0x400;
+        if (*cw & _EM_UNDERFLOW) fpword |= 0x800;
+        if (*cw & _EM_INEXACT) fpword |= 0x1000;
+        switch (*cw & _MCW_RC)
+        {
+        case _RC_UP|_RC_DOWN: fpword |= 0x6000; break;
+        case _RC_UP: fpword |= 0x4000; break;
+        case _RC_DOWN: fpword |= 0x2000; break;
+        }
+        switch (*cw & _MCW_DN)
+        {
+        case _DN_FLUSH_OPERANDS_SAVE_RESULTS: fpword |= 0x0040; break;
+        case _DN_SAVE_OPERANDS_FLUSH_RESULTS: fpword |= 0x8000; break;
+        case _DN_FLUSH: fpword |= 0x8040; break;
+        }
+
+        /* clear status word if anything changes */
+        if (fpword != old_fpword && !sw) fpword &= ~0x3f;
+    }
+
+    if (fpword != old_fpword) set_mxcsr( fpword );
+}
+#endif
+
+void __mingw_setfp( unsigned int *cw, unsigned int cw_mask,
+                    unsigned int *sw, unsigned int sw_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 int flags;
+
+    cw_mask &= _MCW_EM | _MCW_IC | _MCW_RC | _MCW_PC;
+    sw_mask &= _MCW_EM;
+
+    if (sw)
+    {
+        __asm__ __volatile__( "fstsw %0" : "=m" (newsw) );
+        oldsw = newsw;
+
+        flags = 0;
+        if (newsw & 0x1) flags |= _SW_INVALID;
+        if (newsw & 0x2) flags |= _SW_DENORMAL;
+        if (newsw & 0x4) flags |= _SW_ZERODIVIDE;
+        if (newsw & 0x8) flags |= _SW_OVERFLOW;
+        if (newsw & 0x10) flags |= _SW_UNDERFLOW;
+        if (newsw & 0x20) flags |= _SW_INEXACT;
+
+        *sw = (flags & ~sw_mask) | (*sw & sw_mask);
+        newsw &= ~0x3f;
+        if (*sw & _SW_INVALID) newsw |= 0x1;
+        if (*sw & _SW_DENORMAL) newsw |= 0x2;
+        if (*sw & _SW_ZERODIVIDE) newsw |= 0x4;
+        if (*sw & _SW_OVERFLOW) newsw |= 0x8;
+        if (*sw & _SW_UNDERFLOW) newsw |= 0x10;
+        if (*sw & _SW_INEXACT) newsw |= 0x20;
+        *sw = flags;
+    }
+
+    if (cw)
+    {
+        __asm__ __volatile__( "fstcw %0" : "=m" (newcw) );
+        oldcw = newcw;
+
+        flags = 0;
+        if (newcw & 0x1) flags |= _EM_INVALID;
+        if (newcw & 0x2) flags |= _EM_DENORMAL;
+        if (newcw & 0x4) flags |= _EM_ZERODIVIDE;
+        if (newcw & 0x8) flags |= _EM_OVERFLOW;
+        if (newcw & 0x10) flags |= _EM_UNDERFLOW;
+        if (newcw & 0x20) flags |= _EM_INEXACT;
+        switch (newcw & 0xc00)
+        {
+        case 0xc00: flags |= _RC_UP|_RC_DOWN; break;
+        case 0x800: flags |= _RC_UP; break;
+        case 0x400: flags |= _RC_DOWN; break;
+        }
+        switch (newcw & 0x300)
+        {
+        case 0x0: flags |= _PC_24; break;
+        case 0x200: flags |= _PC_53; break;
+        case 0x300: flags |= _PC_64; break;
+        }
+        if (newcw & 0x1000) flags |= _IC_AFFINE;
+
+        *cw = (flags & ~cw_mask) | (*cw & cw_mask);
+        newcw &= ~0x1f3f;
+        if (*cw & _EM_INVALID) newcw |= 0x1;
+        if (*cw & _EM_DENORMAL) newcw |= 0x2;
+        if (*cw & _EM_ZERODIVIDE) newcw |= 0x4;
+        if (*cw & _EM_OVERFLOW) newcw |= 0x8;
+        if (*cw & _EM_UNDERFLOW) newcw |= 0x10;
+        if (*cw & _EM_INEXACT) newcw |= 0x20;
+        switch (*cw & _MCW_RC)
+        {
+        case _RC_UP|_RC_DOWN: newcw |= 0xc00; break;
+        case _RC_UP: newcw |= 0x800; break;
+        case _RC_DOWN: newcw |= 0x400; break;
+        }
+        switch (*cw & _MCW_PC)
+        {
+        case _PC_64: newcw |= 0x300; break;
+        case _PC_53: newcw |= 0x200; break;
+        case _PC_24: newcw |= 0x0; break;
+        }
+        if (*cw & _IC_AFFINE) newcw |= 0x1000;
+    }
+
+    if (oldsw != newsw && (newsw & 0x3f))
+    {
+        struct {
+            WORD control_word;
+            WORD unused1;
+            WORD status_word;
+            WORD unused2;
+            WORD tag_word;
+            WORD unused3;
+            DWORD instruction_pointer;
+            WORD code_segment;
+            WORD unused4;
+            DWORD operand_addr;
+            WORD data_segment;
+            WORD unused5;
+        } fenv;
+
+        __asm__ __volatile__( "fnstenv %0" : "=m" (fenv) );
+        fenv.control_word = newcw;
+        fenv.status_word = newsw;
+        __asm__ __volatile__( "fldenv %0" : : "m" (fenv) : "st", "st(1)",
+                "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)" );
+        return;
+    }
+
+    if (oldsw != newsw)
+        __asm__ __volatile__( "fnclex" );
+    if (oldcw != newcw)
+        __asm__ __volatile__( "fldcw %0" : : "m" (newcw) );
+#elif defined(__aarch64__)
+    ULONG_PTR old_fpsr = 0, fpsr = 0, old_fpcr = 0, fpcr = 0;
+    unsigned int flags;
+
+    cw_mask &= _MCW_EM | _MCW_RC;
+    sw_mask &= _MCW_EM;
+
+    if (sw)
+    {
+        __asm__ __volatile__( "mrs %0, fpsr" : "=r" (fpsr) );
+        old_fpsr = fpsr;
+
+        flags = 0;
+        if (fpsr & 0x1) flags |= _SW_INVALID;
+        if (fpsr & 0x2) flags |= _SW_ZERODIVIDE;
+        if (fpsr & 0x4) flags |= _SW_OVERFLOW;
+        if (fpsr & 0x8) flags |= _SW_UNDERFLOW;
+        if (fpsr & 0x10) flags |= _SW_INEXACT;
+        if (fpsr & 0x80) flags |= _SW_DENORMAL;
+
+        *sw = (flags & ~sw_mask) | (*sw & sw_mask);
+        fpsr &= ~0x9f;
+        if (*sw & _SW_INVALID) fpsr |= 0x1;
+        if (*sw & _SW_ZERODIVIDE) fpsr |= 0x2;
+        if (*sw & _SW_OVERFLOW) fpsr |= 0x4;
+        if (*sw & _SW_UNDERFLOW) fpsr |= 0x8;
+        if (*sw & _SW_INEXACT) fpsr |= 0x10;
+        if (*sw & _SW_DENORMAL) fpsr |= 0x80;
+        *sw = flags;
+    }
+
+    if (cw)
+    {
+        __asm__ __volatile__( "mrs %0, fpcr" : "=r" (fpcr) );
+        old_fpcr = fpcr;
+
+        flags = 0;
+        if (!(fpcr & 0x100)) flags |= _EM_INVALID;
+        if (!(fpcr & 0x200)) flags |= _EM_ZERODIVIDE;
+        if (!(fpcr & 0x400)) flags |= _EM_OVERFLOW;
+        if (!(fpcr & 0x800)) flags |= _EM_UNDERFLOW;
+        if (!(fpcr & 0x1000)) flags |= _EM_INEXACT;
+        if (!(fpcr & 0x8000)) flags |= _EM_DENORMAL;
+        switch (fpcr & 0xc00000)
+        {
+        case 0x400000: flags |= _RC_UP; break;
+        case 0x800000: flags |= _RC_DOWN; break;
+        case 0xc00000: flags |= _RC_CHOP; break;
+        }
+
+        *cw = (flags & ~cw_mask) | (*cw & cw_mask);
+        fpcr &= ~0xc09f00ul;
+        if (!(*cw & _EM_INVALID)) fpcr |= 0x100;
+        if (!(*cw & _EM_ZERODIVIDE)) fpcr |= 0x200;
+        if (!(*cw & _EM_OVERFLOW)) fpcr |= 0x400;
+        if (!(*cw & _EM_UNDERFLOW)) fpcr |= 0x800;
+        if (!(*cw & _EM_INEXACT)) fpcr |= 0x1000;
+        if (!(*cw & _EM_DENORMAL)) fpcr |= 0x8000;
+        switch (*cw & _MCW_RC)
+        {
+        case _RC_CHOP: fpcr |= 0xc00000; break;
+        case _RC_UP: fpcr |= 0x400000; break;
+        case _RC_DOWN: fpcr |= 0x800000; break;
+        }
+    }
+
+    /* mask exceptions if needed */
+    if (old_fpcr != fpcr && ~(old_fpcr >> 8) & fpsr & 0x9f != fpsr & 0x9f)
+    {
+        ULONG_PTR mask = fpcr & ~0x9f00;
+        __asm__ __volatile__( "msr fpcr, %0" :: "r" (mask) );
+    }
+
+    if (old_fpsr != fpsr)
+        __asm__ __volatile__( "msr fpsr, %0" :: "r" (fpsr) );
+    if (old_fpcr != fpcr)
+        __asm__ __volatile__( "msr fpcr, %0" :: "r" (fpcr) );
+#elif defined(__arm__)
+    DWORD old_fpscr, fpscr;
+    unsigned int flags;
+
+    __asm__ __volatile__( "vmrs %0, fpscr" : "=r" (fpscr) );
+    old_fpscr = fpscr;
+
+    cw_mask &= _MCW_EM | _MCW_RC;
+    sw_mask &= _MCW_EM;
+
+    if (sw)
+    {
+        flags = 0;
+        if (fpscr & 0x1) flags |= _SW_INVALID;
+        if (fpscr & 0x2) flags |= _SW_ZERODIVIDE;
+        if (fpscr & 0x4) flags |= _SW_OVERFLOW;
+        if (fpscr & 0x8) flags |= _SW_UNDERFLOW;
+        if (fpscr & 0x10) flags |= _SW_INEXACT;
+        if (fpscr & 0x80) flags |= _SW_DENORMAL;
+
+        *sw = (flags & ~sw_mask) | (*sw & sw_mask);
+        fpscr &= ~0x9f;
+        if (*sw & _SW_INVALID) fpscr |= 0x1;
+        if (*sw & _SW_ZERODIVIDE) fpscr |= 0x2;
+        if (*sw & _SW_OVERFLOW) fpscr |= 0x4;
+        if (*sw & _SW_UNDERFLOW) fpscr |= 0x8;
+        if (*sw & _SW_INEXACT) fpscr |= 0x10;
+        if (*sw & _SW_DENORMAL) fpscr |= 0x80;
+        *sw = flags;
+    }
+
+    if (cw)
+    {
+        flags = 0;
+        if (!(fpscr & 0x100)) flags |= _EM_INVALID;
+        if (!(fpscr & 0x200)) flags |= _EM_ZERODIVIDE;
+        if (!(fpscr & 0x400)) flags |= _EM_OVERFLOW;
+        if (!(fpscr & 0x800)) flags |= _EM_UNDERFLOW;
+        if (!(fpscr & 0x1000)) flags |= _EM_INEXACT;
+        if (!(fpscr & 0x8000)) flags |= _EM_DENORMAL;
+        switch (fpscr & 0xc00000)
+        {
+        case 0x400000: flags |= _RC_UP; break;
+        case 0x800000: flags |= _RC_DOWN; break;
+        case 0xc00000: flags |= _RC_CHOP; break;
+        }
+
+        *cw = (flags & ~cw_mask) | (*cw & cw_mask);
+        fpscr &= ~0xc09f00ul;
+        if (!(*cw & _EM_INVALID)) fpscr |= 0x100;
+        if (!(*cw & _EM_ZERODIVIDE)) fpscr |= 0x200;
+        if (!(*cw & _EM_OVERFLOW)) fpscr |= 0x400;
+        if (!(*cw & _EM_UNDERFLOW)) fpscr |= 0x800;
+        if (!(*cw & _EM_INEXACT)) fpscr |= 0x1000;
+        if (!(*cw & _EM_DENORMAL)) fpscr |= 0x8000;
+        switch (*cw & _MCW_RC)
+        {
+        case _RC_CHOP: fpscr |= 0xc00000; break;
+        case _RC_UP: fpscr |= 0x400000; break;
+        case _RC_DOWN: fpscr |= 0x800000; break;
+        }
+    }
+
+    if (old_fpscr != fpscr)
+        __asm__ __volatile__( "vmsr fpscr, %0" :: "r" (fpscr) );
+#endif
+}
diff --git a/mingw-w64-headers/crt/fenv.h b/mingw-w64-headers/crt/fenv.h
index 871b7a831..d5161757d 100644
--- a/mingw-w64-headers/crt/fenv.h
+++ b/mingw-w64-headers/crt/fenv.h
@@ -8,42 +8,28 @@
 
 #include <float.h>
 
-#if defined(_ARM_) || defined(__arm__) || defined(_ARM64_) || 
defined(__aarch64__)
 
 /* FPU status word exception flags */
-#define FE_INVALID      0x01
-#define FE_DIVBYZERO    0x02
-#define FE_OVERFLOW     0x04
-#define FE_UNDERFLOW    0x08
-#define FE_INEXACT      0x10
-#define FE_ALL_EXCEPT   (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | 
FE_UNDERFLOW | FE_INEXACT)
+#define FE_INEXACT     _SW_INEXACT
+#define FE_UNDERFLOW   _SW_UNDERFLOW
+#define FE_OVERFLOW    _SW_OVERFLOW
+#define FE_DIVBYZERO   _SW_ZERODIVIDE
+#define FE_INVALID     _SW_INVALID
+#define FE_ALL_EXCEPT  (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | FE_OVERFLOW | 
FE_UNDERFLOW)
 
 /* FPU control word rounding flags */
-#define FE_TONEAREST    0x00000000
-#define FE_UPWARD       0x00400000
-#define FE_DOWNWARD     0x00800000
-#define FE_TOWARDZERO   0x00c00000
+#define FE_TONEAREST   _RC_NEAR
+#define FE_UPWARD      _RC_UP
+#define FE_DOWNWARD    _RC_DOWN
+#define FE_TOWARDZERO  _RC_CHOP
+
+#if defined(_ARM_) || defined(__arm__) || defined(_ARM64_) || 
defined(__aarch64__)
 
 /* Amount to shift by to convert an exception to a mask bit.  */
 #define FE_EXCEPT_SHIFT 0x08
 
 #else
 
-#define FE_INVALID     0x01
-#define FE_DENORMAL    0x02
-#define FE_DIVBYZERO   0x04
-#define FE_OVERFLOW    0x08
-#define FE_UNDERFLOW   0x10
-#define FE_INEXACT     0x20
-#define FE_ALL_EXCEPT (FE_INVALID | FE_DENORMAL | FE_DIVBYZERO \
-                      | FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT)
-
-/* FPU control word rounding flags */
-#define FE_TONEAREST   0x0000
-#define FE_DOWNWARD    0x0400
-#define FE_UPWARD      0x0800
-#define FE_TOWARDZERO  0x0c00
-
 /* The MXCSR exception flags are the same as the
    FE flags. */
 #define __MXCSR_EXCEPT_FLAG_SHIFT 0
@@ -62,39 +48,11 @@
 
 #if defined(_ARM_) || defined(__arm__) || defined(_ARM64_) || 
defined(__aarch64__)
 
-/* Type representing floating-point environment.  */
-typedef struct
-{
-    unsigned int __cw;
-} fenv_t;
-
 /* If the default argument is used we use this value.  */
 #define FE_DFL_ENV  ((const fenv_t *) -1l)
 
 #else
 
-/* This 32-byte struct represents the entire floating point
-   environment as stored by fnstenv or fstenv, augmented by
-   the  contents of the MXCSR register, as stored by stmxcsr
-   (if CPU supports it). */
-typedef struct
-{
-  unsigned short __control_word;
-  unsigned short __unused0;
-  unsigned short __status_word;
-  unsigned short __unused1;
-  unsigned short __tag_word;
-  unsigned short __unused2;  
-  unsigned int  __ip_offset;    /* instruction pointer offset */
-  unsigned short __ip_selector;  
-  unsigned short __opcode;
-  unsigned int  __data_offset;
-  unsigned short __data_selector;  
-  unsigned short __unused3;
-  unsigned int   __mxcsr; /* contents of the MXCSR register  */
-} fenv_t;
-
-
 /*The C99 standard (7.6.9) allows us to define implementation-specific macros 
for
   different fp environments */
   
@@ -110,6 +68,12 @@ typedef struct
 
 #endif /* defined(_ARM_) || defined(__arm__) */
 
+typedef struct
+{
+    unsigned long _Fe_ctl;
+    unsigned long _Fe_stat;
+} fenv_t;
+
 /* Type representing exception flags. */
 typedef unsigned long fexcept_t;
 
-- 
2.48.1



_______________________________________________
Mingw-w64-public mailing list
Mingw-w64-public@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to