/* * these lines are here to allow the compilation of this file * in isolation and the lint'ing of the same with 'splint'!! */ #include #define TEST #ifdef TEST #include "../../../arch/powerpc64/bits/fenv.h" typedef unsigned int fe_csr_t; extern unsigned int fe_get_sr(); extern void fe_set_sr(unsigned int); extern unsigned int fe_get_cr(); extern void fe_set_cr(unsigned int); extern double fe_get_csr_f(); extern void fe_set_csr_f(double); extern void fe_get_env(fenv_t *e); extern void fe_set_env(fenv_t *e); extern void fe_set_env_but_keep_raised_excepts(fenv_t *e); extern void fe_get_env_and_clear_all_excepts(fenv_t *e); extern void fescrubexcept(void); extern fenv_t fe_dfl_env; extern int FE_ROUNDING_TYPE(int); #endif /* * fenv-generic.c (use 'fenv-trivial.v' for soft-float environments): * * All architectures which fit the generic mould are assumed to have a status * register (sr) which contains the exception flags are stored and a control * register (cr) which contains the rounding mode. These registers may be one * and the same or they may be distinct. All architecture-dependent features * live in an individual 'fenv.c' file which contains function definitions, * mainly of one-line embedded assembler, declarations, macros and typedef's: * * a) the type 'fe_csr_t' which can store both 'sr' and 'cr', * - this is an unsigned int on all known architectures * b) routines to copy 'sr' and 'cr' into memory, respectively * - fe_csr_ fe_get_sr() and fe_csr_t fe_get_cr() * c) routines to load 'sr' and 'cr' from memory, respectively * - void fe_set_sr(fe_csr_t) and void fe_set_cr(fe_csr_t) * d) routines to copy and load an architecture's floating point environment, * - fe_get_env(fenv_t *) and fe_set_env(fenv_t *) respectively * - 'fenv_t' is defined in an architectures own 'fenv.h' file * e) a routine to reset an architecture's floating point environment * - like fe_set_env(fenv_t *) but currently raised exceptions stay raised * f) a routine to clone an architecture's floating point environment * - like fe_get_env(fenv_t *) but currently raised exceptions get cleared * g) a macro yielding a ratified exception list during a CLEAR operation * - FE_RATIFY_CLEAR_EXCEPTS * h) a macro yielding a ratified exception list during a RAISE operation * - FE_RATIFY_RAISE_EXCEPTS * i) macros which maps from the rounding mode to a rounding style * - FE_ROUNDING_STYLE * j) a (macro) bit-mask of all legal rounding modes * - FE_ROUNDING_MASK * - it returns the value to be returned by the FLT_ROUNDS macro * k) an initialization for the default fenv_t * - i.e. static const fenv_t fe_dfl_env = ..... * * (k) is an implicitly hidden, IEEE 754 default floating point environment in * which all exceptions are clear, non-stop (or continue on exception) mode is * active, and rounding to nearest will be used for all arithmetic. * * For most architectures, the macros (g) and (h) can be allowed to default * to the handling done by the routine 'fe_ratified_excepts'. This routine * reviews all the exceptions implicit in the mask 'excepts' fed to all the * 'fe*' routines, discarding any inappropriate bits. In this case, these * macros are defined automatically as seen below. Only one architecture, * the PowerPC, does not fit this mould, and its definitions are explained * in detail in that architecture's own 'fenv.c'. * * If the architecture-specific 'fenv.c' file does not define the mask (j), * all four IEEE 754 rounding modes are assumed to exist, a mask which is * the OR'd combinations of those modes will be automatically created, as * well as the macro (i) which maps the machine-dependent rounding mode of * the control register to the machine-independent rounding style which is * provided by FLT_ROUNDS. If all four modes are not available, this mask * and macro must be provided in the architecture-specific 'fenv.c' file. * Apart from architectures with soft floating point, only the Hitachi * SuperH RISC chip (sh) needs such definitions. * * After all these definitions and declarations, the architectures-specific * file must then also include this file, i.e. 'fenv-generic,c' following * current practice as * * #include "../fenv-generic.c" */ #ifndef FE_RATIFIED_RAISE_EXCEPTS #define FE_RATIFIED_RAISE_EXCEPTS(e) fe_ratified_excepts(e) #endif #ifndef FE_RATIFIED_CLEAR_EXCEPTS #define FE_RATIFIED_CLEAR_EXCEPTS(e) fe_ratified_excepts(e) #endif /* * take a bit mask which should be just an OR'd combination of IEEE 754 * exceptions and discard any bit which is not one of those exceptions. */ static inline int fe_ratified_excepts(int excepts) { return excepts & FE_ALL_EXCEPT; } #ifndef FE_ROUNDING_MASK #define FE_ROUNDING_MASK (FE_TOWARDZERO|FE_DOWNWARD|FE_UPWARD) /* * Map an architecture-dependent rounding mode to that needed by FLT_ROUNDS. * There are better ways to do this but none of those reads as well as this. */ static inline int fe_rounding_style(int rounding) { return ( rounding == FE_DOWNWARD ? 3 : rounding == FE_UPWARD ? 2 : (int) (rounding == FE_TONEAREST) ); } #define FE_ROUNDING_STYLE(m) fe_rounding_style(m) #endif /* * mask out the bits with 'bits' from 'mask' ensuring type compatability */ static inline fe_csr_t fe_clear_bits(fe_csr_t mask, int bits) { return mask & ~((fe_csr_t) bits); } /* * get the rounding mask */ int fegetround(void) { return (int) (fe_get_cr() & FE_ROUNDING_MASK); } /* * internals for FLT_ROUNDS */ int __flt_rounds() { return FE_ROUNDING_STYLE(fegetround()); } /* * set the rounding mode - returning (-1) if input is not a valid mode */ int fesetround(int rounding_mode) { if ((rounding_mode & FE_ROUNDING_MASK) != rounding_mode) return -1; fe_set_cr(fe_clear_bits(fe_get_cr(), FE_ROUNDING_MASK) | rounding_mode); return 0; } /* * returns a word in which the bits (which represent IEEE 754 exceptions) * are set that were also set in the argument 'excepts' and for which the * corresponding exception is currently set. The only legal bits that can * be given in the argment are those which correspond to an exception. */ int fetestexcept(int excepts) { return (int) (fe_get_sr() & fe_ratified_excepts(excepts)); } /* * clears the supported exceptions represented by the bits in its argument. */ int feclearexcept(int excepts) { excepts = FE_RATIFIED_CLEAR_EXCEPTS(excepts); fe_set_sr(fe_clear_bits(fe_get_sr(), excepts)); return 0; } /* * raises the supported exceptions represented by the bits in its argument. */ int feraiseexcept(int excepts) { /* * assume single OP is faster than double OP */ const float zero = (float) 0; const float one = (float) 1; const float large = 2.076919e+34; // 2^(+108) const float small = 4.814826e-35; // 2^(-108) volatile float x; /* * if it is just a simple exception, arithmetic expressions are optimal */ switch(excepts) { case FE_INVALID: x = zero, x /= x; break; case FE_DIVBYZERO: x = zero, x = one / x; break; case FE_INEXACT: x = small, x += one; break; case FE_OVERFLOW | FE_INEXACT: x = large, x *= x; break; case FE_UNDERFLOW | FE_INEXACT: x = small, x *= x; break; default: /* * if multiple exceptions exist, a sledgehammer is viable */ excepts = FE_RATIFIED_RAISE_EXCEPTS(excepts); fe_set_sr(fe_get_sr() | excepts); break; } return 0; } /* * copy the complete current exception state into *fp */ int fegetexceptflag(fexcept_t *fp, int excepts) { *fp = (fexcept_t) fetestexcept(excepts); return 0; } /* * load the exception state from *fp, but intelligently avoiding * clearing (raising) exceptions which are already cleared (raised). */ int fesetexceptflag(const fexcept_t *fp, int excepts) { const fe_csr_t sr = fe_get_sr(); const fe_csr_t fx = (fe_csr_t) *fp; const int requested = excepts & FE_ALL_EXCEPT; const int excepts_old = (int) (sr & requested); const int excepts_new = (int) (fx & requested); const int essential = excepts_old ^ excepts_new; const int clear = FE_RATIFIED_CLEAR_EXCEPTS(excepts_old & essential); const int raise = FE_RATIFIED_RAISE_EXCEPTS(excepts_new & essential); if ((clear | raise) != 0) fe_set_sr(fe_clear_bits(sr, clear) | raise); return 0; } /* * copy the floating point environment into memory */ int fegetenv(fenv_t *e) { fe_get_env(e); return 0; } /* * load the floating point environment from memory */ int fesetenv(fenv_t *e) { fe_set_env(e != FE_DFL_ENV ? e : &fe_dfl_env); return 0; } /* * as for fesetenv but retain the exception data */ int feupdateenv(fenv_t *e) { fe_set_env_but_keep_raised_excepts(e != FE_DFL_ENV ? e : &fe_dfl_env); return 0; } /* * as for fegetenv but clear the exception states */ int feholdexcept(fenv_t *e) { fe_get_env_and_clear_all_excepts(e); return 0; }