1*498c7b5eSderaadt /* $OpenBSD: fenv_test.c,v 1.7 2021/12/13 18:04:28 deraadt Exp $ */
24c142d94Sbluhm /*-
34c142d94Sbluhm * Copyright (c) 2004 David Schultz <das@FreeBSD.org>
44c142d94Sbluhm * All rights reserved.
54c142d94Sbluhm *
64c142d94Sbluhm * Redistribution and use in source and binary forms, with or without
74c142d94Sbluhm * modification, are permitted provided that the following conditions
84c142d94Sbluhm * are met:
94c142d94Sbluhm * 1. Redistributions of source code must retain the above copyright
104c142d94Sbluhm * notice, this list of conditions and the following disclaimer.
114c142d94Sbluhm * 2. Redistributions in binary form must reproduce the above copyright
124c142d94Sbluhm * notice, this list of conditions and the following disclaimer in the
134c142d94Sbluhm * documentation and/or other materials provided with the distribution.
144c142d94Sbluhm *
154c142d94Sbluhm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
164c142d94Sbluhm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
174c142d94Sbluhm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
184c142d94Sbluhm * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
194c142d94Sbluhm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
204c142d94Sbluhm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
214c142d94Sbluhm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
224c142d94Sbluhm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
234c142d94Sbluhm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
244c142d94Sbluhm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
254c142d94Sbluhm * SUCH DAMAGE.
264c142d94Sbluhm */
274c142d94Sbluhm
28c36e572eSmbuhl #include "macros.h"
29c36e572eSmbuhl
304c142d94Sbluhm /*
314c142d94Sbluhm * Test the correctness and C99-compliance of various fenv.h features.
324c142d94Sbluhm */
334c142d94Sbluhm
344c142d94Sbluhm #include <sys/types.h>
354c142d94Sbluhm #include <sys/wait.h>
364c142d94Sbluhm #include <assert.h>
374c142d94Sbluhm #include <err.h>
384c142d94Sbluhm #include <fenv.h>
394c142d94Sbluhm #include <float.h>
40c36e572eSmbuhl #ifndef __OpenBSD__
41c36e572eSmbuhl #include <libutil.h>
42c36e572eSmbuhl #endif
434c142d94Sbluhm #include <math.h>
444c142d94Sbluhm #include <signal.h>
454c142d94Sbluhm #include <stdio.h>
464c142d94Sbluhm #include <string.h>
474c142d94Sbluhm #include <unistd.h>
484c142d94Sbluhm
49c36e572eSmbuhl #include "test-utils.h"
504c142d94Sbluhm
514c142d94Sbluhm #define NEXCEPTS (sizeof(std_excepts) / sizeof(std_excepts[0]))
524c142d94Sbluhm
534c142d94Sbluhm static const int std_excepts[] = {
544c142d94Sbluhm FE_INVALID,
554c142d94Sbluhm FE_DIVBYZERO,
564c142d94Sbluhm FE_OVERFLOW,
574c142d94Sbluhm FE_UNDERFLOW,
584c142d94Sbluhm FE_INEXACT,
594c142d94Sbluhm };
604c142d94Sbluhm
614c142d94Sbluhm /* init_exceptsets() initializes this to the power set of std_excepts[] */
624c142d94Sbluhm static int std_except_sets[1 << NEXCEPTS];
634c142d94Sbluhm
644c142d94Sbluhm #pragma STDC FENV_ACCESS ON
654c142d94Sbluhm
664c142d94Sbluhm /*
674c142d94Sbluhm * Initialize std_except_sets[] to the power set of std_excepts[]
684c142d94Sbluhm */
69c36e572eSmbuhl static __attribute__((constructor)) void
do_setup(void)70c36e572eSmbuhl do_setup(void)
714c142d94Sbluhm {
724c142d94Sbluhm unsigned i, j, sr;
734c142d94Sbluhm
74c36e572eSmbuhl /* Avoid double output after fork() */
75c36e572eSmbuhl setvbuf(stdout, NULL, _IONBF, 0);
76c36e572eSmbuhl
774c142d94Sbluhm for (i = 0; i < 1 << NEXCEPTS; i++) {
784c142d94Sbluhm for (sr = i, j = 0; sr != 0; sr >>= 1, j++)
794c142d94Sbluhm std_except_sets[i] |= std_excepts[j] & ((~sr & 1) - 1);
804c142d94Sbluhm }
814c142d94Sbluhm }
824c142d94Sbluhm
834c142d94Sbluhm /*
844c142d94Sbluhm * Raise a floating-point exception without relying on the standard
854c142d94Sbluhm * library routines, which we are trying to test.
864c142d94Sbluhm *
874c142d94Sbluhm * XXX We can't raise an {over,under}flow without also raising an
884c142d94Sbluhm * inexact exception.
894c142d94Sbluhm */
904c142d94Sbluhm static void
raiseexcept(int excepts)914c142d94Sbluhm raiseexcept(int excepts)
924c142d94Sbluhm {
934c142d94Sbluhm volatile double d;
944c142d94Sbluhm
954c142d94Sbluhm /*
964c142d94Sbluhm * With a compiler that supports the FENV_ACCESS pragma
974c142d94Sbluhm * properly, simple expressions like '0.0 / 0.0' should
984c142d94Sbluhm * be sufficient to generate traps. Unfortunately, we
994c142d94Sbluhm * need to bring a volatile variable into the equation
1004c142d94Sbluhm * to prevent incorrect optimizations.
1014c142d94Sbluhm */
1024c142d94Sbluhm if (excepts & FE_INVALID) {
1034c142d94Sbluhm d = 0.0;
1044c142d94Sbluhm d = 0.0 / d;
1054c142d94Sbluhm }
1064c142d94Sbluhm if (excepts & FE_DIVBYZERO) {
1074c142d94Sbluhm d = 0.0;
1084c142d94Sbluhm d = 1.0 / d;
1094c142d94Sbluhm }
1104c142d94Sbluhm if (excepts & FE_OVERFLOW) {
1114c142d94Sbluhm d = DBL_MAX;
1124c142d94Sbluhm d *= 2.0;
1134c142d94Sbluhm }
1144c142d94Sbluhm if (excepts & FE_UNDERFLOW) {
1154c142d94Sbluhm d = DBL_MIN;
1164c142d94Sbluhm d /= DBL_MAX;
1174c142d94Sbluhm }
1184c142d94Sbluhm if (excepts & FE_INEXACT) {
1194c142d94Sbluhm d = DBL_MIN;
1204c142d94Sbluhm d += 1.0;
1214c142d94Sbluhm }
1224c142d94Sbluhm
1234c142d94Sbluhm /*
1244c142d94Sbluhm * On the x86 (and some other architectures?) the FPU and
1254c142d94Sbluhm * integer units are decoupled. We need to execute an FWAIT
1264c142d94Sbluhm * or a floating-point instruction to get synchronous exceptions.
1274c142d94Sbluhm */
1284c142d94Sbluhm d = 1.0;
1294c142d94Sbluhm d += 1.0;
1304c142d94Sbluhm }
1314c142d94Sbluhm
1324c142d94Sbluhm /*
1334c142d94Sbluhm * Determine the current rounding mode without relying on the fenv
1344c142d94Sbluhm * routines. This function may raise an inexact exception.
1354c142d94Sbluhm */
1364c142d94Sbluhm static int
getround(void)1374c142d94Sbluhm getround(void)
1384c142d94Sbluhm {
139f5ab549eSbluhm volatile double d, e;
1404c142d94Sbluhm
1414c142d94Sbluhm /*
1424c142d94Sbluhm * This test works just as well with 0.0 - 0.0, except on ia64
1434c142d94Sbluhm * where 0.0 - 0.0 gives the wrong sign when rounding downwards.
144f5ab549eSbluhm * For ia32 use a volatile double to force 64 bit rounding.
145f5ab549eSbluhm * Otherwise the i387 would use its internal 80 bit stack.
1464c142d94Sbluhm */
1474c142d94Sbluhm d = 1.0;
1484c142d94Sbluhm d -= 1.0;
1494c142d94Sbluhm if (copysign(1.0, d) < 0.0)
1504c142d94Sbluhm return (FE_DOWNWARD);
1514c142d94Sbluhm
1524c142d94Sbluhm d = 1.0;
153f5ab549eSbluhm e = d + (DBL_EPSILON * 3.0 / 4.0);
154f5ab549eSbluhm if (e == 1.0)
1554c142d94Sbluhm return (FE_TOWARDZERO);
156f5ab549eSbluhm e = d + (DBL_EPSILON * 1.0 / 4.0);
157f5ab549eSbluhm if (e > 1.0)
1584c142d94Sbluhm return (FE_UPWARD);
1594c142d94Sbluhm
1604c142d94Sbluhm return (FE_TONEAREST);
1614c142d94Sbluhm }
1624c142d94Sbluhm
1634c142d94Sbluhm static void
trap_handler(int sig)1644c142d94Sbluhm trap_handler(int sig)
1654c142d94Sbluhm {
1664c142d94Sbluhm
167c36e572eSmbuhl ATF_CHECK_EQ(SIGFPE, sig);
1684c142d94Sbluhm _exit(0);
1694c142d94Sbluhm }
1704c142d94Sbluhm
1714c142d94Sbluhm /*
1724c142d94Sbluhm * This tests checks the default FP environment, so it must be first.
1734c142d94Sbluhm * The memcmp() test below may be too much to ask for, since there
1744c142d94Sbluhm * could be multiple machine-specific default environments.
1754c142d94Sbluhm */
176c36e572eSmbuhl ATF_TC_WITHOUT_HEAD(dfl_env);
ATF_TC_BODY(dfl_env,tc)177c36e572eSmbuhl ATF_TC_BODY(dfl_env, tc)
1784c142d94Sbluhm {
1794c142d94Sbluhm #ifndef NO_STRICT_DFL_ENV
1804c142d94Sbluhm fenv_t env;
1814c142d94Sbluhm
1824c142d94Sbluhm fegetenv(&env);
183c36e572eSmbuhl /* Print the default environment for debugging purposes. */
184c36e572eSmbuhl hexdump(&env, sizeof(env), "current fenv ", HD_OMIT_CHARS);
185c36e572eSmbuhl hexdump(FE_DFL_ENV, sizeof(env), "default fenv ", HD_OMIT_CHARS);
186c36e572eSmbuhl CHECK_FP_EXCEPTIONS(0, FE_ALL_EXCEPT);
1874c142d94Sbluhm #ifdef __amd64__
1884c142d94Sbluhm /*
1894c142d94Sbluhm * Compare the fields that the AMD [1] and Intel [2] specs say will be
1904c142d94Sbluhm * set once fnstenv returns.
1914c142d94Sbluhm *
1924c142d94Sbluhm * Not all amd64 capable processors implement the fnstenv instruction
1934c142d94Sbluhm * by zero'ing out the env.__x87.__other field (example: AMD Opteron
1944c142d94Sbluhm * 6308). The AMD64/x64 specs aren't explicit on what the
1954c142d94Sbluhm * env.__x87.__other field will contain after fnstenv is executed, so
1964c142d94Sbluhm * the values in env.__x87.__other could be filled with arbitrary
1974c142d94Sbluhm * data depending on how the CPU implements fnstenv.
1984c142d94Sbluhm *
1994c142d94Sbluhm * 1. http://support.amd.com/TechDocs/26569_APM_v5.pdf
2004c142d94Sbluhm * 2. http://www.intel.com/Assets/en_US/PDF/manual/253666.pdf
2014c142d94Sbluhm */
202c36e572eSmbuhl ATF_CHECK(memcmp(&env.__mxcsr, &FE_DFL_ENV->__mxcsr,
2034c142d94Sbluhm sizeof(env.__mxcsr)) == 0);
204c36e572eSmbuhl ATF_CHECK(memcmp(&env.__x87.__control, &FE_DFL_ENV->__x87.__control,
2054c142d94Sbluhm sizeof(env.__x87.__control)) == 0);
206c36e572eSmbuhl ATF_CHECK(memcmp(&env.__x87.__status, &FE_DFL_ENV->__x87.__status,
2074c142d94Sbluhm sizeof(env.__x87.__status)) == 0);
208c36e572eSmbuhl ATF_CHECK(memcmp(&env.__x87.__tag, &FE_DFL_ENV->__x87.__tag,
2094c142d94Sbluhm sizeof(env.__x87.__tag)) == 0);
2104c142d94Sbluhm #else
211c36e572eSmbuhl ATF_CHECK_EQ(0, memcmp(&env, FE_DFL_ENV, sizeof(env)));
2124c142d94Sbluhm #endif
2134c142d94Sbluhm
2144c142d94Sbluhm #endif
215c36e572eSmbuhl CHECK_FP_EXCEPTIONS(0, FE_ALL_EXCEPT);
2164c142d94Sbluhm }
2174c142d94Sbluhm
2184c142d94Sbluhm /*
2194c142d94Sbluhm * Test fetestexcept() and feclearexcept().
2204c142d94Sbluhm */
221c36e572eSmbuhl ATF_TC_WITHOUT_HEAD(fetestclearexcept);
ATF_TC_BODY(fetestclearexcept,tc)222c36e572eSmbuhl ATF_TC_BODY(fetestclearexcept, tc)
2234c142d94Sbluhm {
2244c142d94Sbluhm int excepts, i;
2254c142d94Sbluhm
2264c142d94Sbluhm for (i = 0; i < 1 << NEXCEPTS; i++)
227c36e572eSmbuhl ATF_CHECK_EQ(0, fetestexcept(std_except_sets[i]));
2284c142d94Sbluhm for (i = 0; i < 1 << NEXCEPTS; i++) {
2294c142d94Sbluhm excepts = std_except_sets[i];
2304c142d94Sbluhm
2314c142d94Sbluhm /* FE_ALL_EXCEPT might be special-cased, as on i386. */
2324c142d94Sbluhm raiseexcept(excepts);
233c36e572eSmbuhl ATF_CHECK_EQ(excepts, fetestexcept(excepts));
234c36e572eSmbuhl ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
235c36e572eSmbuhl ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
2364c142d94Sbluhm
2374c142d94Sbluhm raiseexcept(excepts);
238c36e572eSmbuhl ATF_CHECK_EQ(excepts, fetestexcept(excepts));
2394c142d94Sbluhm if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
2404c142d94Sbluhm excepts |= FE_INEXACT;
241c36e572eSmbuhl ATF_CHECK_EQ(excepts, (fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT));
2424c142d94Sbluhm } else {
243c36e572eSmbuhl ATF_CHECK_EQ(excepts, fetestexcept(ALL_STD_EXCEPT));
2444c142d94Sbluhm }
245c36e572eSmbuhl ATF_CHECK_EQ(0, feclearexcept(excepts));
246c36e572eSmbuhl ATF_CHECK_EQ(0, fetestexcept(ALL_STD_EXCEPT));
2474c142d94Sbluhm }
2484c142d94Sbluhm }
2494c142d94Sbluhm
2504c142d94Sbluhm /*
2514c142d94Sbluhm * Test fegetexceptflag() and fesetexceptflag().
2524c142d94Sbluhm *
2534c142d94Sbluhm * Prerequisites: fetestexcept(), feclearexcept()
2544c142d94Sbluhm */
255c36e572eSmbuhl ATF_TC_WITHOUT_HEAD(fegsetexceptflag);
ATF_TC_BODY(fegsetexceptflag,tc)256c36e572eSmbuhl ATF_TC_BODY(fegsetexceptflag, tc)
2574c142d94Sbluhm {
2584c142d94Sbluhm fexcept_t flag;
2594c142d94Sbluhm int excepts, i;
2604c142d94Sbluhm
261c36e572eSmbuhl CHECK_FP_EXCEPTIONS(0, FE_ALL_EXCEPT);
2624c142d94Sbluhm for (i = 0; i < 1 << NEXCEPTS; i++) {
2634c142d94Sbluhm excepts = std_except_sets[i];
2644c142d94Sbluhm
265c36e572eSmbuhl ATF_CHECK_EQ(0, fegetexceptflag(&flag, excepts));
2664c142d94Sbluhm raiseexcept(ALL_STD_EXCEPT);
267c36e572eSmbuhl ATF_CHECK_EQ(0, fesetexceptflag(&flag, excepts));
268c36e572eSmbuhl ATF_CHECK_EQ((ALL_STD_EXCEPT ^ excepts), fetestexcept(ALL_STD_EXCEPT));
2694c142d94Sbluhm
270c36e572eSmbuhl ATF_CHECK_EQ(0, fegetexceptflag(&flag, FE_ALL_EXCEPT));
271c36e572eSmbuhl ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
272c36e572eSmbuhl ATF_CHECK_EQ(0, fesetexceptflag(&flag, excepts));
273c36e572eSmbuhl ATF_CHECK_EQ(0, fetestexcept(ALL_STD_EXCEPT));
274c36e572eSmbuhl ATF_CHECK_EQ(0, fesetexceptflag(&flag, ALL_STD_EXCEPT ^ excepts));
275c36e572eSmbuhl ATF_CHECK_EQ((ALL_STD_EXCEPT ^ excepts), fetestexcept(ALL_STD_EXCEPT));
2764c142d94Sbluhm
277c36e572eSmbuhl ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
2784c142d94Sbluhm }
2794c142d94Sbluhm }
2804c142d94Sbluhm
2814c142d94Sbluhm /*
2824c142d94Sbluhm * Test feraiseexcept().
2834c142d94Sbluhm *
2844c142d94Sbluhm * Prerequisites: fetestexcept(), feclearexcept()
2854c142d94Sbluhm */
286c36e572eSmbuhl ATF_TC_WITHOUT_HEAD(feraiseexcept);
ATF_TC_BODY(feraiseexcept,tc)287c36e572eSmbuhl ATF_TC_BODY(feraiseexcept, tc)
2884c142d94Sbluhm {
2894c142d94Sbluhm int excepts, i;
2904c142d94Sbluhm
2914c142d94Sbluhm for (i = 0; i < 1 << NEXCEPTS; i++) {
2924c142d94Sbluhm excepts = std_except_sets[i];
2934c142d94Sbluhm
294c36e572eSmbuhl ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
295c36e572eSmbuhl ATF_CHECK_EQ(0, feraiseexcept(excepts));
2964c142d94Sbluhm if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
2974c142d94Sbluhm excepts |= FE_INEXACT;
298c36e572eSmbuhl ATF_CHECK_EQ(excepts, (fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT));
2994c142d94Sbluhm } else {
300c36e572eSmbuhl ATF_CHECK_EQ(excepts, fetestexcept(ALL_STD_EXCEPT));
3014c142d94Sbluhm }
302c36e572eSmbuhl ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
3034c142d94Sbluhm }
304c36e572eSmbuhl ATF_CHECK_EQ(0, feraiseexcept(FE_INVALID | FE_DIVBYZERO));
305c36e572eSmbuhl ATF_CHECK_EQ((FE_INVALID | FE_DIVBYZERO), fetestexcept(ALL_STD_EXCEPT));
306c36e572eSmbuhl ATF_CHECK_EQ(0, feraiseexcept(FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT));
307c36e572eSmbuhl ATF_CHECK_EQ(ALL_STD_EXCEPT, fetestexcept(ALL_STD_EXCEPT));
308c36e572eSmbuhl ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
3094c142d94Sbluhm }
3104c142d94Sbluhm
3114c142d94Sbluhm /*
3124c142d94Sbluhm * Test fegetround() and fesetround().
3134c142d94Sbluhm */
314c36e572eSmbuhl ATF_TC_WITHOUT_HEAD(fegsetround);
ATF_TC_BODY(fegsetround,tc)315c36e572eSmbuhl ATF_TC_BODY(fegsetround, tc)
3164c142d94Sbluhm {
3174c142d94Sbluhm
318c36e572eSmbuhl ATF_CHECK_EQ(FE_TONEAREST, fegetround());
319c36e572eSmbuhl ATF_CHECK_EQ(FE_TONEAREST, getround());
320c36e572eSmbuhl ATF_CHECK_EQ(1, FLT_ROUNDS);
3214c142d94Sbluhm
322c36e572eSmbuhl ATF_CHECK_EQ(0, fesetround(FE_DOWNWARD));
323c36e572eSmbuhl ATF_CHECK_EQ(FE_DOWNWARD, fegetround());
324c36e572eSmbuhl ATF_CHECK_EQ(FE_DOWNWARD, getround());
325c36e572eSmbuhl ATF_CHECK_EQ(3, FLT_ROUNDS);
3264c142d94Sbluhm
327c36e572eSmbuhl ATF_CHECK_EQ(0, fesetround(FE_UPWARD));
328c36e572eSmbuhl ATF_CHECK_EQ(FE_UPWARD, getround());
329c36e572eSmbuhl ATF_CHECK_EQ(FE_UPWARD, fegetround());
330c36e572eSmbuhl ATF_CHECK_EQ(2, FLT_ROUNDS);
3314c142d94Sbluhm
332c36e572eSmbuhl ATF_CHECK_EQ(0, fesetround(FE_TOWARDZERO));
333c36e572eSmbuhl ATF_CHECK_EQ(FE_TOWARDZERO, getround());
334c36e572eSmbuhl ATF_CHECK_EQ(FE_TOWARDZERO, fegetround());
335c36e572eSmbuhl ATF_CHECK_EQ(0, FLT_ROUNDS);
3364c142d94Sbluhm
337c36e572eSmbuhl ATF_CHECK_EQ(0, fesetround(FE_TONEAREST));
338c36e572eSmbuhl ATF_CHECK_EQ(FE_TONEAREST, getround());
339c36e572eSmbuhl ATF_CHECK_EQ(1, FLT_ROUNDS);
3404c142d94Sbluhm
341c36e572eSmbuhl ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
3424c142d94Sbluhm }
3434c142d94Sbluhm
3444c142d94Sbluhm /*
3454c142d94Sbluhm * Test fegetenv() and fesetenv().
3464c142d94Sbluhm *
3474c142d94Sbluhm * Prerequisites: fetestexcept(), feclearexcept(), fegetround(), fesetround()
3484c142d94Sbluhm */
349c36e572eSmbuhl ATF_TC_WITHOUT_HEAD(fegsetenv);
ATF_TC_BODY(fegsetenv,tc)350c36e572eSmbuhl ATF_TC_BODY(fegsetenv, tc)
3514c142d94Sbluhm {
3524c142d94Sbluhm fenv_t env1, env2;
3534c142d94Sbluhm int excepts, i;
3544c142d94Sbluhm
3554c142d94Sbluhm for (i = 0; i < 1 << NEXCEPTS; i++) {
3564c142d94Sbluhm excepts = std_except_sets[i];
3574c142d94Sbluhm
358c36e572eSmbuhl ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
359c36e572eSmbuhl ATF_CHECK_EQ(FE_TONEAREST, fegetround());
360c36e572eSmbuhl ATF_CHECK_EQ(0, fegetenv(&env1));
3614c142d94Sbluhm
3624c142d94Sbluhm /*
3634c142d94Sbluhm * fe[gs]etenv() should be able to save and restore
3644c142d94Sbluhm * exception flags without the spurious inexact
3654c142d94Sbluhm * exceptions that afflict raiseexcept().
3664c142d94Sbluhm */
3674c142d94Sbluhm raiseexcept(excepts);
3684c142d94Sbluhm if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0 &&
3694c142d94Sbluhm (excepts & FE_INEXACT) == 0)
370c36e572eSmbuhl ATF_CHECK_EQ(0, feclearexcept(FE_INEXACT));
3714c142d94Sbluhm
3724c142d94Sbluhm fesetround(FE_DOWNWARD);
373c36e572eSmbuhl ATF_CHECK_EQ(0, fegetenv(&env2));
374c36e572eSmbuhl ATF_CHECK_EQ(0, fesetenv(&env1));
375c36e572eSmbuhl ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
376c36e572eSmbuhl ATF_CHECK_EQ(FE_TONEAREST, fegetround());
3774c142d94Sbluhm
378c36e572eSmbuhl ATF_CHECK_EQ(0, fesetenv(&env2));
379c36e572eSmbuhl
380c36e572eSmbuhl /*
381c36e572eSmbuhl * Some platforms like powerpc may set extra exception bits. Since
382c36e572eSmbuhl * only standard exceptions are tested, mask against ALL_STD_EXCEPT
383c36e572eSmbuhl */
384c36e572eSmbuhl ATF_CHECK_EQ(excepts, (fetestexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT));
385c36e572eSmbuhl
386c36e572eSmbuhl ATF_CHECK_EQ(FE_DOWNWARD, fegetround());
387c36e572eSmbuhl ATF_CHECK_EQ(0, fesetenv(&env1));
388c36e572eSmbuhl ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
389c36e572eSmbuhl ATF_CHECK_EQ(FE_TONEAREST, fegetround());
3904c142d94Sbluhm }
3914c142d94Sbluhm }
3924c142d94Sbluhm
3934c142d94Sbluhm /*
3944c142d94Sbluhm * Test fegetexcept(), fedisableexcept(), and feenableexcept().
3954c142d94Sbluhm *
3964c142d94Sbluhm * Prerequisites: fetestexcept(), feraiseexcept()
3974c142d94Sbluhm */
398c36e572eSmbuhl ATF_TC_WITHOUT_HEAD(masking);
ATF_TC_BODY(masking,tc)399c36e572eSmbuhl ATF_TC_BODY(masking, tc)
4004c142d94Sbluhm {
401487aca86Skettenis #if !defined(__arm__) && !defined(__aarch64__) && !defined(__riscv)
4024c142d94Sbluhm struct sigaction act;
4034c142d94Sbluhm int except, pass, raise, status;
4044c142d94Sbluhm unsigned i;
4054c142d94Sbluhm
406c36e572eSmbuhl ATF_REQUIRE_EQ(0, (fegetexcept() & ALL_STD_EXCEPT));
407c36e572eSmbuhl
408c36e572eSmbuhl /*
409c36e572eSmbuhl * Some CPUs, e.g. AArch64 QEMU does not support trapping on FP
410c36e572eSmbuhl * exceptions. In that case the trap enable bits are all RAZ/WI, so
411c36e572eSmbuhl * writing to those bits will be ignored and the the next read will
412c36e572eSmbuhl * return all zeroes for those bits. Skip the test if no floating
413c36e572eSmbuhl * point exceptions are supported and mark it XFAIL if some are missing.
414c36e572eSmbuhl */
415c36e572eSmbuhl ATF_REQUIRE_EQ(0, (feenableexcept(FE_ALL_EXCEPT)));
416c36e572eSmbuhl except = fegetexcept();
417c36e572eSmbuhl if (except == 0) {
418c36e572eSmbuhl atf_tc_skip("CPU does not support trapping on floating point "
419c36e572eSmbuhl "exceptions.");
420c36e572eSmbuhl } else if ((except & ALL_STD_EXCEPT) != ALL_STD_EXCEPT) {
421c36e572eSmbuhl atf_tc_expect_fail("Not all floating point exceptions can be "
422c36e572eSmbuhl "set to trap: %#x vs %#x", except, ALL_STD_EXCEPT);
423c36e572eSmbuhl }
424c36e572eSmbuhl fedisableexcept(FE_ALL_EXCEPT);
425c36e572eSmbuhl
426c36e572eSmbuhl
427c36e572eSmbuhl ATF_CHECK_EQ(0, (feenableexcept(FE_INVALID|FE_OVERFLOW) & ALL_STD_EXCEPT));
428c36e572eSmbuhl ATF_CHECK_EQ((FE_INVALID | FE_OVERFLOW), (feenableexcept(FE_UNDERFLOW) & ALL_STD_EXCEPT));
429c36e572eSmbuhl ATF_CHECK_EQ((FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW), (fedisableexcept(FE_OVERFLOW) & ALL_STD_EXCEPT));
430c36e572eSmbuhl ATF_CHECK_EQ((FE_INVALID | FE_UNDERFLOW), (fegetexcept() & ALL_STD_EXCEPT));
431c36e572eSmbuhl ATF_CHECK_EQ((FE_INVALID | FE_UNDERFLOW), (fedisableexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT));
432c36e572eSmbuhl ATF_CHECK_EQ(0, (fegetexcept() & ALL_STD_EXCEPT));
4334c142d94Sbluhm
4344c142d94Sbluhm sigemptyset(&act.sa_mask);
4354c142d94Sbluhm act.sa_flags = 0;
4364c142d94Sbluhm act.sa_handler = trap_handler;
4374c142d94Sbluhm for (pass = 0; pass < 2; pass++) {
4384c142d94Sbluhm for (i = 0; i < NEXCEPTS; i++) {
4394c142d94Sbluhm except = std_excepts[i];
4404c142d94Sbluhm /* over/underflow may also raise inexact */
4414c142d94Sbluhm if (except == FE_INEXACT)
4424c142d94Sbluhm raise = FE_DIVBYZERO | FE_INVALID;
4434c142d94Sbluhm else
4444c142d94Sbluhm raise = ALL_STD_EXCEPT ^ except;
4454c142d94Sbluhm
4464c142d94Sbluhm /*
4474c142d94Sbluhm * We need to fork a child process because
4484c142d94Sbluhm * there isn't a portable way to recover from
4494c142d94Sbluhm * a floating-point exception.
4504c142d94Sbluhm */
4514c142d94Sbluhm switch(fork()) {
4524c142d94Sbluhm case 0: /* child */
453c36e572eSmbuhl ATF_CHECK_EQ(0, (fegetexcept() & ALL_STD_EXCEPT));
454c36e572eSmbuhl ATF_REQUIRE_EQ(0, (feenableexcept(except) & ALL_STD_EXCEPT));
455c36e572eSmbuhl ATF_CHECK_EQ(except, fegetexcept());
4564c142d94Sbluhm raiseexcept(raise);
457c36e572eSmbuhl ATF_CHECK_EQ(0, feraiseexcept(raise));
458c36e572eSmbuhl ATF_CHECK_EQ(raise, fetestexcept(ALL_STD_EXCEPT));
4594c142d94Sbluhm
460c36e572eSmbuhl ATF_CHECK_EQ(0, sigaction(SIGFPE, &act, NULL));
4614c142d94Sbluhm switch (pass) {
4624c142d94Sbluhm case 0:
4634c142d94Sbluhm raiseexcept(except);
4644c142d94Sbluhm case 1:
4654c142d94Sbluhm feraiseexcept(except);
4664c142d94Sbluhm default:
467c36e572eSmbuhl ATF_REQUIRE(0);
4684c142d94Sbluhm }
469c36e572eSmbuhl ATF_REQUIRE(0);
4704c142d94Sbluhm default: /* parent */
471c36e572eSmbuhl ATF_REQUIRE(wait(&status) > 0);
4724c142d94Sbluhm /*
4734c142d94Sbluhm * Avoid assert() here so that it's possible
4744c142d94Sbluhm * to examine a failed child's core dump.
4754c142d94Sbluhm */
4764c142d94Sbluhm if (!WIFEXITED(status))
4774c142d94Sbluhm errx(1, "child aborted\n");
478c36e572eSmbuhl ATF_CHECK_EQ(0, WEXITSTATUS(status));
4794c142d94Sbluhm break;
4804c142d94Sbluhm case -1: /* error */
481c36e572eSmbuhl ATF_REQUIRE(0);
4824c142d94Sbluhm }
4834c142d94Sbluhm }
4844c142d94Sbluhm }
485c36e572eSmbuhl ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
48662bcd63cSkettenis #endif
4874c142d94Sbluhm }
4884c142d94Sbluhm
4894c142d94Sbluhm /*
4904c142d94Sbluhm * Test feholdexcept() and feupdateenv().
4914c142d94Sbluhm *
4924c142d94Sbluhm * Prerequisites: fetestexcept(), fegetround(), fesetround(),
4934c142d94Sbluhm * fedisableexcept(), feenableexcept()
4944c142d94Sbluhm */
495c36e572eSmbuhl ATF_TC_WITHOUT_HEAD(feholdupdate);
ATF_TC_BODY(feholdupdate,tc)496c36e572eSmbuhl ATF_TC_BODY(feholdupdate, tc)
4974c142d94Sbluhm {
4984c142d94Sbluhm fenv_t env;
4994c142d94Sbluhm
5004c142d94Sbluhm struct sigaction act;
5014c142d94Sbluhm int except, pass, status, raise;
5024c142d94Sbluhm unsigned i;
5034c142d94Sbluhm
5044c142d94Sbluhm sigemptyset(&act.sa_mask);
5054c142d94Sbluhm act.sa_flags = 0;
5064c142d94Sbluhm act.sa_handler = trap_handler;
5074c142d94Sbluhm for (pass = 0; pass < 2; pass++) {
5084c142d94Sbluhm for (i = 0; i < NEXCEPTS; i++) {
5094c142d94Sbluhm except = std_excepts[i];
5104c142d94Sbluhm /* over/underflow may also raise inexact */
5114c142d94Sbluhm if (except == FE_INEXACT)
5124c142d94Sbluhm raise = FE_DIVBYZERO | FE_INVALID;
5134c142d94Sbluhm else
5144c142d94Sbluhm raise = ALL_STD_EXCEPT ^ except;
5154c142d94Sbluhm
5164c142d94Sbluhm /*
5174c142d94Sbluhm * We need to fork a child process because
5184c142d94Sbluhm * there isn't a portable way to recover from
5194c142d94Sbluhm * a floating-point exception.
5204c142d94Sbluhm */
5214c142d94Sbluhm switch(fork()) {
5224c142d94Sbluhm case 0: /* child */
5234c142d94Sbluhm /*
5244c142d94Sbluhm * We don't want to cause a fatal exception in
5254c142d94Sbluhm * the child until the second pass, so we can
5264c142d94Sbluhm * check other properties of feupdateenv().
5274c142d94Sbluhm */
5284c142d94Sbluhm if (pass == 1)
529c36e572eSmbuhl ATF_REQUIRE_EQ(0,
530c36e572eSmbuhl feenableexcept(except) &
531c36e572eSmbuhl ALL_STD_EXCEPT);
5324c142d94Sbluhm raiseexcept(raise);
533c36e572eSmbuhl ATF_CHECK_EQ(0, fesetround(FE_DOWNWARD));
534c36e572eSmbuhl ATF_CHECK_EQ(0, feholdexcept(&env));
535c36e572eSmbuhl ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
5364c142d94Sbluhm raiseexcept(except);
537c36e572eSmbuhl ATF_CHECK_EQ(0, fesetround(FE_UPWARD));
5384c142d94Sbluhm
5394c142d94Sbluhm if (pass == 1)
540c36e572eSmbuhl ATF_CHECK_EQ(0, sigaction(SIGFPE, &act, NULL));
541c36e572eSmbuhl ATF_CHECK_EQ(0, feupdateenv(&env));
542c36e572eSmbuhl ATF_CHECK_EQ(FE_DOWNWARD, fegetround());
543c36e572eSmbuhl ATF_CHECK_EQ((except | raise), fetestexcept(ALL_STD_EXCEPT));
5444c142d94Sbluhm
545c36e572eSmbuhl ATF_CHECK_EQ(0, pass);
5464c142d94Sbluhm _exit(0);
5474c142d94Sbluhm default: /* parent */
548c36e572eSmbuhl ATF_REQUIRE(wait(&status) > 0);
5494c142d94Sbluhm /*
5504c142d94Sbluhm * Avoid assert() here so that it's possible
5514c142d94Sbluhm * to examine a failed child's core dump.
5524c142d94Sbluhm */
5534c142d94Sbluhm if (!WIFEXITED(status))
5544c142d94Sbluhm errx(1, "child aborted\n");
555c36e572eSmbuhl ATF_CHECK_EQ(0, WEXITSTATUS(status));
5564c142d94Sbluhm break;
5574c142d94Sbluhm case -1: /* error */
558c36e572eSmbuhl ATF_REQUIRE(0);
5594c142d94Sbluhm }
5604c142d94Sbluhm }
561487aca86Skettenis #if defined(__arm__) || defined(__aarch64__) || defined(__riscv)
56262bcd63cSkettenis break;
56362bcd63cSkettenis #endif
5644c142d94Sbluhm }
565c36e572eSmbuhl ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
5664c142d94Sbluhm }
5674c142d94Sbluhm
ATF_TP_ADD_TCS(tp)568c36e572eSmbuhl ATF_TP_ADD_TCS(tp)
5694c142d94Sbluhm {
570c36e572eSmbuhl ATF_TP_ADD_TC(tp, dfl_env);
571c36e572eSmbuhl ATF_TP_ADD_TC(tp, fetestclearexcept);
572c36e572eSmbuhl ATF_TP_ADD_TC(tp, fegsetexceptflag);
573c36e572eSmbuhl ATF_TP_ADD_TC(tp, feraiseexcept);
574c36e572eSmbuhl ATF_TP_ADD_TC(tp, fegsetround);
575c36e572eSmbuhl ATF_TP_ADD_TC(tp, fegsetenv);
576c36e572eSmbuhl ATF_TP_ADD_TC(tp, masking);
577c36e572eSmbuhl ATF_TP_ADD_TC(tp, feholdupdate);
5784c142d94Sbluhm
579c36e572eSmbuhl return (atf_no_error());
5804c142d94Sbluhm }
581