|
Message-ID: <alpine.LRH.2.02.2001261420060.4568@key0.esi.com.au> Date: Sun, 26 Jan 2020 14:28:17 +1100 (AEDT) From: Damian McGuckin <damianm@....com.au> To: musl@...ts.openwall.com Subject: Re: Considering x86-64 fenv.s to C Latest below - Apologies if I missed anything about which we spoke. * Lots more comments about architecture. * better explanation about dealing with those PowerPC issues. * some naming is improved I will shortly post some guesses at what I think some hardware specific versions of fenv.c need to be. Not sure how best to handle these because the way used currently is to pull in a template from directory above as seen in .../musl-1.1.24/src/fenv/arm/fenv.c which just does #include "../fenv.c" which includes the most trivial of fenv(3) implementations possible. The following passes 'splint'. /* * fenv-generic.c: * * All generic architectures are assumed to have * * a) a status register in which the exception flags exist * b) a control register in which the rounding mode exists * c) an environment structure, fenv_t * * Routines must be provided with the architecture specific fenv.c routines * to both store (copy) these register into memory and load them from memory. * * If the control & status registers are distinct, then the declaration * * static unsigned int fe_get_sr_arch() { ... } * static void fe_set_sr_arch(unsigned int) { ... } * static unsigned int fe_get_cr_arch() { ... } * static void fe_set_cr_arch(unsigned int) { ... } * * and the macros that use these internal routines * * #define fe_get_sr fe_get_sr_arch * #define fe_set_sr fe_set_sr_arch * #define fe_get_cr fe_get_cr_arch * #define fe_set_cr fe_set_cr_arch * * need to be provided. * * If the control & status register are one and the same, then the declaration * * static unsigned int fe_get_csr_arch() { ... } * static void fe_set_csr_arch(unsigned int) { ... } * * needs to be provided. Those fe_[gs]et_[sc] macros are auto-created. * * Also needed are definitions that respectively * * a) allows a (memory) fenv_t object *e to be filled with the contents * of the current floating point environment, and * b) allows the current floating point environment to mirror the state * of the contents of some (memory) fenv_t object *e. * * #define fe_get_e(e) ... * #define fe_set_e(e) ... * * The default state of the floating point environment must be defined as * * #define FE_DFL_ENV_DATA .... * * Where an architecture like a PowerPC64 supports a list of causes for an * INVALID exceptions, the bit mask encapsulating all those causes must be * defined, FE_ALL_VALID_CAUSES, as well as the bit field associated with * an FE_INVALID exception being caused 'by (an explicit software) request'. * They will commonly be defined using the existing ABI as: * * #define FE_ALL_INVALID_CAUSES FE_ALL_INVALID * #define FE_INVALID_BY_REQUEST FE_INVALID_SOFTWARE * * Where no causes exist, the above 2 lines must NEVER be defined! * * Once the above inline functions and appropriate macros have been defined * within an 'fenv.c' for a given architecture, the ''fenv.c' should pull * this file into its compilation space following existing practice like * * #include "../fenv-generic.c" */ /* * default inspection and modification of FPU status and control registers */ #ifndef fe_get_cr #define fe_get_cr fe_get_csr_arch #endif #ifndef fe_set_cr #define fe_set_cr fe_set_csr_arch #endif #ifndef fe_get_sr #define fe_get_sr fe_get_csr_arch #endif #ifndef fe_set_sr #define fe_set_sr fe_set_csr_arch #endif /* * Compute the rounding mask with some rigid logic */ #ifndef FE_TONEAREST #define FE_TONEAREST 0 #define ROUND_MASK ((unsigned int) 0) #else #ifndef FE_TOWARDZERO #define ROUND_MASK ((unsigned int) 0) #else #ifdef FE_DOWNWARD #ifdef FE_UPWARD #define ROUND_MASK ((unsigned int) (FE_DOWNWARD|FE_UPWARD|FE_TOWARDZERO)) #else #define ROUND_MASK UNSUPPORTED rounding #endif #else #ifdef FE_UPWARD #define ROUND_MASK UNSUPPORTED rounding #else #define ROUND_MASK ((unsigned int) (FE_TOWARDZERO)) #endif #endif #endif #endif /* * The PowerPC architecture has the concept of an INVALID exception cause, * basically the reason why an FE_INVALID exception has occurred. There are * nine such causes, encapsulated in a non-contiguous mask of 9 bits, the * macro called (herein) FE_ALL_INVALID_CAUSES for clarity. Instructions * which involve invalid operations will set the CAUSE bit first, that * action implicitly raising the FE_INVALID exception, i.e. an FE_INVALID * implies the existence of a CAUSE bit, and vica versa. Even on this a * PowerPC, MUSL supports only one of those 9 hardware causes, the macro * called (herein) FE_INVALID_BY_REQUEST. These nomenclature is used to * document that not only do these macros refer to some cause, but that the * FE_INVALID exception has been caused 'by (an explicit software) request'. * * On any other architecture, no such definitions will exist of the mask of * all such causes, the macro FE_ALL_INVALID_CAUSES, nor will the macro * FE_INVALID_BY_REQUEST exist. In that case, the former is just defined as * zero, implying that there are NO causes, while the latter will be just * defined as FE_INVALID to keep the compiler happy (as it is never used * in any logic). See fenv.c for the PowerPC64 for more detailed words on * this interesting, useful, fascinating, but highly non-standard feature. * * The above is important because ignoring it stabs MUSL in the back. When * it is desired that an FE_INVALID exception be cleared on an architecture * with a bit mask of causes as to why any FE_INVALID occurred, any CAUSE * bit field must also to be cleared. If this is not done, any set CAUSE * bit, i.e. one left uncleared, will cause an FE_INVALID exception to be * re-raised when the register image containing those uncleared bits is * fed back into 'fe_set_sr' where the instruction * 'Move to FPSCR' is * used. The single line of code following doing such handling is avoided * on architectures which do not need it because the zero value of the * FE_ALL_INVALID_CAUSE mask should mean that this line of code disappears * into oblivion by compile-time optimization. * * On architectures where a raised FE_INVALID exception implies some CAUSE * bit is also raised (or set), consistently is provided by saying that the * FE_INVALID is caused 'by (an explicit software) request', the bit named * (herein) as FE_INVALID_BY_REQUEST. The single line of code doing such * handling is again avoided on architectures which do not need it because * the zero value of the FE_ALL_INVALID_CAUSE mask should mean that this * line of code is optimized into oblivion at compile-time. To keep the * compiler happy, FE_INVALID_BY_REQUEST is simply set to FE_INVALID. */ #ifndef FE_ALL_INVALID_CAUSES #define FE_ALL_INVALID_CAUSES 0 #define FE_INVALID_BY_REQUEST FE_INVALID #endif static inline void __cleargeneric(int excepts) { /* * Address where FE_INVALID is to be cleared on an architecture with a * matching CAUSE to say why that FE_INVALID occurred. To handle this, * every one of the CAUSE bits must also be cleared - see above. */ if (FE_ALL_INVALID_CAUSES != 0 && (excepts & FE_INVALID) != 0) excepts |= FE_ALL_INVALID_CAUSES; fe_set_sr(fe_get_sr() & ~excepts); } static inline void __raisegeneric(int excepts) { /* * Address where FE_INVALID is to be raised on an architecture demanding * a matching CAUSE to say why the FE_INVALID is being raised. To handle * this, say it is caused 'By (Explicit Software) Request' - see above. */ if (FE_ALL_INVALID_CAUSES != 0 && (excepts & FE_INVALID) != 0) excepts |= FE_INVALID_BY_REQUEST; fe_set_sr(fe_get_sr() | excepts); } static inline void __raisecleverly(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 more than one exception exists, a sledgehammer is viable */ __raisegeneric(excepts); break; } } int feclearexcept(int excepts) { __cleargeneric(excepts & FE_ALL_EXCEPT); return 0; } int feraiseexcept(int excepts) { __raisecleverly(excepts & FE_ALL_EXCEPT); return 0; } int fetestexcept(int excepts) { return (int) (fe_get_sr() & (excepts & FE_ALL_EXCEPT)); } int fegetround(void) { return (int) (fe_get_cr() & ROUND_MASK); } int fesetround(int rounding_mode) { if ((rounding_mode & ~ROUND_MASK) != 0) return -1; fe_set_cr((fe_get_cr() & ~ROUND_MASK) | (rounding_mode & ROUND_MASK)); return 0; } int fegetenv(fenv_t *envp) { fe_get_e(envp); return 0; } int fesetenv(fenv_t *envp) { const fenv_t envpd = FE_DFL_ENV_DATA; const fenv_t *e = envp == FE_DFL_ENV ? &envpd : envp; fe_set_e(e); return 0; } Regards - Damian Pacific Engineering Systems International, 277-279 Broadway, Glebe NSW 2037 Ph:+61-2-8571-0847 .. Fx:+61-2-9692-9623 | unsolicited email not wanted here Views & opinions here are mine and not those of any past or present employer
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.