aboutsummaryrefslogtreecommitdiff
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>