From 404ce57f190e137da6197e2e8094d3d779472a04 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Fri, 27 Oct 2023 20:16:50 +0200 Subject: [PATCH] fenv-rounding: New module. * lib/fenv.in.h (fegetround, fesetround): New declarations. * lib/fenv-private.h: New file, based on glibc. * lib/fenv-round.c: New file, based on glibc. * m4/mathfunc.m4 (gl_MATHFUNC): Handle also the type fp_rnd. * m4/fenv-rounding.m4: New file. * modules/fenv (Depends-on): Add snippet/c++defs. (Makefile.am): Substitute $(CXXDEFS_H) into fenv.h. * modules/fenv-rounding: New file. * doc/posix-functions/fegetround.texi: Mention the new module. * doc/posix-functions/fesetround.texi: Likewise. --- ChangeLog | 14 + doc/posix-functions/fegetround.texi | 8 +- doc/posix-functions/fesetround.texi | 11 +- lib/fenv-private.h | 379 ++++++++++++++++++ lib/fenv-round.c | 572 ++++++++++++++++++++++++++++ lib/fenv.in.h | 42 ++ m4/fenv-rounding.m4 | 77 ++++ m4/mathfunc.m4 | 16 +- modules/fenv | 4 +- modules/fenv-rounding | 36 ++ 10 files changed, 1143 insertions(+), 16 deletions(-) create mode 100644 lib/fenv-private.h create mode 100644 lib/fenv-round.c create mode 100644 m4/fenv-rounding.m4 create mode 100644 modules/fenv-rounding diff --git a/ChangeLog b/ChangeLog index 1b0201ce6c..b4c109b02a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2023-10-27 Bruno Haible + + fenv-rounding: New module. + * lib/fenv.in.h (fegetround, fesetround): New declarations. + * lib/fenv-private.h: New file, based on glibc. + * lib/fenv-round.c: New file, based on glibc. + * m4/mathfunc.m4 (gl_MATHFUNC): Handle also the type fp_rnd. + * m4/fenv-rounding.m4: New file. + * modules/fenv (Depends-on): Add snippet/c++defs. + (Makefile.am): Substitute $(CXXDEFS_H) into fenv.h. + * modules/fenv-rounding: New file. + * doc/posix-functions/fegetround.texi: Mention the new module. + * doc/posix-functions/fesetround.texi: Likewise. + 2023-10-27 Bruno Haible tests: Use C99 initializer syntax for memory_long_double. diff --git a/doc/posix-functions/fegetround.texi b/doc/posix-functions/fegetround.texi index 869698131f..c32eb4230e 100644 --- a/doc/posix-functions/fegetround.texi +++ b/doc/posix-functions/fegetround.texi @@ -4,15 +4,15 @@ POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/fegetround.html} -Gnulib module: --- +Gnulib module: fenv-rounding Portability problems fixed by Gnulib: @itemize +@item +This function is missing on some platforms: +FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4. @end itemize Portability problems not fixed by Gnulib: @itemize -@item -This function is missing on some platforms: -FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4. @end itemize diff --git a/doc/posix-functions/fesetround.texi b/doc/posix-functions/fesetround.texi index 2efc18f31f..f0c74c3467 100644 --- a/doc/posix-functions/fesetround.texi +++ b/doc/posix-functions/fesetround.texi @@ -4,15 +4,18 @@ POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/fesetround.html} -Gnulib module: --- +Gnulib module: fenv-rounding Portability problems fixed by Gnulib: @itemize +@item +This function is missing on some platforms: +FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4. +@item +This function does not work on some platforms: +MSVC 14. @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, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4. @end itemize diff --git a/lib/fenv-private.h b/lib/fenv-private.h new file mode 100644 index 0000000000..286768cf10 --- /dev/null +++ b/lib/fenv-private.h @@ -0,0 +1,379 @@ +/* Common definitions for the implementation of the various modules. + Copyright (C) 1997-2023 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +/* Based on glibc/sysdeps//{fpu_control.h,fenv_private.h,fenv_libc.h}. */ + +#if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86) + +# if !(defined __x86_64__ || defined _M_X64) +# if __GLIBC__ + (__GLIBC_MINOR__ >= 33) > 2 +/* glibc >= 2.33 has an API that tells us whether the CPU has an SSE unit. */ +# include +# elif defined __sun +/* Solaris has a global variable that tells us whether the CPU has an SSE unit. */ +extern int _sse_hw; +# endif +# endif + +/* CPU_HAS_SSE () returns true if the CPU has an SSE unit. */ +# if defined __x86_64__ || defined _M_X64 +# define CPU_HAS_SSE() 1 +# else +# if __GLIBC__ + (__GLIBC_MINOR__ >= 33) > 2 +# define CPU_HAS_SSE() CPU_FEATURE_PRESENT (SSE) +# elif defined __sun +# define CPU_HAS_SSE() _sse_hw +# else +/* Otherwise, we assume that the SSE unit is present. + Only very old 32-bit processors, before Pentium 4, don't have it. + Don't bother testing it, through a 'cpuid' instruction. */ +# define CPU_HAS_SSE() 1 +# endif +# endif + +/* fstat bits 5..2,0 indicate which floating-point exceptions have occurred + in the 387 compatible floating-point unit since the respective bit was last + set to zero. + mxcsr bits 5..2,0 indicate which floating-point exceptions have occurred + in the SSE floating-point unit since the respective bit was last set to + zero. */ +/* fctrl bits 5..2,0 indicate which floating-point exceptions shall, when + occurring in the 387 compatible floating-point unit, *not* 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 floating-point unit, *not* trigger a trap rather + than merely set the corresponding bit in the mxcsr register. */ + +/* Macros that access the control word of the 387 unit, the so-called fctrl + register. */ +# define _FPU_GETCW(cw) __asm__ __volatile__ ("fnstcw %0" : "=m" (*&cw)) +# define _FPU_SETCW(cw) __asm__ __volatile__ ("fldcw %0" : : "m" (*&cw)) + +/* Macros that access the status word of the 387 unit, the so-called fstat + register. */ +# define _FPU_GETSTAT(cw) __asm__ __volatile__ ("fnstsw %0" : "=m" (*&cw)) + +/* Macros that access the control and status word of the SSE unit, the mxcsr + register. */ +# if defined __GNUC__ || defined __clang__ +# define _FPU_GETSSECW(cw) __asm__ __volatile__ ("stmxcsr %0" : "=m" (*&cw)) +# define _FPU_SETSSECW(cw) __asm__ __volatile__ ("ldmxcsr %0" : : "m" (*&cw)) +# elif defined _MSC_VER +# include +/* Documentation: + + + + + */ +# define _FPU_GETSSECW(cw) ((cw) = _mm_getcsr ()) +# define _FPU_SETSSECW(cw) _mm_setcsr (cw) +# endif + +/* The floating-point environment of the 387 unit. */ +typedef struct + { + /* 7 32-bit words: */ + unsigned short __control_word; /* fctrl register */ + unsigned short __reserved1; + unsigned short __status_word; /* fstat register */ + unsigned short __reserved2; + unsigned int more[5]; + } +x86_387_fenv_t; + +# if defined _MSC_VER +/* The MSVC header files have different values for the floating-point exceptions + than all the other platforms. Define some handy macros for conversion. */ +# define exceptions_to_x86hardware(exceptions) \ + ( ((exceptions) & FE_INVALID ? 0x01 : 0) \ + | ((exceptions) & FE_DIVBYZERO ? 0x04 : 0) \ + | ((exceptions) & FE_OVERFLOW ? 0x08 : 0) \ + | ((exceptions) & FE_UNDERFLOW ? 0x10 : 0) \ + | ((exceptions) & FE_INEXACT ? 0x20 : 0)) +# define x86hardware_to_exceptions(fstat) \ + ( ((fstat) & 0x01 ? FE_INVALID : 0) \ + | ((fstat) & 0x04 ? FE_DIVBYZERO : 0) \ + | ((fstat) & 0x08 ? FE_OVERFLOW : 0) \ + | ((fstat) & 0x10 ? FE_UNDERFLOW : 0) \ + | ((fstat) & 0x20 ? FE_INEXACT : 0)) +# else +# define exceptions_to_x86hardware(exceptions) (exceptions) +# define x86hardware_to_exceptions(fstat) (fstat) +# endif + +/* When _MSC_VER is defined, the 387 compatible floating-point unit is *not* + in use. Only the SSE floating-point unit is used. This can be inferred + from two facts: + - sizeof (long double) == sizeof (double). That is, 'long double' + values are just 'double' values and can be processed in the SSE unit. + - After fegetenv (&env), the value of env._Fe_stat is *not* the fstat + register of the 387 unit. Rather, it is a artificial value. In + particular, (env._Fe_stat & 0x3f) is + == x86hardware_to_exceptions (_FPU_GETSSECW () & 0x3f). */ + +#elif defined __aarch64__ /* arm64 */ + +/* fpsr bits 4..0 indicate which floating-point exceptions have occurred + since the respective bit was last set to zero. */ +/* 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. */ + +# if __GNUC__ >= 6 +# define _FPU_GETCW(fpcr) (fpcr = __builtin_aarch64_get_fpcr ()) +# define _FPU_SETCW(fpcr) __builtin_aarch64_set_fpcr (fpcr) +# else +# define _FPU_GETCW(fpcr) \ + __asm__ __volatile__ ("mrs %0, fpcr" : "=r" (fpcr)) +# define _FPU_SETCW(fpcr) \ + __asm__ __volatile__ ("msr fpcr, %0" : : "r" (fpcr)) +# endif + +# if __GNUC__ >= 6 +# define _FPU_GETFPSR(fpsr) (fpsr = __builtin_aarch64_get_fpsr ()) +# define _FPU_SETFPSR(fpsr) __builtin_aarch64_set_fpsr (fpsr) +# else +# define _FPU_GETFPSR(fpsr) \ + __asm__ __volatile__ ("mrs %0, fpsr" : "=r" (fpsr)) +# define _FPU_SETFPSR(fpsr) \ + __asm__ __volatile__ ("msr fpsr, %0" : : "r" (fpsr)) +# endif + +#elif defined __arm__ + +/* fpscr bits 23..22 indicate the rounding direction. */ +/* fpscr bits 4..0 indicate which floating-point exceptions have occurred + since the respective bit was last set to zero. */ +/* 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. */ + +# if !defined __SOFTFP__ +# define _FPU_GETCW(cw) \ + __asm__ __volatile__ ("vmrs %0, fpscr" : "=r" (cw)) +# define _FPU_SETCW(cw) \ + __asm__ __volatile__ ("vmsr fpscr, %0" : : "r" (cw)) +# endif + +#elif defined __alpha + +/* System calls. */ +extern unsigned long __ieee_get_fp_control (void); +extern void __ieee_set_fp_control (unsigned long); + +# define _FPU_GETCW(fpcr) \ + __asm__ __volatile__ ("excb; mf_fpcr %0" : "=f" (fpcr)) +# define _FPU_SETCW(fpcr) \ + __asm__ __volatile__ ("mt_fpcr %0; excb" : : "f" (fpcr)) + +#elif defined __hppa + +/* Bits 31..27 of the first 32-bit word of %fr0 indicate which floating-point + exceptions have occurred since the respective bit was last set to zero. */ +/* Bits 4..0 of the first 32-bit word of %fr0 indicate which floating-point + exceptions shall, when occurring, trigger a trap rather than merely set the + corresponding flag bit. */ + +/* The status register is located in bits 0 to 31 of floating-point register 0. */ +# define _FPU_GETCW(cw) \ +({ \ + union { __extension__ unsigned long long __fpreg; unsigned int __halfreg[2]; } __fullfp; \ + /* Get the current status word. */ \ + __asm__ ("fstd %%fr0,0(%1)\n\t" \ + "fldd 0(%1),%%fr0\n\t" \ + : "=m" (__fullfp.__fpreg) : "r" (&__fullfp.__fpreg) : "%r0"); \ + cw = __fullfp.__halfreg[0]; \ +}) +# define _FPU_SETCW(cw) \ +({ \ + union { __extension__ unsigned long long __fpreg; unsigned int __halfreg[2]; } __fullfp; \ + /* Get the current status word and set the control word. */ \ + __asm__ ("fstd %%fr0,0(%1)\n\t" \ + : "=m" (__fullfp.__fpreg) : "r" (&__fullfp.__fpreg) : "%r0"); \ + __fullfp.__halfreg[0] = cw; \ + __asm__ ("fldd 0(%1),%%fr0\n\t" \ + : : "m" (__fullfp.__fpreg), "r" (&__fullfp.__fpreg) : "%r0" ); \ +}) + +#elif defined __ia64__ + +/* fpsr bits 12..9,7 indicate which floating-point exceptions have occurred + since the respective bit was last set to zero. */ +/* fpsr bits 5..2,0 indicate which floating-point exceptions shall, when + occurring, *not* trigger a trap rather than merely set the corresponding + bit in the fpsr register. */ + +# define _FPU_GETCW(fpsr) \ + __asm__ __volatile__ ("mov.m %0=ar.fpsr" : "=r" (fpsr)) +# define _FPU_SETCW(fpsr) \ + __asm__ __volatile__ ("mov.m ar.fpsr=%0" :: "r" (fpsr) : "memory") + +#elif defined __m68k__ + +/* fpsr bits 7..3 indicate which floating-point exceptions have occurred + since the respective bit was last set to zero. */ +/* 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 + FE_INVALID corresponds to all three: bit 15, bit 14, bit 13. */ + +# define _FPU_GETCW(cw) __asm__ __volatile__ ("fmove%.l %!, %0" : "=dm" (cw)) +# define _FPU_SETCW(cw) __asm__ __volatile__ ("fmove%.l %0, %!" : : "dm" (cw)) + +# define _FPU_GETFPSR(cw) __asm__ __volatile__ ("fmove%.l %/fpsr, %0" : "=dm" (cw)) +# define _FPU_SETFPSR(cw) __asm__ __volatile__ ("fmove%.l %0, %/fpsr" : : "dm" (cw)) + +#elif defined __mips__ + +/* 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. */ +/* 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. */ + +# define _FPU_GETCW(cw) __asm__ __volatile__ ("cfc1 %0,$31" : "=r" (cw)) +# define _FPU_SETCW(cw) __asm__ __volatile__ ("ctc1 %0,$31" : : "r" (cw)) + +#elif defined __loongarch__ + +/* fcsr0 bits 20..16 indicate which floating-point exceptions have occurred + since the respective bit was last set to zero. + fcsr0 bits 28..24 indicate which floating-point exceptions have occurred + in the most recent instruction. */ +/* fcsr0 bits 4..0 indicate which floating-point exceptions shall, when + occurring, trigger a trap rather than merely set the corresponding bit + in the fcsr0 register. */ + +# define _FPU_GETCW(cw) __asm__ __volatile__ ("movfcsr2gr %0,$r0" : "=r" (cw)) +# define _FPU_SETCW(cw) __asm__ __volatile__ ("movgr2fcsr $r0,%0" : : "r" (cw)) + +#elif defined __powerpc__ + +/* 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. */ +/* 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. */ + +# define _FPU_GETCW_AS_DOUBLE(cw) \ + do { double env; __asm__ __volatile__ ("mffs %0" : "=f" (env)); cw = env; } \ + while (0) +# define _FPU_SETCW_AS_DOUBLE(cw) \ + __asm__ __volatile__ ("mtfsf 0xff,%0" : : "f" (cw)) + +#elif defined __riscv + +/* fcsr bits 4..0 indicate which floating-point exceptions have occurred + since the respective bit was last set to zero. */ + +/* Trapping of floating-point exceptions 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. */ + +#elif defined __s390__ || defined __s390x__ + +/* 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. */ +/* 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. */ + +# define _FPU_GETCW(cw) __asm__ __volatile__ ("efpc %0" : "=d" (cw)) +# define _FPU_SETCW(cw) __asm__ __volatile__ ("sfpc %0" : : "d" (cw)) + +#elif defined __sh__ + +/* fpscr bits 6..2 indicate which floating-point exceptions have occurred + since the respective bit was last set to zero. */ +/* 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. */ + +# define _FPU_GETCW(cw) __asm__ ("sts fpscr,%0" : "=r" (cw)) +# define _FPU_SETCW(cw) __asm__ ("lds %0,fpscr" : : "r" (cw)) + +#elif defined __sparc + +/* fsr bits 9..5 indicate which floating-point exceptions have occurred + since the respective bit was last set to zero. */ +/* fsr bits 27..23 indicate which floating-point exceptions shall, when + occurring, trigger a trap rather than merely set the corresponding bit + in the fsr register. */ + +# if defined __sparcv9 || defined __arch64__ /* sparc64 */ +# define _FPU_GETCW(X) __asm__ __volatile__ ("stx %%fsr,%0" : "=m" (X)) +# define _FPU_SETCW(X) __asm__ __volatile__ ("ldx %0,%%fsr" : : "m" (X)) +# else +# define _FPU_GETCW(X) __asm__ __volatile__ ("st %%fsr,%0" : "=m" (X)) +# define _FPU_SETCW(X) __asm__ __volatile__ ("ld %0,%%fsr" : : "m" (X)) +# endif + +#endif + +#if defined _AIX && defined __powerpc__ /* AIX */ + +/* defines a type fpflag_t and macros FP_*. */ + +/* Convert from an 'int exceptions' to an fpflag_t. */ +# if 0 /* Unoptimized */ +# define exceptions_to_fpflag(exceptions) \ + ( ((exceptions) & FE_INVALID ? FP_INVALID : 0) \ + | ((exceptions) & FE_DIVBYZERO ? FP_DIV_BY_ZERO : 0) \ + | ((exceptions) & FE_OVERFLOW ? FP_OVERFLOW : 0) \ + | ((exceptions) & FE_UNDERFLOW ? FP_UNDERFLOW : 0) \ + | ((exceptions) & FE_INEXACT ? FP_INEXACT : 0)) +# else /* Optimized */ +# define exceptions_to_fpflag(exceptions) \ + ((exceptions) & FE_ALL_EXCEPT) +# endif + +/* Convert from an fpflag_t to an 'int exceptions'. */ +# if 0 /* Unoptimized */ +# define fpflag_to_exceptions(f) \ + ( ((f) & FP_INVALID ? FE_INVALID : 0) \ + | ((f) & FP_DIV_BY_ZERO ? FE_DIVBYZERO : 0) \ + | ((f) & FP_OVERFLOW ? FE_OVERFLOW : 0) \ + | ((f) & FP_UNDERFLOW ? FE_UNDERFLOW : 0) \ + | ((f) & FP_INEXACT ? FE_INEXACT : 0)) +# else /* Optimized */ +# define fpflag_to_exceptions(f) \ + ((f) & FE_ALL_EXCEPT) +# endif + +/* The implementation of fegetexcept(). Avoids a module dependency. */ +# define fegetexcept_impl() \ + ( (fp_is_enabled (TRP_INVALID) ? FE_INVALID : 0) \ + | (fp_is_enabled (TRP_DIV_BY_ZERO) ? FE_DIVBYZERO : 0) \ + | (fp_is_enabled (TRP_OVERFLOW) ? FE_OVERFLOW : 0) \ + | (fp_is_enabled (TRP_UNDERFLOW) ? FE_UNDERFLOW : 0) \ + | (fp_is_enabled (TRP_INEXACT) ? FE_INEXACT : 0)) + +#endif diff --git a/lib/fenv-round.c b/lib/fenv-round.c new file mode 100644 index 0000000000..14048a5ca7 --- /dev/null +++ b/lib/fenv-round.c @@ -0,0 +1,572 @@ +/* Functions for controlling the floating-point rounding direction. + Copyright (C) 1997-2023 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +/* Based on glibc/sysdeps//{fegetround.c,fesetround.c} + together with glibc/sysdeps//{fpu_control.h,fenv_private.h,fenv_libc.h}. */ + +#include + +/* Specification. */ +#include + +#include "fenv-private.h" + +#if defined __GNUC__ || defined __clang__ || defined _MSC_VER + +# if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86) + +int +fegetround (void) +{ +# ifdef _MSC_VER + /* XXX Simplify: access the SSE unit. */ + fenv_t env; + if (fegetenv (&env) != 0) + return FE_TONEAREST; + unsigned int fctrl = env._Fe_ctl; +# else + /* Use the rounding direction from the control word of the 387 unit, the + so-called fctrl register. + The rounding direction of the SSE unit, in the mxcsr register, is expected + to be in sync with that. */ + unsigned short fctrl; + _FPU_GETCW (fctrl); +# endif +# ifdef _MSC_VER + /* The MSVC header files have different values for the rounding directions + than all the other platforms. Map + 0x0000 -> 0x0000 = FE_TONEAREST + 0x0400 -> 0x0200 = FE_DOWNWARD + 0x0800 -> 0x0100 = FE_UPWARD + 0x0C00 -> 0x0300 = FE_TOWARDZERO */ + return ((fctrl & 0x0400) >> 1) | ((fctrl & 0x0800) >> 3); +# else + return fctrl & 0x0C00; +# endif +} + +int +fesetround (int rounding_direction) +{ +# ifdef _MSC_VER + /* The MSVC header files have different values for the rounding directions + than all the other platforms. */ + if ((rounding_direction & ~0x0300) != 0) + return -1; + /* Map + FE_TONEAREST = 0x0000 -> 0x0000 + FE_DOWNWARD = 0x0200 -> 0x0400 + FE_UPWARD = 0x0100 -> 0x0800 + FE_TOWARDZERO = 0x0300 -> 0x0C00 */ + rounding_direction = + ((rounding_direction & 0x0200) << 1) | ((rounding_direction & 0x0100) << 3); +# else + if ((rounding_direction & ~0x0C00) != 0) + return -1; +# endif + + /* Set it in the 387 unit. */ +# ifdef _MSC_VER + /* XXX Simplify: only access the SSE unit. */ + fenv_t env; + unsigned long orig_ctl; + if (fegetenv (&env) != 0) + return -1; + orig_ctl = env._Fe_ctl; + env._Fe_ctl = (env._Fe_ctl & ~0x0C00) | rounding_direction; + if (env._Fe_ctl != orig_ctl) + { + if (fesetenv (&env) != 0) + return -1; + } +# else + unsigned short fctrl, orig_fctrl; + _FPU_GETCW (orig_fctrl); + fctrl = (orig_fctrl & ~0x0C00) | rounding_direction; + if (fctrl != orig_fctrl) + _FPU_SETCW (fctrl); +# endif + + if (CPU_HAS_SSE ()) + { + /* Set it in the SSE unit as well. */ + unsigned int mxcsr, orig_mxcsr; + _FPU_GETSSECW (orig_mxcsr); + mxcsr = (orig_mxcsr & ~(0x0C00 << 3)) | (rounding_direction << 3); + if (mxcsr != orig_mxcsr) + _FPU_SETSSECW (mxcsr); + } + + return 0; +} + +# elif defined __aarch64__ /* arm64 */ + +int +fegetround (void) +{ + unsigned long fpcr; + _FPU_GETCW (fpcr); + return (fpcr & 0x00C00000UL) +# if FE_TOWARDZERO == 3 /* FreeBSD compatible FE_* values */ + >> 22 +# endif + ; +} + +int +fesetround (int rounding_direction) +{ +# if FE_TOWARDZERO == 3 /* FreeBSD compatible FE_* values */ + if ((rounding_direction & ~3) != 0) + return -1; + rounding_direction = rounding_direction << 22; +# else /* glibc compatible FE_* values */ + if ((rounding_direction & ~0x00C00000UL) != 0) + return -1; +# endif + unsigned long fpcr, orig_fpcr; + _FPU_GETCW (orig_fpcr); + fpcr = (orig_fpcr & ~0x00C00000UL) | rounding_direction; + if (fpcr != orig_fpcr) + _FPU_SETCW (fpcr); + return 0; +} + +# elif defined __arm__ + +int +fegetround (void) +{ +# ifdef __SOFTFP__ + return FE_TONEAREST; +# else + unsigned int fpscr; + _FPU_GETCW (fpscr); + return fpscr & 0x00C00000U; +# endif +} + +int +fesetround (int rounding_direction) +{ +# ifdef __SOFTFP__ + if (rounding_direction != FE_TONEAREST) + return -1; +# else + if ((rounding_direction & ~0x00C00000U) != 0) + return -1; + unsigned int fpscr, orig_fpscr; + _FPU_GETCW (orig_fpscr); + fpscr = (orig_fpscr & ~0x00C00000U) | rounding_direction; + if (fpscr != orig_fpscr) + _FPU_SETCW (fpscr); +# endif + return 0; +} + +# elif defined __alpha + +int +fegetround (void) +{ + unsigned long fpcr; + _FPU_GETCW (fpcr); + return (fpcr >> 58) & 0x3UL; +} + +int +fesetround (int rounding_direction) +{ + if ((rounding_direction & ~0x3UL) != 0) + return -1; + unsigned long fpcr, orig_fpcr; + _FPU_GETCW (orig_fpcr); + fpcr = (orig_fpcr & ~(0x3UL << 58)) + | ((unsigned long) rounding_direction << 58); + if (fpcr != orig_fpcr) + _FPU_SETCW (fpcr); + return 0; +} + +# elif defined __hppa + +int +fegetround (void) +{ + unsigned int fpstatus; + _FPU_GETCW (fpstatus); + return fpstatus & 0x00000600U; +} + +int +fesetround (int rounding_direction) +{ + if ((rounding_direction & ~0x00000600U) != 0) + return -1; + unsigned int fpstatus, orig_fpstatus; + _FPU_GETCW (orig_fpstatus); + fpstatus = (orig_fpstatus & ~0x00000600U) | rounding_direction; + if (fpstatus != orig_fpstatus) + _FPU_SETCW (fpstatus); + return 0; +} + +# elif defined __ia64__ + +int +fegetround (void) +{ + unsigned long fpsr; + _FPU_GETCW (fpsr); + return (fpsr >> 10) & 0x3UL; +} + +int +fesetround (int rounding_direction) +{ + if ((rounding_direction & ~0x3UL) != 0) + return -1; + unsigned long fpsr, orig_fpsr; + _FPU_GETCW (orig_fpsr); + fpsr = (orig_fpsr & ~(0x3UL << 10)) + | ((unsigned long) rounding_direction << 10); + if (fpsr != orig_fpsr) + _FPU_SETCW (fpsr); + return 0; +} + +# elif defined __m68k__ + +int +fegetround (void) +{ + unsigned int fpcr; + _FPU_GETCW (fpcr); + return fpcr & 0x30U; +} + +int +fesetround (int rounding_direction) +{ + if ((rounding_direction & ~0x30U) != 0) + return -1; + unsigned int fpcr, orig_fpcr; + _FPU_GETCW (orig_fpcr); + fpcr = (orig_fpcr & ~0x30U) | rounding_direction; + if (fpcr != orig_fpcr) + _FPU_SETCW (fpcr); + return 0; +} + +# elif defined __mips__ + +int +fegetround (void) +{ + unsigned int fcsr; + _FPU_GETCW (fcsr); + return fcsr & 0x3U; +} + +int +fesetround (int rounding_direction) +{ + if ((rounding_direction & ~0x3U) != 0) + return -1; + unsigned int fcsr, orig_fcsr; + _FPU_GETCW (orig_fcsr); + fcsr = (orig_fcsr & ~0x3U) | rounding_direction; + if (fcsr != orig_fcsr) + _FPU_SETCW (fcsr); + return 0; +} + +# elif defined __loongarch__ + +int +fegetround (void) +{ + unsigned int fcsr; + _FPU_GETCW (fcsr); + return fcsr & 0x300U; +} + +int +fesetround (int rounding_direction) +{ + if ((rounding_direction & ~0x300U) != 0) + return -1; + unsigned int fcsr, orig_fcsr; + _FPU_GETCW (orig_fcsr); + fcsr = (orig_fcsr & ~0x300U) | rounding_direction; + if (fcsr != orig_fcsr) + _FPU_SETCW (fcsr); + return 0; +} + +# elif defined __powerpc__ + +/* The AIX header files have different values for the rounding directions + than all the other platforms: The values 0 and 1 are swapped. + (They probably did this in order to have a trivial FLT_ROUNDS macro, cf. + .) + Define some handy macros for conversion. */ +# ifdef _AIX +# define fe_to_hardware(x) ((x) ^ ((x) < 2)) +# define hardware_to_fe(x) ((x) ^ ((x) < 2)) +# else +# define fe_to_hardware(x) (x) +# define hardware_to_fe(x) (x) +# endif + +int +fegetround (void) +{ +# if 1 + unsigned int result; + __asm__ __volatile__ ("mcrfs 7,7 ; mfcr %0" : "=r" (result) : : "cr7"); + return hardware_to_fe (result & 3); +# else + union { unsigned long long u; double f; } memenv; + _FPU_GETCW_AS_DOUBLE (memenv.f); + return hardware_to_fe (memenv.u & 3); +# endif +} + +int +fesetround (int rounding_direction) +{ + if ((rounding_direction & ~0x3U) != 0) + return -1; +# ifdef _AIX + rounding_direction = fe_to_hardware (rounding_direction); +# endif + if (rounding_direction & 2) + __asm__ __volatile__ ("mtfsb1 30"); + else + __asm__ __volatile__ ("mtfsb0 30"); + if (rounding_direction & 1) + __asm__ __volatile__ ("mtfsb1 31"); + else + __asm__ __volatile__ ("mtfsb0 31"); + return 0; +} + +# elif defined __riscv + +int +fegetround (void) +{ + int rounding_direction; + __asm__ __volatile__ ("frrm %0" : "=r" (rounding_direction)); +# if FE_UPWARD == 0x60 /* FreeBSD compatible FE_* values */ + return rounding_direction << 5; +# else + return rounding_direction; +# endif +} + +int +fesetround (int rounding_direction) +{ +# if FE_UPWARD == 0x60 /* FreeBSD compatible FE_* values */ + if ((rounding_direction & ~0x60) != 0) + return -1; + rounding_direction = rounding_direction >> 5; +# else + if ((rounding_direction & ~3) != 0) + return -1; +# endif + __asm__ __volatile__ ("fsrm %z0" : : "rJ" (rounding_direction)); + return 0; +} + +# elif defined __s390__ || defined __s390x__ + +int +fegetround (void) +{ + unsigned int fpc; + _FPU_GETCW (fpc); + return fpc & 0x3U; +} + +int +fesetround (int rounding_direction) +{ + if ((rounding_direction & ~0x3U) != 0) + return -1; +# if 1 + __asm__ __volatile__ ("srnm 0(%0)" : : "a" (rounding_direction)); +# else + unsigned int fpc, orig_fpc; + _FPU_GETCW (orig_fpc); + fpc = (orig_fpc & ~0x3U) | rounding_direction; + if (fpc != orig_fpc) + _FPU_SETCW (fpc); +# endif + return 0; +} + +# elif defined __sh__ + +int +fegetround (void) +{ + unsigned int fpscr; + _FPU_GETCW (fpscr); + return fpscr & 0x1U; +} + +int +fesetround (int rounding_direction) +{ + if ((rounding_direction & ~0x1U) != 0) + return -1; + unsigned int fpscr, orig_fpscr; + _FPU_GETCW (orig_fpscr); + fpscr = (orig_fpscr & ~0x1U) | rounding_direction; + if (fpscr != orig_fpscr) + _FPU_SETCW (fpscr); + return 0; +} + +# elif defined __sparc + +int +fegetround (void) +{ + unsigned long fsr; + _FPU_GETCW (fsr); + return (fsr & 0xC0000000UL) +# if FE_DOWNWARD == 3 /* FreeBSD compatible FE_* values */ + >> 30 +# endif + ; +} + +int +fesetround (int rounding_direction) +{ +# if FE_DOWNWARD == 3 /* FreeBSD compatible FE_* values */ + if ((rounding_direction & ~3) != 0) + return -1; + rounding_direction = (unsigned int) rounding_direction << 30; +# else /* glibc compatible FE_* values */ + if (((unsigned int) rounding_direction & ~0xC0000000UL) != 0) + return -1; +# endif + unsigned long fsr, orig_fsr; + _FPU_GETCW (orig_fsr); + fsr = (orig_fsr & ~0xC0000000UL) | (unsigned int) rounding_direction; + if (fsr != orig_fsr) + _FPU_SETCW (fsr); + return 0; +} + +# else + +# if defined __GNUC__ || defined __clang__ +# warning "Unknown CPU / architecture. Please report your platform and compiler to ." +# endif +# define NEED_FALLBACK 1 + +# endif + +#else + +/* The compiler does not support __asm__ statements or equivalent + intrinsics. */ + +# if HAVE_FPSETROUND +/* FreeBSD ≥ 3.1, NetBSD ≥ 1.1, OpenBSD, IRIX, Solaris, Minix ≥ 3.2. */ + +/* Get fpgetround, fpsetround. */ +# include + +int +fegetround (void) +{ + return fpgetround (); +} + +int +fesetround (int rounding_direction) +{ + fpsetround (rounding_direction); + return 0; +} + +# elif defined _AIX /* AIX */ + +/* Get fp_read_rnd, fp_swap_rnd. */ +# include + +/* Documentation: + */ + +int +fegetround (void) +{ + return fp_read_rnd (); +} + +int +fesetround (int rounding_direction) +{ + fp_swap_rnd (rounding_direction); + return 0; +} + +# else + +# define NEED_FALLBACK 1 + +# endif + +#endif + +#if NEED_FALLBACK + +/* A dummy fallback. */ + +# include +# include + +int +fegetround (void) +{ + /* Cf. */ + switch (FLT_ROUNDS) + { + case 0: return FE_TOWARDZERO; + case 1: return FE_TONEAREST; + case 2: return FE_UPWARD; + case 3: return FE_DOWNWARD; + default: abort (); + } +} + +int +fesetround (int rounding_direction) +{ + if (rounding_direction != fegetround ()) + return -1; + return 0; +} + +#endif diff --git a/lib/fenv.in.h b/lib/fenv.in.h index c52d0e0ccb..2c70e343eb 100644 --- a/lib/fenv.in.h +++ b/lib/fenv.in.h @@ -37,6 +37,8 @@ #error "Please include config.h first." #endif +/* The definitions of _GL_FUNCDECL_RPL etc. are copied here. */ + /* The definition of _GL_WARN_ON_USE is copied here. */ @@ -180,6 +182,7 @@ typedef unsigned long fenv_t; # elif defined __aarch64__ /* arm64 */ +/* Attention: FreeBSD libc has these values shifted right by 22 bits! */ # define FE_TONEAREST (0 << 22) # define FE_UPWARD (1 << 22) # define FE_DOWNWARD (2 << 22) @@ -246,6 +249,7 @@ typedef unsigned long fenv_t; # elif defined __riscv +/* Attention: FreeBSD libc has these values shifted left by 5 bits! */ # define FE_TONEAREST 0 # define FE_TOWARDZERO 1 # define FE_DOWNWARD 2 @@ -284,6 +288,44 @@ typedef unsigned long fenv_t; #endif +#if @GNULIB_FEGETROUND@ +/* Returns the current rounding direction. */ +# if @REPLACE_FEGETROUND@ || (!@HAVE_FEGETROUND@ && (defined __GLIBC__ || defined __FreeBSD__)) /* has an inline definition */ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef fegetround +# define fegetround rpl_fegetround +# endif +_GL_FUNCDECL_RPL (fegetround, int, (void)); +_GL_CXXALIAS_RPL (fegetround, int, (void)); +# else +# if !@HAVE_FEGETROUND@ +_GL_FUNCDECL_SYS (fegetround, int, (void)); +# endif +_GL_CXXALIAS_SYS (fegetround, int, (void)); +# endif +_GL_CXXALIASWARN (fegetround); +#endif + +#if @GNULIB_FESETROUND@ +/* Sets the rounding direction of the current thread. + Returns zero if the argument is valid and the operation was thus successful. + Returns non-zero upon failure. */ +# if @REPLACE_FESETROUND@ || (!@HAVE_FESETROUND@ && defined __FreeBSD__) /* has an inline definition */ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef fesetround +# define fesetround rpl_fesetround +# endif +_GL_FUNCDECL_RPL (fesetround, int, (int rounding_direction)); +_GL_CXXALIAS_RPL (fesetround, int, (int rounding_direction)); +# else +# if !@HAVE_FESETROUND@ +_GL_FUNCDECL_SYS (fesetround, int, (int rounding_direction)); +# endif +_GL_CXXALIAS_SYS (fesetround, int, (int rounding_direction)); +# endif +_GL_CXXALIASWARN (fesetround); +#endif + /* ISO C 99 § 7.6.2 Floating-point exceptions ISO C 23 § 7.6.4 Floating-point exceptions diff --git a/m4/fenv-rounding.m4 b/m4/fenv-rounding.m4 new file mode 100644 index 0000000000..f52937c307 --- /dev/null +++ b/m4/fenv-rounding.m4 @@ -0,0 +1,77 @@ +# fenv-rounding.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_ROUNDING], +[ + AC_REQUIRE([gl_FENV_H_DEFAULTS]) + + dnl On FreeBSD 11/i386, fesetround needs -lm while fegetround doesn't. + gl_MATHFUNC([fesetround], [int], [(int)], [#include ]) + if test $gl_cv_func_fesetround_no_libm = yes \ + || test $gl_cv_func_fesetround_in_libm = yes; then + dnl The fesetround function does not work on MSVC 14. + AC_CACHE_CHECK([whether fesetround works], + [gl_cv_func_fesetround_works], + [case "$host_os" in + # Guess no on native Windows other than mingw. + mingw* | windows*) + AC_EGREP_CPP([Problem], [ +#ifndef __MINGW32__ + Problem +#endif + ], + [gl_cv_func_fesetround_works="guessing no"], + [gl_cv_func_fesetround_works="guessing yes"]) + ;; + *) gl_cv_func_fesetround_works="guessing yes" ;; + esac + ]) + case "$gl_cv_func_fesetround_works" in + *yes) ;; + *) REPLACE_FESETROUND=1 ;; + esac + if test $REPLACE_FESETROUND = 1; then + dnl One more function is defined in the same compilation unit. + REPLACE_FEGETROUND=1 + FENV_ROUNDING_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_fesetround_no_libm = yes; then + FENV_ROUNDING_LIBM= + else + FENV_ROUNDING_LIBM=-lm + fi + fi + else + HAVE_FEGETROUND=0 + HAVE_FESETROUND=0 + FENV_ROUNDING_LIBM= + fi + if test $HAVE_FESETROUND = 0 || test $REPLACE_FESETROUND = 1; then + gl_PREREQ_FENV_ROUNDING + dnl Possibly need -lm for fpgetround(), fpsetround(). + if test $gl_cv_func_fpsetround_no_libm = no \ + && test $gl_cv_func_fpsetround_in_libm = yes \ + && test -z "$FENV_ROUNDING_LIBM"; then + FENV_ROUNDING_LIBM=-lm + fi + fi + AC_SUBST([FENV_ROUNDING_LIBM]) +]) + +dnl Prerequisites of lib/fenv-round.c. +AC_DEFUN([gl_PREREQ_FENV_ROUNDING], +[ + gl_MATHFUNC([fpsetround], [fp_rnd], [(fp_rnd)], + [#include + ]) + if test $gl_cv_func_fpsetround_no_libm = yes \ + || test $gl_cv_func_fpsetround_in_libm = yes; then + AC_DEFINE([HAVE_FPSETROUND], [1], + [Define to 1 if you have the 'fpsetround' function.]) + fi +]) diff --git a/m4/mathfunc.m4 b/m4/mathfunc.m4 index 5146e580c0..f220cea8ac 100644 --- a/m4/mathfunc.m4 +++ b/m4/mathfunc.m4 @@ -1,4 +1,4 @@ -# mathfunc.m4 serial 14 +# mathfunc.m4 serial 15 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, @@ -40,12 +40,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_rnd], [1])], [fp_except_t], [1])], [int], [2])], [float], [1.618034f])], diff --git a/modules/fenv b/modules/fenv index 270db4bd27..87a9a02d28 100644 --- a/modules/fenv +++ b/modules/fenv @@ -9,6 +9,7 @@ m4/fenv_h.m4 Depends-on: gen-header include_next +snippet/c++defs snippet/warn-on-use configure.ac: @@ -20,7 +21,7 @@ Makefile.am: BUILT_SOURCES += fenv.h # We need the following in order to create an override of . -fenv.h: fenv.in.h $(top_builddir)/config.status $(WARN_ON_USE_H) +fenv.h: fenv.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(WARN_ON_USE_H) @NMD@ $(AM_V_GEN)$(MKDIR_P) '%reldir%' $(gl_V_at)$(SED_HEADER_STDOUT) \ -e 's|@''GUARD_PREFIX''@|${gl_include_guard_prefix}|g' \ @@ -72,6 +73,7 @@ fenv.h: fenv.in.h $(top_builddir)/config.status $(WARN_ON_USE_H) -e 's|@''REPLACE_FESETEXCEPTFLAG''@|$(REPLACE_FESETEXCEPTFLAG)|g' \ -e 's|@''REPLACE_FESETROUND''@|$(REPLACE_FESETROUND)|g' \ -e 's|@''REPLACE_FETESTEXCEPT''@|$(REPLACE_FETESTEXCEPT)|g' \ + -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \ -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \ $(srcdir)/fenv.in.h > $@-t $(AM_V_at)mv $@-t $@ diff --git a/modules/fenv-rounding b/modules/fenv-rounding new file mode 100644 index 0000000000..bd7eb48824 --- /dev/null +++ b/modules/fenv-rounding @@ -0,0 +1,36 @@ +Description: +Functions for controlling the floating-point rounding direction: +fegetround, fesetround. + +Files: +lib/fenv-round.c +lib/fenv-private.h +m4/fenv-rounding.m4 +m4/mathfunc.m4 + +Depends-on: +fenv + +configure.ac: +gl_FENV_ROUNDING +gl_CONDITIONAL([GL_COND_OBJ_FENV_ROUNDING], + [test $HAVE_FESETROUND = 0 || test $REPLACE_FESETROUND = 1]) +gl_FENV_MODULE_INDICATOR([fegetround]) +gl_FENV_MODULE_INDICATOR([fesetround]) + +Makefile.am: +if GL_COND_OBJ_FENV_ROUNDING +lib_SOURCES += fenv-round.c +endif + +Include: +#include + +Link: +$(FENV_ROUNDING_LIBM) + +License: +LGPLv2+ + +Maintainer: +all -- 2.39.5