These patch adds a module 'fenv-exceptions-tracking-c23', that implements the ISO C 23 API for tracking floating-point exceptions.
The new unit tests uncovered two glibc bugs. 2023-10-29 Bruno Haible <br...@clisp.org> fenv-exceptions-tracking-c23: Add tests. * tests/test-fenv-except-tracking-4.c: New file. * tests/test-fenv-except-tracking-5.c: New file. * modules/fenv-exceptions-tracking-c23-tests: New file. fenv-exceptions-tracking-c23: New module. * lib/fenv.in.h (fesetexcept): New declaration. * lib/fenv-except-tracking-set.c: New file, based on glibc. * lib/fenv-private.h (_GETMSR, _SETMSR, MSR_FP_EXC_MASK, PR_SET_FPEXC, PR_FP_EXC_DISABLED, PR_FP_EXC_NONRECOV, PR_FP_EXC_ASYNC, PR_FP_EXC_PRECISE, prctl) [NetBSD/powerpc]: New macros. * m4/fenv-exceptions-tracking-c23.m4: New file. * modules/fenv-exceptions-tracking-c23: New file. * doc/posix-functions/fesetexcept.texi: Mention the new module and the glibc bugs.
From b21a4c46d03409e91a44b0dad5fc1e868afe4112 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Sun, 29 Oct 2023 22:20:02 +0100 Subject: [PATCH 1/2] fenv-exceptions-tracking-c23: New module. * lib/fenv.in.h (fesetexcept): New declaration. * lib/fenv-except-tracking-set.c: New file, based on glibc. * lib/fenv-private.h (_GETMSR, _SETMSR, MSR_FP_EXC_MASK, PR_SET_FPEXC, PR_FP_EXC_DISABLED, PR_FP_EXC_NONRECOV, PR_FP_EXC_ASYNC, PR_FP_EXC_PRECISE, prctl) [NetBSD/powerpc]: New macros. * m4/fenv-exceptions-tracking-c23.m4: New file. * modules/fenv-exceptions-tracking-c23: New file. * doc/posix-functions/fesetexcept.texi: Mention the new module and the glibc bugs. --- ChangeLog | 13 + doc/posix-functions/fesetexcept.texi | 14 +- lib/fenv-except-tracking-set.c | 500 +++++++++++++++++++++++++++ lib/fenv-private.h | 27 ++ lib/fenv.in.h | 27 ++ m4/fenv-exceptions-tracking-c23.m4 | 86 +++++ modules/fenv-exceptions-tracking-c23 | 36 ++ 7 files changed, 699 insertions(+), 4 deletions(-) create mode 100644 lib/fenv-except-tracking-set.c create mode 100644 m4/fenv-exceptions-tracking-c23.m4 create mode 100644 modules/fenv-exceptions-tracking-c23 diff --git a/ChangeLog b/ChangeLog index 928ddf72b8..36256c30e4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2023-10-29 Bruno Haible <br...@clisp.org> + + fenv-exceptions-tracking-c23: New module. + * lib/fenv.in.h (fesetexcept): New declaration. + * lib/fenv-except-tracking-set.c: New file, based on glibc. + * lib/fenv-private.h (_GETMSR, _SETMSR, MSR_FP_EXC_MASK, PR_SET_FPEXC, + PR_FP_EXC_DISABLED, PR_FP_EXC_NONRECOV, PR_FP_EXC_ASYNC, + PR_FP_EXC_PRECISE, prctl) [NetBSD/powerpc]: New macros. + * m4/fenv-exceptions-tracking-c23.m4: New file. + * modules/fenv-exceptions-tracking-c23: New file. + * doc/posix-functions/fesetexcept.texi: Mention the new module and the + glibc bugs. + 2023-10-28 Bruno Haible <br...@clisp.org> fenv-exceptions-tracking-c99: Fix feraiseexcept (FE_OVERFLOW) on MSVC. diff --git a/doc/posix-functions/fesetexcept.texi b/doc/posix-functions/fesetexcept.texi index e1535f31a5..23c3ea0868 100644 --- a/doc/posix-functions/fesetexcept.texi +++ b/doc/posix-functions/fesetexcept.texi @@ -10,15 +10,21 @@ @url{https://www.gnu.org/software/libc/manual/html_node/Status-bit-operations.html}. @end ifnotinfo -Gnulib module: --- +Gnulib module: fenv-exceptions-tracking-c23 Portability problems fixed by Gnulib: @itemize +@item +This function is missing on all non-glibc platforms: +glibc 2.24, macOS 11.1, FreeBSD 13.0, NetBSD 9.0, OpenBSD 6.7, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, Solaris 11.4, Cygwin 2.9, mingw, MSVC 14, Android 9.0. +@item +This function triggers floating-point exception traps although it shouldn't, on +@c https://sourceware.org/bugzilla/show_bug.cgi?id=30989 +glibc 2.37/i386, +@c https://sourceware.org/bugzilla/show_bug.cgi?id=30988 +glibc 2.37/powerpc. @end itemize Portability problems not fixed by Gnulib: @itemize -@item -This function is missing on all non-glibc platforms: -glibc 2.24, macOS 11.1, FreeBSD 13.0, NetBSD 9.0, OpenBSD 6.7, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, Solaris 11.4, Cygwin 2.9, mingw, MSVC 14, Android 9.0. @end itemize diff --git a/lib/fenv-except-tracking-set.c b/lib/fenv-except-tracking-set.c new file mode 100644 index 0000000000..73b566cb01 --- /dev/null +++ b/lib/fenv-except-tracking-set.c @@ -0,0 +1,500 @@ +/* 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 <https://www.gnu.org/licenses/>. */ + +/* Based on glibc/sysdeps/<cpu>/fesetexcept.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 +fesetexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + +# if defined _MSC_VER + exceptions = exceptions_to_x86hardware (exceptions); + + /* Set the flags 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 + + /* We can set the flags in the 387 unit or in the SSE unit. + Either works, due to the way fetestexcept() is implemented. + Choose the simplest approach. */ +# if defined __x86_64__ || defined _M_X64 + /* Set the flags 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 + if (CPU_HAS_SSE ()) + { + /* Set the flags 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 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 |= exceptions; + if (env.__status_word != orig_status_word) + { + if ((~env.__control_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.4 does not allow it. */ + return -1; + } + __asm__ __volatile__ ("fldenv %0" : : "m" (*&env)); + } + } +# endif +# endif + + return 0; +} + +# elif defined __aarch64__ /* arm64 */ + +int +fesetexcept (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 +fesetexcept (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 +fesetexcept (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 +fesetexcept (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 +fesetexcept (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 +fesetexcept (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 +fesetexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned int fcsr, orig_fcsr; + _FPU_GETCW (orig_fcsr); + fcsr = orig_fcsr | exceptions; + if (fcsr != orig_fcsr) + _FPU_SETCW (fcsr); + + return 0; +} + +# elif defined __loongarch__ + +int +fesetexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + unsigned int fcsr, orig_fcsr; + _FPU_GETCW (orig_fcsr); + fcsr = orig_fcsr | exceptions; + if (fcsr != orig_fcsr) + _FPU_SETCW (fcsr); + + return 0; +} + +# elif defined __powerpc__ + +int +fesetexcept (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; + + 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)) + { + if (memenv.u & (exceptions >> 22)) + { + /* Setting the exception flags may trigger a trap. + ISO C 23 § 7.6.4.4 does not allow it. */ + return -1; + } + _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 +fesetexcept (int exceptions) +{ + /* This is identical to feraiseexcept(), because the hardware does not + support trapping on floating-point exceptions. */ + exceptions &= FE_ALL_EXCEPT; + __asm__ __volatile__ ("csrs fflags, %0" : : "r" (exceptions)); + return 0; +} + +# elif defined __s390__ || defined __s390x__ + +int +fesetexcept (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); +# else /* musl libc compatible FE_* values */ + fpc = orig_fpc | exceptions; +# endif + if (fpc != orig_fpc) + _FPU_SETCW (fpc); + + return 0; +} + +# elif defined __sh__ + +int +fesetexcept (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 +fesetexcept (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 <bug-gnulib@gnu.org>." +# endif +# define NEED_FALLBACK 1 + +# endif + +#else + +/* The compiler does not support __asm__ statements or equivalent + intrinsics. */ + +# if defined __sun && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)) && defined __SUNPRO_C +/* Solaris/i386, Solaris/x86_64. */ + +/* 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 +fesetexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + /* Set the flags in the SSE unit. */ + unsigned int mxcsr, orig_mxcsr; + getssecw (&orig_mxcsr); + mxcsr = orig_mxcsr | 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 <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 + +int +fesetexcept (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 <float.h> +# include <fpxcp.h> + +# include <fptrap.h> + +/* Documentation: + <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-clr-flag-fp-set-flag-fp-read-flag-fp-swap-flag-subroutine> */ + +int +fesetexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + /* 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 & FE_INVALID + ? exceptions_to_fpflag (exceptions & ~FE_INVALID) | (1U << 10) + : exceptions_to_fpflag (exceptions)); + if (f_to_set != 0) + { + if ((fegetexcept_impl () & exceptions) != 0) + { + /* Setting the exception flags may trigger a trap. + ISO C 23 § 7.6.4.4 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 +fesetexcept (int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + if (exceptions != 0) + return -1; + return 0; +} + +#endif diff --git a/lib/fenv-private.h b/lib/fenv-private.h index 286768cf10..b5e542b65d 100644 --- a/lib/fenv-private.h +++ b/lib/fenv-private.h @@ -287,6 +287,33 @@ extern void __ieee_set_fp_control (unsigned long); # define _FPU_SETCW_AS_DOUBLE(cw) \ __asm__ __volatile__ ("mtfsf 0xff,%0" : : "f" (cw)) +# if defined __NetBSD__ +/* Modifying the FE0 and FE1 bits of the machine state register (MSR) is + only possible from the kernel. NetBSD allows it to be done from user + space, by emulating the mfmsr and mtmsr instructions when they trap. + In other words, these instructions are actually system calls in NetBSD. */ +# define _GETMSR(msr) __asm__ __volatile__ ("mfmsr %0" : "=r" (msr)) +# define _SETMSR(msr) __asm__ __volatile__ ("mtmsr %0" : : "r" (msr)) +# define MSR_FP_EXC_MASK 0x00000900 +/* This allows us to simulate the Linux prctl() through a macro. */ +# define PR_SET_FPEXC 1 +# define PR_FP_EXC_DISABLED 0x00000000 /* FP exceptions disabled */ +# define PR_FP_EXC_NONRECOV 0x00000100 /* async non-recoverable exc. mode */ +# define PR_FP_EXC_ASYNC 0x00000800 /* async recoverable exception mode */ +# define PR_FP_EXC_PRECISE 0x00000900 /* precise exception mode */ +# define prctl(operation,arg) \ + do { \ + if ((operation) == PR_SET_FPEXC) \ + { \ + unsigned int local_msr; \ + _GETMSR (local_msr); \ + local_msr &= ~MSR_FP_EXC_MASK; \ + local_msr |= (arg) & MSR_FP_EXC_MASK; \ + _SETMSR (local_msr); \ + } \ + } while (0) +# endif + #elif defined __riscv /* fcsr bits 4..0 indicate which floating-point exceptions have occurred diff --git a/lib/fenv.in.h b/lib/fenv.in.h index 9662558805..0c119c5936 100644 --- a/lib/fenv.in.h +++ b/lib/fenv.in.h @@ -514,6 +514,33 @@ _GL_CXXALIAS_SYS (fetestexcept, int, (int exceptions)); _GL_CXXALIASWARN (fetestexcept); #endif +/* Added in ISO C 23 § 7.6.4 Floating-point exceptions. */ + +#if @GNULIB_FESETEXCEPT@ +/* Sets the specified exception flags, without triggering handlers or traps, + and returns 0. Upon failure, it returns non-zero. */ +# if @REPLACE_FESETEXCEPT@ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef fesetexcept +# define fesetexcept rpl_fesetexcept +# endif +_GL_FUNCDECL_RPL (fesetexcept, int, (int exceptions)); +_GL_CXXALIAS_RPL (fesetexcept, int, (int exceptions)); +# else +# if !@HAVE_FESETEXCEPT@ +_GL_FUNCDECL_SYS (fesetexcept, int, (int exceptions)); +# endif +_GL_CXXALIAS_SYS (fesetexcept, int, (int exceptions)); +# endif +_GL_CXXALIASWARN (fesetexcept); +#elif defined GNULIB_POSIXCHECK +# undef fesetexcept +# if HAVE_RAW_DECL_FESETEXCEPT +_GL_WARN_ON_USE (fesetexcept, "fesetexcept is unportable - " + "use gnulib module fenv-exceptions-tracking-c23 for portability"); +# endif +#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-c23.m4 b/m4/fenv-exceptions-tracking-c23.m4 new file mode 100644 index 0000000000..61c3363d33 --- /dev/null +++ b/m4/fenv-exceptions-tracking-c23.m4 @@ -0,0 +1,86 @@ +# fenv-exceptions-tracking-c23.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_TRACKING_C23], +[ + AC_REQUIRE([gl_FENV_H_DEFAULTS]) + AC_REQUIRE([AC_CANONICAL_HOST]) + + gl_MATHFUNC([fesetexcept], [int], [(int)], [#include <fenv.h>]) + if test $gl_cv_func_fesetexcept_no_libm != yes \ + && test $gl_cv_func_fesetexcept_in_libm != yes; then + HAVE_FESETEXCEPT=0 + else + dnl Persuade glibc <fenv.h> to declare feenableexcept(). + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + gl_MATHFUNC([feenableexcept], [int], [(int)], [#include <fenv.h>]) + dnl On glibc 2.37 for PowerPC and i386, fesetexcept may raise traps. + AC_CACHE_CHECK([whether fesetexcept is guaranteed non-trapping], + [gl_cv_func_fesetexcept_works], + [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 <fenv.h> + static volatile double a, b; + static volatile long double al, bl; + ]], + [[if (feclearexcept (FE_INVALID) == 0 + && feenableexcept (FE_INVALID) == 0 + && fesetexcept (FE_INVALID) == 0) + { + a = 1.0; b = a + a; + al = 1.0L; bl = al + al; + } + return 0; + ]]) + ], + [gl_cv_func_fesetexcept_works=yes], + [gl_cv_func_fesetexcept_works=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_fesetexcept_works="guessing no" ;; + *) + gl_cv_func_fesetexcept_works="guessing yes" ;; + esac + ;; + # If we don't know, obey --enable-cross-guesses. + *) gl_cv_func_fesetexcept_works="$gl_cross_guess_normal" ;; + esac + ]) + LIBS="$save_LIBS" + else + gl_cv_func_fesetexcept_works="guessing yes" + fi + ]) + case "$gl_cv_func_fesetexcept_works" in + *yes) ;; + *) REPLACE_FESETEXCEPT=1 ;; + esac + fi + + dnl Modify FENV_EXCEPTIONS_TRACKING_LIBM, set by gl_FENV_EXCEPTIONS_TRACKING. + AC_REQUIRE([gl_FENV_EXCEPTIONS_TRACKING]) + if test $HAVE_FESETEXCEPT = 0 || test $REPLACE_FESETEXCEPT = 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 +]) diff --git a/modules/fenv-exceptions-tracking-c23 b/modules/fenv-exceptions-tracking-c23 new file mode 100644 index 0000000000..a10145a24e --- /dev/null +++ b/modules/fenv-exceptions-tracking-c23 @@ -0,0 +1,36 @@ +Description: +Functions for tracking which floating-point exceptions have occurred: +feclearexcept, fesetexcept, feraiseexcept, fetestexcept. + +Files: +lib/fenv-except-tracking-set.c +lib/fenv-private.h +m4/fenv-exceptions-tracking-c23.m4 +m4/mathfunc.m4 + +Depends-on: +fenv +fenv-exceptions-tracking-c99 + +configure.ac: +gl_FENV_EXCEPTIONS_TRACKING_C23 +gl_CONDITIONAL([GL_COND_OBJ_FENV_EXCEPTIONS_TRACKING_C23], + [test $HAVE_FESETEXCEPT = 0 || test $REPLACE_FESETEXCEPT = 1]) +gl_FENV_MODULE_INDICATOR([fesetexcept]) + +Makefile.am: +if GL_COND_OBJ_FENV_EXCEPTIONS_TRACKING_C23 +lib_SOURCES += fenv-except-tracking-set.c +endif + +Include: +#include <fenv.h> + +Link: +$(FENV_EXCEPTIONS_TRACKING_LIBM) + +License: +LGPLv2+ + +Maintainer: +all -- 2.34.1
>From 1c0a1eaf2bac5aa5fc78239580338dda0953941b Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Sun, 29 Oct 2023 22:27:38 +0100 Subject: [PATCH 2/2] fenv-exceptions-tracking-c23: Add tests. * tests/test-fenv-except-tracking-4.c: New file. * tests/test-fenv-except-tracking-5.c: New file. * modules/fenv-exceptions-tracking-c23-tests: New file. --- ChangeLog | 5 ++ modules/fenv-exceptions-tracking-c23-tests | 21 +++++ tests/test-fenv-except-tracking-4.c | 94 ++++++++++++++++++++++ tests/test-fenv-except-tracking-5.c | 79 ++++++++++++++++++ 4 files changed, 199 insertions(+) create mode 100644 modules/fenv-exceptions-tracking-c23-tests create mode 100644 tests/test-fenv-except-tracking-4.c create mode 100644 tests/test-fenv-except-tracking-5.c diff --git a/ChangeLog b/ChangeLog index 36256c30e4..26686c3fb8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2023-10-29 Bruno Haible <br...@clisp.org> + fenv-exceptions-tracking-c23: Add tests. + * tests/test-fenv-except-tracking-4.c: New file. + * tests/test-fenv-except-tracking-5.c: New file. + * modules/fenv-exceptions-tracking-c23-tests: New file. + fenv-exceptions-tracking-c23: New module. * lib/fenv.in.h (fesetexcept): New declaration. * lib/fenv-except-tracking-set.c: New file, based on glibc. diff --git a/modules/fenv-exceptions-tracking-c23-tests b/modules/fenv-exceptions-tracking-c23-tests new file mode 100644 index 0000000000..4c39f00ec0 --- /dev/null +++ b/modules/fenv-exceptions-tracking-c23-tests @@ -0,0 +1,21 @@ +Files: +tests/test-fenv-except-tracking-4.c +tests/test-fenv-except-tracking-5.c +tests/macros.h +m4/musl.m4 + +Depends-on: +fpe-trapping + +configure.ac: +gl_MUSL_LIBC + +Makefile.am: +TESTS += \ + test-fenv-except-tracking-4 \ + test-fenv-except-tracking-5 +check_PROGRAMS += \ + test-fenv-except-tracking-4 \ + test-fenv-except-tracking-5 +test_fenv_except_tracking_4_LDADD = $(LDADD) @FENV_EXCEPTIONS_TRACKING_LIBM@ +test_fenv_except_tracking_5_LDADD = $(LDADD) @FENV_EXCEPTIONS_TRACKING_LIBM@ @FPE_TRAPPING_LIBM@ diff --git a/tests/test-fenv-except-tracking-4.c b/tests/test-fenv-except-tracking-4.c new file mode 100644 index 0000000000..30c4f02c38 --- /dev/null +++ b/tests/test-fenv-except-tracking-4.c @@ -0,0 +1,94 @@ +/* Test of tracking of floating-point exceptions. + Copyright (C) 2023 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>, 2023. */ + +#include <config.h> + +/* Specification. */ +#include <fenv.h> + +#include "macros.h" + +/* Check that fesetexcept() works. */ + +int +main () +{ + /* Test setting all exception flags. */ + if (feraiseexcept (FE_ALL_EXCEPT) != 0) + { + fputs ("Skipping test: floating-point exceptions are not supported on this machine.\n", stderr); + return 77; + } + + /* Clear all exception flags. */ + ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0); + + + /* Test setting just one exception flag: FE_INVALID. */ + ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0); + ASSERT (fesetexcept (FE_INVALID) == 0); + ASSERT (fetestexcept (FE_ALL_EXCEPT) == FE_INVALID); + ASSERT (fetestexcept (FE_INVALID) == FE_INVALID); + ASSERT (fetestexcept (FE_DIVBYZERO) == 0); + ASSERT (fetestexcept (FE_OVERFLOW) == 0); + ASSERT (fetestexcept (FE_UNDERFLOW) == 0); + ASSERT (fetestexcept (FE_INEXACT) == 0); + + /* Test setting just one exception flag: FE_DIVBYZERO. */ + ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0); + ASSERT (fesetexcept (FE_DIVBYZERO) == 0); + ASSERT (fetestexcept (FE_ALL_EXCEPT) == FE_DIVBYZERO); + ASSERT (fetestexcept (FE_INVALID) == 0); + ASSERT (fetestexcept (FE_DIVBYZERO) == FE_DIVBYZERO); + ASSERT (fetestexcept (FE_OVERFLOW) == 0); + ASSERT (fetestexcept (FE_UNDERFLOW) == 0); + ASSERT (fetestexcept (FE_INEXACT) == 0); + + /* Test setting just one exception flag: FE_OVERFLOW. */ + ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0); + ASSERT (fesetexcept (FE_OVERFLOW) == 0); + ASSERT (fetestexcept (FE_ALL_EXCEPT) == FE_OVERFLOW); + ASSERT (fetestexcept (FE_INVALID) == 0); + ASSERT (fetestexcept (FE_DIVBYZERO) == 0); + ASSERT (fetestexcept (FE_OVERFLOW) == FE_OVERFLOW); + ASSERT (fetestexcept (FE_UNDERFLOW) == 0); + ASSERT (fetestexcept (FE_INEXACT) == 0); + + /* Test setting just one exception flag: FE_UNDERFLOW. */ + ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0); + ASSERT (fesetexcept (FE_UNDERFLOW) == 0); + ASSERT (fetestexcept (FE_ALL_EXCEPT) == FE_UNDERFLOW); + ASSERT (fetestexcept (FE_INVALID) == 0); + ASSERT (fetestexcept (FE_DIVBYZERO) == 0); + ASSERT (fetestexcept (FE_OVERFLOW) == 0); + ASSERT (fetestexcept (FE_UNDERFLOW) == FE_UNDERFLOW); + ASSERT (fetestexcept (FE_INEXACT) == 0); + + /* Test setting just one exception flag: FE_INEXACT. */ + ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0); + ASSERT (fesetexcept (FE_INEXACT) == 0); + ASSERT (fetestexcept (FE_ALL_EXCEPT) == FE_INEXACT); + ASSERT (fetestexcept (FE_INVALID) == 0); + ASSERT (fetestexcept (FE_DIVBYZERO) == 0); + ASSERT (fetestexcept (FE_OVERFLOW) == 0); + ASSERT (fetestexcept (FE_UNDERFLOW) == 0); + ASSERT (fetestexcept (FE_INEXACT) == FE_INEXACT); + + + return 0; +} diff --git a/tests/test-fenv-except-tracking-5.c b/tests/test-fenv-except-tracking-5.c new file mode 100644 index 0000000000..0efb5e6664 --- /dev/null +++ b/tests/test-fenv-except-tracking-5.c @@ -0,0 +1,79 @@ +/* Test of tracking of floating-point exceptions. + Copyright (C) 2023 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>, 2023. */ + +#include <config.h> + +/* Specification. */ +#include <fenv.h> + +#include <stdio.h> + +#include "fpe-trapping.h" +#include "macros.h" + +/* musl libc does not support floating-point exception trapping, even where + the hardware supports it. See + <https://wiki.musl-libc.org/functional-differences-from-glibc.html> */ +#if HAVE_FPE_TRAPPING && (!MUSL_LIBC || GNULIB_FEENABLEEXCEPT) + +/* Check that fesetexcept() does not trigger a trap. */ + +static volatile double a, b; +static volatile long double al, bl; + +int +main () +{ + /* Clear FE_INVALID exceptions from past operations. */ + feclearexcept (FE_INVALID); + + /* An FE_INVALID exception shall trigger a SIGFPE signal, which by default + terminates the program. */ + if (sigfpe_on_invalid () < 0) + { + fputs ("Skipping test: trapping floating-point exceptions are not supported on this machine.\n", stderr); + return 77; + } + + /* Attempt to set the FE_INVALID exception flag. */ + _GL_UNUSED int rc = fesetexcept (FE_INVALID); + /* On older i386 and on PowerPC, there is no way to implement + fesetexcept() such that it does not trigger a trap. fesetexcept() + is expected to fail in this case. */ +# if !((defined __i386 || defined _M_IX86) || defined __powerpc__) + ASSERT (rc == 0); +# endif + + /* Do a harmless floating-point operation (since on some CPUs, floating-point + exceptions trigger a trap only at the next floating-point operation). */ + a = 1.0; b = a + a; + al = 1.0L; bl = al + al; + + return 0; +} + +#else + +int +main () +{ + fputs ("Skipping test: feenableexcept or fpsetmask or fp_enable not available\n", stderr); + return 77; +} + +#endif -- 2.34.1