Collin Funk wrote:
> But, I think most of my issues came from trying to implement 'fegetenv'
> and 'fesetenv'.

Well, you started with the wrong module. Because the module
'fenv-environment' depends on 'fenv-exceptions-tracking-c99'
and 'fenv-exceptions-trapping', you need to have these two working
before you can tackle the module 'fenv-environment'. So, it's
best to work bottom-up:
  1. fenv-h
  2. fenv-rounding
  3. fenv-exceptions-tracking-c99, fenv-exceptions-tracking-c23.
  4. fenv-exceptions-trapping
  5. fenv-exceptions-state-c99, fenv-exceptions-state-c23.
  6. fenv-environment

> Because in those functions we must be able to save and
> restore the X87 FPU status & control word and the MXCSR control and
> status register in 2 32-bit integers:
> 
>     typedef struct
>     {
>       unsigned long _Fe_ctl;
>       unsigned long _Fe_stat;
>     } fenv_t;
> 
> I thought the proper way to do this was to reserve the low bits for the
> exception and trap bits and then use the higher bits to mark if they
> were set in the X87 FPU, MXCSR, or both.

Rather than invent this bit assignment in gnulib, it's better to use the
one from mingw13 — because that guarantees compatility with other mingw
code.

But of course a simple implementation of this bit assignment is preferred,
compared to the terribly convoluted on in the mingw code.

> -# if defined _MSC_VER
> +# if defined _MSC_VER || (defined __MINGW32__ && FE_INVALID != 0x01)
>  /* The MSVC header files have different values for the floating-point 
> exceptions
>     than all the other platforms.  Define some handy macros for conversion.  
> */
>  #  define exceptions_to_x86hardware(exceptions) \

That was a step in the right direction.

I've completed the port and found that the following configuration variables
need to be set in order to work around mingw 13 bugs:
  gl_cv_func_fesetexceptflag_works1="guessing no"
  gl_cv_func_fesetenv_works="guessing no"
  gl_cv_func_feholdexcept_works="guessing no"
  gl_cv_func_feeupdateenv_works="guessing no"

In summary, the mingw approach that considers both the 387 unit and the
SSE floating-point unit basically works. But, as usual with mingw, it
comes with bugs:
  - The older mingw bugs in this area are still present.
  - They added two new bugs.


2025-06-11  Bruno Haible  <[email protected]>

        fenv*: Fix compilation error with mingw 13.
        It's caused by an ABI change in mingw:
        
<https://sourceforge.net/p/mingw-w64/mingw-w64/ci/5c5973cf5f021db8fd75e9667e63881ccd169320/>.
        Reported by Collin Funk in
        <https://lists.gnu.org/archive/html/bug-gnulib/2025-04/msg00215.html>.
        * m4/fenv-environment.m4 (gl_FENV_ENVIRONMENT): Update comments for
        mingw 13.
        * lib/fenv-private.h (exceptions_to_x86hardware,
        x86hardware_to_exceptions): On mingw >= 13, define these like on MSVC.
        * lib/fenv-round.c (fegetround, fesetround): Do the safe mapping also on
        mingw >= 13.
        * lib/fenv-except-state-set.c (fesetexceptflag): Do the
        exceptions_to_x86hardware conversion also on other platforms than MSVC.
        * lib/fenv-except-tracking-clear.c (feclearexcept): Likewise.
        * lib/fenv-except-tracking-set.c (fesetexcept): Likewise.
        * lib/fenv-except-trapping.c (feenableexcept, fedisableexcept,
        fegetexcept): Do the exceptions_to_x86hardware and
        x86hardware_to_exceptions conversions also on other platforms than MSVC.
        * lib/fenv-env.c (fegetenv, fesetenv): Add new implementation for
        mingw >= 13.
        * doc/posix-functions/fesetenv.texi: Mention the new mingw bug.
        * doc/posix-functions/feupdateenv.texi: Mention the new mingw bug.

diff --git a/doc/posix-functions/fesetenv.texi 
b/doc/posix-functions/fesetenv.texi
index 649b85e929..e759d77683 100644
--- a/doc/posix-functions/fesetenv.texi
+++ b/doc/posix-functions/fesetenv.texi
@@ -15,7 +15,9 @@
 @item
 This function does not work on some platforms:
 @c 
https://cgit.freebsd.org/src/commit/?id=34cc08e336987a8ebc316595e3f552a4c09f1fd4
-FreeBSD 12.2/arm64.
+FreeBSD 12.2/arm64,
+@c fesetenv (FE_DFL_ENV) does not reset the rounding direction.
+mingw 13.
 @item
 This function does not restore the floating-point exception trap bits
 on some platforms:
