diff options
Diffstat (limited to 'sysdeps/i386/fpu/fesetexcept.c')
-rw-r--r-- | sysdeps/i386/fpu/fesetexcept.c | 46 |
1 files changed, 42 insertions, 4 deletions
diff --git a/sysdeps/i386/fpu/fesetexcept.c b/sysdeps/i386/fpu/fesetexcept.c index 18949e982a..58f577d93d 100644 --- a/sysdeps/i386/fpu/fesetexcept.c +++ b/sysdeps/i386/fpu/fesetexcept.c @@ -17,15 +17,53 @@ <https://www.gnu.org/licenses/>. */ #include <fenv.h> +#include <ldsodefs.h> int fesetexcept (int excepts) { - fenv_t temp; + /* The flags can be set in the 387 unit or in the SSE unit. To set a flag, + it is sufficient to do it in the SSE unit, because that is guaranteed to + not trap. However, on i386 CPUs that have only a 387 unit, set the flags + in the 387, as long as this cannot trap. */ - __asm__ ("fnstenv %0" : "=m" (*&temp)); - temp.__status_word |= excepts & FE_ALL_EXCEPT; - __asm__ ("fldenv %0" : : "m" (*&temp)); + excepts &= FE_ALL_EXCEPT; + + if (CPU_FEATURE_USABLE (SSE)) + { + /* Get the control word of the SSE unit. */ + unsigned int mxcsr; + __asm__ ("stmxcsr %0" : "=m" (*&mxcsr)); + + /* Set relevant flags. */ + mxcsr |= excepts; + + /* Put the new data in effect. */ + __asm__ ("ldmxcsr %0" : : "m" (*&mxcsr)); + } + else + { + fenv_t temp; + + /* Note: fnstenv masks all floating-point exceptions until the fldenv + or fldcw below. */ + __asm__ ("fnstenv %0" : "=m" (*&temp)); + + /* Set relevant flags. */ + temp.__status_word |= excepts; + + if ((~temp.__control_word) & excepts) + { + /* Setting the exception flags may trigger a trap (at the next + floating-point instruction, but that does not matter). + ISO C23 (7.6.4.4) does not allow it. */ + __asm__ volatile ("fldcw %0" : : "m" (*&temp.__control_word)); + return -1; + } + + /* Store the new status word (along with the rest of the environment). */ + __asm__ ("fldenv %0" : : "m" (*&temp)); + } return 0; } |