From 2ce970a2718bdf9685517fba9efa5ec60d773953 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Sat, 28 Oct 2023 18:31:22 +0200 Subject: [PATCH] fenv-exceptions-tracking-c99: New module. * lib/fenv.in.h (feclearexcept, feraiseexcept, fetestexcept): New declarations. * lib/fenv-except-tracking-clear.c: New file, based on glibc. * lib/fenv-except-tracking-raise.c: New file, based on glibc. * lib/fenv-except-tracking-test.c: New file, based on glibc. * m4/fenv-exceptions-tracking.m4: New file. * m4/fenv-exceptions.m4: New file. * modules/fenv-exceptions-tracking-c99: New file. * doc/posix-functions/feclearexcept.texi: Mention the new module. * doc/posix-functions/fetestexcept.texi: Likewise. * doc/posix-functions/feraiseexcept.texi: Likewise. Mention the glibc and Cygwin bugs. --- ChangeLog | 16 + doc/posix-functions/feclearexcept.texi | 8 +- doc/posix-functions/feraiseexcept.texi | 15 +- doc/posix-functions/fetestexcept.texi | 8 +- lib/fenv-except-tracking-clear.c | 392 +++++++++++++++++++++ lib/fenv-except-tracking-raise.c | 457 +++++++++++++++++++++++++ lib/fenv-except-tracking-test.c | 266 ++++++++++++++ lib/fenv.in.h | 57 +++ m4/fenv-exceptions-tracking.m4 | 152 ++++++++ m4/fenv-exceptions.m4 | 23 ++ modules/fenv-exceptions-tracking-c99 | 50 +++ 11 files changed, 1432 insertions(+), 12 deletions(-) create mode 100644 lib/fenv-except-tracking-clear.c create mode 100644 lib/fenv-except-tracking-raise.c create mode 100644 lib/fenv-except-tracking-test.c create mode 100644 m4/fenv-exceptions-tracking.m4 create mode 100644 m4/fenv-exceptions.m4 create mode 100644 modules/fenv-exceptions-tracking-c99 diff --git a/ChangeLog b/ChangeLog index 2eb57b8d9d..b1c73c0f98 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2023-10-28 Bruno Haible + + fenv-exceptions-tracking-c99: New module. + * lib/fenv.in.h (feclearexcept, feraiseexcept, fetestexcept): New + declarations. + * lib/fenv-except-tracking-clear.c: New file, based on glibc. + * lib/fenv-except-tracking-raise.c: New file, based on glibc. + * lib/fenv-except-tracking-test.c: New file, based on glibc. + * m4/fenv-exceptions-tracking.m4: New file. + * m4/fenv-exceptions.m4: New file. + * modules/fenv-exceptions-tracking-c99: New file. + * doc/posix-functions/feclearexcept.texi: Mention the new module. + * doc/posix-functions/fetestexcept.texi: Likewise. + * doc/posix-functions/feraiseexcept.texi: Likewise. Mention the glibc + and Cygwin bugs. + 2023-10-27 Bruno Haible fenv-rounding: Add tests. diff --git a/doc/posix-functions/feclearexcept.texi b/doc/posix-functions/feclearexcept.texi index dc56c3fd45..8b218804bd 100644 --- a/doc/posix-functions/feclearexcept.texi +++ b/doc/posix-functions/feclearexcept.texi @@ -4,15 +4,15 @@ POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/feclearexcept.html} -Gnulib module: --- +Gnulib module: fenv-exceptions-tracking-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/feraiseexcept.texi b/doc/posix-functions/feraiseexcept.texi index 07befbf263..a4f2ebab01 100644 --- a/doc/posix-functions/feraiseexcept.texi +++ b/doc/posix-functions/feraiseexcept.texi @@ -4,15 +4,22 @@ POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/feraiseexcept.html} -Gnulib module: --- +Gnulib module: fenv-exceptions-tracking-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 does not detect failures on +glibc 2.19/arm. +@item +This function does not trigger traps on +@c https://sourceware.org/pipermail/cygwin/2023-October/254667.html +Cygwin 3.4.6/x86_64. @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/doc/posix-functions/fetestexcept.texi b/doc/posix-functions/fetestexcept.texi index 182e23ff35..b8eba903be 100644 --- a/doc/posix-functions/fetestexcept.texi +++ b/doc/posix-functions/fetestexcept.texi @@ -4,15 +4,15 @@ POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/fetestexcept.html} -Gnulib module: --- +Gnulib module: fenv-exceptions-tracking-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/lib/fenv-except-tracking-clear.c b/lib/fenv-except-tracking-clear.c new file mode 100644 index 0000000000..22c85c98e0 --- /dev/null +++ b/lib/fenv-except-tracking-clear.c @@ -0,0 +1,392 @@ +/* Functions for tracking which floating-point exceptions have occurred. + 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//{fclrexcpt.c,fraiseexcpt.c,ftestexcept.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) + +int +feclearexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + +# if defined _MSC_VER + + exceptions = exceptions_to_x86hardware (exceptions); + + /* Clear the bits only in the SSE unit. */ + unsigned int mxcsr, orig_mxcsr; + _FPU_GETSSECW (orig_mxcsr); + mxcsr = orig_mxcsr & ~exceptions; + if (mxcsr != orig_mxcsr) + _FPU_SETSSECW (mxcsr); + +# else + + /* Clear the bits 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 &= ~exceptions; + if (env.__status_word != orig_status_word) + __asm__ __volatile__ ("fldenv %0" : : "m" (*&env)); + + if (CPU_HAS_SSE ()) + { + /* Clear the bits in the SSE unit as well. */ + unsigned int mxcsr, orig_mxcsr; + _FPU_GETSSECW (orig_mxcsr); + mxcsr = orig_mxcsr & ~exceptions; + if (mxcsr != orig_mxcsr) + _FPU_SETSSECW (mxcsr); + } + +# endif + + return 0; +} + +# elif defined __aarch64__ /* arm64 */ + +int +feclearexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned long fpsr, orig_fpsr; + _FPU_GETFPSR (orig_fpsr); + fpsr = orig_fpsr & ~exceptions; + if (fpsr != orig_fpsr) + _FPU_SETFPSR (fpsr); + + return 0; +} + +# elif defined __arm__ + +int +feclearexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + +# ifdef __SOFTFP__ + if (exceptions != 0) + return -1; +# else + unsigned int fpscr, orig_fpscr; + _FPU_GETCW (orig_fpscr); + fpscr = orig_fpscr & ~exceptions; + if (fpscr != orig_fpscr) + _FPU_SETCW (fpscr); +# endif + return 0; +} + +# elif defined __alpha + +int +feclearexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned long swcr, orig_swcr; + orig_swcr = __ieee_get_fp_control (); + swcr = orig_swcr & ~exceptions; + if (swcr != orig_swcr) + __ieee_set_fp_control (swcr); + + return 0; +} + +# elif defined __hppa + +int +feclearexcept (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 << 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"); + } + + return 0; +} + +# elif defined __ia64__ + +int +feclearexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned long fpsr, orig_fpsr; + _FPU_GETCW (orig_fpsr); + fpsr = orig_fpsr & ~ (unsigned long) (exceptions << 13); + if (fpsr != orig_fpsr) + _FPU_SETCW (fpsr); + + return 0; +} + +# elif defined __m68k__ + +int +feclearexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned int fpsr, orig_fpsr; + _FPU_GETFPSR (orig_fpsr); + fpsr = orig_fpsr & ~ exceptions; + if (fpsr != orig_fpsr) + _FPU_SETFPSR (fpsr); + + return 0; +} + +# elif defined __mips__ + +int +feclearexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + /* Clear also the cause bits. If the cause bit is not cleared, the next + CTC instruction (just below) will re-generate the exception. */ + unsigned int fcsr, orig_fcsr; + _FPU_GETCW (orig_fcsr); + fcsr = orig_fcsr & ~ ((exceptions << 10) | exceptions); + if (fcsr != orig_fcsr) + _FPU_SETCW (fcsr); + + return 0; +} + +# elif defined __loongarch__ + +int +feclearexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + /* Clear also the cause bits. If the cause bit is not cleared, the next + CTC instruction (just below) will re-generate the exception. */ + unsigned int fcsr, orig_fcsr; + _FPU_GETCW (orig_fcsr); + fcsr = orig_fcsr & ~ ((exceptions << 8) | exceptions); + if (fcsr != orig_fcsr) + _FPU_SETCW (fcsr); + + return 0; +} + +# elif defined __powerpc__ + +int +feclearexcept (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; + + /* Instead of clearing FE_INVALID (= bit 29), we need to clear the + individual bits. */ + memenv.u &= ~ (exceptions & FE_INVALID + ? (exceptions & ~FE_INVALID) | 0x01F80700U + : exceptions); + + if (!(memenv.u == orig_memenv.u)) + _FPU_SETCW_AS_DOUBLE (memenv.f); + + return 0; +} + +# elif defined __riscv + +int +feclearexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + __asm__ __volatile__ ("csrc fflags, %0" : : "r" (exceptions)); + return 0; +} + +# elif defined __s390__ || defined __s390x__ + +int +feclearexcept (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 & ~(exceptions << 16); + if ((fpc & 0x00000300) == 0) + fpc &= ~(exceptions << 8); +# else /* musl libc compatible FE_* values */ + fpc = orig_fpc & ~exceptions; + if ((fpc & 0x00000300) == 0) + fpc &= ~(exceptions >> 8); +# endif + if (fpc != orig_fpc) + _FPU_SETCW (fpc); + + return 0; +} + +# elif defined __sh__ + +int +feclearexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned int fpscr, orig_fpscr; + _FPU_GETCW (orig_fpscr); + fpscr = orig_fpscr & ~exceptions; + if (fpscr != orig_fpscr) + _FPU_SETCW (fpscr); + + return 0; +} + +# elif defined __sparc + +int +feclearexcept (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; +# else /* Solaris compatible FE_* values */ + fsr = orig_fsr & ~(exceptions << 5); +# endif + if (fsr != orig_fsr) + _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 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 +feclearexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + fp_except_t flags, orig_flags; + orig_flags = fpgetsticky (); + flags = orig_flags & ~exceptions; + if (flags != orig_flags) + fpsetsticky (flags); + + return 0; +} + +# elif defined _AIX && defined __powerpc__ /* AIX */ + +# include +# include + +/* Documentation: + */ + +int +feclearexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + /* 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) + | (exceptions & FE_INVALID ? 0x01F80700U : 0); + if (f_to_clear != 0) + fp_clr_flag (f_to_clear); + + return 0; +} + +# else + +# define NEED_FALLBACK 1 + +# endif + +#endif + +#if NEED_FALLBACK + +/* A dummy fallback. */ + +int +feclearexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + if (exceptions != 0) + return -1; + return 0; +} + +#endif diff --git a/lib/fenv-except-tracking-raise.c b/lib/fenv-except-tracking-raise.c new file mode 100644 index 0000000000..a9f8b04963 --- /dev/null +++ b/lib/fenv-except-tracking-raise.c @@ -0,0 +1,457 @@ +/* Functions for tracking which floating-point exceptions have occurred. + 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//{fclrexcpt.c,fraiseexcpt.c,ftestexcept.c} + together with glibc/sysdeps//{fpu_control.h,fenv_private.h,fenv_libc.h}. */ + +#include + +/* Specification. */ +#include + +#include "fenv-private.h" + +_GL_UNUSED static void +generic_feraiseexcept (int exceptions) +{ + /* First: invalid exception. */ + if (exceptions & FE_INVALID) + { + double volatile a; + _GL_UNUSED double volatile b; + a = 0; b = a / a; + } + /* Next: division by zero. */ + if (exceptions & FE_DIVBYZERO) + { + double volatile a, b; + _GL_UNUSED double volatile c; + a = 1; b = 0; c = a / b; + } + /* Next: overflow. */ + if (exceptions & FE_OVERFLOW) + { + double volatile a; + _GL_UNUSED double volatile b; + a = 1e200; b = a * a; + } + /* Next: underflow. */ + if (exceptions & FE_UNDERFLOW) + { + double volatile a; + _GL_UNUSED double volatile b; + a = 1e-200; b = a * a; + } + /* Last: inexact. */ + if (exceptions & FE_INEXACT) + { + double volatile a, b; + _GL_UNUSED double volatile c; + a = 1; b = 3; c = a / b; + } +} + +#if defined __GNUC__ || defined __clang__ || defined _MSC_VER + +# if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86) + +int +feraiseexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + if ((exceptions & ~(FE_INVALID | FE_DIVBYZERO)) == 0) + { + /* Like generic_feraiseexcept (exceptions). */ + /* This code is probably faster than the general code below. */ + /* First: invalid exception. */ + if (exceptions & FE_INVALID) + { + double volatile a; + _GL_UNUSED double volatile b; + a = 0; b = a / a; + } + /* Next: division by zero. */ + if (exceptions & FE_DIVBYZERO) + { + double volatile a, b; + _GL_UNUSED double volatile c; + a = 1; b = 0; c = a / b; + } + } + else + { + /* The general case. */ +# if defined _MSC_VER + + exceptions = exceptions_to_x86hardware (exceptions); + + /* Set the bits only in the SSE unit. */ + unsigned int mxcsr, orig_mxcsr; + _FPU_GETSSECW (orig_mxcsr); + mxcsr = orig_mxcsr | exceptions; + if (mxcsr != orig_mxcsr) + _FPU_SETSSECW (mxcsr); + +# else + + /* Set the bits 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 |= exceptions; + if (env.__status_word != orig_status_word) + { + __asm__ __volatile__ ("fldenv %0" : : "m" (*&env)); + /* A trap (if enabled) is triggered only at the next floating-point + instruction. Force it to occur here. */ + __asm__ __volatile__ ("fwait"); + } + +# endif + } + return 0; +} + +# elif defined __aarch64__ /* arm64 */ + +int +feraiseexcept (int exceptions) +{ +# if 0 + /* This would just set the flag bits and make fetestexcept() work as expected. + But it would not cause the hardware to trap on the exception. */ + exceptions &= FE_ALL_EXCEPT; + + unsigned long fpsr, orig_fpsr; + _FPU_GETFPSR (orig_fpsr); + fpsr = orig_fpsr | exceptions; + if (fpsr != orig_fpsr) + _FPU_SETFPSR (fpsr); +# else + /* This is how glibc does it. + The drawback is that when FE_OVERFLOW is raised, FE_INEXACT is raised + with it. */ + generic_feraiseexcept (exceptions); +# endif + return 0; +} + +# elif defined __arm__ + +int +feraiseexcept (int exceptions) +{ +# ifdef __SOFTFP__ + exceptions &= FE_ALL_EXCEPT; + + if (exceptions != 0) + return -1; +# else + /* Raise exceptions represented by EXCEPTIONS. But we must raise only + one signal at a time. It is important that if the overflow/underflow + exception and the inexact exception are given at the same time, + the overflow/underflow exception follows the inexact exception. After + each exception we read from the fpscr, to force the exception to be + raised immediately. */ + unsigned int fpscr, orig_fpscr; + /* First: invalid exception. */ + if (exceptions & FE_INVALID) + { + _FPU_GETCW (orig_fpscr); + fpscr = orig_fpscr | FE_INVALID; + if (fpscr != orig_fpscr) + { + _FPU_SETCW (fpscr); + _FPU_GETCW (fpscr); + } + } + /* Next: division by zero. */ + if (exceptions & FE_DIVBYZERO) + { + _FPU_GETCW (orig_fpscr); + fpscr = orig_fpscr | FE_DIVBYZERO; + if (fpscr != orig_fpscr) + { + _FPU_SETCW (fpscr); + _FPU_GETCW (fpscr); + } + } + /* Next: overflow. */ + if (exceptions & FE_OVERFLOW) + { + _FPU_GETCW (orig_fpscr); + fpscr = orig_fpscr | FE_OVERFLOW; + if (fpscr != orig_fpscr) + { + _FPU_SETCW (fpscr); + _FPU_GETCW (fpscr); + } + } + /* Next: underflow. */ + if (exceptions & FE_UNDERFLOW) + { + _FPU_GETCW (orig_fpscr); + fpscr = orig_fpscr | FE_UNDERFLOW; + if (fpscr != orig_fpscr) + { + _FPU_SETCW (fpscr); + _FPU_GETCW (fpscr); + } + } + /* Last: inexact. */ + if (exceptions & FE_INEXACT) + { + _FPU_GETCW (orig_fpscr); + fpscr = orig_fpscr | FE_INEXACT; + if (fpscr != orig_fpscr) + { + _FPU_SETCW (fpscr); + _FPU_GETCW (fpscr); + } + } +# endif + return 0; +} + +# elif defined __alpha + +/* Prefer the Linux system call when available. + See glibc/sysdeps/unix/sysv/linux/alpha/fraiseexcpt.S */ +# if !defined __linux__ +int +feraiseexcept (int exceptions) +{ + /* This implementation cannot raise FE_INEXACT. */ + generic_feraiseexcept (exceptions); + return 0; +} +# endif + +# elif defined __hppa + +int +feraiseexcept (int exceptions) +{ + generic_feraiseexcept (exceptions); + return 0; +} + +# elif defined __ia64__ + +int +feraiseexcept (int exceptions) +{ + /* Raise exceptions represented by EXCEPTIONS. But we must raise only + one signal at a time. It is important that if the overflow/underflow + exception and the inexact exception are given at the same time, + the overflow/underflow exception precedes the inexact exception. */ + generic_feraiseexcept (exceptions); + return 0; +} + +# elif defined __m68k__ + +int +feraiseexcept (int exceptions) +{ + generic_feraiseexcept (exceptions); + return 0; +} + +# elif defined __mips__ + +int +feraiseexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + /* Set also the cause bits. The setting of the cause bits is what actually + causes the hardware to trap on the exception, if the corresponding enable + bit is set as well. */ + unsigned int fcsr, orig_fcsr; + _FPU_GETCW (orig_fcsr); + fcsr = orig_fcsr | ((exceptions << 10) | exceptions); + if (fcsr != orig_fcsr) + _FPU_SETCW (fcsr); + + return 0; +} + +# elif defined __loongarch__ + +int +feraiseexcept (int exceptions) +{ +# if 0 + /* This would just set the flag bits and make fetestexcept() work as expected. + But it would not cause the hardware to trap on the exception. */ + exceptions &= FE_ALL_EXCEPT; + + /* Set also the cause bits. The setting of the cause bits is what actually + causes the hardware to trap on the exception, if the corresponding enable + bit is set as well. */ + unsigned int fcsr, orig_fcsr; + _FPU_GETCW (orig_fcsr); + fcsr = orig_fcsr | ((exceptions << 8) | exceptions); + if (fcsr != orig_fcsr) + _FPU_SETCW (fcsr); +# else + /* This is how glibc does it. + The drawback is that when FE_OVERFLOW is raised, FE_INEXACT is raised + with it. */ + generic_feraiseexcept (exceptions); +# endif + return 0; +} + +# elif defined __powerpc__ + +int +feraiseexcept (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; + + /* 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 |= (exceptions & FE_INVALID + ? (exceptions & ~FE_INVALID) | (1U << 10) + : exceptions); + + if (!(memenv.u == orig_memenv.u)) + { + _FPU_SETCW_AS_DOUBLE (memenv.f); + if (exceptions & 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 + +int +feraiseexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + __asm__ __volatile__ ("csrs fflags, %0" : : "r" (exceptions)); + return 0; +} + +# elif defined __s390__ || defined __s390x__ + +int +feraiseexcept (int exceptions) +{ + generic_feraiseexcept (exceptions); + return 0; +} + +# elif defined __sh__ + +int +feraiseexcept (int exceptions) +{ +# if 0 + /* This would just set the flag bits and make fetestexcept() work as expected. + But it would not cause the hardware to trap on the exception. */ + exceptions &= FE_ALL_EXCEPT; + + unsigned int fpscr, orig_fpscr; + _FPU_GETCW (orig_fpscr); + fpscr = orig_fpscr | exceptions; + if (fpscr != orig_fpscr) + _FPU_SETCW (fpscr); +# else + /* This is how glibc does it. + The drawback is that when FE_OVERFLOW is raised, FE_INEXACT is raised + with it. */ + generic_feraiseexcept (exceptions); +# endif + return 0; +} + +# elif defined __sparc + +int +feraiseexcept (int exceptions) +{ +# if 0 + /* This would just set the flag bits and make fetestexcept() work as expected. + But it would not cause the hardware to trap on the exception. */ + 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; +# else /* Solaris compatible FE_* values */ + fsr = orig_fsr | (exceptions << 5); +# endif + if (fsr != orig_fsr) + _FPU_SETCW (fsr); +# else + /* This is how glibc does it. + The drawback is that when FE_OVERFLOW is raised, FE_INEXACT is raised + with it. */ + generic_feraiseexcept (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. */ + +# define NEED_FALLBACK 1 + +#endif + +#if NEED_FALLBACK + +/* A fallback that should work everywhere. */ + +int +feraiseexcept (int exceptions) +{ + generic_feraiseexcept (exceptions); + return 0; +} + +#endif diff --git a/lib/fenv-except-tracking-test.c b/lib/fenv-except-tracking-test.c new file mode 100644 index 0000000000..efb9baa9ae --- /dev/null +++ b/lib/fenv-except-tracking-test.c @@ -0,0 +1,266 @@ +/* Functions for tracking which floating-point exceptions have occurred. + 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//{fclrexcpt.c,fraiseexcpt.c,ftestexcept.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) + +int +fetestexcept (int exceptions) +{ +# if defined _MSC_VER + + /* Look at the flags in the SSE unit. */ + unsigned int mxcsr; + _FPU_GETSSECW (mxcsr); + return 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); + } + + return x86hardware_to_exceptions (fstat | mxcsr) & FE_ALL_EXCEPT & exceptions; +# endif +} + +# elif defined __aarch64__ /* arm64 */ + +int +fetestexcept (int exceptions) +{ + unsigned long fpsr; + _FPU_GETFPSR (fpsr); + return fpsr & FE_ALL_EXCEPT & exceptions; +} + +# elif defined __arm__ + +int +fetestexcept (int exceptions) +{ +# ifdef __SOFTFP__ + return 0; +# else + unsigned int fpscr; + _FPU_GETCW (fpscr); + return fpscr & FE_ALL_EXCEPT & exceptions; +# endif +} + +# elif defined __alpha + +int +fetestexcept (int exceptions) +{ + unsigned long swcr = __ieee_get_fp_control (); + return swcr & FE_ALL_EXCEPT & exceptions; +} + +# elif defined __hppa + +int +fetestexcept (int exceptions) +{ + 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 (s.halfreg[0] >> 27) & FE_ALL_EXCEPT & exceptions; +} + +# elif defined __ia64__ + +int +fetestexcept (int exceptions) +{ + unsigned long fpsr; + _FPU_GETCW (fpsr); + return (fpsr >> 13) & FE_ALL_EXCEPT & exceptions; +} + +# elif defined __m68k__ + +int +fetestexcept (int exceptions) +{ + unsigned int fpsr; + _FPU_GETFPSR (fpsr); + return fpsr & FE_ALL_EXCEPT & exceptions; +} + +# elif defined __mips__ + +int +fetestexcept (int exceptions) +{ + unsigned int fcsr; + _FPU_GETCW (fcsr); + return fcsr & FE_ALL_EXCEPT & exceptions; +} + +# elif defined __loongarch__ + +int +fetestexcept (int exceptions) +{ + unsigned int fcsr; + _FPU_GETCW (fcsr); + return fcsr & FE_ALL_EXCEPT & exceptions; +} + +# elif defined __powerpc__ + +int +fetestexcept (int exceptions) +{ + union { unsigned long long u; double f; } memenv; + _FPU_GETCW_AS_DOUBLE (memenv.f); + return memenv.u & FE_ALL_EXCEPT & exceptions; +} + +# elif defined __riscv + +int +fetestexcept (int exceptions) +{ + unsigned int flags; + __asm__ __volatile__ ("frflags %0" : "=r" (flags)); /* same as "csrr %0, fflags" */ + return flags & FE_ALL_EXCEPT & exceptions; +} + +# elif defined __s390__ || defined __s390x__ + +int +fetestexcept (int exceptions) +{ + unsigned int fpc; + _FPU_GETCW (fpc); +# if FE_INEXACT == 8 /* glibc compatible FE_* values */ + return ((fpc >> 16) | ((fpc & 0x00000300) == 0 ? fpc >> 8 : 0)) + & FE_ALL_EXCEPT & exceptions; +# else /* musl libc compatible FE_* values */ + return (fpc | ((fpc & 0x00000300) == 0 ? fpc << 8 : 0)) + & FE_ALL_EXCEPT & exceptions; +# endif +} + +# elif defined __sh__ + +int +fetestexcept (int exceptions) +{ + unsigned int fpscr; + _FPU_GETCW (fpscr); + return fpscr & FE_ALL_EXCEPT & exceptions; +} + +# elif defined __sparc + +int +fetestexcept (int exceptions) +{ + unsigned long fsr; + _FPU_GETCW (fsr); +# if FE_INEXACT == 32 /* glibc compatible FE_* values */ + return fsr & FE_ALL_EXCEPT & exceptions; +# else /* Solaris compatible FE_* values */ + return (fsr >> 5) & FE_ALL_EXCEPT & exceptions; +# endif +} + +# 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 +fetestexcept (int exceptions) +{ + fp_except_t flags = fpgetsticky (); + return flags & FE_ALL_EXCEPT & exceptions; +} + +# elif defined _AIX && defined __powerpc__ /* AIX */ + +# include +# include + +/* Documentation: + */ + +int +fetestexcept (int exceptions) +{ + fpflag_t flags = fp_read_flag (); + return fpflag_to_exceptions (flags) & FE_ALL_EXCEPT & exceptions; +} + +# else + +# define NEED_FALLBACK 1 + +# endif + +#endif + +#if NEED_FALLBACK + +/* A dummy fallback. */ + +int +fetestexcept (int exceptions) +{ + return 0; +} + +#endif diff --git a/lib/fenv.in.h b/lib/fenv.in.h index 2c70e343eb..9662558805 100644 --- a/lib/fenv.in.h +++ b/lib/fenv.in.h @@ -457,6 +457,63 @@ _GL_CXXALIASWARN (fesetround); #endif +#if @GNULIB_FECLEAREXCEPT@ +/* Clears the specified exception flags, and returns 0. + Upon failure, it returns non-zero. */ +# if @REPLACE_FECLEAREXCEPT@ || (!@HAVE_FECLEAREXCEPT@ && (defined __GLIBC__ || defined __FreeBSD__)) /* has an inline definition */ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef feclearexcept +# define feclearexcept rpl_feclearexcept +# endif +_GL_FUNCDECL_RPL (feclearexcept, int, (int exceptions)); +_GL_CXXALIAS_RPL (feclearexcept, int, (int exceptions)); +# else +# if !@HAVE_FECLEAREXCEPT@ +_GL_FUNCDECL_SYS (feclearexcept, int, (int exceptions)); +# endif +_GL_CXXALIAS_SYS (feclearexcept, int, (int exceptions)); +# endif +_GL_CXXALIASWARN (feclearexcept); +#endif + +#if @GNULIB_FERAISEEXCEPT@ +/* Sets the specified exception flags, triggering handlers or traps if enabled, + and returns 0. Upon failure, it returns non-zero. */ +# if @REPLACE_FERAISEEXCEPT@ || (!@HAVE_FERAISEEXCEPT@ && (defined __GLIBC__ || defined __FreeBSD__ || defined _MSC_VER)) /* has an inline definition */ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef feraiseexcept +# define feraiseexcept rpl_feraiseexcept +# endif +_GL_FUNCDECL_RPL (feraiseexcept, int, (int exceptions)); +_GL_CXXALIAS_RPL (feraiseexcept, int, (int exceptions)); +# else +# if !@HAVE_FERAISEEXCEPT@ +_GL_FUNCDECL_SYS (feraiseexcept, int, (int exceptions)); +# endif +_GL_CXXALIAS_SYS (feraiseexcept, int, (int exceptions)); +# endif +_GL_CXXALIASWARN (feraiseexcept); +#endif + +#if @GNULIB_FETESTEXCEPT@ +/* Returns a bitmask of those exception flags among EXCEPTIONS that are + currently set. */ +# if @REPLACE_FETESTEXCEPT@ || (!@HAVE_FETESTEXCEPT@ && defined __FreeBSD__) /* has an inline definition */ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef fetestexcept +# define fetestexcept rpl_fetestexcept +# endif +_GL_FUNCDECL_RPL (fetestexcept, int, (int exceptions)); +_GL_CXXALIAS_RPL (fetestexcept, int, (int exceptions)); +# else +# if !@HAVE_FETESTEXCEPT@ +_GL_FUNCDECL_SYS (fetestexcept, int, (int exceptions)); +# endif +_GL_CXXALIAS_SYS (fetestexcept, int, (int exceptions)); +# endif +_GL_CXXALIASWARN (fetestexcept); +#endif + /* ISO C 99 § 7.6.2 Floating-point exceptions ISO C 23 § 7.6.4 Floating-point exceptions diff --git a/m4/fenv-exceptions-tracking.m4 b/m4/fenv-exceptions-tracking.m4 new file mode 100644 index 0000000000..a7ffa80784 --- /dev/null +++ b/m4/fenv-exceptions-tracking.m4 @@ -0,0 +1,152 @@ +# fenv-exceptions-tracking.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_TRACKING], +[ + AC_REQUIRE([gl_FENV_H_DEFAULTS]) + AC_REQUIRE([AC_CANONICAL_HOST]) + + gl_MATHFUNC([feraiseexcept], [int], [(int)], [#include ]) + if test $gl_cv_func_feraiseexcept_no_libm = yes \ + || test $gl_cv_func_feraiseexcept_in_libm = yes; then + dnl On glibc 2.19/arm, feraiseexcept does not detect failures. + dnl Fixed through + dnl . + dnl On Cygwin 3.4.6/x86_64, feraiseexcept does not trigger traps. + dnl See . + dnl On musl libc, on those CPUs where fenv-except-tracking-raise.c + dnl uses the "generic" approach, feraiseexcept does not trigger traps + dnl because it merely manipulates flags in the control register. + case "$host" in + arm*-*-linux*) + AC_CACHE_CHECK([whether feraiseexcept works], + [gl_cv_func_feraiseexcept_works], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ + #ifdef __SOFTFP__ + #include /* for __GNU_LIBRARY__ */ + #ifdef __GNU_LIBRARY__ + #include + #if __GLIBC__ == 2 && __GLIBC_MINOR__ <= 19 + Unlucky user + #endif + #endif + #endif + ]], + [])], + [gl_cv_func_feraiseexcept_works="guessing yes"], + [gl_cv_func_feraiseexcept_works="guessing no"]) + ]) + case "$gl_cv_func_feraiseexcept_works" in + *yes) ;; + *) REPLACE_FERAISEEXCEPT=1 ;; + esac + ;; + x86_64-*-cygwin*) + AC_CACHE_CHECK([whether feraiseexcept works], + [gl_cv_func_feraiseexcept_works], + [AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ + #include + ]], + [[feclearexcept (FE_INVALID); + feenableexcept (FE_INVALID); + feraiseexcept (FE_INVALID); + return 0; + ]]) + ], + [gl_cv_func_feraiseexcept_works=no], + [gl_cv_func_feraiseexcept_works=yes], + [gl_cv_func_feraiseexcept_works="guessing no"]) + ]) + case "$gl_cv_func_feraiseexcept_works" in + *yes) ;; + *) REPLACE_FERAISEEXCEPT=1 ;; + esac + ;; + *-*-*-musl* | *-*-midipix*) + dnl This is only needed when the module 'fenv-exceptions-trapping' is + dnl in use. + m4_ifdef([g][l_FENV_EXCEPTIONS_TRAPPING], [ + AC_CACHE_CHECK([whether feraiseexcept works], + [gl_cv_func_feraiseexcept_works], + [case "$host_cpu" in + aarch64* | loongarch* | m68k* | s390* | sh4* | *86* | sparc*) + gl_cv_func_feraiseexcept_works="guessing no" ;; + *) + gl_cv_func_feraiseexcept_works="guessing yes" ;; + esac + ]) + case "$gl_cv_func_feraiseexcept_works" in + *yes) ;; + *) REPLACE_FERAISEEXCEPT=1 ;; + esac + ]) + ;; + powerpc*-*-aix*) + dnl On AIX, the unit test test-fenv-except-state-1 fails if we don't + dnl override feraiseexcept. XXX Not reproducible any more. + dnl REPLACE_FERAISEEXCEPT=1 + ;; + *86*-*-mingw* | *86*-*-windows*) + dnl On MSVC 14/clang, without this override, there are test failures + dnl (but not with MSVC 14 itself). Maybe fetestexcept does not consider + dnl the exception flags in the SSE unit? It's not clear. + AC_CACHE_CHECK([whether fetestexcept works], + [gl_cv_func_fetestexcept_works], + [AC_EGREP_CPP([Problem], [ +#ifndef __MINGW32__ + Problem +#endif + ], + [gl_cv_func_fetestexcept_works="guessing no"], + [gl_cv_func_fetestexcept_works="guessing yes"]) + ]) + case "$gl_cv_func_fetestexcept_works" in + *yes) ;; + *) REPLACE_FERAISEEXCEPT=1 ;; + esac + ;; + esac + if test $REPLACE_FECLEAREXCEPT = 1 \ + && test $REPLACE_FETESTEXCEPT = 1 \ + && test $REPLACE_FERAISEEXCEPT = 1; then + FENV_EXCEPTIONS_TRACKING_LIBM= + else + dnl It needs linking with -lm on + dnl glibc, FreeBSD, NetBSD, OpenBSD, AIX, HP-UX, IRIX, Solaris, Android. + if test $gl_cv_func_feraiseexcept_no_libm = yes; then + FENV_EXCEPTIONS_TRACKING_LIBM= + else + FENV_EXCEPTIONS_TRACKING_LIBM=-lm + fi + fi + else + HAVE_FECLEAREXCEPT=0 + HAVE_FETESTEXCEPT=0 + HAVE_FERAISEEXCEPT=0 + case "$host" in + alpha*-*-linux*) + dnl For feraiseexcept, which we take from libm. + FENV_EXCEPTIONS_TRACKING_LIBM=-lm + ;; + *) + FENV_EXCEPTIONS_TRACKING_LIBM= + ;; + esac + fi + if { test $HAVE_FECLEAREXCEPT = 0 || test $REPLACE_FECLEAREXCEPT = 1; } \ + || { test $HAVE_FETESTEXCEPT = 0 || test $REPLACE_FETESTEXCEPT = 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_TRACKING_LIBM"; then + FENV_EXCEPTIONS_TRACKING_LIBM=-lm + fi + fi + AC_SUBST([FENV_EXCEPTIONS_TRACKING_LIBM]) +]) diff --git a/m4/fenv-exceptions.m4 b/m4/fenv-exceptions.m4 new file mode 100644 index 0000000000..be5e92c4ee --- /dev/null +++ b/m4/fenv-exceptions.m4 @@ -0,0 +1,23 @@ +# fenv-exceptions.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. + +dnl Prerequisites of most lib/fenv-except-*.c files. +AC_DEFUN([gl_PREREQ_FENV_EXCEPTIONS], +[ + gl_MATHFUNC([fpsetsticky], [fp_except_t], [(fp_except_t)], + [#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 + ]) + if test $gl_cv_func_fpsetsticky_no_libm = yes \ + || test $gl_cv_func_fpsetsticky_in_libm = yes; then + AC_DEFINE([HAVE_FPSETSTICKY], [1], + [Define to 1 if you have the 'fpsetsticky' function.]) + fi +]) diff --git a/modules/fenv-exceptions-tracking-c99 b/modules/fenv-exceptions-tracking-c99 new file mode 100644 index 0000000000..2e800cc47b --- /dev/null +++ b/modules/fenv-exceptions-tracking-c99 @@ -0,0 +1,50 @@ +Description: +Functions for tracking which floating-point exceptions have occurred: +feclearexcept, feraiseexcept, fetestexcept. + +Files: +lib/fenv-except-tracking-clear.c +lib/fenv-except-tracking-test.c +lib/fenv-except-tracking-raise.c +lib/fenv-private.h +m4/fenv-exceptions-tracking.m4 +m4/fenv-exceptions.m4 +m4/mathfunc.m4 + +Depends-on: +fenv + +configure.ac: +gl_FENV_EXCEPTIONS_TRACKING +gl_CONDITIONAL([GL_COND_OBJ_FENV_EXCEPTIONS_TRACKING_CLEAR], + [test $HAVE_FECLEAREXCEPT = 0 || test $REPLACE_FECLEAREXCEPT = 1]) +gl_CONDITIONAL([GL_COND_OBJ_FENV_EXCEPTIONS_TRACKING_TEST], + [test $HAVE_FETESTEXCEPT = 0 || test $REPLACE_FETESTEXCEPT = 1]) +gl_CONDITIONAL([GL_COND_OBJ_FENV_EXCEPTIONS_TRACKING_RAISE], + [test $HAVE_FERAISEEXCEPT = 0 || test $REPLACE_FERAISEEXCEPT = 1]) +gl_FENV_MODULE_INDICATOR([feclearexcept]) +gl_FENV_MODULE_INDICATOR([fetestexcept]) +gl_FENV_MODULE_INDICATOR([feraiseexcept]) + +Makefile.am: +if GL_COND_OBJ_FENV_EXCEPTIONS_TRACKING_CLEAR +lib_SOURCES += fenv-except-tracking-clear.c +endif +if GL_COND_OBJ_FENV_EXCEPTIONS_TRACKING_TEST +lib_SOURCES += fenv-except-tracking-test.c +endif +if GL_COND_OBJ_FENV_EXCEPTIONS_TRACKING_RAISE +lib_SOURCES += fenv-except-tracking-raise.c +endif + +Include: +#include + +Link: +$(FENV_EXCEPTIONS_TRACKING_LIBM) + +License: +LGPLv2+ + +Maintainer: +all -- 2.39.5