1*f9faf20aSandvar /* $NetBSD: fenv.c,v 1.10 2021/09/03 21:54:59 andvar Exp $ */
27f1183f2Sjoerg
37f1183f2Sjoerg /*-
47f1183f2Sjoerg * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG>
57f1183f2Sjoerg * All rights reserved.
67f1183f2Sjoerg *
77f1183f2Sjoerg * Redistribution and use in source and binary forms, with or without
87f1183f2Sjoerg * modification, are permitted provided that the following conditions
97f1183f2Sjoerg * are met:
107f1183f2Sjoerg * 1. Redistributions of source code must retain the above copyright
117f1183f2Sjoerg * notice, this list of conditions and the following disclaimer.
127f1183f2Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
137f1183f2Sjoerg * notice, this list of conditions and the following disclaimer in the
147f1183f2Sjoerg * documentation and/or other materials provided with the distribution.
157f1183f2Sjoerg *
167f1183f2Sjoerg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
177f1183f2Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
187f1183f2Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
197f1183f2Sjoerg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
207f1183f2Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
217f1183f2Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
227f1183f2Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237f1183f2Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
247f1183f2Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257f1183f2Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267f1183f2Sjoerg * SUCH DAMAGE.
277f1183f2Sjoerg */
287f1183f2Sjoerg
297f1183f2Sjoerg #include <sys/cdefs.h>
30*f9faf20aSandvar __RCSID("$NetBSD: fenv.c,v 1.10 2021/09/03 21:54:59 andvar Exp $");
317e30e943Schs
327e30e943Schs #include "namespace.h"
337f1183f2Sjoerg
347f1183f2Sjoerg #include <sys/param.h>
357f1183f2Sjoerg #include <sys/sysctl.h>
367f1183f2Sjoerg #include <assert.h>
377f1183f2Sjoerg #include <fenv.h>
387f1183f2Sjoerg #include <stddef.h>
397f1183f2Sjoerg #include <string.h>
407f1183f2Sjoerg
417e30e943Schs #ifdef __weak_alias
427e30e943Schs __weak_alias(feclearexcept,_feclearexcept)
437e30e943Schs __weak_alias(fedisableexcept,_fedisableexcept)
447e30e943Schs __weak_alias(feenableexcept,_feenableexcept)
457e30e943Schs __weak_alias(fegetenv,_fegetenv)
467e30e943Schs __weak_alias(fegetexcept,_fegetexcept)
477e30e943Schs __weak_alias(fegetexceptflag,_fegetexceptflag)
487e30e943Schs __weak_alias(fegetround,_fegetround)
497e30e943Schs __weak_alias(feholdexcept,_feholdexcept)
507e30e943Schs __weak_alias(feraiseexcept,_feraiseexcept)
517e30e943Schs __weak_alias(fesetenv,_fesetenv)
527e30e943Schs __weak_alias(fesetexceptflag,_fesetexceptflag)
537e30e943Schs __weak_alias(fesetround,_fesetround)
547e30e943Schs __weak_alias(fetestexcept,_fetestexcept)
557e30e943Schs __weak_alias(feupdateenv,_feupdateenv)
567e30e943Schs #endif
577e30e943Schs
587f1183f2Sjoerg /* Load x87 Control Word */
597f1183f2Sjoerg #define __fldcw(__cw) __asm__ __volatile__ \
607f1183f2Sjoerg ("fldcw %0" : : "m" (__cw))
617f1183f2Sjoerg
627f1183f2Sjoerg /* No-Wait Store Control Word */
637f1183f2Sjoerg #define __fnstcw(__cw) __asm__ __volatile__ \
647f1183f2Sjoerg ("fnstcw %0" : "=m" (*(__cw)))
657f1183f2Sjoerg
667f1183f2Sjoerg /* No-Wait Store Status Word */
677f1183f2Sjoerg #define __fnstsw(__sw) __asm__ __volatile__ \
687f1183f2Sjoerg ("fnstsw %0" : "=am" (*(__sw)))
697f1183f2Sjoerg
707f1183f2Sjoerg /* No-Wait Clear Exception Flags */
717f1183f2Sjoerg #define __fnclex() __asm__ __volatile__ \
727f1183f2Sjoerg ("fnclex")
737f1183f2Sjoerg
747f1183f2Sjoerg /* Load x87 Environment */
757f1183f2Sjoerg #define __fldenv(__env) __asm__ __volatile__ \
767f1183f2Sjoerg ("fldenv %0" : : "m" (__env))
777f1183f2Sjoerg
787f1183f2Sjoerg /* No-Wait Store x87 environment */
797f1183f2Sjoerg #define __fnstenv(__env) __asm__ __volatile__ \
807f1183f2Sjoerg ("fnstenv %0" : "=m" (*(__env)))
817f1183f2Sjoerg
827f1183f2Sjoerg /* Check for and handle pending unmasked x87 pending FPU exceptions */
837f1183f2Sjoerg #define __fwait(__env) __asm__ __volatile__ \
847f1183f2Sjoerg ("fwait")
857f1183f2Sjoerg
867f1183f2Sjoerg /* Load the MXCSR register */
877f1183f2Sjoerg #define __ldmxcsr(__mxcsr) __asm__ __volatile__ \
887f1183f2Sjoerg ("ldmxcsr %0" : : "m" (__mxcsr))
897f1183f2Sjoerg
907f1183f2Sjoerg /* Store the MXCSR register state */
917f1183f2Sjoerg #define __stmxcsr(__mxcsr) __asm__ __volatile__ \
927f1183f2Sjoerg ("stmxcsr %0" : "=m" (*(__mxcsr)))
937f1183f2Sjoerg
947f1183f2Sjoerg /*
957f1183f2Sjoerg * The following constant represents the default floating-point environment
967f1183f2Sjoerg * (that is, the one installed at program startup) and has type pointer to
977f1183f2Sjoerg * const-qualified fenv_t.
987f1183f2Sjoerg *
997f1183f2Sjoerg * It can be used as an argument to the functions within the <fenv.h> header
1007f1183f2Sjoerg * that manage the floating-point environment, namely fesetenv() and
1017f1183f2Sjoerg * feupdateenv().
1027f1183f2Sjoerg *
1037f1183f2Sjoerg * x87 fpu registers are 16bit wide. The upper bits, 31-16, are marked as
1047f1183f2Sjoerg * RESERVED. We provide a partial floating-point environment, where we
1057f1183f2Sjoerg * define only the lower bits. The reserved bits are extracted and set by the
1067f1183f2Sjoerg * consumers of FE_DFL_ENV, during runtime.
1077f1183f2Sjoerg */
1087f1183f2Sjoerg fenv_t __fe_dfl_env = {
10926ca7efeSchristos .x87 = {
11026ca7efeSchristos .control = __NetBSD_NPXCW__, /* Control word register */
11126ca7efeSchristos .unused1 = 0, /* Unused */
11226ca7efeSchristos .status = 0, /* Status word register */
11326ca7efeSchristos .unused2 = 0, /* Unused */
11426ca7efeSchristos .tag = 0xffff, /* Tag word register */
11526ca7efeSchristos .unused3 = 0, /* Unused */
11626ca7efeSchristos .others = {
11726ca7efeSchristos 0, 0, 0, 0x0000ffff,
1187f1183f2Sjoerg }
1197f1183f2Sjoerg },
12026ca7efeSchristos .mxcsr = __INITIAL_MXCSR__ /* MXCSR register */
1217f1183f2Sjoerg };
1227f1183f2Sjoerg
1237f1183f2Sjoerg /*
1247f1183f2Sjoerg * Test for SSE support on this processor.
1257f1183f2Sjoerg *
1267f1183f2Sjoerg * We need to use ldmxcsr/stmxcsr to get correct results if any part
1277f1183f2Sjoerg * of the program was compiled to use SSE floating-point, but we can't
1287f1183f2Sjoerg * use SSE on older processors.
1297f1183f2Sjoerg *
1307f1183f2Sjoerg * In order to do so, we need to query the processor capabilities via the CPUID
1317f1183f2Sjoerg * instruction. We can make it even simpler though, by querying the machdep.sse
1327f1183f2Sjoerg * sysctl.
1337f1183f2Sjoerg */
1347f1183f2Sjoerg static int __HAS_SSE = 0;
1357f1183f2Sjoerg
1360c8d18a9Sjoerg static void __init_libm(void) __attribute__ ((constructor, used));
1377f1183f2Sjoerg
__init_libm(void)1380c8d18a9Sjoerg static void __init_libm(void)
1397f1183f2Sjoerg {
1405dad8344Staca size_t oldlen = sizeof(__HAS_SSE);
1417f1183f2Sjoerg int rv;
1420c8d18a9Sjoerg uint16_t control;
1437f1183f2Sjoerg
1447f1183f2Sjoerg rv = sysctlbyname("machdep.sse", &__HAS_SSE, &oldlen, NULL, 0);
1457f1183f2Sjoerg if (rv == -1)
1467f1183f2Sjoerg __HAS_SSE = 0;
1470c8d18a9Sjoerg
1480c8d18a9Sjoerg __fnstcw(&control);
1490c8d18a9Sjoerg __fe_dfl_env.x87.control = control;
1507f1183f2Sjoerg }
1517f1183f2Sjoerg
1527f1183f2Sjoerg /*
1537f1183f2Sjoerg * The feclearexcept() function clears the supported floating-point exceptions
1547f1183f2Sjoerg * represented by `excepts'.
1557f1183f2Sjoerg */
1567f1183f2Sjoerg int
feclearexcept(int excepts)1577f1183f2Sjoerg feclearexcept(int excepts)
1587f1183f2Sjoerg {
1597f1183f2Sjoerg fenv_t env;
1607f1183f2Sjoerg uint32_t mxcsr;
1617f1183f2Sjoerg int ex;
1627f1183f2Sjoerg
1637f1183f2Sjoerg _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
1647f1183f2Sjoerg
1657f1183f2Sjoerg ex = excepts & FE_ALL_EXCEPT;
1667f1183f2Sjoerg
1677f1183f2Sjoerg /* It's ~3x faster to call fnclex, than store/load fp env */
1687f1183f2Sjoerg if (ex == FE_ALL_EXCEPT) {
1697f1183f2Sjoerg __fnclex();
1707f1183f2Sjoerg } else {
1717f1183f2Sjoerg __fnstenv(&env);
1727f1183f2Sjoerg env.x87.status &= ~ex;
1737f1183f2Sjoerg __fldenv(env);
1747f1183f2Sjoerg }
1757f1183f2Sjoerg
1767f1183f2Sjoerg if (__HAS_SSE) {
1777f1183f2Sjoerg __stmxcsr(&mxcsr);
1787f1183f2Sjoerg mxcsr &= ~ex;
1797f1183f2Sjoerg __ldmxcsr(mxcsr);
1807f1183f2Sjoerg }
1817f1183f2Sjoerg
1827f1183f2Sjoerg /* Success */
1837f1183f2Sjoerg return (0);
1847f1183f2Sjoerg }
1857f1183f2Sjoerg
1867f1183f2Sjoerg /*
1877f1183f2Sjoerg * The fegetexceptflag() function stores an implementation-defined
1887f1183f2Sjoerg * representation of the states of the floating-point status flags indicated by
1897f1183f2Sjoerg * the argument excepts in the object pointed to by the argument flagp.
1907f1183f2Sjoerg */
1917f1183f2Sjoerg int
fegetexceptflag(fexcept_t * flagp,int excepts)1927f1183f2Sjoerg fegetexceptflag(fexcept_t *flagp, int excepts)
1937f1183f2Sjoerg {
1947f1183f2Sjoerg uint32_t mxcsr;
1957f1183f2Sjoerg uint16_t status;
1967f1183f2Sjoerg int ex;
1977f1183f2Sjoerg
1987f1183f2Sjoerg _DIAGASSERT(flagp != NULL);
1997f1183f2Sjoerg _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
2007f1183f2Sjoerg
2017f1183f2Sjoerg ex = excepts & FE_ALL_EXCEPT;
2027f1183f2Sjoerg
2037f1183f2Sjoerg __fnstsw(&status);
2047f1183f2Sjoerg if (__HAS_SSE)
2057f1183f2Sjoerg __stmxcsr(&mxcsr);
2067f1183f2Sjoerg else
2077f1183f2Sjoerg mxcsr = 0;
2087f1183f2Sjoerg
2097f1183f2Sjoerg *flagp = (mxcsr | status) & ex;
2107f1183f2Sjoerg
2117f1183f2Sjoerg /* Success */
2127f1183f2Sjoerg return (0);
2137f1183f2Sjoerg }
2147f1183f2Sjoerg
2157f1183f2Sjoerg /*
2167f1183f2Sjoerg * The feraiseexcept() function raises the supported floating-point exceptions
2177f1183f2Sjoerg * represented by the argument `excepts'.
2187f1183f2Sjoerg *
2197f1183f2Sjoerg * The standard explicitly allows us to execute an instruction that has the
2207f1183f2Sjoerg * exception as a side effect, but we choose to manipulate the status register
2217f1183f2Sjoerg * directly.
2227f1183f2Sjoerg *
2237f1183f2Sjoerg * The validation of input is being deferred to fesetexceptflag().
2247f1183f2Sjoerg */
2257f1183f2Sjoerg int
feraiseexcept(int excepts)2267f1183f2Sjoerg feraiseexcept(int excepts)
2277f1183f2Sjoerg {
2287f1183f2Sjoerg fexcept_t ex;
2297f1183f2Sjoerg
2307f1183f2Sjoerg _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
2317f1183f2Sjoerg
2327f1183f2Sjoerg ex = excepts & FE_ALL_EXCEPT;
2337f1183f2Sjoerg fesetexceptflag(&ex, excepts);
2347f1183f2Sjoerg __fwait();
2357f1183f2Sjoerg
2367f1183f2Sjoerg /* Success */
2377f1183f2Sjoerg return (0);
2387f1183f2Sjoerg }
2397f1183f2Sjoerg
2407f1183f2Sjoerg /*
2417f1183f2Sjoerg * This function sets the floating-point status flags indicated by the argument
2427f1183f2Sjoerg * `excepts' to the states stored in the object pointed to by `flagp'. It does
2437f1183f2Sjoerg * NOT raise any floating-point exceptions, but only sets the state of the flags.
2447f1183f2Sjoerg */
2457f1183f2Sjoerg int
fesetexceptflag(const fexcept_t * flagp,int excepts)2467f1183f2Sjoerg fesetexceptflag(const fexcept_t *flagp, int excepts)
2477f1183f2Sjoerg {
2487f1183f2Sjoerg fenv_t env;
2497f1183f2Sjoerg uint32_t mxcsr;
2507f1183f2Sjoerg int ex;
2517f1183f2Sjoerg
2527f1183f2Sjoerg _DIAGASSERT(flagp != NULL);
2537f1183f2Sjoerg _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
2547f1183f2Sjoerg
2557f1183f2Sjoerg ex = excepts & FE_ALL_EXCEPT;
2567f1183f2Sjoerg
2577f1183f2Sjoerg __fnstenv(&env);
2587f1183f2Sjoerg env.x87.status &= ~ex;
2597f1183f2Sjoerg env.x87.status |= *flagp & ex;
2607f1183f2Sjoerg __fldenv(env);
2617f1183f2Sjoerg
2627f1183f2Sjoerg if (__HAS_SSE) {
2637f1183f2Sjoerg __stmxcsr(&mxcsr);
2647f1183f2Sjoerg mxcsr &= ~ex;
2657f1183f2Sjoerg mxcsr |= *flagp & ex;
2667f1183f2Sjoerg __ldmxcsr(mxcsr);
2677f1183f2Sjoerg }
2687f1183f2Sjoerg
2697f1183f2Sjoerg /* Success */
2707f1183f2Sjoerg return (0);
2717f1183f2Sjoerg }
2727f1183f2Sjoerg
2737f1183f2Sjoerg /*
2747f1183f2Sjoerg * The fetestexcept() function determines which of a specified subset of the
2757f1183f2Sjoerg * floating-point exception flags are currently set. The `excepts' argument
2767f1183f2Sjoerg * specifies the floating-point status flags to be queried.
2777f1183f2Sjoerg */
2787f1183f2Sjoerg int
fetestexcept(int excepts)2797f1183f2Sjoerg fetestexcept(int excepts)
2807f1183f2Sjoerg {
2817f1183f2Sjoerg uint32_t mxcsr;
2827f1183f2Sjoerg uint16_t status;
2837f1183f2Sjoerg int ex;
2847f1183f2Sjoerg
2857f1183f2Sjoerg _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
2867f1183f2Sjoerg
2877f1183f2Sjoerg ex = excepts & FE_ALL_EXCEPT;
2887f1183f2Sjoerg
2897f1183f2Sjoerg __fnstsw(&status);
2907f1183f2Sjoerg if (__HAS_SSE)
2917f1183f2Sjoerg __stmxcsr(&mxcsr);
2927f1183f2Sjoerg else
2937f1183f2Sjoerg mxcsr = 0;
2947f1183f2Sjoerg
2957f1183f2Sjoerg return ((status | mxcsr) & ex);
2967f1183f2Sjoerg }
2977f1183f2Sjoerg
2987f1183f2Sjoerg int
fegetround(void)2997f1183f2Sjoerg fegetround(void)
3007f1183f2Sjoerg {
3017f1183f2Sjoerg uint16_t control;
3027f1183f2Sjoerg
3037f1183f2Sjoerg /*
3047f1183f2Sjoerg * We assume that the x87 and the SSE unit agree on the
3057f1183f2Sjoerg * rounding mode. Reading the control word on the x87 turns
3067f1183f2Sjoerg * out to be about 5 times faster than reading it on the SSE
3077f1183f2Sjoerg * unit on an Opteron 244.
3087f1183f2Sjoerg */
3097f1183f2Sjoerg __fnstcw(&control);
3107f1183f2Sjoerg
3117f1183f2Sjoerg return (control & __X87_ROUND_MASK);
3127f1183f2Sjoerg }
3137f1183f2Sjoerg
3147f1183f2Sjoerg /*
3157f1183f2Sjoerg * The fesetround() function shall establish the rounding direction represented
3167f1183f2Sjoerg * by its argument round. If the argument is not equal to the value of a
3177f1183f2Sjoerg * rounding direction macro, the rounding direction is not changed.
3187f1183f2Sjoerg */
3197f1183f2Sjoerg int
fesetround(int round)3207f1183f2Sjoerg fesetround(int round)
3217f1183f2Sjoerg {
3227f1183f2Sjoerg uint32_t mxcsr;
3237f1183f2Sjoerg uint16_t control;
3247f1183f2Sjoerg
3257f1183f2Sjoerg if (round & ~__X87_ROUND_MASK) {
3267f1183f2Sjoerg /* Failure */
3277f1183f2Sjoerg return (-1);
3287f1183f2Sjoerg }
3297f1183f2Sjoerg
3307f1183f2Sjoerg __fnstcw(&control);
3317f1183f2Sjoerg control &= ~__X87_ROUND_MASK;
3327f1183f2Sjoerg control |= round;
3337f1183f2Sjoerg __fldcw(control);
3347f1183f2Sjoerg
3357f1183f2Sjoerg if (__HAS_SSE) {
3367f1183f2Sjoerg __stmxcsr(&mxcsr);
3377f1183f2Sjoerg mxcsr &= ~(__X87_ROUND_MASK << __SSE_ROUND_SHIFT);
3387f1183f2Sjoerg mxcsr |= round << __SSE_ROUND_SHIFT;
3397f1183f2Sjoerg __ldmxcsr(mxcsr);
3407f1183f2Sjoerg }
3417f1183f2Sjoerg
3427f1183f2Sjoerg /* Success */
3437f1183f2Sjoerg return (0);
3447f1183f2Sjoerg }
3457f1183f2Sjoerg
3467f1183f2Sjoerg /*
3477f1183f2Sjoerg * The fegetenv() function attempts to store the current floating-point
3487f1183f2Sjoerg * environment in the object pointed to by envp.
3497f1183f2Sjoerg */
3507f1183f2Sjoerg int
fegetenv(fenv_t * envp)3517f1183f2Sjoerg fegetenv(fenv_t *envp)
3527f1183f2Sjoerg {
3537f1183f2Sjoerg uint32_t mxcsr;
3547f1183f2Sjoerg
3557f1183f2Sjoerg _DIAGASSERT(flagp != NULL);
3567f1183f2Sjoerg
3577f1183f2Sjoerg /*
3587f1183f2Sjoerg * fnstenv masks all exceptions, so we need to restore the old control
3597f1183f2Sjoerg * word to avoid this side effect.
3607f1183f2Sjoerg */
3617f1183f2Sjoerg __fnstenv(envp);
3627f1183f2Sjoerg __fldcw(envp->x87.control);
3637f1183f2Sjoerg if (__HAS_SSE) {
3647f1183f2Sjoerg __stmxcsr(&mxcsr);
3657f1183f2Sjoerg envp->mxcsr = mxcsr;
3667f1183f2Sjoerg }
3677f1183f2Sjoerg
3687f1183f2Sjoerg /* Success */
3697f1183f2Sjoerg return (0);
3707f1183f2Sjoerg }
3717f1183f2Sjoerg
3727f1183f2Sjoerg /*
3737f1183f2Sjoerg * The feholdexcept() function saves the current floating-point environment in
3747f1183f2Sjoerg * the object pointed to by envp, clears the floating-point status flags, and
3757f1183f2Sjoerg * then installs a non-stop (continue on floating-point exceptions) mode, if
3767f1183f2Sjoerg * available, for all floating-point exceptions.
3777f1183f2Sjoerg */
3787f1183f2Sjoerg int
feholdexcept(fenv_t * envp)3797f1183f2Sjoerg feholdexcept(fenv_t *envp)
3807f1183f2Sjoerg {
3817f1183f2Sjoerg uint32_t mxcsr;
3827f1183f2Sjoerg
3837f1183f2Sjoerg _DIAGASSERT(envp != NULL);
3847f1183f2Sjoerg
3857f1183f2Sjoerg __fnstenv(envp);
3867f1183f2Sjoerg __fnclex();
3877f1183f2Sjoerg if (__HAS_SSE) {
3887f1183f2Sjoerg __stmxcsr(&mxcsr);
3897f1183f2Sjoerg envp->mxcsr = mxcsr;
3907f1183f2Sjoerg mxcsr &= ~FE_ALL_EXCEPT;
3917f1183f2Sjoerg mxcsr |= FE_ALL_EXCEPT << __SSE_EMASK_SHIFT;
3927f1183f2Sjoerg __ldmxcsr(mxcsr);
3937f1183f2Sjoerg }
3947f1183f2Sjoerg
3957f1183f2Sjoerg /* Success */
3967f1183f2Sjoerg return (0);
3977f1183f2Sjoerg }
3987f1183f2Sjoerg
3997f1183f2Sjoerg /*
4007f1183f2Sjoerg * The fesetenv() function attempts to establish the floating-point environment
4017f1183f2Sjoerg * represented by the object pointed to by envp. The argument `envp' points
4027f1183f2Sjoerg * to an object set by a call to fegetenv() or feholdexcept(), or equal a
4037f1183f2Sjoerg * floating-point environment macro. The fesetenv() function does not raise
4047f1183f2Sjoerg * floating-point exceptions, but only installs the state of the floating-point
4057f1183f2Sjoerg * status flags represented through its argument.
4067f1183f2Sjoerg */
4077f1183f2Sjoerg int
fesetenv(const fenv_t * envp)4087f1183f2Sjoerg fesetenv(const fenv_t *envp)
4097f1183f2Sjoerg {
4107f1183f2Sjoerg fenv_t env;
4117f1183f2Sjoerg
4127f1183f2Sjoerg _DIAGASSERT(envp != NULL);
4137f1183f2Sjoerg
4147f1183f2Sjoerg /* Store the x87 floating-point environment */
4157f1183f2Sjoerg memset(&env, 0, sizeof(env));
4167f1183f2Sjoerg __fnstenv(&env);
4177f1183f2Sjoerg
4187f1183f2Sjoerg __fe_dfl_env.x87.unused1 = env.x87.unused1;
4197f1183f2Sjoerg __fe_dfl_env.x87.unused2 = env.x87.unused2;
4207f1183f2Sjoerg __fe_dfl_env.x87.unused3 = env.x87.unused3;
421f9c7ee08Schristos memcpy(__fe_dfl_env.x87.others, env.x87.others,
422f9c7ee08Schristos sizeof(__fe_dfl_env.x87.others));
4237f1183f2Sjoerg
4247f1183f2Sjoerg __fldenv(envp->x87);
4257f1183f2Sjoerg if (__HAS_SSE)
4267f1183f2Sjoerg __ldmxcsr(envp->mxcsr);
4277f1183f2Sjoerg
4287f1183f2Sjoerg /* Success */
4297f1183f2Sjoerg return (0);
4307f1183f2Sjoerg }
4317f1183f2Sjoerg
4327f1183f2Sjoerg /*
4337f1183f2Sjoerg * The feupdateenv() function saves the currently raised floating-point
4347f1183f2Sjoerg * exceptions in its automatic storage, installs the floating-point environment
4357f1183f2Sjoerg * represented by the object pointed to by `envp', and then raises the saved
4367f1183f2Sjoerg * floating-point exceptions. The argument `envp' shall point to an object set
4377f1183f2Sjoerg * by a call to feholdexcept() or fegetenv(), or equal a floating-point
4387f1183f2Sjoerg * environment macro.
4397f1183f2Sjoerg */
4407f1183f2Sjoerg int
feupdateenv(const fenv_t * envp)4417f1183f2Sjoerg feupdateenv(const fenv_t *envp)
4427f1183f2Sjoerg {
4437f1183f2Sjoerg fenv_t env;
4447f1183f2Sjoerg uint32_t mxcsr;
4457f1183f2Sjoerg uint16_t status;
4467f1183f2Sjoerg
4477f1183f2Sjoerg _DIAGASSERT(envp != NULL);
4487f1183f2Sjoerg
4497f1183f2Sjoerg /* Store the x87 floating-point environment */
4507f1183f2Sjoerg memset(&env, 0, sizeof(env));
4517f1183f2Sjoerg __fnstenv(&env);
4527f1183f2Sjoerg
4537f1183f2Sjoerg __fe_dfl_env.x87.unused1 = env.x87.unused1;
4547f1183f2Sjoerg __fe_dfl_env.x87.unused2 = env.x87.unused2;
4557f1183f2Sjoerg __fe_dfl_env.x87.unused3 = env.x87.unused3;
456f9c7ee08Schristos memcpy(__fe_dfl_env.x87.others, env.x87.others,
457f9c7ee08Schristos sizeof(__fe_dfl_env.x87.others));
4587f1183f2Sjoerg
4597f1183f2Sjoerg __fnstsw(&status);
4607f1183f2Sjoerg if (__HAS_SSE)
4617f1183f2Sjoerg __stmxcsr(&mxcsr);
4627f1183f2Sjoerg else
4637f1183f2Sjoerg mxcsr = 0;
4647f1183f2Sjoerg fesetenv(envp);
4657f1183f2Sjoerg feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT);
4667f1183f2Sjoerg
4677f1183f2Sjoerg /* Success */
4687f1183f2Sjoerg return (0);
4697f1183f2Sjoerg }
4707f1183f2Sjoerg
4717f1183f2Sjoerg /*
472*f9faf20aSandvar * The following functions are extensions to the standard
4737f1183f2Sjoerg */
4747f1183f2Sjoerg int
feenableexcept(int mask)4757f1183f2Sjoerg feenableexcept(int mask)
4767f1183f2Sjoerg {
4777f1183f2Sjoerg uint32_t mxcsr, omask;
4787f1183f2Sjoerg uint16_t control;
4797f1183f2Sjoerg
4807f1183f2Sjoerg mask &= FE_ALL_EXCEPT;
4817f1183f2Sjoerg __fnstcw(&control);
4827f1183f2Sjoerg if (__HAS_SSE)
4837f1183f2Sjoerg __stmxcsr(&mxcsr);
4847f1183f2Sjoerg else
4857f1183f2Sjoerg mxcsr = 0;
4867f1183f2Sjoerg
4877f1183f2Sjoerg omask = (control | mxcsr >> __SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
4887f1183f2Sjoerg control &= ~mask;
4897f1183f2Sjoerg __fldcw(control);
4907f1183f2Sjoerg if (__HAS_SSE) {
4917f1183f2Sjoerg mxcsr &= ~(mask << __SSE_EMASK_SHIFT);
4927f1183f2Sjoerg __ldmxcsr(mxcsr);
4937f1183f2Sjoerg }
4947f1183f2Sjoerg
4953a4a1ac2Sriastradh return (FE_ALL_EXCEPT & ~omask);
4967f1183f2Sjoerg }
4977f1183f2Sjoerg
4987f1183f2Sjoerg int
fedisableexcept(int mask)4997f1183f2Sjoerg fedisableexcept(int mask)
5007f1183f2Sjoerg {
5017f1183f2Sjoerg uint32_t mxcsr, omask;
5027f1183f2Sjoerg uint16_t control;
5037f1183f2Sjoerg
5047f1183f2Sjoerg mask &= FE_ALL_EXCEPT;
5057f1183f2Sjoerg __fnstcw(&control);
5067f1183f2Sjoerg if (__HAS_SSE)
5077f1183f2Sjoerg __stmxcsr(&mxcsr);
5087f1183f2Sjoerg else
5097f1183f2Sjoerg mxcsr = 0;
5107f1183f2Sjoerg
5117f1183f2Sjoerg omask = (control | mxcsr >> __SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
5127f1183f2Sjoerg control |= mask;
5137f1183f2Sjoerg __fldcw(control);
5147f1183f2Sjoerg if (__HAS_SSE) {
5157f1183f2Sjoerg mxcsr |= mask << __SSE_EMASK_SHIFT;
5167f1183f2Sjoerg __ldmxcsr(mxcsr);
5177f1183f2Sjoerg }
5187f1183f2Sjoerg
5193a4a1ac2Sriastradh return (FE_ALL_EXCEPT & ~omask);
5207f1183f2Sjoerg }
5217f1183f2Sjoerg
5227f1183f2Sjoerg int
fegetexcept(void)5237f1183f2Sjoerg fegetexcept(void)
5247f1183f2Sjoerg {
5257f1183f2Sjoerg uint16_t control;
5267f1183f2Sjoerg
5277f1183f2Sjoerg /*
5287f1183f2Sjoerg * We assume that the masks for the x87 and the SSE unit are
5297f1183f2Sjoerg * the same.
5307f1183f2Sjoerg */
5317f1183f2Sjoerg __fnstcw(&control);
5327f1183f2Sjoerg
5330eddede8Sriastradh return (~control & FE_ALL_EXCEPT);
5347f1183f2Sjoerg }
535