diff options
-rw-r--r-- | ChangeLog | 19 | ||||
-rw-r--r-- | NEWS | 4 | ||||
-rw-r--r-- | sysdeps/i386/fpu/fegetenv.c | 6 | ||||
-rw-r--r-- | sysdeps/i386/fpu/feholdexcpt.c | 4 | ||||
-rw-r--r-- | sysdeps/i386/fpu/fesetenv.c | 50 | ||||
-rw-r--r-- | sysdeps/x86/fpu/Makefile | 2 | ||||
-rw-r--r-- | sysdeps/x86/fpu/test-fenv-sse.c | 138 |
7 files changed, 204 insertions, 19 deletions
@@ -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 @@ -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> |