mirror of
https://https.git.savannah.gnu.org/git/gnulib.git
synced 2026-05-13 15:13:36 +00:00
1166 lines
32 KiB
C
1166 lines
32 KiB
C
/* Functions for controlling the floating-point environment as a whole.
|
|
Copyright (C) 1997-2024 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>/{fegetenv.c,fesetenv.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"
|
|
#include "verify.h"
|
|
|
|
/* The big problem of the fegetenv/fesetenv API is that it is not well-defined
|
|
which parts of the floating-point environment need to be restored by fesetenv
|
|
and which don't. For example:
|
|
- On x86_64 and i386, some platforms restore all the x86_387_fenv_t,
|
|
whereas others restore only the control word and the status words.
|
|
- On m68k, musl libc restores the fpiar register value, whereas glibc
|
|
doesn't.
|
|
Some platforms don't even do this consistently. For example, musl libc on
|
|
sh4 restores the precision mode bit (fpscr bit 19) during an fesetenv of a
|
|
previously saved environment, but not during an fesetenv (FE_DFL_ENV)
|
|
invocation. */
|
|
|
|
#if defined _AIX && defined __powerpc__ /* AIX */
|
|
|
|
/* On AIX, fenv_t is a struct { unsigned short rmode; unsigned int fpstat, trapstate; }. */
|
|
|
|
/* On AIX, the register fpscr is augmented with a 32-bit word named fpscrx
|
|
in thread-local storage. Therefore AIX does complex things behind the scene,
|
|
and its best to use the system-provided fegetenv() and fesetenv() functions
|
|
and just add the missing behaviour, namely saving and restoring the exception
|
|
trap bits. */
|
|
|
|
# include <float.h>
|
|
# include <fpxcp.h>
|
|
|
|
# include <fptrap.h>
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
#undef fegetenv
|
|
{
|
|
/* Invoke the original fegetenv(), and additionally save the exception trap
|
|
bits (fpscr bits 7..3). */
|
|
union { unsigned long long u; double f; } memenv;
|
|
_FPU_GETCW_AS_DOUBLE (memenv.f);
|
|
fegetenv (envp);
|
|
/* AIX's original fegetenv() sets envp->trapstate = 0. */
|
|
envp->trapstate = FE_ALL_EXCEPT & ((unsigned int) memenv.u << 22);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
#undef fesetenv
|
|
{
|
|
/* Invoke the original fesetenv(), and additionally restore the exception trap
|
|
bits (fpscr bits 7..3). */
|
|
if (fesetenv (envp) != 0)
|
|
return -1;
|
|
union { unsigned long long u; double f; } orig_memenv, memenv;
|
|
_FPU_GETCW_AS_DOUBLE (orig_memenv.f);
|
|
unsigned int trapbits;
|
|
if (envp == FE_DFL_ENV)
|
|
trapbits = 0;
|
|
else
|
|
trapbits = (envp->trapstate & FE_ALL_EXCEPT) >> 22;
|
|
memenv.u = (orig_memenv.u & ~0x000000f8) | trapbits;
|
|
if (!(memenv.u == orig_memenv.u))
|
|
{
|
|
if ((orig_memenv.u & 0x000000f8) == 0 && (memenv.u & 0x000000f8) != 0)
|
|
{
|
|
/* Put the thread into precise trapping mode. */
|
|
/* Documentation: <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-trap-subroutine> */
|
|
fp_trap (FP_TRAP_SYNC);
|
|
}
|
|
_FPU_SETCW_AS_DOUBLE (memenv.f);
|
|
if ((orig_memenv.u & 0x000000f8) != 0 && (memenv.u & 0x000000f8) == 0)
|
|
{
|
|
/* Put the thread into no-trapping mode. */
|
|
/* Documentation: <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-trap-subroutine> */
|
|
fp_trap (FP_TRAP_OFF);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#elif defined __GNUC__ || defined __clang__ || defined _MSC_VER
|
|
|
|
# if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)
|
|
|
|
/* On all OSes except MSVC, macOS, Solaris, fenv_t is binary-equivalent to
|
|
- either a x86_387_fenv_t (7 'unsigned int' words)
|
|
where mxcsr is stored:
|
|
- for glibc/i386: in __eip = more[1].
|
|
- for musl libc/i386: the lower 6 bits ORed into __status_word.
|
|
- for FreeBSD/i386, Haiku/i386, mingw/i386:
|
|
the upper 16 bits in __reserved1, the lower 16 bits in __reserved2.
|
|
- or a struct { x86_387_fenv_t 387; unsigned int mxcsr; }
|
|
On MSVC, it's a
|
|
struct { unsigned int _Fe_ctl, _Fe_stat; }, where
|
|
_Fe_ctl bits 9..8 are the rounding direction,
|
|
_Fe_ctl bits 4..0 are the exception trap bits (inverted),
|
|
_Fe_stat bits 4..0 are the exception flags.
|
|
On macOS, it's a
|
|
struct { unsigned short __control, __status; unsigned int __mxcsr; ... }.
|
|
On Solaris, it's a
|
|
struct { __fex_handler_t __handlers; unsigned long __fsr; }. */
|
|
|
|
/* Like FE_ALL_EXCEPT, except that it also includes FE_DENORMAL. */
|
|
# define _FE_ALL_EXCEPT (0x3F * FE_INVALID)
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
# if defined _MSC_VER
|
|
|
|
unsigned int mxcsr;
|
|
_FPU_GETSSECW (mxcsr);
|
|
envp->_Fe_ctl = ((mxcsr & 0x6000) >> 5) /* rounding direction */
|
|
/* exception trap bits (inverted): */
|
|
| (mxcsr & (1 << 7) ? FE_INVALID : 0)
|
|
| (mxcsr & (1 << 9) ? FE_DIVBYZERO : 0)
|
|
| (mxcsr & (1 << 10) ? FE_OVERFLOW : 0)
|
|
| (mxcsr & (1 << 11) ? FE_UNDERFLOW : 0)
|
|
| (mxcsr & (1 << 12) ? FE_INEXACT : 0);
|
|
envp->_Fe_stat = /* exception flags: */
|
|
(mxcsr & (1 << 0) ? FE_INVALID : 0)
|
|
| (mxcsr & (1 << 2) ? FE_DIVBYZERO : 0)
|
|
| (mxcsr & (1 << 3) ? FE_OVERFLOW : 0)
|
|
| (mxcsr & (1 << 4) ? FE_UNDERFLOW : 0)
|
|
| (mxcsr & (1 << 5) ? FE_INEXACT : 0);
|
|
|
|
# elif defined __APPLE__ && defined __MACH__ /* macOS */
|
|
|
|
_FPU_GETCW (envp->__control);
|
|
_FPU_GETSTAT (envp->__status);
|
|
_FPU_GETSSECW (envp->__mxcsr);
|
|
|
|
# elif defined __sun /* Solaris */
|
|
|
|
fex_getexcepthandler (&envp->__handlers, FEX_ALL);
|
|
|
|
unsigned short fctrl;
|
|
unsigned short fstat;
|
|
unsigned int mxcsr;
|
|
_FPU_GETCW (fctrl);
|
|
_FPU_GETSTAT (fstat);
|
|
_FPU_GETSSECW (mxcsr);
|
|
envp->__fsr =
|
|
(((unsigned int) (fctrl ^ 0x3F) & ~0xE0C0U) << 16)
|
|
| ((unsigned int) fstat | (mxcsr & 0x3F));
|
|
|
|
# else
|
|
|
|
verify (sizeof (fenv_t) >= sizeof (x86_387_fenv_t));
|
|
|
|
__asm__ __volatile__ ("fnstenv %0" : "=m" (* (x86_387_fenv_t *) envp));
|
|
/* Note: fnstenv masks all floating-point exceptions until the fldcw
|
|
below. */
|
|
__asm__ __volatile__ ("fldcw %0" : : "m" (((x86_387_fenv_t *) envp)->__control_word));
|
|
|
|
unsigned int mxcsr;
|
|
_FPU_GETSSECW (mxcsr);
|
|
if (sizeof (fenv_t) > sizeof (x86_387_fenv_t))
|
|
{
|
|
/* Store mxcsr in the struct field after the x86_387_fenv_t. */
|
|
* (unsigned int *) ((x86_387_fenv_t *) envp + 1) = mxcsr;
|
|
}
|
|
else
|
|
{
|
|
# if defined __GLIBC__
|
|
((x86_387_fenv_t *) envp)->more[1] = mxcsr;
|
|
# elif MUSL_LIBC
|
|
((x86_387_fenv_t *) envp)->__status_word |= mxcsr & 0x3F;
|
|
# else
|
|
((x86_387_fenv_t *) envp)->__reserved1 = mxcsr >> 16;
|
|
((x86_387_fenv_t *) envp)->__reserved2 = mxcsr & 0xFFFF;
|
|
# endif
|
|
}
|
|
|
|
# endif
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
# if defined _MSC_VER
|
|
|
|
unsigned int mxcsr;
|
|
_FPU_GETSSECW (mxcsr);
|
|
mxcsr &= ~(0x6000 | (0x3F << 7) | 0x3F);
|
|
/* On MSVC, FE_DFL_ENV is the address of a global variable.
|
|
Let's ignore this variable. */
|
|
if (envp == FE_DFL_ENV)
|
|
/* Default: rounding direction = FE_TONEAREST, exception trap bits = 0,
|
|
exception flags = 0. */
|
|
mxcsr = mxcsr | (0x3F << 7);
|
|
else
|
|
mxcsr = mxcsr
|
|
| ((envp->_Fe_ctl & 0x300) << 5) /* rounding direction */
|
|
/* exception trap bits (inverted): */
|
|
| (envp->_Fe_ctl & FE_INVALID ? 1 << 7 : 0)
|
|
| (envp->_Fe_ctl & FE_DIVBYZERO ? 1 << 9 : 0)
|
|
| (envp->_Fe_ctl & FE_OVERFLOW ? 1 << 10 : 0)
|
|
| (envp->_Fe_ctl & FE_UNDERFLOW ? 1 << 11 : 0)
|
|
| (envp->_Fe_ctl & FE_INEXACT ? 1 << 12 : 0)
|
|
/* exception flags: */
|
|
| (envp->_Fe_stat & FE_INVALID ? 1 << 0 : 0)
|
|
| (envp->_Fe_stat & FE_DIVBYZERO ? 1 << 2 : 0)
|
|
| (envp->_Fe_stat & FE_OVERFLOW ? 1 << 3 : 0)
|
|
| (envp->_Fe_stat & FE_UNDERFLOW ? 1 << 4 : 0)
|
|
| (envp->_Fe_stat & FE_INEXACT ? 1 << 5 : 0);
|
|
_FPU_SETSSECW (mxcsr);
|
|
|
|
# elif defined __APPLE__ && defined __MACH__ /* macOS */
|
|
|
|
unsigned short fctrl;
|
|
unsigned short fstat;
|
|
unsigned int mxcsr;
|
|
/* On macOS, FE_DFL_ENV is the address of a global variable;
|
|
no special code is needed in this case. */
|
|
if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
|
|
{
|
|
fctrl = 0x3F;
|
|
fstat = 0;
|
|
mxcsr = 0x3F << 7;
|
|
}
|
|
else
|
|
{
|
|
fctrl = envp->__control;
|
|
fstat = envp->__status;
|
|
mxcsr = envp->__mxcsr;
|
|
}
|
|
/* In the SSE unit. */
|
|
_FPU_SETSSECW (mxcsr);
|
|
|
|
/* In the 387 unit. */
|
|
x86_387_fenv_t env387;
|
|
__asm__ __volatile__ ("fnstenv %0" : "=m" (*&env387));
|
|
/* Note: fnstenv masks all floating-point exceptions until the fldenv
|
|
below. */
|
|
env387.__control_word = fctrl;
|
|
env387.__status_word = fstat;
|
|
__asm__ __volatile__ ("fldenv %0" : : "m" (*&env387));
|
|
|
|
# elif defined __sun /* Solaris */
|
|
|
|
unsigned short env_fctrl;
|
|
unsigned int env_mxcsr;
|
|
/* On Solaris, FE_DFL_ENV is the address of a global variable;
|
|
no special code is needed in this case. */
|
|
if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
|
|
{
|
|
env_fctrl = 0x3F;
|
|
env_mxcsr = 0x3F << 7;
|
|
}
|
|
else
|
|
{
|
|
fex_setexcepthandler (&envp->__handlers, FEX_ALL);
|
|
env_fctrl = (envp->__fsr >> 16) ^ 0x3F;
|
|
env_mxcsr = envp->__fsr & 0x3F;
|
|
}
|
|
|
|
/* Store the exception flags in mxcsr, not in env387.__status_word. */
|
|
|
|
/* In the SSE unit. */
|
|
unsigned int mxcsr;
|
|
_FPU_GETSSECW (mxcsr);
|
|
mxcsr = (mxcsr & ~0x3F) | env_mxcsr;
|
|
_FPU_SETSSECW (mxcsr);
|
|
|
|
/* In the 387 unit. */
|
|
x86_387_fenv_t env387;
|
|
__asm__ __volatile__ ("fnstenv %0" : "=m" (*&env387));
|
|
/* Note: fnstenv masks all floating-point exceptions until the fldenv
|
|
below. */
|
|
env387.__control_word = env_fctrl;
|
|
env387.__status_word &= ~0x3F;
|
|
__asm__ __volatile__ ("fldenv %0" : : "m" (*&env387));
|
|
|
|
# else
|
|
|
|
verify (sizeof (fenv_t) >= sizeof (x86_387_fenv_t));
|
|
|
|
unsigned short env_fctrl;
|
|
unsigned short env_fstat;
|
|
unsigned int env_mxcsr;
|
|
/* On *BSD, Solaris, Cygwin, Android, Haiku, Minix, FE_DFL_ENV is the address
|
|
of a global variable; no special code is needed in this case.
|
|
But on mingw, FE_DFL_ENV is NULL. */
|
|
if (envp == FE_DFL_ENV)
|
|
{
|
|
env_fctrl = 0x3F;
|
|
env_fstat = 0;
|
|
env_mxcsr = 0x3F << 7;
|
|
}
|
|
else
|
|
{
|
|
env_fctrl = ((x86_387_fenv_t const *) envp)->__control_word;
|
|
env_fstat = ((x86_387_fenv_t const *) envp)->__status_word;
|
|
if (sizeof (fenv_t) > sizeof (x86_387_fenv_t))
|
|
env_mxcsr = * (unsigned int const *) ((x86_387_fenv_t const *) envp + 1);
|
|
else
|
|
{
|
|
# if defined __GLIBC__
|
|
env_mxcsr = ((x86_387_fenv_t const *) envp)->more[1];
|
|
# elif MUSL_LIBC
|
|
env_mxcsr = env_fstat & 0x3F;
|
|
# else
|
|
env_mxcsr = (((x86_387_fenv_t const *) envp)->__reserved1 << 16)
|
|
| ((x86_387_fenv_t const *) envp)->__reserved2;
|
|
# endif
|
|
}
|
|
}
|
|
|
|
/* In the SSE unit. */
|
|
unsigned int mxcsr = env_mxcsr;
|
|
_FPU_SETSSECW (mxcsr);
|
|
|
|
/* In the 387 unit. */
|
|
x86_387_fenv_t env387;
|
|
__asm__ __volatile__ ("fnstenv %0" : "=m" (*&env387));
|
|
/* Note: fnstenv masks all floating-point exceptions until the fldenv
|
|
below. */
|
|
env387.__control_word = env_fctrl;
|
|
env387.__status_word = env_fstat;
|
|
__asm__ __volatile__ ("fldenv %0" : : "m" (*&env387));
|
|
|
|
# endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
# elif defined __aarch64__ /* arm64 */
|
|
|
|
/* On all OSes except FreeBSD and macOS, fenv_t is binary-equivalent to a
|
|
struct { unsigned int fpcr, fpsr; }.
|
|
On FreeBSD, it's binary-equivalent to a
|
|
struct { unsigned int fpsr, fpcr; }, i.e. the fields are swapped.
|
|
On macOS, it's binary-equivalent to a
|
|
struct { unsigned long fpsr, fpcr; }. */
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
unsigned long fpcr;
|
|
unsigned long fpsr;
|
|
_FPU_GETCW (fpcr);
|
|
_FPU_GETFPSR (fpsr);
|
|
# if defined __APPLE__ && defined __MACH__
|
|
((unsigned long *) envp)[0] = fpsr;
|
|
((unsigned long *) envp)[1] = fpcr;
|
|
# elif defined __FreeBSD__
|
|
((unsigned int *) envp)[0] = fpsr;
|
|
((unsigned int *) envp)[1] = fpcr;
|
|
# else
|
|
((unsigned int *) envp)[0] = fpcr;
|
|
((unsigned int *) envp)[1] = fpsr;
|
|
# endif
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
unsigned long orig_fpcr, fpcr;
|
|
unsigned long orig_fpsr, fpsr;
|
|
_FPU_GETCW (orig_fpcr);
|
|
_FPU_GETFPSR (orig_fpsr);
|
|
/* On *BSD and Android, FE_DFL_ENV is the address of a global variable;
|
|
no special code is needed in this case. */
|
|
if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
|
|
{
|
|
/* Default: rounding direction = FE_TONEAREST, exceptions trap bits = 0. */
|
|
fpcr = (orig_fpcr & 0xFE0FE0F8U) | 0x00000000U;
|
|
/* Default: exception flags = 0. */
|
|
fpsr = (orig_fpsr & 0x0FFFFFE0U) | 0x00000000U;
|
|
}
|
|
else
|
|
{
|
|
# if defined __APPLE__ && defined __MACH__
|
|
fpsr = ((unsigned long const *) envp)[0];
|
|
fpcr = ((unsigned long const *) envp)[1];
|
|
# elif defined __FreeBSD__
|
|
fpsr = ((unsigned int const *) envp)[0];
|
|
fpcr = ((unsigned int const *) envp)[1];
|
|
# else
|
|
fpcr = ((unsigned int const *) envp)[0];
|
|
fpsr = ((unsigned int const *) envp)[1];
|
|
# endif
|
|
}
|
|
if (fpcr != orig_fpcr)
|
|
_FPU_SETCW (fpcr);
|
|
if (fpsr != orig_fpsr)
|
|
_FPU_SETFPSR (fpsr);
|
|
return 0;
|
|
}
|
|
|
|
# elif defined __arm__
|
|
|
|
/* On all OSes except OpenBSD, fenv_t is binary-equivalent to an 'unsigned int'.
|
|
On OpenBSD, it's a struct { unsigned int __sticky, __mask, __round; }. */
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
# ifdef __SOFTFP__
|
|
return -1;
|
|
# else
|
|
unsigned int fpscr;
|
|
_FPU_GETCW (fpscr);
|
|
# if defined __OpenBSD__
|
|
envp->__sticky = fpscr & FE_ALL_EXCEPT;
|
|
envp->__mask = (fpscr >> 8) & FE_ALL_EXCEPT;
|
|
envp->__round = (fpscr >> 22) & 3;
|
|
# else
|
|
* (unsigned int *) envp = fpscr;
|
|
# endif
|
|
return 0;
|
|
# endif
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
# ifdef __SOFTFP__
|
|
return -1;
|
|
# else
|
|
unsigned int fpscr, orig_fpscr;
|
|
_FPU_GETCW (orig_fpscr);
|
|
/* On *BSD and Android, FE_DFL_ENV is the address of a global variable;
|
|
no special code is needed in this case. */
|
|
if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
|
|
/* Default: rounding direction = FE_TONEAREST, exception trap bits = 0,
|
|
exception flags = 0. */
|
|
fpscr = (orig_fpscr & 0x00086060) | 0x00000000;
|
|
else
|
|
/* Here, glibc ignores orig_fpscr entirely... */
|
|
fpscr = (orig_fpscr & ~((3U << 22) | (FE_ALL_EXCEPT << 8) | FE_ALL_EXCEPT))
|
|
# if defined __OpenBSD__
|
|
| ((envp->__round & 3U) << 22)
|
|
| ((envp->__mask & FE_ALL_EXCEPT) << 8)
|
|
| (envp->__sticky & FE_ALL_EXCEPT)
|
|
# else
|
|
| (* (unsigned int const *) envp & ((3U << 22) | (FE_ALL_EXCEPT << 8) | FE_ALL_EXCEPT))
|
|
# endif
|
|
;
|
|
if (((fpscr ^ orig_fpscr) & ~0xF0000000) != 0)
|
|
_FPU_SETCW (fpscr);
|
|
return 0;
|
|
# endif
|
|
}
|
|
|
|
# elif defined __alpha
|
|
|
|
/* On all OSes except OpenBSD, fenv_t is binary-equivalent to an 'unsigned long'.
|
|
On OpenBSD, it's a struct { unsigned int __sticky, __mask, __round; }. */
|
|
|
|
/* Like FE_ALL_EXCEPT, except that it also includes FE_DENORMAL. */
|
|
# define _FE_ALL_EXCEPT (0x3FUL * FE_INVALID)
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
unsigned long swcr = __ieee_get_fp_control ();
|
|
unsigned long fpcr;
|
|
_FPU_GETCW (fpcr);
|
|
# if defined __OpenBSD__
|
|
envp->__sticky = (swcr >> 17) & _FE_ALL_EXCEPT;
|
|
envp->__mask = (swcr >> 1) & _FE_ALL_EXCEPT;
|
|
envp->__round = (fpcr >> 58) & 0x3UL;
|
|
# else
|
|
* (unsigned long *) envp =
|
|
(fpcr & 0x0C00000000000000UL) /* rounding direction */
|
|
| (swcr & 0x007E307EUL); /* exception flags,
|
|
mapping of denormal inputs and outputs,
|
|
exception trap bits */
|
|
# endif
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
unsigned long env;
|
|
/* On *BSD, FE_DFL_ENV is the address of a global variable;
|
|
on glibc, it is a magic address. */
|
|
if (envp == FE_DFL_ENV)
|
|
/* Default: rounding direction = FE_TONEAREST, exception flags = 0,
|
|
mapping = 0, exception trap bits = 0. */
|
|
env = 0x0800000000000000UL;
|
|
else
|
|
{
|
|
# if defined __OpenBSD__
|
|
env = ((unsigned long) (envp->__round & 0x3UL) << 58)
|
|
| ((envp->__sticky & _FE_ALL_EXCEPT) << 17)
|
|
| ((envp->__mask & _FE_ALL_EXCEPT) << 1);
|
|
# else
|
|
env = * (unsigned long const *) envp;
|
|
# endif
|
|
}
|
|
|
|
/* Set the rounding direction. */
|
|
unsigned long fpcr, orig_fpcr;
|
|
_FPU_GETCW (orig_fpcr);
|
|
fpcr = (orig_fpcr & ~0x0C00000000000000UL) | (env & 0x0C00000000000000UL);
|
|
if (fpcr != orig_fpcr)
|
|
_FPU_SETCW (fpcr);
|
|
/* Set the exception flags, the mapping of denormal inputs and outputs, and
|
|
the exception trap bits. */
|
|
__ieee_set_fp_control (env & 0x007E307EUL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
# elif defined __hppa
|
|
|
|
/* On all OSes except glibc, fenv_t is binary-equivalent to an 'unsigned int'.
|
|
On glibc, it's a struct { unsigned int __status_word, __exception[7]; },
|
|
of which only the __status_word field is used. */
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
unsigned int fpstatus;
|
|
_FPU_GETCW (fpstatus);
|
|
* (unsigned int *) envp = fpstatus;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
unsigned int fpstatus, orig_fpstatus;
|
|
_FPU_GETCW (orig_fpstatus);
|
|
/* On *BSD, FE_DFL_ENV is the address of a global variable;
|
|
no special code is needed in this case. */
|
|
if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
|
|
/* Default: exception flags = 0, rounding direction = FE_TONEAREST,
|
|
exception trap bits = 0. */
|
|
fpstatus = 0x00000000U;
|
|
else
|
|
{
|
|
unsigned int env = * (unsigned int const *) envp;
|
|
fpstatus = (orig_fpstatus & ~0xF800061FU) | (env & 0xF800061FU);
|
|
}
|
|
if (fpstatus != orig_fpstatus)
|
|
_FPU_SETCW (fpstatus);
|
|
return 0;
|
|
}
|
|
|
|
# elif defined __ia64__
|
|
|
|
/* On all OSes, fenv_t is binary-equivalent to an 'unsigned long'. */
|
|
|
|
/* Like FE_ALL_EXCEPT, except that it also includes FE_DENORMAL. */
|
|
# define _FE_ALL_EXCEPT (0x3FUL * FE_INVALID)
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
unsigned long fpsr;
|
|
_FPU_GETCW (fpsr);
|
|
* (unsigned long *) envp = fpsr;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
unsigned long env;
|
|
/* On NetBSD, FE_DFL_ENV is the address of a global variable;
|
|
on glibc, it is a magic address. */
|
|
if (envp == FE_DFL_ENV)
|
|
/* Default:
|
|
sf0 = 0b0000000001100, sf1 = 0b0000001001110, sf2 = sf3 = 0000001001100
|
|
i.e. precision control = 80-bits "extended",
|
|
rounding direction = FE_TONEAREST,
|
|
exception flags = 0,
|
|
exceptions trap bits = all 1, i.e. all masked. */
|
|
env = 0x0009804C0270033FUL;
|
|
else
|
|
env = * (unsigned long const *) envp;
|
|
unsigned long fpsr = env;
|
|
_FPU_SETCW (fpsr);
|
|
return 0;
|
|
}
|
|
|
|
# elif defined __m68k__
|
|
|
|
/* On all OSes, fenv_t is binary-equivalent to a struct
|
|
{ unsigned int control_register, status_register, instruction_address; }. */
|
|
|
|
# define _FPU_GETFPIAR(cw) __asm__ __volatile__ ("fmove%.l %/fpiar, %0" : "=dm" (cw))
|
|
# define _FPU_SETFPIAR(cw) __asm__ __volatile__ ("fmove%.l %0, %/fpiar" : : "dm" (cw))
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
unsigned int fpcr;
|
|
unsigned int fpsr;
|
|
unsigned int fpiar;
|
|
_FPU_GETCW (fpcr);
|
|
_FPU_GETFPSR (fpsr);
|
|
_FPU_GETFPIAR (fpiar);
|
|
((unsigned int *) envp)[0] = fpcr;
|
|
((unsigned int *) envp)[1] = fpsr;
|
|
((unsigned int *) envp)[2] = fpiar;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
unsigned int env_fpcr;
|
|
unsigned int env_fpsr;
|
|
if (envp == FE_DFL_ENV)
|
|
{
|
|
/* Default: exceptions trap bits = 0, rounding direction = FE_TONEAREST. */
|
|
env_fpcr = 0;
|
|
/* Default: exception flags = 0. */
|
|
env_fpsr = 0;
|
|
}
|
|
else
|
|
{
|
|
/* No need to restore the instruction address here. */
|
|
env_fpcr = ((unsigned int const *) envp)[0];
|
|
env_fpsr = ((unsigned int const *) envp)[1];
|
|
}
|
|
unsigned int fpcr, orig_fpcr;
|
|
unsigned int fpsr, orig_fpsr;
|
|
unsigned int fpiar, orig_fpiar;
|
|
_FPU_GETCW (orig_fpcr);
|
|
_FPU_GETFPSR (orig_fpsr);
|
|
_FPU_GETFPIAR (orig_fpiar);
|
|
/* glibc uses 0x3E30U here. I think 0xFF30U is a better choice. */
|
|
fpcr = (orig_fpcr & ~0xFF30U) | (env_fpcr & 0xFF30U);
|
|
fpsr = (orig_fpsr & ~0x00F8U) | (env_fpsr & 0x00F8U);
|
|
fpiar = orig_fpiar;
|
|
_FPU_SETCW (fpcr);
|
|
_FPU_SETFPSR (fpsr);
|
|
_FPU_SETFPIAR (fpiar);
|
|
return 0;
|
|
}
|
|
|
|
# elif defined __mips__
|
|
|
|
/* On all OSes, fenv_t is binary-equivalent to an 'unsigned int'. */
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
unsigned int fcsr;
|
|
_FPU_GETCW (fcsr);
|
|
* (unsigned int *) envp = fcsr;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
unsigned int env;
|
|
/* On *BSD, FE_DFL_ENV is the address of a global variable;
|
|
no special code is needed in this case. */
|
|
if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
|
|
/* Default: exceptions trap bits = 0, exception flags = 0,
|
|
rounding direction = FE_TONEAREST. */
|
|
env = 0x00000000U;
|
|
else
|
|
env = * (unsigned int const *) envp;
|
|
unsigned int fcsr = env;
|
|
_FPU_SETCW (fcsr);
|
|
return 0;
|
|
}
|
|
|
|
# elif defined __loongarch__
|
|
|
|
/* On all OSes, fenv_t is binary-equivalent to an 'unsigned int'. */
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
unsigned int fcsr;
|
|
_FPU_GETCW (fcsr);
|
|
* (unsigned int *) envp = fcsr;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
unsigned int env;
|
|
if (envp == FE_DFL_ENV)
|
|
/* Default: exception flags = 0, rounding direction = FE_TONEAREST,
|
|
exceptions trap bits = 0. */
|
|
env = 0x00000000U;
|
|
else
|
|
env = * (unsigned int const *) envp;
|
|
unsigned int fcsr = env;
|
|
_FPU_SETCW (fcsr);
|
|
return 0;
|
|
}
|
|
|
|
# elif defined __powerpc__
|
|
|
|
/* On all OSes except *BSD and AIX, fenv_t is a 'double'.
|
|
On *BSD, it's an 'unsigned int'.
|
|
On AIX, it's a struct { unsigned short rmode; unsigned int fpstat, trapstate; }.
|
|
But AIX has already been dealt with above. */
|
|
|
|
# if defined __linux__
|
|
# include <sys/prctl.h>
|
|
# endif
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
# if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__
|
|
union { unsigned long long u; double f; } memenv;
|
|
_FPU_GETCW_AS_DOUBLE (memenv.f);
|
|
* (unsigned int *) envp = (unsigned int) memenv.u;
|
|
# else
|
|
_FPU_GETCW_AS_DOUBLE (*envp);
|
|
# endif
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
union { unsigned long long u; double f; } orig_memenv, memenv;
|
|
_FPU_GETCW_AS_DOUBLE (orig_memenv.f);
|
|
/* On glibc and *BSD, FE_DFL_ENV is the address of a global variable;
|
|
no special code is needed in this case. */
|
|
if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
|
|
memenv.u = 0xFFF8000000000000ULL;
|
|
else
|
|
# if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__
|
|
memenv.u = * (unsigned int const *) envp;
|
|
# else
|
|
memenv.f = *envp;
|
|
# endif
|
|
if (!(memenv.u == orig_memenv.u))
|
|
{
|
|
if ((orig_memenv.u & 0x000000f8) == 0 && (memenv.u & 0x000000f8) != 0)
|
|
{
|
|
/* Put the thread into precise trapping mode. */
|
|
# if defined __linux__ || defined __NetBSD__
|
|
prctl (PR_SET_FPEXC, PR_FP_EXC_PRECISE);
|
|
# endif
|
|
}
|
|
_FPU_SETCW_AS_DOUBLE (memenv.f);
|
|
if ((orig_memenv.u & 0x000000f8) != 0 && (memenv.u & 0x000000f8) == 0)
|
|
{
|
|
/* Put the thread into no-trapping mode. */
|
|
# if defined __linux__ || defined __NetBSD__
|
|
prctl (PR_SET_FPEXC, PR_FP_EXC_DISABLED);
|
|
# endif
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
# elif defined __riscv
|
|
|
|
/* On all OSes except FreeBSD, fenv_t is binary-equivalent to an 'unsigned int'.
|
|
On FreeBSD, it's binary-equivalent to an 'unsigned long'. */
|
|
|
|
# define _FPU_GETCW(cw) __asm__ __volatile__ ("frcsr %0" : "=r" (cw))
|
|
# define _FPU_SETCW(cw) __asm__ __volatile__ ("fscsr %z0" : : "rJ" (cw))
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
unsigned int fcsr;
|
|
_FPU_GETCW (fcsr);
|
|
# if defined __FreeBSD__
|
|
* (unsigned long *) envp = fcsr;
|
|
# else
|
|
* (unsigned int *) envp = fcsr;
|
|
# endif
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
unsigned int env;
|
|
/* On *BSD and Android, FE_DFL_ENV is the address of a global variable;
|
|
no special code is needed in this case. */
|
|
if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
|
|
/* Default: rounding direction = FE_TONEAREST, exception flags = 0. */
|
|
env = 0x00000000U;
|
|
else
|
|
# if defined __FreeBSD__
|
|
env = * (unsigned long const *) envp;
|
|
# else
|
|
env = * (unsigned int const *) envp;
|
|
# endif
|
|
unsigned int fcsr = env;
|
|
_FPU_SETCW (fcsr);
|
|
return 0;
|
|
}
|
|
|
|
# elif defined __s390__ || defined __s390x__
|
|
|
|
/* On all OSes, fenv_t is binary-equivalent to a struct whose first (and only
|
|
relevant) field is an 'unsigned int'. */
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
unsigned int fpc;
|
|
_FPU_GETCW (fpc);
|
|
* (unsigned int *) envp = fpc;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
unsigned int env;
|
|
if (envp == FE_DFL_ENV)
|
|
/* Default: exceptions trap bits = 0, exception flags = 0,
|
|
rounding direction = FE_TONEAREST. */
|
|
env = 0x00000000U;
|
|
else
|
|
env = * (unsigned int const *) envp;
|
|
unsigned int fpc = env;
|
|
_FPU_SETCW (fpc);
|
|
return 0;
|
|
}
|
|
|
|
# elif defined __sh__
|
|
|
|
/* On all OSes, fenv_t is binary-equivalent to an 'unsigned int'. */
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
unsigned int fpscr;
|
|
_FPU_GETCW (fpscr);
|
|
* (unsigned int *) envp = fpscr;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
unsigned int env;
|
|
/* On OpenBSD, FE_DFL_ENV is the address of a global variable;
|
|
no special code is needed in this case. */
|
|
if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
|
|
/* Default: PR = 1, exceptions trap bits = 0, exception flags = 0,
|
|
rounding direction = FE_TONEAREST. */
|
|
env = 0x00080000U;
|
|
else
|
|
env = * (unsigned int const *) envp;
|
|
unsigned int fpscr = env;
|
|
_FPU_SETCW (fpscr);
|
|
return 0;
|
|
}
|
|
|
|
# elif defined __sparc
|
|
|
|
/* On all OSes except Solaris, fenv_t is binary-equivalent to an 'unsigned long'.
|
|
On Solaris, it's a struct { __fex_handler_t __handlers; unsigned long __fsr; }. */
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
unsigned long fsr;
|
|
_FPU_GETCW (fsr);
|
|
# if defined __sun
|
|
fex_getexcepthandler (&envp->__handlers, FEX_ALL);
|
|
envp->__fsr = fsr;
|
|
# else
|
|
* (unsigned long *) envp = fsr;
|
|
# endif
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
unsigned long fsr;
|
|
/* On *BSD and Solaris, FE_DFL_ENV is the address of a global variable;
|
|
no special code is needed in this case. */
|
|
if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
|
|
{
|
|
/* Default: rounding direction = FE_TONEAREST, exceptions trap bits = 0,
|
|
exception flags = 0. */
|
|
fsr = 0x00000000U;
|
|
}
|
|
else
|
|
{
|
|
# if defined __sun
|
|
fex_setexcepthandler (&envp->__handlers, FEX_ALL);
|
|
fsr = envp->__fsr;
|
|
# else
|
|
fsr = * (unsigned long const *) envp;
|
|
# endif
|
|
}
|
|
_FPU_SETCW (fsr);
|
|
return 0;
|
|
}
|
|
|
|
# else
|
|
|
|
# if defined __GNUC__ || defined __clang__
|
|
# warning "Unknown CPU / architecture. Please report your platform and compiler to <bug-gnulib@gnu.org>."
|
|
# endif
|
|
# define NEED_FALLBACK 1
|
|
|
|
# endif
|
|
|
|
#else
|
|
|
|
/* The compiler does not support __asm__ statements or equivalent
|
|
intrinsics. */
|
|
|
|
# if defined __sun && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)) && defined __SUNPRO_C
|
|
/* Solaris/i386, Solaris/x86_64. */
|
|
|
|
/* Like FE_ALL_EXCEPT, except that it also includes FE_DENORMAL. */
|
|
# define _FE_ALL_EXCEPT (0x3F * FE_INVALID)
|
|
|
|
/* Accessors for the 387 unit's special registers. */
|
|
|
|
static void
|
|
getfctrl (unsigned short *fctrl_p)
|
|
{
|
|
# if defined __x86_64__ || defined _M_X64
|
|
asm ("fnstcw (%rdi)");
|
|
# else
|
|
/* The compiler generates a stack frame. Therefore the first argument is in
|
|
8(%ebp), not in 4(%esp). */
|
|
asm ("movl 8(%ebp),%eax");
|
|
asm ("fnstcw (%eax)");
|
|
# endif
|
|
}
|
|
|
|
static void
|
|
getfstat (unsigned short *fstat_p)
|
|
{
|
|
# if defined __x86_64__ || defined _M_X64
|
|
asm ("fnstsw (%rdi)");
|
|
# else
|
|
/* The compiler generates a stack frame. Therefore the first argument is in
|
|
8(%ebp), not in 4(%esp). */
|
|
asm ("movl 8(%ebp),%eax");
|
|
asm ("fnstsw (%eax)");
|
|
# endif
|
|
}
|
|
|
|
static void
|
|
getenv387 (x86_387_fenv_t *env387_p)
|
|
{
|
|
# if defined __x86_64__ || defined _M_X64
|
|
asm ("fnstenv (%rdi)");
|
|
# else
|
|
/* The compiler generates a stack frame. Therefore the first argument is in
|
|
8(%ebp), not in 4(%esp). */
|
|
asm ("movl 8(%ebp),%eax");
|
|
asm ("fnstenv (%eax)");
|
|
# endif
|
|
}
|
|
|
|
static void
|
|
setenv387 (x86_387_fenv_t const *env387_p)
|
|
{
|
|
# if defined __x86_64__ || defined _M_X64
|
|
asm ("fldenv (%rdi)");
|
|
# else
|
|
/* The compiler generates a stack frame. Therefore the first argument is in
|
|
8(%ebp), not in 4(%esp). */
|
|
asm ("movl 8(%ebp),%eax");
|
|
asm ("fldenv (%eax)");
|
|
# endif
|
|
}
|
|
|
|
/* Accessors for the mxcsr register. */
|
|
|
|
static void
|
|
getssecw (unsigned int *mxcsr_p)
|
|
{
|
|
# if defined __x86_64__ || defined _M_X64
|
|
asm ("stmxcsr (%rdi)");
|
|
# else
|
|
/* The compiler generates a stack frame. Therefore the first argument is in
|
|
8(%ebp), not in 4(%esp). */
|
|
asm ("movl 8(%ebp),%eax");
|
|
asm ("stmxcsr (%eax)");
|
|
# endif
|
|
}
|
|
|
|
static void
|
|
setssecw (unsigned int const *mxcsr_p)
|
|
{
|
|
# if defined __x86_64__ || defined _M_X64
|
|
asm ("ldmxcsr (%rdi)");
|
|
# else
|
|
/* The compiler generates a stack frame. Therefore the first argument is in
|
|
8(%ebp), not in 4(%esp). */
|
|
asm ("movl 8(%ebp),%eax");
|
|
asm ("ldmxcsr (%eax)");
|
|
# endif
|
|
}
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
fex_getexcepthandler (&envp->__handlers, FEX_ALL);
|
|
|
|
unsigned short fctrl;
|
|
unsigned short fstat;
|
|
unsigned int mxcsr;
|
|
getfctrl (&fctrl);
|
|
getfstat (&fstat);
|
|
getssecw (&mxcsr);
|
|
envp->__fsr =
|
|
(((unsigned int) (fctrl ^ 0x3F) & ~0xE0C0U) << 16)
|
|
| ((unsigned int) fstat | (mxcsr & 0x3F));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
unsigned short env_fctrl;
|
|
unsigned int env_mxcsr;
|
|
/* On Solaris, FE_DFL_ENV is the address of a global variable;
|
|
no special code is needed in this case. */
|
|
if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
|
|
{
|
|
env_fctrl = 0x3F;
|
|
env_mxcsr = 0x3F << 7;
|
|
}
|
|
else
|
|
{
|
|
fex_setexcepthandler (&envp->__handlers, FEX_ALL);
|
|
env_fctrl = (envp->__fsr >> 16) ^ 0x3F;
|
|
env_mxcsr = envp->__fsr & 0x3F;
|
|
}
|
|
|
|
/* Store the exception flags in mxcsr, not in env387.__status_word. */
|
|
|
|
/* In the SSE unit. */
|
|
unsigned int mxcsr;
|
|
getssecw (&mxcsr);
|
|
mxcsr = (mxcsr & ~0x3F) | env_mxcsr;
|
|
setssecw (&mxcsr);
|
|
|
|
/* In the 387 unit. */
|
|
x86_387_fenv_t env387;
|
|
getenv387 (&env387);
|
|
/* Note: fnstenv masks all floating-point exceptions until the setenv387
|
|
below. */
|
|
env387.__control_word = env_fctrl;
|
|
env387.__status_word &= ~0x3F;
|
|
setenv387 (&env387);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#elif defined __sun && defined __sparc && defined __SUNPRO_C
|
|
/* Solaris/sparc. */
|
|
|
|
/* Accessors for the fsr register. */
|
|
|
|
static void
|
|
getfsr (unsigned long *fsr_p)
|
|
{
|
|
# if defined __sparcv9 || defined __arch64__ /* sparc64 */
|
|
asm ("stx %fsr,[%i0]");
|
|
# else
|
|
asm ("st %fsr,[%i0]");
|
|
# endif
|
|
}
|
|
|
|
static void
|
|
setfsr (unsigned long const *fsr_p)
|
|
{
|
|
# if defined __sparcv9 || defined __arch64__ /* sparc64 */
|
|
asm ("ldx [%i0],%fsr");
|
|
# else
|
|
asm ("ld [%i0],%fsr");
|
|
# endif
|
|
}
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
unsigned long fsr;
|
|
getfsr (&fsr);
|
|
fex_getexcepthandler (&envp->__handlers, FEX_ALL);
|
|
envp->__fsr = fsr;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
unsigned long fsr;
|
|
/* On *BSD and Solaris, FE_DFL_ENV is the address of a global variable;
|
|
no special code is needed in this case. */
|
|
if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
|
|
{
|
|
/* Default: rounding direction = FE_TONEAREST, exceptions trap bits = 0,
|
|
exception flags = 0. */
|
|
fsr = 0x00000000U;
|
|
}
|
|
else
|
|
{
|
|
fex_setexcepthandler (&envp->__handlers, FEX_ALL);
|
|
fsr = envp->__fsr;
|
|
}
|
|
setfsr (&fsr);
|
|
return 0;
|
|
}
|
|
|
|
# else
|
|
|
|
# define NEED_FALLBACK 1
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
#if NEED_FALLBACK
|
|
|
|
/* A dummy fallback. */
|
|
|
|
int
|
|
fegetenv (fenv_t *envp)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
fesetenv (fenv_t const *envp)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
#endif
|