From 787282dede7f134fdb22155cee0c35172e3e28f3 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Tue, 24 Oct 2023 08:37:16 -0300 Subject: x86: Do not raises floating-point exception traps on fesetexceptflag (BZ 30990) According to ISO C23 (7.6.4.4), fesetexcept is supposed to set floating-point exception flags without raising a trap (unlike feraiseexcept, which is supposed to raise a trap if feenableexcept was called with the appropriate argument). The flags can be set in the 387 unit or in the SSE unit. When we need to clear a flag, we need to do so in both units, due to the way fetestexcept is implemented. When we need 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. Co-authored-by: Adhemerval Zanella Reviewed-by: Carlos O'Donell --- sysdeps/i386/fpu/fsetexcptflg.c | 63 +++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 21 deletions(-) (limited to 'sysdeps/i386/fpu') diff --git a/sysdeps/i386/fpu/fsetexcptflg.c b/sysdeps/i386/fpu/fsetexcptflg.c index e724b7d6fd..480165cff9 100644 --- a/sysdeps/i386/fpu/fsetexcptflg.c +++ b/sysdeps/i386/fpu/fsetexcptflg.c @@ -17,42 +17,63 @@ . */ #include -#include -#include #include -#include int __fesetexceptflag (const fexcept_t *flagp, int excepts) { - fenv_t temp; + /* The flags can be set in the 387 unit or in the SSE unit. When we need to + clear a flag, we need to do so in both units, due to the way fetestexcept + is implemented. + When we need 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. */ - /* Get the current environment. We have to do this since we cannot - separately set the status word. */ - __asm__ ("fnstenv %0" : "=m" (*&temp)); + fenv_t temp; - temp.__status_word &= ~(excepts & FE_ALL_EXCEPT); - temp.__status_word |= *flagp & excepts & FE_ALL_EXCEPT; + excepts &= FE_ALL_EXCEPT; - /* Store the new status word (along with the rest of the environment. - Possibly new exceptions are set but they won't get executed unless - the next floating-point instruction. */ - __asm__ ("fldenv %0" : : "m" (*&temp)); + /* Get the current x87 FPU environment. We have to do this since we + cannot separately set the status word. + Note: fnstenv masks all floating-point exceptions until the fldenv + or fldcw below. */ + __asm__ ("fnstenv %0" : "=m" (*&temp)); - /* If the CPU supports SSE, we set the MXCSR as well. */ if (CPU_FEATURE_USABLE (SSE)) { - unsigned int xnew_exc; + unsigned int mxcsr; + + /* Clear relevant flags. */ + temp.__status_word &= ~(excepts & ~ *flagp); - /* Get the current MXCSR. */ - __asm__ ("stmxcsr %0" : "=m" (*&xnew_exc)); + /* Store the new status word (along with the rest of the environment). */ + __asm__ ("fldenv %0" : : "m" (*&temp)); - /* Set the relevant bits. */ - xnew_exc &= ~(excepts & FE_ALL_EXCEPT); - xnew_exc |= *flagp & excepts & FE_ALL_EXCEPT; + /* And now similarly for SSE. */ + __asm__ ("stmxcsr %0" : "=m" (*&mxcsr)); + + /* Clear or set relevant flags. */ + mxcsr ^= (mxcsr ^ *flagp) & excepts; /* Put the new data in effect. */ - __asm__ ("ldmxcsr %0" : : "m" (*&xnew_exc)); + __asm__ ("ldmxcsr %0" : : "m" (*&mxcsr)); + } + else + { + /* Clear or set relevant flags. */ + temp.__status_word ^= (temp.__status_word ^ *flagp) & excepts; + + if ((~temp.__control_word) & temp.__status_word & excepts) + { + /* Setting the exception flags may trigger a trap (at the next + floating-point instruction, but that does not matter). + ISO C 23 ยง 7.6.4.5 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)); } /* Success. */ -- cgit 1.4.1