1 /*- 2 * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: src/lib/msun/i387/fenv.c,v 1.8 2011/10/21 06:25:31 das Exp $ 27 */ 28 29 #include <include/cdefs-compat.h> 30 #include <include/types-compat.h> 31 #ifdef __WIN32__ 32 #include <i387/bsd_npx.h> 33 #else 34 #include <machine/npx.h> 35 #endif 36 37 #define __fenv_static 38 #include "fenv.h" 39 40 #ifdef __GNUC_GNU_INLINE__ 41 #error "This file must be compiled with C99 'inline' semantics" 42 #endif 43 44 const fenv_t __fe_dfl_env = { 45 __INITIAL_NPXCW__, 46 0x0000, 47 0x0000, 48 0x1f80, 49 0xffffffff, 50 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 51 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff } 52 }; 53 54 enum __sse_support __has_sse = 55 #ifdef __SSE__ 56 __SSE_YES; 57 #else 58 __SSE_UNK; 59 #endif 60 61 #define getfl(x) __asm __volatile("pushfl\n\tpopl %0" : "=mr" (*(x))) 62 #define setfl(x) __asm __volatile("pushl %0\n\tpopfl" : : "g" (x)) 63 #define cpuid_dx(x) __asm __volatile("pushl %%ebx\n\tmovl $1, %%eax\n\t" \ 64 "cpuid\n\tpopl %%ebx" \ 65 : "=d" (*(x)) : : "eax", "ecx") 66 67 /* 68 * Test for SSE support on this processor. We need to do this because 69 * we need to use ldmxcsr/stmxcsr to get correct results if any part 70 * of the program was compiled to use SSE floating-point, but we can't 71 * use SSE on older processors. 72 */ 73 int 74 __test_sse(void) 75 { 76 int flag, nflag; 77 int dx_features; 78 79 /* Am I a 486? */ 80 getfl(&flag); 81 nflag = flag ^ 0x200000; 82 setfl(nflag); 83 getfl(&nflag); 84 if (flag != nflag) { 85 /* Not a 486, so CPUID should work. */ 86 cpuid_dx(&dx_features); 87 if (dx_features & 0x2000000) { 88 __has_sse = __SSE_YES; 89 return (1); 90 } 91 } 92 __has_sse = __SSE_NO; 93 return (0); 94 } 95 96 extern inline int feclearexcept(int __excepts); 97 extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts); 98 99 int 100 fesetexceptflag(const fexcept_t *flagp, int excepts) 101 { 102 fenv_t env; 103 uint32_t mxcsr; 104 105 __fnstenv(&env); 106 env.__status &= ~excepts; 107 env.__status |= *flagp & excepts; 108 __fldenv(env); 109 110 if (__HAS_SSE()) { 111 __stmxcsr(&mxcsr); 112 mxcsr &= ~excepts; 113 mxcsr |= *flagp & excepts; 114 __ldmxcsr(mxcsr); 115 } 116 117 return (0); 118 } 119 120 int 121 feraiseexcept(int excepts) 122 { 123 fexcept_t ex = excepts; 124 125 fesetexceptflag(&ex, excepts); 126 __fwait(); 127 return (0); 128 } 129 130 extern inline int fetestexcept(int __excepts); 131 extern inline int fegetround(void); 132 extern inline int fesetround(int __round); 133 134 int 135 fegetenv(fenv_t *envp) 136 { 137 uint32_t mxcsr; 138 139 __fnstenv(envp); 140 /* 141 * fnstenv masks all exceptions, so we need to restore 142 * the old control word to avoid this side effect. 143 */ 144 __fldcw(envp->__control); 145 if (__HAS_SSE()) { 146 __stmxcsr(&mxcsr); 147 __set_mxcsr(*envp, mxcsr); 148 } 149 return (0); 150 } 151 152 int 153 feholdexcept(fenv_t *envp) 154 { 155 uint32_t mxcsr; 156 157 __fnstenv(envp); 158 __fnclex(); 159 if (__HAS_SSE()) { 160 __stmxcsr(&mxcsr); 161 __set_mxcsr(*envp, mxcsr); 162 mxcsr &= ~FE_ALL_EXCEPT; 163 mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT; 164 __ldmxcsr(mxcsr); 165 } 166 return (0); 167 } 168 169 extern inline int fesetenv(const fenv_t *__envp); 170 171 int 172 feupdateenv(const fenv_t *envp) 173 { 174 uint32_t mxcsr; 175 uint16_t status; 176 177 __fnstsw(&status); 178 if (__HAS_SSE()) 179 __stmxcsr(&mxcsr); 180 else 181 mxcsr = 0; 182 fesetenv(envp); 183 feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT); 184 return (0); 185 } 186 187 int 188 __feenableexcept(int mask) 189 { 190 uint32_t mxcsr, omask; 191 uint16_t control; 192 193 mask &= FE_ALL_EXCEPT; 194 __fnstcw(&control); 195 if (__HAS_SSE()) 196 __stmxcsr(&mxcsr); 197 else 198 mxcsr = 0; 199 omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT; 200 control &= ~mask; 201 __fldcw(control); 202 if (__HAS_SSE()) { 203 mxcsr &= ~(mask << _SSE_EMASK_SHIFT); 204 __ldmxcsr(mxcsr); 205 } 206 return (omask); 207 } 208 209 int 210 __fedisableexcept(int mask) 211 { 212 uint32_t mxcsr, omask; 213 uint16_t control; 214 215 mask &= FE_ALL_EXCEPT; 216 __fnstcw(&control); 217 if (__HAS_SSE()) 218 __stmxcsr(&mxcsr); 219 else 220 mxcsr = 0; 221 omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT; 222 control |= mask; 223 __fldcw(control); 224 if (__HAS_SSE()) { 225 mxcsr |= mask << _SSE_EMASK_SHIFT; 226 __ldmxcsr(mxcsr); 227 } 228 return (omask); 229 } 230 231 __weak_reference(__feenableexcept, feenableexcept); 232 __weak_reference(__fedisableexcept, fedisableexcept); 233