]> Savannah Git Hosting - gnulib.git/commitdiff
fpe-tracking, fpe-trapping: New modules.
authorBruno Haible <bruno@clisp.org>
Thu, 12 Oct 2023 22:41:57 +0000 (00:41 +0200)
committerBruno Haible <bruno@clisp.org>
Thu, 12 Oct 2023 22:50:47 +0000 (00:50 +0200)
* lib/fpe-trapping.h: New file.
* m4/mathfunc.m4 (gl_MATHFUNC): Handle also the type fp_except_t.
* m4/fpe.m4: New file.
* modules/fpe-tracking: New file.
* modules/fpe-trapping: New file.

ChangeLog
lib/fpe-trapping.h [new file with mode: 0644]
m4/fpe.m4 [new file with mode: 0644]
m4/mathfunc.m4
modules/fpe-tracking [new file with mode: 0644]
modules/fpe-trapping [new file with mode: 0644]

index f0f23b8bbb92e9e95233264180ebad95f630cc38..a3d58dc308e59c020aab021623adb0383c784908 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2023-10-12  Bruno Haible  <bruno@clisp.org>
+
+       fpe-tracking, fpe-trapping: New modules.
+       * lib/fpe-trapping.h: New file.
+       * m4/mathfunc.m4 (gl_MATHFUNC): Handle also the type fp_except_t.
+       * m4/fpe.m4: New file.
+       * modules/fpe-tracking: New file.
+       * modules/fpe-trapping: New file.
+
 2023-10-12  Bruno Haible  <bruno@clisp.org>
 
        snan: Comments.
