about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog19
-rw-r--r--NEWS24
-rw-r--r--sysdeps/i386/fpu/fesetenv.c40
-rw-r--r--sysdeps/x86/fpu/Makefile3
-rw-r--r--sysdeps/x86/fpu/test-fenv-sse-2.c176
-rw-r--r--sysdeps/x86/fpu/test-fenv-x87.c169
-rw-r--r--sysdeps/x86_64/fpu/fesetenv.c40
7 files changed, 438 insertions, 33 deletions
diff --git a/ChangeLog b/ChangeLog
index 52cd9f3977..9ef00a7a32 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,24 @@
 2015-10-28  Joseph Myers  <joseph@codesourcery.com>
 
+	[BZ #16068]
+	* sysdeps/i386/fpu/fesetenv.c: Include <fpu_control.h>.
+	(FE_ALL_EXCEPT_X86): New macro.
+	(__fesetenv): Use FE_ALL_EXCEPT_X86 in most places instead of
+	FE_ALL_EXCEPT.  Ensure precision control is included in
+	floating-point state.  Ensure that FE_DFL_ENV and FE_NOMASK_ENV
+	handle "denormal operand exception" and clear FZ and DAZ bits.
+	* sysdeps/x86_64/fpu/fesetenv.c: Include <fpu_control.h>.
+	(FE_ALL_EXCEPT_X86): New macro.
+	(__fesetenv): Use FE_ALL_EXCEPT_X86 in most places instead of
+	FE_ALL_EXCEPT.  Ensure precision control is included in
+	floating-point state.  Ensure that FE_DFL_ENV and FE_NOMASK_ENV
+	handle "denormal operand exception" and clear FZ and DAZ bits.
+	* sysdeps/x86/fpu/test-fenv-sse-2.c: New file.
+	* sysdeps/x86/fpu/test-fenv-x87.c: Likewise.
+	* sysdeps/x86/fpu/Makefile [$(subdir) = math] (tests): Add
+	test-fenv-x87 and test-fenv-sse-2.
+	[$(subdir) = math] (CFLAGS-test-fenv-sse-2.c): New variable.
+
 	* math/libm-test.inc (BUILD_COMPLEX): Remove macro.
 	* math/test-double.h (BUILD_COMPLEX): New macro.
 	* math/test-float.h (BUILD_COMPLEX): Likewise.
diff --git a/NEWS b/NEWS
index 9b343917f5..0831d35881 100644
--- a/NEWS
+++ b/NEWS
@@ -10,18 +10,18 @@ Version 2.23
 * The following bugs are resolved with this release:
 
   887, 2542, 2543, 2558, 2898, 4404, 6803, 10432, 14341, 14912, 15367,
-  15384, 15470, 15491, 15786, 15918, 16141, 16296, 16347, 16399, 16415,
-  16422, 16517, 16519, 16520, 16521, 16620, 16734, 16973, 16985, 17118,
-  17243, 17244, 17250, 17404, 17441, 17787, 17886, 17887, 17905, 18084,
-  18086, 18240, 18265, 18370, 18421, 18480, 18525, 18595, 18589, 18610,
-  18611, 18618, 18647, 18661, 18674, 18675, 18681, 18699, 18724, 18743,
-  18757, 18778, 18781, 18787, 18789, 18790, 18795, 18796, 18803, 18820,
-  18823, 18824, 18825, 18857, 18863, 18870, 18872, 18873, 18875, 18887,
-  18918, 18921, 18928, 18951, 18952, 18953, 18956, 18961, 18966, 18967,
-  18969, 18970, 18977, 18980, 18981, 18982, 18985, 19003, 19007, 19012,
-  19016, 19018, 19032, 19046, 19048, 19049, 19050, 19059, 19071, 19074,
-  19076, 19077, 19078, 19079, 19085, 19086, 19088, 19094, 19095, 19124,
-  19125, 19129, 19134, 19137, 19156, 19174, 19181.
+  15384, 15470, 15491, 15786, 15918, 16068, 16141, 16296, 16347, 16399,
+  16415, 16422, 16517, 16519, 16520, 16521, 16620, 16734, 16973, 16985,
+  17118, 17243, 17244, 17250, 17404, 17441, 17787, 17886, 17887, 17905,
+  18084, 18086, 18240, 18265, 18370, 18421, 18480, 18525, 18595, 18589,
+  18610, 18611, 18618, 18647, 18661, 18674, 18675, 18681, 18699, 18724,
+  18743, 18757, 18778, 18781, 18787, 18789, 18790, 18795, 18796, 18803,
+  18820, 18823, 18824, 18825, 18857, 18863, 18870, 18872, 18873, 18875,
+  18887, 18918, 18921, 18928, 18951, 18952, 18953, 18956, 18961, 18966,
+  18967, 18969, 18970, 18977, 18980, 18981, 18982, 18985, 19003, 19007,
+  19012, 19016, 19018, 19032, 19046, 19048, 19049, 19050, 19059, 19071,
+  19074, 19076, 19077, 19078, 19079, 19085, 19086, 19088, 19094, 19095,
+  19124, 19125, 19129, 19134, 19137, 19156, 19174, 19181.
 
 * A defect in the malloc implementation, present since glibc 2.15 (2012) or
   glibc 2.10 via --enable-experimental-malloc (2009), could result in the
diff --git a/sysdeps/i386/fpu/fesetenv.c b/sysdeps/i386/fpu/fesetenv.c
index 910aa0991c..f3b9cefee5 100644
--- a/sysdeps/i386/fpu/fesetenv.c
+++ b/sysdeps/i386/fpu/fesetenv.c
@@ -18,12 +18,18 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <fenv.h>
+#include <fpu_control.h>
 #include <assert.h>
 #include <unistd.h>
 #include <ldsodefs.h>
 #include <dl-procinfo.h>
 
 
+/* All exceptions, including the x86-specific "denormal operand"
+   exception.  */
+#define FE_ALL_EXCEPT_X86 (FE_ALL_EXCEPT | __FE_DENORM)
+
+
 int
 __fesetenv (const fenv_t *envp)
 {
@@ -40,22 +46,30 @@ __fesetenv (const fenv_t *envp)
 
   if (envp == FE_DFL_ENV)
     {
-      temp.__control_word |= FE_ALL_EXCEPT;
+      temp.__control_word |= FE_ALL_EXCEPT_X86;
       temp.__control_word &= ~FE_TOWARDZERO;
-      temp.__status_word &= ~FE_ALL_EXCEPT;
+      temp.__control_word |= _FPU_EXTENDED;
+      temp.__status_word &= ~FE_ALL_EXCEPT_X86;
     }
   else if (envp == FE_NOMASK_ENV)
     {
       temp.__control_word &= ~(FE_ALL_EXCEPT | FE_TOWARDZERO);
-      temp.__status_word &= ~FE_ALL_EXCEPT;
+      /* Keep the "denormal operand" exception masked.  */
+      temp.__control_word |= __FE_DENORM;
+      temp.__control_word |= _FPU_EXTENDED;
+      temp.__status_word &= ~FE_ALL_EXCEPT_X86;
     }
   else
     {
-      temp.__control_word &= ~(FE_ALL_EXCEPT | FE_TOWARDZERO);
+      temp.__control_word &= ~(FE_ALL_EXCEPT_X86
+			       | FE_TOWARDZERO
+			       | _FPU_EXTENDED);
       temp.__control_word |= (envp->__control_word
-			      & (FE_ALL_EXCEPT | FE_TOWARDZERO));
-      temp.__status_word &= ~FE_ALL_EXCEPT;
-      temp.__status_word |= envp->__status_word & FE_ALL_EXCEPT;
+			      & (FE_ALL_EXCEPT_X86
+				 | FE_TOWARDZERO
+				 | _FPU_EXTENDED));
+      temp.__status_word &= ~FE_ALL_EXCEPT_X86;
+      temp.__status_word |= envp->__status_word & FE_ALL_EXCEPT_X86;
     }
   temp.__eip = 0;
   temp.__cs_selector = 0;
@@ -73,22 +87,28 @@ __fesetenv (const fenv_t *envp)
       if (envp == FE_DFL_ENV)
 	{
 	  /* Clear SSE exceptions.  */
-	  mxcsr &= ~FE_ALL_EXCEPT;
+	  mxcsr &= ~FE_ALL_EXCEPT_X86;
 	  /* Set mask for SSE MXCSR.  */
-	  mxcsr |= (FE_ALL_EXCEPT << 7);
+	  mxcsr |= (FE_ALL_EXCEPT_X86 << 7);
 	  /* Set rounding to FE_TONEAREST.  */
 	  mxcsr &= ~0x6000;
 	  mxcsr |= (FE_TONEAREST << 3);
+	  /* Clear the FZ and DAZ bits.  */
+	  mxcsr &= ~0x8040;
 	}
       else if (envp == FE_NOMASK_ENV)
 	{
 	  /* Clear SSE exceptions.  */
-	  mxcsr &= ~FE_ALL_EXCEPT;
+	  mxcsr &= ~FE_ALL_EXCEPT_X86;
 	  /* Do not mask exceptions.  */
 	  mxcsr &= ~(FE_ALL_EXCEPT << 7);
+	  /* Keep the "denormal operand" exception masked.  */
+	  mxcsr |= (__FE_DENORM << 7);
 	  /* Set rounding to FE_TONEAREST.  */
 	  mxcsr &= ~0x6000;
 	  mxcsr |= (FE_TONEAREST << 3);
+	  /* Clear the FZ and DAZ bits.  */
+	  mxcsr &= ~0x8040;
 	}
       else
 	mxcsr = envp->__eip;
diff --git a/sysdeps/x86/fpu/Makefile b/sysdeps/x86/fpu/Makefile
index d38497cff3..b561995658 100644
--- a/sysdeps/x86/fpu/Makefile
+++ b/sysdeps/x86/fpu/Makefile
@@ -1,6 +1,7 @@
 ifeq ($(subdir),math)
 libm-support += powl_helper
-tests += test-fenv-sse test-fenv-clear-sse
+tests += test-fenv-sse test-fenv-clear-sse test-fenv-x87 test-fenv-sse-2
 CFLAGS-test-fenv-sse.c += -msse2 -mfpmath=sse
 CFLAGS-test-fenv-clear-sse.c += -msse2 -mfpmath=sse
+CFLAGS-test-fenv-sse-2.c += -msse2 -mfpmath=sse
 endif
diff --git a/sysdeps/x86/fpu/test-fenv-sse-2.c b/sysdeps/x86/fpu/test-fenv-sse-2.c
new file mode 100644
index 0000000000..12946e63ce
--- /dev/null
+++ b/sysdeps/x86/fpu/test-fenv-sse-2.c
@@ -0,0 +1,176 @@
+/* Test x86-specific floating-point environment (bug 16068): SSE part.
+   Copyright (C) 2015 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 <stdint.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 uint32_t
+get_sse_mxcsr (void)
+{
+  uint32_t temp;
+  __asm__ __volatile__ ("stmxcsr %0" : "=m" (temp));
+  return temp;
+}
+
+static void
+set_sse_mxcsr (uint32_t val)
+{
+  __asm__ __volatile__ ("ldmxcsr %0" : : "m" (val));
+}
+
+static void
+set_sse_mxcsr_bits (uint32_t mask, uint32_t bits)
+{
+  uint32_t mxcsr = get_sse_mxcsr ();
+  mxcsr = (mxcsr & ~mask) | bits;
+  set_sse_mxcsr (mxcsr);
+}
+
+static int
+test_sse_mxcsr_bits (const char *test, uint32_t mask, uint32_t bits)
+{
+  uint32_t mxcsr = get_sse_mxcsr ();
+  printf ("Testing %s: mxcsr = %x\n", test, mxcsr);
+  if ((mxcsr & mask) == bits)
+    {
+      printf ("PASS: %s\n", test);
+      return 0;
+    }
+  else
+    {
+      printf ("FAIL: %s\n", test);
+      return 1;
+    }
+}
+
+#define MXCSR_FZ 0x8000
+#define MXCSR_DAZ 0x40
+#define MXCSR_DE 0x2
+#define MXCSR_DM 0x100
+
+static __attribute__ ((noinline)) int
+sse_tests (void)
+{
+  int result = 0;
+  fenv_t env1, env2;
+  /* Test FZ bit.  */
+  fegetenv (&env1);
+  set_sse_mxcsr_bits (MXCSR_FZ, MXCSR_FZ);
+  fegetenv (&env2);
+  fesetenv (&env1);
+  result |= test_sse_mxcsr_bits ("fesetenv FZ restoration",
+				 MXCSR_FZ, 0);
+  set_sse_mxcsr_bits (MXCSR_FZ, 0);
+  fesetenv (&env2);
+  result |= test_sse_mxcsr_bits ("fesetenv FZ restoration 2",
+				 MXCSR_FZ, MXCSR_FZ);
+  set_sse_mxcsr_bits (MXCSR_FZ, MXCSR_FZ);
+  fesetenv (FE_NOMASK_ENV);
+  result |= test_sse_mxcsr_bits ("fesetenv (FE_NOMASK_ENV) FZ restoration",
+				 MXCSR_FZ, 0);
+  set_sse_mxcsr_bits (MXCSR_FZ, MXCSR_FZ);
+  fesetenv (FE_DFL_ENV);
+  result |= test_sse_mxcsr_bits ("fesetenv (FE_DFL_ENV) FZ restoration",
+				 MXCSR_FZ, 0);
+  /* Test DAZ bit.  */
+  set_sse_mxcsr_bits (MXCSR_DAZ, MXCSR_DAZ);
+  fegetenv (&env2);
+  fesetenv (&env1);
+  result |= test_sse_mxcsr_bits ("fesetenv DAZ restoration",
+				 MXCSR_DAZ, 0);
+  set_sse_mxcsr_bits (MXCSR_DAZ, 0);
+  fesetenv (&env2);
+  result |= test_sse_mxcsr_bits ("fesetenv DAZ restoration 2",
+				 MXCSR_DAZ, MXCSR_DAZ);
+  set_sse_mxcsr_bits (MXCSR_DAZ, MXCSR_DAZ);
+  fesetenv (FE_NOMASK_ENV);
+  result |= test_sse_mxcsr_bits ("fesetenv (FE_NOMASK_ENV) DAZ restoration",
+				 MXCSR_DAZ, 0);
+  set_sse_mxcsr_bits (MXCSR_DAZ, MXCSR_DAZ);
+  fesetenv (FE_DFL_ENV);
+  result |= test_sse_mxcsr_bits ("fesetenv (FE_DFL_ENV) DAZ restoration",
+				 MXCSR_DAZ, 0);
+  /* Test DM bit.  */
+  set_sse_mxcsr_bits (MXCSR_DM, 0);
+  fegetenv (&env2);
+  fesetenv (&env1);
+  result |= test_sse_mxcsr_bits ("fesetenv DM restoration",
+				 MXCSR_DM, MXCSR_DM);
+  set_sse_mxcsr_bits (MXCSR_DM, MXCSR_DM);
+  fesetenv (&env2);
+  result |= test_sse_mxcsr_bits ("fesetenv DM restoration 2",
+				 MXCSR_DM, 0);
+  set_sse_mxcsr_bits (MXCSR_DM, 0);
+  /* Presume FE_NOMASK_ENV should leave the "denormal operand"
+     exception masked, as not a standard exception.  */
+  fesetenv (FE_NOMASK_ENV);
+  result |= test_sse_mxcsr_bits ("fesetenv (FE_NOMASK_ENV) DM restoration",
+				 MXCSR_DM, MXCSR_DM);
+  set_sse_mxcsr_bits (MXCSR_DM, 0);
+  fesetenv (FE_DFL_ENV);
+  result |= test_sse_mxcsr_bits ("fesetenv (FE_DFL_ENV) DM restoration",
+				 MXCSR_DM, MXCSR_DM);
+  /* Test DE bit.  */
+  set_sse_mxcsr_bits (MXCSR_DE, MXCSR_DE);
+  fegetenv (&env2);
+  fesetenv (&env1);
+  result |= test_sse_mxcsr_bits ("fesetenv DE restoration",
+				 MXCSR_DE, 0);
+  set_sse_mxcsr_bits (MXCSR_DE, 0);
+  fesetenv (&env2);
+  result |= test_sse_mxcsr_bits ("fesetenv DE restoration 2",
+				 MXCSR_DE, MXCSR_DE);
+  set_sse_mxcsr_bits (MXCSR_DE, MXCSR_DE);
+  fesetenv (FE_NOMASK_ENV);
+  result |= test_sse_mxcsr_bits ("fesetenv (FE_NOMASK_ENV) DE restoration",
+				 MXCSR_DE, 0);
+  set_sse_mxcsr_bits (MXCSR_DE, MXCSR_DE);
+  fesetenv (FE_DFL_ENV);
+  result |= test_sse_mxcsr_bits ("fesetenv (FE_DFL_ENV) DE restoration",
+				 MXCSR_DE, 0);
+  return result;
+}
+
+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>
diff --git a/sysdeps/x86/fpu/test-fenv-x87.c b/sysdeps/x86/fpu/test-fenv-x87.c
new file mode 100644
index 0000000000..2f74471eac
--- /dev/null
+++ b/sysdeps/x86/fpu/test-fenv-x87.c
@@ -0,0 +1,169 @@
+/* Test x86-specific floating-point environment (bug 16068): x87 part.
+   Copyright (C) 2015 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 <fenv.h>
+#include <float.h>
+#include <fpu_control.h>
+#include <stdint.h>
+#include <stdio.h>
+
+static uint16_t
+get_x87_cw (void)
+{
+  fpu_control_t cw;
+  _FPU_GETCW (cw);
+  return cw;
+}
+
+static void
+set_x87_cw (uint16_t val)
+{
+  fpu_control_t cw = val;
+  _FPU_SETCW (cw);
+}
+
+static void
+set_x87_cw_bits (uint16_t mask, uint16_t bits)
+{
+  uint16_t cw = get_x87_cw ();
+  cw = (cw & ~mask) | bits;
+  set_x87_cw (cw);
+}
+
+static int
+test_x87_cw_bits (const char *test, uint16_t mask, uint16_t bits)
+{
+  uint16_t cw = get_x87_cw ();
+  printf ("Testing %s: cw = %x\n", test, cw);
+  if ((cw & mask) == bits)
+    {
+      printf ("PASS: %s\n", test);
+      return 0;
+    }
+  else
+    {
+      printf ("FAIL: %s\n", test);
+      return 1;
+    }
+}
+
+static uint16_t
+get_x87_sw (void)
+{
+  uint16_t temp;
+  __asm__ __volatile__ ("fnstsw %0" : "=a" (temp));
+  return temp;
+}
+
+static void
+set_x87_sw_bits (uint16_t mask, uint16_t bits)
+{
+  fenv_t temp;
+  __asm__ __volatile__ ("fnstenv %0" : "=m" (temp));
+  temp.__status_word = (temp.__status_word & ~mask) | bits;
+  __asm__ __volatile__ ("fldenv %0" : : "m" (temp));
+}
+
+static int
+test_x87_sw_bits (const char *test, uint16_t mask, uint16_t bits)
+{
+  uint16_t sw = get_x87_sw ();
+  printf ("Testing %s: sw = %x\n", test, sw);
+  if ((sw & mask) == bits)
+    {
+      printf ("PASS: %s\n", test);
+      return 0;
+    }
+  else
+    {
+      printf ("FAIL: %s\n", test);
+      return 1;
+    }
+}
+
+#define X87_CW_PREC_MASK _FPU_EXTENDED
+
+static int
+do_test (void)
+{
+  int result = 0;
+  fenv_t env1, env2;
+  /* Test precision mask.  */
+  fegetenv (&env1);
+  set_x87_cw_bits (X87_CW_PREC_MASK, _FPU_SINGLE);
+  fegetenv (&env2);
+  fesetenv (&env1);
+  result |= test_x87_cw_bits ("fesetenv precision restoration",
+			      X87_CW_PREC_MASK, _FPU_EXTENDED);
+  set_x87_cw_bits (X87_CW_PREC_MASK, _FPU_EXTENDED);
+  fesetenv (&env2);
+  result |= test_x87_cw_bits ("fesetenv precision restoration 2",
+			      X87_CW_PREC_MASK, _FPU_SINGLE);
+  set_x87_cw_bits (X87_CW_PREC_MASK, _FPU_DOUBLE);
+  fesetenv (FE_NOMASK_ENV);
+  result |= test_x87_cw_bits ("fesetenv (FE_NOMASK_ENV) precision restoration",
+			      X87_CW_PREC_MASK, _FPU_EXTENDED);
+  set_x87_cw_bits (X87_CW_PREC_MASK, _FPU_SINGLE);
+  fesetenv (FE_DFL_ENV);
+  result |= test_x87_cw_bits ("fesetenv (FE_DFL_ENV) precision restoration",
+			      X87_CW_PREC_MASK, _FPU_EXTENDED);
+  /* Test x87 denormal operand masking.  */
+  set_x87_cw_bits (_FPU_MASK_DM, 0);
+  fegetenv (&env2);
+  fesetenv (&env1);
+  result |= test_x87_cw_bits ("fesetenv denormal mask restoration",
+			      _FPU_MASK_DM, _FPU_MASK_DM);
+  set_x87_cw_bits (_FPU_MASK_DM, _FPU_MASK_DM);
+  fesetenv (&env2);
+  result |= test_x87_cw_bits ("fesetenv denormal mask restoration 2",
+			      _FPU_MASK_DM, 0);
+  set_x87_cw_bits (_FPU_MASK_DM, 0);
+  /* Presume FE_NOMASK_ENV should leave the "denormal operand"
+     exception masked, as not a standard exception.  */
+  fesetenv (FE_NOMASK_ENV);
+  result |= test_x87_cw_bits ("fesetenv (FE_NOMASK_ENV) denormal mask "
+			      "restoration",
+			      _FPU_MASK_DM, _FPU_MASK_DM);
+  set_x87_cw_bits (_FPU_MASK_DM, 0);
+  fesetenv (FE_DFL_ENV);
+  result |= test_x87_cw_bits ("fesetenv (FE_DFL_ENV) denormal mask "
+			      "restoration",
+			      _FPU_MASK_DM, _FPU_MASK_DM);
+  /* Test x87 denormal operand exception.  */
+  set_x87_sw_bits (__FE_DENORM, __FE_DENORM);
+  fegetenv (&env2);
+  fesetenv (&env1);
+  result |= test_x87_sw_bits ("fesetenv denormal exception restoration",
+			      __FE_DENORM, 0);
+  set_x87_sw_bits (__FE_DENORM, 0);
+  fesetenv (&env2);
+  result |= test_x87_sw_bits ("fesetenv denormal exception restoration 2",
+			      __FE_DENORM, __FE_DENORM);
+  set_x87_sw_bits (__FE_DENORM, __FE_DENORM);
+  fesetenv (FE_NOMASK_ENV);
+  result |= test_x87_sw_bits ("fesetenv (FE_NOMASK_ENV) exception restoration",
+			      __FE_DENORM, 0);
+  set_x87_sw_bits (__FE_DENORM, __FE_DENORM);
+  fesetenv (FE_DFL_ENV);
+  result |= test_x87_sw_bits ("fesetenv (FE_DFL_ENV) exception restoration",
+			      __FE_DENORM, 0);
+  return result;
+}
+
+#define TEST_FUNCTION do_test ()
+#include <test-skeleton.c>
diff --git a/sysdeps/x86_64/fpu/fesetenv.c b/sysdeps/x86_64/fpu/fesetenv.c
index 9950aa734a..381540f687 100644
--- a/sysdeps/x86_64/fpu/fesetenv.c
+++ b/sysdeps/x86_64/fpu/fesetenv.c
@@ -17,9 +17,15 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <fenv.h>
+#include <fpu_control.h>
 #include <assert.h>
 
 
