+++ /dev/null
-/* fenv.cc
-
-This file is part of Cygwin.
-
-This software is a copyrighted work licensed under the terms of the
-Cygwin license. Please consult the file "CYGWIN_LICENSE" for
-details. */
-
-#include "winsup.h"
-#include "fenv.h"
-#include "errno.h"
-#include "wincap.h"
-#include <string.h>
-
-/* x87 supports subnormal numbers so we need it below. */
-#define __FE_DENORM (1 << 1)
-/* mask (= 0x3f) to disable all exceptions at initialization */
-#define __FE_ALL_EXCEPT_X86 (FE_ALL_EXCEPT | __FE_DENORM)
-
-/* Mask and shift amount for rounding bits. */
-#define FE_CW_ROUND_MASK (0x0c00)
-#define FE_CW_ROUND_SHIFT (10)
-/* Same, for SSE MXCSR. */
-#define FE_MXCSR_ROUND_MASK (0x6000)
-#define FE_MXCSR_ROUND_SHIFT (13)
-
-/* Mask and shift amount for precision bits. */
-#define FE_CW_PREC_MASK (0x0300)
-#define FE_CW_PREC_SHIFT (8)
-
-/* In x87, exception status bits and mask bits occupy
- corresponding bit positions in the status and control
- registers, respectively. In SSE, they are both located
- in the control-and-status register, with the status bits
- corresponding to the x87 positions, and the mask bits
- shifted by this amount to the left. */
-#define FE_SSE_EXCEPT_MASK_SHIFT (7)
-
-/* These are writable so we can initialise them at startup. */
-static fenv_t fe_nomask_env;
-
-/* These pointers provide the outside world with read-only access to them. */
-const fenv_t *_fe_nomask_env = &fe_nomask_env;
-
-/* Although Cygwin assumes i686 or above (hence SSE available) these
- days, and the compiler feels free to use it (depending on compile-
- time flags of course), we should avoid needlessly breaking any
- purely integer mode apps (or apps compiled with -mno-sse), so we
- only manage SSE state in this fenv module if we detect that SSE
- instructions are available at runtime. If we didn't do this, all
- applications run on older machines would bomb out with an invalid
- instruction exception right at startup; let's not be *that* WJM! */
-static bool use_sse = false;
-
-/* This function enables traps for each of the exceptions as indicated
- by the parameter except. The individual exceptions are described in
- [ ... glibc manual xref elided ...]. Only the specified exceptions are
- enabled, the status of the other exceptions is not changed.
- The function returns the previous enabled exceptions in case the
- operation was successful, -1 otherwise. */
-int
-feenableexcept (int excepts)
-{
- unsigned short cw, old_cw;
- unsigned int mxcsr = 0;
-
- if (excepts & ~FE_ALL_EXCEPT)
- return -1;
-
- /* Get control words. */
- __asm__ volatile ("fnstcw %0" : "=m" (old_cw) : );
- if (use_sse)
- __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
-
- /* Enable exceptions by clearing mask bits. */
- cw = old_cw & ~excepts;
- mxcsr &= ~(excepts << FE_SSE_EXCEPT_MASK_SHIFT);
-
- /* Store updated control words. */
- __asm__ volatile ("fldcw %0" :: "m" (cw));
- if (use_sse)
- __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));
-
- /* Return old value. We assume SSE and x87 stay in sync. Note that
- we are returning a mask of enabled exceptions, which is the opposite
- of the flags in the register, which are set to disable (mask) their
- related exceptions. */
- return (~old_cw) & FE_ALL_EXCEPT;
-}
-
-/* This function disables traps for each of the exceptions as indicated
- by the parameter except. The individual exceptions are described in
- [ ... glibc manual xref elided ...]. Only the specified exceptions are
- disabled, the status of the other exceptions is not changed.
- The function returns the previous enabled exceptions in case the
- operation was successful, -1 otherwise. */
-int
-fedisableexcept (int excepts)
-{
- unsigned short cw, old_cw;
- unsigned int mxcsr = 0;
-
- if (excepts & ~FE_ALL_EXCEPT)
- return -1;
-
- /* Get control words. */
- __asm__ volatile ("fnstcw %0" : "=m" (old_cw) : );
- if (use_sse)
- __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
-
- /* Disable exceptions by setting mask bits. */
- cw = old_cw | excepts;
- mxcsr |= (excepts << FE_SSE_EXCEPT_MASK_SHIFT);
-
- /* Store updated control words. */
- __asm__ volatile ("fldcw %0" :: "m" (cw));
- if (use_sse)
- __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));
-
- /* Return old value. We assume SSE and x87 stay in sync. Note that
- we are returning a mask of enabled exceptions, which is the opposite
- of the flags in the register, which are set to disable (mask) their
- related exceptions. */
- return (~old_cw) & FE_ALL_EXCEPT;
-}
-
-/* This function returns a bitmask of all currently enabled exceptions. It
- returns -1 in case of failure. */
-int
-fegetexcept (void)
-{
- unsigned short cw;
-
- /* Get control word. We assume SSE and x87 stay in sync. */
- __asm__ volatile ("fnstcw %0" : "=m" (cw) : );
-
- /* Exception is *dis*abled when mask bit is set. */
- return (~cw) & FE_ALL_EXCEPT;
-}
-
-/* Store the floating-point environment in the variable pointed to by envp.
- The function returns zero in case the operation was successful, a non-zero
- value otherwise. */
-int
-fegetenv (fenv_t *envp)
-{
- /* fnstenv disables all exceptions in the x87 FPU; as this is not what is
- desired here, reload the cfg saved from the x87 FPU, back to the FPU */
- __asm__ volatile ("fnstenv %0\n\
- fldenv %0"
- : "=m" (envp->_fpu) : );
- if (use_sse)
- __asm__ volatile ("stmxcsr %0" : "=m" (envp->_sse_mxcsr) : );
- return 0;
-}
-
-/* Store the current floating-point environment in the object pointed to
- by envp. Then clear all exception flags, and set the FPU to trap no
- exceptions. Not all FPUs support trapping no exceptions; if feholdexcept
- cannot set this mode, it returns nonzero value. If it succeeds, it
- returns zero. */
-int
-feholdexcept (fenv_t *envp)
-{
- unsigned int mxcsr;
- fegetenv (envp);
- mxcsr = envp->_sse_mxcsr & ~FE_ALL_EXCEPT;
- if (use_sse)
- __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));
- __asm__ volatile ("fnclex");
- fedisableexcept (FE_ALL_EXCEPT);
- return 0;
-}
-
-/* Set the floating-point environment to that described by envp. The
- function returns zero in case the operation was successful, a non-zero
- value otherwise. */
-int
-fesetenv (const fenv_t *envp)
-{
- __asm__ volatile ("fldenv %0" :: "m" (envp->_fpu) );
- if (use_sse)
- __asm__ volatile ("ldmxcsr %0" :: "m" (envp->_sse_mxcsr));
- return 0;
-}
-
-/* Like fesetenv, this function sets the floating-point environment to
- that described by envp. However, if any exceptions were flagged in the
- status word before feupdateenv was called, they remain flagged after
- the call. In other words, after feupdateenv is called, the status
- word is the bitwise OR of the previous status word and the one saved
- in envp. The function returns zero in case the operation was successful,
- a non-zero value otherwise. */
-int
-feupdateenv (const fenv_t *envp)
-{
- fenv_t envcopy;
- unsigned int mxcsr = 0;
- unsigned short sw;
-
- /* Don't want to modify *envp, but want to update environment atomically,
- so take a copy and merge the existing exceptions into it. */
- memcpy (&envcopy, envp, sizeof *envp);
- __asm__ volatile ("fnstsw %0" : "=m" (sw) : );
- if (use_sse)
- __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
- envcopy._fpu._fpu_sw |= (sw & FE_ALL_EXCEPT);
- envcopy._sse_mxcsr |= (mxcsr & FE_ALL_EXCEPT);
-
- return fesetenv (&envcopy);
-}
-
-/* This function clears all of the supported exception flags indicated by
- excepts. The function returns zero in case the operation was successful,
- a non-zero value otherwise. */
-int
-feclearexcept (int excepts)
-{
- fenv_t fenv;
-
- if (excepts & ~FE_ALL_EXCEPT)
- return EINVAL;
-
- /* Need to save/restore whole environment to modify status word. */
- fegetenv (&fenv);
-
- /* Mask undesired bits out. */
- fenv._fpu._fpu_sw &= ~excepts;
- fenv._sse_mxcsr &= ~excepts;
-
- /* Set back into FPU state. */
- return fesetenv (&fenv);
-}
-
-/* This function raises the supported exceptions indicated by
- excepts. If more than one exception bit in excepts is set the order
- in which the exceptions are raised is undefined except that overflow
- (FE_OVERFLOW) or underflow (FE_UNDERFLOW) are raised before inexact
- (FE_INEXACT). Whether for overflow or underflow the inexact exception
- is also raised is also implementation dependent. The function returns
- zero in case the operation was successful, a non-zero value otherwise. */
-int
-feraiseexcept (int excepts)
-{
- fenv_t fenv;
-
- if (excepts & ~FE_ALL_EXCEPT)
- return EINVAL;
-
- /* Need to save/restore whole environment to modify status word. */
- __asm__ volatile ("fnstenv %0" : "=m" (fenv) : );
-
- /* Set desired exception bits. */
- fenv._fpu._fpu_sw |= excepts;
-
- /* Set back into FPU state. */
- __asm__ volatile ("fldenv %0" :: "m" (fenv));
-
- /* And trigger them - whichever are unmasked. */
- __asm__ volatile ("fwait");
-
- return 0;
-}
-
-/* Test whether the exception flags indicated by the parameter except
- are currently set. If any of them are, a nonzero value is returned
- which specifies which exceptions are set. Otherwise the result is zero. */
-int
-fetestexcept (int excepts)
-{
- unsigned short sw;
- unsigned int mxcsr = 0;
-
- if (excepts & ~FE_ALL_EXCEPT)
- return EINVAL;
-
- /* Get status registers. */
- __asm__ volatile ("fnstsw %0" : "=m" (sw) : );
- if (use_sse)
- __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
-
- /* Mask undesired bits out and return result. */
- return (sw | mxcsr) & excepts;
-}
-/* This function stores in the variable pointed to by flagp an
- implementation-defined value representing the current setting of the
- exception flags indicated by excepts. The function returns zero in
- case the operation was successful, a non-zero value otherwise. */
-int
-fegetexceptflag (fexcept_t *flagp, int excepts)
-{
- unsigned short sw;
- unsigned int mxcsr = 0;
-
- if (excepts & ~FE_ALL_EXCEPT)
- return EINVAL;
-
- /* Get status registers. */
- __asm__ volatile ("fnstsw %0" : "=m" (sw) : );
- if (use_sse)
- __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
-
- /* Mask undesired bits out and set result. */
- *flagp = (sw | mxcsr) & excepts;
-
- return 0;
-}
-
-/* This function restores the flags for the exceptions indicated by
- excepts to the values stored in the variable pointed to by flagp. */
-int
-fesetexceptflag (const fexcept_t *flagp, int excepts)
-{
- fenv_t fenv;
-
- if (excepts & ~FE_ALL_EXCEPT)
- return EINVAL;
-
- /* Need to save/restore whole environment to modify status word. */
- fegetenv (&fenv);
-
- /* Set/Clear desired exception bits. */
- fenv._fpu._fpu_sw &= ~excepts;
- fenv._fpu._fpu_sw |= excepts & *flagp;
- fenv._sse_mxcsr &= ~excepts;
- fenv._sse_mxcsr |= excepts & *flagp;
-
- /* Set back into FPU state. */
- return fesetenv (&fenv);
-}
-
-/* Returns the currently selected rounding mode, represented by one of the
- values of the defined rounding mode macros. */
-int
-fegetround (void)
-{
- unsigned short cw;
-
- /* Get control word. We assume SSE and x87 stay in sync. */
- __asm__ volatile ("fnstcw %0" : "=m" (cw) : );
-
- return (cw & FE_CW_ROUND_MASK) >> FE_CW_ROUND_SHIFT;
-}
-
-/* Changes the currently selected rounding mode to round. If round does
- not correspond to one of the supported rounding modes nothing is changed.
- fesetround returns zero if it changed the rounding mode, a nonzero value
- if the mode is not supported. */
-int
-fesetround (int round)
-{
- unsigned short cw;
- unsigned int mxcsr = 0;
-
- /* Will succeed for any valid value of the input parameter. */
- if (round < FE_TONEAREST || round > FE_TOWARDZERO)
- return EINVAL;
-
- /* Get control words. */
- __asm__ volatile ("fnstcw %0" : "=m" (cw) : );
- if (use_sse)
- __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
-
- /* Twiddle bits. */
- cw &= ~FE_CW_ROUND_MASK;
- cw |= (round << FE_CW_ROUND_SHIFT);
- mxcsr &= ~FE_MXCSR_ROUND_MASK;
- mxcsr |= (round << FE_MXCSR_ROUND_SHIFT);
-
- /* Set back into FPU state. */
- __asm__ volatile ("fldcw %0" :: "m" (cw));
- if (use_sse)
- __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));
-
- /* Indicate success. */
- return 0;
-}
-
-/* Returns the currently selected precision, represented by one of the
- values of the defined precision macros. */
-int
-fegetprec (void)
-{
- unsigned short cw;
-
- /* Get control word. */
- __asm__ volatile ("fnstcw %0" : "=m" (cw) : );
-
- return (cw & FE_CW_PREC_MASK) >> FE_CW_PREC_SHIFT;
-}
-
-/* http://www.open-std.org/jtc1/sc22//WG14/www/docs/n752.htm:
-
- The fesetprec function establishes the precision represented by its
- argument prec. If the argument does not match a precision macro, the
- precision is not changed.
-
- The fesetprec function returns a nonzero value if and only if the
- argument matches a precision macro (that is, if and only if the requested
- precision can be established). */
-int
-fesetprec (int prec)
-{
- unsigned short cw;
-
- /* Will succeed for any valid value of the input parameter. */
- switch (prec)
- {
- case FE_FLTPREC:
- case FE_DBLPREC:
- case FE_LDBLPREC:
- break;
- default:
- return 0;
- }
-
- /* Get control word. */
- __asm__ volatile ("fnstcw %0" : "=m" (cw) : );
-
- /* Twiddle bits. */
- cw &= ~FE_CW_PREC_MASK;
- cw |= (prec << FE_CW_PREC_SHIFT);
-
- /* Set back into FPU state. */
- __asm__ volatile ("fldcw %0" :: "m" (cw));
-
- /* Indicate success. */
- return 1;
-}
-
-/* Set up the FPU and SSE environment at the start of execution. */
-void
-_feinitialise (void)
-{
- unsigned int edx, eax;
- extern fenv_t __fe_dfl_env;
-
- /* Check for presence of SSE: invoke CPUID #1, check EDX bit 25. */
- eax = 1;
- __asm__ volatile ("cpuid" : "=d" (edx), "+a" (eax) :: "%ecx", "%ebx");
- /* If this flag isn't set we'll avoid trying to execute any SSE. */
- if ((edx & (1 << 25)) != 0)
- use_sse = true;
-
- /* Reset FPU: extended prec, all exceptions cleared and masked off. */
- __asm__ volatile ("fninit");
- /* The default cw value, 0x37f, is rounding mode zero. The MXCSR has
- no precision control, so the only thing to do is set the exception
- mask bits. */
-
- /* initialize the MXCSR register: mask all exceptions */
- unsigned int mxcsr = __FE_ALL_EXCEPT_X86 << FE_SSE_EXCEPT_MASK_SHIFT;
- if (use_sse)
- __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));
-
- /* Setup unmasked environment, but leave __FE_DENORM masked. */
- feenableexcept (FE_ALL_EXCEPT);
- fegetenv (&fe_nomask_env);
-
- /* Restore default exception masking (all masked). */
- fedisableexcept (FE_ALL_EXCEPT);
-
- /* Finally cache state as default environment. */
- fegetenv (&__fe_dfl_env);
-}
-
+++ /dev/null
-/* fenv.h
-
-This file is part of Cygwin.
-
-This software is a copyrighted work licensed under the terms of the
-Cygwin license. Please consult the file "CYGWIN_LICENSE" for
-details. */
-
-#ifndef _FENV_H
-#define _FENV_H 1
-
-#include <sys/cdefs.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-/* Primary sources:
-
- The Open Group Base Specifications Issue 6:
- http://www.opengroup.org/onlinepubs/000095399/basedefs/fenv.h.html
-
- C99 Language spec (draft n1256):
- <url unknown>
-
- Intel(R) 64 and IA-32 Architectures Software Developer's Manuals:
- http://www.intel.com/products/processor/manuals/
-
- GNU C library manual pages:
- http://www.gnu.org/software/libc/manual/html_node/Control-Functions.html
- http://www.gnu.org/software/libc/manual/html_node/Rounding.html
- http://www.gnu.org/software/libc/manual/html_node/FP-Exceptions.html
- http://www.gnu.org/software/libc/manual/html_node/Status-bit-operations.html
-
- Linux online man page(s):
- http://linux.die.net/man/3/fegetexcept
-
- The documentation quotes these sources for reference. All definitions and
- code have been developed solely based on the information from these specs.
-
-*/
-
-/* Represents the entire floating-point environment. The floating-point
- environment refers collectively to any floating-point status flags and
- control modes supported by the implementation.
- In this implementation, the struct contains the state information from
- the fstenv/fnstenv instructions and a copy of the SSE MXCSR, since GCC
- uses SSE for a lot of floating-point operations. (Cygwin assumes i686
- or above these days, as does the compiler.) */
-
-typedef struct _fenv_t
-{
- struct _fpu_env_info {
- unsigned int _fpu_cw; /* low 16 bits only. */
- unsigned int _fpu_sw; /* low 16 bits only. */
- unsigned int _fpu_tagw; /* low 16 bits only. */
- unsigned int _fpu_ipoff;
- unsigned int _fpu_ipsel;
- unsigned int _fpu_opoff;
- unsigned int _fpu_opsel; /* low 16 bits only. */
- } _fpu;
- unsigned int _sse_mxcsr;
-} fenv_t;
-
-/* Represents the floating-point status flags collectively, including
- any status the implementation associates with the flags. A floating-point
- status flag is a system variable whose value is set (but never cleared)
- when a floating-point exception is raised, which occurs as a side effect
- of exceptional floating-point arithmetic to provide auxiliary information.
- A floating-point control mode is a system variable whose value may be
- set by the user to affect the subsequent behavior of floating-point
- arithmetic. */
-
-typedef __uint32_t fexcept_t;
-
-/* The <fenv.h> header shall define the following constants if and only
- if the implementation supports the floating-point exception by means
- of the floating-point functions feclearexcept(), fegetexceptflag(),
- feraiseexcept(), fesetexceptflag(), and fetestexcept(). Each expands to
- an integer constant expression with values such that bitwise-inclusive
- ORs of all combinations of the constants result in distinct values. */
-
-#define FE_DIVBYZERO (1 << 2)
-#define FE_INEXACT (1 << 5)
-#define FE_INVALID (1 << 0)
-#define FE_OVERFLOW (1 << 3)
-#define FE_UNDERFLOW (1 << 4)
-
-/* The <fenv.h> header shall define the following constant, which is
- simply the bitwise-inclusive OR of all floating-point exception
- constants defined above: */
-
-/* in agreement w/ Linux the subnormal exception will always be masked */
-#define FE_ALL_EXCEPT \
- (FE_INEXACT | FE_UNDERFLOW | FE_OVERFLOW | FE_DIVBYZERO | FE_INVALID)
-
-/* The <fenv.h> header shall define the following constants if and only
- if the implementation supports getting and setting the represented
- rounding direction by means of the fegetround() and fesetround()
- functions. Each expands to an integer constant expression whose values
- are distinct non-negative vales. */
-
-#define FE_DOWNWARD (1)
-#define FE_TONEAREST (0)
-#define FE_TOWARDZERO (3)
-#define FE_UPWARD (2)
-
-/* Only Solaris and QNX implement fegetprec/fesetprec. As Solaris, use the
- values defined by http://www.open-std.org/jtc1/sc22//WG14/www/docs/n752.htm
- QNX defines different values. */
-#if __MISC_VISIBLE
-#define FE_FLTPREC (0)
-#define FE_DBLPREC (2)
-#define FE_LDBLPREC (3)
-#endif
-
-/* The <fenv.h> header shall define the following constant, which
- represents the default floating-point environment (that is, the one
- installed at program startup) and has type pointer to const-qualified
- fenv_t. It can be used as an argument to the functions within the
- <fenv.h> header that manage the floating-point environment. */
-
-extern const fenv_t *_fe_dfl_env;
-#define FE_DFL_ENV (_fe_dfl_env)
-
-/* Additional implementation-defined environments, with macro
- definitions beginning with FE_ and an uppercase letter,and having
- type "pointer to const-qualified fenv_t",may also be specified by
- the implementation. */
-
-#if __GNU_VISIBLE
-/* If possible, the GNU C Library defines a macro FE_NOMASK_ENV which
- represents an environment where every exception raised causes a trap
- to occur. You can test for this macro using #ifdef. It is only defined
- if _GNU_SOURCE is defined. */
-extern const fenv_t *_fe_nomask_env;
-#define FE_NOMASK_ENV (_fe_nomask_env)
-#endif /* __GNU_VISIBLE */
-
-
-/* The following shall be declared as functions and may also be
- defined as macros. Function prototypes shall be provided. */
-extern int feclearexcept (int __excepts);
-extern int fegetexceptflag (fexcept_t *__flagp, int __excepts);
-extern int feraiseexcept (int __excepts);
-extern int fesetexceptflag (const fexcept_t *__flagp, int __excepts);
-extern int fetestexcept (int __excepts);
-extern int fegetround (void);
-extern int fesetround (int __round);
-extern int fegetenv (fenv_t *__envp);
-extern int feholdexcept (fenv_t *__envp);
-extern int fesetenv (const fenv_t *__envp);
-extern int feupdateenv (const fenv_t *__envp);
-
-#if __GNU_VISIBLE
-/* These are GNU extensions defined in glibc. */
-extern int feenableexcept (int __excepts);
-extern int fedisableexcept (int __excepts);
-extern int fegetexcept (void);
-#endif
-
-#if __MISC_VISIBLE
-extern int fegetprec (void);
-extern int fesetprec (int __prec);
-#endif
-
-#ifdef __INSIDE_CYGWIN__
-/* This is Cygwin-custom, not from the standard, for use in the Cygwin CRT. */
-extern void _feinitialise ();
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _FENV_H */