Include SSE state in i386 fenv_t (bug 16064)

Message ID Pine.LNX.4.64.1405091644060.14073@digraph.polyomino.org.uk
State Committed
Headers

Commit Message

Joseph Myers May 9, 2014, 4:45 p.m. UTC
  On Fri, 9 May 2014, H.J. Lu wrote:

> When loading load x87 fenv_t, we should either not to
> change __eip or set it to 0 together with other fields,
> similar to other code paths in __fesetenv, to avoid any
> potential issues.

This patch sets all those fields to 0 unconditionally.

2014-05-09  Joseph Myers  <joseph@codesourcery.com>

	[BZ #16064]
	* sysdeps/i386/fpu/fegetenv.c: Include <unistd.h>, <ldsodefs.h>
	and <dl-procinfo.h>.
	(__fegetenv): Save SSE state in envp->__eip if supported.
	* sysdeps/i386/fpu/feholdexcpt.c (feholdexcept): Save SSE state in
	envp->__eip if supported.
	* sysdeps/i386/fpu/fesetenv.c: Include <unistd.h>, <ldsodefs.h>
	and <dl-procinfo.h>.
	(__fesetenv): Always set __eip, __cs_selector, __opcode,
	__data_offset and __data_selector in environment to 0.  Set SSE
	state if supported.
	* sysdeps/x86/fpu/Makefile [$(subdir) = math] (tests): Add
	test-fenv-sse.
	[$(subdir) = math] (CFLAGS-test-fenv-sse.c): Add -msse2
	-mfpmath=sse.
	* sysdeps/x86/fpu/test-fenv-sse.c: New file.
  

Comments

H.J. Lu May 9, 2014, 4:51 p.m. UTC | #1
On Fri, May 9, 2014 at 9:45 AM, Joseph S. Myers <joseph@codesourcery.com> wrote:
> On Fri, 9 May 2014, H.J. Lu wrote:
>
>> When loading load x87 fenv_t, we should either not to
>> change __eip or set it to 0 together with other fields,
>> similar to other code paths in __fesetenv, to avoid any
>> potential issues.
>
> This patch sets all those fields to 0 unconditionally.
>
> 2014-05-09  Joseph Myers  <joseph@codesourcery.com>
>
>         [BZ #16064]
>         * sysdeps/i386/fpu/fegetenv.c: Include <unistd.h>, <ldsodefs.h>
>         and <dl-procinfo.h>.
>         (__fegetenv): Save SSE state in envp->__eip if supported.
>         * sysdeps/i386/fpu/feholdexcpt.c (feholdexcept): Save SSE state in
>         envp->__eip if supported.
>         * sysdeps/i386/fpu/fesetenv.c: Include <unistd.h>, <ldsodefs.h>
>         and <dl-procinfo.h>.
>         (__fesetenv): Always set __eip, __cs_selector, __opcode,
>         __data_offset and __data_selector in environment to 0.  Set SSE
>         state if supported.
>         * sysdeps/x86/fpu/Makefile [$(subdir) = math] (tests): Add
>         test-fenv-sse.
>         [$(subdir) = math] (CFLAGS-test-fenv-sse.c): Add -msse2
>         -mfpmath=sse.
>         * sysdeps/x86/fpu/test-fenv-sse.c: New file.
>

Looks good to me.

Thanks.
  

Patch

diff --git a/sysdeps/i386/fpu/fegetenv.c b/sysdeps/i386/fpu/fegetenv.c
index 8dbdb57..8c45b6b 100644
--- a/sysdeps/i386/fpu/fegetenv.c
+++ b/sysdeps/i386/fpu/fegetenv.c
@@ -18,6 +18,9 @@ 
    <http://www.gnu.org/licenses/>.  */
 
 #include <fenv.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <dl-procinfo.h>
 
 int
 __fegetenv (fenv_t *envp)
@@ -28,6 +31,9 @@  __fegetenv (fenv_t *envp)
      would block all exceptions.  */
   __asm__ ("fldenv %0" : : "m" (*envp));
 
+  if ((GLRO(dl_hwcap) & HWCAP_I386_XMM) != 0)
+    __asm__ ("stmxcsr %0" : "=m" (envp->__eip));
+
   /* Success.  */
   return 0;
 }
diff --git a/sysdeps/i386/fpu/feholdexcpt.c b/sysdeps/i386/fpu/feholdexcpt.c
index d475ca8..dc9d703 100644
--- a/sysdeps/i386/fpu/feholdexcpt.c
+++ b/sysdeps/i386/fpu/feholdexcpt.c
@@ -35,10 +35,10 @@  feholdexcept (fenv_t *envp)
       unsigned int xwork;
 
       /* Get the current control word.  */
-      __asm__ ("stmxcsr %0" : "=m" (*&xwork));
+      __asm__ ("stmxcsr %0" : "=m" (envp->__eip));
 
       /* Set all exceptions to non-stop and clear them.  */
-      xwork = (xwork | 0x1f80) & ~0x3f;
+      xwork = (envp->__eip | 0x1f80) & ~0x3f;
 
       __asm__ ("ldmxcsr %0" : : "m" (*&xwork));
     }
