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