]> Savannah Git Hosting - gnulib.git/commitdiff
fenv-environment: New module.
authorBruno Haible <bruno@clisp.org>
Sun, 5 Nov 2023 14:00:29 +0000 (15:00 +0100)
committerBruno Haible <bruno@clisp.org>
Sun, 5 Nov 2023 14:20:04 +0000 (15:20 +0100)
* lib/fenv.in.h (fenv_t) [hppa]: Remove the __exception field.
(FE_DFL_ENV): Override if <fenv.h> exists but HAVE_FE_DFL_ENV is not
defined.
(fegetenv, fesetenv, feupdateenv, feholdexcept): New declarations.
* lib/fenv-env.c: New file, based on glibc.
* lib/fenv-env-hold.c: New file.
* lib/fenv-env-update.c: New file.
* m4/fenv-environment.m4: New file.
* m4/mathfunc.m4 (gl_MATHFUNC): Handle also the 'fenv_t *' type.
* m4/fenv_h.m4 (gl_FENV_H_DEFAULTS): Initialize REPLACE_FEGETENV,
REPLACE_FEHOLDEXCEPT, REPLACE_FESETENV, REPLACE_FEUPDATEENV.
* modules/fenv (Makefile.am): Substitute REPLACE_FEGETENV,
REPLACE_FEHOLDEXCEPT, REPLACE_FESETENV, REPLACE_FEUPDATEENV.
* modules/fenv-environment: New file.
* doc/posix-functions/fegetenv.texi: Mention the new module and the bugs
on glibc, macOS, AIX.
* doc/posix-functions/feholdexcept.texi: Mention the new module and the
bugs on glibc, musl libc, FreeBSD, AIX, mingw, MSVC.
* doc/posix-functions/fesetenv.texi: Mention the new module and the bugs
on musl libc, FreeBSD, NetBSD, AIX, Solaris, Cygwin, mingw, MSVC.
* doc/posix-functions/feupdateenv.texi: Mention the new module and the
bugs on glibc, musl libc, macOS, FreeBSD, AIX, Solaris, mingw, MSVC.

14 files changed:
ChangeLog
doc/posix-functions/fegetenv.texi
doc/posix-functions/feholdexcept.texi
doc/posix-functions/fesetenv.texi
doc/posix-functions/feupdateenv.texi
lib/fenv-env-hold.c [new file with mode: 0644]
lib/fenv-env-update.c [new file with mode: 0644]
lib/fenv-env.c [new file with mode: 0644]
lib/fenv.in.h
m4/fenv-environment.m4 [new file with mode: 0644]
m4/fenv_h.m4
m4/mathfunc.m4
modules/fenv
modules/fenv-environment [new file with mode: 0644]

index 7c2d58913db15888b4041969570a05fae0f6b99e..18ee7639c21a7fb7719a7f316ac5b4d56085db75 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+2023-11-05  Bruno Haible  <bruno@clisp.org>
+
+       fenv-environment: New module.
+       * lib/fenv.in.h (fenv_t) [hppa]: Remove the __exception field.
+       (FE_DFL_ENV): Override if <fenv.h> exists but HAVE_FE_DFL_ENV is not
+       defined.
+       (fegetenv, fesetenv, feupdateenv, feholdexcept): New declarations.
+       * lib/fenv-env.c: New file, based on glibc.
+       * lib/fenv-env-hold.c: New file.
+       * lib/fenv-env-update.c: New file.
+       * m4/fenv-environment.m4: New file.
+       * m4/mathfunc.m4 (gl_MATHFUNC): Handle also the 'fenv_t *' type.
+       * m4/fenv_h.m4 (gl_FENV_H_DEFAULTS): Initialize REPLACE_FEGETENV,
+       REPLACE_FEHOLDEXCEPT, REPLACE_FESETENV, REPLACE_FEUPDATEENV.
+       * modules/fenv (Makefile.am): Substitute REPLACE_FEGETENV,
+       REPLACE_FEHOLDEXCEPT, REPLACE_FESETENV, REPLACE_FEUPDATEENV.
+       * modules/fenv-environment: New file.
+       * doc/posix-functions/fegetenv.texi: Mention the new module and the bugs
+       on glibc, macOS, AIX.
+       * doc/posix-functions/feholdexcept.texi: Mention the new module and the
+       bugs on glibc, musl libc, FreeBSD, AIX, mingw, MSVC.
+       * doc/posix-functions/fesetenv.texi: Mention the new module and the bugs
+       on musl libc, FreeBSD, NetBSD, AIX, Solaris, Cygwin, mingw, MSVC.
+       * doc/posix-functions/feupdateenv.texi: Mention the new module and the
+       bugs on glibc, musl libc, macOS, FreeBSD, AIX, Solaris, mingw, MSVC.
+
 2023-11-05  Bruno Haible  <bruno@clisp.org>
 
        fenv-exceptions-tracking-c99 tests: Enhance tests.
index 99d8d5250cb1e124e0182e557acbfc4f83e9ce44..4bde34b5aaadf7eb0621b28ba39e5335c6be70fe 100644 (file)
@@ -4,15 +4,23 @@
 
 POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/fegetenv.html}
 
-Gnulib module: ---
+Gnulib module: fenv-environment
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4.
+@item
+This function clears all exception trap bits on some platforms:
+@c https://sourceware.org/bugzilla/show_bug.cgi?id=16198
+glibc 2.19/x86_64,
+Mac OS X 10.5/i386, Mac OS X 10.5/x86_64.
+@item
+This function does not save the exception trap bits on some platforms:
+AIX 7.3.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on some platforms:
-FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4.
 @end itemize
index 52d8219c22fbf4302d6438097cb8c95483d6e38c..dcd99a2f92ae06062685e6000315502534bf4f91 100644 (file)
@@ -4,15 +4,32 @@
 
 POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/feholdexcept.html}
 
-Gnulib module: ---
+Gnulib module: fenv-environment
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4.
+@item
+This function does not work on some platforms:
+@c https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a8c79c4088e8c04e4297936efa0dee6c8e6e974d
+glibc 2.5,
+@c https://cgit.freebsd.org/src/commit/?id=34cc08e336987a8ebc316595e3f552a4c09f1fd4
+FreeBSD 12.2/arm64.
+@item
+This function does not save the exception trap bits on some platforms:
+AIX 7.3.
+@item
+This function does not clear the exception trap bits on some platforms:
+musl libc, mingw.
+@item
+This function may fail on some platforms:
+MSVC 14.
+@item
+
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on some platforms:
-FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4.
 @end itemize
index 7a74beaad115d597d4bb5eddf6fae67528fefc6b..f7d45cf54357d2b627ff459a212f235259a03308 100644 (file)
@@ -4,15 +4,32 @@
 
 POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/fesetenv.html}
 
-Gnulib module: ---
+Gnulib module: fenv-environment
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4.
+@item
+This function does not work on some platforms:
+@c https://cgit.freebsd.org/src/commit/?id=34cc08e336987a8ebc316595e3f552a4c09f1fd4
+FreeBSD 12.2/arm64.
+@item
+This function does not restore the floating-point exception trap bits
+on some platforms:
+musl libc/i386, musl libc/x86_64, AIX 7.3, Solaris 10 and 11, MSVC 14.
+@item
+The macro @code{FE_DFL_ENV} cannot be used because it leads to a link error
+on some platforms:
+NetBSD 9.3/hppa, NetBSD 9.3/sparc, Cygwin 2.9.0.
+@item
+This function, when called with @code{FE_DFL_ENV} argument, has no effect
+on the x86 @code{mxcsr} register and thus on floating-point operations
+performed in the SSE unit on some platforms:
+mingw 10.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on some platforms:
-FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4.
 @end itemize
index 4bcbf5cfd81abc198849152cd6590b75a984e83b..bb844ec2f19e68a1907d18f182eefe8ad32b458b 100644 (file)
@@ -4,15 +4,42 @@
 
 POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/feupdateenv.html}
 
-Gnulib module: ---
+Gnulib module: fenv-environment
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 14, Android 4.4.
+@item
+This function crashes when given the argument @code{FE_DFL_ENV}
+on some platforms:
+@c https://sourceware.org/bugzilla/show_bug.cgi?id=31022
+glibc 2.37/riscv64.
+@item
+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.
+@item
+This function forgets about the currently set floating-point exception flags
+on some platforms:
+Mac OS X 10.5/i386, Mac OS X 10.5/x86_64.
+@item
+This function does not restore the floating-point exception trap bits
+on some platforms:
+musl libc/i386, musl libc/x86_64, AIX 7.3, Solaris 10 and 11, mingw 10.
+@item
+This function does not trigger traps on
+@c https://sourceware.org/bugzilla/show_bug.cgi?id=31023
+glibc 2.37/hppa,
+musl libc/s390x.
+@item
+This function may fail on some platforms:
+MSVC 14.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on some platforms:
-FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 14, Android 4.4.
 @end itemize
