From: Bruno Haible Date: Sun, 5 Nov 2023 14:00:29 +0000 (+0100) Subject: fenv-environment: New module. X-Git-Tag: v1.0~644 X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=c19cabc0bf5d8e1afec9fd50fee47104000fa3e4;p=gnulib.git fenv-environment: New module. * lib/fenv.in.h (fenv_t) [hppa]: Remove the __exception field. (FE_DFL_ENV): Override if 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. --- diff --git a/ChangeLog b/ChangeLog index 7c2d58913d..18ee7639c2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +2023-11-05 Bruno Haible + + fenv-environment: New module. + * lib/fenv.in.h (fenv_t) [hppa]: Remove the __exception field. + (FE_DFL_ENV): Override if 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 fenv-exceptions-tracking-c99 tests: Enhance tests. diff --git a/doc/posix-functions/fegetenv.texi b/doc/posix-functions/fegetenv.texi index 99d8d5250c..4bde34b5aa 100644 --- a/doc/posix-functions/fegetenv.texi +++ b/doc/posix-functions/fegetenv.texi @@ -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 diff --git a/doc/posix-functions/feholdexcept.texi b/doc/posix-functions/feholdexcept.texi index 52d8219c22..dcd99a2f92 100644 --- a/doc/posix-functions/feholdexcept.texi +++ b/doc/posix-functions/feholdexcept.texi @@ -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 diff --git a/doc/posix-functions/fesetenv.texi b/doc/posix-functions/fesetenv.texi index 7a74beaad1..f7d45cf543 100644 --- a/doc/posix-functions/fesetenv.texi +++ b/doc/posix-functions/fesetenv.texi @@ -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 diff --git a/doc/posix-functions/feupdateenv.texi b/doc/posix-functions/feupdateenv.texi index 4bcbf5cfd8..bb844ec2f1 100644 --- a/doc/posix-functions/feupdateenv.texi +++ b/doc/posix-functions/feupdateenv.texi @@ -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 index 0000000000..ab156d5e74 --- /dev/null +++ b/lib/fenv-env-hold.c @@ -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 . */ + +#include + +/* Specification. */ +#include + +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 index 0000000000..876c223409 --- /dev/null +++ b/lib/fenv-env-update.c @@ -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 . */ + +#include + +/* Specification. */ +#include + +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 index 0000000000..3e97dc3e65 --- /dev/null +++ b/lib/fenv-env.c @@ -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 . */ + +/* Based on glibc/sysdeps//{fegetenv.c,fesetenv.c} + together with glibc/sysdeps//{fpu_control.h,fenv_private.h,fenv_libc.h}. */ + +#include + +/* Specification. */ +#include + +#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 +# include + +# include + +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: */ + 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: */ + 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 +# 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 ." +# 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 diff --git a/lib/fenv.in.h b/lib/fenv.in.h index b1c52d6e27..6d479124df 100644 --- a/lib/fenv.in.h +++ b/lib/fenv.in.h @@ -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 index 0000000000..572a20f7a7 --- /dev/null +++ b/m4/fenv-environment.m4 @@ -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 ). + 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_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 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_t fenv_ret; + #if HAVE_FE_DFL_ENV + /* FreeBSD often has fesetenv inline in , 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 ) + 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 . + 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 /* for __GNU_LIBRARY__ */ + #ifdef __GNU_LIBRARY__ + #include + #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_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 ) + 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 /* for __GNU_LIBRARY__ */ + #ifdef __GNU_LIBRARY__ + #include + #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 ), + 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 + #include + ]], + [[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 + #include + ]], + [[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_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 ) + dnl and on glibc 2.37/hppa + dnl (see ) + dnl and on glibc 2.5/{i386,x86_64,ia64} (see + dnl ) + 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 ). + 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 + ]], + [[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_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 /* for __GNU_LIBRARY__ */ + #ifdef __GNU_LIBRARY__ + #include + #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 + #include + ]], + [[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 + #include + ]], + [[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_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]) +]) diff --git a/m4/fenv_h.m4 b/m4/fenv_h.m4 index 853f66e5a9..9ad078a9ff 100644 --- a/m4/fenv_h.m4 +++ b/m4/fenv_h.m4 @@ -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]) ]) diff --git a/m4/mathfunc.m4 b/m4/mathfunc.m4 index bf83bd2b8a..feca041287 100644 --- a/m4/mathfunc.m4 +++ b/m4/mathfunc.m4 @@ -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])], diff --git a/modules/fenv b/modules/fenv index 87a9a02d28..6c02818818 100644 --- a/modules/fenv +++ b/modules/fenv @@ -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 index 0000000000..ab5e15bcab --- /dev/null +++ b/modules/fenv-environment @@ -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 + +Link: +$(FENV_ENVIRONMENT_LIBM) + +License: +LGPLv2+ + +Maintainer: +all