about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog19
-rw-r--r--NEWS4
-rw-r--r--sysdeps/i386/fpu/fegetenv.c6
-rw-r--r--sysdeps/i386/fpu/feholdexcpt.c4
-rw-r--r--sysdeps/i386/fpu/fesetenv.c50
-rw-r--r--sysdeps/x86/fpu/Makefile2
-rw-r--r--sysdeps/x86/fpu/test-fenv-sse.c138
7 files changed, 204 insertions, 19 deletions
diff --git a/ChangeLog b/ChangeLog
index 307aabb69e..4510093bfa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+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.
+
 2014-05-09  Will Newton  <will.newton@linaro.org>
 
 	* sysdeps/arm/preconfigure.ac: Set libc_commonpagesize
diff --git a/NEWS b/NEWS
index fae2b82607..0a2b04f963 100644
--- a/NEWS
+++ b/NEWS
@@ -9,8 +9,8 @@ Version 2.20
 
 * The following bugs are resolved with this release:
 
-  6804, 9894, 12994, 13347, 13651, 14308, 14770, 15119, 15347, 15514,
-  15804, 15894, 16002, 16198, 16284, 16348, 16349, 16357, 16362, 16447,
+  6804, 9894, 12994, 13347, 13651, 14308, 14770, 15119, 15347, 15514, 15804,
+  15894, 16002, 16064, 16198, 16284, 16348, 16349, 16357, 16362, 16447,
   16532, 16545, 16574, 16599, 16600, 16609, 16610, 16611, 16613, 16619,
   16623, 16629, 16632, 16634, 16639, 16642, 16648, 16649, 16670, 16674,
   16677, 16680, 16683, 16689, 16695, 16701, 16706, 16707, 16712, 16713,
diff --git a/sysdeps/i386/fpu/fegetenv.c b/sysdeps/i386/fpu/fegetenv.c
index 8dbdb5787a..8c45b6b8cb 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 d475ca850c..dc9d7031ca 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 95b2f0a1ab..2a84657030 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 8054380477..9cb7bb2d85 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 0000000000..c81eb16f79
--- /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>