diff --git a/doc/posix-functions/feupdateenv.texi 
b/doc/posix-functions/feupdateenv.texi
index 6bff8768b2..5f9ae3a09f 100644
--- a/doc/posix-functions/feupdateenv.texi
+++ b/doc/posix-functions/feupdateenv.texi
@@ -22,7 +22,9 @@
 @c 
https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a8c79c4088e8c04e4297936efa0dee6c8e6e974d
 glibc 2.5/ia64,
 @c 
https://cgit.freebsd.org/src/commit/?id=34cc08e336987a8ebc316595e3f552a4c09f1fd4
-FreeBSD 12.2/arm64.
+FreeBSD 12.2/arm64,
+@c feupdateenv (FE_DFL_ENV) does not restore the rounding direction.
+mingw 13.
 @item
 This function forgets about the currently set floating-point exception flags
 on some platforms:
diff --git a/lib/fenv-env.c b/lib/fenv-env.c
index f363c5f2e2..637bc3e8c8 100644
--- a/lib/fenv-env.c
+++ b/lib/fenv-env.c
@@ -105,7 +105,8 @@ fesetenv (fenv_t const *envp)
 
 # if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined 
_M_IX86)
 
-/* On all OSes except MSVC, macOS, Solaris, fenv_t is binary-equivalent to
+/* On all OSes except MSVC, mingw ≥ 13, macOS, Solaris, fenv_t is
+   binary-equivalent to
      - either a x86_387_fenv_t (7 'unsigned int' words)
        where mxcsr is stored:
          - for glibc/i386: in __eip = more[1].
@@ -118,6 +119,23 @@ fesetenv (fenv_t const *envp)
      _Fe_ctl bits 9..8 are the rounding direction,
      _Fe_ctl bits 4..0 are the exception trap bits (inverted),
      _Fe_stat bits 4..0 are the exception flags.
+   On mingw ≥ 13, it's a
+     struct { unsigned int _Fe_ctl, _Fe_stat; }, where
+     _Fe_ctl bits 9..8 are the rounding direction,
+     _Fe_ctl bits 23..22 are the rounding direction
+                             from the 387 compatible floating-point unit,
+     _Fe_ctl bits 31..30 are the rounding direction
+                             from the SSE compatible floating-point unit,
+     _Fe_ctl bits 4..0 are the exception trap bits (inverted),
+     _Fe_ctl bits 20..16 are the exception trap bits (inverted)
+                             from the 387 compatible floating-point unit,
+     _Fe_ctl bits 28..24 are the exception trap bits (inverted)
+                             from the SSE compatible floating-point unit,
+     _Fe_stat bits 4..0 are the exception flags,
+     _Fe_stat bits 20..16 are the exception flags
+                              from the 387 compatible floating-point unit,
+     _Fe_stat bits 28..24 are the exception flags
+                              from the SSE compatible floating-point unit.
    On macOS, it's a
      struct { unsigned short __control, __status; unsigned int __mxcsr; ... }.
    On Solaris, it's a
@@ -147,6 +165,38 @@ fegetenv (fenv_t *envp)
                    | (mxcsr & (1 << 4) ? FE_UNDERFLOW : 0)
                    | (mxcsr & (1 << 5) ? FE_INEXACT : 0);
 
+#  elif (defined __MINGW32__ && FE_INVALID != 0x01) /* mingw ≥ 13 */
+
+  x86_387_fenv_t env387;
+
+  __asm__ __volatile__ ("fnstenv %0" : "=m" (*&env387));
+  /* Note: fnstenv masks all floating-point exceptions until the fldcw
+     below.  */
+  __asm__ __volatile__ ("fldcw %0" : : "m" (env387.__control_word));
+
+  /* rounding direction */
+  unsigned int round_387 = (env387.__control_word & 0x0C00) >> 2;
+  /* exception trap bits (inverted) */
+  unsigned int trapbits_387 = x86hardware_to_exceptions 
(env387.__control_word);
+  /* exception flags */
+  unsigned int excflags_387 = x86hardware_to_exceptions (env387.__status_word);
+
+  unsigned int mxcsr;
+  _FPU_GETSSECW (mxcsr);
+
+  /* rounding direction */
+  unsigned int round_sse = (mxcsr & 0x6000) >> 5;
+  /* exception trap bits (inverted) */
+  unsigned int trapbits_sse = x86hardware_to_exceptions (mxcsr >> 7);
+  /* exception flags */
+  unsigned int excflags_sse = x86hardware_to_exceptions (mxcsr);
+
+  envp->_Fe_ctl =
+      (round_sse << 22) | (round_387 << 14) | round_sse | round_387
+    | (trapbits_sse << 24) | (trapbits_387 << 16) | trapbits_sse | 
trapbits_387;
+  envp->_Fe_stat =
+    (excflags_sse << 24) | (excflags_387 << 16) | excflags_sse | excflags_387;
+
 #  elif defined __APPLE__ && defined __MACH__ /* macOS */
 
   _FPU_GETCW (envp->__control);