diff --git a/lib/fenv-env-hold.c b/lib/fenv-env-hold.c
new file mode 100644 (file)
index 0000000..ab156d5
--- /dev/null
@@ -0,0 +1,32 @@
+/* Functions for controlling the floating-point environment as a whole.
+   Copyright (C) 1997-2023 Free Software Foundation, Inc.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   This file is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <fenv.h>
+
+int
+feholdexcept (fenv_t *envp)
+{
+  if (fegetenv (envp))
+    return -1;
+  if (feclearexcept (FE_ALL_EXCEPT))
+    return -1;
+  if (fedisableexcept (FE_ALL_EXCEPT) == -1)
+    return -1;
+  return 0;
+}
diff --git a/lib/fenv-env-update.c b/lib/fenv-env-update.c
new file mode 100644 (file)
index 0000000..876c223
--- /dev/null
@@ -0,0 +1,31 @@
+/* Functions for controlling the floating-point environment as a whole.
+   Copyright (C) 1997-2023 Free Software Foundation, Inc.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   This file is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <fenv.h>
+
+int
+feupdateenv (fenv_t const *envp)
+{
+  int previously_flagged_exceptions = fetestexcept (FE_ALL_EXCEPT);
+  if (fesetenv (envp))
+    return -1;
+  if (feraiseexcept (previously_flagged_exceptions))
+    return -1;
+  return 0;
+}
diff --git a/lib/fenv-env.c b/lib/fenv-env.c
new file mode 100644 (file)
index 0000000..3e97dc3
--- /dev/null
@@ -0,0 +1,1165 @@
+/* Functions for controlling the floating-point environment as a whole.
+   Copyright (C) 1997-2023 Free Software Foundation, Inc.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   This file is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Based on glibc/sysdeps/<cpu>/{fegetenv.c,fesetenv.c}
+   together with glibc/sysdeps/<cpu>/{fpu_control.h,fenv_private.h,fenv_libc.h}.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <fenv.h>
+
+#include "fenv-private.h"
+#include "verify.h"
+
+/* The big problem of the fegetenv/fesetenv API is that it is not well-defined
+   which parts of the floating-point environment need to be restored by fesetenv
+   and which don't.  For example:
+     - On x86_64 and i386, some platforms restore all the x86_387_fenv_t,
+       whereas others restore only the control word and the status words.
+     - On m68k, musl libc restores the fpiar register value, whereas glibc
+       doesn't.
+   Some platforms don't even do this consistently.  For example, musl libc on
+   sh4 restores the precision mode bit (fpscr bit 19) during an fesetenv of a
+   previously saved environment, but not during an fesetenv (FE_DFL_ENV)
+   invocation.  */
+
+#if defined _AIX && defined __powerpc__ /* AIX */
+
+/* On AIX, fenv_t is a struct { unsigned short rmode; unsigned int fpstat, trapstate; }.  */
+
+/* On AIX, the register fpscr is augmented with a 32-bit word named fpscrx
+   in thread-local storage.  Therefore AIX does complex things behind the scene,
+   and its best to use the system-provided fegetenv() and fesetenv() functions
+   and just add the missing behaviour, namely saving and restoring the exception
+   trap bits.  */
+
+# include <float.h>
+# include <fpxcp.h>
+
+# include <fptrap.h>
+
+int
+fegetenv (fenv_t *envp)
+#undef fegetenv
+{
+  /* Invoke the original fegetenv(), and additionally save the exception trap
+     bits (fpscr bits 7..3).  */
+  union { unsigned long long u; double f; } memenv;
+  _FPU_GETCW_AS_DOUBLE (memenv.f);
+  fegetenv (envp);
+  /* AIX's original fegetenv() sets envp->trapstate = 0.  */
+  envp->trapstate = FE_ALL_EXCEPT & ((unsigned int) memenv.u << 22);
+  return 0;
+}
+
+int
+fesetenv (fenv_t const *envp)
+#undef fesetenv
+{
+  /* Invoke the original fesetenv(), and additionally restore the exception trap
+     bits (fpscr bits 7..3).  */
+  if (fesetenv (envp) != 0)
+    return -1;
+  union { unsigned long long u; double f; } orig_memenv, memenv;
+  _FPU_GETCW_AS_DOUBLE (orig_memenv.f);
+  unsigned int trapbits;
+  if (envp == FE_DFL_ENV)
+    trapbits = 0;
+  else
+    trapbits = (envp->trapstate & FE_ALL_EXCEPT) >> 22;
+  memenv.u = (orig_memenv.u & ~0x000000f8) | trapbits;
+  if (!(memenv.u == orig_memenv.u))
+    {
+      if ((orig_memenv.u & 0x000000f8) == 0 && (memenv.u & 0x000000f8) != 0)
+        {
+          /* Put the thread into precise trapping mode.  */
+          /* Documentation: <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-trap-subroutine>  */
+          fp_trap (FP_TRAP_SYNC);
+        }
+      _FPU_SETCW_AS_DOUBLE (memenv.f);
+      if ((orig_memenv.u & 0x000000f8) != 0 && (memenv.u & 0x000000f8) == 0)
+        {
+          /* Put the thread into no-trapping mode.  */
+          /* Documentation: <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-trap-subroutine>  */
+          fp_trap (FP_TRAP_OFF);
+        }
+    }
+  return 0;
+}
+
+#elif defined __GNUC__ || defined __clang__ || defined _MSC_VER
+
+# 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
+     - either a x86_387_fenv_t (7 'unsigned int' words)
+       where mxcsr is stored:
+         - for glibc/i386: in __eip = more[1].
+         - for musl libc/i386: the lower 6 bits ORed into __status_word.
+         - for FreeBSD/i386, Haiku/i386, mingw/i386:
+           the upper 16 bits in __reserved1, the lower 16 bits in __reserved2.
+     - or a struct { x86_387_fenv_t 387; unsigned int mxcsr; }
+   On MSVC, it's a
+     struct { unsigned int _Fe_ctl, _Fe_stat; }, where
+     _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 macOS, it's a
+     struct { unsigned short __control, __status; unsigned int __mxcsr; ... }.
+   On Solaris, it's a
+     struct { __fex_handler_t __handlers; unsigned long __fsr; }.  */
+
+/* Like FE_ALL_EXCEPT, except that it also includes FE_DENORMAL.  */
+#  define _FE_ALL_EXCEPT (0x3F * FE_INVALID)
+
+int
+fegetenv (fenv_t *envp)
+{
+#  if defined _MSC_VER
+
+  unsigned int mxcsr;
+  _FPU_GETSSECW (mxcsr);
+  envp->_Fe_ctl = ((mxcsr & 0x6000) >> 5) /* rounding direction */
+                  /* exception trap bits (inverted):  */
+                  | (mxcsr & (1 << 7) ? FE_INVALID : 0)
+                  | (mxcsr & (1 << 9) ? FE_DIVBYZERO : 0)
+                  | (mxcsr & (1 << 10) ? FE_OVERFLOW : 0)
+                  | (mxcsr & (1 << 11) ? FE_UNDERFLOW : 0)
+                  | (mxcsr & (1 << 12) ? FE_INEXACT : 0);
+  envp->_Fe_stat = /* exception flags:  */
+                     (mxcsr & (1 << 0) ? FE_INVALID : 0)
+                   | (mxcsr & (1 << 2) ? FE_DIVBYZERO : 0)
+                   | (mxcsr & (1 << 3) ? FE_OVERFLOW : 0)
+                   | (mxcsr & (1 << 4) ? FE_UNDERFLOW : 0)
+                   | (mxcsr & (1 << 5) ? FE_INEXACT : 0);
+
+#  elif defined __APPLE__ && defined __MACH__ /* macOS */
+
+  _FPU_GETCW (envp->__control);
+  _FPU_GETSTAT (envp->__status);
+  _FPU_GETSSECW (envp->__mxcsr);
+
+#  elif defined __sun /* Solaris */
+
+  fex_getexcepthandler (&envp->__handlers, FEX_ALL);
+
+  unsigned short fctrl;
+  unsigned short fstat;
+  unsigned int mxcsr;
+  _FPU_GETCW (fctrl);
+  _FPU_GETSTAT (fstat);
+  _FPU_GETSSECW (mxcsr);
+  envp->__fsr =
+    (((unsigned int) (fctrl ^ 0x3F) & ~0xE0C0U) << 16)
+    | ((unsigned int) fstat | (mxcsr & 0x3F));
+
+#  else
+
+  verify (sizeof (fenv_t) >= sizeof (x86_387_fenv_t));
+
+  __asm__ __volatile__ ("fnstenv %0" : "=m" (* (x86_387_fenv_t *) envp));
+  /* Note: fnstenv masks all floating-point exceptions until the fldcw
+     below.  */
+  __asm__ __volatile__ ("fldcw %0" : : "m" (((x86_387_fenv_t *) envp)->__control_word));
+
+  unsigned int mxcsr;
+  _FPU_GETSSECW (mxcsr);
+  if (sizeof (fenv_t) > sizeof (x86_387_fenv_t))
+    {
+      /* Store mxcsr in the struct field after the x86_387_fenv_t.  */
+      * (unsigned int *) ((x86_387_fenv_t *) envp + 1) = mxcsr;
+    }
+  else
+    {
+#   if defined __GLIBC__
+      ((x86_387_fenv_t *) envp)->more[1] = mxcsr;
+#   elif MUSL_LIBC
+      ((x86_387_fenv_t *) envp)->__status_word |= mxcsr & 0x3F;
+#   else
+      ((x86_387_fenv_t *) envp)->__reserved1 = mxcsr >> 16;
+      ((x86_387_fenv_t *) envp)->__reserved2 = mxcsr & 0xFFFF;
+#   endif
+    }
+
+#  endif
+  return 0;
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+#  if defined _MSC_VER
+
+  unsigned int mxcsr;
+  _FPU_GETSSECW (mxcsr);
+  mxcsr &= ~(0x6000 | (0x3F << 7) | 0x3F);
+  /* On MSVC, FE_DFL_ENV is the address of a global variable.
+     Let's ignore this variable.  */
+  if (envp == FE_DFL_ENV)
+    /* Default: rounding direction = FE_TONEAREST, exception trap bits = 0,
+       exception flags = 0.  */
+    mxcsr = mxcsr | (0x3F << 7);
+  else
+    mxcsr = mxcsr
+            | ((envp->_Fe_ctl & 0x300) << 5) /* rounding direction */
+            /* exception trap bits (inverted):  */
+            | (envp->_Fe_ctl & FE_INVALID   ? 1 << 7 : 0)
+            | (envp->_Fe_ctl & FE_DIVBYZERO ? 1 << 9 : 0)
+            | (envp->_Fe_ctl & FE_OVERFLOW  ? 1 << 10 : 0)
+            | (envp->_Fe_ctl & FE_UNDERFLOW ? 1 << 11 : 0)
+            | (envp->_Fe_ctl & FE_INEXACT   ? 1 << 12 : 0)
+            /* exception flags:  */
+            | (envp->_Fe_stat & FE_INVALID   ? 1 << 0 : 0)
+            | (envp->_Fe_stat & FE_DIVBYZERO ? 1 << 2 : 0)
+            | (envp->_Fe_stat & FE_OVERFLOW  ? 1 << 3 : 0)
+            | (envp->_Fe_stat & FE_UNDERFLOW ? 1 << 4 : 0)
+            | (envp->_Fe_stat & FE_INEXACT   ? 1 << 5 : 0);
+  _FPU_SETSSECW (mxcsr);
+
+#  elif defined __APPLE__ && defined __MACH__ /* macOS */
+
+  unsigned short fctrl;
+  unsigned short fstat;
+  unsigned int mxcsr;
+  /* On macOS, FE_DFL_ENV is the address of a global variable;
+     no special code is needed in this case.  */
+  if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
+    {
+      fctrl = 0x3F;
+      fstat = 0;
+      mxcsr = 0x3F << 7;
+    }
+  else
+    {
+      fctrl = envp->__control;
+      fstat = envp->__status;
+      mxcsr = envp->__mxcsr;
+    }
+  /* In the SSE unit.  */
+  _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 = fctrl;
+  env387.__status_word = fstat;
+  __asm__ __volatile__ ("fldenv %0" : : "m" (*&env387));
+
+#  elif defined __sun /* Solaris */
+
+  unsigned short env_fctrl;
+  unsigned int env_mxcsr;
+  /* On Solaris, FE_DFL_ENV is the address of a global variable;
+     no special code is needed in this case.  */
+  if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
+    {
+      env_fctrl = 0x3F;
+      env_mxcsr = 0x3F << 7;
+    }
+  else
+    {
+      fex_setexcepthandler (&envp->__handlers, FEX_ALL);
+      env_fctrl = (envp->__fsr >> 16) ^ 0x3F;
+      env_mxcsr = envp->__fsr & 0x3F;
+    }
+
+  /* Store the exception flags in mxcsr, not in env387.__status_word.  */
+
+  /* In the SSE unit.  */
+  unsigned int mxcsr;
+  _FPU_GETSSECW (mxcsr);
+  mxcsr = (mxcsr & ~0x3F) | 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 &= ~0x3F;
+  __asm__ __volatile__ ("fldenv %0" : : "m" (*&env387));
+
+#  else
+
+  verify (sizeof (fenv_t) >= sizeof (x86_387_fenv_t));
+
+  unsigned short env_fctrl;
+  unsigned short env_fstat;
+  unsigned int env_mxcsr;
+  /* On *BSD, Solaris, Cygwin, Android, Haiku, Minix, FE_DFL_ENV is the address
+     of a global variable; no special code is needed in this case.
+     But on mingw, FE_DFL_ENV is NULL.  */
+  if (envp == FE_DFL_ENV)
+    {
+      env_fctrl = 0x3F;
+      env_fstat = 0;
+      env_mxcsr = 0x3F << 7;
+    }
+  else
+    {
+      env_fctrl = ((x86_387_fenv_t const *) envp)->__control_word;
+      env_fstat = ((x86_387_fenv_t const *) envp)->__status_word;
+      if (sizeof (fenv_t) > sizeof (x86_387_fenv_t))
+        env_mxcsr = * (unsigned int const *) ((x86_387_fenv_t const *) envp + 1);
+      else
+        {
+#   if defined __GLIBC__
+          env_mxcsr = ((x86_387_fenv_t const *) envp)->more[1];
+#   elif MUSL_LIBC
+          env_mxcsr = env_fstat & 0x3F;
+#   else
+          env_mxcsr = (((x86_387_fenv_t const *) envp)->__reserved1 << 16)
+                      | ((x86_387_fenv_t const *) envp)->__reserved2;
+#   endif
+        }
+    }
+
+  /* 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));
+
+#  endif
+
+  return 0;
+}
+
+# elif defined __aarch64__ /* arm64 */
+
+/* On all OSes except FreeBSD and macOS, fenv_t is binary-equivalent to a
+   struct { unsigned int fpcr, fpsr; }.
+   On FreeBSD, it's binary-equivalent to a
+   struct { unsigned int fpsr, fpcr; }, i.e. the fields are swapped.
+   On macOS, it's binary-equivalent to a
+   struct { unsigned long fpsr, fpcr; }.  */
+
+int
+fegetenv (fenv_t *envp)
+{
+  unsigned long fpcr;
+  unsigned long fpsr;
+  _FPU_GETCW (fpcr);
+  _FPU_GETFPSR (fpsr);
+#  if defined __APPLE__ && defined __MACH__
+  ((unsigned long *) envp)[0] = fpsr;
+  ((unsigned long *) envp)[1] = fpcr;
+#  elif defined __FreeBSD__
+  ((unsigned int *) envp)[0] = fpsr;
+  ((unsigned int *) envp)[1] = fpcr;
+#  else
+  ((unsigned int *) envp)[0] = fpcr;
+  ((unsigned int *) envp)[1] = fpsr;
+#  endif
+  return 0;
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+  unsigned long orig_fpcr, fpcr;
+  unsigned long orig_fpsr, fpsr;
+  _FPU_GETCW (orig_fpcr);
+  _FPU_GETFPSR (orig_fpsr);
+  /* On *BSD and Android, FE_DFL_ENV is the address of a global variable;
+     no special code is needed in this case.  */
+  if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
+    {
+      /* Default: rounding direction = FE_TONEAREST, exceptions trap bits = 0. */
+      fpcr = (orig_fpcr & 0xFE0FE0F8U) | 0x00000000U;
+      /* Default: exception flags = 0.  */
+      fpsr = (orig_fpsr & 0x0FFFFFE0U) | 0x00000000U;
+    }
+  else
+    {
+#  if defined __APPLE__ && defined __MACH__
+      fpsr = ((unsigned long const *) envp)[0];
+      fpcr = ((unsigned long const *) envp)[1];
+#  elif defined __FreeBSD__
+      fpsr = ((unsigned int const *) envp)[0];
+      fpcr = ((unsigned int const *) envp)[1];
+#  else
+      fpcr = ((unsigned int const *) envp)[0];
+      fpsr = ((unsigned int const *) envp)[1];
+#  endif
+    }
+  if (fpcr != orig_fpcr)
+    _FPU_SETCW (fpcr);
+  if (fpsr != orig_fpsr)
+    _FPU_SETFPSR (fpsr);
+  return 0;
+}
+
+# elif defined __arm__
+
+/* On all OSes except OpenBSD, fenv_t is binary-equivalent to an 'unsigned int'.
+   On OpenBSD, it's a struct { unsigned int __sticky, __mask, __round; }.  */
+
+int
+fegetenv (fenv_t *envp)
+{
+#  ifdef __SOFTFP__
+  return -1;
+#  else
+  unsigned int fpscr;
+  _FPU_GETCW (fpscr);
+#   if defined __OpenBSD__
+  envp->__sticky = fpscr & FE_ALL_EXCEPT;
+  envp->__mask = (fpscr >> 8) & FE_ALL_EXCEPT;
+  envp->__round = (fpscr >> 22) & 3;
+#   else
+  * (unsigned int *) envp = fpscr;
+#   endif
+  return 0;
+#  endif
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+#  ifdef __SOFTFP__
+  return -1;
+#  else
+  unsigned int fpscr, orig_fpscr;
+  _FPU_GETCW (orig_fpscr);
+  /* On *BSD and Android, FE_DFL_ENV is the address of a global variable;
+     no special code is needed in this case.  */
+  if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
+    /* Default: rounding direction = FE_TONEAREST, exception trap bits = 0,
+       exception flags = 0.  */
+    fpscr = (orig_fpscr & 0x00086060) | 0x00000000;
+  else
+    /* Here, glibc ignores orig_fpscr entirely...  */
+    fpscr = (orig_fpscr & ~((3U << 22) | (FE_ALL_EXCEPT << 8) | FE_ALL_EXCEPT))
+#   if defined __OpenBSD__
+      | ((envp->__round & 3U) << 22)
+      | ((envp->__mask & FE_ALL_EXCEPT) << 8)
+      | (envp->__sticky & FE_ALL_EXCEPT)
+#   else
+      | (* (unsigned int const *) envp & ((3U << 22) | (FE_ALL_EXCEPT << 8) | FE_ALL_EXCEPT))
+#   endif
+      ;
+  if (((fpscr ^ orig_fpscr) & ~0xF0000000) != 0)
+    _FPU_SETCW (fpscr);
+  return 0;
+#  endif
+}
+
+# elif defined __alpha
+
+/* On all OSes except OpenBSD, fenv_t is binary-equivalent to an 'unsigned long'.
+   On OpenBSD, it's a struct { unsigned int __sticky, __mask, __round; }.  */
+
+/* Like FE_ALL_EXCEPT, except that it also includes FE_DENORMAL.  */
+#  define _FE_ALL_EXCEPT (0x3FUL * FE_INVALID)
+
+int
+fegetenv (fenv_t *envp)
+{
+  unsigned long swcr = __ieee_get_fp_control ();
+  unsigned long fpcr;
+  _FPU_GETCW (fpcr);
+#   if defined __OpenBSD__
+  envp->__sticky = (swcr >> 17) & _FE_ALL_EXCEPT;
+  envp->__mask = (swcr >> 1) & _FE_ALL_EXCEPT;
+  envp->__round = (fpcr >> 58) & 0x3UL;
+#   else
+  * (unsigned long *) envp =
+    (fpcr & 0x0C00000000000000UL) /* rounding direction */
+    | (swcr & 0x007E307EUL); /* exception flags,
+                                mapping of denormal inputs and outputs,
+                                exception trap bits */
+#   endif
+  return 0;
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+  unsigned long env;
+  /* On *BSD, FE_DFL_ENV is the address of a global variable;
+     on glibc, it is a magic address.  */
+  if (envp == FE_DFL_ENV)
+    /* Default: rounding direction = FE_TONEAREST, exception flags = 0,
+       mapping = 0, exception trap bits = 0.  */
+    env = 0x0800000000000000UL;
+  else
+    {
+#   if defined __OpenBSD__
+      env = ((unsigned long) (envp->__round & 0x3UL) << 58)
+            | ((envp->__sticky & _FE_ALL_EXCEPT) << 17)
+            | ((envp->__mask & _FE_ALL_EXCEPT) << 1);
+#   else
+      env = * (unsigned long const *) envp;
+#   endif
+    }
+
+  /* Set the rounding direction.  */
+  unsigned long fpcr, orig_fpcr;
+  _FPU_GETCW (orig_fpcr);
+  fpcr = (orig_fpcr & ~0x0C00000000000000UL) | (env & 0x0C00000000000000UL);
+  if (fpcr != orig_fpcr)
+    _FPU_SETCW (fpcr);
+  /* Set the exception flags, the mapping of denormal inputs and outputs, and
+     the exception trap bits.  */
+  __ieee_set_fp_control (env & 0x007E307EUL);
+
+  return 0;
+}
+
+# elif defined __hppa
+
+/* On all OSes except glibc, fenv_t is binary-equivalent to an 'unsigned int'.
+   On glibc, it's a struct { unsigned int __status_word, __exception[7]; },
+   of which only the __status_word field is used.  */
+
+int
+fegetenv (fenv_t *envp)
+{
+  unsigned int fpstatus;
+  _FPU_GETCW (fpstatus);
+  * (unsigned int *) envp = fpstatus;
+  return 0;
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+  unsigned int fpstatus, orig_fpstatus;
+  _FPU_GETCW (orig_fpstatus);
+  /* On *BSD, FE_DFL_ENV is the address of a global variable;
+     no special code is needed in this case.  */
+  if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
+    /* Default: exception flags = 0, rounding direction = FE_TONEAREST,
+       exception trap bits = 0.  */
+    fpstatus = 0x00000000U;
+  else
+    {
+      unsigned int env = * (unsigned int const *) envp;
+      fpstatus = (orig_fpstatus & ~0xF800061FU) | (env & 0xF800061FU);
+    }
+  if (fpstatus != orig_fpstatus)
+    _FPU_SETCW (fpstatus);
+  return 0;
+}
+
+# elif defined __ia64__
+
+/* On all OSes, fenv_t is binary-equivalent to an 'unsigned long'.  */
+
+/* Like FE_ALL_EXCEPT, except that it also includes FE_DENORMAL.  */
+#  define _FE_ALL_EXCEPT (0x3FUL * FE_INVALID)
+
+int
+fegetenv (fenv_t *envp)
+{
+  unsigned long fpsr;
+  _FPU_GETCW (fpsr);
+  * (unsigned long *) envp = fpsr;
+  return 0;
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+  unsigned long env;
+  /* On NetBSD, FE_DFL_ENV is the address of a global variable;
+     on glibc, it is a magic address.  */
+  if (envp == FE_DFL_ENV)
+    /* Default:
+       sf0 = 0b0000000001100, sf1 = 0b0000001001110, sf2 = sf3 = 0000001001100
+       i.e. precision control = 80-bits "extended",
+            rounding direction = FE_TONEAREST,
+            exception flags = 0,
+       exceptions trap bits = all 1, i.e. all masked.  */
+    env = 0x0009804C0270033FUL;
+  else
+    env = * (unsigned long const *) envp;
+  unsigned long fpsr = env;
+  _FPU_SETCW (fpsr);
+  return 0;
+}
+
+# elif defined __m68k__
+
+/* On all OSes, fenv_t is binary-equivalent to a struct
+   { unsigned int control_register, status_register, instruction_address; }.  */
+
+#  define _FPU_GETFPIAR(cw) __asm__ __volatile__ ("fmove%.l %/fpiar, %0" : "=dm" (cw))
+#  define _FPU_SETFPIAR(cw) __asm__ __volatile__ ("fmove%.l %0, %/fpiar" : : "dm" (cw))
+
+int
+fegetenv (fenv_t *envp)
+{
+  unsigned int fpcr;
+  unsigned int fpsr;
+  unsigned int fpiar;
+  _FPU_GETCW (fpcr);
+  _FPU_GETFPSR (fpsr);
+  _FPU_GETFPIAR (fpiar);
+  ((unsigned int *) envp)[0] = fpcr;
+  ((unsigned int *) envp)[1] = fpsr;
+  ((unsigned int *) envp)[2] = fpiar;
+  return 0;
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+  unsigned int env_fpcr;
+  unsigned int env_fpsr;
+  if (envp == FE_DFL_ENV)
+    {
+      /* Default: exceptions trap bits = 0, rounding direction = FE_TONEAREST.  */
+      env_fpcr = 0;
+      /* Default: exception flags = 0.  */
+      env_fpsr = 0;
+    }
+  else
+    {
+      /* No need to restore the instruction address here.  */
+      env_fpcr = ((unsigned int const *) envp)[0];
+      env_fpsr = ((unsigned int const *) envp)[1];
+    }
+  unsigned int fpcr, orig_fpcr;
+  unsigned int fpsr, orig_fpsr;
+  unsigned int fpiar, orig_fpiar;
+  _FPU_GETCW (orig_fpcr);
+  _FPU_GETFPSR (orig_fpsr);
+  _FPU_GETFPIAR (orig_fpiar);
+  /* glibc uses 0x3E30U here.  I think 0xFF30U is a better choice.  */
+  fpcr = (orig_fpcr & ~0xFF30U) | (env_fpcr & 0xFF30U);
+  fpsr = (orig_fpsr & ~0x00F8U) | (env_fpsr & 0x00F8U);
+  fpiar = orig_fpiar;
+  _FPU_SETCW (fpcr);
+  _FPU_SETFPSR (fpsr);
+  _FPU_SETFPIAR (fpiar);
+  return 0;
+}
+
+# elif defined __mips__
+
+/* On all OSes, fenv_t is binary-equivalent to an 'unsigned int'.  */
+
+int
+fegetenv (fenv_t *envp)
+{
+  unsigned int fcsr;
+  _FPU_GETCW (fcsr);
+  * (unsigned int *) envp = fcsr;
+  return 0;
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+  unsigned int env;
+  /* On *BSD, FE_DFL_ENV is the address of a global variable;
+     no special code is needed in this case.  */
+  if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
+    /* Default: exceptions trap bits = 0, exception flags = 0,
+       rounding direction = FE_TONEAREST.  */
+    env = 0x00000000U;
+  else
+    env = * (unsigned int const *) envp;
+  unsigned int fcsr = env;
+  _FPU_SETCW (fcsr);
+  return 0;
+}
+
+# elif defined __loongarch__
+
+/* On all OSes, fenv_t is binary-equivalent to an 'unsigned int'.  */
+
+int
+fegetenv (fenv_t *envp)
+{
+  unsigned int fcsr;
+  _FPU_GETCW (fcsr);
+  * (unsigned int *) envp = fcsr;
+  return 0;
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+  unsigned int env;
+  if (envp == FE_DFL_ENV)
+    /* Default: exception flags = 0, rounding direction = FE_TONEAREST,
+       exceptions trap bits = 0.  */
+    env = 0x00000000U;
+  else
+    env = * (unsigned int const *) envp;
+  unsigned int fcsr = env;
+  _FPU_SETCW (fcsr);
+  return 0;
+}
+
+# elif defined __powerpc__
+
+/* On all OSes except *BSD and AIX, fenv_t is a 'double'.
+   On *BSD, it's an 'unsigned int'.
+   On AIX, it's a struct { unsigned short rmode; unsigned int fpstat, trapstate; }.
+   But AIX has already been dealt with above.  */
+
+#  if defined __linux__
+#   include <sys/prctl.h>
+#  endif
+
+int
+fegetenv (fenv_t *envp)
+{
+#  if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__
+  union { unsigned long long u; double f; } memenv;
+  _FPU_GETCW_AS_DOUBLE (memenv.f);
+  * (unsigned int *) envp = (unsigned int) memenv.u;
+#  else
+  _FPU_GETCW_AS_DOUBLE (*envp);
+#  endif
+  return 0;
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+  union { unsigned long long u; double f; } orig_memenv, memenv;
+  _FPU_GETCW_AS_DOUBLE (orig_memenv.f);
+  /* On glibc and *BSD, FE_DFL_ENV is the address of a global variable;
+     no special code is needed in this case.  */
+  if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
+    memenv.u = 0xFFF8000000000000ULL;
+  else
+#  if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__
+    memenv.u = * (unsigned int const *) envp;
+#  else
+    memenv.f = *envp;
+#  endif
+  if (!(memenv.u == orig_memenv.u))
+    {
+      if ((orig_memenv.u & 0x000000f8) == 0 && (memenv.u & 0x000000f8) != 0)
+        {
+          /* Put the thread into precise trapping mode.  */
+#  if defined __linux__ || defined __NetBSD__
+          prctl (PR_SET_FPEXC, PR_FP_EXC_PRECISE);
+#  endif
+        }
+      _FPU_SETCW_AS_DOUBLE (memenv.f);
+      if ((orig_memenv.u & 0x000000f8) != 0 && (memenv.u & 0x000000f8) == 0)
+        {
+          /* Put the thread into no-trapping mode.  */
+#  if defined __linux__ || defined __NetBSD__
+          prctl (PR_SET_FPEXC, PR_FP_EXC_DISABLED);
+#  endif
+        }
+    }
+  return 0;
+}
+
+# elif defined __riscv
+
+/* On all OSes except FreeBSD, fenv_t is binary-equivalent to an 'unsigned int'.
+   On FreeBSD, it's binary-equivalent to an 'unsigned long'.  */
+
+#  define _FPU_GETCW(cw) __asm__ __volatile__ ("frcsr %0" : "=r" (cw))
+#  define _FPU_SETCW(cw) __asm__ __volatile__ ("fscsr %z0" : : "rJ" (cw))
+
+int
+fegetenv (fenv_t *envp)
+{
+  unsigned int fcsr;
+  _FPU_GETCW (fcsr);
+#  if defined __FreeBSD__
+  * (unsigned long *) envp = fcsr;
+#  else
+  * (unsigned int *) envp = fcsr;
+#  endif
+  return 0;
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+  unsigned int env;
+  /* On *BSD and Android, FE_DFL_ENV is the address of a global variable;
+     no special code is needed in this case.  */
+  if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
+    /* Default: rounding direction = FE_TONEAREST, exception flags = 0.  */
+    env = 0x00000000U;
+  else
+#  if defined __FreeBSD__
+    env = * (unsigned long const *) envp;
+#  else
+    env = * (unsigned int const *) envp;
+#  endif
+  unsigned int fcsr = env;
+  _FPU_SETCW (fcsr);
+  return 0;
+}
+
+# elif defined __s390__ || defined __s390x__
+
+/* On all OSes, fenv_t is binary-equivalent to a struct whose first (and only
+   relevant) field is an 'unsigned int'.  */
+
+int
+fegetenv (fenv_t *envp)
+{
+  unsigned int fpc;
+  _FPU_GETCW (fpc);
+  * (unsigned int *) envp = fpc;
+  return 0;
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+  unsigned int env;
+  if (envp == FE_DFL_ENV)
+    /* Default: exceptions trap bits = 0, exception flags = 0,
+       rounding direction = FE_TONEAREST.  */
+    env = 0x00000000U;
+  else
+    env = * (unsigned int const *) envp;
+  unsigned int fpc = env;
+  _FPU_SETCW (fpc);
+  return 0;
+}
+
+# elif defined __sh__
+
+/* On all OSes, fenv_t is binary-equivalent to an 'unsigned int'.  */
+
+int
+fegetenv (fenv_t *envp)
+{
+  unsigned int fpscr;
+  _FPU_GETCW (fpscr);
+  * (unsigned int *) envp = fpscr;
+  return 0;
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+  unsigned int env;
+  /* On OpenBSD, FE_DFL_ENV is the address of a global variable;
+     no special code is needed in this case.  */
+  if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
+    /* Default: PR = 1, exceptions trap bits = 0, exception flags = 0,
+       rounding direction = FE_TONEAREST.  */
+    env = 0x00080000U;
+  else
+    env = * (unsigned int const *) envp;
+  unsigned int fpscr = env;
+  _FPU_SETCW (fpscr);
+  return 0;
+}
+
+# elif defined __sparc
+
+/* On all OSes except Solaris, fenv_t is binary-equivalent to an 'unsigned long'.
+   On Solaris, it's a struct { __fex_handler_t __handlers; unsigned long __fsr; }.  */
+
+int
+fegetenv (fenv_t *envp)
+{
+  unsigned long fsr;
+  _FPU_GETCW (fsr);
+#  if defined __sun
+  fex_getexcepthandler (&envp->__handlers, FEX_ALL);
+  envp->__fsr = fsr;
+#  else
+  * (unsigned long *) envp = fsr;
+#  endif
+  return 0;
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+  unsigned long fsr;
+  /* On *BSD and Solaris, FE_DFL_ENV is the address of a global variable;
+     no special code is needed in this case.  */
+  if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
+    {
+      /* Default: rounding direction = FE_TONEAREST, exceptions trap bits = 0,
+         exception flags = 0.  */
+      fsr = 0x00000000U;
+    }
+  else
+    {
+#  if defined __sun
+      fex_setexcepthandler (&envp->__handlers, FEX_ALL);
+      fsr = envp->__fsr;
+#  else
+      fsr = * (unsigned long const *) envp;
+#  endif
+    }
+  _FPU_SETCW (fsr);
+  return 0;
+}
+
+# else
+
+#  if defined __GNUC__ || defined __clang__
+#   warning "Unknown CPU / architecture. Please report your platform and compiler to <bug-gnulib@gnu.org>."
+#  endif
+#  define NEED_FALLBACK 1
+
+# endif
+
+#else
+
+/* The compiler does not support __asm__ statements or equivalent
+   intrinsics.  */
+
+# if defined __sun && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)) && defined __SUNPRO_C
+/* Solaris/i386, Solaris/x86_64.  */
+
+/* Like FE_ALL_EXCEPT, except that it also includes FE_DENORMAL.  */
+#  define _FE_ALL_EXCEPT (0x3F * FE_INVALID)
+
+/* Accessors for the 387 unit's special registers.  */
+
+static void
+getfctrl (unsigned short *fctrl_p)
+{
+#  if defined __x86_64__ || defined _M_X64
+  asm ("fnstcw (%rdi)");
+#  else
+  /* The compiler generates a stack frame.  Therefore the first argument is in
+     8(%ebp), not in 4(%esp).  */
+  asm ("movl 8(%ebp),%eax");
+  asm ("fnstcw (%eax)");
+#  endif
+}
+
+static void
+getfstat (unsigned short *fstat_p)
+{
+#  if defined __x86_64__ || defined _M_X64
+  asm ("fnstsw (%rdi)");
+#  else
+  /* The compiler generates a stack frame.  Therefore the first argument is in
+     8(%ebp), not in 4(%esp).  */
+  asm ("movl 8(%ebp),%eax");
+  asm ("fnstsw (%eax)");
+#  endif
+}
+
+static void
+getenv387 (x86_387_fenv_t *env387_p)
+{
+#  if defined __x86_64__ || defined _M_X64
+  asm ("fnstenv (%rdi)");
+#  else
+  /* The compiler generates a stack frame.  Therefore the first argument is in
+     8(%ebp), not in 4(%esp).  */
+  asm ("movl 8(%ebp),%eax");
+  asm ("fnstenv (%eax)");
+#  endif
+}
+
+static void
+setenv387 (x86_387_fenv_t const *env387_p)
+{
+#  if defined __x86_64__ || defined _M_X64
+  asm ("fldenv (%rdi)");
+#  else
+  /* The compiler generates a stack frame.  Therefore the first argument is in
+     8(%ebp), not in 4(%esp).  */
+  asm ("movl 8(%ebp),%eax");
+  asm ("fldenv (%eax)");
+#  endif
+}
+
+/* Accessors for the mxcsr register.  */
+
+static void
+getssecw (unsigned int *mxcsr_p)
+{
+#  if defined __x86_64__ || defined _M_X64
+  asm ("stmxcsr (%rdi)");
+#  else
+  /* The compiler generates a stack frame.  Therefore the first argument is in
+     8(%ebp), not in 4(%esp).  */
+  asm ("movl 8(%ebp),%eax");
+  asm ("stmxcsr (%eax)");
+#  endif
+}
+
+static void
+setssecw (unsigned int const *mxcsr_p)
+{
+#  if defined __x86_64__ || defined _M_X64
+  asm ("ldmxcsr (%rdi)");
+#  else
+  /* The compiler generates a stack frame.  Therefore the first argument is in
+     8(%ebp), not in 4(%esp).  */
+  asm ("movl 8(%ebp),%eax");
+  asm ("ldmxcsr (%eax)");
+#  endif
+}
+
+int
+fegetenv (fenv_t *envp)
+{
+  fex_getexcepthandler (&envp->__handlers, FEX_ALL);
+
+  unsigned short fctrl;
+  unsigned short fstat;
+  unsigned int mxcsr;
+  getfctrl (&fctrl);
+  getfstat (&fstat);
+  getssecw (&mxcsr);
+  envp->__fsr =
+    (((unsigned int) (fctrl ^ 0x3F) & ~0xE0C0U) << 16)
+    | ((unsigned int) fstat | (mxcsr & 0x3F));
+
+  return 0;
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+  unsigned short env_fctrl;
+  unsigned int env_mxcsr;
+  /* On Solaris, FE_DFL_ENV is the address of a global variable;
+     no special code is needed in this case.  */
+  if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
+    {
+      env_fctrl = 0x3F;
+      env_mxcsr = 0x3F << 7;
+    }
+  else
+    {
+      fex_setexcepthandler (&envp->__handlers, FEX_ALL);
+      env_fctrl = (envp->__fsr >> 16) ^ 0x3F;
+      env_mxcsr = envp->__fsr & 0x3F;
+    }
+
+  /* Store the exception flags in mxcsr, not in env387.__status_word.  */
+
+  /* In the SSE unit.  */
+  unsigned int mxcsr;
+  getssecw (&mxcsr);
+  mxcsr = (mxcsr & ~0x3F) | env_mxcsr;
+  setssecw (&mxcsr);
+
+  /* In the 387 unit.  */
+  x86_387_fenv_t env387;
+  getenv387 (&env387);
+  /* Note: fnstenv masks all floating-point exceptions until the setenv387
+     below.  */
+  env387.__control_word = env_fctrl;
+  env387.__status_word &= ~0x3F;
+  setenv387 (&env387);
+
+  return 0;
+}
+
+#elif defined __sun && defined __sparc && defined __SUNPRO_C
+/* Solaris/sparc.  */
+
+/* Accessors for the fsr register.  */
+
+static void
+getfsr (unsigned long *fsr_p)
+{
+#  if defined __sparcv9 || defined __arch64__ /* sparc64 */
+  asm ("stx %fsr,[%i0]");
+#  else
+  asm ("st %fsr,[%i0]");
+#  endif
+}
+
+static void
+setfsr (unsigned long const *fsr_p)
+{
+#  if defined __sparcv9 || defined __arch64__ /* sparc64 */
+  asm ("ldx [%i0],%fsr");
+#  else
+  asm ("ld [%i0],%fsr");
+#  endif
+}
+
+int
+fegetenv (fenv_t *envp)
+{
+  unsigned long fsr;
+  getfsr (&fsr);
+  fex_getexcepthandler (&envp->__handlers, FEX_ALL);
+  envp->__fsr = fsr;
+  return 0;
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+  unsigned long fsr;
+  /* On *BSD and Solaris, FE_DFL_ENV is the address of a global variable;
+     no special code is needed in this case.  */
+  if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
+    {
+      /* Default: rounding direction = FE_TONEAREST, exceptions trap bits = 0,
+         exception flags = 0.  */
+      fsr = 0x00000000U;
+    }
+  else
+    {
+      fex_setexcepthandler (&envp->__handlers, FEX_ALL);
+      fsr = envp->__fsr;
+    }
+  setfsr (&fsr);
+  return 0;
+}
+
+# else
+
+#  define NEED_FALLBACK 1
+
+# endif
+
+#endif
+
+#if NEED_FALLBACK
+
+/* A dummy fallback.  */
+
+int
+fegetenv (fenv_t *envp)
+{
+  return -1;
+}
+
+int
+fesetenv (fenv_t const *envp)
+{
+  return -1;
+}
+
+#endif
index b1c52d6e278ca66a7175dc8db9f5a1729cd8ff94..6d479124df07955ab8e0328f63b47e681368ccf7 100644 (file)
@@ -101,7 +101,6 @@ typedef unsigned long fenv_t;
 typedef struct
 {
   unsigned int __status_word;  /* floating point status register */
-  unsigned int __exception[7]; /* floating point exception registers */
 } fenv_t;
 
 # elif defined __ia64__
@@ -164,6 +163,96 @@ typedef unsigned long fenv_t;
 
 # define FE_DFL_ENV ((const fenv_t *) (-1))
 
+#else
+
+# if !HAVE_FE_DFL_ENV
+#  undef FE_DFL_ENV
+#  define FE_DFL_ENV ((const fenv_t *) (-1))
+# endif
+
+#endif
+
+#if @GNULIB_FEGETENV@
+/* Stores the thread's current floating-point environment in *ENVP and
+   returns zero.  Upon failure, it returns non-zero.  */
+# if @REPLACE_FEGETENV@ || (!@HAVE_FEGETENV@ && defined __FreeBSD__) /* has an inline definition */
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef fegetenv
+#   define fegetenv rpl_fegetenv
+#  endif
+_GL_FUNCDECL_RPL (fegetenv, int, (fenv_t *envp));
+_GL_CXXALIAS_RPL (fegetenv, int, (fenv_t *envp));
+# else
+#  if !@HAVE_FEGETENV@
+_GL_FUNCDECL_SYS (fegetenv, int, (fenv_t *envp));
+#  endif
+_GL_CXXALIAS_SYS (fegetenv, int, (fenv_t *envp));
+# endif
+_GL_CXXALIASWARN (fegetenv);
+#endif
+
+#if @GNULIB_FESETENV@
+/* Activates the given object *ENVP as the thread's current floating-point
+   environment, without raising floating-point exceptions, and returns zero.
+   ENVP may be FE_DFL_ENV.
+   Upon failure, it returns non-zero.  */
+# if @REPLACE_FESETENV@ || (!@HAVE_FESETENV@ && defined __FreeBSD__) /* has an inline definition */
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef fesetenv
+#   define fesetenv rpl_fesetenv
+#  endif
+_GL_FUNCDECL_RPL (fesetenv, int, (fenv_t const *envp));
+_GL_CXXALIAS_RPL (fesetenv, int, (fenv_t const *envp));
+# else
+#  if !@HAVE_FESETENV@
+_GL_FUNCDECL_SYS (fesetenv, int, (fenv_t const *envp));
+#  endif
+_GL_CXXALIAS_SYS (fesetenv, int, (fenv_t const *envp));
+# endif
+_GL_CXXALIASWARN (fesetenv);
+#endif
+
+#if @GNULIB_FEUPDATEENV@
+/* Activates the given object *ENVP as the thread's current floating-point
+   environment, raising exactly those floating-point exceptions that were
+   raised before, and returns zero.
+   ENVP may be FE_DFL_ENV.
+   Upon failure, it returns non-zero.  */
+# if @REPLACE_FEUPDATEENV@ || (!@HAVE_FEUPDATEENV@ && (defined __FreeBSD__ || defined _MSC_VER)) /* has an inline definition */
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef feupdateenv
+#   define feupdateenv rpl_feupdateenv
+#  endif
+_GL_FUNCDECL_RPL (feupdateenv, int, (fenv_t const *envp));
+_GL_CXXALIAS_RPL (feupdateenv, int, (fenv_t const *envp));
+# else
+#  if !@HAVE_FEUPDATEENV@
+_GL_FUNCDECL_SYS (feupdateenv, int, (fenv_t const *envp));
+#  endif
+_GL_CXXALIAS_SYS (feupdateenv, int, (fenv_t const *envp));
+# endif
+_GL_CXXALIASWARN (feupdateenv);
+#endif
+
+#if @GNULIB_FEHOLDEXCEPT@
+/* Stores the thread's current floating-point environment in *ENVP, clears
+   the floating-point exception status flags, disables trapping for all
+   floating-point exceptions, and returns zero.  Upon failure, it returns
+   non-zero.  */
+# if @REPLACE_FEHOLDEXCEPT@ || (!@HAVE_FEHOLDEXCEPT@ && defined __FreeBSD__) /* has an inline definition */
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef feholdexcept
+#   define feholdexcept rpl_feholdexcept
+#  endif
+_GL_FUNCDECL_RPL (feholdexcept, int, (fenv_t *envp));
+_GL_CXXALIAS_RPL (feholdexcept, int, (fenv_t *envp));
+# else
+#  if !@HAVE_FEHOLDEXCEPT@
+_GL_FUNCDECL_SYS (feholdexcept, int, (fenv_t *envp));
+#  endif
+_GL_CXXALIAS_SYS (feholdexcept, int, (fenv_t *envp));
+# endif
+_GL_CXXALIASWARN (feholdexcept);
 #endif
 
 
diff --git a/m4/fenv-environment.m4 b/m4/fenv-environment.m4
new file mode 100644 (file)
index 0000000..572a20f
--- /dev/null
@@ -0,0 +1,515 @@
+# fenv-environment.m4 serial 1
+dnl Copyright (C) 2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FENV_ENVIRONMENT],
+[
+  AC_REQUIRE([gl_FENV_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST])
+
+  dnl FE_DFL_ENV references an undefined global variable on
+  dnl NetBSD 9.3/{hppa,sparc} and Cygwin 2.9.0 (see
+  dnl <https://sourceware.org/pipermail/cygwin/2018-July/237888.html>).
+  AC_CACHE_CHECK([whether FE_DFL_ENV is usable],
+    [gl_cv_var_FE_DFL_ENV_works],
+    [case "$host_os" in
+       netbsd* | cygwin*)
+         save_LIBS="$LIBS"
+         LIBS="$LIBS -lm"
+         AC_LINK_IFELSE(
+           [AC_LANG_PROGRAM([[
+              #include <fenv.h>
+              fenv_t volatile env;
+              ]],
+              [[fenv_t const * volatile fenv_default = FE_DFL_ENV;
+                env = *fenv_default;
+              ]])
+           ],
+           [gl_cv_var_FE_DFL_ENV_works=yes],
+           [gl_cv_var_FE_DFL_ENV_works=no])
+         LIBS="$save_LIBS"
+         ;;
+       *)
+         gl_cv_var_FE_DFL_ENV_works="guessing yes"
+         ;;
+     esac
+    ])
+  case "$gl_cv_var_FE_DFL_ENV_works" in
+    *yes)
+      AC_DEFINE([HAVE_FE_DFL_ENV], [1],
+        [Define to 1 if FE_DFL_ENV from <fenv.h> is usable.])
+      ;;
+    *)
+      dnl If FE_DFL_ENV is not usable, we not only need to redefine it,
+      dnl but also override fesetenv and feupdateenv.
+      REPLACE_FESETENV=1
+      REPLACE_FEUPDATEENV=1
+      ;;
+  esac
+  gl_MATHFUNC([fesetenv], [int], [(fenv_t const *)], [
+    #include <fenv.h>
+    fenv_t fenv_ret;
+    #if HAVE_FE_DFL_ENV
+    /* FreeBSD often has fesetenv inline in <fenv.h>, but needs -lm for
+       FE_DFL_ENV.  */
+    fenv_t const *fenv_default = FE_DFL_ENV;
+    #endif
+  ])
+  if test $gl_cv_func_fesetenv_no_libm = yes \
+     || test $gl_cv_func_fesetenv_in_libm = yes; then
+    dnl It needs linking with -lm on
+    dnl glibc, FreeBSD, NetBSD, OpenBSD, AIX, HP-UX, IRIX, Solaris, Android.
+    if test $gl_cv_func_fesetenv_no_libm = yes; then
+      FENV_ENVIRONMENT_LIBM=
+    else
+      FENV_ENVIRONMENT_LIBM=-lm
+    fi
+    dnl The feholdexcept function does not work on glibc 2.5 (see
+    dnl <https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a8c79c4088e8c04e4297936efa0dee6c8e6e974d>)
+    dnl and on FreeBSD 12.2/arm64.
+    dnl Also, on musl libc for most CPUs, it does not clear the exception trap
+    dnl bits. It's mentioned in
+    dnl <https://wiki.musl-libc.org/functional-differences-from-glibc.html>.
+    dnl Likewise on mingw: it does not clear the exception trap bits.
+    dnl Whereas on MSVC, this function may even fail.
+    AC_CACHE_CHECK([whether feholdexcept works],
+      [gl_cv_func_feholdexcept_works],
+      [case "$host_os" in
+         # Guess no on glibc ≤ 2.5.
+         *-gnu* | gnu*)
+           AC_COMPILE_IFELSE(
+             [AC_LANG_PROGRAM([[
+                #include <string.h> /* for __GNU_LIBRARY__ */
+                #ifdef __GNU_LIBRARY__
+                 #include <features.h>
+                 #if __GLIBC__ == 2 && __GLIBC_MINOR__ <= 5
+                  Unlucky user
+                 #endif
+                #endif
+                ]],
+                [])],
+             [gl_cv_func_feholdexcept_works="guessing yes"],
+             [gl_cv_func_feholdexcept_works="guessing no"])
+           ;;
+         *-musl* | midipix*)
+           dnl This is only needed when the module 'fenv-exceptions-trapping' is
+           dnl in use.
+           m4_ifdef([g][l_FENV_EXCEPTIONS_TRAPPING], [
+             case "$host_cpu" in
+               riscv*)
+                 dnl This architecture does not have exception trap bits anyway.
+                 gl_cv_func_feholdexcept_works="guessing yes"
+                 ;;
+               i?86)
+                 dnl On this architecture, the feholdexcept override happens to
+                 dnl not be needed.
+                 gl_cv_func_feholdexcept_works="guessing yes"
+                 ;;
+               *)
+                 gl_cv_func_feholdexcept_works="guessing no"
+                 ;;
+             esac
+           ], [gl_cv_func_feholdexcept_works="guessing yes"])
+           ;;
+         mingw* | windows*)
+           gl_cv_func_feholdexcept_works="guessing no"
+           ;;
+         *)
+           dnl This test fails on FreeBSD 12.2/arm64.
+           save_LIBS="$LIBS"
+           LIBS="$LIBS $FENV_ENVIRONMENT_LIBM"
+           AC_RUN_IFELSE(
+             [AC_LANG_PROGRAM([[
+                #include <fenv.h>
+                ]],
+                [[fenv_t env;
+                  feclearexcept (FE_ALL_EXCEPT);
+                  feraiseexcept (FE_INVALID | FE_OVERFLOW | FE_INEXACT);
+                  return (feholdexcept (&env) == 0
+                          && fetestexcept (FE_ALL_EXCEPT) != 0);
+                ]])
+             ],
+             [gl_cv_func_feholdexcept_works="guessing yes"],
+             [gl_cv_func_feholdexcept_works=no],
+             [gl_cv_func_feholdexcept_works="guessing yes"])
+           LIBS="$save_LIBS"
+           ;;
+       esac
+      ])
+    case "$gl_cv_func_feholdexcept_works" in
+      *yes) ;;
+      *) REPLACE_FEHOLDEXCEPT=1 ;;
+    esac
+    dnl The fegetenv function does not work on glibc 2.19/x86_64
+    dnl (see <https://sourceware.org/bugzilla/show_bug.cgi?id=16198>)
+    dnl and Mac OS X 10.5/{i386,x86_64}.
+    AC_CACHE_CHECK([whether fegetenv works],
+      [gl_cv_func_fegetenv_works],
+      [case "$host" in
+         # Guess no on glibc ≤ 2.19 / x86_64.
+         x86_64-*-*-gnu* | x86_64-*-gnu*)
+           AC_COMPILE_IFELSE(
+             [AC_LANG_PROGRAM([[
+                #include <string.h> /* for __GNU_LIBRARY__ */
+                #ifdef __GNU_LIBRARY__
+                 #include <features.h>
+                 #if __GLIBC__ == 2 && __GLIBC_MINOR__ <= 19
+                  Unlucky user
+                 #endif
+                #endif
+                ]],
+                [])],
+             [gl_cv_func_fegetenv_works="guessing yes"],
+             [gl_cv_func_fegetenv_works="guessing no"])
+           ;;
+         # Guess no on Mac OS X/{i386,x86_64}.
+         *86*-*-darwin*)
+           gl_cv_func_fegetenv_works="guessing no"
+           ;;
+         *) gl_cv_func_fegetenv_works="guessing yes" ;;
+       esac
+      ])
+    case "$gl_cv_func_fegetenv_works" in
+      *yes) ;;
+      *) REPLACE_FEGETENV=1 ;;
+    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 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 and on NetBSD/m68k.
+    AC_CACHE_CHECK([whether fesetenv works],
+      [gl_cv_func_fesetenv_works],
+      [case "$host" in
+         i?86-*-*-musl* | x86_64-*-*-musl*)
+           dnl This is only needed when the module 'fenv-exceptions-trapping' is
+           dnl in use.
+           m4_ifdef([g][l_FENV_EXCEPTIONS_TRAPPING], [
+             gl_cv_func_fesetenv_works="guessing no"
+           ], [gl_cv_func_fesetenv_works="guessing yes"])
+           ;;
+         *-*-aix*)
+           save_LIBS="$LIBS"
+           LIBS="$LIBS $FENV_ENVIRONMENT_LIBM"
+           AC_RUN_IFELSE(
+             [AC_LANG_PROGRAM([[
+                #include <fenv.h>
+                #include <fptrap.h>
+                ]],
+                [[fenv_t env;
+                  feclearexcept (FE_ALL_EXCEPT);
+                  if (fegetenv (&env) == 0)
+                    {
+                      fp_enable (TRP_DIV_BY_ZERO);
+                      if (!fp_is_enabled (TRP_DIV_BY_ZERO))
+                        return 1;
+                      if (fesetenv (&env) == 0
+                          && fp_is_enabled (TRP_DIV_BY_ZERO))
+                        return 2;
+                    }
+                  return 0;
+                ]])
+             ],
+             [gl_cv_func_fesetenv_works="guessing yes"],
+             [gl_cv_func_fesetenv_works=no],
+             [gl_cv_func_fesetenv_works="guessing no"])
+           LIBS="$save_LIBS"
+           ;;
+         *-*-solaris*)
+           save_LIBS="$LIBS"
+           LIBS="$LIBS $FENV_ENVIRONMENT_LIBM"
+           AC_RUN_IFELSE(
+             [AC_LANG_PROGRAM([[
+                #include <fenv.h>
+                #include <ieeefp.h>
+                ]],
+                [[fenv_t env1, env2;
+                  feclearexcept (FE_ALL_EXCEPT);
+                  if (fegetenv (&env1) == 0)
+                    {
+                      fpsetmask (FP_X_DZ);
+                      if (fegetenv (&env2) == 0)
+                        {
+                          if (fpgetmask () != FP_X_DZ)
+                            return 1;
+                          if (fesetenv (&env1) == 0)
+                            {
+                              if (fpgetmask () != 0)
+                                return 2;
+                              fpsetmask (FP_X_INV);
+                              if (fpgetmask () != FP_X_INV)
+                                return 3;
+                              if (fesetenv (&env2) == 0)
+                                {
+                                  if (fpgetmask () != FP_X_DZ)
+                                    return 4;
+                                }
+                            }
+                        }
+                    }
+                  return 0;
+                ]])
+             ],
+             [gl_cv_func_fesetenv_works="guessing yes"],
+             [gl_cv_func_fesetenv_works=no],
+             [gl_cv_func_fesetenv_works="guessing no"])
+           LIBS="$save_LIBS"
+           ;;
+         # Guess no on NetBSD/m68k.
+         m68*-*-netbsd*)
+           gl_cv_func_fesetenv_works="guessing no"
+           ;;
+         # Guess no on mingw and MSVC.
+         *86*-*-mingw* | *86*-*-windows*)
+           gl_cv_func_fesetenv_works="guessing no"
+           ;;
+         *)
+           dnl This test fails on FreeBSD 12.2/arm64.
+           dnl Here we test only the rounding mode restoration, but the function
+           dnl is more broken than that.
+           save_LIBS="$LIBS"
+           LIBS="$LIBS $FENV_ENVIRONMENT_LIBM"
+           AC_RUN_IFELSE(
+             [AC_LANG_PROGRAM([[
+                #include <fenv.h>
+                ]],
+                [[fenv_t env;
+                  return (fesetround (FE_TOWARDZERO) == 0
+                          && fegetenv (&env) == 0
+                          && fesetround (FE_TONEAREST) == 0
+                          && fesetenv (&env) == 0
+                          && fegetround () != FE_TOWARDZERO);
+                ]])
+             ],
+             [gl_cv_func_fesetenv_works="guessing yes"],
+             [gl_cv_func_fesetenv_works=no],
+             [gl_cv_func_fesetenv_works="guessing yes"])
+           LIBS="$save_LIBS"
+           ;;
+       esac
+      ])
+    case "$gl_cv_func_fesetenv_works" in
+      *yes) ;;
+      *) REPLACE_FESETENV=1 ;;
+    esac
+    dnl The feupdateenv function does not work on glibc 2.37/riscv64
+    dnl (see <https://sourceware.org/bugzilla/show_bug.cgi?id=31022>)
+    dnl and on glibc 2.37/hppa
+    dnl (see <https://sourceware.org/bugzilla/show_bug.cgi?id=31023>)
+    dnl and on glibc 2.5/{i386,x86_64,ia64} (see
+    dnl <https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a8c79c4088e8c04e4297936efa0dee6c8e6e974d>)
+    dnl and on Mac OS X 10.5/{i386,x86_64} (where it forgets about the currently
+    dnl set floating-point exception flags)
+    dnl and on musl libc/s390x (where it does not trigger traps)
+    dnl on musl libc/{i386,x86_64} and AIX and Solaris and mingw 10 (where it
+    dnl 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 On MSVC 14 it may even fail.
+    AC_CACHE_CHECK([whether feupdateenv works],
+      [gl_cv_func_feupdateenv_works],
+      [case "$host" in
+         # Guess no on glibc/riscv.
+         riscv*-*-*-gnu*)
+           save_LIBS="$LIBS"
+           LIBS="$LIBS $FENV_ENVIRONMENT_LIBM"
+           AC_RUN_IFELSE(
+             [AC_LANG_PROGRAM([[
+                #include <fenv.h>
+                ]],
+                [[return feupdateenv (FE_DFL_ENV) != 0;
+                ]])
+             ],
+             [gl_cv_func_feupdateenv_works=yes],
+             [gl_cv_func_feupdateenv_works=no],
+             [gl_cv_func_feupdateenv_works="guessing no"])
+           LIBS="$save_LIBS"
+           ;;
+         # Guess no on glibc/hppa.
+         hppa*-*-*-gnu*)
+           save_LIBS="$LIBS"
+           LIBS="$LIBS $FENV_ENVIRONMENT_LIBM"
+           AC_RUN_IFELSE(
+             [AC_LANG_PROGRAM([[
+                #include <fenv.h>
+                ]],
+                [[fenv_t env;
+                  feclearexcept (FE_ALL_EXCEPT);
+                  feenableexcept (FE_INVALID);
+                  fegetenv (&env);
+                  fesetenv (FE_DFL_ENV);
+                  feraiseexcept (FE_INVALID);
+                  feupdateenv (&env);
+                  return 0;
+                ]])
+             ],
+             [gl_cv_func_feupdateenv_works=no],
+             [gl_cv_func_feupdateenv_works="guessing yes"],
+             [gl_cv_func_feupdateenv_works="guessing no"])
+           LIBS="$save_LIBS"
+           ;;
+         # Guess no on glibc ≤ 2.5/{i386,x86_64,ia64}.
+         *86*-*-*-gnu* | *86*-*-gnu* | ia64*-*-*-gnu*)
+           AC_COMPILE_IFELSE(
+             [AC_LANG_PROGRAM([[
+                #include <string.h> /* for __GNU_LIBRARY__ */
+                #ifdef __GNU_LIBRARY__
+                 #include <features.h>
+                 #if __GLIBC__ == 2 && __GLIBC_MINOR__ <= 5
+                  Unlucky user
+                 #endif
+                #endif
+                ]],
+                [])],
+             [gl_cv_func_feupdateenv_works="guessing yes"],
+             [gl_cv_func_feupdateenv_works="guessing no"])
+           ;;
+         i?86-*-*-musl* | x86_64-*-*-musl* | \
+         s390*-*-*-musl*)
+           dnl This is only needed when the module 'fenv-exceptions-trapping' is
+           dnl in use.
+           m4_ifdef([g][l_FENV_EXCEPTIONS_TRAPPING], [
+             gl_cv_func_feupdateenv_works="guessing no"
+           ], [gl_cv_func_feupdateenv_works="guessing yes"])
+           ;;
+         *-*-aix*)
+           save_LIBS="$LIBS"
+           LIBS="$LIBS $FENV_ENVIRONMENT_LIBM"
+           AC_RUN_IFELSE(
+             [AC_LANG_PROGRAM([[
+                #include <fenv.h>
+                #include <fptrap.h>
+                ]],
+                [[fenv_t env;
+                  feclearexcept (FE_ALL_EXCEPT);
+                  if (fegetenv (&env) == 0)
+                    {
+                      fp_enable (TRP_DIV_BY_ZERO);
+                      if (!fp_is_enabled (TRP_DIV_BY_ZERO))
+                        return 1;
+                      if (feupdateenv (&env) == 0
+                          && fp_is_enabled (TRP_DIV_BY_ZERO))
+                        return 2;
+                    }
+                  return 0;
+                ]])
+             ],
+             [gl_cv_func_feupdateenv_works="guessing yes"],
+             [gl_cv_func_feupdateenv_works=no],
+             [gl_cv_func_feupdateenv_works="guessing no"])
+           LIBS="$save_LIBS"
+           ;;
+         *-*-solaris*)
+           save_LIBS="$LIBS"
+           LIBS="$LIBS $FENV_ENVIRONMENT_LIBM"
+           AC_RUN_IFELSE(
+             [AC_LANG_PROGRAM([[
+                #include <fenv.h>
+                #include <ieeefp.h>
+                ]],
+                [[fenv_t env1, env2;
+                  feclearexcept (FE_ALL_EXCEPT);
+                  if (fegetenv (&env1) == 0)
+                    {
+                      fpsetmask (FP_X_DZ);
+                      if (fegetenv (&env2) == 0)
+                        {
+                          if (fpgetmask () != FP_X_DZ)
+                            return 1;
+                          if (feupdateenv (&env1) == 0)
+                            {
+                              if (fpgetmask () != 0)
+                                return 2;
+                              fpsetmask (FP_X_INV);
+                              if (fpgetmask () != FP_X_INV)
+                                return 3;
+                              if (feupdateenv (&env2) == 0)
+                                {
+                                  if (fpgetmask () != FP_X_DZ)
+                                    return 4;
+                                }
+                            }
+                        }
+                    }
+                  return 0;
+                ]])
+             ],
+             [gl_cv_func_feupdateenv_works="guessing yes"],
+             [gl_cv_func_feupdateenv_works=no],
+             [gl_cv_func_feupdateenv_works="guessing no"])
+           LIBS="$save_LIBS"
+           ;;
+         # Guess no on Mac OS X/{i386,x86_64}.
+         *86*-*-darwin*)
+           gl_cv_func_feupdateenv_works="guessing no"
+           ;;
+         # Guess no on mingw and MSVC.
+         *86*-*-mingw* | *86*-*-windows*)
+           gl_cv_func_feupdateenv_works="guessing no"
+           ;;
+         *)
+           dnl This test fails on FreeBSD 12.2/arm64.
+           dnl Here we test only the rounding mode restoration, but the function
+           dnl is more broken than that.
+           save_LIBS="$LIBS"
+           LIBS="$LIBS $FENV_ENVIRONMENT_LIBM"
+           AC_RUN_IFELSE(
+             [AC_LANG_PROGRAM([[
+                #include <fenv.h>
+                ]],
+                [[fenv_t env;
+                  return (fesetround (FE_TOWARDZERO) == 0
+                          && fegetenv (&env) == 0
+                          && fesetround (FE_TONEAREST) == 0
+                          && feupdateenv (&env) == 0
+                          && fegetround () != FE_TOWARDZERO);
+                ]])
+             ],
+             [gl_cv_func_feupdateenv_works="guessing yes"],
+             [gl_cv_func_feupdateenv_works=no],
+             [gl_cv_func_feupdateenv_works="guessing yes"])
+           LIBS="$save_LIBS"
+           ;;
+       esac
+      ])
+    case "$gl_cv_func_feupdateenv_works" in
+      *yes) ;;
+      *) REPLACE_FEUPDATEENV=1 ;;
+    esac
+    dnl Two functions are in the same compilation unit.
+    if test $REPLACE_FEGETENV = 1 || test $REPLACE_FESETENV = 1; then
+      REPLACE_FEGETENV=1
+      REPLACE_FESETENV=1
+    fi
+    dnl On AIX, feholdexcept, like fegetenv, does not save the exception trap
+    dnl bits.
+    case "$host_os" in
+      aix*)
+        if test $REPLACE_FEGETENV = 1; then
+          REPLACE_FEHOLDEXCEPT=1
+        fi
+        ;;
+    esac
+  else
+    HAVE_FEGETENV=0
+    HAVE_FESETENV=0
+    HAVE_FEUPDATEENV=0
+    HAVE_FEHOLDEXCEPT=0
+    dnl It needs linking with -lm on platforms which define FE_DFL_ENV as the
+    dnl address of a global variable.
+    case "$host" in
+      powerpc*-*-linux*-gnu* | \
+      *-*-freebsd* | *-*-dragonfly* | \
+      *-*-netbsd* | \
+      *-*-openbsd* | \
+      *-*-solaris* )
+        FENV_ENVIRONMENT_LIBM=-lm ;;
+      *) FENV_ENVIRONMENT_LIBM= ;;
+    esac
+  fi
+  AC_SUBST([FENV_ENVIRONMENT_LIBM])
+])
index 853f66e5a9bbfdffb92022fba26e3df3e774b90d..9ad078a9fff88a79d65f2fa3b9e9092acec2b63f 100644 (file)
@@ -1,4 +1,4 @@
-# fenv_h.m4 serial 1
+# fenv_h.m4 serial 2
 dnl Copyright (C) 2023 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -88,12 +88,16 @@ AC_DEFUN([gl_FENV_H_DEFAULTS],
   REPLACE_FECLEAREXCEPT=0;           AC_SUBST([REPLACE_FECLEAREXCEPT])
   REPLACE_FEDISABLEEXCEPT=0;         AC_SUBST([REPLACE_FEDISABLEEXCEPT])
   REPLACE_FEENABLEEXCEPT=0;          AC_SUBST([REPLACE_FEENABLEEXCEPT])
+  REPLACE_FEGETENV=0;                AC_SUBST([REPLACE_FEGETENV])
   REPLACE_FEGETEXCEPT=0;             AC_SUBST([REPLACE_FEGETEXCEPT])
   REPLACE_FEGETEXCEPTFLAG=0;         AC_SUBST([REPLACE_FEGETEXCEPTFLAG])
   REPLACE_FEGETROUND=0;              AC_SUBST([REPLACE_FEGETROUND])
+  REPLACE_FEHOLDEXCEPT=0;            AC_SUBST([REPLACE_FEHOLDEXCEPT])
   REPLACE_FERAISEEXCEPT=0;           AC_SUBST([REPLACE_FERAISEEXCEPT])
+  REPLACE_FESETENV=0;                AC_SUBST([REPLACE_FESETENV])
   REPLACE_FESETEXCEPT=0;             AC_SUBST([REPLACE_FESETEXCEPT])
   REPLACE_FESETEXCEPTFLAG=0;         AC_SUBST([REPLACE_FESETEXCEPTFLAG])
   REPLACE_FESETROUND=0;              AC_SUBST([REPLACE_FESETROUND])
   REPLACE_FETESTEXCEPT=0;            AC_SUBST([REPLACE_FETESTEXCEPT])
+  REPLACE_FEUPDATEENV=0;             AC_SUBST([REPLACE_FEUPDATEENV])
 ])
