mirror of
https://github.com/opnsense/src.git
synced 2026-05-28 04:12:45 -04:00
Output a hex dump of the current fenv and the expected value to allow
comparing them without having to resort to interactive use of GDB.
(cherry picked from commit 6ccdee8ab5)
567 lines
16 KiB
C
567 lines
16 KiB
C
/*-
|
|
* Copyright (c) 2004 David Schultz <das@FreeBSD.org>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* Test the correctness and C99-compliance of various fenv.h features.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <assert.h>
|
|
#include <err.h>
|
|
#include <fenv.h>
|
|
#include <float.h>
|
|
#include <libutil.h>
|
|
#include <math.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "test-utils.h"
|
|
|
|
#define NEXCEPTS (sizeof(std_excepts) / sizeof(std_excepts[0]))
|
|
|
|
static const int std_excepts[] = {
|
|
FE_INVALID,
|
|
FE_DIVBYZERO,
|
|
FE_OVERFLOW,
|
|
FE_UNDERFLOW,
|
|
FE_INEXACT,
|
|
};
|
|
|
|
/* init_exceptsets() initializes this to the power set of std_excepts[] */
|
|
static int std_except_sets[1 << NEXCEPTS];
|
|
|
|
#pragma STDC FENV_ACCESS ON
|
|
|
|
/*
|
|
* Initialize std_except_sets[] to the power set of std_excepts[]
|
|
*/
|
|
static __attribute__((constructor)) void
|
|
do_setup(void)
|
|
{
|
|
unsigned i, j, sr;
|
|
|
|
/* Avoid double output after fork() */
|
|
setvbuf(stdout, NULL, _IONBF, 0);
|
|
|
|
for (i = 0; i < 1 << NEXCEPTS; i++) {
|
|
for (sr = i, j = 0; sr != 0; sr >>= 1, j++)
|
|
std_except_sets[i] |= std_excepts[j] & ((~sr & 1) - 1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Raise a floating-point exception without relying on the standard
|
|
* library routines, which we are trying to test.
|
|
*
|
|
* XXX We can't raise an {over,under}flow without also raising an
|
|
* inexact exception.
|
|
*/
|
|
static void
|
|
raiseexcept(int excepts)
|
|
{
|
|
volatile double d;
|
|
|
|
/*
|
|
* With a compiler that supports the FENV_ACCESS pragma
|
|
* properly, simple expressions like '0.0 / 0.0' should
|
|
* be sufficient to generate traps. Unfortunately, we
|
|
* need to bring a volatile variable into the equation
|
|
* to prevent incorrect optimizations.
|
|
*/
|
|
if (excepts & FE_INVALID) {
|
|
d = 0.0;
|
|
d = 0.0 / d;
|
|
}
|
|
if (excepts & FE_DIVBYZERO) {
|
|
d = 0.0;
|
|
d = 1.0 / d;
|
|
}
|
|
if (excepts & FE_OVERFLOW) {
|
|
d = DBL_MAX;
|
|
d *= 2.0;
|
|
}
|
|
if (excepts & FE_UNDERFLOW) {
|
|
d = DBL_MIN;
|
|
d /= DBL_MAX;
|
|
}
|
|
if (excepts & FE_INEXACT) {
|
|
d = DBL_MIN;
|
|
d += 1.0;
|
|
}
|
|
|
|
/*
|
|
* On the x86 (and some other architectures?) the FPU and
|
|
* integer units are decoupled. We need to execute an FWAIT
|
|
* or a floating-point instruction to get synchronous exceptions.
|
|
*/
|
|
d = 1.0;
|
|
d += 1.0;
|
|
}
|
|
|
|
/*
|
|
* Determine the current rounding mode without relying on the fenv
|
|
* routines. This function may raise an inexact exception.
|
|
*/
|
|
static int
|
|
getround(void)
|
|
{
|
|
volatile double d;
|
|
|
|
/*
|
|
* This test works just as well with 0.0 - 0.0, except on ia64
|
|
* where 0.0 - 0.0 gives the wrong sign when rounding downwards.
|
|
*/
|
|
d = 1.0;
|
|
d -= 1.0;
|
|
if (copysign(1.0, d) < 0.0)
|
|
return (FE_DOWNWARD);
|
|
|
|
d = 1.0;
|
|
if (d + (DBL_EPSILON * 3.0 / 4.0) == 1.0)
|
|
return (FE_TOWARDZERO);
|
|
if (d + (DBL_EPSILON * 1.0 / 4.0) > 1.0)
|
|
return (FE_UPWARD);
|
|
|
|
return (FE_TONEAREST);
|
|
}
|
|
|
|
static void
|
|
trap_handler(int sig)
|
|
{
|
|
|
|
ATF_CHECK_EQ(SIGFPE, sig);
|
|
_exit(0);
|
|
}
|
|
|
|
/*
|
|
* This tests checks the default FP environment, so it must be first.
|
|
* The memcmp() test below may be too much to ask for, since there
|
|
* could be multiple machine-specific default environments.
|
|
*/
|
|
ATF_TC_WITHOUT_HEAD(dfl_env);
|
|
ATF_TC_BODY(dfl_env, tc)
|
|
{
|
|
#ifndef NO_STRICT_DFL_ENV
|
|
fenv_t env;
|
|
|
|
fegetenv(&env);
|
|
/* Print the default environment for debugging purposes. */
|
|
hexdump(&env, sizeof(env), "current fenv ", HD_OMIT_CHARS);
|
|
hexdump(FE_DFL_ENV, sizeof(env), "default fenv ", HD_OMIT_CHARS);
|
|
CHECK_FP_EXCEPTIONS(0, FE_ALL_EXCEPT);
|
|
#ifdef __amd64__
|
|
/*
|
|
* Compare the fields that the AMD [1] and Intel [2] specs say will be
|
|
* set once fnstenv returns.
|
|
*
|
|
* Not all amd64 capable processors implement the fnstenv instruction
|
|
* by zero'ing out the env.__x87.__other field (example: AMD Opteron
|
|
* 6308). The AMD64/x64 specs aren't explicit on what the
|
|
* env.__x87.__other field will contain after fnstenv is executed, so
|
|
* the values in env.__x87.__other could be filled with arbitrary
|
|
* data depending on how the CPU implements fnstenv.
|
|
*
|
|
* 1. http://support.amd.com/TechDocs/26569_APM_v5.pdf
|
|
* 2. http://www.intel.com/Assets/en_US/PDF/manual/253666.pdf
|
|
*/
|
|
ATF_CHECK(memcmp(&env.__mxcsr, &FE_DFL_ENV->__mxcsr,
|
|
sizeof(env.__mxcsr)) == 0);
|
|
ATF_CHECK(memcmp(&env.__x87.__control, &FE_DFL_ENV->__x87.__control,
|
|
sizeof(env.__x87.__control)) == 0);
|
|
ATF_CHECK(memcmp(&env.__x87.__status, &FE_DFL_ENV->__x87.__status,
|
|
sizeof(env.__x87.__status)) == 0);
|
|
ATF_CHECK(memcmp(&env.__x87.__tag, &FE_DFL_ENV->__x87.__tag,
|
|
sizeof(env.__x87.__tag)) == 0);
|
|
#else
|
|
ATF_CHECK_EQ(0, memcmp(&env, FE_DFL_ENV, sizeof(env)));
|
|
#endif
|
|
|
|
#endif
|
|
CHECK_FP_EXCEPTIONS(0, FE_ALL_EXCEPT);
|
|
}
|
|
|
|
/*
|
|
* Test fetestexcept() and feclearexcept().
|
|
*/
|
|
ATF_TC_WITHOUT_HEAD(fetestclearexcept);
|
|
ATF_TC_BODY(fetestclearexcept, tc)
|
|
{
|
|
int excepts, i;
|
|
|
|
for (i = 0; i < 1 << NEXCEPTS; i++)
|
|
ATF_CHECK_EQ(0, fetestexcept(std_except_sets[i]));
|
|
for (i = 0; i < 1 << NEXCEPTS; i++) {
|
|
excepts = std_except_sets[i];
|
|
|
|
/* FE_ALL_EXCEPT might be special-cased, as on i386. */
|
|
raiseexcept(excepts);
|
|
ATF_CHECK_EQ(excepts, fetestexcept(excepts));
|
|
ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
|
|
ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
|
|
|
|
raiseexcept(excepts);
|
|
ATF_CHECK_EQ(excepts, fetestexcept(excepts));
|
|
if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
|
|
excepts |= FE_INEXACT;
|
|
ATF_CHECK_EQ(excepts, (fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT));
|
|
} else {
|
|
ATF_CHECK_EQ(excepts, fetestexcept(ALL_STD_EXCEPT));
|
|
}
|
|
ATF_CHECK_EQ(0, feclearexcept(excepts));
|
|
ATF_CHECK_EQ(0, fetestexcept(ALL_STD_EXCEPT));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Test fegetexceptflag() and fesetexceptflag().
|
|
*
|
|
* Prerequisites: fetestexcept(), feclearexcept()
|
|
*/
|
|
ATF_TC_WITHOUT_HEAD(fegsetexceptflag);
|
|
ATF_TC_BODY(fegsetexceptflag, tc)
|
|
{
|
|
fexcept_t flag;
|
|
int excepts, i;
|
|
|
|
CHECK_FP_EXCEPTIONS(0, FE_ALL_EXCEPT);
|
|
for (i = 0; i < 1 << NEXCEPTS; i++) {
|
|
excepts = std_except_sets[i];
|
|
|
|
ATF_CHECK_EQ(0, fegetexceptflag(&flag, excepts));
|
|
raiseexcept(ALL_STD_EXCEPT);
|
|
ATF_CHECK_EQ(0, fesetexceptflag(&flag, excepts));
|
|
ATF_CHECK_EQ((ALL_STD_EXCEPT ^ excepts), fetestexcept(ALL_STD_EXCEPT));
|
|
|
|
ATF_CHECK_EQ(0, fegetexceptflag(&flag, FE_ALL_EXCEPT));
|
|
ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
|
|
ATF_CHECK_EQ(0, fesetexceptflag(&flag, excepts));
|
|
ATF_CHECK_EQ(0, fetestexcept(ALL_STD_EXCEPT));
|
|
ATF_CHECK_EQ(0, fesetexceptflag(&flag, ALL_STD_EXCEPT ^ excepts));
|
|
ATF_CHECK_EQ((ALL_STD_EXCEPT ^ excepts), fetestexcept(ALL_STD_EXCEPT));
|
|
|
|
ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Test feraiseexcept().
|
|
*
|
|
* Prerequisites: fetestexcept(), feclearexcept()
|
|
*/
|
|
ATF_TC_WITHOUT_HEAD(feraiseexcept);
|
|
ATF_TC_BODY(feraiseexcept, tc)
|
|
{
|
|
int excepts, i;
|
|
|
|
for (i = 0; i < 1 << NEXCEPTS; i++) {
|
|
excepts = std_except_sets[i];
|
|
|
|
ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
|
|
ATF_CHECK_EQ(0, feraiseexcept(excepts));
|
|
if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
|
|
excepts |= FE_INEXACT;
|
|
ATF_CHECK_EQ(excepts, (fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT));
|
|
} else {
|
|
ATF_CHECK_EQ(excepts, fetestexcept(ALL_STD_EXCEPT));
|
|
}
|
|
ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
|
|
}
|
|
ATF_CHECK_EQ(0, feraiseexcept(FE_INVALID | FE_DIVBYZERO));
|
|
ATF_CHECK_EQ((FE_INVALID | FE_DIVBYZERO), fetestexcept(ALL_STD_EXCEPT));
|
|
ATF_CHECK_EQ(0, feraiseexcept(FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT));
|
|
ATF_CHECK_EQ(ALL_STD_EXCEPT, fetestexcept(ALL_STD_EXCEPT));
|
|
ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
|
|
}
|
|
|
|
/*
|
|
* Test fegetround() and fesetround().
|
|
*/
|
|
ATF_TC_WITHOUT_HEAD(fegsetround);
|
|
ATF_TC_BODY(fegsetround, tc)
|
|
{
|
|
|
|
ATF_CHECK_EQ(FE_TONEAREST, fegetround());
|
|
ATF_CHECK_EQ(FE_TONEAREST, getround());
|
|
ATF_CHECK_EQ(1, FLT_ROUNDS);
|
|
|
|
ATF_CHECK_EQ(0, fesetround(FE_DOWNWARD));
|
|
ATF_CHECK_EQ(FE_DOWNWARD, fegetround());
|
|
ATF_CHECK_EQ(FE_DOWNWARD, getround());
|
|
ATF_CHECK_EQ(3, FLT_ROUNDS);
|
|
|
|
ATF_CHECK_EQ(0, fesetround(FE_UPWARD));
|
|
ATF_CHECK_EQ(FE_UPWARD, getround());
|
|
ATF_CHECK_EQ(FE_UPWARD, fegetround());
|
|
ATF_CHECK_EQ(2, FLT_ROUNDS);
|
|
|
|
ATF_CHECK_EQ(0, fesetround(FE_TOWARDZERO));
|
|
ATF_CHECK_EQ(FE_TOWARDZERO, getround());
|
|
ATF_CHECK_EQ(FE_TOWARDZERO, fegetround());
|
|
ATF_CHECK_EQ(0, FLT_ROUNDS);
|
|
|
|
ATF_CHECK_EQ(0, fesetround(FE_TONEAREST));
|
|
ATF_CHECK_EQ(FE_TONEAREST, getround());
|
|
ATF_CHECK_EQ(1, FLT_ROUNDS);
|
|
|
|
ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
|
|
}
|
|
|
|
/*
|
|
* Test fegetenv() and fesetenv().
|
|
*
|
|
* Prerequisites: fetestexcept(), feclearexcept(), fegetround(), fesetround()
|
|
*/
|
|
ATF_TC_WITHOUT_HEAD(fegsetenv);
|
|
ATF_TC_BODY(fegsetenv, tc)
|
|
{
|
|
fenv_t env1, env2;
|
|
int excepts, i;
|
|
|
|
for (i = 0; i < 1 << NEXCEPTS; i++) {
|
|
excepts = std_except_sets[i];
|
|
|
|
ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
|
|
ATF_CHECK_EQ(FE_TONEAREST, fegetround());
|
|
ATF_CHECK_EQ(0, fegetenv(&env1));
|
|
|
|
/*
|
|
* fe[gs]etenv() should be able to save and restore
|
|
* exception flags without the spurious inexact
|
|
* exceptions that afflict raiseexcept().
|
|
*/
|
|
raiseexcept(excepts);
|
|
if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0 &&
|
|
(excepts & FE_INEXACT) == 0)
|
|
ATF_CHECK_EQ(0, feclearexcept(FE_INEXACT));
|
|
|
|
fesetround(FE_DOWNWARD);
|
|
ATF_CHECK_EQ(0, fegetenv(&env2));
|
|
ATF_CHECK_EQ(0, fesetenv(&env1));
|
|
ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
|
|
ATF_CHECK_EQ(FE_TONEAREST, fegetround());
|
|
|
|
ATF_CHECK_EQ(0, fesetenv(&env2));
|
|
|
|
/*
|
|
* Some platforms like powerpc may set extra exception bits. Since
|
|
* only standard exceptions are tested, mask against ALL_STD_EXCEPT
|
|
*/
|
|
ATF_CHECK_EQ(excepts, (fetestexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT));
|
|
|
|
ATF_CHECK_EQ(FE_DOWNWARD, fegetround());
|
|
ATF_CHECK_EQ(0, fesetenv(&env1));
|
|
ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
|
|
ATF_CHECK_EQ(FE_TONEAREST, fegetround());
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Test fegetexcept(), fedisableexcept(), and feenableexcept().
|
|
*
|
|
* Prerequisites: fetestexcept(), feraiseexcept()
|
|
*/
|
|
ATF_TC_WITHOUT_HEAD(masking);
|
|
ATF_TC_BODY(masking, tc)
|
|
{
|
|
struct sigaction act;
|
|
int except, pass, raise, status;
|
|
unsigned i;
|
|
|
|
ATF_REQUIRE_EQ(0, (fegetexcept() & ALL_STD_EXCEPT));
|
|
|
|
/*
|
|
* Some CPUs, e.g. AArch64 QEMU does not support trapping on FP
|
|
* exceptions. In that case the trap enable bits are all RAZ/WI, so
|
|
* writing to those bits will be ignored and the the next read will
|
|
* return all zeroes for those bits. Skip the test if no floating
|
|
* point exceptions are supported and mark it XFAIL if some are missing.
|
|
*/
|
|
ATF_REQUIRE_EQ(0, (feenableexcept(FE_ALL_EXCEPT)));
|
|
except = fegetexcept();
|
|
if (except == 0) {
|
|
atf_tc_skip("CPU does not support trapping on floating point "
|
|
"exceptions.");
|
|
} else if ((except & ALL_STD_EXCEPT) != ALL_STD_EXCEPT) {
|
|
atf_tc_expect_fail("Not all floating point exceptions can be "
|
|
"set to trap: %#x vs %#x", except, ALL_STD_EXCEPT);
|
|
}
|
|
fedisableexcept(FE_ALL_EXCEPT);
|
|
|
|
|
|
ATF_CHECK_EQ(0, (feenableexcept(FE_INVALID|FE_OVERFLOW) & ALL_STD_EXCEPT));
|
|
ATF_CHECK_EQ((FE_INVALID | FE_OVERFLOW), (feenableexcept(FE_UNDERFLOW) & ALL_STD_EXCEPT));
|
|
ATF_CHECK_EQ((FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW), (fedisableexcept(FE_OVERFLOW) & ALL_STD_EXCEPT));
|
|
ATF_CHECK_EQ((FE_INVALID | FE_UNDERFLOW), (fegetexcept() & ALL_STD_EXCEPT));
|
|
ATF_CHECK_EQ((FE_INVALID | FE_UNDERFLOW), (fedisableexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT));
|
|
ATF_CHECK_EQ(0, (fegetexcept() & ALL_STD_EXCEPT));
|
|
|
|
sigemptyset(&act.sa_mask);
|
|
act.sa_flags = 0;
|
|
act.sa_handler = trap_handler;
|
|
for (pass = 0; pass < 2; pass++) {
|
|
for (i = 0; i < NEXCEPTS; i++) {
|
|
except = std_excepts[i];
|
|
/* over/underflow may also raise inexact */
|
|
if (except == FE_INEXACT)
|
|
raise = FE_DIVBYZERO | FE_INVALID;
|
|
else
|
|
raise = ALL_STD_EXCEPT ^ except;
|
|
|
|
/*
|
|
* We need to fork a child process because
|
|
* there isn't a portable way to recover from
|
|
* a floating-point exception.
|
|
*/
|
|
switch(fork()) {
|
|
case 0: /* child */
|
|
ATF_CHECK_EQ(0, (fegetexcept() & ALL_STD_EXCEPT));
|
|
ATF_REQUIRE_EQ(0, (feenableexcept(except) & ALL_STD_EXCEPT));
|
|
ATF_CHECK_EQ(except, fegetexcept());
|
|
raiseexcept(raise);
|
|
ATF_CHECK_EQ(0, feraiseexcept(raise));
|
|
ATF_CHECK_EQ(raise, fetestexcept(ALL_STD_EXCEPT));
|
|
|
|
ATF_CHECK_EQ(0, sigaction(SIGFPE, &act, NULL));
|
|
switch (pass) {
|
|
case 0:
|
|
raiseexcept(except);
|
|
case 1:
|
|
feraiseexcept(except);
|
|
default:
|
|
ATF_REQUIRE(0);
|
|
}
|
|
ATF_REQUIRE(0);
|
|
default: /* parent */
|
|
ATF_REQUIRE(wait(&status) > 0);
|
|
/*
|
|
* Avoid assert() here so that it's possible
|
|
* to examine a failed child's core dump.
|
|
*/
|
|
if (!WIFEXITED(status))
|
|
errx(1, "child aborted\n");
|
|
ATF_CHECK_EQ(0, WEXITSTATUS(status));
|
|
break;
|
|
case -1: /* error */
|
|
ATF_REQUIRE(0);
|
|
}
|
|
}
|
|
}
|
|
ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
|
|
}
|
|
|
|
/*
|
|
* Test feholdexcept() and feupdateenv().
|
|
*
|
|
* Prerequisites: fetestexcept(), fegetround(), fesetround(),
|
|
* fedisableexcept(), feenableexcept()
|
|
*/
|
|
ATF_TC_WITHOUT_HEAD(feholdupdate);
|
|
ATF_TC_BODY(feholdupdate, tc)
|
|
{
|
|
fenv_t env;
|
|
|
|
struct sigaction act;
|
|
int except, pass, status, raise;
|
|
unsigned i;
|
|
|
|
sigemptyset(&act.sa_mask);
|
|
act.sa_flags = 0;
|
|
act.sa_handler = trap_handler;
|
|
for (pass = 0; pass < 2; pass++) {
|
|
for (i = 0; i < NEXCEPTS; i++) {
|
|
except = std_excepts[i];
|
|
/* over/underflow may also raise inexact */
|
|
if (except == FE_INEXACT)
|
|
raise = FE_DIVBYZERO | FE_INVALID;
|
|
else
|
|
raise = ALL_STD_EXCEPT ^ except;
|
|
|
|
/*
|
|
* We need to fork a child process because
|
|
* there isn't a portable way to recover from
|
|
* a floating-point exception.
|
|
*/
|
|
switch(fork()) {
|
|
case 0: /* child */
|
|
/*
|
|
* We don't want to cause a fatal exception in
|
|
* the child until the second pass, so we can
|
|
* check other properties of feupdateenv().
|
|
*/
|
|
if (pass == 1)
|
|
ATF_REQUIRE_EQ(0, feenableexcept(except) & ALL_STD_EXCEPT);
|
|
raiseexcept(raise);
|
|
ATF_CHECK_EQ(0, fesetround(FE_DOWNWARD));
|
|
ATF_CHECK_EQ(0, feholdexcept(&env));
|
|
ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
|
|
raiseexcept(except);
|
|
ATF_CHECK_EQ(0, fesetround(FE_UPWARD));
|
|
|
|
if (pass == 1)
|
|
ATF_CHECK_EQ(0, sigaction(SIGFPE, &act, NULL));
|
|
ATF_CHECK_EQ(0, feupdateenv(&env));
|
|
ATF_CHECK_EQ(FE_DOWNWARD, fegetround());
|
|
ATF_CHECK_EQ((except | raise), fetestexcept(ALL_STD_EXCEPT));
|
|
|
|
ATF_CHECK_EQ(0, pass);
|
|
_exit(0);
|
|
default: /* parent */
|
|
ATF_REQUIRE(wait(&status) > 0);
|
|
/*
|
|
* Avoid assert() here so that it's possible
|
|
* to examine a failed child's core dump.
|
|
*/
|
|
if (!WIFEXITED(status))
|
|
errx(1, "child aborted\n");
|
|
ATF_CHECK_EQ(0, WEXITSTATUS(status));
|
|
break;
|
|
case -1: /* error */
|
|
ATF_REQUIRE(0);
|
|
}
|
|
}
|
|
}
|
|
ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
|
|
}
|
|
|
|
ATF_TP_ADD_TCS(tp)
|
|
{
|
|
ATF_TP_ADD_TC(tp, dfl_env);
|
|
ATF_TP_ADD_TC(tp, fetestclearexcept);
|
|
ATF_TP_ADD_TC(tp, fegsetexceptflag);
|
|
ATF_TP_ADD_TC(tp, feraiseexcept);
|
|
ATF_TP_ADD_TC(tp, fegsetround);
|
|
ATF_TP_ADD_TC(tp, fegsetenv);
|
|
ATF_TP_ADD_TC(tp, masking);
|
|
ATF_TP_ADD_TC(tp, feholdupdate);
|
|
|
|
return (atf_no_error());
|
|
}
|