diff --git a/sysdeps/i386/fpu/fesetenv.c b/sysdeps/i386/fpu/fesetenv.c
index 95b2f0a..2a84657 100644
--- a/sysdeps/i386/fpu/fesetenv.c
+++ b/sysdeps/i386/fpu/fesetenv.c
@@ -19,6 +19,9 @@ 
 
 #include <fenv.h>
 #include <assert.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <dl-procinfo.h>
 
 
 int
@@ -40,21 +43,11 @@  __fesetenv (const fenv_t *envp)
       temp.__control_word |= FE_ALL_EXCEPT;
       temp.__control_word &= ~FE_TOWARDZERO;
       temp.__status_word &= ~FE_ALL_EXCEPT;
-      temp.__eip = 0;
-      temp.__cs_selector = 0;
-      temp.__opcode = 0;
-      temp.__data_offset = 0;
-      temp.__data_selector = 0;
     }
   else if (envp == FE_NOMASK_ENV)
     {
       temp.__control_word &= ~(FE_ALL_EXCEPT | FE_TOWARDZERO);
       temp.__status_word &= ~FE_ALL_EXCEPT;
-      temp.__eip = 0;
-      temp.__cs_selector = 0;
-      temp.__opcode = 0;
-      temp.__data_offset = 0;
-      temp.__data_selector = 0;
     }
   else
     {
@@ -63,15 +56,42 @@  __fesetenv (const fenv_t *envp)
 			      & (FE_ALL_EXCEPT | FE_TOWARDZERO));
       temp.__status_word &= ~FE_ALL_EXCEPT;
       temp.__status_word |= envp->__status_word & FE_ALL_EXCEPT;
-      temp.__eip = envp->__eip;
-      temp.__cs_selector = envp->__cs_selector;
-      temp.__opcode = envp->__opcode;
-      temp.__data_offset = envp->__data_offset;
-      temp.__data_selector = envp->__data_selector;
     }
+  temp.__eip = 0;
+  temp.__cs_selector = 0;
+  temp.__opcode = 0;
+  temp.__data_offset = 0;
+  temp.__data_selector = 0;
 
   __asm__ ("fldenv %0" : : "m" (temp));
 
+  if ((GLRO(dl_hwcap) & HWCAP_I386_XMM) != 0)
+    {
+      unsigned int mxcsr;
+      __asm__ ("stmxcsr %0" : "=m" (mxcsr));
+
+      if (envp == FE_DFL_ENV)
+	{
+	  /* Set mask for SSE MXCSR.  */
+	  mxcsr |= (FE_ALL_EXCEPT << 7);
+	  /* Set rounding to FE_TONEAREST.  */
+	  mxcsr &= ~0x6000;
+	  mxcsr |= (FE_TONEAREST << 3);
+	}
+      else if (envp == FE_NOMASK_ENV)
+	{
+	  /* Do not mask exceptions.  */
+	  mxcsr &= ~(FE_ALL_EXCEPT << 7);
+	  /* Set rounding to FE_TONEAREST.  */
+	  mxcsr &= ~0x6000;
+	  mxcsr |= (FE_TONEAREST << 3);
+	}
+      else
+	mxcsr = envp->__eip;
+
+      __asm__ ("ldmxcsr %0" : : "m" (mxcsr));
+    }
+
   /* Success.  */
   return 0;
 }
