Oops, this code has a bug on x86_64 and i386: It may leave the exception
traps masked, due to a side effect of the 'fnsetenv' instruction. This
patch should fix it.


2023-10-30  Bruno Haible  <br...@clisp.org>

        fenv-exceptions-tracking-{c99,c23}: Fix the x86_64 and i386 case.
        * lib/fenv-except-tracking-clear.c (feclearexcept): Make sure to restore
        the exception trap bits in all cases.
        * lib/fenv-except-tracking-raise.c (feraiseexcept): Likewise.
        * lib/fenv-except-tracking-set.c (fesetexcept): Likewise.

From 6d2dfab57ddfad6bede47999eced62e517e88efd Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Mon, 30 Oct 2023 16:32:30 +0100
Subject: [PATCH 01/14] fenv-exceptions-tracking-{c99,c23}: Fix the x86_64 and
 i386 case.

* lib/fenv-except-tracking-clear.c (feclearexcept): Make sure to restore
the exception trap bits in all cases.
* lib/fenv-except-tracking-raise.c (feraiseexcept): Likewise.
* lib/fenv-except-tracking-set.c (fesetexcept): Likewise.
---
 ChangeLog                        |  8 ++++++++
 lib/fenv-except-tracking-clear.c |  7 +++----
 lib/fenv-except-tracking-raise.c | 16 ++++++----------
 lib/fenv-except-tracking-set.c   | 20 +++++++++-----------
 4 files changed, 26 insertions(+), 25 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 26686c3fb8..1fc40e4ae2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2023-10-30  Bruno Haible  <br...@clisp.org>
+
+	fenv-exceptions-tracking-{c99,c23}: Fix the x86_64 and i386 case.
+	* lib/fenv-except-tracking-clear.c (feclearexcept): Make sure to restore
+	the exception trap bits in all cases.
+	* lib/fenv-except-tracking-raise.c (feraiseexcept): Likewise.
+	* lib/fenv-except-tracking-set.c (fesetexcept): Likewise.
+
 2023-10-29  Bruno Haible  <br...@clisp.org>
 
 	fenv-exceptions-tracking-c23: Add tests.
diff --git a/lib/fenv-except-tracking-clear.c b/lib/fenv-except-tracking-clear.c
index 22c85c98e0..9819832a5f 100644
--- a/lib/fenv-except-tracking-clear.c
+++ b/lib/fenv-except-tracking-clear.c
@@ -48,12 +48,11 @@ feclearexcept (int exceptions)
 
   /* Clear the bits in the 387 unit.  */
   x86_387_fenv_t env;
-  unsigned short orig_status_word;
   __asm__ __volatile__ ("fnstenv %0" : "=m" (*&env));
-  orig_status_word = env.__status_word;
+  /* Note: fnstenv masks all floating-point exceptions until the fldenv
+     below.  */
   env.__status_word &= ~exceptions;
-  if (env.__status_word != orig_status_word)
-    __asm__ __volatile__ ("fldenv %0" : : "m" (*&env));
+  __asm__ __volatile__ ("fldenv %0" : : "m" (*&env));
 
   if (CPU_HAS_SSE ())
     {
diff --git a/lib/fenv-except-tracking-raise.c b/lib/fenv-except-tracking-raise.c
index 8699662ef5..c263151e60 100644
--- a/lib/fenv-except-tracking-raise.c
+++ b/lib/fenv-except-tracking-raise.c
@@ -106,18 +106,14 @@ feraiseexcept (int exceptions)
 
       /* Set the bits in the 387 unit.  */
       x86_387_fenv_t env;
-      unsigned short orig_status_word;
       __asm__ __volatile__ ("fnstenv %0" : "=m" (*&env));
-      orig_status_word = env.__status_word;
+      /* Note: fnstenv masks all floating-point exceptions until the fldenv
+         below.  */
       env.__status_word |= exceptions;
-      if (env.__status_word != orig_status_word)
-        {
-          __asm__ __volatile__ ("fldenv %0" : : "m" (*&env));
-          /* A trap (if enabled) is triggered only at the next floating-point
-             instruction.  Force it to occur here.  */
-          __asm__ __volatile__ ("fwait");
-        }
-
+      __asm__ __volatile__ ("fldenv %0" : : "m" (*&env));
+      /* A trap (if enabled) is triggered only at the next floating-point
+         instruction.  Force it to occur here.  */
+      __asm__ __volatile__ ("fwait");
     }
 
 #  endif
diff --git a/lib/fenv-except-tracking-set.c b/lib/fenv-except-tracking-set.c
index 73b566cb01..d4237015e8 100644
--- a/lib/fenv-except-tracking-set.c
+++ b/lib/fenv-except-tracking-set.c
@@ -69,21 +69,19 @@ fesetexcept (int exceptions)
     {
       /* Set the flags in the 387 unit.  */
       x86_387_fenv_t env;
-      unsigned short orig_status_word;
       __asm__ __volatile__ ("fnstenv %0" : "=m" (*&env));
-      orig_status_word = env.__status_word;
+      /* Note: fnstenv masks all floating-point exceptions until the fldenv
+         or fldcw below.  */
       env.__status_word |= exceptions;
-      if (env.__status_word != orig_status_word)
+      if ((~env.__control_word) & exceptions)
         {
-          if ((~env.__control_word) & exceptions)
-            {
-              /* 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.4 does not allow it.  */
-              return -1;
-            }
-          __asm__ __volatile__ ("fldenv %0" : : "m" (*&env));
+          /* 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.4 does not allow it.  */
+          __asm__ __volatile__ ("fldcw %0" : : "m" (*&env.__control_word));
+          return -1;
         }
+      __asm__ __volatile__ ("fldenv %0" : : "m" (*&env));
     }
 #   endif
 #  endif
-- 
2.34.1

Reply via email to