diff --git a/lib/fpe-trapping.h b/lib/fpe-trapping.h
new file mode 100644 (file)
index 0000000..6a63327
--- /dev/null
@@ -0,0 +1,614 @@
+/* Trapping 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/>.  */
+
+/* HAVE_FPE_TRAPPING
+   Defined to 1 when sigfpe_on_invalid is available.  */
+
+/* sigfpe_on_invalid
+   Enables a SIGFPE signal when an FE_INVALID exception occurs.
+   A SIGFPE signal by default terminates the program.
+   Returns >= 0 when successful, -1 upon failure.  */
+
+
+#define HAVE_FPE_TRAPPING 1
+
+#if HAVE_FEENABLEEXCEPT
+/* glibc, FreeBSD ≥ 6.0, NetBSD ≥ 7.0, OpenBSD ≥ 5.0, Cygwin ≥ 1.7.8, Android, Haiku.  */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  /* Clear FE_INVALID exceptions from past operations.  */
+  feclearexcept (FE_INVALID);
+
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     This call may fail on arm, arm64, riscv64 CPUs.
+     Also, possibly a bug in glibc/sysdeps/m68k/fpu/feenablxcpt.c: it sets
+     only bit 13, but should better set both bit 14 and bit 13 of the
+     control register.  */
+  int ret = feenableexcept (FE_INVALID);
+  if (ret == -1)
+    return -1;
+
+  #if ((__GLIBC__ == 2 && __GLIBC_MINOR__ < 20) || defined __FreeBSD__ || defined __NetBSD__) && defined __aarch64__
+  /* Work around a bug with glibc 2.19 and FreeBSD 12.2 on arm64 CPUs:
+     feenableexcept returns success even if the CPU does not support the
+     request.  */
+  fenv_t env;
+  fegetenv (&env);
+  #if __GLIBC__ >= 2 || defined __NetBSD__
+  /* fenv_t is a struct { unsigned int __fpcr, __fpsr; }  */
+  if (((FE_INVALID << 8) & ~env.__fpcr) != 0)
+    return -1;
+  #else /* defined __FreeBSD__ */
+  /* fenv_t is an 'uint64_t' that merges fpcr and fpsr.
+     But watch out for this incompatible change:
+     <https://cgit.freebsd.org/src/commit/?id=34cc08e336987a8ebc316595e3f552a4c09f1fd4>  */
+  #if __FreeBSD__ < 14
+  if (((FE_INVALID << 8) & ~env) != 0)
+  #else
+  if (((FE_INVALID << 8) & ~(env >> 32)) != 0)
+  #endif
+    return -1;
+  #endif
+  #endif
+
+  #if (defined __FreeBSD__ || defined __NetBSD__) && defined __arm__
+  /* Work around a bug with FreeBSD 12.2 on arm64 CPUs:
+     feenableexcept returns success even if the CPU does not support the
+     request.  */
+  /* Test whether fpscr was actually changed as desired.  */
+  fenv_t env;
+  fegetenv (&env);
+  if (((FE_INVALID << 8) & ~env) != 0)
+    return -1;
+  #endif
+
+  return 0;
+}
+
+/* But it does not work on RISC-V.  That's because the fcsr register has only
+   bits for floating-point exception status, but no bits for trapping
+   floating-point exceptions.  */
+# if defined __riscv
+#  undef HAVE_FPE_TRAPPING
+# endif
+
+#elif HAVE_FPSETMASK
+/* FreeBSD ≥ 6.0, NetBSD ≥ 1.4, OpenBSD ≥ 3.1, IRIX, Solaris, Minix ≥ 3.2.  */
+
+# include <ieeefp.h>
+  /* The type is called 'fp_except_t' on FreeBSD, but 'fp_except' on
+     all other systems.  */
+# if !defined __FreeBSD__
+#  define fp_except_t fp_except
+# endif
+
+static int
+sigfpe_on_invalid ()
+{
+  fpsetmask (fpgetmask () | FP_X_INV);
+
+  return 0;
+}
+
+#elif HAVE_FESETTRAPENABLE
+/* HP-UX, QNX */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fesettrapenable (fegettrapenable () | FE_INVALID);
+
+  return 0;
+}
+
+#elif defined _AIX
+/* AIX */
+
+# include <fptrap.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  /* Enable precise trapping mode.
+     Documentation: <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-trap-subroutine>  */
+  fp_trap (FP_TRAP_SYNC);
+  /* Documentation: <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-any-enable-fp-is-enabled-fp-enable-all-fp-enable-fp-disable-all-fp-disable-subroutine>  */
+  fp_enable (TRP_INVALID);
+
+  return 0;
+}
+
+#elif __MINGW32__ && defined __x86_64__
+/* mingw/x86_64 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  /* Clear FE_INVALID exceptions from past operations.  */
+  feclearexcept (FE_INVALID);
+
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fctrl bits 5..2,0 indicate which floating-point exceptions shall, when
+     occurring in the 387 compatible floating-point unit, trigger a trap rather
+     than merely set the corresponding bit in the fstat register.
+     mxcsr bits 12..9,7 indicate which floating-point exceptions shall, when
+     occurring in the SSE registers floating-point unit, trigger a trap rather
+     than merely set the corresponding bit in the lower part of the mxcsr
+     register.  */
+  /* mingw's _controlfp_s implementation
+     <https://github.com/mingw-w64/mingw-w64/blob/master/mingw-w64-crt/secapi/_controlfp_s.c>
+     is broken.  This code here works.  */
+  {
+    unsigned short fctrl, orig_fctrl;
+    unsigned int mxcsr, orig_mxcsr;
+
+    __asm__ __volatile__ ("fstcw %0" : "=m" (*&fctrl));
+    __asm__ __volatile__ ("stmxcsr %0" : "=m" (*&mxcsr));
+    orig_fctrl = fctrl;
+    orig_mxcsr = mxcsr;
+    fctrl &= ~FE_INVALID;
+    mxcsr &= ~(FE_INVALID << 7);
+    if (!(fctrl == orig_fctrl && mxcsr == orig_mxcsr))
+      {
+        __asm__ __volatile__ ("fldcw %0" : : "m" (*&fctrl));
+        __asm__ __volatile__ ("ldmxcsr %0" : : "m" (*&mxcsr));
+      }
+  }
+
+  return 0;
+}
+
+#elif defined _WIN32 && !defined __CYGWIN__
+/* native Windows */
+
+# include <float.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  /* Documentation:
+     <https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/controlfp-s>  */
+  unsigned int control;
+  if (_controlfp_s (&control, 0, 0) == 0)
+    if (_controlfp_s (&control, control & ~_EM_INVALID, _MCW_EM) == 0)
+      return 0;
+
+  return -1;
+}
+
+#elif MUSL_LIBC && defined __x86_64__
+/* musl libc/x86_64 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fstat bits 5..2,0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero, in the 387 compatible
+     floating-point unit.
+     mxcsr bits 5..2,0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero, in the SSE registers
+     floating-point unit.  */
+  env.__status_word &= ~FE_INVALID;
+  env.__mxcsr &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fctrl bits 5..2,0 indicate which floating-point exceptions shall, when
+     occurring in the 387 compatible floating-point unit, trigger a trap rather
+     than merely set the corresponding bit in the fstat register.
+     mxcsr bits 12..9,7 indicate which floating-point exceptions shall, when
+     occurring in the SSE registers floating-point unit, trigger a trap rather
+     than merely set the corresponding bit in the lower part of the mxcsr
+     register.  */
+  env.__control_word &= ~FE_INVALID;
+  env.__mxcsr &= ~(FE_INVALID << 7);
+
+  if (!(env.__control_word == orig_env.__control_word
+        && env.__status_word == orig_env.__status_word
+        && env.__mxcsr == orig_env.__mxcsr))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#elif MUSL_LIBC && defined __i386__
+/* musl libc/x86 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* The mxcsr register exists, like for x86_64, but only if the CPU supports
+     SSE instructions.  Here it is not part of fenv_t.  Instead, it is
+     transparently handled by the fegetenv and fesetenv functions.  */
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fstat bits 5..2,0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero, in the 387 compatible
+     floating-point unit.  */
+  env.__status_word &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fctrl bits 5..2,0 indicate which floating-point exceptions shall, when
+     occurring in the 387 compatible floating-point unit, trigger a trap rather
+     than merely set the corresponding bit in the fstat register.  */
+  env.__control_word &= ~FE_INVALID;
+
+  if (!(env.__control_word == orig_env.__control_word
+        && env.__status_word == orig_env.__status_word))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#elif MUSL_LIBC && defined __aarch64__
+/* musl libc/arm64 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fpsr bits 4..0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero.  */
+  env.__fpsr &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fpcr bits 12..8 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fpsr register.  */
+  env.__fpcr |= (FE_INVALID << 8);
+
+  if (!(env.__fpsr == orig_env.__fpsr && env.__fpcr == orig_env.__fpcr))
+    {
+      fesetenv (&env);
+      /* Test whether __fpcr was actually changed as desired.  */
+      fenv_t new_env;
+      fegetenv (&new_env);
+      if (new_env.__fpcr != env.__fpcr)
+        return -1;
+    }
+
+  return 0;
+}
+
+#elif MUSL_LIBC && defined __arm__
+/* musl libc/arm */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env;
+  unsigned int orig_fpscr;
+  fegetenv (&env);
+  orig_fpscr = env.__cw;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fpscr bits 4..0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero.  */
+  env.__cw &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fpscr bits 12..8 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fpscr register.  */
+  env.__cw |= (FE_INVALID << 8);
+
+  if (!(env.__cw == orig_fpscr))
+    {
+      fesetenv (&env);
+      /* Test whether fpscr was actually changed as desired.  */
+      fenv_t new_env;
+      fegetenv (&new_env);
+      if (new_env.__cw != env.__cw)
+        return -1;
+    }
+
+  return 0;
+}
+
+#elif MUSL_LIBC && defined __m68k__
+/* musl libc/m68k */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fpsr bits 7..3 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero.  */
+  env.__status_register &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fpcr bits 15..8 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fpsr register:
+       - bit 15: branch/set on unordered
+       - bit 14: signaling not-a-number
+       - bit 13: operand error
+       - bit 12: overflow
+       - bit 11: underflow
+       - bit 10: divide by zero
+       - bit 9:  inexact operation
+       - bit 8:  inexact decimal input
+     Although (FE_INVALID << 6) has bit 13 set, we need to set bit 14 and
+     bit 13.  */
+  env.__control_register |= (1U << 14) | (FE_INVALID << 6);
+
+  if (!(env.__status_register == orig_env.__status_register
+        && env.__control_register == orig_env.__control_register))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#elif MUSL_LIBC && defined __mips__
+/* musl libc/mips64,mipsn32,mips32 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fcsr bits 6..2 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero.
+     fcsr bits 17..12 indicate which floating-point exceptions have occurred
+     in the most recent instruction.  */
+  env.__cw &= ~((FE_INVALID << 10) | FE_INVALID);
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fcsr bits 11..7 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fcsr register.  */
+  env.__cw |= (FE_INVALID << 5);
+
+  if (!(env.__cw == orig_env.__cw))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#elif MUSL_LIBC && (defined __powerpc__ || defined __powerpc64__)
+/* musl libc/powerpc64,powerpc */
+
+# include <fenv.h>
+# include <sys/prctl.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  /* fenv_t is a 'double'.  */
+  union { unsigned long long u; fenv_t f; } env, orig_env;
+  fegetenv (&env.f);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fpscr bits 28..25 indicate which floating-point exceptions, other than
+     FE_INVALID, have occurred since the respective bit was last set to zero.
+     fpscr bits 24..19, 10..8 do the same thing, for various kinds of Invalid
+     Operation.  fpscr bit 29 is the summary (the OR) of all these bits.
+     Instead of clearing FE_INVALID (= bit 29), we need to clear these
+     individual bits.  */
+  env.u &= ~FE_ALL_INVALID; /* not ~FE_INVALID ! */
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fpscr bits 7..3 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fpscr register.  */
+  env.u |= (FE_INVALID >> 22);
+
+  if (!(env.u == orig_env.u))
+    {
+      /* Enable precise trapping mode.  */
+      prctl (PR_SET_FPEXC, PR_FP_EXC_PRECISE);
+
+      fesetenv (&env.f);
+    }
+
+  return 0;
+}
+
+#elif MUSL_LIBC && (defined __s390__ || defined __s390x__)
+/* musl libc/s390x,s390 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fpc bits 23..19 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero.
+     fpc bits 15..11 are part of the "data exception code" (DXC) and have a
+     similar meaning if bits 9..8 are both zero.  */
+  env &= ~FE_INVALID;
+  if ((env & 0x00000300) == 0)
+    env &= ~(FE_INVALID >> 8);
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fpc bits 31..27 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fpc register.  */
+  env |= ((unsigned int) FE_INVALID << 8);
+
+  if (!(env == orig_env))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#elif MUSL_LIBC && defined __sh__
+/* musl libc/sh4 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fpscr bits 6..2 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero.  */
+  env.__cw &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fpscr bits 11..7 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fpscr register.  */
+  env.__cw |= (FE_INVALID << 5);
+
+  if (!(env.__cw == orig_env.__cw))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#elif (defined __APPLE__ && defined __MACH__) && (defined __i386__ || defined __x86_64__)
+/* Mac OS X/i386,x86_64 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fstat bits 5..2,0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero, in the 387 compatible
+     floating-point unit.
+     mxcsr bits 5..2,0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero, in the SSE registers
+     floating-point unit.  */
+  env.__status &= ~FE_INVALID;
+  env.__mxcsr &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fctrl bits 5..2,0 indicate which floating-point exceptions shall, when
+     occurring in the 387 compatible floating-point unit, trigger a trap rather
+     than merely set the corresponding bit in the fstat register.
+     mxcsr bits 12..9,7 indicate which floating-point exceptions shall, when
+     occurring in the SSE registers floating-point unit, trigger a trap rather
+     than merely set the corresponding bit in the lower part of the mxcsr
+     register.  */
+  env.__control &= ~FE_INVALID;
+  env.__mxcsr &= ~(FE_INVALID << 7);
+
+  if (!(env.__control == orig_env.__control
+        && env.__status == orig_env.__status
+        && env.__mxcsr == orig_env.__mxcsr))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#elif (defined __APPLE__ && defined __MACH__) && defined __aarch64__
+/* macOS/arm64 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fpsr bits 4..0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero.  */
+  env.__fpsr &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a SIGILL signal.
+     fpcr bits 12..8 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fpsr register.  */
+  env.__fpcr |= (FE_INVALID << 8); /* __fpcr_trap_invalid */
+
+  if (!(env.__fpsr == orig_env.__fpsr && env.__fpcr == orig_env.__fpcr))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#elif (defined __APPLE__ && defined __MACH__) && defined __arm__
+/* macOS/arm */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env;
+  unsigned int orig_fpscr;
+  fegetenv (&env);
+  orig_fpscr = env.__fpscr;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fpscr bits 4..0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero.  */
+  env.__fpscr &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a signal.
+     fpscr bits 12..8 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fpscr register.  */
+  env.__fpscr |= (FE_INVALID << 8); /* __fpscr_trap_invalid */
+
+  if (!(env.__fpscr == orig_fpscr))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#else
+
+# undef HAVE_FPE_TRAPPING
+
+#endif
diff --git a/m4/fpe.m4 b/m4/fpe.m4
new file mode 100644 (file)
index 0000000..f8a8103
--- /dev/null
+++ b/m4/fpe.m4
@@ -0,0 +1,115 @@
+# fpe.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.
+
+# Prerequisites for tracking FE_INVALID exceptions.
+AC_DEFUN_ONCE([gl_FPE_TRACKING],
+[
+  AC_CACHE_CHECK([whether FE_INVALID exceptions can be tracked],
+    [gl_cv_fenv_invalid_api],
+    [AC_COMPILE_IFELSE(
+       [AC_LANG_PROGRAM(
+          [[#include <fenv.h>
+          ]],
+          [[feclearexcept (FE_INVALID);
+          ]])
+       ],
+       [gl_cv_fenv_invalid_api=yes],
+       [gl_cv_fenv_invalid_api=no])
+    ])
+  if test $gl_cv_fenv_invalid_api = yes; then
+    AC_DEFINE([HAVE_FE_INVALID], [1],
+      [Define if FE_INVALID exceptions can be programmatically tracked.])
+    AC_CACHE_CHECK([whether feclearexcept can be used without linking with libm],
+      [gl_cv_func_feclearexcept_no_libm],
+      [AC_LINK_IFELSE(
+         [AC_LANG_PROGRAM(
+            [[#include <fenv.h>
+            ]],
+            [[feclearexcept (FE_INVALID);
+            ]])
+         ],
+         [gl_cv_func_feclearexcept_no_libm=yes],
+         [gl_cv_func_feclearexcept_no_libm=no])
+      ])
+    if test $gl_cv_func_feclearexcept_no_libm != yes; then
+      FPE_TRACKING_LIBM=-lm
+    else
+      FPE_TRACKING_LIBM=
+    fi
+  else
+    FPE_TRACKING_LIBM=
+  fi
+  AC_SUBST([FPE_TRACKING_LIBM])
+])
+
+# Prerequisites for turning FE_INVALID exceptions into a SIGFPE signal.
+AC_DEFUN_ONCE([gl_FPE_TRAPPING],
+[
+  dnl Persuade glibc <fenv.h> to declare feenableexcept().
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+
+  gl_MATHFUNC([feenableexcept], [int], [(int)], [#include <fenv.h>])
+  if test $gl_cv_func_feenableexcept_no_libm = yes \
+     || test $gl_cv_func_feenableexcept_in_libm = yes; then
+    AC_DEFINE([HAVE_FEENABLEEXCEPT], [1],
+      [Define to 1 if you have the 'feenableexcept' function.])
+  fi
+
+  gl_MATHFUNC([fpsetmask], [fp_except_t], [(fp_except_t)],
+    [#include <ieeefp.h>
+     /* The type is called 'fp_except_t' on FreeBSD, but 'fp_except' on
+        all other systems.  */
+     #if !defined __FreeBSD__
+     #define fp_except_t fp_except
+     #endif
+    ])
+  if test $gl_cv_func_fpsetmask_no_libm = yes \
+     || test $gl_cv_func_fpsetmask_in_libm = yes; then
+    AC_DEFINE([HAVE_FPSETMASK], [1],
+      [Define to 1 if you have the 'fpsetmask' function.])
+  fi
+
+  gl_MATHFUNC([fesettrapenable], [int], [(int)], [#include <fenv.h>])
+  if test $gl_cv_func_fesettrapenable_no_libm = yes \
+     || test $gl_cv_func_fesettrapenable_in_libm = yes; then
+    AC_DEFINE([HAVE_FESETTRAPENABLE], [1],
+      [Define to 1 if you have the 'fesettrapenable' function.])
+  fi
+
+  gl_MUSL_LIBC
+
+  if test $gl_cv_func_feenableexcept_no_libm = yes \
+     || test $gl_cv_func_feenableexcept_in_libm = yes; then
+    if test $gl_cv_func_feenableexcept_no_libm != yes; then
+      FPE_TRAPPING_LIBM=-lm
+    else
+      FPE_TRAPPING_LIBM=
+    fi
+  else
+    if test $gl_cv_func_fpsetmask_no_libm = yes \
+       || test $gl_cv_func_fpsetmask_in_libm = yes; then
+      if test $gl_cv_func_fpsetmask_no_libm != yes; then
+        FPE_TRAPPING_LIBM=-lm
+      else
+        FPE_TRAPPING_LIBM=
+      fi
+    else
+      if test $gl_cv_func_fesettrapenable_no_libm = yes \
+         || test $gl_cv_func_fesettrapenable_in_libm = yes; then
+        if test $gl_cv_func_fesettrapenable_no_libm != yes; then
+          FPE_TRAPPING_LIBM=-lm
+        else
+          FPE_TRAPPING_LIBM=
+        fi
+      else
+        FPE_TRAPPING_LIBM=
+      fi
+    fi
+  fi
+  AC_REQUIRE([gl_FPE_TRACKING])
+  FPE_TRAPPING_LIBM="$FPE_TRAPPING_LIBM $FPE_TRACKING_LIBM"
+  AC_SUBST([FPE_TRAPPING_LIBM])
+])
index 00216ceae6508fd27712449d3cbf44416cf6d800..5146e580c062fe96aa2f4d034c24d9a056a8429e 100644 (file)
@@ -1,4 +1,4 @@
-# mathfunc.m4 serial 13
+# mathfunc.m4 serial 14
 dnl Copyright (C) 2010-2023 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -39,12 +39,14 @@ AC_DEFUN([gl_MATHFUNC],
                                        [m4_bpatsubst(
                                           [m4_bpatsubst(
                                              [m4_bpatsubst(
-                                                [$3],
-                                                [int\( const\)? \*],
-                                                [&i_ret])],
-                                             [float\( const\)? \*], [&f_ret])],
-                                          [double\( const\)? \*], [&d_ret])],
-                                       [long double\( const\)? \*], [&l_ret])],
+                                                [m4_bpatsubst(
+                                                   [$3],
+                                                   [int\( const\)? \*],
+                                                   [&i_ret])],
+                                                [float\( const\)? \*], [&f_ret])],
+                                             [double\( const\)? \*], [&d_ret])],
+                                          [long double\( const\)? \*], [&l_ret])],
+                                       [fp_except_t], [1])],
                                     [int], [2])],
                                  [float], [1.618034f])],
                               [long double], [1.618033988749894848L])],
