* 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-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.
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
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
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
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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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
typedef struct
{
unsigned int __status_word; /* floating point status register */
- unsigned int __exception[7]; /* floating point exception registers */
} fenv_t;
# elif defined __ia64__
# 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
--- /dev/null
+# 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])
+])
-# 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,
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])
])
-# 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,
[m4_bpatsubst(
[m4_bpatsubst(
[m4_bpatsubst(
- [$3],
+ [m4_bpatsubst(
+ [$3],
+ [fenv_t\( const\)? \*], [&fenv_ret])],
[fexcept_t\( const\)? \*], [&fx_ret])],
[int\( const\)? \*],
[&i_ret])],
-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
--- /dev/null
+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