@@ -230,6 +280,50 @@ fesetenv (fenv_t const *envp)
             | (envp->_Fe_stat & FE_INEXACT   ? 1 << 5 : 0);
   _FPU_SETSSECW (mxcsr);
 
+#  elif (defined __MINGW32__ && FE_INVALID != 0x01) /* mingw ≥ 13 */
+
+  unsigned short env_fctrl;
+  unsigned short env_fstat;
+  unsigned int env_mxcsr;
+  /* On mingw, FE_DFL_ENV is NULL.  */
+  if (envp == FE_DFL_ENV)
+    {
+      env_fctrl = 0x3F;
+      env_fstat = 0;
+      env_mxcsr = 0x3F << 7;
+    }
+  else
+    {
+      /* rounding direction */
+      unsigned int round_387 = (envp->_Fe_ctl >> 14) & 0x0300;
+      unsigned int round_sse = (envp->_Fe_ctl >> 22) & 0x0300;
+      /* exception trap bits (inverted) */
+      unsigned int trapbits_387 = (envp->_Fe_ctl >> 16) & 0x1F;
+      unsigned int trapbits_sse = (envp->_Fe_ctl >> 24) & 0x1F;
+      /* exception flags */
+      unsigned int excflags_387 = (envp->_Fe_stat >> 16) & 0x1F;
+      unsigned int excflags_sse = (envp->_Fe_stat >> 24) & 0x1F;
+
+      env_fctrl = (round_387 << 2) | exceptions_to_x86hardware (trapbits_387);
+      env_fstat = exceptions_to_x86hardware (excflags_387);
+      env_mxcsr = (round_sse << 5)
+                  | (exceptions_to_x86hardware (trapbits_sse) << 7)
+                  | exceptions_to_x86hardware (excflags_sse);
+    }
+
+  /* In the SSE unit.  */
+  unsigned int mxcsr = env_mxcsr;
+  _FPU_SETSSECW (mxcsr);
+
+  /* In the 387 unit.  */
+  x86_387_fenv_t env387;
+  __asm__ __volatile__ ("fnstenv %0" : "=m" (*&env387));
+  /* Note: fnstenv masks all floating-point exceptions until the fldenv
+     below.  */
+  env387.__control_word = env_fctrl;
+  env387.__status_word = env_fstat;
+  __asm__ __volatile__ ("fldenv %0" : : "m" (*&env387));
+
 #  elif defined __APPLE__ && defined __MACH__ /* macOS */
 
   unsigned short fctrl;
diff --git a/lib/fenv-except-state-set.c b/lib/fenv-except-state-set.c
index 3314a43ddf..d43db14b30 100644
--- a/lib/fenv-except-state-set.c
+++ b/lib/fenv-except-state-set.c
@@ -40,10 +40,11 @@ fesetexceptflag (fexcept_t const *saved_flags, int 
exceptions)
 
   unsigned int desired_flags = (unsigned int) *saved_flags;
 
-#  if defined _MSC_VER
   exceptions = exceptions_to_x86hardware (exceptions);
   desired_flags = exceptions_to_x86hardware (desired_flags);
 
+#  if defined _MSC_VER
+
   /* Modify the flags in the SSE unit.  */
   unsigned int mxcsr, orig_mxcsr;
   _FPU_GETSSECW (orig_mxcsr);
