mirror of git://sourceware.org/git/glibc.git
i686: Do not raise exception traps on fesetexcept (BZ 30989)
According to ISO C23 (7.6.4.4), fesetexcept is supposed to set floating-point exception flags without raising a trap (unlike feraiseexcept, which is supposed to raise a trap if feenableexcept was called with the appropriate argument). The flags can be set in the 387 unit or in the SSE unit. To set a flag, it is sufficient to do it in the SSE unit, because that is guaranteed to not trap. However, on i386 CPUs that have only a 387 unit, set the flags in the 387, as long as this cannot trap. Checked on i686-linux-gnu. Reviewed-by: Carlos O'Donell <carlos@redhat.com>
This commit is contained in:
parent
ecb1e7220d
commit
47a9eeb9ba
|
@ -19,6 +19,7 @@
|
||||||
#include <fenv.h>
|
#include <fenv.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <math-tests.h>
|
#include <math-tests.h>
|
||||||
|
#include <math-barriers.h>
|
||||||
|
|
||||||
static int
|
static int
|
||||||
do_test (void)
|
do_test (void)
|
||||||
|
@ -41,12 +42,32 @@ do_test (void)
|
||||||
|
|
||||||
/* Verify fesetexcept does not cause exception traps. For architectures
|
/* Verify fesetexcept does not cause exception traps. For architectures
|
||||||
where setting the exception might result in traps the function should
|
where setting the exception might result in traps the function should
|
||||||
return a nonzero value. */
|
return a nonzero value.
|
||||||
|
Also check if the function does not alter the exception mask. */
|
||||||
ret = fesetexcept (FE_ALL_EXCEPT);
|
ret = fesetexcept (FE_ALL_EXCEPT);
|
||||||
|
|
||||||
_Static_assert (!(EXCEPTION_SET_FORCES_TRAP && !EXCEPTION_TESTS(float)),
|
_Static_assert (!(EXCEPTION_SET_FORCES_TRAP && !EXCEPTION_TESTS(float)),
|
||||||
"EXCEPTION_SET_FORCES_TRAP only makes sense if the "
|
"EXCEPTION_SET_FORCES_TRAP only makes sense if the "
|
||||||
"architecture suports exceptions");
|
"architecture suports exceptions");
|
||||||
|
{
|
||||||
|
int exc_before = fegetexcept ();
|
||||||
|
ret = fesetexcept (FE_ALL_EXCEPT);
|
||||||
|
int exc_after = fegetexcept ();
|
||||||
|
if (exc_before != exc_after)
|
||||||
|
{
|
||||||
|
puts ("fesetexcept (FE_ALL_EXCEPT) changed the exceptions mask");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Execute some floating-point operations, since on some CPUs exceptions
|
||||||
|
triggers a trap only at the next floating-point instruction. */
|
||||||
|
volatile double a = 1.0;
|
||||||
|
volatile double b = a + a;
|
||||||
|
math_force_eval (b);
|
||||||
|
volatile long double al = 1.0L;
|
||||||
|
volatile long double bl = al + al;
|
||||||
|
math_force_eval (bl);
|
||||||
|
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
|
@ -72,5 +93,4 @@ do_test (void)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TEST_FUNCTION do_test ()
|
#include <support/test-driver.c>
|
||||||
#include "../test-skeleton.c"
|
|
||||||
|
|
|
@ -17,15 +17,53 @@
|
||||||
<https://www.gnu.org/licenses/>. */
|
<https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
#include <fenv.h>
|
#include <fenv.h>
|
||||||
|
#include <ldsodefs.h>
|
||||||
|
|
||||||
int
|
int
|
||||||
fesetexcept (int excepts)
|
fesetexcept (int excepts)
|
||||||
{
|
{
|
||||||
fenv_t temp;
|
/* The flags can be set in the 387 unit or in the SSE unit. To set a flag,
|
||||||
|
it is sufficient to do it in the SSE unit, because that is guaranteed to
|
||||||
|
not trap. However, on i386 CPUs that have only a 387 unit, set the flags
|
||||||
|
in the 387, as long as this cannot trap. */
|
||||||
|
|
||||||
__asm__ ("fnstenv %0" : "=m" (*&temp));
|
excepts &= FE_ALL_EXCEPT;
|
||||||
temp.__status_word |= excepts & FE_ALL_EXCEPT;
|
|
||||||
__asm__ ("fldenv %0" : : "m" (*&temp));
|
if (CPU_FEATURE_USABLE (SSE))
|
||||||
|
{
|
||||||
|
/* Get the control word of the SSE unit. */
|
||||||
|
unsigned int mxcsr;
|
||||||
|
__asm__ ("stmxcsr %0" : "=m" (*&mxcsr));
|
||||||
|
|
||||||
|
/* Set relevant flags. */
|
||||||
|
mxcsr |= excepts;
|
||||||
|
|
||||||
|
/* Put the new data in effect. */
|
||||||
|
__asm__ ("ldmxcsr %0" : : "m" (*&mxcsr));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fenv_t temp;
|
||||||
|
|
||||||
|
/* Note: fnstenv masks all floating-point exceptions until the fldenv
|
||||||
|
or fldcw below. */
|
||||||
|
__asm__ ("fnstenv %0" : "=m" (*&temp));
|
||||||
|
|
||||||
|
/* Set relevant flags. */
|
||||||
|
temp.__status_word |= excepts;
|
||||||
|
|
||||||
|
if ((~temp.__control_word) & excepts)
|
||||||
|
{
|
||||||
|
/* Setting the exception flags may trigger a trap (at the next
|
||||||
|
floating-point instruction, but that does not matter).
|
||||||
|
ISO C23 (7.6.4.4) does not allow it. */
|
||||||
|
__asm__ volatile ("fldcw %0" : : "m" (*&temp.__control_word));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store the new status word (along with the rest of the environment). */
|
||||||
|
__asm__ ("fldenv %0" : : "m" (*&temp));
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* Configuration for math tests: support for setting exception flags
|
||||||
|
without causing enabled traps. i686 version.
|
||||||
|
Copyright (C) 2023 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
|
||||||
|
<https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#ifndef I386_FPU_MATH_TESTS_TRAP_FORCE_H
|
||||||
|
#define I386_FPU_MATH_TESTS_TRAP_FORCE_H 1
|
||||||
|
|
||||||
|
#include <cpu-features.h>
|
||||||
|
|
||||||
|
/* Setting exception flags in FPU Status Register results in enabled traps for
|
||||||
|
those exceptions being taken. */
|
||||||
|
#define EXCEPTION_SET_FORCES_TRAP !CPU_FEATURE_USABLE (SSE)
|
||||||
|
|
||||||
|
#endif /* math-tests-trap-force.h. */
|
|
@ -22,17 +22,8 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <cpu-features.h>
|
||||||
static bool
|
#include <support/check.h>
|
||||||
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
|
static uint32_t
|
||||||
get_sse_mxcsr (void)
|
get_sse_mxcsr (void)
|
||||||
|
@ -164,13 +155,9 @@ sse_tests (void)
|
||||||
static int
|
static int
|
||||||
do_test (void)
|
do_test (void)
|
||||||
{
|
{
|
||||||
if (!have_sse2 ())
|
if (!CPU_FEATURE_USABLE (SSE2))
|
||||||
{
|
FAIL_UNSUPPORTED ("CPU does not support SSE2");
|
||||||
puts ("CPU does not support SSE2, cannot test");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return sse_tests ();
|
return sse_tests ();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TEST_FUNCTION do_test ()
|
#include <support/test-driver.c>
|
||||||
#include <test-skeleton.c>
|
|
||||||
|
|
Loading…
Reference in New Issue