xref: /minix3/lib/libm/arch/i387/fenv.c (revision 84d9c625bfea59e274550651111ae9edfdc40fbd)
1*84d9c625SLionel Sambuc /* $NetBSD: fenv.c,v 1.6 2013/11/11 00:31:51 joerg Exp $ */
22fe8fb19SBen Gras 
32fe8fb19SBen Gras /*-
42fe8fb19SBen Gras  * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG>
52fe8fb19SBen Gras  * All rights reserved.
62fe8fb19SBen Gras  *
72fe8fb19SBen Gras  * Redistribution and use in source and binary forms, with or without
82fe8fb19SBen Gras  * modification, are permitted provided that the following conditions
92fe8fb19SBen Gras  * are met:
102fe8fb19SBen Gras  * 1. Redistributions of source code must retain the above copyright
112fe8fb19SBen Gras  *    notice, this list of conditions and the following disclaimer.
122fe8fb19SBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
132fe8fb19SBen Gras  *    notice, this list of conditions and the following disclaimer in the
142fe8fb19SBen Gras  *    documentation and/or other materials provided with the distribution.
152fe8fb19SBen Gras  *
162fe8fb19SBen Gras  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
172fe8fb19SBen Gras  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
182fe8fb19SBen Gras  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
192fe8fb19SBen Gras  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
202fe8fb19SBen Gras  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
212fe8fb19SBen Gras  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
222fe8fb19SBen Gras  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
232fe8fb19SBen Gras  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
242fe8fb19SBen Gras  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
252fe8fb19SBen Gras  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
262fe8fb19SBen Gras  * SUCH DAMAGE.
272fe8fb19SBen Gras  */
282fe8fb19SBen Gras 
292fe8fb19SBen Gras #include <sys/cdefs.h>
30*84d9c625SLionel Sambuc __RCSID("$NetBSD: fenv.c,v 1.6 2013/11/11 00:31:51 joerg Exp $");
312fe8fb19SBen Gras 
322fe8fb19SBen Gras #include <sys/param.h>
332fe8fb19SBen Gras #include <sys/sysctl.h>
342fe8fb19SBen Gras #include <assert.h>
352fe8fb19SBen Gras #include <fenv.h>
362fe8fb19SBen Gras #include <stddef.h>
372fe8fb19SBen Gras #include <string.h>
382fe8fb19SBen Gras 
392fe8fb19SBen Gras /* Load x87 Control Word */
402fe8fb19SBen Gras #define	__fldcw(__cw)		__asm__ __volatile__	\
412fe8fb19SBen Gras 	("fldcw %0" : : "m" (__cw))
422fe8fb19SBen Gras 
432fe8fb19SBen Gras /* No-Wait Store Control Word */
442fe8fb19SBen Gras #define	__fnstcw(__cw)		__asm__ __volatile__	\
452fe8fb19SBen Gras 	("fnstcw %0" : "=m" (*(__cw)))
462fe8fb19SBen Gras 
472fe8fb19SBen Gras /* No-Wait Store Status Word */
482fe8fb19SBen Gras #define	__fnstsw(__sw)		__asm__ __volatile__	\
492fe8fb19SBen Gras 	("fnstsw %0" : "=am" (*(__sw)))
502fe8fb19SBen Gras 
512fe8fb19SBen Gras /* No-Wait Clear Exception Flags */
522fe8fb19SBen Gras #define	__fnclex()		__asm__ __volatile__	\
532fe8fb19SBen Gras 	("fnclex")
542fe8fb19SBen Gras 
552fe8fb19SBen Gras /* Load x87 Environment */
562fe8fb19SBen Gras #define	__fldenv(__env)		__asm__ __volatile__	\
572fe8fb19SBen Gras 	("fldenv %0" : : "m" (__env))
582fe8fb19SBen Gras 
592fe8fb19SBen Gras /* No-Wait Store x87 environment */
602fe8fb19SBen Gras #define	__fnstenv(__env)	__asm__ __volatile__	\
612fe8fb19SBen Gras 	("fnstenv %0" : "=m" (*(__env)))
622fe8fb19SBen Gras 
632fe8fb19SBen Gras /* Check for and handle pending unmasked x87 pending FPU exceptions */
642fe8fb19SBen Gras #define	__fwait(__env)		__asm__	__volatile__	\
652fe8fb19SBen Gras 	("fwait")
662fe8fb19SBen Gras 
672fe8fb19SBen Gras /* Load the MXCSR register */
682fe8fb19SBen Gras #define	__ldmxcsr(__mxcsr)	__asm__ __volatile__	\
692fe8fb19SBen Gras 	("ldmxcsr %0" : : "m" (__mxcsr))
702fe8fb19SBen Gras 
712fe8fb19SBen Gras /* Store the MXCSR register state */
722fe8fb19SBen Gras #define	__stmxcsr(__mxcsr)	__asm__ __volatile__	\
732fe8fb19SBen Gras 	("stmxcsr %0" : "=m" (*(__mxcsr)))
742fe8fb19SBen Gras 
752fe8fb19SBen Gras /*
762fe8fb19SBen Gras  * The following constant represents the default floating-point environment
772fe8fb19SBen Gras  * (that is, the one installed at program startup) and has type pointer to
782fe8fb19SBen Gras  * const-qualified fenv_t.
792fe8fb19SBen Gras  *
802fe8fb19SBen Gras  * It can be used as an argument to the functions within the <fenv.h> header
812fe8fb19SBen Gras  * that manage the floating-point environment, namely fesetenv() and
822fe8fb19SBen Gras  * feupdateenv().
832fe8fb19SBen Gras  *
842fe8fb19SBen Gras  * x87 fpu registers are 16bit wide. The upper bits, 31-16, are marked as
852fe8fb19SBen Gras  * RESERVED. We provide a partial floating-point environment, where we
862fe8fb19SBen Gras  * define only the lower bits. The reserved bits are extracted and set by the
872fe8fb19SBen Gras  * consumers of FE_DFL_ENV, during runtime.
882fe8fb19SBen Gras  */
892fe8fb19SBen Gras fenv_t __fe_dfl_env = {
902fe8fb19SBen Gras 	{
912fe8fb19SBen Gras 		__NetBSD_NPXCW__,       /* Control word register */
922fe8fb19SBen Gras 		0x0,			/* Unused */
932fe8fb19SBen Gras 		0x0000,                 /* Status word register */
942fe8fb19SBen Gras 		0x0,			/* Unused */
952fe8fb19SBen Gras 		0x0000ffff,             /* Tag word register */
962fe8fb19SBen Gras 		0x0,			/* Unused */
972fe8fb19SBen Gras 		{
982fe8fb19SBen Gras 			0x0000, 0x0000,
992fe8fb19SBen Gras 			0x0000, 0xffff
1002fe8fb19SBen Gras 		}
1012fe8fb19SBen Gras 	},
1022fe8fb19SBen Gras 	__INITIAL_MXCSR__		/* MXCSR register */
1032fe8fb19SBen Gras };
1042fe8fb19SBen Gras 
1052fe8fb19SBen Gras /*
1062fe8fb19SBen Gras  * Test for SSE support on this processor.
1072fe8fb19SBen Gras  *
1082fe8fb19SBen Gras  * We need to use ldmxcsr/stmxcsr to get correct results if any part
1092fe8fb19SBen Gras  * of the program was compiled to use SSE floating-point, but we can't
1102fe8fb19SBen Gras  * use SSE on older processors.
1112fe8fb19SBen Gras  *
1122fe8fb19SBen Gras  * In order to do so, we need to query the processor capabilities via the CPUID
1132fe8fb19SBen Gras  * instruction. We can make it even simpler though, by querying the machdep.sse
1142fe8fb19SBen Gras  * sysctl.
1152fe8fb19SBen Gras  */
1162fe8fb19SBen Gras static int __HAS_SSE = 0;
1172fe8fb19SBen Gras 
118*84d9c625SLionel Sambuc static void __init_libm(void) __attribute__ ((constructor, used));
1192fe8fb19SBen Gras 
__init_libm(void)120*84d9c625SLionel Sambuc static void __init_libm(void)
1212fe8fb19SBen Gras {
122*84d9c625SLionel Sambuc #if !defined(__minix)
1232fe8fb19SBen Gras 	size_t oldlen = sizeof(__HAS_SSE);
1242fe8fb19SBen Gras 	int rv;
125*84d9c625SLionel Sambuc 	uint16_t control;
1262fe8fb19SBen Gras 
1272fe8fb19SBen Gras 	rv = sysctlbyname("machdep.sse", &__HAS_SSE, &oldlen, NULL, 0);
1282fe8fb19SBen Gras 	if (rv == -1)
1292fe8fb19SBen Gras 		__HAS_SSE = 0;
130*84d9c625SLionel Sambuc #else
131*84d9c625SLionel Sambuc 	uint16_t control;
132*84d9c625SLionel Sambuc 	__HAS_SSE = 0;
133*84d9c625SLionel Sambuc #endif /* !defined(__minix) */
134*84d9c625SLionel Sambuc 
135*84d9c625SLionel Sambuc 	__fnstcw(&control);
136*84d9c625SLionel Sambuc 	__fe_dfl_env.x87.control = control;
1372fe8fb19SBen Gras }
1382fe8fb19SBen Gras 
1392fe8fb19SBen Gras /*
1402fe8fb19SBen Gras  * The feclearexcept() function clears the supported floating-point exceptions
1412fe8fb19SBen Gras  * represented by `excepts'.
1422fe8fb19SBen Gras  */
1432fe8fb19SBen Gras int
feclearexcept(int excepts)1442fe8fb19SBen Gras feclearexcept(int excepts)
1452fe8fb19SBen Gras {
1462fe8fb19SBen Gras 	fenv_t env;
1472fe8fb19SBen Gras 	uint32_t mxcsr;
1482fe8fb19SBen Gras 	int ex;
1492fe8fb19SBen Gras 
1502fe8fb19SBen Gras 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
1512fe8fb19SBen Gras 
1522fe8fb19SBen Gras 	ex = excepts & FE_ALL_EXCEPT;
1532fe8fb19SBen Gras 
1542fe8fb19SBen Gras 	/* It's ~3x faster to call fnclex, than store/load fp env */
1552fe8fb19SBen Gras 	if (ex == FE_ALL_EXCEPT) {
1562fe8fb19SBen Gras 		__fnclex();
1572fe8fb19SBen Gras 	} else {
1582fe8fb19SBen Gras 		__fnstenv(&env);
1592fe8fb19SBen Gras 		env.x87.status &= ~ex;
1602fe8fb19SBen Gras 		__fldenv(env);
1612fe8fb19SBen Gras 	}
1622fe8fb19SBen Gras 
1632fe8fb19SBen Gras 	if (__HAS_SSE) {
1642fe8fb19SBen Gras 		__stmxcsr(&mxcsr);
1652fe8fb19SBen Gras 		mxcsr &= ~ex;
1662fe8fb19SBen Gras 		__ldmxcsr(mxcsr);
1672fe8fb19SBen Gras 	}
1682fe8fb19SBen Gras 
1692fe8fb19SBen Gras 	/* Success */
1702fe8fb19SBen Gras 	return (0);
1712fe8fb19SBen Gras }
1722fe8fb19SBen Gras 
1732fe8fb19SBen Gras /*
1742fe8fb19SBen Gras  * The fegetexceptflag() function stores an implementation-defined
1752fe8fb19SBen Gras  * representation of the states of the floating-point status flags indicated by
1762fe8fb19SBen Gras  * the argument excepts in the object pointed to by the argument flagp.
1772fe8fb19SBen Gras  */
1782fe8fb19SBen Gras int
fegetexceptflag(fexcept_t * flagp,int excepts)1792fe8fb19SBen Gras fegetexceptflag(fexcept_t *flagp, int excepts)
1802fe8fb19SBen Gras {
1812fe8fb19SBen Gras 	uint32_t mxcsr;
1822fe8fb19SBen Gras 	uint16_t status;
1832fe8fb19SBen Gras 	int ex;
1842fe8fb19SBen Gras 
1852fe8fb19SBen Gras 	_DIAGASSERT(flagp != NULL);
1862fe8fb19SBen Gras 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
1872fe8fb19SBen Gras 
1882fe8fb19SBen Gras 	ex = excepts & FE_ALL_EXCEPT;
1892fe8fb19SBen Gras 
1902fe8fb19SBen Gras 	__fnstsw(&status);
1912fe8fb19SBen Gras 	if (__HAS_SSE)
1922fe8fb19SBen Gras 		__stmxcsr(&mxcsr);
1932fe8fb19SBen Gras 	else
1942fe8fb19SBen Gras 		mxcsr = 0;
1952fe8fb19SBen Gras 
1962fe8fb19SBen Gras 	*flagp = (mxcsr | status) & ex;
1972fe8fb19SBen Gras 
1982fe8fb19SBen Gras 	/* Success */
1992fe8fb19SBen Gras 	return (0);
2002fe8fb19SBen Gras }
2012fe8fb19SBen Gras 
2022fe8fb19SBen Gras /*
2032fe8fb19SBen Gras  * The feraiseexcept() function raises the supported floating-point exceptions
2042fe8fb19SBen Gras  * represented by the argument `excepts'.
2052fe8fb19SBen Gras  *
2062fe8fb19SBen Gras  * The standard explicitly allows us to execute an instruction that has the
2072fe8fb19SBen Gras  * exception as a side effect, but we choose to manipulate the status register
2082fe8fb19SBen Gras  * directly.
2092fe8fb19SBen Gras  *
2102fe8fb19SBen Gras  * The validation of input is being deferred to fesetexceptflag().
2112fe8fb19SBen Gras  */
2122fe8fb19SBen Gras int
feraiseexcept(int excepts)2132fe8fb19SBen Gras feraiseexcept(int excepts)
2142fe8fb19SBen Gras {
2152fe8fb19SBen Gras 	fexcept_t ex;
2162fe8fb19SBen Gras 
2172fe8fb19SBen Gras 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
2182fe8fb19SBen Gras 
2192fe8fb19SBen Gras 	ex = excepts & FE_ALL_EXCEPT;
2202fe8fb19SBen Gras 	fesetexceptflag(&ex, excepts);
2212fe8fb19SBen Gras 	__fwait();
2222fe8fb19SBen Gras 
2232fe8fb19SBen Gras 	/* Success */
2242fe8fb19SBen Gras 	return (0);
2252fe8fb19SBen Gras }
2262fe8fb19SBen Gras 
2272fe8fb19SBen Gras /*
2282fe8fb19SBen Gras  * This function sets the floating-point status flags indicated by the argument
2292fe8fb19SBen Gras  * `excepts' to the states stored in the object pointed to by `flagp'. It does
2302fe8fb19SBen Gras  * NOT raise any floating-point exceptions, but only sets the state of the flags.
2312fe8fb19SBen Gras  */
2322fe8fb19SBen Gras int
fesetexceptflag(const fexcept_t * flagp,int excepts)2332fe8fb19SBen Gras fesetexceptflag(const fexcept_t *flagp, int excepts)
2342fe8fb19SBen Gras {
2352fe8fb19SBen Gras 	fenv_t env;
2362fe8fb19SBen Gras 	uint32_t mxcsr;
2372fe8fb19SBen Gras 	int ex;
2382fe8fb19SBen Gras 
2392fe8fb19SBen Gras 	_DIAGASSERT(flagp != NULL);
2402fe8fb19SBen Gras 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
2412fe8fb19SBen Gras 
2422fe8fb19SBen Gras 	ex = excepts & FE_ALL_EXCEPT;
2432fe8fb19SBen Gras 
2442fe8fb19SBen Gras 	__fnstenv(&env);
2452fe8fb19SBen Gras 	env.x87.status &= ~ex;
2462fe8fb19SBen Gras 	env.x87.status |= *flagp & ex;
2472fe8fb19SBen Gras 	__fldenv(env);
2482fe8fb19SBen Gras 
2492fe8fb19SBen Gras 	if (__HAS_SSE) {
2502fe8fb19SBen Gras 		__stmxcsr(&mxcsr);
2512fe8fb19SBen Gras 		mxcsr &= ~ex;
2522fe8fb19SBen Gras 		mxcsr |= *flagp & ex;
2532fe8fb19SBen Gras 		__ldmxcsr(mxcsr);
2542fe8fb19SBen Gras 	}
2552fe8fb19SBen Gras 
2562fe8fb19SBen Gras 	/* Success */
2572fe8fb19SBen Gras 	return (0);
2582fe8fb19SBen Gras }
2592fe8fb19SBen Gras 
2602fe8fb19SBen Gras /*
2612fe8fb19SBen Gras  * The fetestexcept() function determines which of a specified subset of the
2622fe8fb19SBen Gras  * floating-point exception flags are currently set. The `excepts' argument
2632fe8fb19SBen Gras  * specifies the floating-point status flags to be queried.
2642fe8fb19SBen Gras  */
2652fe8fb19SBen Gras int
fetestexcept(int excepts)2662fe8fb19SBen Gras fetestexcept(int excepts)
2672fe8fb19SBen Gras {
2682fe8fb19SBen Gras 	uint32_t mxcsr;
2692fe8fb19SBen Gras 	uint16_t status;
2702fe8fb19SBen Gras 	int ex;
2712fe8fb19SBen Gras 
2722fe8fb19SBen Gras 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
2732fe8fb19SBen Gras 
2742fe8fb19SBen Gras 	ex = excepts & FE_ALL_EXCEPT;
2752fe8fb19SBen Gras 
2762fe8fb19SBen Gras 	__fnstsw(&status);
2772fe8fb19SBen Gras 	if (__HAS_SSE)
2782fe8fb19SBen Gras 		__stmxcsr(&mxcsr);
2792fe8fb19SBen Gras 	else
2802fe8fb19SBen Gras 		mxcsr = 0;
2812fe8fb19SBen Gras 
2822fe8fb19SBen Gras 	return ((status | mxcsr) & ex);
2832fe8fb19SBen Gras }
2842fe8fb19SBen Gras 
2852fe8fb19SBen Gras int
fegetround(void)2862fe8fb19SBen Gras fegetround(void)
2872fe8fb19SBen Gras {
2882fe8fb19SBen Gras 	uint16_t control;
2892fe8fb19SBen Gras 
2902fe8fb19SBen Gras 	/*
2912fe8fb19SBen Gras 	 * We assume that the x87 and the SSE unit agree on the
2922fe8fb19SBen Gras 	 * rounding mode.  Reading the control word on the x87 turns
2932fe8fb19SBen Gras 	 * out to be about 5 times faster than reading it on the SSE
2942fe8fb19SBen Gras 	 * unit on an Opteron 244.
2952fe8fb19SBen Gras 	 */
2962fe8fb19SBen Gras 	__fnstcw(&control);
2972fe8fb19SBen Gras 
2982fe8fb19SBen Gras 	return (control & __X87_ROUND_MASK);
2992fe8fb19SBen Gras }
3002fe8fb19SBen Gras 
3012fe8fb19SBen Gras /*
3022fe8fb19SBen Gras  * The fesetround() function shall establish the rounding direction represented
3032fe8fb19SBen Gras  * by its argument round. If the argument is not equal to the value of a
3042fe8fb19SBen Gras  * rounding direction macro, the rounding direction is not changed.
3052fe8fb19SBen Gras  */
3062fe8fb19SBen Gras int
fesetround(int round)3072fe8fb19SBen Gras fesetround(int round)
3082fe8fb19SBen Gras {
3092fe8fb19SBen Gras 	uint32_t mxcsr;
3102fe8fb19SBen Gras 	uint16_t control;
3112fe8fb19SBen Gras 
3122fe8fb19SBen Gras 	if (round & ~__X87_ROUND_MASK) {
3132fe8fb19SBen Gras 		/* Failure */
3142fe8fb19SBen Gras 		return (-1);
3152fe8fb19SBen Gras 	}
3162fe8fb19SBen Gras 
3172fe8fb19SBen Gras 	__fnstcw(&control);
3182fe8fb19SBen Gras 	control &= ~__X87_ROUND_MASK;
3192fe8fb19SBen Gras 	control |= round;
3202fe8fb19SBen Gras 	__fldcw(control);
3212fe8fb19SBen Gras 
3222fe8fb19SBen Gras 	if (__HAS_SSE) {
3232fe8fb19SBen Gras 		__stmxcsr(&mxcsr);
3242fe8fb19SBen Gras 		mxcsr &= ~(__X87_ROUND_MASK << __SSE_ROUND_SHIFT);
3252fe8fb19SBen Gras 		mxcsr |= round << __SSE_ROUND_SHIFT;
3262fe8fb19SBen Gras 		__ldmxcsr(mxcsr);
3272fe8fb19SBen Gras 	}
3282fe8fb19SBen Gras 
3292fe8fb19SBen Gras 	/* Success */
3302fe8fb19SBen Gras 	return (0);
3312fe8fb19SBen Gras }
3322fe8fb19SBen Gras 
3332fe8fb19SBen Gras /*
3342fe8fb19SBen Gras  * The fegetenv() function attempts to store the current floating-point
3352fe8fb19SBen Gras  * environment in the object pointed to by envp.
3362fe8fb19SBen Gras  */
3372fe8fb19SBen Gras int
fegetenv(fenv_t * envp)3382fe8fb19SBen Gras fegetenv(fenv_t *envp)
3392fe8fb19SBen Gras {
3402fe8fb19SBen Gras 	uint32_t mxcsr;
3412fe8fb19SBen Gras 
3422fe8fb19SBen Gras 	_DIAGASSERT(flagp != NULL);
3432fe8fb19SBen Gras 
3442fe8fb19SBen Gras 	/*
3452fe8fb19SBen Gras 	 * fnstenv masks all exceptions, so we need to restore the old control
3462fe8fb19SBen Gras 	 * word to avoid this side effect.
3472fe8fb19SBen Gras 	 */
3482fe8fb19SBen Gras 	__fnstenv(envp);
3492fe8fb19SBen Gras 	__fldcw(envp->x87.control);
3502fe8fb19SBen Gras 	if (__HAS_SSE) {
3512fe8fb19SBen Gras 		__stmxcsr(&mxcsr);
3522fe8fb19SBen Gras 		envp->mxcsr = mxcsr;
3532fe8fb19SBen Gras 	}
3542fe8fb19SBen Gras 
3552fe8fb19SBen Gras 	/* Success */
3562fe8fb19SBen Gras 	return (0);
3572fe8fb19SBen Gras }
3582fe8fb19SBen Gras 
3592fe8fb19SBen Gras /*
3602fe8fb19SBen Gras  * The feholdexcept() function saves the current floating-point environment in
3612fe8fb19SBen Gras  * the object pointed to by envp, clears the floating-point status flags, and
3622fe8fb19SBen Gras  * then installs a non-stop (continue on floating-point exceptions) mode, if
3632fe8fb19SBen Gras  * available, for all floating-point exceptions.
3642fe8fb19SBen Gras  */
3652fe8fb19SBen Gras int
feholdexcept(fenv_t * envp)3662fe8fb19SBen Gras feholdexcept(fenv_t *envp)
3672fe8fb19SBen Gras {
3682fe8fb19SBen Gras 	uint32_t mxcsr;
3692fe8fb19SBen Gras 
3702fe8fb19SBen Gras 	_DIAGASSERT(envp != NULL);
3712fe8fb19SBen Gras 
3722fe8fb19SBen Gras 	__fnstenv(envp);
3732fe8fb19SBen Gras 	__fnclex();
3742fe8fb19SBen Gras 	if (__HAS_SSE) {
3752fe8fb19SBen Gras 		__stmxcsr(&mxcsr);
3762fe8fb19SBen Gras 		envp->mxcsr = mxcsr;
3772fe8fb19SBen Gras 		mxcsr &= ~FE_ALL_EXCEPT;
3782fe8fb19SBen Gras 		mxcsr |= FE_ALL_EXCEPT << __SSE_EMASK_SHIFT;
3792fe8fb19SBen Gras 		__ldmxcsr(mxcsr);
3802fe8fb19SBen Gras 	}
3812fe8fb19SBen Gras 
3822fe8fb19SBen Gras 	/* Success */
3832fe8fb19SBen Gras 	return (0);
3842fe8fb19SBen Gras }
3852fe8fb19SBen Gras 
3862fe8fb19SBen Gras /*
3872fe8fb19SBen Gras  * The fesetenv() function attempts to establish the floating-point environment
3882fe8fb19SBen Gras  * represented by the object pointed to by envp. The argument `envp' points
3892fe8fb19SBen Gras  * to an object set by a call to fegetenv() or feholdexcept(), or equal a
3902fe8fb19SBen Gras  * floating-point environment macro. The fesetenv() function does not raise
3912fe8fb19SBen Gras  * floating-point exceptions, but only installs the state of the floating-point
3922fe8fb19SBen Gras  * status flags represented through its argument.
3932fe8fb19SBen Gras  */
3942fe8fb19SBen Gras int
fesetenv(const fenv_t * envp)3952fe8fb19SBen Gras fesetenv(const fenv_t *envp)
3962fe8fb19SBen Gras {
3972fe8fb19SBen Gras 	fenv_t env;
3982fe8fb19SBen Gras 
3992fe8fb19SBen Gras 	_DIAGASSERT(envp != NULL);
4002fe8fb19SBen Gras 
4012fe8fb19SBen Gras 	/* Store the x87 floating-point environment */
4022fe8fb19SBen Gras 	memset(&env, 0, sizeof(env));
4032fe8fb19SBen Gras 	__fnstenv(&env);
4042fe8fb19SBen Gras 
4052fe8fb19SBen Gras 	__fe_dfl_env.x87.unused1 = env.x87.unused1;
4062fe8fb19SBen Gras 	__fe_dfl_env.x87.unused2 = env.x87.unused2;
4072fe8fb19SBen Gras 	__fe_dfl_env.x87.unused3 = env.x87.unused3;
4082fe8fb19SBen Gras 	memcpy(__fe_dfl_env.x87.others,
4092fe8fb19SBen Gras 	       env.x87.others,
4102fe8fb19SBen Gras 	       sizeof(__fe_dfl_env.x87.others) / sizeof(uint32_t));
4112fe8fb19SBen Gras 
4122fe8fb19SBen Gras 	__fldenv(envp->x87);
4132fe8fb19SBen Gras 	if (__HAS_SSE)
4142fe8fb19SBen Gras 		__ldmxcsr(envp->mxcsr);
4152fe8fb19SBen Gras 
4162fe8fb19SBen Gras 	/* Success */
4172fe8fb19SBen Gras 	return (0);
4182fe8fb19SBen Gras }
4192fe8fb19SBen Gras 
4202fe8fb19SBen Gras /*
4212fe8fb19SBen Gras  * The feupdateenv() function saves the currently raised floating-point
4222fe8fb19SBen Gras  * exceptions in its automatic storage, installs the floating-point environment
4232fe8fb19SBen Gras  * represented by the object pointed to by `envp', and then raises the saved
4242fe8fb19SBen Gras  * floating-point exceptions. The argument `envp' shall point to an object set
4252fe8fb19SBen Gras  * by a call to feholdexcept() or fegetenv(), or equal a floating-point
4262fe8fb19SBen Gras  * environment macro.
4272fe8fb19SBen Gras  */
4282fe8fb19SBen Gras int
feupdateenv(const fenv_t * envp)4292fe8fb19SBen Gras feupdateenv(const fenv_t *envp)
4302fe8fb19SBen Gras {
4312fe8fb19SBen Gras 	fenv_t env;
4322fe8fb19SBen Gras 	uint32_t mxcsr;
4332fe8fb19SBen Gras 	uint16_t status;
4342fe8fb19SBen Gras 
4352fe8fb19SBen Gras 	_DIAGASSERT(envp != NULL);
4362fe8fb19SBen Gras 
4372fe8fb19SBen Gras 	/* Store the x87 floating-point environment */
4382fe8fb19SBen Gras 	memset(&env, 0, sizeof(env));
4392fe8fb19SBen Gras 	__fnstenv(&env);
4402fe8fb19SBen Gras 
4412fe8fb19SBen Gras 	__fe_dfl_env.x87.unused1 = env.x87.unused1;
4422fe8fb19SBen Gras 	__fe_dfl_env.x87.unused2 = env.x87.unused2;
4432fe8fb19SBen Gras 	__fe_dfl_env.x87.unused3 = env.x87.unused3;
4442fe8fb19SBen Gras 	memcpy(__fe_dfl_env.x87.others,
4452fe8fb19SBen Gras 	       env.x87.others,
4462fe8fb19SBen Gras 	       sizeof(__fe_dfl_env.x87.others) / sizeof(uint32_t));
4472fe8fb19SBen Gras 
4482fe8fb19SBen Gras 	__fnstsw(&status);
4492fe8fb19SBen Gras 	if (__HAS_SSE)
4502fe8fb19SBen Gras 		__stmxcsr(&mxcsr);
4512fe8fb19SBen Gras 	else
4522fe8fb19SBen Gras 		mxcsr = 0;
4532fe8fb19SBen Gras 	fesetenv(envp);
4542fe8fb19SBen Gras 	feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT);
4552fe8fb19SBen Gras 
4562fe8fb19SBen Gras 	/* Success */
4572fe8fb19SBen Gras 	return (0);
4582fe8fb19SBen Gras }
4592fe8fb19SBen Gras 
4602fe8fb19SBen Gras /*
4612fe8fb19SBen Gras  * The following functions are extentions to the standard
4622fe8fb19SBen Gras  */
4632fe8fb19SBen Gras int
feenableexcept(int mask)4642fe8fb19SBen Gras feenableexcept(int mask)
4652fe8fb19SBen Gras {
4662fe8fb19SBen Gras 	uint32_t mxcsr, omask;
4672fe8fb19SBen Gras 	uint16_t control;
4682fe8fb19SBen Gras 
4692fe8fb19SBen Gras 	mask &= FE_ALL_EXCEPT;
4702fe8fb19SBen Gras 	__fnstcw(&control);
4712fe8fb19SBen Gras 	if (__HAS_SSE)
4722fe8fb19SBen Gras 		__stmxcsr(&mxcsr);
4732fe8fb19SBen Gras 	else
4742fe8fb19SBen Gras 		mxcsr = 0;
4752fe8fb19SBen Gras 
4762fe8fb19SBen Gras 	omask = (control | mxcsr >> __SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
4772fe8fb19SBen Gras 	control &= ~mask;
4782fe8fb19SBen Gras 	__fldcw(control);
4792fe8fb19SBen Gras 	if (__HAS_SSE) {
4802fe8fb19SBen Gras 		mxcsr &= ~(mask << __SSE_EMASK_SHIFT);
4812fe8fb19SBen Gras 		__ldmxcsr(mxcsr);
4822fe8fb19SBen Gras 	}
4832fe8fb19SBen Gras 
484*84d9c625SLionel Sambuc 	return (FE_ALL_EXCEPT & ~omask);
4852fe8fb19SBen Gras }
4862fe8fb19SBen Gras 
4872fe8fb19SBen Gras int
fedisableexcept(int mask)4882fe8fb19SBen Gras fedisableexcept(int mask)
4892fe8fb19SBen Gras {
4902fe8fb19SBen Gras 	uint32_t mxcsr, omask;
4912fe8fb19SBen Gras 	uint16_t control;
4922fe8fb19SBen Gras 
4932fe8fb19SBen Gras 	mask &= FE_ALL_EXCEPT;
4942fe8fb19SBen Gras 	__fnstcw(&control);
4952fe8fb19SBen Gras 	if (__HAS_SSE)
4962fe8fb19SBen Gras 		__stmxcsr(&mxcsr);
4972fe8fb19SBen Gras 	else
4982fe8fb19SBen Gras 		mxcsr = 0;
4992fe8fb19SBen Gras 
5002fe8fb19SBen Gras 	omask = (control | mxcsr >> __SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
5012fe8fb19SBen Gras 	control |= mask;
5022fe8fb19SBen Gras 	__fldcw(control);
5032fe8fb19SBen Gras 	if (__HAS_SSE) {
5042fe8fb19SBen Gras 		mxcsr |= mask << __SSE_EMASK_SHIFT;
5052fe8fb19SBen Gras 		__ldmxcsr(mxcsr);
5062fe8fb19SBen Gras 	}
5072fe8fb19SBen Gras 
508*84d9c625SLionel Sambuc 	return (FE_ALL_EXCEPT & ~omask);
5092fe8fb19SBen Gras }
5102fe8fb19SBen Gras 
5112fe8fb19SBen Gras int
fegetexcept(void)5122fe8fb19SBen Gras fegetexcept(void)
5132fe8fb19SBen Gras {
5142fe8fb19SBen Gras 	uint16_t control;
5152fe8fb19SBen Gras 
5162fe8fb19SBen Gras 	/*
5172fe8fb19SBen Gras 	 * We assume that the masks for the x87 and the SSE unit are
5182fe8fb19SBen Gras 	 * the same.
5192fe8fb19SBen Gras 	 */
5202fe8fb19SBen Gras 	__fnstcw(&control);
5212fe8fb19SBen Gras 
522*84d9c625SLionel Sambuc 	return (~control & FE_ALL_EXCEPT);
5232fe8fb19SBen Gras }
524