]> git.ipfire.org Git - thirdparty/gnulib.git/commitdiff
fenv*: Fix compilation error with mingw 13.
authorBruno Haible <bruno@clisp.org>
Wed, 11 Jun 2025 12:43:04 +0000 (14:43 +0200)
committerBruno Haible <bruno@clisp.org>
Wed, 11 Jun 2025 13:00:33 +0000 (15:00 +0200)
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.

12 files changed:
ChangeLog
doc/posix-functions/fesetenv.texi
doc/posix-functions/feupdateenv.texi
lib/fenv-env.c
lib/fenv-except-state-set.c
lib/fenv-except-tracking-clear.c
lib/fenv-except-tracking-set.c
lib/fenv-except-tracking-test.c
lib/fenv-except-trapping.c
lib/fenv-private.h
lib/fenv-round.c
m4/fenv-environment.m4

index 37713d555138f1fc4a73ebf83e4982ab8b69d187..2e2565704816f43c57820ab158d5ac2340ff3fdc 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2025-06-11  Bruno Haible  <bruno@clisp.org>
+
+       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.
+
 2025-06-09  Collin Funk  <collin.funk1@gmail.com>
 
        Silence 'time-stamp' warnings with bleeding-edge Emacs.
index 649b85e929614155572d4a5bac9dcecd6a226927..e759d776839b3f1c2fe8ddf37babc6089bbab567 100644 (file)
@@ -15,7 +15,9 @@ FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, Solaris 9, Cygwin 1.
 @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:
index 6bff8768b23e5a739f22c2be3ff5c816a059ea53..5f9ae3a09f6e14b58a5460d5a042a0e89bf7d30b 100644 (file)
@@ -22,7 +22,9 @@ This function does not work on some platforms:
 @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:
index f363c5f2e23b29b11e78256fe57d8cad91292434..637bc3e8c82384c7332f16cbc668949943543b91 100644 (file)
@@ -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;
index 3314a43ddf501155597f508db273159c4679b568..d43db14b30283a547ffbfb10a71e59b5750689c2 100644 (file)
@@ -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);
index a0bbc32d3cd71224b160a24a6e2a7af06ca4abb2..32ba66d27a9f0ec492fa29eee9d1f560caf636c5 100644 (file)
@@ -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);
index 280a222990691b8c980c387b521dd3ec8f760e6a..0f0e00891193a27828ed3d8ec667bca1b134202a 100644 (file)
@@ -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);
index e9ce9751fe56607906e3a09665e7e72567a3b26b..77b9d1d2cbc1ab764b8891dd0bfe41ff306329f1 100644 (file)
@@ -70,6 +70,7 @@ fetestexcept (int exceptions)
     }
 
   return x86hardware_to_exceptions (fstat | mxcsr) & FE_ALL_EXCEPT & exceptions;
+
 #  endif
 }
 
index fa09545191bc25d267ce708ae9af08e116a76f1f..62f244615744335ff8f1232ecdd74c58cab68c51 100644 (file)
@@ -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 */
index 24755f1c3270725cc59ce534547e40d41c333ddb..772d8dce9d600cd46448fbaea0c7b01b0f758c67 100644 (file)
@@ -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) \
index 49a7e8abae7a3a37c1bb409c4bc3f78ef3da319c..7303cef460db230bedff199e8ed9285fe6016a5e 100644 (file)
@@ -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
index 9667ba5771492331262919964a212e4add333dc2..e5f08be2ced3eff4614668963ebf003c89d345e3 100644 (file)
@@ -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],