index bf83bd2b8a3c9e083ddb3b3700d3534fca830acb..feca041287b2c84c4e41a55587f0137a84a2c41d 100644 (file)
@@ -1,4 +1,4 @@
-# mathfunc.m4 serial 16
+# mathfunc.m4 serial 17
 dnl Copyright (C) 2010-2023 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -42,7 +42,9 @@ AC_DEFUN([gl_MATHFUNC],
                                                 [m4_bpatsubst(
                                                    [m4_bpatsubst(
                                                       [m4_bpatsubst(
-                                                         [$3],
+                                                         [m4_bpatsubst(
+                                                            [$3],
+                                                            [fenv_t\( const\)? \*], [&fenv_ret])],
                                                          [fexcept_t\( const\)? \*], [&fx_ret])],
                                                       [int\( const\)? \*],
                                                       [&i_ret])],
index 87a9a02d2862fc91a8a64277d74396a81841219c..6c028188182fbb1fd361e8850a1dbac28e3d2498 100644 (file)
@@ -65,14 +65,18 @@ fenv.h: fenv.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(WARN_ON_USE_H)
              -e 's|@''REPLACE_FECLEAREXCEPT''@|$(REPLACE_FECLEAREXCEPT)|g' \
              -e 's|@''REPLACE_FEDISABLEEXCEPT''@|$(REPLACE_FEDISABLEEXCEPT)|g' \
              -e 's|@''REPLACE_FEENABLEEXCEPT''@|$(REPLACE_FEENABLEEXCEPT)|g' \
+             -e 's|@''REPLACE_FEGETENV''@|$(REPLACE_FEGETENV)|g' \
              -e 's|@''REPLACE_FEGETEXCEPT''@|$(REPLACE_FEGETEXCEPT)|g' \
              -e 's|@''REPLACE_FEGETEXCEPTFLAG''@|$(REPLACE_FEGETEXCEPTFLAG)|g' \
              -e 's|@''REPLACE_FEGETROUND''@|$(REPLACE_FEGETROUND)|g' \
+             -e 's|@''REPLACE_FEHOLDEXCEPT''@|$(REPLACE_FEHOLDEXCEPT)|g' \
              -e 's|@''REPLACE_FERAISEEXCEPT''@|$(REPLACE_FERAISEEXCEPT)|g' \
+             -e 's|@''REPLACE_FESETENV''@|$(REPLACE_FESETENV)|g' \
              -e 's|@''REPLACE_FESETEXCEPT''@|$(REPLACE_FESETEXCEPT)|g' \
              -e 's|@''REPLACE_FESETEXCEPTFLAG''@|$(REPLACE_FESETEXCEPTFLAG)|g' \
              -e 's|@''REPLACE_FESETROUND''@|$(REPLACE_FESETROUND)|g' \
              -e 's|@''REPLACE_FETESTEXCEPT''@|$(REPLACE_FETESTEXCEPT)|g' \
+             -e 's|@''REPLACE_FEUPDATEENV''@|$(REPLACE_FEUPDATEENV)|g' \
              -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
              -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
              $(srcdir)/fenv.in.h > $@-t
diff --git a/modules/fenv-environment b/modules/fenv-environment
new file mode 100644 (file)
index 0000000..ab5e15b
--- /dev/null
@@ -0,0 +1,56 @@
+Description:
+Functions for controlling the floating-point environment as a whole:
+fegetenv, fesetenv, feupdateenv, feholdexcept.
+
+Files:
+lib/fenv-env.c
+lib/fenv-env-update.c
+lib/fenv-env-hold.c
+lib/fenv-private.h
+m4/fenv-environment.m4
+m4/mathfunc.m4
+m4/musl.m4
+
+Depends-on:
+fenv
+extensions
+verify
+fenv-exceptions-tracking-c99
+fenv-exceptions-trapping
+
+configure.ac:
+gl_FENV_ENVIRONMENT
+gl_CONDITIONAL([GL_COND_OBJ_FENV_ENVIRONMENT],
+               [test $HAVE_FESETENV = 0 || test $REPLACE_FESETENV = 1])
+gl_CONDITIONAL([GL_COND_OBJ_FENV_ENVIRONMENT_UPDATE],
+               [test $HAVE_FEUPDATEENV = 0 || test $REPLACE_FEUPDATEENV = 1])
+gl_CONDITIONAL([GL_COND_OBJ_FENV_ENVIRONMENT_HOLD],
+               [test $HAVE_FEHOLDEXCEPT = 0 || test $REPLACE_FEHOLDEXCEPT = 1])
+gl_FENV_MODULE_INDICATOR([fegetenv])
+gl_FENV_MODULE_INDICATOR([fesetenv])
+gl_FENV_MODULE_INDICATOR([feupdateenv])
+gl_FENV_MODULE_INDICATOR([feholdexcept])
+gl_MUSL_LIBC
+
+Makefile.am:
+if GL_COND_OBJ_FENV_ENVIRONMENT
+lib_SOURCES += fenv-env.c
+endif
+if GL_COND_OBJ_FENV_ENVIRONMENT_UPDATE
+lib_SOURCES += fenv-env-update.c
+endif
+if GL_COND_OBJ_FENV_ENVIRONMENT_HOLD
+lib_SOURCES += fenv-env-hold.c
+endif
+
+Include:
+#include <fenv.h>
+
+Link:
+$(FENV_ENVIRONMENT_LIBM)
+
+License:
+LGPLv2+
+
+Maintainer:
+all