diff --git a/lib/fenv-except-tracking-clear.c b/lib/fenv-except-tracking-clear.c
index a0bbc32d3c..32ba66d27a 100644
--- a/lib/fenv-except-tracking-clear.c
+++ b/lib/fenv-except-tracking-clear.c
@@ -33,10 +33,10 @@ feclearexcept (int exceptions)
 {
   exceptions &= FE_ALL_EXCEPT;
 
-#  if defined _MSC_VER
-
   exceptions = exceptions_to_x86hardware (exceptions);
 
+#  if defined _MSC_VER
+
   /* Clear the bits only in the SSE unit.  */
   unsigned int mxcsr, orig_mxcsr;
   _FPU_GETSSECW (orig_mxcsr);
diff --git a/lib/fenv-except-tracking-set.c b/lib/fenv-except-tracking-set.c
index 280a222990..0f0e008911 100644
--- a/lib/fenv-except-tracking-set.c
+++ b/lib/fenv-except-tracking-set.c
@@ -33,9 +33,10 @@ fesetexcept (int exceptions)
 {
   exceptions &= FE_ALL_EXCEPT;
 
-#  if defined _MSC_VER
   exceptions = exceptions_to_x86hardware (exceptions);
 
+#  if defined _MSC_VER
+
   /* Set the flags in the SSE unit.  */
   unsigned int mxcsr, orig_mxcsr;
   _FPU_GETSSECW (orig_mxcsr);
diff --git a/lib/fenv-except-tracking-test.c b/lib/fenv-except-tracking-test.c
index e9ce9751fe..77b9d1d2cb 100644
--- a/lib/fenv-except-tracking-test.c
+++ b/lib/fenv-except-tracking-test.c
@@ -70,6 +70,7 @@ fetestexcept (int exceptions)
     }
 
   return x86hardware_to_exceptions (fstat | mxcsr) & FE_ALL_EXCEPT & 
exceptions;
+
 #  endif
 }
 
diff --git a/lib/fenv-except-trapping.c b/lib/fenv-except-trapping.c
index fa09545191..62f2446157 100644
--- a/lib/fenv-except-trapping.c
+++ b/lib/fenv-except-trapping.c
@@ -33,10 +33,10 @@ feenableexcept (int exceptions)
 {
   exceptions &= FE_ALL_EXCEPT;
 
-#  if defined _MSC_VER
-
   exceptions = exceptions_to_x86hardware (exceptions);
 
+#  if defined _MSC_VER
+
   /* Enable the traps only in the SSE unit.  */
   unsigned int mxcsr, orig_mxcsr;
   _FPU_GETSSECW (orig_mxcsr);
@@ -45,7 +45,6 @@ feenableexcept (int exceptions)
     _FPU_SETSSECW (mxcsr);
 
   unsigned int trapbits = 0x3f & ~(orig_mxcsr >> 7);
-  return x86hardware_to_exceptions (trapbits);
 
 #  else
 
@@ -66,9 +65,11 @@ feenableexcept (int exceptions)
         _FPU_SETSSECW (mxcsr);
     }
 
-  return FE_ALL_EXCEPT & ~orig_fctrl;
+  unsigned int trapbits = 0x3f & ~orig_fctrl;
 
 #  endif
+
+  return x86hardware_to_exceptions (trapbits);
 }
 
 int
@@ -76,10 +77,10 @@ fedisableexcept (int exceptions)
 {
   exceptions &= FE_ALL_EXCEPT;
 
-#  if defined _MSC_VER
-
   exceptions = exceptions_to_x86hardware (exceptions);
 
+#  if defined _MSC_VER
+
   /* Disable the traps only in the SSE unit.  */
   unsigned int mxcsr, orig_mxcsr;
   _FPU_GETSSECW (orig_mxcsr);
@@ -88,7 +89,6 @@ fedisableexcept (int exceptions)
     _FPU_SETSSECW (mxcsr);
 
   unsigned int trapbits = 0x3f & ~(orig_mxcsr >> 7);
-  return x86hardware_to_exceptions (trapbits);
 
 #  else
 
@@ -109,9 +109,11 @@ fedisableexcept (int exceptions)
         _FPU_SETSSECW (mxcsr);
     }
 
-  return FE_ALL_EXCEPT & ~orig_fctrl;
+  unsigned int trapbits = 0x3f & ~orig_fctrl;
 
 #  endif
+
+  return x86hardware_to_exceptions (trapbits);
 }
 
 int
@@ -122,13 +124,14 @@ fegetexcept (void)
   unsigned int mxcsr;
   _FPU_GETSSECW (mxcsr);
   unsigned int trapbits = 0x3f & ~(mxcsr >> 7);
-  return x86hardware_to_exceptions (trapbits);
 #  else
   /* Look at the trap bits in the 387 unit.  */
   unsigned short fctrl;
   _FPU_GETCW (fctrl);
-  return FE_ALL_EXCEPT & ~fctrl;
+  unsigned int trapbits = 0x3f & ~fctrl;
 #  endif
+
+  return x86hardware_to_exceptions (trapbits);
 }
 
 # elif defined __aarch64__ /* arm64 */
diff --git a/lib/fenv-private.h b/lib/fenv-private.h
index 24755f1c32..772d8dce9d 100644
--- a/lib/fenv-private.h
+++ b/lib/fenv-private.h
@@ -95,9 +95,10 @@ typedef struct
   }
 x86_387_fenv_t;
 
