10b71a226SDavid Schultz /*- 29233b45aSDavid Schultz * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG> 30b71a226SDavid Schultz * All rights reserved. 40b71a226SDavid Schultz * 50b71a226SDavid Schultz * Redistribution and use in source and binary forms, with or without 60b71a226SDavid Schultz * modification, are permitted provided that the following conditions 70b71a226SDavid Schultz * are met: 80b71a226SDavid Schultz * 1. Redistributions of source code must retain the above copyright 90b71a226SDavid Schultz * notice, this list of conditions and the following disclaimer. 100b71a226SDavid Schultz * 2. Redistributions in binary form must reproduce the above copyright 110b71a226SDavid Schultz * notice, this list of conditions and the following disclaimer in the 120b71a226SDavid Schultz * documentation and/or other materials provided with the distribution. 130b71a226SDavid Schultz * 140b71a226SDavid Schultz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 150b71a226SDavid Schultz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 160b71a226SDavid Schultz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 170b71a226SDavid Schultz * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 180b71a226SDavid Schultz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 190b71a226SDavid Schultz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 200b71a226SDavid Schultz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 210b71a226SDavid Schultz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 220b71a226SDavid Schultz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 230b71a226SDavid Schultz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 240b71a226SDavid Schultz * SUCH DAMAGE. 250b71a226SDavid Schultz * 260b71a226SDavid Schultz * $FreeBSD$ 270b71a226SDavid Schultz */ 280b71a226SDavid Schultz 299233b45aSDavid Schultz #include <sys/cdefs.h> 300b71a226SDavid Schultz #include <sys/types.h> 310b71a226SDavid Schultz #include <machine/npx.h> 32d78e594bSDavid Schultz 33d78e594bSDavid Schultz #define __fenv_static 34*5d9fefacSDavid Schultz #include "fenv.h" 35d78e594bSDavid Schultz 36d78e594bSDavid Schultz #ifdef __GNUC_GNU_INLINE__ 37d78e594bSDavid Schultz #error "This file must be compiled with C99 'inline' semantics" 38d78e594bSDavid Schultz #endif 390b71a226SDavid Schultz 400b71a226SDavid Schultz const fenv_t __fe_dfl_env = { 419233b45aSDavid Schultz __INITIAL_NPXCW__, 429233b45aSDavid Schultz 0x0000, 439233b45aSDavid Schultz 0x0000, 449233b45aSDavid Schultz 0x1f80, 450b71a226SDavid Schultz 0xffffffff, 460b71a226SDavid Schultz { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 470b71a226SDavid Schultz 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff } 480b71a226SDavid Schultz }; 499233b45aSDavid Schultz 509233b45aSDavid Schultz enum __sse_support __has_sse = 519233b45aSDavid Schultz #ifdef __SSE__ 529233b45aSDavid Schultz __SSE_YES; 539233b45aSDavid Schultz #else 549233b45aSDavid Schultz __SSE_UNK; 559233b45aSDavid Schultz #endif 569233b45aSDavid Schultz 579233b45aSDavid Schultz #define getfl(x) __asm __volatile("pushfl\n\tpopl %0" : "=mr" (*(x))) 589233b45aSDavid Schultz #define setfl(x) __asm __volatile("pushl %0\n\tpopfl" : : "g" (x)) 599233b45aSDavid Schultz #define cpuid_dx(x) __asm __volatile("pushl %%ebx\n\tmovl $1, %%eax\n\t" \ 609233b45aSDavid Schultz "cpuid\n\tpopl %%ebx" \ 619233b45aSDavid Schultz : "=d" (*(x)) : : "eax", "ecx") 629233b45aSDavid Schultz 639233b45aSDavid Schultz /* 649233b45aSDavid Schultz * Test for SSE support on this processor. We need to do this because 659233b45aSDavid Schultz * we need to use ldmxcsr/stmxcsr to get correct results if any part 669233b45aSDavid Schultz * of the program was compiled to use SSE floating-point, but we can't 679233b45aSDavid Schultz * use SSE on older processors. 689233b45aSDavid Schultz */ 699233b45aSDavid Schultz int 709233b45aSDavid Schultz __test_sse(void) 719233b45aSDavid Schultz { 729233b45aSDavid Schultz int flag, nflag; 739233b45aSDavid Schultz int dx_features; 749233b45aSDavid Schultz 759233b45aSDavid Schultz /* Am I a 486? */ 769233b45aSDavid Schultz getfl(&flag); 779233b45aSDavid Schultz nflag = flag ^ 0x200000; 789233b45aSDavid Schultz setfl(nflag); 799233b45aSDavid Schultz getfl(&nflag); 809233b45aSDavid Schultz if (flag != nflag) { 819233b45aSDavid Schultz /* Not a 486, so CPUID should work. */ 829233b45aSDavid Schultz cpuid_dx(&dx_features); 839233b45aSDavid Schultz if (dx_features & 0x2000000) { 849233b45aSDavid Schultz __has_sse = __SSE_YES; 859233b45aSDavid Schultz return (1); 869233b45aSDavid Schultz } 879233b45aSDavid Schultz } 889233b45aSDavid Schultz __has_sse = __SSE_NO; 899233b45aSDavid Schultz return (0); 909233b45aSDavid Schultz } 919233b45aSDavid Schultz 92d78e594bSDavid Schultz extern inline int feclearexcept(int __excepts); 93d78e594bSDavid Schultz extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts); 94d78e594bSDavid Schultz 959233b45aSDavid Schultz int 969233b45aSDavid Schultz fesetexceptflag(const fexcept_t *flagp, int excepts) 979233b45aSDavid Schultz { 989233b45aSDavid Schultz fenv_t env; 9960d818efSKonstantin Belousov __uint32_t mxcsr; 1009233b45aSDavid Schultz 1019233b45aSDavid Schultz __fnstenv(&env); 1029233b45aSDavid Schultz env.__status &= ~excepts; 1039233b45aSDavid Schultz env.__status |= *flagp & excepts; 1049233b45aSDavid Schultz __fldenv(env); 1059233b45aSDavid Schultz 1069233b45aSDavid Schultz if (__HAS_SSE()) { 1079233b45aSDavid Schultz __stmxcsr(&mxcsr); 1089233b45aSDavid Schultz mxcsr &= ~excepts; 1099233b45aSDavid Schultz mxcsr |= *flagp & excepts; 1109233b45aSDavid Schultz __ldmxcsr(mxcsr); 1119233b45aSDavid Schultz } 1129233b45aSDavid Schultz 1139233b45aSDavid Schultz return (0); 1149233b45aSDavid Schultz } 1159233b45aSDavid Schultz 1169233b45aSDavid Schultz int 1179233b45aSDavid Schultz feraiseexcept(int excepts) 1189233b45aSDavid Schultz { 1199233b45aSDavid Schultz fexcept_t ex = excepts; 1209233b45aSDavid Schultz 1219233b45aSDavid Schultz fesetexceptflag(&ex, excepts); 1229233b45aSDavid Schultz __fwait(); 1239233b45aSDavid Schultz return (0); 1249233b45aSDavid Schultz } 1259233b45aSDavid Schultz 126d78e594bSDavid Schultz extern inline int fetestexcept(int __excepts); 127d78e594bSDavid Schultz extern inline int fegetround(void); 128d78e594bSDavid Schultz extern inline int fesetround(int __round); 129d78e594bSDavid Schultz 1309233b45aSDavid Schultz int 1319233b45aSDavid Schultz fegetenv(fenv_t *envp) 1329233b45aSDavid Schultz { 13360d818efSKonstantin Belousov __uint32_t mxcsr; 1349233b45aSDavid Schultz 1359233b45aSDavid Schultz __fnstenv(envp); 1363cb636ceSDavid Schultz /* 1373cb636ceSDavid Schultz * fnstenv masks all exceptions, so we need to restore 1383cb636ceSDavid Schultz * the old control word to avoid this side effect. 1393cb636ceSDavid Schultz */ 1403cb636ceSDavid Schultz __fldcw(envp->__control); 1419233b45aSDavid Schultz if (__HAS_SSE()) { 1429233b45aSDavid Schultz __stmxcsr(&mxcsr); 1439233b45aSDavid Schultz __set_mxcsr(*envp, mxcsr); 1449233b45aSDavid Schultz } 1459233b45aSDavid Schultz return (0); 1469233b45aSDavid Schultz } 1479233b45aSDavid Schultz 1489233b45aSDavid Schultz int 1499233b45aSDavid Schultz feholdexcept(fenv_t *envp) 1509233b45aSDavid Schultz { 15160d818efSKonstantin Belousov __uint32_t mxcsr; 1529233b45aSDavid Schultz 1539233b45aSDavid Schultz __fnstenv(envp); 1549233b45aSDavid Schultz __fnclex(); 1559233b45aSDavid Schultz if (__HAS_SSE()) { 1569233b45aSDavid Schultz __stmxcsr(&mxcsr); 1579233b45aSDavid Schultz __set_mxcsr(*envp, mxcsr); 1589233b45aSDavid Schultz mxcsr &= ~FE_ALL_EXCEPT; 1599233b45aSDavid Schultz mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT; 1609233b45aSDavid Schultz __ldmxcsr(mxcsr); 1619233b45aSDavid Schultz } 1629233b45aSDavid Schultz return (0); 1639233b45aSDavid Schultz } 1649233b45aSDavid Schultz 165d78e594bSDavid Schultz extern inline int fesetenv(const fenv_t *__envp); 166d78e594bSDavid Schultz 1679233b45aSDavid Schultz int 1689233b45aSDavid Schultz feupdateenv(const fenv_t *envp) 1699233b45aSDavid Schultz { 17060d818efSKonstantin Belousov __uint32_t mxcsr; 17160d818efSKonstantin Belousov __uint16_t status; 1729233b45aSDavid Schultz 1739233b45aSDavid Schultz __fnstsw(&status); 1749233b45aSDavid Schultz if (__HAS_SSE()) 1759233b45aSDavid Schultz __stmxcsr(&mxcsr); 1769233b45aSDavid Schultz else 1779233b45aSDavid Schultz mxcsr = 0; 1789233b45aSDavid Schultz fesetenv(envp); 1799233b45aSDavid Schultz feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT); 1809233b45aSDavid Schultz return (0); 1819233b45aSDavid Schultz } 1829233b45aSDavid Schultz 1839233b45aSDavid Schultz int 1849233b45aSDavid Schultz __feenableexcept(int mask) 1859233b45aSDavid Schultz { 18660d818efSKonstantin Belousov __uint32_t mxcsr, omask; 18760d818efSKonstantin Belousov __uint16_t control; 1889233b45aSDavid Schultz 1899233b45aSDavid Schultz mask &= FE_ALL_EXCEPT; 1909233b45aSDavid Schultz __fnstcw(&control); 1919233b45aSDavid Schultz if (__HAS_SSE()) 1929233b45aSDavid Schultz __stmxcsr(&mxcsr); 1939233b45aSDavid Schultz else 1949233b45aSDavid Schultz mxcsr = 0; 1959233b45aSDavid Schultz omask = (control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT; 1969233b45aSDavid Schultz control &= ~mask; 1979233b45aSDavid Schultz __fldcw(control); 1989233b45aSDavid Schultz if (__HAS_SSE()) { 1999233b45aSDavid Schultz mxcsr &= ~(mask << _SSE_EMASK_SHIFT); 2009233b45aSDavid Schultz __ldmxcsr(mxcsr); 2019233b45aSDavid Schultz } 2029233b45aSDavid Schultz return (~omask); 2039233b45aSDavid Schultz } 2049233b45aSDavid Schultz 2059233b45aSDavid Schultz int 2069233b45aSDavid Schultz __fedisableexcept(int mask) 2079233b45aSDavid Schultz { 20860d818efSKonstantin Belousov __uint32_t mxcsr, omask; 20960d818efSKonstantin Belousov __uint16_t control; 2109233b45aSDavid Schultz 2119233b45aSDavid Schultz mask &= FE_ALL_EXCEPT; 2129233b45aSDavid Schultz __fnstcw(&control); 2139233b45aSDavid Schultz if (__HAS_SSE()) 2149233b45aSDavid Schultz __stmxcsr(&mxcsr); 2159233b45aSDavid Schultz else 2169233b45aSDavid Schultz mxcsr = 0; 2179233b45aSDavid Schultz omask = (control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT; 2189233b45aSDavid Schultz control |= mask; 2199233b45aSDavid Schultz __fldcw(control); 2209233b45aSDavid Schultz if (__HAS_SSE()) { 2219233b45aSDavid Schultz mxcsr |= mask << _SSE_EMASK_SHIFT; 2229233b45aSDavid Schultz __ldmxcsr(mxcsr); 2239233b45aSDavid Schultz } 2249233b45aSDavid Schultz return (~omask); 2259233b45aSDavid Schultz } 22690a83ac6SDavid Schultz 22790a83ac6SDavid Schultz __weak_reference(__feenableexcept, feenableexcept); 22890a83ac6SDavid Schultz __weak_reference(__fedisableexcept, fedisableexcept); 229