xref: /netbsd-src/lib/libm/arch/i387/fenv.c (revision f9faf20aeffd68c90a9d1a577a17402726208619)
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