From: Bruno Haible <bruno@clisp.org> Date: Tue, 31 Oct 2023 20:46:13 +0000 (+0100) Subject: fenv-exceptions-trapping: New module. X-Git-Tag: v1.0~662 X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=e5a52e29dfa5a292f9f94c56c9ed2f3c9ff189af;p=gnulib.git fenv-exceptions-trapping: New module. * lib/fenv.in.h (feenableexcept, fedisableexcept, fegetexcept): New declarations. * lib/fenv-except-trapping.c: New file, based on glibc. * m4/fenv_h.m4 (gl_FENV_H): Test also whether fegetexcept is declared. * m4/fenv-exceptions-trapping.m4: New file. * modules/fenv-exceptions-trapping: New file. * doc/glibc-functions/fegetexcept.texi: Mention the new module. * doc/glibc-functions/fedisableexcept.texi: Likewise. * doc/glibc-functions/feenableexcept.texi: Likewise. Mention the glibc, macOS, FreeBSD bugs. --- diff --git a/ChangeLog b/ChangeLog index 0476828c50..f7d1a2237e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2023-10-31 Bruno Haible <bruno@clisp.org> + + fenv-exceptions-trapping: New module. + * lib/fenv.in.h (feenableexcept, fedisableexcept, fegetexcept): New + declarations. + * lib/fenv-except-trapping.c: New file, based on glibc. + * m4/fenv_h.m4 (gl_FENV_H): Test also whether fegetexcept is declared. + * m4/fenv-exceptions-trapping.m4: New file. + * modules/fenv-exceptions-trapping: New file. + * doc/glibc-functions/fegetexcept.texi: Mention the new module. + * doc/glibc-functions/fedisableexcept.texi: Likewise. + * doc/glibc-functions/feenableexcept.texi: Likewise. Mention the glibc, + macOS, FreeBSD bugs. + 2023-10-30 Bruno Haible <bruno@clisp.org> fenv-exceptions-state-c99: Fix the x86_64 and i386 case. diff --git a/doc/glibc-functions/fedisableexcept.texi b/doc/glibc-functions/fedisableexcept.texi index 8b41ae4d87..d26397f44f 100644 --- a/doc/glibc-functions/fedisableexcept.texi +++ b/doc/glibc-functions/fedisableexcept.texi @@ -17,15 +17,15 @@ Documentation: @uref{https://www.kernel.org/doc/man-pages/online/pages/man3/fedisableexcept.3.html,,man fedisableexcept}. @end itemize -Gnulib module: --- +Gnulib module: fenv-exceptions-trapping Portability problems fixed by Gnulib: @itemize +@item +This function is missing on some platforms: +macOS 11.1, musl libc, FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 11.4, Cygwin 1.7.7, mingw, MSVC 14, Android 4.4. @end itemize Portability problems not fixed by Gnulib: @itemize -@item -This function is missing on some platforms: -macOS 11.1, FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 11.4, Cygwin 1.7.7, mingw, MSVC 14, Android 4.4. @end itemize diff --git a/doc/glibc-functions/feenableexcept.texi b/doc/glibc-functions/feenableexcept.texi index dfca91f2a7..a858b4d6c6 100644 --- a/doc/glibc-functions/feenableexcept.texi +++ b/doc/glibc-functions/feenableexcept.texi @@ -17,15 +17,22 @@ Documentation: @uref{https://www.kernel.org/doc/man-pages/online/pages/man3/feenableexcept.3.html,,man feenableexcept}. @end itemize -Gnulib module: --- +Gnulib module: fenv-exceptions-trapping Portability problems fixed by Gnulib: @itemize +@item +This function is missing on some platforms: +macOS 11.1, musl libc, FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 11.4, Cygwin 1.7.7, mingw, MSVC 14, Android 4.4. +@item +This function does not detect failures on +glibc 2.19/aarch64, FreeBSD 12.2/arm, FreeBSD 12.2/arm64. @end itemize Portability problems not fixed by Gnulib: @itemize @item -This function is missing on some platforms: -macOS 11.1, FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 11.4, Cygwin 1.7.7, mingw, MSVC 14, Android 4.4. +The resulting signal is @code{SIGILL} instead of @code{SIGFPE} +on some platforms: +macOS 12.5/arm64. @end itemize diff --git a/doc/glibc-functions/fegetexcept.texi b/doc/glibc-functions/fegetexcept.texi index e3e3e43959..eaa1882d83 100644 --- a/doc/glibc-functions/fegetexcept.texi +++ b/doc/glibc-functions/fegetexcept.texi @@ -17,15 +17,15 @@ Documentation: @uref{https://www.kernel.org/doc/man-pages/online/pages/man3/fegetexcept.3.html,,man fegetexcept}. @end itemize -Gnulib module: --- +Gnulib module: fenv-exceptions-trapping Portability problems fixed by Gnulib: @itemize +@item +This function is missing on many non-glibc platforms: +macOS 11.1, musl libc, FreeBSD 13.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 11.4, Cygwin 1.7.7, mingw, MSVC 14, Android 4.4. @end itemize Portability problems not fixed by Gnulib: @itemize -@item -This function is missing on many non-glibc platforms: -macOS 11.1, FreeBSD 13.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 11.4, Cygwin 1.7.7, mingw, MSVC 14, Android 4.4. @end itemize diff --git a/lib/fenv-except-trapping.c b/lib/fenv-except-trapping.c new file mode 100644 index 0000000000..a86cbd2d39 --- /dev/null +++ b/lib/fenv-except-trapping.c @@ -0,0 +1,951 @@ +/* Functions for turning floating-point exceptions into traps (signals). + 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>/{feenablxcpt.c,fedisblxcpt.c,fegetexcept.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" + +#if defined __GNUC__ || defined __clang__ || defined _MSC_VER + +# if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86) + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + +# if defined _MSC_VER + + exceptions = exceptions_to_x86hardware (exceptions); + + /* Enable the traps only in the SSE unit. */ + unsigned int mxcsr, orig_mxcsr; + _FPU_GETSSECW (orig_mxcsr); + mxcsr = orig_mxcsr & ~(exceptions << 7); + if (mxcsr != orig_mxcsr) + _FPU_SETSSECW (mxcsr); + + unsigned int trapbits = 0x3f & ~(orig_mxcsr >> 7); + return x86hardware_to_exceptions (trapbits); + +# else + + /* Enable the traps in the 387 unit. */ + unsigned short fctrl, orig_fctrl; + _FPU_GETCW (orig_fctrl); + fctrl = orig_fctrl & ~exceptions; + if (fctrl != orig_fctrl) + _FPU_SETCW (fctrl); + + if (CPU_HAS_SSE ()) + { + /* Enable the traps in the SSE unit as well. */ + unsigned int mxcsr, orig_mxcsr; + _FPU_GETSSECW (orig_mxcsr); + mxcsr = orig_mxcsr & ~(exceptions << 7); + if (mxcsr != orig_mxcsr) + _FPU_SETSSECW (mxcsr); + } + + return FE_ALL_EXCEPT & ~orig_fctrl; + +# endif +} + +int +fedisableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + +# if defined _MSC_VER + + exceptions = exceptions_to_x86hardware (exceptions); + + /* Disable the traps only in the SSE unit. */ + unsigned int mxcsr, orig_mxcsr; + _FPU_GETSSECW (orig_mxcsr); + mxcsr = orig_mxcsr | (exceptions << 7); + if (mxcsr != orig_mxcsr) + _FPU_SETSSECW (mxcsr); + + unsigned int trapbits = 0x3f & ~(orig_mxcsr >> 7); + return x86hardware_to_exceptions (trapbits); + +# else + + /* Disable the traps in the 387 unit. */ + unsigned short fctrl, orig_fctrl; + _FPU_GETCW (orig_fctrl); + fctrl = orig_fctrl | exceptions; + if (fctrl != orig_fctrl) + _FPU_SETCW (fctrl); + + if (CPU_HAS_SSE ()) + { + /* Disable the traps in the SSE unit as well. */ + unsigned int mxcsr, orig_mxcsr; + _FPU_GETSSECW (orig_mxcsr); + mxcsr = orig_mxcsr | (exceptions << 7); + if (mxcsr != orig_mxcsr) + _FPU_SETSSECW (mxcsr); + } + + return FE_ALL_EXCEPT & ~orig_fctrl; + +# endif +} + +int +fegetexcept (void) +{ +# if defined _MSC_VER + /* Look at the trap bits in the SSE unit. */ + unsigned int mxcsr; + _FPU_GETSSECW (mxcsr); + unsigned int trapbits = 0x3f & ~(mxcsr >> 7); + return x86hardware_to_exceptions (trapbits); +# else + /* Look at the trap bits in the 387 unit. */ + unsigned short fctrl; + _FPU_GETCW (fctrl); + return FE_ALL_EXCEPT & ~fctrl; +# endif +} + +# elif defined __aarch64__ /* arm64 */ + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned long fpcr, orig_fpcr; + _FPU_GETCW (orig_fpcr); + fpcr = orig_fpcr | (exceptions << 8); + if (fpcr != orig_fpcr) + { + _FPU_SETCW (fpcr); + /* Test whether fpcr was actually changed as desired. */ + unsigned long new_fpcr; + _FPU_GETCW (new_fpcr); + if (new_fpcr != fpcr) + return -1; + } + + return FE_ALL_EXCEPT & (orig_fpcr >> 8); +} + +int +fedisableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned long fpcr, orig_fpcr; + _FPU_GETCW (orig_fpcr); + fpcr = orig_fpcr & ~(exceptions << 8); + if (fpcr != orig_fpcr) + { + _FPU_SETCW (fpcr); + /* Testing whether fpcr was actually changed as desired is not needed + here, since we only cleared some bits. */ + } + + return FE_ALL_EXCEPT & (orig_fpcr >> 8); +} + +int +fegetexcept (void) +{ + unsigned long fpcr; + _FPU_GETCW (fpcr); + return FE_ALL_EXCEPT & (fpcr >> 8); +} + +# elif defined __arm__ + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + +# ifdef __SOFTFP__ + if (exceptions != 0) + return -1; + return 0; +# else + unsigned int fpscr, orig_fpscr; + _FPU_GETCW (orig_fpscr); + fpscr = orig_fpscr | (exceptions << 8); + if (fpscr != orig_fpscr) + { + _FPU_SETCW (fpscr); + /* Test whether fpscr was actually changed as desired. */ + unsigned int new_fpscr; + _FPU_GETCW (new_fpscr); + if (new_fpscr != fpscr) + return -1; + } + + return FE_ALL_EXCEPT & (orig_fpscr >> 8); +# endif +} + +int +fedisableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + +# ifdef __SOFTFP__ + return 0; +# else + unsigned int fpscr, orig_fpscr; + _FPU_GETCW (orig_fpscr); + fpscr = orig_fpscr & ~(exceptions << 8); + if (fpscr != orig_fpscr) + { + _FPU_SETCW (fpscr); + /* Testing whether fpscr was actually changed as desired is not needed + here, since we only cleared some bits. */ + } + + return FE_ALL_EXCEPT & (orig_fpscr >> 8); +# endif +} + +int +fegetexcept (void) +{ +# ifdef __SOFTFP__ + return 0; +# else + unsigned int fpscr; + _FPU_GETCW (fpscr); + return FE_ALL_EXCEPT & (fpscr >> 8); +# endif +} + +# elif defined __alpha + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned long swcr, orig_swcr; + orig_swcr = __ieee_get_fp_control (); + swcr = orig_swcr | ((unsigned long) exceptions >> 16); + if (swcr != orig_swcr) + __ieee_set_fp_control (swcr); + + return FE_ALL_EXCEPT & (orig_swcr << 16); +} + +int +fedisableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned long swcr, orig_swcr; + orig_swcr = __ieee_get_fp_control (); + swcr = orig_swcr & ~((unsigned long) exceptions >> 16); + if (swcr != orig_swcr) + __ieee_set_fp_control (swcr); + + return FE_ALL_EXCEPT & (orig_swcr << 16); +} + +int +fegetexcept (void) +{ + unsigned long swcr = __ieee_get_fp_control (); + return FE_ALL_EXCEPT & (swcr << 16); +} + +# elif defined __hppa + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + union { unsigned long long fpreg; unsigned int halfreg[2]; } s; + /* Get the current status word. */ + __asm__ __volatile__ ("fstd %%fr0,0(%1)" : "=m" (s.fpreg) : "r" (&s.fpreg) : "%r0"); + unsigned int old_halfreg0 = s.halfreg[0]; + /* Set all the relevant bits. */ + s.halfreg[0] |= (unsigned int) exceptions; + if (s.halfreg[0] != old_halfreg0) + { + /* Store the new status word. */ + __asm__ __volatile__ ("fldd 0(%0),%%fr0" : : "r" (&s.fpreg), "m" (s.fpreg) : "%r0"); + } + + return FE_ALL_EXCEPT & old_halfreg0; +} + +int +fedisableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + union { unsigned long long fpreg; unsigned int halfreg[2]; } s; + /* Get the current status word. */ + __asm__ __volatile__ ("fstd %%fr0,0(%1)" : "=m" (s.fpreg) : "r" (&s.fpreg) : "%r0"); + unsigned int old_halfreg0 = s.halfreg[0]; + /* Clear all the relevant bits. */ + s.halfreg[0] &= ~ (unsigned int) exceptions; + if (s.halfreg[0] != old_halfreg0) + { + /* Store the new status word. */ + __asm__ __volatile__ ("fldd 0(%0),%%fr0" : : "r" (&s.fpreg), "m" (s.fpreg) : "%r0"); + } + + return FE_ALL_EXCEPT & old_halfreg0; +} + +int +fegetexcept (void) +{ + union { unsigned long long fpreg; unsigned int halfreg[2]; } s; + /* Get the current status word. */ + __asm__ __volatile__ ("fstd %%fr0,0(%1)" : "=m" (s.fpreg) : "r" (&s.fpreg) : "%r0"); + + return FE_ALL_EXCEPT & s.halfreg[0]; +} + +# elif defined __ia64__ + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned long fpsr, orig_fpsr; + _FPU_GETCW (orig_fpsr); + fpsr = orig_fpsr & ~ (unsigned long) exceptions; + if (fpsr != orig_fpsr) + _FPU_SETCW (fpsr); + + return FE_ALL_EXCEPT & ~orig_fpsr; +} + +int +fedisableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned long fpsr, orig_fpsr; + _FPU_GETCW (orig_fpsr); + fpsr = orig_fpsr | (unsigned long) exceptions; + if (fpsr != orig_fpsr) + _FPU_SETCW (fpsr); + + return FE_ALL_EXCEPT & ~orig_fpsr; +} + +int +fegetexcept (void) +{ + unsigned long fpsr; + _FPU_GETCW (fpsr); + return FE_ALL_EXCEPT & ~fpsr; +} + +# elif defined __m68k__ + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned int fpcr, orig_fpcr; + _FPU_GETCW (orig_fpcr); + fpcr = orig_fpcr | ((exceptions << 6) | (exceptions & FE_INVALID ? 0xc000 : 0)); + if (fpcr != orig_fpcr) + _FPU_SETCW (fpcr); + + return FE_ALL_EXCEPT & (orig_fpcr >> 6); +} + +int +fedisableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned int fpcr, orig_fpcr; + _FPU_GETCW (orig_fpcr); + fpcr = orig_fpcr & ~ ((exceptions << 6) | (exceptions & FE_INVALID ? 0xc000 : 0)); + if (fpcr != orig_fpcr) + _FPU_SETCW (fpcr); + + return FE_ALL_EXCEPT & (orig_fpcr >> 6); +} + +int +fegetexcept (void) +{ + unsigned int fpcr; + _FPU_GETCW (fpcr); + return FE_ALL_EXCEPT & (fpcr >> 6); +} + +# elif defined __mips__ + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned int fcsr, orig_fcsr; + _FPU_GETCW (orig_fcsr); + fcsr = orig_fcsr | (exceptions << 5); + if (fcsr != orig_fcsr) + _FPU_SETCW (fcsr); + + return FE_ALL_EXCEPT & (orig_fcsr >> 5); +} + +int +fedisableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned int fcsr, orig_fcsr; + _FPU_GETCW (orig_fcsr); + fcsr = orig_fcsr & ~ (exceptions << 5); + if (fcsr != orig_fcsr) + _FPU_SETCW (fcsr); + + return FE_ALL_EXCEPT & (orig_fcsr >> 5); +} + +int +fegetexcept (void) +{ + unsigned int fcsr; + _FPU_GETCW (fcsr); + return FE_ALL_EXCEPT & (fcsr >> 5); +} + +# elif defined __loongarch__ + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned int fcsr, orig_fcsr; + _FPU_GETCW (orig_fcsr); + fcsr = orig_fcsr | (exceptions >> 16); + if (fcsr != orig_fcsr) + _FPU_SETCW (fcsr); + + return FE_ALL_EXCEPT & (orig_fcsr << 16); +} + +int +fedisableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned int fcsr, orig_fcsr; + _FPU_GETCW (orig_fcsr); + fcsr = orig_fcsr & ~ (exceptions >> 16); + if (fcsr != orig_fcsr) + _FPU_SETCW (fcsr); + + return FE_ALL_EXCEPT & (orig_fcsr << 16); +} + +int +fegetexcept (void) +{ + unsigned int fcsr; + _FPU_GETCW (fcsr); + return FE_ALL_EXCEPT & (fcsr << 16); +} + +# elif defined __powerpc__ + +# if defined __linux__ +# include <sys/prctl.h> +# elif defined _AIX +# include <fptrap.h> +# endif + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + union { unsigned long long u; double f; } memenv, orig_memenv; + _FPU_GETCW_AS_DOUBLE (memenv.f); + orig_memenv = memenv; + + memenv.u |= ((unsigned int) exceptions >> 22); + + if (!(memenv.u == orig_memenv.u)) + { + if ((orig_memenv.u & 0x000000f7) == 0 && (memenv.u & 0x000000f7) != 0) + { + /* Put the thread into precise trapping mode. */ +# if defined __linux__ || defined __NetBSD__ + prctl (PR_SET_FPEXC, PR_FP_EXC_PRECISE); +# elif defined _AIX + /* Documentation: <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-trap-subroutine> */ + fp_trap (FP_TRAP_SYNC); +# endif + } + + _FPU_SETCW_AS_DOUBLE (memenv.f); + } + + return FE_ALL_EXCEPT & ((unsigned int) orig_memenv.u << 22); +} + +int +fedisableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + union { unsigned long long u; double f; } memenv, orig_memenv; + _FPU_GETCW_AS_DOUBLE (memenv.f); + orig_memenv = memenv; + + memenv.u &= ~ ((unsigned int) exceptions >> 22); + + if (!(memenv.u == orig_memenv.u)) + { + _FPU_SETCW_AS_DOUBLE (memenv.f); + + if ((orig_memenv.u & 0x000000f7) != 0 && (memenv.u & 0x000000f7) == 0) + { + /* Put the thread into no-trapping mode. */ +# if defined __linux__ || defined __NetBSD__ + prctl (PR_SET_FPEXC, PR_FP_EXC_DISABLED); +# elif defined _AIX + /* Documentation: <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-trap-subroutine> */ + fp_trap (FP_TRAP_OFF); +# endif + } + } + + return FE_ALL_EXCEPT & ((unsigned int) orig_memenv.u << 22); +} + +int +fegetexcept (void) +{ + union { unsigned long long u; double f; } memenv; + _FPU_GETCW_AS_DOUBLE (memenv.f); + + return FE_ALL_EXCEPT & ((unsigned int) memenv.u << 22); +} + +# elif defined __riscv + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + if (exceptions != 0) + return -1; + return 0; +} + +int +fedisableexcept (int exceptions) +{ + return 0; +} + +int +fegetexcept (void) +{ + return 0; +} + +# elif defined __s390__ || defined __s390x__ + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned int fpc, orig_fpc; + _FPU_GETCW (orig_fpc); +# if FE_INEXACT == 8 /* glibc compatible FE_* values */ + fpc = orig_fpc | ((unsigned int) exceptions << 24); +# else /* musl libc compatible FE_* values */ + fpc = orig_fpc | ((unsigned int) exceptions << 8); +# endif + if (fpc != orig_fpc) + _FPU_SETCW (fpc); + +# if FE_INEXACT == 8 /* glibc compatible FE_* values */ + return FE_ALL_EXCEPT & (orig_fpc >> 24); +# else /* musl libc compatible FE_* values */ + return FE_ALL_EXCEPT & (orig_fpc >> 8); +# endif +} + +int +fedisableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned int fpc, orig_fpc; + _FPU_GETCW (orig_fpc); +# if FE_INEXACT == 8 /* glibc compatible FE_* values */ + fpc = orig_fpc & ~((unsigned int) exceptions << 24); +# else /* musl libc compatible FE_* values */ + fpc = orig_fpc & ~((unsigned int) exceptions << 8); +# endif + if (fpc != orig_fpc) + _FPU_SETCW (fpc); + +# if FE_INEXACT == 8 /* glibc compatible FE_* values */ + return FE_ALL_EXCEPT & (orig_fpc >> 24); +# else /* musl libc compatible FE_* values */ + return FE_ALL_EXCEPT & (orig_fpc >> 8); +# endif +} + +int +fegetexcept (void) +{ + unsigned int fpc; + _FPU_GETCW (fpc); +# if FE_INEXACT == 8 /* glibc compatible FE_* values */ + return FE_ALL_EXCEPT & (fpc >> 24); +# else /* musl libc compatible FE_* values */ + return FE_ALL_EXCEPT & (fpc >> 8); +# endif +} + +# elif defined __sh__ + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned int fpscr, orig_fpscr; + _FPU_GETCW (orig_fpscr); + fpscr = orig_fpscr | ((unsigned int) exceptions << 5); + if (fpscr != orig_fpscr) + _FPU_SETCW (fpscr); + + return FE_ALL_EXCEPT & (orig_fpscr >> 5); +} + +int +fedisableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned int fpscr, orig_fpscr; + _FPU_GETCW (orig_fpscr); + fpscr = orig_fpscr & ~ ((unsigned int) exceptions << 5); + if (fpscr != orig_fpscr) + _FPU_SETCW (fpscr); + + return FE_ALL_EXCEPT & (orig_fpscr >> 5); +} + +int +fegetexcept (void) +{ + unsigned int fpscr; + _FPU_GETCW (fpscr); + return FE_ALL_EXCEPT & (fpscr >> 5); +} + +# elif defined __sparc + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned long fsr, orig_fsr; + _FPU_GETCW (orig_fsr); +# if FE_INEXACT == 32 /* glibc compatible FE_* values */ + fsr = orig_fsr | (exceptions << 18); +# else /* Solaris compatible FE_* values */ + fsr = orig_fsr | (exceptions << 23); +# endif + if (fsr != orig_fsr) + _FPU_SETCW (fsr); + +# if FE_INEXACT == 32 /* glibc compatible FE_* values */ + return FE_ALL_EXCEPT & (orig_fsr >> 18); +# else /* Solaris compatible FE_* values */ + return FE_ALL_EXCEPT & (orig_fsr >> 23); +# endif +} + +int +fedisableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned long fsr, orig_fsr; + _FPU_GETCW (orig_fsr); +# if FE_INEXACT == 32 /* glibc compatible FE_* values */ + fsr = orig_fsr & ~(exceptions << 18); +# else /* Solaris compatible FE_* values */ + fsr = orig_fsr & ~(exceptions << 23); +# endif + if (fsr != orig_fsr) + _FPU_SETCW (fsr); + +# if FE_INEXACT == 32 /* glibc compatible FE_* values */ + return FE_ALL_EXCEPT & (orig_fsr >> 18); +# else /* Solaris compatible FE_* values */ + return FE_ALL_EXCEPT & (orig_fsr >> 23); +# endif +} + +int +fegetexcept (void) +{ + unsigned long fsr; + _FPU_GETCW (fsr); +# if FE_INEXACT == 32 /* glibc compatible FE_* values */ + return FE_ALL_EXCEPT & (fsr >> 18); +# else /* Solaris compatible FE_* values */ + return FE_ALL_EXCEPT & (fsr >> 23); +# endif +} + +# 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 HAVE_FPSETMASK +/* FreeBSD ⥠3.1, NetBSD ⥠1.1, OpenBSD, IRIX, Solaris, Minix ⥠3.2. */ + +/* Get fpgetmask, fpsetmask. */ +# include <ieeefp.h> + +/* The type is called 'fp_except_t' on FreeBSD, but 'fp_except' on + all other systems. */ +# if !defined __FreeBSD__ +# define fp_except_t fp_except +# endif + +static fp_except_t +exceptions_to_mask (int exceptions) +{ + fp_except_t m = 0; + if (exceptions & FE_INVALID) + m |= FP_X_INV; + if (exceptions & FE_DIVBYZERO) + m |= FP_X_DZ; + if (exceptions & FE_OVERFLOW) + m |= FP_X_OFL; + if (exceptions & FE_UNDERFLOW) + m |= FP_X_UFL; + if (exceptions & FE_INEXACT) + m |= FP_X_IMP; + return m; +} + +static int +mask_to_exceptions (fp_except_t m) +{ + int exceptions = 0; + if (m & FP_X_INV) + exceptions |= FE_INVALID; + if (m & FP_X_DZ) + exceptions |= FE_DIVBYZERO; + if (m & FP_X_OFL) + exceptions |= FE_OVERFLOW; + if (m & FP_X_UFL) + exceptions |= FE_UNDERFLOW; + if (m & FP_X_IMP) + exceptions |= FE_INEXACT; + return exceptions; +} + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + fp_except_t trapbits = fpgetmask (); + fpsetmask (trapbits | exceptions_to_mask (exceptions)); + return mask_to_exceptions (trapbits); +} + +int +fedisableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + fp_except_t trapbits = fpgetmask (); + fpsetmask (trapbits & ~ exceptions_to_mask (exceptions)); + return mask_to_exceptions (trapbits); +} + +int +fegetexcept (void) +{ + fp_except_t trapbits = fpgetmask (); + return mask_to_exceptions (trapbits); +} + +# elif defined _AIX && defined __powerpc__ /* AIX */ + +# include <fptrap.h> +/* Documentation: + <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-any-enable-fp-is-enabled-fp-enable-all-fp-enable-fp-disable-all-fp-disable-subroutine> + <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-trap-subroutine> */ + +/* Convert from an 'int exceptions' to an fptrap_t. */ +# if 0 /* Unoptimized */ +# define exceptions_to_fptrap(exceptions) \ + ( ((exceptions) & FE_INVALID ? TRP_INVALID : 0) \ + | ((exceptions) & FE_DIVBYZERO ? TRP_DIV_BY_ZERO : 0) \ + | ((exceptions) & FE_OVERFLOW ? TRP_OVERFLOW : 0) \ + | ((exceptions) & FE_UNDERFLOW ? TRP_UNDERFLOW : 0) \ + | ((exceptions) & FE_INEXACT ? TRP_INEXACT : 0)) +# else /* Optimized */ +# define exceptions_to_fptrap(exceptions) \ + (((unsigned int) (exceptions) & FE_ALL_EXCEPT) >> 22) +# endif + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + int prev_enabled = fegetexcept (); + if (exceptions != 0) + { + fp_enable (exceptions_to_fptrap (exceptions)); + if (prev_enabled == 0 /* && fegetexcept () != 0 */) + /* Enable precise trapping mode. */ + fp_trap (FP_TRAP_SYNC); + } + return prev_enabled; +} + +int +fedisableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + int prev_enabled = fegetexcept (); + if (exceptions != 0) + { + fp_disable (exceptions_to_fptrap (exceptions)); + if (prev_enabled != 0 && fegetexcept () == 0) + /* Disable precise trapping mode. */ + fp_trap (FP_TRAP_OFF); + } + return prev_enabled; +} + +int +fegetexcept (void) +{ + return fegetexcept_impl (); +} + +# elif HAVE_FESETTRAPENABLE +/* HP-UX, QNX */ + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + int prev_enabled = fegettrapenable (); + if (exceptions != 0) + fesettrapenable (prev_enabled | exceptions); + return prev_enabled; +} + +int +fedisableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + int prev_enabled = fegettrapenable (); + if (exceptions != 0) + fesettrapenable (prev_enabled & ~exceptions); + return prev_enabled; +} + +int +fegetexcept (void) +{ + return fegettrapenable (); +} + +# else + +# define NEED_FALLBACK 1 + +# endif + +#endif + +#if NEED_FALLBACK + +/* A dummy fallback. */ + +int +feenableexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + if (exceptions != 0) + return -1; + return 0; +} + +int +fedisableexcept (int exceptions) +{ + return 0; +} + +int +fegetexcept (void) +{ + return 0; +} + +#endif diff --git a/lib/fenv.in.h b/lib/fenv.in.h index 2b2f770e2c..4fd5ea2f1a 100644 --- a/lib/fenv.in.h +++ b/lib/fenv.in.h @@ -542,6 +542,100 @@ _GL_WARN_ON_USE (fesetexcept, "fesetexcept is unportable - " #endif +/* GNU extensions. */ + +#if @GNULIB_FEENABLEEXCEPT@ +/* Enables trapping for the floating-point exceptions denoted by the bit mask + EXCEPTIONS. + Returns the bit mask of floating-point exceptions for which trapping was + enabled before the call, or -1 upon failure. + Note: This function is a misnomer. It does not enable the specified + floating-point exceptions; it enables *trapping* on them. It should better + be called 'feenabletraps'. */ +# if @REPLACE_FEENABLEEXCEPT@ || (!@HAVE_FEENABLEEXCEPT@ && defined __FreeBSD__) /* has an inline definition */ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef feenableexcept +# define feenableexcept rpl_feenableexcept +# endif +_GL_FUNCDECL_RPL (feenableexcept, int, (int exceptions)); +_GL_CXXALIAS_RPL (feenableexcept, int, (int exceptions)); +# else +# if !@HAVE_FEENABLEEXCEPT@ +_GL_FUNCDECL_SYS (feenableexcept, int, (int exceptions)); +# endif +_GL_CXXALIAS_SYS (feenableexcept, int, (int exceptions)); +# endif +_GL_CXXALIASWARN (feenableexcept); +#elif defined GNULIB_POSIXCHECK +# undef feenableexcept +# if HAVE_RAW_DECL_FEENABLEEXCEPT +_GL_WARN_ON_USE (feenableexcept, "feenableexcept is unportable - " + "use gnulib module fenv-exceptions-trapping for portability"); +# endif +#endif + +#if @GNULIB_FEDISABLEEXCEPT@ +/* Disables trapping for the floating-point exceptions denoted by the bit mask + EXCEPTIONS. + Returns the bit mask of floating-point exceptions for which trapping was + enabled before the call, or -1 upon failure. + Note: This function is a misnomer. It does not disable the specified + floating-point exceptions; it disables *trapping* on them. It should better + be called 'fedisabletraps'. */ +# if @REPLACE_FEDISABLEEXCEPT@ || (!@HAVE_FEDISABLEEXCEPT@ && defined __FreeBSD__) /* has an inline definition */ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef fedisableexcept +# define fedisableexcept rpl_fedisableexcept +# endif +_GL_FUNCDECL_RPL (fedisableexcept, int, (int exceptions)); +_GL_CXXALIAS_RPL (fedisableexcept, int, (int exceptions)); +# else +# if !@HAVE_FEDISABLEEXCEPT@ +_GL_FUNCDECL_SYS (fedisableexcept, int, (int exceptions)); +# endif +_GL_CXXALIAS_SYS (fedisableexcept, int, (int exceptions)); +# endif +_GL_CXXALIASWARN (fedisableexcept); +#elif defined GNULIB_POSIXCHECK +# undef fedisableexcept +# if HAVE_RAW_DECL_FEDISABLEEXCEPT +_GL_WARN_ON_USE (fedisableexcept, "fedisableexcept is unportable - " + "use gnulib module fenv-exceptions-trapping for portability"); +# endif +#endif + +#if @GNULIB_FEGETEXCEPT@ +/* Returns the bit mask of floating-point exceptions for which trapping is + enabled. + Note: This function is an even bigger misnomer: + - It does not test for the specified floating-point exceptions; + fetestexcept() does that. It tests whether *trapping* on these + exceptions is enabled. + - It is in no way the opposite of fesetexcept(). + It should better be called 'fegettraps'. */ +# if @REPLACE_FEGETEXCEPT@ || (!@HAVE_FEGETEXCEPT@ && defined __FreeBSD__) /* has an inline definition */ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef fegetexcept +# define fegetexcept rpl_fegetexcept +# endif +_GL_FUNCDECL_RPL (fegetexcept, int, (void)); +_GL_CXXALIAS_RPL (fegetexcept, int, (void)); +# else +# if !@HAVE_FEGETEXCEPT@ +_GL_FUNCDECL_SYS (fegetexcept, int, (void)); +# endif +_GL_CXXALIAS_SYS (fegetexcept, int, (void)); +# endif +_GL_CXXALIASWARN (fegetexcept); +#elif defined GNULIB_POSIXCHECK +# undef fegetexcept +# if HAVE_RAW_DECL_FEGETEXCEPT +_GL_WARN_ON_USE (fegetexcept, "fegetexcept is unportable - " + "use gnulib module fenv-exceptions-trapping for portability"); +# endif +#endif + + /* ISO C 99 § 7.6.2 Floating-point exceptions ISO C 23 § 7.6.4 Floating-point exceptions API with fexcept_t. diff --git a/m4/fenv-exceptions-trapping.m4 b/m4/fenv-exceptions-trapping.m4 new file mode 100644 index 0000000000..f85e356a88 --- /dev/null +++ b/m4/fenv-exceptions-trapping.m4 @@ -0,0 +1,117 @@ +# fenv-exceptions-trapping.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_ONCE([gl_FENV_EXCEPTIONS_TRAPPING], +[ + AC_REQUIRE([gl_FENV_H_DEFAULTS]) + dnl Persuade glibc <fenv.h> to declare feenableexcept(). + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + AC_REQUIRE([AC_CANONICAL_HOST]) + + gl_MATHFUNC([feenableexcept], [int], [(int)], [#include <fenv.h>]) + if test $gl_cv_func_feenableexcept_no_libm = yes \ + || test $gl_cv_func_feenableexcept_in_libm = yes; then + dnl On glibc 2.19/aarch64, feenableexcept does not detect failures. + dnl Fixed through + dnl <https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=302949e2940a9da3f6364a1574619e621b7e1e71>. + dnl Similarly on FreeBSD 12.2/arm and FreeBSD 12.2/arm64. + case "$host" in + aarch64*-*-linux*) + AC_CACHE_CHECK([whether feenableexcept works], + [gl_cv_func_feenableexcept_works], + [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_feenableexcept_works="guessing yes"], + [gl_cv_func_feenableexcept_works="guessing no"]) + ]) + case "$gl_cv_func_feenableexcept_works" in + *yes) ;; + *) REPLACE_FEENABLEEXCEPT=1 ;; + esac + ;; + arm*-*-freebsd* | aarch64*-*-freebsd*) + REPLACE_FEENABLEEXCEPT=1 + ;; + esac + if test $REPLACE_FEENABLEEXCEPT = 1; then + dnl Two more functions are defined in the same compilation unit. + dnl Override them as well. + REPLACE_FEDISABLEEXCEPT=1 + REPLACE_FEGETEXCEPT=1 + FENV_EXCEPTIONS_TRAPPING_LIBM= + else + dnl It needs linking with -lm on glibc. + if test $gl_cv_func_feenableexcept_no_libm = yes; then + FENV_EXCEPTIONS_TRAPPING_LIBM= + else + FENV_EXCEPTIONS_TRAPPING_LIBM=-lm + fi + fi + else + HAVE_FEENABLEEXCEPT=0 + HAVE_FEDISABLEEXCEPT=0 + HAVE_FEGETEXCEPT=0 + case "$host" in + alpha*-*-linux*) + dnl For feenableexcept, which we take from libm. + FENV_EXCEPTIONS_TRAPPING_LIBM=-lm + ;; + *) + FENV_EXCEPTIONS_TRAPPING_LIBM= + ;; + esac + fi + if test $HAVE_FEENABLEEXCEPT = 0 || test $REPLACE_FEENABLEEXCEPT = 1; then + gl_PREREQ_FENV_EXCEPTIONS_TRAPPING + dnl Possibly need -lm for fpgetmask(), fpsetmask(). + if test $gl_cv_func_fpsetmask_no_libm = no \ + && test $gl_cv_func_fpsetmask_in_libm = yes \ + && test -z "$FENV_EXCEPTIONS_TRAPPING_LIBM"; then + FENV_EXCEPTIONS_TRAPPING_LIBM=-lm + fi + dnl Possibly need -lm for fegettrapenable(), fesettrapenable(). + if test $gl_cv_func_fesettrapenable_no_libm = no \ + && test $gl_cv_func_fesettrapenable_in_libm = yes \ + && test -z "$FENV_EXCEPTIONS_TRAPPING_LIBM"; then + FENV_EXCEPTIONS_TRAPPING_LIBM=-lm + fi + fi + AC_SUBST([FENV_EXCEPTIONS_TRAPPING_LIBM]) +]) + +dnl Prerequisites of lib/fenv-except-trapping.c. +AC_DEFUN([gl_PREREQ_FENV_EXCEPTIONS_TRAPPING], +[ + gl_MATHFUNC([fpsetmask], [fp_except_t], [(fp_except_t)], + [#include <ieeefp.h> + /* The type is called 'fp_except_t' on FreeBSD, but 'fp_except' on + all other systems. */ + #if !defined __FreeBSD__ + #define fp_except_t fp_except + #endif + ]) + if test $gl_cv_func_fpsetmask_no_libm = yes \ + || test $gl_cv_func_fpsetmask_in_libm = yes; then + AC_DEFINE([HAVE_FPSETMASK], [1], + [Define to 1 if you have the 'fpsetmask' function.]) + fi + + gl_MATHFUNC([fesettrapenable], [int], [(int)], [#include <fenv.h>]) + if test $gl_cv_func_fesettrapenable_no_libm = yes \ + || test $gl_cv_func_fesettrapenable_in_libm = yes; then + AC_DEFINE([HAVE_FESETTRAPENABLE], [1], + [Define to 1 if you have the 'fesettrapenable' function.]) + fi +]) diff --git a/m4/fenv_h.m4 b/m4/fenv_h.m4 index 9e7c809d06..853f66e5a9 100644 --- a/m4/fenv_h.m4 +++ b/m4/fenv_h.m4 @@ -22,7 +22,8 @@ AC_DEFUN_ONCE([gl_FENV_H], dnl corresponding gnulib module is not in use, and which is not dnl guaranteed by C99. gl_WARN_ON_USE_PREPARE([[#include <fenv.h> - ]], [fedisableexcept feenableexcept fesetexcept fetestexceptflag]) + ]], [fedisableexcept feenableexcept fegetexcept fesetexcept + fetestexceptflag]) ]) # gl_FENV_MODULE_INDICATOR([modulename]) diff --git a/modules/fenv-exceptions-trapping b/modules/fenv-exceptions-trapping new file mode 100644 index 0000000000..7ce55fcbd6 --- /dev/null +++ b/modules/fenv-exceptions-trapping @@ -0,0 +1,40 @@ +Description: +Functions for turning floating-point exceptions into traps (signals): +feenableexcept, fedisableexcept, fegetexcept. + +Files: +lib/fenv-except-trapping.c +lib/fenv-private.h +m4/fenv-exceptions-trapping.m4 +m4/mathfunc.m4 +m4/musl.m4 + +Depends-on: +fenv +extensions + +configure.ac: +gl_FENV_EXCEPTIONS_TRAPPING +gl_CONDITIONAL([GL_COND_OBJ_FENV_EXCEPTIONS_TRAPPING], + [test $HAVE_FEENABLEEXCEPT = 0 || test $REPLACE_FEENABLEEXCEPT = 1]) +gl_FENV_MODULE_INDICATOR([feenableexcept]) +gl_FENV_MODULE_INDICATOR([fedisableexcept]) +gl_FENV_MODULE_INDICATOR([fegetexcept]) +gl_MODULE_INDICATOR([feenableexcept]) + +Makefile.am: +if GL_COND_OBJ_FENV_EXCEPTIONS_TRAPPING +lib_SOURCES += fenv-except-trapping.c +endif + +Include: +#include <fenv.h> + +Link: +$(FENV_EXCEPTIONS_TRAPPING_LIBM) + +License: +LGPLv2+ + +Maintainer: +all