xref: /minix3/lib/libm/arch/x86_64/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 (at) 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 <assert.h>
332fe8fb19SBen Gras #include <fenv.h>
342fe8fb19SBen Gras #include <stddef.h>
352fe8fb19SBen Gras #include <string.h>
362fe8fb19SBen Gras 
372fe8fb19SBen Gras /* Load x87 Control Word */
382fe8fb19SBen Gras #define	__fldcw(__cw)		__asm__ __volatile__ \
392fe8fb19SBen Gras 	("fldcw %0" : : "m" (__cw))
402fe8fb19SBen Gras 
412fe8fb19SBen Gras /* No-Wait Store Control Word */
422fe8fb19SBen Gras #define	__fnstcw(__cw)		__asm__ __volatile__ \
432fe8fb19SBen Gras 	("fnstcw %0" : "=m" (*(__cw)))
442fe8fb19SBen Gras 
452fe8fb19SBen Gras /* No-Wait Store Status Word */
462fe8fb19SBen Gras #define	__fnstsw(__sw)		__asm__ __volatile__ \
472fe8fb19SBen Gras 	("fnstsw %0" : "=am" (*(__sw)))
482fe8fb19SBen Gras 
492fe8fb19SBen Gras /* No-Wait Clear Exception Flags */
502fe8fb19SBen Gras #define	__fnclex()		__asm__ __volatile__ \
512fe8fb19SBen Gras 	("fnclex")
522fe8fb19SBen Gras 
532fe8fb19SBen Gras /* Load x87 Environment */
542fe8fb19SBen Gras #define	__fldenv(__env)		__asm__ __volatile__ \
552fe8fb19SBen Gras 	("fldenv %0" : : "m" (__env))
562fe8fb19SBen Gras 
572fe8fb19SBen Gras /* No-Wait Store x87 environment */
582fe8fb19SBen Gras #define	__fnstenv(__env)	__asm__ __volatile__ \
592fe8fb19SBen Gras 	("fnstenv %0" : "=m" (*(__env)))
602fe8fb19SBen Gras 
61*84d9c625SLionel Sambuc /* Check for and handle pending unmasked x87 pending FPU exceptions */
62*84d9c625SLionel Sambuc #define	__fwait(__env)		__asm__	__volatile__	\
63*84d9c625SLionel Sambuc 	("fwait")
64*84d9c625SLionel Sambuc 
652fe8fb19SBen Gras /* Load the MXCSR register */
662fe8fb19SBen Gras #define	__ldmxcsr(__mxcsr)	__asm__ __volatile__ \
672fe8fb19SBen Gras 	("ldmxcsr %0" : : "m" (__mxcsr))
682fe8fb19SBen Gras 
692fe8fb19SBen Gras /* Store the MXCSR register state */
702fe8fb19SBen Gras #define	__stmxcsr(__mxcsr)	__asm__ __volatile__ \
712fe8fb19SBen Gras 	("stmxcsr %0" : "=m" (*(__mxcsr)))
722fe8fb19SBen Gras 
732fe8fb19SBen Gras /*
742fe8fb19SBen Gras  * The following constant represents the default floating-point environment
752fe8fb19SBen Gras  * (that is, the one installed at program startup) and has type pointer to
762fe8fb19SBen Gras  * const-qualified fenv_t.
772fe8fb19SBen Gras  *
782fe8fb19SBen Gras  * It can be used as an argument to the functions within the <fenv.h> header
792fe8fb19SBen Gras  * that manage the floating-point environment, namely fesetenv() and
802fe8fb19SBen Gras  * feupdateenv().
812fe8fb19SBen Gras  *
822fe8fb19SBen Gras  * x87 fpu registers are 16bit wide. The upper bits, 31-16, are marked as
832fe8fb19SBen Gras  * RESERVED. We provide a partial floating-point environment, where we
842fe8fb19SBen Gras  * define only the lower bits. The reserved bits are extracted and set by
852fe8fb19SBen Gras  * the consumers of FE_DFL_ENV, during runtime.
862fe8fb19SBen Gras  */
872fe8fb19SBen Gras fenv_t __fe_dfl_env = {
882fe8fb19SBen Gras 	{
892fe8fb19SBen Gras 		__NetBSD_NPXCW__,	/* Control word register */
902fe8fb19SBen Gras 		0x00000000,		/* Status word register */
912fe8fb19SBen Gras 		0x0000ffff,		/* Tag word register */
922fe8fb19SBen Gras 		{
932fe8fb19SBen Gras 			0x00000000,
942fe8fb19SBen Gras 			0x00000000,
952fe8fb19SBen Gras 			0x00000000,
962fe8fb19SBen Gras 			0x00000000,
972fe8fb19SBen Gras 		},
982fe8fb19SBen Gras 	},
992fe8fb19SBen Gras 	__INITIAL_MXCSR__       /* MXCSR register */
1002fe8fb19SBen Gras };
1012fe8fb19SBen Gras #define FE_DFL_ENV      ((const fenv_t *) &__fe_dfl_env)
1022fe8fb19SBen Gras 
103*84d9c625SLionel Sambuc static void __init_libm(void) __attribute__ ((constructor, used));
104*84d9c625SLionel Sambuc 
__init_libm(void)105*84d9c625SLionel Sambuc static void __init_libm(void)
106*84d9c625SLionel Sambuc {
107*84d9c625SLionel Sambuc 	uint16_t control;
108*84d9c625SLionel Sambuc 
109*84d9c625SLionel Sambuc 	__fnstcw(&control);
110*84d9c625SLionel Sambuc 	__fe_dfl_env.x87.control = control;
111*84d9c625SLionel Sambuc }
112*84d9c625SLionel Sambuc 
1132fe8fb19SBen Gras 
1142fe8fb19SBen Gras /*
1152fe8fb19SBen Gras  * The feclearexcept() function clears the supported floating-point exceptions
1162fe8fb19SBen Gras  * represented by `excepts'.
1172fe8fb19SBen Gras  */
1182fe8fb19SBen Gras int
feclearexcept(int excepts)1192fe8fb19SBen Gras feclearexcept(int excepts)
1202fe8fb19SBen Gras {
1212fe8fb19SBen Gras 	fenv_t fenv;
1222fe8fb19SBen Gras 	int ex;
1232fe8fb19SBen Gras 
1242fe8fb19SBen Gras 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
1252fe8fb19SBen Gras 
1262fe8fb19SBen Gras 	ex = excepts & FE_ALL_EXCEPT;
1272fe8fb19SBen Gras 
1282fe8fb19SBen Gras 	/* Store the current x87 floating-point environment */
1292fe8fb19SBen Gras 	__fnstenv(&fenv);
1302fe8fb19SBen Gras 
1312fe8fb19SBen Gras 	/* Clear the requested floating-point exceptions */
1322fe8fb19SBen Gras 	fenv.x87.status &= ~ex;
1332fe8fb19SBen Gras 
1342fe8fb19SBen Gras 	/* Load the x87 floating-point environent */
1352fe8fb19SBen Gras 	__fldenv(fenv);
1362fe8fb19SBen Gras 
1372fe8fb19SBen Gras 	/* Same for SSE environment */
1382fe8fb19SBen Gras 	__stmxcsr(&fenv.mxcsr);
1392fe8fb19SBen Gras 	fenv.mxcsr &= ~ex;
1402fe8fb19SBen Gras 	__ldmxcsr(fenv.mxcsr);
1412fe8fb19SBen Gras 
1422fe8fb19SBen Gras 	/* Success */
1432fe8fb19SBen Gras 	return (0);
1442fe8fb19SBen Gras }
1452fe8fb19SBen Gras 
1462fe8fb19SBen Gras /*
1472fe8fb19SBen Gras  * The fegetexceptflag() function stores an implementation-defined
1482fe8fb19SBen Gras  * representation of the states of the floating-point status flags indicated by
1492fe8fb19SBen Gras  * the argument excepts in the object pointed to by the argument flagp.
1502fe8fb19SBen Gras  */
1512fe8fb19SBen Gras int
fegetexceptflag(fexcept_t * flagp,int excepts)1522fe8fb19SBen Gras fegetexceptflag(fexcept_t *flagp, int excepts)
1532fe8fb19SBen Gras {
1542fe8fb19SBen Gras 	uint32_t mxcsr;
1552fe8fb19SBen Gras 	uint16_t x87_status;
1562fe8fb19SBen Gras 	int ex;
1572fe8fb19SBen Gras 
1582fe8fb19SBen Gras 	_DIAGASSERT(flagp != NULL);
1592fe8fb19SBen Gras 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
1602fe8fb19SBen Gras 
1612fe8fb19SBen Gras 	ex = excepts & FE_ALL_EXCEPT;
1622fe8fb19SBen Gras 
1632fe8fb19SBen Gras 	/* Store the current x87 status register */
1642fe8fb19SBen Gras 	__fnstsw(&x87_status);
1652fe8fb19SBen Gras 
1662fe8fb19SBen Gras 	/* Store the MXCSR register */
1672fe8fb19SBen Gras 	__stmxcsr(&mxcsr);
1682fe8fb19SBen Gras 
1692fe8fb19SBen Gras 	/* Store the results in flagp */
1702fe8fb19SBen Gras 	*flagp = (x87_status | mxcsr) & ex;
1712fe8fb19SBen Gras 
1722fe8fb19SBen Gras 	/* Success */
1732fe8fb19SBen Gras 	return (0);
1742fe8fb19SBen Gras }
1752fe8fb19SBen Gras 
1762fe8fb19SBen Gras /*
1772fe8fb19SBen Gras  * The feraiseexcept() function raises the supported floating-point exceptions
1782fe8fb19SBen Gras  * represented by the argument `excepts'.
1792fe8fb19SBen Gras  *
1802fe8fb19SBen Gras  * The standard explicitly allows us to execute an instruction that has the
1812fe8fb19SBen Gras  * exception as a side effect, but we choose to manipulate the status register
1822fe8fb19SBen Gras  * directly.
1832fe8fb19SBen Gras  *
1842fe8fb19SBen Gras  * The validation of input is being deferred to fesetexceptflag().
1852fe8fb19SBen Gras  */
1862fe8fb19SBen Gras int
feraiseexcept(int excepts)1872fe8fb19SBen Gras feraiseexcept(int excepts)
1882fe8fb19SBen Gras {
1892fe8fb19SBen Gras 	int ex;
1902fe8fb19SBen Gras 
1912fe8fb19SBen Gras 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
1922fe8fb19SBen Gras 
1932fe8fb19SBen Gras 	ex = excepts & FE_ALL_EXCEPT;
194*84d9c625SLionel Sambuc 	fesetexceptflag((unsigned int *)&ex, ex);
195*84d9c625SLionel Sambuc 	__fwait();
1962fe8fb19SBen Gras 
1972fe8fb19SBen Gras 	/* Success */
1982fe8fb19SBen Gras 	return (0);
1992fe8fb19SBen Gras }
2002fe8fb19SBen Gras 
2012fe8fb19SBen Gras /*
2022fe8fb19SBen Gras  * This function sets the floating-point status flags indicated by the argument
2032fe8fb19SBen Gras  * `excepts' to the states stored in the object pointed to by `flagp'. It does
2042fe8fb19SBen Gras  * NOT raise any floating-point exceptions, but only sets the state of the flags.
2052fe8fb19SBen Gras  */
2062fe8fb19SBen Gras int
fesetexceptflag(const fexcept_t * flagp,int excepts)2072fe8fb19SBen Gras fesetexceptflag(const fexcept_t *flagp, int excepts)
2082fe8fb19SBen Gras {
2092fe8fb19SBen Gras 	fenv_t fenv;
2102fe8fb19SBen Gras 	int ex;
2112fe8fb19SBen Gras 
2122fe8fb19SBen Gras 	_DIAGASSERT(flagp != NULL);
2132fe8fb19SBen Gras 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
2142fe8fb19SBen Gras 
2152fe8fb19SBen Gras 	ex = excepts & FE_ALL_EXCEPT;
2162fe8fb19SBen Gras 
2172fe8fb19SBen Gras 	/* Store the current x87 floating-point environment */
2182fe8fb19SBen Gras 	__fnstenv(&fenv);
2192fe8fb19SBen Gras 
2202fe8fb19SBen Gras 	/* Set the requested status flags */
2212fe8fb19SBen Gras 	fenv.x87.status |= *flagp & ex;
2222fe8fb19SBen Gras 
2232fe8fb19SBen Gras 	/* Load the x87 floating-point environent */
2242fe8fb19SBen Gras 	__fldenv(fenv);
2252fe8fb19SBen Gras 
2262fe8fb19SBen Gras 	/* Same for SSE environment */
2272fe8fb19SBen Gras 	__stmxcsr(&fenv.mxcsr);
2282fe8fb19SBen Gras 	fenv.mxcsr |= *flagp & ex;
2292fe8fb19SBen Gras 	__ldmxcsr(fenv.mxcsr);
2302fe8fb19SBen Gras 
2312fe8fb19SBen Gras 	/* Success */
2322fe8fb19SBen Gras 	return (0);
2332fe8fb19SBen Gras }
2342fe8fb19SBen Gras 
2352fe8fb19SBen Gras /*
2362fe8fb19SBen Gras  * The fetestexcept() function determines which of a specified subset of the
2372fe8fb19SBen Gras  * floating-point exception flags are currently set. The `excepts' argument
2382fe8fb19SBen Gras  * specifies the floating-point status flags to be queried.
2392fe8fb19SBen Gras  */
2402fe8fb19SBen Gras int
fetestexcept(int excepts)2412fe8fb19SBen Gras fetestexcept(int excepts)
2422fe8fb19SBen Gras {
2432fe8fb19SBen Gras 	fenv_t fenv;
2442fe8fb19SBen Gras 	uint32_t mxcsr;
2452fe8fb19SBen Gras 	uint16_t status;
2462fe8fb19SBen Gras 	int ex;
2472fe8fb19SBen Gras 
2482fe8fb19SBen Gras 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
2492fe8fb19SBen Gras 
2502fe8fb19SBen Gras 	ex = excepts & FE_ALL_EXCEPT;
2512fe8fb19SBen Gras 
2522fe8fb19SBen Gras 	/* Store the current x87 floating-point environment */
2532fe8fb19SBen Gras 	memset(&fenv, 0, sizeof(fenv));
2542fe8fb19SBen Gras 
2552fe8fb19SBen Gras 	__fnstenv(&fenv);
2562fe8fb19SBen Gras 	__fnstsw(&status);
2572fe8fb19SBen Gras 
2582fe8fb19SBen Gras 	/* Store the MXCSR register state */
2592fe8fb19SBen Gras 	__stmxcsr(&fenv.mxcsr);
2602fe8fb19SBen Gras 	__stmxcsr(&mxcsr);
2612fe8fb19SBen Gras 
2622fe8fb19SBen Gras 	return ((fenv.x87.status | fenv.mxcsr) & ex);
2632fe8fb19SBen Gras }
2642fe8fb19SBen Gras 
2652fe8fb19SBen Gras /*
2662fe8fb19SBen Gras  * The fegetround() function gets the current rounding direction.
2672fe8fb19SBen Gras  */
2682fe8fb19SBen Gras int
fegetround(void)2692fe8fb19SBen Gras fegetround(void)
2702fe8fb19SBen Gras {
2712fe8fb19SBen Gras 	uint32_t mxcsr;
2722fe8fb19SBen Gras 	uint16_t control;
2732fe8fb19SBen Gras 
2742fe8fb19SBen Gras 	/*
2752fe8fb19SBen Gras 	 * We check both the x87 floating-point unit _and_ the SSE unit.
2762fe8fb19SBen Gras 	 * Normally, those two must agree with respect to each other. If they
2772fe8fb19SBen Gras 	 * don't, it's not our fault and the result is non-determinable, in
2782fe8fb19SBen Gras 	 * which case POSIX says that a negative value should be returned.
2792fe8fb19SBen Gras 	 */
2802fe8fb19SBen Gras 	__fnstcw(&control);
2812fe8fb19SBen Gras 	__stmxcsr(&mxcsr);
2822fe8fb19SBen Gras 
2832fe8fb19SBen Gras 	if ((control & _X87_ROUNDING_MASK)
2842fe8fb19SBen Gras 	    != ((mxcsr & _SSE_ROUNDING_MASK) >> 3)) {
2852fe8fb19SBen Gras 		return (-1);
2862fe8fb19SBen Gras 	}
2872fe8fb19SBen Gras 
2882fe8fb19SBen Gras 	return (control & _X87_ROUNDING_MASK);
2892fe8fb19SBen Gras }
2902fe8fb19SBen Gras 
2912fe8fb19SBen Gras /*
2922fe8fb19SBen Gras  * The fesetround() function establishes the rounding direction represented by
2932fe8fb19SBen Gras  * its argument `round'. If the argument is not equal to the value of a rounding
2942fe8fb19SBen Gras  * direction macro, the rounding direction is not changed.
2952fe8fb19SBen Gras  */
2962fe8fb19SBen Gras int
fesetround(int round)2972fe8fb19SBen Gras fesetround(int round)
2982fe8fb19SBen Gras {
2992fe8fb19SBen Gras 	uint32_t  mxcsr;
3002fe8fb19SBen Gras 	uint16_t control;
3012fe8fb19SBen Gras 
3022fe8fb19SBen Gras 	/* Check whether requested rounding direction is supported */
3032fe8fb19SBen Gras 	if (round & (~_X87_ROUNDING_MASK))
3042fe8fb19SBen Gras 		return (-1);
3052fe8fb19SBen Gras 
3062fe8fb19SBen Gras 	/* Store the current x87 control word register  */
3072fe8fb19SBen Gras 	__fnstcw(&control);
3082fe8fb19SBen Gras 
3092fe8fb19SBen Gras 	/*
3102fe8fb19SBen Gras 	 * Set the rounding direction
3112fe8fb19SBen Gras 	 * Rounding Control is bits 10-11, so shift appropriately
3122fe8fb19SBen Gras 	 */
3132fe8fb19SBen Gras 	control &= ~_X87_ROUNDING_MASK;
3142fe8fb19SBen Gras 	control |= round;
3152fe8fb19SBen Gras 
3162fe8fb19SBen Gras 	/* Load the x87 control word register */
3172fe8fb19SBen Gras 	__fldcw(control);
3182fe8fb19SBen Gras 
3192fe8fb19SBen Gras 	/*
3202fe8fb19SBen Gras 	 * Same for the SSE environment
3212fe8fb19SBen Gras 	 * Rounding Control is bits 13-14, so shift appropriately
3222fe8fb19SBen Gras 	 */
3232fe8fb19SBen Gras 	__stmxcsr(&mxcsr);
3242fe8fb19SBen Gras 	mxcsr &= ~_SSE_ROUNDING_MASK;
3252fe8fb19SBen Gras 	mxcsr |= (round << _SSE_ROUND_SHIFT);
3262fe8fb19SBen Gras 	__ldmxcsr(mxcsr);
3272fe8fb19SBen Gras 
3282fe8fb19SBen Gras 	/* Success */
3292fe8fb19SBen Gras 	return (0);
3302fe8fb19SBen Gras }
3312fe8fb19SBen Gras 
3322fe8fb19SBen Gras /*
3332fe8fb19SBen Gras  * The fegetenv() function attempts to store the current floating-point
3342fe8fb19SBen Gras  * environment in the object pointed to by envp.
3352fe8fb19SBen Gras  */
3362fe8fb19SBen Gras int
fegetenv(fenv_t * envp)3372fe8fb19SBen Gras fegetenv(fenv_t *envp)
3382fe8fb19SBen Gras {
3392fe8fb19SBen Gras 	_DIAGASSERT(envp != NULL);
3402fe8fb19SBen Gras 
3412fe8fb19SBen Gras 	/* Store the current x87 floating-point environment */
3422fe8fb19SBen Gras 	__fnstenv(envp);
3432fe8fb19SBen Gras 
3442fe8fb19SBen Gras 	/* Store the MXCSR register state */
3452fe8fb19SBen Gras 	__stmxcsr(&envp->mxcsr);
3462fe8fb19SBen Gras 
3472fe8fb19SBen Gras      /*
3482fe8fb19SBen Gras       * When an FNSTENV instruction is executed, all pending exceptions are
3492fe8fb19SBen Gras       * essentially lost (either the x87 FPU status register is cleared or all
3502fe8fb19SBen Gras       * exceptions are masked).
3512fe8fb19SBen Gras       *
3522fe8fb19SBen Gras       * 8.6 X87 FPU EXCEPTION SYNCHRONIZATION -
3532fe8fb19SBen Gras       * Intel(R) 64 and IA-32 Architectures Softare Developer's Manual - Vol 1
3542fe8fb19SBen Gras       *
3552fe8fb19SBen Gras       */
3562fe8fb19SBen Gras 	__fldcw(envp->x87.control);
3572fe8fb19SBen Gras 
3582fe8fb19SBen Gras 	/* Success */
3592fe8fb19SBen Gras 	return (0);
3602fe8fb19SBen Gras }
3612fe8fb19SBen Gras 
3622fe8fb19SBen Gras /*
3632fe8fb19SBen Gras  * The feholdexcept() function saves the current floating-point environment
3642fe8fb19SBen Gras  * in the object pointed to by envp, clears the floating-point status flags, and
3652fe8fb19SBen Gras  * then installs a non-stop (continue on floating-point exceptions) mode, if
3662fe8fb19SBen Gras  * available, for all floating-point exceptions.
3672fe8fb19SBen Gras  */
3682fe8fb19SBen Gras int
feholdexcept(fenv_t * envp)3692fe8fb19SBen Gras feholdexcept(fenv_t *envp)
3702fe8fb19SBen Gras {
3712fe8fb19SBen Gras 	uint32_t mxcsr;
3722fe8fb19SBen Gras 
3732fe8fb19SBen Gras 	_DIAGASSERT(envp != NULL);
3742fe8fb19SBen Gras 
3752fe8fb19SBen Gras 	/* Store the current x87 floating-point environment */
3762fe8fb19SBen Gras 	__fnstenv(envp);
3772fe8fb19SBen Gras 
3782fe8fb19SBen Gras 	/* Clear all exception flags in FPU */
3792fe8fb19SBen Gras 	__fnclex();
3802fe8fb19SBen Gras 
3812fe8fb19SBen Gras 	/* Store the MXCSR register state */
3822fe8fb19SBen Gras 	__stmxcsr(&envp->mxcsr);
3832fe8fb19SBen Gras 
3842fe8fb19SBen Gras 	/* Clear exception flags in MXCSR XXX */
3852fe8fb19SBen Gras 	mxcsr = envp->mxcsr;
3862fe8fb19SBen Gras 	mxcsr &= ~FE_ALL_EXCEPT;
3872fe8fb19SBen Gras 
3882fe8fb19SBen Gras 	/* Mask all exceptions */
3892fe8fb19SBen Gras 	mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT;
3902fe8fb19SBen Gras 
3912fe8fb19SBen Gras 	__ldmxcsr(mxcsr);
3922fe8fb19SBen Gras 
3932fe8fb19SBen Gras 	/* Success */
3942fe8fb19SBen Gras 	return (0);
3952fe8fb19SBen Gras }
3962fe8fb19SBen Gras 
3972fe8fb19SBen Gras /*
3982fe8fb19SBen Gras  * The fesetenv() function attempts to establish the floating-point environment
3992fe8fb19SBen Gras  * represented by the object pointed to by envp. The argument `envp' points
4002fe8fb19SBen Gras  * to an object set by a call to fegetenv() or feholdexcept(), or equal a
4012fe8fb19SBen Gras  * floating-point environment macro. The fesetenv() function does not raise
4022fe8fb19SBen Gras  * floating-point exceptions, but only installs the state of the floating-point
4032fe8fb19SBen Gras  * status flags represented through its argument.
4042fe8fb19SBen Gras  */
4052fe8fb19SBen Gras int
fesetenv(const fenv_t * envp)4062fe8fb19SBen Gras fesetenv(const fenv_t *envp)
4072fe8fb19SBen Gras {
4082fe8fb19SBen Gras 	fenv_t fenv;
4092fe8fb19SBen Gras 
4102fe8fb19SBen Gras 	_DIAGASSERT(envp != NULL);
4112fe8fb19SBen Gras 
4122fe8fb19SBen Gras 	/* Store the x87 floating-point environment */
4132fe8fb19SBen Gras 	memset(&fenv, 0, sizeof fenv);
4142fe8fb19SBen Gras 	__fnstenv(&fenv);
4152fe8fb19SBen Gras 
4162fe8fb19SBen Gras 	__fe_dfl_env.x87.control = (fenv.x87.control & 0xffff0000)
4172fe8fb19SBen Gras 	    | (__fe_dfl_env.x87.control & 0x0000ffff);
4182fe8fb19SBen Gras 	__fe_dfl_env.x87.status = (fenv.x87.status & 0xffff0000)
4192fe8fb19SBen Gras 	    | (__fe_dfl_env.x87.status & 0x0000ffff);
4202fe8fb19SBen Gras 	__fe_dfl_env.x87.tag = (fenv.x87.tag & 0xffff0000)
4212fe8fb19SBen Gras 	    | (__fe_dfl_env.x87.tag & 0x0000ffff);
4222fe8fb19SBen Gras 	__fe_dfl_env.x87.others[3] = (fenv.x87.others[3] & 0xffff0000)
4232fe8fb19SBen Gras 	    | (__fe_dfl_env.x87.others[3] & 0x0000ffff);
4242fe8fb19SBen Gras 	__fldenv(*envp);
4252fe8fb19SBen Gras 
4262fe8fb19SBen Gras 	/* Store the MXCSR register */
4272fe8fb19SBen Gras 	__ldmxcsr(envp->mxcsr);
4282fe8fb19SBen Gras 
4292fe8fb19SBen Gras 	/* Success */
4302fe8fb19SBen Gras 	return (0);
4312fe8fb19SBen Gras }
4322fe8fb19SBen Gras 
4332fe8fb19SBen Gras /*
4342fe8fb19SBen Gras  * The feupdateenv() function saves the currently raised floating-point
4352fe8fb19SBen Gras  * exceptions in its automatic storage, installs the floating-point environment
4362fe8fb19SBen Gras  * represented by the object pointed to by `envp', and then raises the saved
4372fe8fb19SBen Gras  * floating-point exceptions. The argument `envp' shall point to an object set
4382fe8fb19SBen Gras  * by a call to feholdexcept() or fegetenv(), or equal a floating-point
4392fe8fb19SBen Gras  * environment macro.
4402fe8fb19SBen Gras  */
4412fe8fb19SBen Gras int
feupdateenv(const fenv_t * envp)4422fe8fb19SBen Gras feupdateenv(const fenv_t *envp)
4432fe8fb19SBen Gras {
4442fe8fb19SBen Gras 	fenv_t fenv;
4452fe8fb19SBen Gras 	uint32_t mxcsr;
4462fe8fb19SBen Gras 	uint16_t sw;
4472fe8fb19SBen Gras 
4482fe8fb19SBen Gras 	_DIAGASSERT(envp != NULL);
4492fe8fb19SBen Gras 
4502fe8fb19SBen Gras 	/* Store the x87 floating-point environment */
4512fe8fb19SBen Gras 	memset(&fenv, 0, sizeof(fenv));
4522fe8fb19SBen Gras 	__fnstenv(&fenv);
4532fe8fb19SBen Gras 
4542fe8fb19SBen Gras 	__fe_dfl_env.x87.control = (fenv.x87.control & 0xffff0000)
4552fe8fb19SBen Gras 	    | (__fe_dfl_env.x87.control & 0x0000ffff);
4562fe8fb19SBen Gras 	__fe_dfl_env.x87.status = (fenv.x87.status & 0xffff0000)
4572fe8fb19SBen Gras 	    | (__fe_dfl_env.x87.status & 0x0000ffff);
4582fe8fb19SBen Gras 	__fe_dfl_env.x87.tag = (fenv.x87.tag & 0xffff0000)
4592fe8fb19SBen Gras 	    | (__fe_dfl_env.x87.tag & 0x0000ffff);
4602fe8fb19SBen Gras 	__fe_dfl_env.x87.others[3] = (fenv.x87.others[3] & 0xffff0000)
4612fe8fb19SBen Gras 	    | (__fe_dfl_env.x87.others[3] & 0x0000ffff);
4622fe8fb19SBen Gras 
4632fe8fb19SBen Gras 	/* Store the x87 status register */
4642fe8fb19SBen Gras 	__fnstsw(&sw);
4652fe8fb19SBen Gras 
4662fe8fb19SBen Gras 	/* Store the MXCSR register */
4672fe8fb19SBen Gras 	__stmxcsr(&mxcsr);
4682fe8fb19SBen Gras 
4692fe8fb19SBen Gras 	/* Install new floating-point environment */
4702fe8fb19SBen Gras 	fesetenv(envp);
4712fe8fb19SBen Gras 
4722fe8fb19SBen Gras 	/* Raise any previously accumulated exceptions */
4732fe8fb19SBen Gras 	feraiseexcept((sw | mxcsr) & FE_ALL_EXCEPT);
4742fe8fb19SBen Gras 
4752fe8fb19SBen Gras 	/* Success */
4762fe8fb19SBen Gras 	return (0);
4772fe8fb19SBen Gras }
4782fe8fb19SBen Gras 
4792fe8fb19SBen Gras /*
4802fe8fb19SBen Gras  * The following functions are extentions to the standard
4812fe8fb19SBen Gras  */
4822fe8fb19SBen Gras int
feenableexcept(int mask)4832fe8fb19SBen Gras feenableexcept(int mask)
4842fe8fb19SBen Gras {
4852fe8fb19SBen Gras 	uint32_t mxcsr, omask;
4862fe8fb19SBen Gras 	uint16_t control;
4872fe8fb19SBen Gras 
4882fe8fb19SBen Gras 	_DIAGASSERT((mask & ~FE_ALL_EXCEPT) == 0);
4892fe8fb19SBen Gras 	mask &= FE_ALL_EXCEPT;
4902fe8fb19SBen Gras 
4912fe8fb19SBen Gras 	__fnstcw(&control);
4922fe8fb19SBen Gras 	__stmxcsr(&mxcsr);
4932fe8fb19SBen Gras 
4942fe8fb19SBen Gras 	omask = (control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
4952fe8fb19SBen Gras 	control &= ~mask;
4962fe8fb19SBen Gras 	__fldcw(control);
4972fe8fb19SBen Gras 
4982fe8fb19SBen Gras 	mxcsr &= ~(mask << _SSE_EMASK_SHIFT);
4992fe8fb19SBen Gras 	__ldmxcsr(mxcsr);
5002fe8fb19SBen Gras 
501*84d9c625SLionel Sambuc 	return (FE_ALL_EXCEPT & ~omask);
5022fe8fb19SBen Gras 
5032fe8fb19SBen Gras }
5042fe8fb19SBen Gras 
5052fe8fb19SBen Gras int
fedisableexcept(int mask)5062fe8fb19SBen Gras fedisableexcept(int mask)
5072fe8fb19SBen Gras {
5082fe8fb19SBen Gras 	uint32_t mxcsr, omask;
5092fe8fb19SBen Gras 	uint16_t control;
5102fe8fb19SBen Gras 
5112fe8fb19SBen Gras 	_DIAGASSERT((mask & ~FE_ALL_EXCEPT) == 0);
5122fe8fb19SBen Gras 
5132fe8fb19SBen Gras 	__fnstcw(&control);
5142fe8fb19SBen Gras 	__stmxcsr(&mxcsr);
5152fe8fb19SBen Gras 
5162fe8fb19SBen Gras 	omask = (control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
5172fe8fb19SBen Gras 	control |= mask;
5182fe8fb19SBen Gras 	__fldcw(control);
5192fe8fb19SBen Gras 
5202fe8fb19SBen Gras 	mxcsr |= mask << _SSE_EMASK_SHIFT;
5212fe8fb19SBen Gras 	__ldmxcsr(mxcsr);
5222fe8fb19SBen Gras 
523*84d9c625SLionel Sambuc 	return (FE_ALL_EXCEPT & ~omask);
5242fe8fb19SBen Gras }
5252fe8fb19SBen Gras 
5262fe8fb19SBen Gras int
fegetexcept(void)5272fe8fb19SBen Gras fegetexcept(void)
5282fe8fb19SBen Gras {
5292fe8fb19SBen Gras 	uint16_t control;
5302fe8fb19SBen Gras 
5312fe8fb19SBen Gras 	/*
5322fe8fb19SBen Gras 	 * We assume that the masks for the x87 and the SSE unit are
5332fe8fb19SBen Gras 	 * the same.
5342fe8fb19SBen Gras 	 */
5352fe8fb19SBen Gras 	__fnstcw(&control);
5362fe8fb19SBen Gras 
537*84d9c625SLionel Sambuc 	return (~control & FE_ALL_EXCEPT);
5382fe8fb19SBen Gras }
5392fe8fb19SBen Gras 
540