diff --git a/sysdeps/x86/fpu/Makefile b/sysdeps/x86/fpu/Makefile
index 8054380..9cb7bb2 100644
--- a/sysdeps/x86/fpu/Makefile
+++ b/sysdeps/x86/fpu/Makefile
@@ -1,3 +1,5 @@ 
 ifeq ($(subdir),math)
 libm-support += powl_helper
+tests += test-fenv-sse
+CFLAGS-test-fenv-sse.c += -msse2 -mfpmath=sse
 endif
diff --git a/sysdeps/x86/fpu/test-fenv-sse.c b/sysdeps/x86/fpu/test-fenv-sse.c
new file mode 100644
index 0000000..c81eb16
--- /dev/null
+++ b/sysdeps/x86/fpu/test-fenv-sse.c
@@ -0,0 +1,138 @@ 
+/* Test floating-point environment includes SSE state (bug 16064).
+   Copyright (C) 2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <cpuid.h>
+#include <fenv.h>
+#include <float.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+static bool
+have_sse2 (void)
+{
+  unsigned int eax, ebx, ecx, edx;
+
+  if (!__get_cpuid (1, &eax, &ebx, &ecx, &edx))
+    return false;
+
+  return (edx & bit_SSE2) != 0;
+}
+
+static __attribute__ ((noinline)) int
+sse_tests (void)
+{
+  int ret = 0;
+  fenv_t base_env;
+  if (fegetenv (&base_env) != 0)
+    {
+      puts ("fegetenv (&base_env) failed");
+      return 1;
+    }
+  if (fesetround (FE_UPWARD) != 0)
+    {
+      puts ("fesetround (FE_UPWARD) failed");
+      return 1;
+    }
+  if (fesetenv (&base_env) != 0)
+    {
+      puts ("fesetenv (&base_env) failed");
+      return 1;
+    }
+  volatile float a = 1.0f, b = FLT_MIN, c;
+  c = a + b;
+  if (c != 1.0f)
+    {
+      puts ("fesetenv did not restore rounding mode");
+      ret = 1;
+    }
+  if (fesetround (FE_DOWNWARD) != 0)
+    {
+      puts ("fesetround (FE_DOWNWARD) failed");
+      return 1;
+    }
+  if (feupdateenv (&base_env) != 0)
+    {
+      puts ("feupdateenv (&base_env) failed");
+      return 1;
+    }
+  volatile float d = -FLT_MIN, e;
+  e = a + d;
+  if (e != 1.0f)
+    {
+      puts ("feupdateenv did not restore rounding mode");
+      ret = 1;
+    }
+  if (fesetround (FE_UPWARD) != 0)
+    {
+      puts ("fesetround (FE_UPWARD) failed");
+      return 1;
+    }
+  fenv_t upward_env;
+  if (feholdexcept (&upward_env) != 0)
+    {
+      puts ("feholdexcept (&upward_env) failed");
+      return 1;
+    }
+  if (fesetround (FE_DOWNWARD) != 0)
+    {
+      puts ("fesetround (FE_DOWNWARD) failed");
+      return 1;
+    }
+  if (fesetenv (&upward_env) != 0)
+    {
+      puts ("fesetenv (&upward_env) failed");
+      return 1;
+    }
+  e = a + d;
+  if (e != 1.0f)
+    {
+      puts ("fesetenv did not restore rounding mode from feholdexcept");
+      ret = 1;
+    }
+  if (fesetround (FE_UPWARD) != 0)
+    {
+      puts ("fesetround (FE_UPWARD) failed");
+      return 1;
+    }
+  if (fesetenv (FE_DFL_ENV) != 0)
+    {
+      puts ("fesetenv (FE_DFL_ENV) failed");
+      return 1;
+    }
+  c = a + b;
+  if (c != 1.0f)
+    {
+      puts ("fesetenv (FE_DFL_ENV) did not restore rounding mode");
+      ret = 1;
+    }
+  return ret;
+}
+
+static int
+do_test (void)
+{
+  if (!have_sse2 ())
+    {
+      puts ("CPU does not support SSE2, cannot test");
+      return 0;
+    }
+  return sse_tests ();
+}
+
+#define TEST_FUNCTION do_test ()
+#include <test-skeleton.c>