xref: /freebsd/lib/msun/i387/fenv.c (revision 5e53a4f90f82c4345f277dd87cc9292f26e04a29)
10b71a226SDavid Schultz /*-
2*5e53a4f9SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*5e53a4f9SPedro F. Giffuni  *
49233b45aSDavid Schultz  * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG>
50b71a226SDavid Schultz  * All rights reserved.
60b71a226SDavid Schultz  *
70b71a226SDavid Schultz  * Redistribution and use in source and binary forms, with or without
80b71a226SDavid Schultz  * modification, are permitted provided that the following conditions
90b71a226SDavid Schultz  * are met:
100b71a226SDavid Schultz  * 1. Redistributions of source code must retain the above copyright
110b71a226SDavid Schultz  *    notice, this list of conditions and the following disclaimer.
120b71a226SDavid Schultz  * 2. Redistributions in binary form must reproduce the above copyright
130b71a226SDavid Schultz  *    notice, this list of conditions and the following disclaimer in the
140b71a226SDavid Schultz  *    documentation and/or other materials provided with the distribution.
150b71a226SDavid Schultz  *
160b71a226SDavid Schultz  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
170b71a226SDavid Schultz  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
180b71a226SDavid Schultz  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
190b71a226SDavid Schultz  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
200b71a226SDavid Schultz  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
210b71a226SDavid Schultz  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
220b71a226SDavid Schultz  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
230b71a226SDavid Schultz  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
240b71a226SDavid Schultz  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
250b71a226SDavid Schultz  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
260b71a226SDavid Schultz  * SUCH DAMAGE.
270b71a226SDavid Schultz  *
280b71a226SDavid Schultz  * $FreeBSD$
290b71a226SDavid Schultz  */
300b71a226SDavid Schultz 
319233b45aSDavid Schultz #include <sys/cdefs.h>
320b71a226SDavid Schultz #include <sys/types.h>
330b71a226SDavid Schultz #include <machine/npx.h>
34d78e594bSDavid Schultz 
35d78e594bSDavid Schultz #define	__fenv_static
365d9fefacSDavid Schultz #include "fenv.h"
37d78e594bSDavid Schultz 
38d78e594bSDavid Schultz #ifdef __GNUC_GNU_INLINE__
39d78e594bSDavid Schultz #error "This file must be compiled with C99 'inline' semantics"
40d78e594bSDavid Schultz #endif
410b71a226SDavid Schultz 
420b71a226SDavid Schultz const fenv_t __fe_dfl_env = {
439233b45aSDavid Schultz 	__INITIAL_NPXCW__,
449233b45aSDavid Schultz 	0x0000,
459233b45aSDavid Schultz 	0x0000,
469233b45aSDavid Schultz 	0x1f80,
470b71a226SDavid Schultz 	0xffffffff,
480b71a226SDavid Schultz 	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490b71a226SDavid Schultz 	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff }
500b71a226SDavid Schultz };
519233b45aSDavid Schultz 
529233b45aSDavid Schultz enum __sse_support __has_sse =
539233b45aSDavid Schultz #ifdef __SSE__
549233b45aSDavid Schultz 	__SSE_YES;
559233b45aSDavid Schultz #else
569233b45aSDavid Schultz 	__SSE_UNK;
579233b45aSDavid Schultz #endif
589233b45aSDavid Schultz 
599233b45aSDavid Schultz #define	getfl(x)	__asm __volatile("pushfl\n\tpopl %0" : "=mr" (*(x)))
609233b45aSDavid Schultz #define	setfl(x)	__asm __volatile("pushl %0\n\tpopfl" : : "g" (x))
619233b45aSDavid Schultz #define	cpuid_dx(x)	__asm __volatile("pushl %%ebx\n\tmovl $1, %%eax\n\t"  \
629233b45aSDavid Schultz 					 "cpuid\n\tpopl %%ebx"		      \
639233b45aSDavid Schultz 					: "=d" (*(x)) : : "eax", "ecx")
649233b45aSDavid Schultz 
659233b45aSDavid Schultz /*
669233b45aSDavid Schultz  * Test for SSE support on this processor.  We need to do this because
679233b45aSDavid Schultz  * we need to use ldmxcsr/stmxcsr to get correct results if any part
689233b45aSDavid Schultz  * of the program was compiled to use SSE floating-point, but we can't
699233b45aSDavid Schultz  * use SSE on older processors.
709233b45aSDavid Schultz  */
719233b45aSDavid Schultz int
729233b45aSDavid Schultz __test_sse(void)
739233b45aSDavid Schultz {
749233b45aSDavid Schultz 	int flag, nflag;
759233b45aSDavid Schultz 	int dx_features;
769233b45aSDavid Schultz 
779233b45aSDavid Schultz 	/* Am I a 486? */
789233b45aSDavid Schultz 	getfl(&flag);
799233b45aSDavid Schultz 	nflag = flag ^ 0x200000;
809233b45aSDavid Schultz 	setfl(nflag);
819233b45aSDavid Schultz 	getfl(&nflag);
829233b45aSDavid Schultz 	if (flag != nflag) {
839233b45aSDavid Schultz 		/* Not a 486, so CPUID should work. */
849233b45aSDavid Schultz 		cpuid_dx(&dx_features);
859233b45aSDavid Schultz 		if (dx_features & 0x2000000) {
869233b45aSDavid Schultz 			__has_sse = __SSE_YES;
879233b45aSDavid Schultz 			return (1);
889233b45aSDavid Schultz 		}
899233b45aSDavid Schultz 	}
909233b45aSDavid Schultz 	__has_sse = __SSE_NO;
919233b45aSDavid Schultz 	return (0);
929233b45aSDavid Schultz }
939233b45aSDavid Schultz 
94d78e594bSDavid Schultz extern inline int feclearexcept(int __excepts);
95d78e594bSDavid Schultz extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts);
96d78e594bSDavid Schultz 
979233b45aSDavid Schultz int
989233b45aSDavid Schultz fesetexceptflag(const fexcept_t *flagp, int excepts)
999233b45aSDavid Schultz {
1009233b45aSDavid Schultz 	fenv_t env;
10160d818efSKonstantin Belousov 	__uint32_t mxcsr;
1029233b45aSDavid Schultz 
1039233b45aSDavid Schultz 	__fnstenv(&env);
1049233b45aSDavid Schultz 	env.__status &= ~excepts;
1059233b45aSDavid Schultz 	env.__status |= *flagp & excepts;
1069233b45aSDavid Schultz 	__fldenv(env);
1079233b45aSDavid Schultz 
1089233b45aSDavid Schultz 	if (__HAS_SSE()) {
1099233b45aSDavid Schultz 		__stmxcsr(&mxcsr);
1109233b45aSDavid Schultz 		mxcsr &= ~excepts;
1119233b45aSDavid Schultz 		mxcsr |= *flagp & excepts;
1129233b45aSDavid Schultz 		__ldmxcsr(mxcsr);
1139233b45aSDavid Schultz 	}
1149233b45aSDavid Schultz 
1159233b45aSDavid Schultz 	return (0);
1169233b45aSDavid Schultz }
1179233b45aSDavid Schultz 
1189233b45aSDavid Schultz int
1199233b45aSDavid Schultz feraiseexcept(int excepts)
1209233b45aSDavid Schultz {
1219233b45aSDavid Schultz 	fexcept_t ex = excepts;
1229233b45aSDavid Schultz 
1239233b45aSDavid Schultz 	fesetexceptflag(&ex, excepts);
1249233b45aSDavid Schultz 	__fwait();
1259233b45aSDavid Schultz 	return (0);
1269233b45aSDavid Schultz }
1279233b45aSDavid Schultz 
128d78e594bSDavid Schultz extern inline int fetestexcept(int __excepts);
129d78e594bSDavid Schultz extern inline int fegetround(void);
130d78e594bSDavid Schultz extern inline int fesetround(int __round);
131d78e594bSDavid Schultz 
1329233b45aSDavid Schultz int
1339233b45aSDavid Schultz fegetenv(fenv_t *envp)
1349233b45aSDavid Schultz {
13560d818efSKonstantin Belousov 	__uint32_t mxcsr;
1369233b45aSDavid Schultz 
1379233b45aSDavid Schultz 	__fnstenv(envp);
1383cb636ceSDavid Schultz 	/*
1393cb636ceSDavid Schultz 	 * fnstenv masks all exceptions, so we need to restore
1403cb636ceSDavid Schultz 	 * the old control word to avoid this side effect.
1413cb636ceSDavid Schultz 	 */
1423cb636ceSDavid Schultz 	__fldcw(envp->__control);
1439233b45aSDavid Schultz 	if (__HAS_SSE()) {
1449233b45aSDavid Schultz 		__stmxcsr(&mxcsr);
1459233b45aSDavid Schultz 		__set_mxcsr(*envp, mxcsr);
1469233b45aSDavid Schultz 	}
1479233b45aSDavid Schultz 	return (0);
1489233b45aSDavid Schultz }
1499233b45aSDavid Schultz 
1509233b45aSDavid Schultz int
1519233b45aSDavid Schultz feholdexcept(fenv_t *envp)
1529233b45aSDavid Schultz {
15360d818efSKonstantin Belousov 	__uint32_t mxcsr;
1549233b45aSDavid Schultz 
1559233b45aSDavid Schultz 	__fnstenv(envp);
1569233b45aSDavid Schultz 	__fnclex();
1579233b45aSDavid Schultz 	if (__HAS_SSE()) {
1589233b45aSDavid Schultz 		__stmxcsr(&mxcsr);
1599233b45aSDavid Schultz 		__set_mxcsr(*envp, mxcsr);
1609233b45aSDavid Schultz 		mxcsr &= ~FE_ALL_EXCEPT;
1619233b45aSDavid Schultz 		mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT;
1629233b45aSDavid Schultz 		__ldmxcsr(mxcsr);
1639233b45aSDavid Schultz 	}
1649233b45aSDavid Schultz 	return (0);
1659233b45aSDavid Schultz }
1669233b45aSDavid Schultz 
167d78e594bSDavid Schultz extern inline int fesetenv(const fenv_t *__envp);
168d78e594bSDavid Schultz 
1699233b45aSDavid Schultz int
1709233b45aSDavid Schultz feupdateenv(const fenv_t *envp)
1719233b45aSDavid Schultz {
17260d818efSKonstantin Belousov 	__uint32_t mxcsr;
17360d818efSKonstantin Belousov 	__uint16_t status;
1749233b45aSDavid Schultz 
1759233b45aSDavid Schultz 	__fnstsw(&status);
1769233b45aSDavid Schultz 	if (__HAS_SSE())
1779233b45aSDavid Schultz 		__stmxcsr(&mxcsr);
1789233b45aSDavid Schultz 	else
1799233b45aSDavid Schultz 		mxcsr = 0;
1809233b45aSDavid Schultz 	fesetenv(envp);
1819233b45aSDavid Schultz 	feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT);
1829233b45aSDavid Schultz 	return (0);
1839233b45aSDavid Schultz }
1849233b45aSDavid Schultz 
1859233b45aSDavid Schultz int
1869233b45aSDavid Schultz __feenableexcept(int mask)
1879233b45aSDavid Schultz {
18860d818efSKonstantin Belousov 	__uint32_t mxcsr, omask;
18960d818efSKonstantin Belousov 	__uint16_t control;
1909233b45aSDavid Schultz 
1919233b45aSDavid Schultz 	mask &= FE_ALL_EXCEPT;
1929233b45aSDavid Schultz 	__fnstcw(&control);
1939233b45aSDavid Schultz 	if (__HAS_SSE())
1949233b45aSDavid Schultz 		__stmxcsr(&mxcsr);
1959233b45aSDavid Schultz 	else
1969233b45aSDavid Schultz 		mxcsr = 0;
197741ae1d0SDavid Schultz 	omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
1989233b45aSDavid Schultz 	control &= ~mask;
1999233b45aSDavid Schultz 	__fldcw(control);
2009233b45aSDavid Schultz 	if (__HAS_SSE()) {
2019233b45aSDavid Schultz 		mxcsr &= ~(mask << _SSE_EMASK_SHIFT);
2029233b45aSDavid Schultz 		__ldmxcsr(mxcsr);
2039233b45aSDavid Schultz 	}
204741ae1d0SDavid Schultz 	return (omask);
2059233b45aSDavid Schultz }
2069233b45aSDavid Schultz 
2079233b45aSDavid Schultz int
2089233b45aSDavid Schultz __fedisableexcept(int mask)
2099233b45aSDavid Schultz {
21060d818efSKonstantin Belousov 	__uint32_t mxcsr, omask;
21160d818efSKonstantin Belousov 	__uint16_t control;
2129233b45aSDavid Schultz 
2139233b45aSDavid Schultz 	mask &= FE_ALL_EXCEPT;
2149233b45aSDavid Schultz 	__fnstcw(&control);
2159233b45aSDavid Schultz 	if (__HAS_SSE())
2169233b45aSDavid Schultz 		__stmxcsr(&mxcsr);
2179233b45aSDavid Schultz 	else
2189233b45aSDavid Schultz 		mxcsr = 0;
219741ae1d0SDavid Schultz 	omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
2209233b45aSDavid Schultz 	control |= mask;
2219233b45aSDavid Schultz 	__fldcw(control);
2229233b45aSDavid Schultz 	if (__HAS_SSE()) {
2239233b45aSDavid Schultz 		mxcsr |= mask << _SSE_EMASK_SHIFT;
2249233b45aSDavid Schultz 		__ldmxcsr(mxcsr);
2259233b45aSDavid Schultz 	}
226741ae1d0SDavid Schultz 	return (omask);
2279233b45aSDavid Schultz }
22890a83ac6SDavid Schultz 
22990a83ac6SDavid Schultz __weak_reference(__feenableexcept, feenableexcept);
23090a83ac6SDavid Schultz __weak_reference(__fedisableexcept, fedisableexcept);
231