+/* All exceptions, including the x86-specific "denormal operand"
+   exception.  */
+#define FE_ALL_EXCEPT_X86 (FE_ALL_EXCEPT | __FE_DENORM)
+
+
 int
 __fesetenv (const fenv_t *envp)
 {
@@ -34,47 +40,61 @@ __fesetenv (const fenv_t *envp)
 
   if (envp == FE_DFL_ENV)
     {
-      temp.__control_word |= FE_ALL_EXCEPT;
+      temp.__control_word |= FE_ALL_EXCEPT_X86;
       temp.__control_word &= ~FE_TOWARDZERO;
-      temp.__status_word &= ~FE_ALL_EXCEPT;
+      temp.__control_word |= _FPU_EXTENDED;
+      temp.__status_word &= ~FE_ALL_EXCEPT_X86;
       temp.__eip = 0;
       temp.__cs_selector = 0;
       temp.__opcode = 0;
       temp.__data_offset = 0;
       temp.__data_selector = 0;
       /* Clear SSE exceptions.  */
-      temp.__mxcsr &= ~FE_ALL_EXCEPT;
+      temp.__mxcsr &= ~FE_ALL_EXCEPT_X86;
       /* Set mask for SSE MXCSR.  */
-      temp.__mxcsr |= (FE_ALL_EXCEPT << 7);
+      temp.__mxcsr |= (FE_ALL_EXCEPT_X86 << 7);
       /* Set rounding to FE_TONEAREST.  */
       temp.__mxcsr &= ~ 0x6000;
       temp.__mxcsr |= (FE_TONEAREST << 3);
+      /* Clear the FZ and DAZ bits.  */
+      temp.__mxcsr &= ~0x8040;
     }
   else if (envp == FE_NOMASK_ENV)
     {
       temp.__control_word &= ~(FE_ALL_EXCEPT | FE_TOWARDZERO);
-      temp.__status_word &= ~FE_ALL_EXCEPT;
+      /* Keep the "denormal operand" exception masked.  */
+      temp.__control_word |= __FE_DENORM;
+      temp.__control_word |= _FPU_EXTENDED;
+      temp.__status_word &= ~FE_ALL_EXCEPT_X86;
       temp.__eip = 0;
       temp.__cs_selector = 0;
       temp.__opcode = 0;
       temp.__data_offset = 0;
       temp.__data_selector = 0;
       /* Clear SSE exceptions.  */
-      temp.__mxcsr &= ~FE_ALL_EXCEPT;
+      temp.__mxcsr &= ~FE_ALL_EXCEPT_X86;
       /* Set mask for SSE MXCSR.  */
       /* Set rounding to FE_TONEAREST.  */
       temp.__mxcsr &= ~ 0x6000;
       temp.__mxcsr |= (FE_TONEAREST << 3);
       /* Do not mask exceptions.  */
       temp.__mxcsr &= ~(FE_ALL_EXCEPT << 7);
+      /* Keep the "denormal operand" exception masked.  */
+      temp.__mxcsr |= (__FE_DENORM << 7);
+      /* Clear the FZ and DAZ bits.  */
+      temp.__mxcsr &= ~0x8040;
     }
   else
     {
-      temp.__control_word &= ~(FE_ALL_EXCEPT | FE_TOWARDZERO);
+      temp.__control_word &= ~(FE_ALL_EXCEPT_X86
+			       | FE_TOWARDZERO
+			       | _FPU_EXTENDED);
       temp.__control_word |= (envp->__control_word
-			      & (FE_ALL_EXCEPT | FE_TOWARDZERO));
-      temp.__status_word &= ~FE_ALL_EXCEPT;
-      temp.__status_word |= envp->__status_word & FE_ALL_EXCEPT;
+			      & (FE_ALL_EXCEPT_X86
+				 | FE_TOWARDZERO
+				 | _FPU_EXTENDED));
+      temp.__status_word &= ~FE_ALL_EXCEPT_X86;
+      temp.__status_word |= envp->__status_word & FE_ALL_EXCEPT_X86;
       temp.__eip = envp->__eip;
       temp.__cs_selector = envp->__cs_selector;
       temp.__opcode = envp->__opcode;