10b71a226SDavid Schultz /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
35e53a4f9SPedro 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
290b71a226SDavid Schultz #include <sys/types.h>
300b71a226SDavid Schultz #include <machine/npx.h>
31d78e594bSDavid Schultz
32d78e594bSDavid Schultz #define __fenv_static
335d9fefacSDavid Schultz #include "fenv.h"
34d78e594bSDavid Schultz
35d78e594bSDavid Schultz #ifdef __GNUC_GNU_INLINE__
36d78e594bSDavid Schultz #error "This file must be compiled with C99 'inline' semantics"
37d78e594bSDavid Schultz #endif
380b71a226SDavid Schultz
390b71a226SDavid Schultz const fenv_t __fe_dfl_env = {
409233b45aSDavid Schultz __INITIAL_NPXCW__,
419233b45aSDavid Schultz 0x0000,
429233b45aSDavid Schultz 0x0000,
439233b45aSDavid Schultz 0x1f80,
440b71a226SDavid Schultz 0xffffffff,
450b71a226SDavid Schultz { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
460b71a226SDavid Schultz 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff }
470b71a226SDavid Schultz };
489233b45aSDavid Schultz
499233b45aSDavid Schultz enum __sse_support __has_sse =
509233b45aSDavid Schultz #ifdef __SSE__
519233b45aSDavid Schultz __SSE_YES;
529233b45aSDavid Schultz #else
539233b45aSDavid Schultz __SSE_UNK;
549233b45aSDavid Schultz #endif
559233b45aSDavid Schultz
569233b45aSDavid Schultz #define getfl(x) __asm __volatile("pushfl\n\tpopl %0" : "=mr" (*(x)))
579233b45aSDavid Schultz #define setfl(x) __asm __volatile("pushl %0\n\tpopfl" : : "g" (x))
589233b45aSDavid Schultz #define cpuid_dx(x) __asm __volatile("pushl %%ebx\n\tmovl $1, %%eax\n\t" \
599233b45aSDavid Schultz "cpuid\n\tpopl %%ebx" \
609233b45aSDavid Schultz : "=d" (*(x)) : : "eax", "ecx")
619233b45aSDavid Schultz
629233b45aSDavid Schultz /*
639233b45aSDavid Schultz * Test for SSE support on this processor. We need to do this because
649233b45aSDavid Schultz * we need to use ldmxcsr/stmxcsr to get correct results if any part
659233b45aSDavid Schultz * of the program was compiled to use SSE floating-point, but we can't
669233b45aSDavid Schultz * use SSE on older processors.
679233b45aSDavid Schultz */
689233b45aSDavid Schultz int
__test_sse(void)699233b45aSDavid Schultz __test_sse(void)
709233b45aSDavid Schultz {
719233b45aSDavid Schultz int flag, nflag;
729233b45aSDavid Schultz int dx_features;
739233b45aSDavid Schultz
749233b45aSDavid Schultz /* Am I a 486? */
759233b45aSDavid Schultz getfl(&flag);
769233b45aSDavid Schultz nflag = flag ^ 0x200000;
779233b45aSDavid Schultz setfl(nflag);
789233b45aSDavid Schultz getfl(&nflag);
799233b45aSDavid Schultz if (flag != nflag) {
809233b45aSDavid Schultz /* Not a 486, so CPUID should work. */
819233b45aSDavid Schultz cpuid_dx(&dx_features);
829233b45aSDavid Schultz if (dx_features & 0x2000000) {
839233b45aSDavid Schultz __has_sse = __SSE_YES;
849233b45aSDavid Schultz return (1);
859233b45aSDavid Schultz }
869233b45aSDavid Schultz }
879233b45aSDavid Schultz __has_sse = __SSE_NO;
889233b45aSDavid Schultz return (0);
899233b45aSDavid Schultz }
909233b45aSDavid Schultz
91d78e594bSDavid Schultz extern inline int feclearexcept(int __excepts);
92d78e594bSDavid Schultz extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts);
93d78e594bSDavid Schultz
949233b45aSDavid Schultz int
fesetexceptflag(const fexcept_t * flagp,int excepts)959233b45aSDavid Schultz fesetexceptflag(const fexcept_t *flagp, int excepts)
969233b45aSDavid Schultz {
979233b45aSDavid Schultz fenv_t env;
9860d818efSKonstantin Belousov __uint32_t mxcsr;
999233b45aSDavid Schultz
1009233b45aSDavid Schultz __fnstenv(&env);
1019233b45aSDavid Schultz env.__status &= ~excepts;
1029233b45aSDavid Schultz env.__status |= *flagp & excepts;
103b451efbeSDimitry Andric __fldenv(&env);
1049233b45aSDavid Schultz
1059233b45aSDavid Schultz if (__HAS_SSE()) {
1069233b45aSDavid Schultz __stmxcsr(&mxcsr);
1079233b45aSDavid Schultz mxcsr &= ~excepts;
1089233b45aSDavid Schultz mxcsr |= *flagp & excepts;
109b451efbeSDimitry Andric __ldmxcsr(&mxcsr);
1109233b45aSDavid Schultz }
1119233b45aSDavid Schultz
1129233b45aSDavid Schultz return (0);
1139233b45aSDavid Schultz }
1149233b45aSDavid Schultz
1159233b45aSDavid Schultz int
feraiseexcept(int excepts)1169233b45aSDavid Schultz feraiseexcept(int excepts)
1179233b45aSDavid Schultz {
1189233b45aSDavid Schultz fexcept_t ex = excepts;
1199233b45aSDavid Schultz
1209233b45aSDavid Schultz fesetexceptflag(&ex, excepts);
1219233b45aSDavid Schultz __fwait();
1229233b45aSDavid Schultz return (0);
1239233b45aSDavid Schultz }
1249233b45aSDavid Schultz
125d78e594bSDavid Schultz extern inline int fetestexcept(int __excepts);
126d78e594bSDavid Schultz extern inline int fegetround(void);
127d78e594bSDavid Schultz extern inline int fesetround(int __round);
128d78e594bSDavid Schultz
1299233b45aSDavid Schultz int
fegetenv(fenv_t * envp)1309233b45aSDavid Schultz fegetenv(fenv_t *envp)
1319233b45aSDavid Schultz {
13260d818efSKonstantin Belousov __uint32_t mxcsr;
1339233b45aSDavid Schultz
1349233b45aSDavid Schultz __fnstenv(envp);
1353cb636ceSDavid Schultz /*
1363cb636ceSDavid Schultz * fnstenv masks all exceptions, so we need to restore
1373cb636ceSDavid Schultz * the old control word to avoid this side effect.
1383cb636ceSDavid Schultz */
139b451efbeSDimitry Andric __fldcw(&envp->__control);
1409233b45aSDavid Schultz if (__HAS_SSE()) {
1419233b45aSDavid Schultz __stmxcsr(&mxcsr);
1429233b45aSDavid Schultz __set_mxcsr(*envp, mxcsr);
1439233b45aSDavid Schultz }
1449233b45aSDavid Schultz return (0);
1459233b45aSDavid Schultz }
1469233b45aSDavid Schultz
1479233b45aSDavid Schultz int
feholdexcept(fenv_t * envp)1489233b45aSDavid Schultz feholdexcept(fenv_t *envp)
1499233b45aSDavid Schultz {
15060d818efSKonstantin Belousov __uint32_t mxcsr;
1519233b45aSDavid Schultz
1529233b45aSDavid Schultz __fnstenv(envp);
1539233b45aSDavid Schultz __fnclex();
1549233b45aSDavid Schultz if (__HAS_SSE()) {
1559233b45aSDavid Schultz __stmxcsr(&mxcsr);
1569233b45aSDavid Schultz __set_mxcsr(*envp, mxcsr);
1579233b45aSDavid Schultz mxcsr &= ~FE_ALL_EXCEPT;
1589233b45aSDavid Schultz mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT;
159b451efbeSDimitry Andric __ldmxcsr(&mxcsr);
1609233b45aSDavid Schultz }
1619233b45aSDavid Schultz return (0);
1629233b45aSDavid Schultz }
1639233b45aSDavid Schultz
164d78e594bSDavid Schultz extern inline int fesetenv(const fenv_t *__envp);
165d78e594bSDavid Schultz
1669233b45aSDavid Schultz int
feupdateenv(const fenv_t * envp)1679233b45aSDavid Schultz feupdateenv(const fenv_t *envp)
1689233b45aSDavid Schultz {
16960d818efSKonstantin Belousov __uint32_t mxcsr;
17060d818efSKonstantin Belousov __uint16_t status;
1719233b45aSDavid Schultz
1729233b45aSDavid Schultz __fnstsw(&status);
1739233b45aSDavid Schultz if (__HAS_SSE())
1749233b45aSDavid Schultz __stmxcsr(&mxcsr);
1759233b45aSDavid Schultz else
1769233b45aSDavid Schultz mxcsr = 0;
1779233b45aSDavid Schultz fesetenv(envp);
1789233b45aSDavid Schultz feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT);
1799233b45aSDavid Schultz return (0);
1809233b45aSDavid Schultz }
1819233b45aSDavid Schultz
1829233b45aSDavid Schultz int
__feenableexcept(int mask)1839233b45aSDavid Schultz __feenableexcept(int mask)
1849233b45aSDavid Schultz {
18560d818efSKonstantin Belousov __uint32_t mxcsr, omask;
18660d818efSKonstantin Belousov __uint16_t control;
1879233b45aSDavid Schultz
1889233b45aSDavid Schultz mask &= FE_ALL_EXCEPT;
1899233b45aSDavid Schultz __fnstcw(&control);
1909233b45aSDavid Schultz if (__HAS_SSE())
1919233b45aSDavid Schultz __stmxcsr(&mxcsr);
1929233b45aSDavid Schultz else
1939233b45aSDavid Schultz mxcsr = 0;
194741ae1d0SDavid Schultz omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
1959233b45aSDavid Schultz control &= ~mask;
196b451efbeSDimitry Andric __fldcw(&control);
1979233b45aSDavid Schultz if (__HAS_SSE()) {
1989233b45aSDavid Schultz mxcsr &= ~(mask << _SSE_EMASK_SHIFT);
199b451efbeSDimitry Andric __ldmxcsr(&mxcsr);
2009233b45aSDavid Schultz }
201741ae1d0SDavid Schultz return (omask);
2029233b45aSDavid Schultz }
2039233b45aSDavid Schultz
2049233b45aSDavid Schultz int
__fedisableexcept(int mask)2059233b45aSDavid Schultz __fedisableexcept(int mask)
2069233b45aSDavid Schultz {
20760d818efSKonstantin Belousov __uint32_t mxcsr, omask;
20860d818efSKonstantin Belousov __uint16_t control;
2099233b45aSDavid Schultz
2109233b45aSDavid Schultz mask &= FE_ALL_EXCEPT;
2119233b45aSDavid Schultz __fnstcw(&control);
2129233b45aSDavid Schultz if (__HAS_SSE())
2139233b45aSDavid Schultz __stmxcsr(&mxcsr);
2149233b45aSDavid Schultz else
2159233b45aSDavid Schultz mxcsr = 0;
216741ae1d0SDavid Schultz omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
2179233b45aSDavid Schultz control |= mask;
218b451efbeSDimitry Andric __fldcw(&control);
2199233b45aSDavid Schultz if (__HAS_SSE()) {
2209233b45aSDavid Schultz mxcsr |= mask << _SSE_EMASK_SHIFT;
221b451efbeSDimitry Andric __ldmxcsr(&mxcsr);
2229233b45aSDavid Schultz }
223741ae1d0SDavid Schultz return (omask);
2249233b45aSDavid Schultz }
22590a83ac6SDavid Schultz
22690a83ac6SDavid Schultz __weak_reference(__feenableexcept, feenableexcept);
22790a83ac6SDavid Schultz __weak_reference(__fedisableexcept, fedisableexcept);
228