@@ -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;
}
@@ -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));
}
@@ -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;
}
@@ -1,3 +1,5 @@
ifeq ($(subdir),math)
libm-support += powl_helper
+tests += test-fenv-sse
+CFLAGS-test-fenv-sse.c += -msse2 -mfpmath=sse
endif
new file mode 100644
@@ -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>