1
0
mirror of https://git.savannah.gnu.org/git/gnulib.git synced 2025-08-08 17:22:05 +03:00

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.
This commit is contained in:
Bruno Haible
2023-10-28 18:31:22 +02:00
parent 72bd753df7
commit 2ce970a271
11 changed files with 1434 additions and 14 deletions

View File

@@ -1,3 +1,19 @@
2023-10-28 Bruno Haible <bruno@clisp.org>
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 <bruno@clisp.org>
fenv-rounding: Add tests.

View File

@@ -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
@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
Portability problems not fixed by Gnulib:
@itemize
@end itemize

View File

@@ -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

View File

@@ -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
@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
Portability problems not fixed by Gnulib:
@itemize
@end itemize

View File

@@ -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 <https://www.gnu.org/licenses/>. */
/* Based on glibc/sysdeps/<cpu>/{fclrexcpt.c,fraiseexcpt.c,ftestexcept.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
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 <bug-gnulib@gnu.org>."
# 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 <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
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 <float.h>
# include <fpxcp.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
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

View File

@@ -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 <https://www.gnu.org/licenses/>. */
/* Based on glibc/sysdeps/<cpu>/{fclrexcpt.c,fraiseexcpt.c,ftestexcept.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"
_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 <bug-gnulib@gnu.org>."
# 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

View File

@@ -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 <https://www.gnu.org/licenses/>. */
/* Based on glibc/sysdeps/<cpu>/{fclrexcpt.c,fraiseexcpt.c,ftestexcept.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
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 <bug-gnulib@gnu.org>."
# 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 <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
fetestexcept (int exceptions)
{
fp_except_t flags = fpgetsticky ();
return flags & FE_ALL_EXCEPT & exceptions;
}
# elif defined _AIX && defined __powerpc__ /* AIX */
# include <float.h>
# include <fpxcp.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
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

View File

@@ -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

View File

@@ -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 <fenv.h>])
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 <https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=1a2f40e5d14ed6450696feacf04fca5eeceae7ef>.
dnl On Cygwin 3.4.6/x86_64, feraiseexcept does not trigger traps.
dnl See <https://sourceware.org/pipermail/cygwin/2023-October/254667.html>.
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 <string.h> /* for __GNU_LIBRARY__ */
#ifdef __GNU_LIBRARY__
#include <features.h>
#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 <fenv.h>
]],
[[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])
])

23
m4/fenv-exceptions.m4 Normal file
View File

@@ -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 <ieeefp.h>
/* The type is called 'fp_except_t' on FreeBSD, but 'fp_except' on
all other systems. */
#if !defined __FreeBSD__
#define fp_except_t fp_except
#endif
])
if test $gl_cv_func_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
])

View File

@@ -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 <fenv.h>
Link:
$(FENV_EXCEPTIONS_TRACKING_LIBM)
License:
LGPLv2+
Maintainer:
all