diff options
author | bde <bde@FreeBSD.org> | 2008-01-11 18:27:01 +0000 |
---|---|---|
committer | bde <bde@FreeBSD.org> | 2008-01-11 18:27:01 +0000 |
commit | b1a379ee656f7f3ee5bb8d3f47b2fd30ee48b8eb (patch) | |
tree | 16a832da476a257c12b147ebb2243e595b3218a5 /sys/i386/include/ieeefp.h | |
parent | 466cc1c021f230db7007d6a57d79d2cefb5c0993 (diff) | |
download | FreeBSD-src-b1a379ee656f7f3ee5bb8d3f47b2fd30ee48b8eb.zip FreeBSD-src-b1a379ee656f7f3ee5bb8d3f47b2fd30ee48b8eb.tar.gz |
Separate fpresetsticky() from the other fpset functions so that the
others can be replaced cleanly by the amd64 versions. There is no
current amd64 version to merge, but there is an old one which is
similar.
Fix the following bugs in fpresetsticky():
- garbage args clobbered non-sticky bits in the status register
- the return value was usually garbage since it was masked with the
arg instead of with the field selector.
Optimize fpresetsticky() to avoid using the environment as in
feclearexcept() (use only fnclex() if possible) and also to avoid
using fnclex() for null changes. The second of these optimizations
might not be so good since its branch might cost more than it saves.
Diffstat (limited to 'sys/i386/include/ieeefp.h')
-rw-r--r-- | sys/i386/include/ieeefp.h | 31 |
1 files changed, 27 insertions, 4 deletions
diff --git a/sys/i386/include/ieeefp.h b/sys/i386/include/ieeefp.h index 37068e7..22b4f5d 100644 --- a/sys/i386/include/ieeefp.h +++ b/sys/i386/include/ieeefp.h @@ -115,6 +115,7 @@ typedef enum { #ifdef __GNUCLIKE_ASM #define __fldenv(addr) __asm __volatile("fldenv %0" : : "m" (*(addr))) +#define __fnclex() __asm __volatile("fnclex") #define __fnstcw(addr) __asm __volatile("fnstcw %0" : "=m" (*(addr))) #define __fnstenv(addr) __asm __volatile("fnstenv %0" : "=m" (*(addr))) #define __fnstsw(addr) __asm __volatile("fnstsw %0" : "=m" (*(addr))) @@ -165,8 +166,6 @@ __fpsetreg(int _m, int _reg, int _fld, int _off) return _p; } -#endif /* __GNUCLIKE_ASM */ - /* * SysV/386 FP control interface */ @@ -185,8 +184,32 @@ __fpsetreg(int _m, int _reg, int _fld, int _off) (FP_MSKS_FLD >> FP_MSKS_OFF)) #define fpgetsticky() ((fp_except_t) \ ((__fpgetreg(FP_STKY_REG) & FP_STKY_FLD) >> FP_STKY_OFF)) -#define fpresetsticky(m) ((fp_except_t) \ - __fpsetreg(0, FP_STKY_REG, (m), FP_STKY_OFF)) + +static __inline fp_except_t +fpresetsticky(fp_except_t _m) +{ + struct { + unsigned _cw; + unsigned _sw; + unsigned _other[5]; + } _env; + fp_except_t _p; + + _m &= FP_STKY_FLD >> FP_STKY_OFF; + _p = fpgetsticky(); + if ((_p & ~_m) == _p) + return (_p); + if ((_p & ~_m) == 0) { + __fnclex(); + return (_p); + } + __fnstenv(&_env); + _env._sw &= ~_m; + __fldenv(&_env); + return (_p); +} + +#endif /* __GNUCLIKE_ASM */ /* Suppress prototypes in the MI header. */ #define _IEEEFP_INLINED_ 1 |