diff --git a/modules/fpe-tracking b/modules/fpe-tracking
new file mode 100644 (file)
index 0000000..d1afcf9
--- /dev/null
@@ -0,0 +1,27 @@
+Description:
+Tracking floating-point exceptions, i.e. determining whether
+a floating-point exception has occurred during a certain computation.
+
+Files:
+m4/fpe.m4
+
+Depends-on:
+
+configure.ac:
+gl_FPE_TRACKING
+
+Makefile.am:
+
+Include:
+#if HAVE_FE_INVALID
+# include <fenv.h>
+#endif
+
+Link:
+$(FPE_TRACKING_LIBM)
+
+License:
+GPL
+
+Maintainer:
+all
diff --git a/modules/fpe-trapping b/modules/fpe-trapping
new file mode 100644 (file)
index 0000000..d8acbe8
--- /dev/null
@@ -0,0 +1,30 @@
+Description:
+Trapping floating-point exceptions, i.e. turning a floating-point exception
+into a signal.
+
+Files:
+lib/fpe-trapping.h
+m4/fpe.m4
+m4/mathfunc.m4
+m4/musl.m4
+
+Depends-on:
+extensions
+
+configure.ac:
+gl_FPE_TRAPPING
+
+Makefile.am:
+lib_SOURCES += fpe-trapping.h
+
+Include:
+"fpe-trapping.h"
+
+Link:
+$(FPE_TRAPPING_LIBM)
+
+License:
+GPL
+
+Maintainer:
+all