From ee6460d256b0ba048ef5ecf39f028b10c9986b12 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Mon, 30 Oct 2023 16:39:19 +0100 Subject: [PATCH] fenv-exceptions-state-c99: New module. * lib/fenv.in.h (fegetexceptflag, fesetexceptflag): New declarations. * lib/fenv-except-state-get.c: New file, baed on glibc. * lib/fenv-except-state-set.c: New file, baed on glibc. * m4/mathfunc.m4 (gl_MATHFUNC): Handle also the 'fexcept_t *' type. * m4/fenv-exceptions-state.m4: New file. * modules/fenv-exceptions-state-c99: New file. * doc/posix-functions/fegetexceptflag.texi: Mention the new module. * doc/posix-functions/fesetexceptflag.texi: Mention the new module and the glibc, musl libc, macOS, AIX, mingw bugs. --- ChangeLog | 13 + doc/posix-functions/fegetexceptflag.texi | 8 +- doc/posix-functions/fesetexceptflag.texi | 23 +- lib/fenv-except-state-get.c | 353 ++++++++++++++ lib/fenv-except-state-set.c | 597 +++++++++++++++++++++++ lib/fenv.in.h | 49 +- m4/fenv-exceptions-state.m4 | 221 +++++++++ m4/mathfunc.m4 | 6 +- modules/fenv-exceptions-state-c99 | 43 ++ 9 files changed, 1302 insertions(+), 11 deletions(-) create mode 100644 lib/fenv-except-state-get.c create mode 100644 lib/fenv-except-state-set.c create mode 100644 m4/fenv-exceptions-state.m4 create mode 100644 modules/fenv-exceptions-state-c99 diff --git a/ChangeLog b/ChangeLog index 1fc40e4ae2..291e89ec60 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2023-10-30 Bruno Haible + + fenv-exceptions-state-c99: New module. + * lib/fenv.in.h (fegetexceptflag, fesetexceptflag): New declarations. + * lib/fenv-except-state-get.c: New file, baed on glibc. + * lib/fenv-except-state-set.c: New file, baed on glibc. + * m4/mathfunc.m4 (gl_MATHFUNC): Handle also the 'fexcept_t *' type. + * m4/fenv-exceptions-state.m4: New file. + * modules/fenv-exceptions-state-c99: New file. + * doc/posix-functions/fegetexceptflag.texi: Mention the new module. + * doc/posix-functions/fesetexceptflag.texi: Mention the new module and + the glibc, musl libc, macOS, AIX, mingw bugs. + 2023-10-30 Bruno Haible fenv-exceptions-tracking-{c99,c23}: Fix the x86_64 and i386 case. diff --git a/doc/posix-functions/fegetexceptflag.texi b/doc/posix-functions/fegetexceptflag.texi index 5b45fe8d26..58915e0530 100644 --- a/doc/posix-functions/fegetexceptflag.texi +++ b/doc/posix-functions/fegetexceptflag.texi @@ -4,15 +4,15 @@ POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/fegetexceptflag.html} -Gnulib module: --- +Gnulib module: fenv-exceptions-state-c99 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. @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/fesetexceptflag.texi b/doc/posix-functions/fesetexceptflag.texi index 5258d35099..66075be675 100644 --- a/doc/posix-functions/fesetexceptflag.texi +++ b/doc/posix-functions/fesetexceptflag.texi @@ -4,15 +4,30 @@ POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/fesetexceptflag.html} -Gnulib module: --- +Gnulib module: fenv-exceptions-state-c99 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 triggers floating-point exception traps although it shouldn't, on +@c https://sourceware.org/bugzilla/show_bug.cgi?id=30990 +glibc 2.37/i386, glibc 2.37/x86_64, +@c https://sourceware.org/bugzilla/show_bug.cgi?id=30988 +glibc 2.37/powerpc, +musl libc, Mac OS X 10.5, mingw. +@item +This function clears too many floating-point exception flags on +@c https://sourceware.org/bugzilla/show_bug.cgi?id=30998 +glibc 2.37/alpha. +@item +This function is also buggy on +@c Without the override, the unit test test-fenv-except-state-1 fails. +AIX 7.3.1. @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/lib/fenv-except-state-get.c b/lib/fenv-except-state-get.c new file mode 100644 index 0000000000..0e22e7f7c8 --- /dev/null +++ b/lib/fenv-except-state-get.c @@ -0,0 +1,353 @@ +/* Functions for saving the floating-point exception status flags. + 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//{fgetexcptflg.c,fsetexcptflg.c} + together with glibc/sysdeps//{fpu_control.h,fenv_private.h,fenv_libc.h}. */ + +#include + +/* Specification. */ +#include + +#include "fenv-private.h" + +#if defined __GNUC__ || defined __clang__ || defined _MSC_VER + +# if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86) + +/* On most OSes, fexcept_t is binary-equivalent to an 'unsigned short'. + On NetBSD, OpenBSD, Solaris, Cygwin, MSVC, Android/x86_64, Minix, fexcept_t + is equivalent to an 'unsigned int'. + A simple C cast does the necessary conversion. */ + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ +# if defined _MSC_VER + + unsigned int mxcsr; + _FPU_GETSSECW (mxcsr); + *saved_flags = x86hardware_to_exceptions (mxcsr) & FE_ALL_EXCEPT & exceptions; + +# else + + unsigned short fstat; + _FPU_GETSTAT (fstat); + + unsigned int mxcsr = 0; + if (CPU_HAS_SSE ()) + { + /* Look at the flags in the SSE unit as well. */ + _FPU_GETSSECW (mxcsr); + } + + *saved_flags = x86hardware_to_exceptions (fstat | mxcsr) + & FE_ALL_EXCEPT & exceptions; +# endif + + return 0; +} + +# elif defined __aarch64__ /* arm64 */ + +/* On Linux, NetBSD, and Android, fexcept_t is binary-equivalent to + an 'unsigned int'. + On macOS, fexcept_t is binary-equivalent to an 'unsigned short'. + On FreeBSD and OpenBSD, fexcept_t is binary-equivalent to an 'unsigned long'. + A simple C cast does the necessary conversion. */ + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ + unsigned long fpsr; + _FPU_GETFPSR (fpsr); + *saved_flags = fpsr & FE_ALL_EXCEPT & exceptions; + return 0; +} + +# elif defined __arm__ + +/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */ + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ +# ifdef __SOFTFP__ + return -1; +# else + unsigned int fpscr; + _FPU_GETCW (fpscr); + *saved_flags = fpscr & FE_ALL_EXCEPT & exceptions; + return 0; +# endif +} + +# elif defined __alpha + +/* On all OSes except NetBSD and OpenBSD, fexcept_t is binary-equivalent to + an 'unsigned long'. + On NetBSD, it is equivalent to an 'unsigned short'. + On OpenBSD, it is equivalent to an 'unsigned int'. + A simple C cast does the necessary conversion. */ + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ + unsigned long swcr = __ieee_get_fp_control (); + *saved_flags = swcr & FE_ALL_EXCEPT & exceptions; + return 0; +} + +# elif defined __hppa + +/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */ + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ + 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"); + *saved_flags = (s.halfreg[0] >> 27) & FE_ALL_EXCEPT & exceptions; + return 0; +} + +# elif defined __ia64__ + +/* On all OSes except NetBSD, fexcept_t is binary-equivalent to + an 'unsigned long'. + On NetBSD, it is equivalent to an 'unsigned short'. + A simple C cast does the necessary conversion. */ + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ + unsigned long fpsr; + _FPU_GETCW (fpsr); + *saved_flags = (fpsr >> 13) & FE_ALL_EXCEPT & exceptions; + return 0; +} + +# elif defined __m68k__ + +/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */ + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ + unsigned int fpsr; + _FPU_GETFPSR (fpsr); + *saved_flags = fpsr & FE_ALL_EXCEPT & exceptions; + return 0; +} + +# elif defined __mips__ + +/* On all OSes except NetBSD and OpenBSD, fexcept_t is binary-equivalent to + an 'unsigned short'. + On NetBSD and OpenBSD, it is equivalent to an 'unsigned int'. + A simple C cast does the necessary conversion. */ + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ + unsigned int fcsr; + _FPU_GETCW (fcsr); + *saved_flags = fcsr & FE_ALL_EXCEPT & exceptions; + return 0; +} + +# elif defined __loongarch__ + +/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */ + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ + unsigned int fcsr; + _FPU_GETCW (fcsr); + *saved_flags = fcsr & FE_ALL_EXCEPT & exceptions; + return 0; +} + +# elif defined __powerpc__ + +/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */ + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ + union { unsigned long long u; double f; } memenv; + _FPU_GETCW_AS_DOUBLE (memenv.f); + *saved_flags = memenv.u & FE_ALL_EXCEPT & exceptions; + return 0; +} + +# elif defined __riscv + +/* On all OSes except FreeBSD, fexcept_t is binary-equivalent to + an 'unsigned int'. + On FreeBSD, it is equivalent to an 'unsigned long'. + A simple C cast does the necessary conversion. */ + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ + unsigned int flags; + __asm__ __volatile__ ("frflags %0" : "=r" (flags)); /* same as "csrr %0, fflags" */ + *saved_flags = flags & FE_ALL_EXCEPT & exceptions; + return 0; +} + +# elif defined __s390__ || defined __s390x__ + +/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */ + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ + unsigned int fpc; + _FPU_GETCW (fpc); +# if FE_INEXACT == 8 /* glibc compatible FE_* values */ + *saved_flags = ((fpc >> 16) | ((fpc & 0x00000300) == 0 ? fpc >> 8 : 0)) + & FE_ALL_EXCEPT & exceptions; +# else /* musl libc compatible FE_* values */ + *saved_flags = (fpc | ((fpc & 0x00000300) == 0 ? fpc << 8 : 0)) + & FE_ALL_EXCEPT & exceptions; +# endif + return 0; +} + +# elif defined __sh__ + +/* On glibc, fexcept_t is binary-equivalent to an 'unsigned short'. + On all other OSes, fexcept_t is binary-equivalent to an 'unsigned int'. + A simple C cast does the necessary conversion. */ + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ + unsigned int fpscr; + _FPU_GETCW (fpscr); + *saved_flags = fpscr & FE_ALL_EXCEPT & exceptions; + return 0; +} + +# elif defined __sparc + +/* On all OSes except Solaris, fexcept_t is binary-equivalent to an 'unsigned long'. + On Solaris, fexcept_t is an 'int'. + A simple C cast does the necessary conversion. */ + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ + unsigned long fsr; + _FPU_GETCW (fsr); +# if FE_INEXACT == 32 /* glibc compatible FE_* values */ + *saved_flags = fsr & FE_ALL_EXCEPT & exceptions; +# else /* Solaris compatible FE_* values */ + *saved_flags = (fsr >> 5) & FE_ALL_EXCEPT & exceptions; +# endif + 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 HAVE_FPSETSTICKY +/* FreeBSD ≥ 3.1, NetBSD ≥ 1.1, OpenBSD, IRIX, Solaris, Minix ≥ 3.2. */ + +/* Get fpgetsticky, fpsetsticky. */ +# include +/* 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 + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ + fp_except_t flags = fpgetsticky (); + *saved_flags = flags & FE_ALL_EXCEPT & exceptions; + return 0; +} + +# elif defined _AIX && defined __powerpc__ /* AIX */ + +# include +# include + +/* Documentation: + */ + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ + fpflag_t flags = fp_read_flag (); + *saved_flags = fpflag_to_exceptions (flags) & FE_ALL_EXCEPT & exceptions; + return 0; +} + +# else + +# define NEED_FALLBACK 1 + +# endif + +#endif + +#if NEED_FALLBACK + +/* A dummy fallback. */ + +int +fegetexceptflag (fexcept_t *saved_flags, int exceptions) +{ + /* Just like fetestexcept. */ + *saved_flags = 0; + return 0; +} + +#endif diff --git a/lib/fenv-except-state-set.c b/lib/fenv-except-state-set.c new file mode 100644 index 0000000000..6ba4d63a7d --- /dev/null +++ b/lib/fenv-except-state-set.c @@ -0,0 +1,597 @@ +/* Functions for saving the floating-point exception status flags. + 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//{fgetexcptflg.c,fsetexcptflg.c} + together with glibc/sysdeps//{fpu_control.h,fenv_private.h,fenv_libc.h}. */ + +#include + +/* Specification. */ +#include + +#include "fenv-private.h" + +#if defined __GNUC__ || defined __clang__ || defined _MSC_VER + +# if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86) + +/* On most OSes, fexcept_t is binary-equivalent to an 'unsigned short'. + On NetBSD, OpenBSD, Solaris, Cygwin, MSVC, Android/x86_64, Minix, fexcept_t + is equivalent to an 'unsigned int'. + A simple C cast does the necessary conversion. */ + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned int desired_flags = (unsigned int) *saved_flags; + +# if defined _MSC_VER + exceptions = exceptions_to_x86hardware (exceptions); + desired_flags = exceptions_to_x86hardware (desired_flags); + + /* Modify the flags in the SSE unit. */ + unsigned int mxcsr, orig_mxcsr; + _FPU_GETSSECW (orig_mxcsr); + mxcsr = orig_mxcsr ^ ((orig_mxcsr ^ desired_flags) & exceptions); + if (mxcsr != orig_mxcsr) + _FPU_SETSSECW (mxcsr); + +# else + + /* The flags can be set in the 387 unit or in the SSE unit. + When we need to clear a flag, we need to do so in both units, + due to the way fetestexcept() is implemented. + When we need to set a flag, it is sufficient to do it in the SSE unit, + because that is guaranteed to not trap. However, on i386 CPUs that have + only a 387 unit, set the flags in the 387, as long as this cannot trap. */ + + if (CPU_HAS_SSE ()) + { + /* Modify the flags in the SSE unit. */ + unsigned int mxcsr, orig_mxcsr; + _FPU_GETSSECW (orig_mxcsr); + mxcsr = orig_mxcsr ^ ((orig_mxcsr ^ desired_flags) & exceptions); + if (mxcsr != orig_mxcsr) + _FPU_SETSSECW (mxcsr); + + /* Modify the flags in the 387 unit, but only by clearing bits, not by + setting bits. */ + x86_387_fenv_t env; + unsigned short orig_status_word; + __asm__ __volatile__ ("fnstenv %0" : "=m" (*&env)); + orig_status_word = env.__status_word; + env.__status_word &= ~ (exceptions & ~desired_flags); + if (env.__status_word != orig_status_word) + __asm__ __volatile__ ("fldenv %0" : : "m" (*&env)); + } +# if !(defined __x86_64__ || defined _M_X64) + else + { + /* Modify the flags in the 387 unit. */ + x86_387_fenv_t env; + unsigned short orig_status_word; + __asm__ __volatile__ ("fnstenv %0" : "=m" (*&env)); + orig_status_word = env.__status_word; + env.__status_word ^= ((env.__status_word ^ desired_flags) & exceptions); + if (env.__status_word != orig_status_word) + { + if ((~env.__control_word) & env.__status_word & exceptions) + { + /* Setting the exception flags may trigger a trap (at the next + floating-point instruction, but that does not matter). + ISO C 23 § 7.6.4.5 does not allow it. */ + return -1; + } + __asm__ __volatile__ ("fldenv %0" : : "m" (*&env)); + } + } +# endif +# endif + return 0; +} + +# elif defined __aarch64__ /* arm64 */ + +/* On Linux, NetBSD, and Android, fexcept_t is binary-equivalent to + an 'unsigned int'. + On macOS, fexcept_t is binary-equivalent to an 'unsigned short'. + On FreeBSD and OpenBSD, fexcept_t is binary-equivalent to an 'unsigned long'. + A simple C cast does the necessary conversion. */ + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ + unsigned long desired_flags = (unsigned long) *saved_flags; + unsigned long fpsr, orig_fpsr; + _FPU_GETFPSR (orig_fpsr); + fpsr = orig_fpsr ^ ((orig_fpsr ^ desired_flags) & FE_ALL_EXCEPT & exceptions); + if (fpsr != orig_fpsr) + _FPU_SETFPSR (fpsr); + /* Possibly some new exception flags have been set. But just storing them + does not cause a trap to be executed (when traps are enabled). */ + return 0; +} + +# elif defined __arm__ + +/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */ + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ +# ifdef __SOFTFP__ + if (exceptions != 0) + return -1; +# else + unsigned int desired_flags = (unsigned int) *saved_flags; + unsigned int fpscr, orig_fpscr; + _FPU_GETCW (orig_fpscr); + fpscr = orig_fpscr ^ ((orig_fpscr ^ desired_flags) & FE_ALL_EXCEPT & exceptions); + if (fpscr != orig_fpscr) + _FPU_SETCW (fpscr); +# endif + return 0; +} + +# elif defined __alpha + +/* On all OSes except NetBSD and OpenBSD, fexcept_t is binary-equivalent to + an 'unsigned long'. + On NetBSD, it is equivalent to an 'unsigned short'. + On OpenBSD, it is equivalent to an 'unsigned int'. + A simple C cast does the necessary conversion. */ + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ + unsigned long desired_flags = (unsigned long) *saved_flags; + unsigned long swcr, orig_swcr; + orig_swcr = __ieee_get_fp_control (); + swcr = orig_swcr ^ ((orig_swcr ^ desired_flags) & FE_ALL_EXCEPT & exceptions); + if (swcr != orig_swcr) + __ieee_set_fp_control (swcr); + /* Possibly some new exception flags have been set. But just storing them + does not cause a trap to be executed (when traps are enabled). */ + return 0; +} + +# elif defined __hppa + +/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */ + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ + unsigned int desired_flags = (unsigned int) *saved_flags; + 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]; + /* Modify all the relevant bits. */ + s.halfreg[0] ^= (s.halfreg[0] ^ (desired_flags << 27)) & ((FE_ALL_EXCEPT & exceptions) << 27); + if (s.halfreg[0] != old_halfreg0) + { + /* Store the new status word. */ + __asm__ __volatile__ ("fldd 0(%0),%%fr0" : : "r" (&s.fpreg), "m" (s.fpreg) : "%r0"); + } + /* Possibly some new exception flags have been set. But just storing them + does not cause a trap to be executed (when traps are enabled). */ + return 0; +} + +# elif defined __ia64__ + +/* On all OSes except NetBSD, fexcept_t is binary-equivalent to + an 'unsigned long'. + On NetBSD, it is equivalent to an 'unsigned short'. + A simple C cast does the necessary conversion. */ + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ + unsigned long desired_flags = (unsigned long) *saved_flags; + unsigned long fpsr, orig_fpsr; + _FPU_GETCW (orig_fpsr); + fpsr = orig_fpsr ^ ((orig_fpsr ^ (desired_flags << 13)) & ((FE_ALL_EXCEPT & exceptions) << 13)); + if (fpsr != orig_fpsr) + _FPU_SETCW (fpsr); + /* Possibly some new exception flags have been set. But just storing them + does not cause a trap to be executed (when traps are enabled). */ + return 0; +} + +# elif defined __m68k__ + +/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */ + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ + unsigned int desired_flags = (unsigned int) *saved_flags; + unsigned int fpsr, orig_fpsr; + _FPU_GETFPSR (orig_fpsr); + fpsr = orig_fpsr ^ ((orig_fpsr ^ desired_flags) & FE_ALL_EXCEPT & exceptions); + if (fpsr != orig_fpsr) + _FPU_SETFPSR (fpsr); + /* Possibly some new exception flags have been set. But just storing them + does not cause a trap to be executed (when traps are enabled). */ + return 0; +} + +# elif defined __mips__ + +/* On all OSes except NetBSD and OpenBSD, fexcept_t is binary-equivalent to + an 'unsigned short'. + On NetBSD and OpenBSD, it is equivalent to an 'unsigned int'. + A simple C cast does the necessary conversion. */ + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ + unsigned int desired_flags = (unsigned int) *saved_flags; + unsigned int fcsr, orig_fcsr; + _FPU_GETCW (orig_fcsr); + fcsr = orig_fcsr ^ ((orig_fcsr ^ desired_flags) & FE_ALL_EXCEPT & exceptions); + if (fcsr != orig_fcsr) + _FPU_SETCW (fcsr); + return 0; +} + +# elif defined __loongarch__ + +/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */ + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ + unsigned int desired_flags = (unsigned int) *saved_flags; + unsigned int fcsr, orig_fcsr; + _FPU_GETCW (orig_fcsr); + fcsr = orig_fcsr ^ ((orig_fcsr ^ desired_flags) & FE_ALL_EXCEPT & exceptions); + if (fcsr != orig_fcsr) + _FPU_SETCW (fcsr); + /* Possibly some new exception flags have been set. But just storing them + does not cause a trap to be executed (when traps are enabled). */ + return 0; +} + +# elif defined __powerpc__ + +/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */ + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ + /* The hardware does not support setting an exception flag without triggering + a trap, except through the "Ignore Exceptions Mode", bits FE0 and FE1 of + the MSR register set to zero, that can be obtained through a system call: + - On Linux and NetBSD: prctl (PR_SET_FPEXC, PR_FP_EXC_DISABLED); + - On AIX: fp_trap (FP_TRAP_OFF); + But that is not what we need here, as it would have a persistent effect on + the thread. */ + exceptions &= FE_ALL_EXCEPT; + + unsigned int desired_flags = (unsigned int) *saved_flags; + desired_flags &= exceptions; + + union { unsigned long long u; double f; } memenv, orig_memenv; + _FPU_GETCW_AS_DOUBLE (memenv.f); + orig_memenv = memenv; + + /* Instead of clearing FE_INVALID (= bit 29), we need to clear the + individual bits. */ + memenv.u &= ~ (exceptions & FE_INVALID + ? (exceptions & ~FE_INVALID) | 0x01F80700U + : exceptions); + /* Instead of setting FE_INVALID (= bit 29), we need to set one of the + individual bits: bit 10 or, if that does not work, bit 24. */ + memenv.u |= (desired_flags & FE_INVALID + ? (desired_flags & ~FE_INVALID) | (1U << 10) + : desired_flags); + + if (!(memenv.u == orig_memenv.u)) + { + if (memenv.u & (exceptions >> 22)) + { + /* Setting the exception flags may trigger a trap. + ISO C 23 § 7.6.4.5 does not allow it. */ + return -1; + } + _FPU_SETCW_AS_DOUBLE (memenv.f); + if (desired_flags & FE_INVALID) + { + /* Did it work? */ + _FPU_GETCW_AS_DOUBLE (memenv.f); + if ((memenv.u & FE_INVALID) == 0) + { + memenv.u |= (1U << 24); + _FPU_SETCW_AS_DOUBLE (memenv.f); + } + } + } + + return 0; +} + +# elif defined __riscv + +/* On all OSes except FreeBSD, fexcept_t is binary-equivalent to + an 'unsigned int'. + On FreeBSD, it is equivalent to an 'unsigned long'. + A simple C cast does the necessary conversion. */ + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ +# if 1 /* both should be equivalent */ + exceptions &= FE_ALL_EXCEPT; + + unsigned int desired_flags = (unsigned int) *saved_flags; + desired_flags &= exceptions; + + __asm__ __volatile__ ("csrc fflags, %0" : : "r" (exceptions)); + __asm__ __volatile__ ("csrs fflags, %0" : : "r" (desired_flags)); +# else + unsigned int desired_flags = (unsigned int) *saved_flags; + unsigned int fcsr, orig_fcsr; + __asm__ __volatile__ ("frflags %0" : "=r" (orig_fcsr)); /* same as "csrr %0, fflags" */ + fcsr = orig_fcsr ^ ((orig_fcsr ^ desired_flags) & FE_ALL_EXCEPT & exceptions); + if (fcsr != orig_fcsr) + __asm__ __volatile__ ("fsflags %0" : : "r" (fcsr)); /* same as "csrw fflags, %0" */ +# endif + + return 0; +} + +# elif defined __s390__ || defined __s390x__ + +/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */ + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ + unsigned int desired_flags = (unsigned int) *saved_flags; + unsigned int fpc, orig_fpc; + _FPU_GETCW (orig_fpc); +# if FE_INEXACT == 8 /* glibc compatible FE_* values */ + fpc = orig_fpc ^ ((orig_fpc ^ (desired_flags << 16)) & ((FE_ALL_EXCEPT & exceptions) << 16)); + if ((fpc & 0x00000300) == 0) + /* Clear the corresponding bits of the "data exception code". */ + fpc &= ~((FE_ALL_EXCEPT & exceptions) << 8); +# else /* musl libc compatible FE_* values */ + fpc = orig_fpc ^ ((orig_fpc ^ desired_flags) & FE_ALL_EXCEPT & exceptions); + if ((fpc & 0x00000300) == 0) + /* Clear the corresponding bits of the "data exception code". */ + fpc &= ~((FE_ALL_EXCEPT & exceptions) >> 8); +# endif + if (fpc != orig_fpc) + _FPU_SETCW (fpc); + /* Possibly some new exception flags have been set. But just storing them + does not cause a trap to be executed (when traps are enabled). */ + return 0; +} + +# elif defined __sh__ + +/* On glibc, fexcept_t is binary-equivalent to an 'unsigned short'. + On all other OSes, fexcept_t is binary-equivalent to an 'unsigned int'. + A simple C cast does the necessary conversion. */ + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ + unsigned int desired_flags = (unsigned int) *saved_flags; + unsigned int fpscr, orig_fpscr; + _FPU_GETCW (orig_fpscr); + fpscr = orig_fpscr ^ ((orig_fpscr ^ desired_flags) & FE_ALL_EXCEPT & exceptions); + if (fpscr != orig_fpscr) + _FPU_SETCW (fpscr); + /* Possibly some new exception flags have been set. But just storing them + does not cause a trap to be executed (when traps are enabled). */ + return 0; +} + +# elif defined __sparc + +/* On all OSes except Solaris, fexcept_t is binary-equivalent to an 'unsigned long'. + On Solaris, fexcept_t is an 'int'. + A simple C cast does the necessary conversion. */ + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ + unsigned long desired_flags = (unsigned long) *saved_flags; + unsigned long fsr, orig_fsr; + _FPU_GETCW (orig_fsr); +# if FE_INEXACT == 32 /* glibc compatible FE_* values */ + fsr = orig_fsr ^ ((orig_fsr ^ desired_flags) & FE_ALL_EXCEPT & exceptions); +# else /* Solaris compatible FE_* values */ + fsr = orig_fsr ^ ((orig_fsr ^ (desired_flags << 5)) & ((FE_ALL_EXCEPT & exceptions) << 5)); +# endif + if (fsr != orig_fsr) + _FPU_SETCW (fsr); + /* Possibly some new exception flags have been set. But just storing them + does not cause a trap to be executed (when traps are enabled). */ + 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. */ + +/* On these platforms, fpsetsticky cannot be used here, because it may generate + traps (since fpsetsticky calls _putsw, which modifies the control word of the + 387 unit). Instead, we need to modify only the flags in the SSE unit. */ + +/* Accessors for the mxcsr register. Fortunately, the Solaris cc supports a + poor form of 'asm'. */ + +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 +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + unsigned int desired_flags = (unsigned int) *saved_flags; + + /* Modify the flags in the SSE unit. */ + unsigned int mxcsr, orig_mxcsr; + getssecw (&orig_mxcsr); + mxcsr = orig_mxcsr ^ ((orig_mxcsr ^ desired_flags) & exceptions); + if (mxcsr != orig_mxcsr) + setssecw (&mxcsr); + + return 0; +} + +# elif HAVE_FPSETSTICKY +/* FreeBSD ≥ 3.1, NetBSD ≥ 1.1, OpenBSD, IRIX, Solaris, Minix ≥ 3.2. */ + +/* Get fpgetsticky, fpsetsticky. */ +# include +/* 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 + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ + unsigned long desired_flags = (unsigned long) *saved_flags; + fp_except_t flags, orig_flags; + orig_flags = fpgetsticky (); + flags = orig_flags ^ ((orig_fsr ^ desired_flags) & FE_ALL_EXCEPT & exceptions); + if (flags != orig_flags) + fpsetsticky (flags); + /* Possibly some new exception flags have been set. But just storing them + does not cause a trap to be executed (when traps are enabled). */ + return 0; +} + +# elif defined _AIX && defined __powerpc__ /* AIX */ + +# include +# include + +# include + +/* Documentation: + */ + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned int desired_flags = (unsigned int) *saved_flags; + int exceptions_to_clear = exceptions & ~desired_flags; + int exceptions_to_set = exceptions & desired_flags; + + fpflag_t orig_flags = fp_read_flag (); + /* In addition to clearing FE_INVALID (= bit 29), we also need to clear the + individual bits. */ + fpflag_t f_to_clear = + exceptions_to_fpflag (exceptions_to_clear) + | (exceptions_to_clear & FE_INVALID ? 0x01F80700U : 0); + /* Instead of setting FE_INVALID (= bit 29), we need to set one of the + individual bits: bit 10 or, if that does not work, bit 24. */ + fpflag_t f_to_set = + (exceptions_to_set & FE_INVALID + ? exceptions_to_fpflag (exceptions_to_set & ~FE_INVALID) | (1U << 10) + : exceptions_to_fpflag (exceptions_to_set)); + if (f_to_clear != 0) + fp_clr_flag (f_to_clear); + if (f_to_set != 0) + { + if ((fegetexcept_impl () & exceptions) != 0) + { + /* Setting the exception flags may trigger a trap. + ISO C 23 § 7.6.4.5 does not allow it. */ + return -1; + } + fp_set_flag (f_to_set); + if (exceptions & FE_INVALID) + { + /* Did it work? */ + if ((fp_read_flag () & FP_INVALID) == 0) + fp_set_flag (1U << 24); + } + } + + return 0; +} + +# else + +# define NEED_FALLBACK 1 + +# endif + +#endif + +#if NEED_FALLBACK + +/* A dummy fallback. */ + +int +fesetexceptflag (fexcept_t const *saved_flags, int exceptions) +{ + if (exceptions != 0) + return -1; + return 0; +} + +#endif diff --git a/lib/fenv.in.h b/lib/fenv.in.h index 0c119c5936..ad61e2c4c0 100644 --- a/lib/fenv.in.h +++ b/lib/fenv.in.h @@ -544,7 +544,8 @@ _GL_WARN_ON_USE (fesetexcept, "fesetexcept is unportable - " /* ISO C 99 § 7.6.2 Floating-point exceptions ISO C 23 § 7.6.4 Floating-point exceptions - API with fexcept_t */ + API with fexcept_t. + The return type changed from 'void' to 'int' in ISO C 11. */ #if !@HAVE_FENV_H@ @@ -610,6 +611,52 @@ typedef unsigned long fexcept_t; #endif +#if @GNULIB_FEGETEXCEPTFLAG@ +/* Stores the current floating-point exception status flags denoted + by EXCEPTIONS in *SAVED_FLAGS. */ +# if @REPLACE_FEGETEXCEPTFLAG@ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef fegetexceptflag +# define fegetexceptflag rpl_fegetexceptflag +# endif +_GL_FUNCDECL_RPL (fegetexceptflag, int, + (fexcept_t *saved_flags, int exceptions)); +_GL_CXXALIAS_RPL (fegetexceptflag, int, + (fexcept_t *saved_flags, int exceptions)); +# else +# if !@HAVE_FEGETEXCEPTFLAG@ +_GL_FUNCDECL_SYS (fegetexceptflag, int, + (fexcept_t *saved_flags, int exceptions)); +# endif +_GL_CXXALIAS_SYS (fegetexceptflag, int, + (fexcept_t *saved_flags, int exceptions)); +# endif +_GL_CXXALIASWARN (fegetexceptflag); +#endif + +#if @GNULIB_FESETEXCEPTFLAG@ +/* Copies the flags denoted by EXCEPTIONS from *SAVED_FLAGS to the + floating-point exception status flags. */ +# if @REPLACE_FESETEXCEPTFLAG@ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef fesetexceptflag +# define fesetexceptflag rpl_fesetexceptflag +# endif +_GL_FUNCDECL_RPL (fesetexceptflag, int, + (fexcept_t const *saved_flags, int exceptions)); +_GL_CXXALIAS_RPL (fesetexceptflag, int, + (fexcept_t const *saved_flags, int exceptions)); +# else +# if !@HAVE_FESETEXCEPTFLAG@ +_GL_FUNCDECL_SYS (fesetexceptflag, int, + (fexcept_t const *saved_flags, int exceptions)); +# endif +_GL_CXXALIAS_SYS (fesetexceptflag, int, + (fexcept_t const *saved_flags, int exceptions)); +# endif +_GL_CXXALIASWARN (fesetexceptflag); +#endif + #endif /* _@GUARD_PREFIX@_FENV_H */ #endif /* _@GUARD_PREFIX@_FENV_H */ diff --git a/m4/fenv-exceptions-state.m4 b/m4/fenv-exceptions-state.m4 new file mode 100644 index 0000000000..bd443be721 --- /dev/null +++ b/m4/fenv-exceptions-state.m4 @@ -0,0 +1,221 @@ +# fenv-exceptions-state.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_EXCEPTIONS_STATE], +[ + AC_REQUIRE([gl_FENV_H_DEFAULTS]) + AC_REQUIRE([AC_CANONICAL_HOST]) + + gl_MATHFUNC([fesetexceptflag], [int], [(fexcept_t const *, int)], + [#include + fexcept_t fx_ret; + ]) + if test $gl_cv_func_fesetexceptflag_no_libm = yes \ + || test $gl_cv_func_fesetexceptflag_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_fesetexceptflag_no_libm = yes; then + FENV_EXCEPTIONS_STATE_LIBM= + else + FENV_EXCEPTIONS_STATE_LIBM=-lm + fi + dnl On glibc 2.19/s390,s390x, fegetexceptflag is not consistent with the + dnl generic implementation of fetestexceptflag, leading to a failure of + dnl test-fenv-except-state-3. Fixed through + dnl . + case "$host" in + s390*-*-linux*) + AC_CACHE_CHECK([whether fegetexceptflag works], + [gl_cv_func_fegetexceptflag_works], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ + #include /* for __GNU_LIBRARY__ */ + #ifdef __GNU_LIBRARY__ + #include + #if __GLIBC__ == 2 && __GLIBC_MINOR__ <= 22 + Unlucky user + #endif + #endif + ]], + [])], + [gl_cv_func_fegetexceptflag_works="guessing yes"], + [gl_cv_func_fegetexceptflag_works="guessing no"]) + ]) + case "$gl_cv_func_fegetexceptflag_works" in + *yes) ;; + *) + REPLACE_FEGETEXCEPTFLAG=1 + REPLACE_FESETEXCEPTFLAG=1 + ;; + esac + ;; + esac + if test $REPLACE_FESETEXCEPTFLAG = 0; then + dnl Persuade glibc to declare feenableexcept(). + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + gl_MATHFUNC([feenableexcept], [int], [(int)], [#include ]) + dnl On glibc 2.37 for PowerPC, i386, x86_64, fesetexceptflag may raise + dnl traps. Likewise on Mac OS X 10.5.8 on i386, x86_64 and on mingw. + AC_CACHE_CHECK([whether fesetexceptflag is guaranteed non-trapping], + [gl_cv_func_fesetexceptflag_works1], + [if test $gl_cv_func_feenableexcept_no_libm = yes \ + || test $gl_cv_func_feenableexcept_in_libm = yes; then + dnl A platform that has feenableexcept. + save_LIBS="$LIBS" + if test $gl_cv_func_feenableexcept_no_libm != yes; then + LIBS="$LIBS -lm" + fi + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ + #include + static volatile double a, b; + static volatile long double al, bl; + ]], + [[fexcept_t saved_flags; + if (feraiseexcept (FE_INVALID) == 0 + && fegetexceptflag (&saved_flags, FE_INVALID) == 0 + && feclearexcept (FE_INVALID) == 0 + && feenableexcept (FE_INVALID) == 0 + && fesetexceptflag (&saved_flags, FE_INVALID) == 0) + { + a = 1.0; b = a + a; + al = 1.0L; bl = al + al; + } + return 0; + ]]) + ], + [gl_cv_func_fesetexceptflag_works1=yes], + [gl_cv_func_fesetexceptflag_works1=no], + [case "$host_os" in + # Guess yes or no on glibc systems, depending on CPU. + *-gnu*) + case "$host_cpu" in +changequote(,)dnl + powerpc* | i[34567]86 | x86_64) +changequote([,])dnl + gl_cv_func_fesetexceptflag_works1="guessing no" ;; + *) + gl_cv_func_fesetexceptflag_works1="guessing yes" ;; + esac + ;; + # If we don't know, obey --enable-cross-guesses. + *) gl_cv_func_fesetexceptflag_works1="$gl_cross_guess_normal" ;; + esac + ]) + LIBS="$save_LIBS" + else + case "$host_os" in + # Guess no on musl libc. + # In musl/src/fenv/fesetexceptflag.c, fesetexceptflag() explicitly + # invokes feraiseexcept(). It's mentioned in + # . + *-musl* | midipix*) + gl_cv_func_fesetexceptflag_works1="guessing no" + ;; + # Guess no on macOS. + darwin*) + gl_cv_func_fesetexceptflag_works1="guessing no" + ;; + # Guess no on mingw. + mingw* | windows*) + AC_EGREP_CPP([Problem], [ +#ifdef __MINGW32__ + Problem +#endif + ], + [gl_cv_func_fesetexceptflag_works1="guessing no"], + [gl_cv_func_fesetexceptflag_works1="guessing yes"]) + ;; + *) gl_cv_func_fesetexceptflag_works1="guessing yes" ;; + esac + fi + ]) + dnl On glibc 2.37 for alpha, fesetexceptflag clears too many flag bits. + AC_CACHE_CHECK([whether fesetexceptflag obeys its arguments], + [gl_cv_func_fesetexceptflag_works2], + [save_LIBS="$LIBS" + LIBS="$LIBS $FENV_EXCEPTIONS_STATE_LIBM" + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ + #include + ]], + [[fexcept_t f1, f2; + if (feraiseexcept (FE_DIVBYZERO | FE_OVERFLOW | FE_INEXACT) == 0 + && fegetexceptflag (&f1, FE_DIVBYZERO | FE_OVERFLOW | FE_INEXACT) == 0 + && feclearexcept (FE_OVERFLOW | FE_INEXACT) == 0 + && fegetexceptflag (&f2, FE_OVERFLOW) == 0 + && fesetexceptflag (&f1, FE_DIVBYZERO | FE_OVERFLOW) == 0 + && fesetexceptflag (&f2, FE_OVERFLOW) == 0 + && fetestexcept (FE_DIVBYZERO) != FE_DIVBYZERO) + return 1; + return 0; + ]]) + ], + [gl_cv_func_fesetexceptflag_works2=yes], + [gl_cv_func_fesetexceptflag_works2=no], + [case "$host_os" in + # Guess yes or no on glibc systems, depending on CPU. + *-gnu*) + case "$host_cpu" in + alpha*) + gl_cv_func_fesetexceptflag_works2="guessing no" ;; + *) + gl_cv_func_fesetexceptflag_works2="guessing yes" ;; + esac + ;; + # If we don't know, obey --enable-cross-guesses. + *) gl_cv_func_fesetexceptflag_works2="$gl_cross_guess_normal" ;; + esac + ]) + LIBS="$save_LIBS" + ]) + case "$gl_cv_func_fesetexceptflag_works1" in + *yes) ;; + *) REPLACE_FESETEXCEPTFLAG=1 ;; + esac + case "$gl_cv_func_fesetexceptflag_works2" in + *yes) ;; + *) REPLACE_FESETEXCEPTFLAG=1 ;; + esac + dnl Additionally, on AIX, the unit test test-fenv-except-state-1 fails + dnl if we don't override fesetexceptflag. + case "$host" in + powerpc*-*-aix*) REPLACE_FESETEXCEPTFLAG=1 ;; + esac + dnl Additionally, on MSVC, we want the bits in the saved state to be + dnl identified by the FE_* macros, so that the fetestexceptflag function + dnl can be implemented like on other platforms. This requires conversions + dnl (exceptions_to_x86hardware, x86hardware_to_exceptions) in both + dnl of the fegetexceptflag, fesetexceptflag functions. + case "$host_os" in + mingw* | windows*) + AC_EGREP_CPP([Problem], [ +#ifndef __MINGW32__ + Problem +#endif + ], + [REPLACE_FEGETEXCEPTFLAG=1; REPLACE_FESETEXCEPTFLAG=1], + []) + ;; + esac + fi + else + HAVE_FEGETEXCEPTFLAG=0 + HAVE_FESETEXCEPTFLAG=0 + FENV_EXCEPTIONS_STATE_LIBM= + fi + if { test $HAVE_FEGETEXCEPTFLAG = 0 || test $REPLACE_FEGETEXCEPTFLAG = 1; } \ + || { test $HAVE_FESETEXCEPTFLAG = 0 || test $REPLACE_FESETEXCEPTFLAG = 1; }; then + gl_PREREQ_FENV_EXCEPTIONS + dnl Possibly need -lm for fpgetsticky(), fpsetsticky(). + if test $gl_cv_func_fpsetsticky_no_libm = no \ + && test $gl_cv_func_fpsetsticky_in_libm = yes \ + && test -z "$FENV_EXCEPTIONS_STATE_LIBM"; then + FENV_EXCEPTIONS_STATE_LIBM=-lm + fi + fi + AC_SUBST([FENV_EXCEPTIONS_STATE_LIBM]) +]) diff --git a/m4/mathfunc.m4 b/m4/mathfunc.m4 index f220cea8ac..bf83bd2b8a 100644 --- a/m4/mathfunc.m4 +++ b/m4/mathfunc.m4 @@ -1,4 +1,4 @@ -# mathfunc.m4 serial 15 +# mathfunc.m4 serial 16 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, @@ -41,7 +41,9 @@ AC_DEFUN([gl_MATHFUNC], [m4_bpatsubst( [m4_bpatsubst( [m4_bpatsubst( - [$3], + [m4_bpatsubst( + [$3], + [fexcept_t\( const\)? \*], [&fx_ret])], [int\( const\)? \*], [&i_ret])], [float\( const\)? \*], [&f_ret])], diff --git a/modules/fenv-exceptions-state-c99 b/modules/fenv-exceptions-state-c99 new file mode 100644 index 0000000000..dcf7c42c10 --- /dev/null +++ b/modules/fenv-exceptions-state-c99 @@ -0,0 +1,43 @@ +Description: +Functions for saving the floating-point exception status flags: +fegetexceptflag, fesetexceptflag. + +Files: +lib/fenv-except-state-get.c +lib/fenv-except-state-set.c +lib/fenv-private.h +m4/fenv-exceptions-state.m4 +m4/fenv-exceptions.m4 +m4/mathfunc.m4 + +Depends-on: +fenv + +configure.ac: +gl_FENV_EXCEPTIONS_STATE +gl_CONDITIONAL([GL_COND_OBJ_FENV_EXCEPTIONS_STATE_GET], + [test $HAVE_FEGETEXCEPTFLAG = 0 || test $REPLACE_FEGETEXCEPTFLAG = 1]) +gl_CONDITIONAL([GL_COND_OBJ_FENV_EXCEPTIONS_STATE_SET], + [test $HAVE_FESETEXCEPTFLAG = 0 || test $REPLACE_FESETEXCEPTFLAG = 1]) +gl_FENV_MODULE_INDICATOR([fegetexceptflag]) +gl_FENV_MODULE_INDICATOR([fesetexceptflag]) + +Makefile.am: +if GL_COND_OBJ_FENV_EXCEPTIONS_STATE_GET +lib_SOURCES += fenv-except-state-get.c +endif +if GL_COND_OBJ_FENV_EXCEPTIONS_STATE_SET +lib_SOURCES += fenv-except-state-set.c +endif + +Include: +#include + +Link: +$(FENV_EXCEPTIONS_STATE_LIBM) + +License: +LGPLv2+ + +Maintainer: +all -- 2.39.5