-# if defined _MSC_VER
-/* The MSVC header files have different values for the floating-point 
exceptions
-   than all the other platforms.  Define some handy macros for conversion.  */
+# if defined _MSC_VER || (defined __MINGW32__ && FE_INVALID != 0x01)
+/* The MSVC and mingw ≥ 13 header files have different values for the
+   floating-point exceptions than all the other platforms.  Define some
+   handy macros for conversion.  */
 #  define exceptions_to_x86hardware(exceptions) \
      (  ((exceptions) & FE_INVALID   ? 0x01 : 0) \
       | ((exceptions) & FE_DIVBYZERO ? 0x04 : 0) \
diff --git a/lib/fenv-round.c b/lib/fenv-round.c
index 49a7e8abae..7303cef460 100644
--- a/lib/fenv-round.c
+++ b/lib/fenv-round.c
@@ -44,9 +44,9 @@ fegetround (void)
   unsigned short fctrl;
   _FPU_GETCW (fctrl);
 #  endif
-#  ifdef _MSC_VER
+#  if defined _MSC_VER || (defined __MINGW32__ && FE_INVALID != 0x01)
   /* The MSVC header files have different values for the rounding directions
-     than all the other platforms, and the even changed between MSVC 14 and
+     than all the other platforms, and they even changed between MSVC 14 and
      MSVC 14.30 (!).  Map
        0x0000 -> FE_TONEAREST = 0
        0x0400 -> FE_DOWNWARD
@@ -61,13 +61,13 @@ fegetround (void)
 int
 fesetround (int rounding_direction)
 {
-#  ifdef _MSC_VER
+#  if defined _MSC_VER || (defined __MINGW32__ && FE_INVALID != 0x01)
   /* The MSVC header files have different values for the rounding directions
      than all the other platforms.  */
   if ((rounding_direction & ~0x0300) != 0)
     return -1;
   /* The MSVC header files have different values for the rounding directions
-     than all the other platforms, and the even changed between MSVC 14 and
+     than all the other platforms, and they even changed between MSVC 14 and
      MSVC 14.30 (!).  Map
      FE_TONEAREST = 0                        -> 0x0000
      FE_DOWNWARD                             -> 0x0400
diff --git a/m4/fenv-environment.m4 b/m4/fenv-environment.m4
index 9667ba5771..e5f08be2ce 100644
--- a/m4/fenv-environment.m4
+++ b/m4/fenv-environment.m4
@@ -1,5 +1,5 @@
 # fenv-environment.m4
-# serial 4
+# serial 5
 dnl Copyright (C) 2023-2025 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -179,10 +179,12 @@ AC_DEFUN([gl_FENV_ENVIRONMENT]
     esac
     dnl The fesetenv function does not work on FreeBSD 12.2/arm64 (see
     dnl 
<https://cgit.freebsd.org/src/commit/?id=34cc08e336987a8ebc316595e3f552a4c09f1fd4>),
+    dnl on mingw 13 (where fesetenv (FE_DFL_ENV) does not reset the rounding
+    dnl direction),
     dnl on musl libc/{i386,x86_64} and AIX and Solaris and MSVC 14 (where it
     dnl fails to restore the exception trap bits),
-    dnl on mingw (where calling it with FE_DFL_ENV argument has no effect on
-    dnl the mxcsr register),
+    dnl on mingw < 13 (where calling it with FE_DFL_ENV argument has no effect
+    dnl on the mxcsr register),
     dnl and on NetBSD/m68k.
     AC_CACHE_CHECK([whether fesetenv works],
       [gl_cv_func_fesetenv_works],
@@ -311,7 +313,9 @@ AC_DEFUN([gl_FENV_ENVIRONMENT]
     dnl and on musl libc/{i386,x86_64} and AIX and Solaris and mingw 10 (where
     dnl it fails to restore the exception trap bits),
     dnl and on FreeBSD 12.2/arm64 (see
-    dnl 
<https://cgit.freebsd.org/src/commit/?id=34cc08e336987a8ebc316595e3f552a4c09f1fd4>).
+    dnl 
<https://cgit.freebsd.org/src/commit/?id=34cc08e336987a8ebc316595e3f552a4c09f1fd4>),
+    dnl and on mingw 13 (where feupdateenv (FE_DFL_ENV) does not restore the
+    dnl rounding direction).
     dnl On MSVC 14 it may even fail.
     AC_CACHE_CHECK([whether feupdateenv works],
       [gl_cv_func_feupdateenv_works],




Reply via email to