1 /* $OpenBSD: fenv.c,v 1.3 2012/12/05 23:20:02 deraadt Exp $ */ 2 /* $NetBSD: fenv.c,v 1.1 2010/07/31 21:47:53 joerg Exp $ */ 3 4 /*- 5 * Copyright (c) 2004-2005 David Schultz <das (at) FreeBSD.ORG> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <fenv.h> 31 #include <machine/fpu.h> 32 33 #define SSE_MASK_SHIFT 7 34 35 /* 36 * The following symbol is simply the bitwise-inclusive OR of all floating-point 37 * rounding direction constants defined above. 38 */ 39 #define X87_ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | FE_UPWARD | FE_TOWARDZERO) 40 #define SSE_ROUND_SHIFT 3 41 42 /* 43 * The following constant represents the default floating-point environment 44 * (that is, the one installed at program startup) and has type pointer to 45 * const-qualified fenv_t. 46 * 47 * It can be used as an argument to the functions within the <fenv.h> header 48 * that manage the floating-point environment, namely fesetenv() and 49 * feupdateenv(). 50 * 51 * x87 fpu registers are 16bit wide. The upper bits, 31-16, are marked as 52 * RESERVED. 53 */ 54 const fenv_t __fe_dfl_env = { 55 { 56 0xffff0000 | __INITIAL_NPXCW__, /* Control word register */ 57 0xffff0000, /* Status word register */ 58 0xffffffff, /* Tag word register */ 59 { 60 0x00000000, 61 0x00000000, 62 0x00000000, 63 0xffff0000 64 } 65 }, 66 __INITIAL_MXCSR__ /* MXCSR register */ 67 }; 68 69 70 /* 71 * The feclearexcept() function clears the supported floating-point exceptions 72 * represented by `excepts'. 73 */ 74 int 75 feclearexcept(int excepts) 76 { 77 fenv_t fenv; 78 unsigned int mxcsr; 79 80 excepts &= FE_ALL_EXCEPT; 81 82 /* Store the current x87 floating-point environment */ 83 __asm__ __volatile__ ("fnstenv %0" : "=m" (fenv)); 84 85 /* Clear the requested floating-point exceptions */ 86 fenv.__x87.__status &= ~excepts; 87 88 /* Load the x87 floating-point environent */ 89 __asm__ __volatile__ ("fldenv %0" : : "m" (fenv)); 90 91 /* Same for SSE environment */ 92 __asm__ __volatile__ ("stmxcsr %0" : "=m" (mxcsr)); 93 mxcsr &= ~excepts; 94 __asm__ __volatile__ ("ldmxcsr %0" : : "m" (mxcsr)); 95 96 return (0); 97 } 98 99 /* 100 * The fegetexceptflag() function stores an implementation-defined 101 * representation of the states of the floating-point status flags indicated by 102 * the argument excepts in the object pointed to by the argument flagp. 103 */ 104 int 105 fegetexceptflag(fexcept_t *flagp, int excepts) 106 { 107 unsigned short status; 108 unsigned int mxcsr; 109 110 excepts &= FE_ALL_EXCEPT; 111 112 /* Store the current x87 status register */ 113 __asm__ __volatile__ ("fnstsw %0" : "=am" (status)); 114 115 /* Store the MXCSR register */ 116 __asm__ __volatile__ ("stmxcsr %0" : "=m" (mxcsr)); 117 118 /* Store the results in flagp */ 119 *flagp = (status | mxcsr) & excepts; 120 121 return (0); 122 } 123 124 /* 125 * The feraiseexcept() function raises the supported floating-point exceptions 126 * represented by the argument `excepts'. 127 * 128 * The standard explicitly allows us to execute an instruction that has the 129 * exception as a side effect, but we choose to manipulate the status register 130 * directly. 131 * 132 * The validation of input is being deferred to fesetexceptflag(). 133 */ 134 int 135 feraiseexcept(int excepts) 136 { 137 excepts &= FE_ALL_EXCEPT; 138 139 fesetexceptflag((fexcept_t *)&excepts, excepts); 140 __asm__ __volatile__ ("fwait"); 141 142 return (0); 143 } 144 145 /* 146 * This function sets the floating-point status flags indicated by the argument 147 * `excepts' to the states stored in the object pointed to by `flagp'. It does 148 * NOT raise any floating-point exceptions, but only sets the state of the flags. 149 */ 150 int 151 fesetexceptflag(const fexcept_t *flagp, int excepts) 152 { 153 fenv_t fenv; 154 unsigned int mxcsr; 155 156 excepts &= FE_ALL_EXCEPT; 157 158 /* Store the current x87 floating-point environment */ 159 __asm__ __volatile__ ("fnstenv %0" : "=m" (fenv)); 160 161 /* Set the requested status flags */ 162 fenv.__x87.__status &= ~excepts; 163 fenv.__x87.__status |= *flagp & excepts; 164 165 /* Load the x87 floating-point environent */ 166 __asm__ __volatile__ ("fldenv %0" : : "m" (fenv)); 167 168 /* Same for SSE environment */ 169 __asm__ __volatile__ ("stmxcsr %0" : "=m" (mxcsr)); 170 mxcsr &= ~excepts; 171 mxcsr |= *flagp & excepts; 172 __asm__ __volatile__ ("ldmxcsr %0" : : "m" (mxcsr)); 173 174 return (0); 175 } 176 177 /* 178 * The fetestexcept() function determines which of a specified subset of the 179 * floating-point exception flags are currently set. The `excepts' argument 180 * specifies the floating-point status flags to be queried. 181 */ 182 int 183 fetestexcept(int excepts) 184 { 185 unsigned short status; 186 unsigned int mxcsr; 187 188 excepts &= FE_ALL_EXCEPT; 189 190 /* Store the current x87 status register */ 191 __asm__ __volatile__ ("fnstsw %0" : "=am" (status)); 192 193 /* Store the MXCSR register state */ 194 __asm__ __volatile__ ("stmxcsr %0" : "=m" (mxcsr)); 195 196 return ((status | mxcsr) & excepts); 197 } 198 199 /* 200 * The fegetround() function gets the current rounding direction. 201 */ 202 int 203 fegetround(void) 204 { 205 unsigned short control; 206 207 /* 208 * We assume that the x87 and the SSE unit agree on the 209 * rounding mode. Reading the control word on the x87 turns 210 * out to be about 5 times faster than reading it on the SSE 211 * unit on an Opteron 244. 212 */ 213 __asm__ __volatile__ ("fnstcw %0" : "=m" (control)); 214 215 return (control & X87_ROUND_MASK); 216 } 217 218 /* 219 * The fesetround() function establishes the rounding direction represented by 220 * its argument `round'. If the argument is not equal to the value of a rounding 221 * direction macro, the rounding direction is not changed. 222 */ 223 int 224 fesetround(int round) 225 { 226 unsigned short control; 227 unsigned int mxcsr; 228 229 /* Check whether requested rounding direction is supported */ 230 if (round & ~X87_ROUND_MASK) 231 return (-1); 232 233 /* Store the current x87 control word register */ 234 __asm__ __volatile__ ("fnstcw %0" : "=m" (control)); 235 236 /* Set the rounding direction */ 237 control &= ~X87_ROUND_MASK; 238 control |= round; 239 240 /* Load the x87 control word register */ 241 __asm__ __volatile__ ("fldcw %0" : : "m" (control)); 242 243 /* Same for the SSE environment */ 244 __asm__ __volatile__ ("stmxcsr %0" : "=m" (mxcsr)); 245 mxcsr &= ~(X87_ROUND_MASK << SSE_ROUND_SHIFT); 246 mxcsr |= round << SSE_ROUND_SHIFT; 247 __asm__ __volatile__ ("ldmxcsr %0" : : "m" (mxcsr)); 248 249 return (0); 250 } 251 252 /* 253 * The fegetenv() function attempts to store the current floating-point 254 * environment in the object pointed to by envp. 255 */ 256 int 257 fegetenv(fenv_t *envp) 258 { 259 /* Store the current x87 floating-point environment */ 260 __asm__ __volatile__ ("fnstenv %0" : "=m" (*envp)); 261 262 /* Store the MXCSR register state */ 263 __asm__ __volatile__ ("stmxcsr %0" : "=m" (envp->__mxcsr)); 264 265 /* 266 * When an FNSTENV instruction is executed, all pending exceptions are 267 * essentially lost (either the x87 FPU status register is cleared or 268 * all exceptions are masked). 269 * 270 * 8.6 X87 FPU EXCEPTION SYNCHRONIZATION - 271 * Intel(R) 64 and IA-32 Architectures Softare Developer's Manual - Vol1 272 */ 273 __asm__ __volatile__ ("fldcw %0" : : "m" (envp->__x87.__control)); 274 275 return (0); 276 } 277 278 /* 279 * The feholdexcept() function saves the current floating-point environment 280 * in the object pointed to by envp, clears the floating-point status flags, and 281 * then installs a non-stop (continue on floating-point exceptions) mode, if 282 * available, for all floating-point exceptions. 283 */ 284 int 285 feholdexcept(fenv_t *envp) 286 { 287 unsigned int mxcsr; 288 289 /* Store the current x87 floating-point environment */ 290 __asm__ __volatile__ ("fnstenv %0" : "=m" (*envp)); 291 292 /* Clear all exception flags in FPU */ 293 __asm__ __volatile__ ("fnclex"); 294 295 /* Store the MXCSR register state */ 296 __asm__ __volatile__ ("stmxcsr %0" : "=m" (envp->__mxcsr)); 297 298 /* Clear exception flags in MXCSR */ 299 mxcsr = envp->__mxcsr; 300 mxcsr &= ~FE_ALL_EXCEPT; 301 302 /* Mask all exceptions */ 303 mxcsr |= FE_ALL_EXCEPT << SSE_MASK_SHIFT; 304 305 /* Store the MXCSR register */ 306 __asm__ __volatile__ ("ldmxcsr %0" : : "m" (mxcsr)); 307 308 return (0); 309 } 310 311 /* 312 * The fesetenv() function attempts to establish the floating-point environment 313 * represented by the object pointed to by envp. The argument `envp' points 314 * to an object set by a call to fegetenv() or feholdexcept(), or equal a 315 * floating-point environment macro. The fesetenv() function does not raise 316 * floating-point exceptions, but only installs the state of the floating-point 317 * status flags represented through its argument. 318 */ 319 int 320 fesetenv(const fenv_t *envp) 321 { 322 /* Load the x87 floating-point environent */ 323 __asm__ __volatile__ ("fldenv %0" : : "m" (*envp)); 324 325 /* Store the MXCSR register */ 326 __asm__ __volatile__ ("ldmxcsr %0" : : "m" (envp->__mxcsr)); 327 328 return (0); 329 } 330 331 /* 332 * The feupdateenv() function saves the currently raised floating-point 333 * exceptions in its automatic storage, installs the floating-point environment 334 * represented by the object pointed to by `envp', and then raises the saved 335 * floating-point exceptions. The argument `envp' shall point to an object set 336 * by a call to feholdexcept() or fegetenv(), or equal a floating-point 337 * environment macro. 338 */ 339 int 340 feupdateenv(const fenv_t *envp) 341 { 342 unsigned short status; 343 unsigned int mxcsr; 344 345 /* Store the x87 status register */ 346 __asm__ __volatile__ ("fnstsw %0" : "=am" (status)); 347 348 /* Store the MXCSR register */ 349 __asm__ __volatile__ ("stmxcsr %0" : "=m" (mxcsr)); 350 351 /* Install new floating-point environment */ 352 fesetenv(envp); 353 354 /* Raise any previously accumulated exceptions */ 355 feraiseexcept(status | mxcsr); 356 357 return (0); 358 } 359 360 /* 361 * The following functions are extentions to the standard 362 */ 363 int 364 feenableexcept(int mask) 365 { 366 unsigned int mxcsr, omask; 367 unsigned short control; 368 369 mask &= FE_ALL_EXCEPT; 370 371 __asm__ __volatile__ ("fnstcw %0" : "=m" (control)); 372 __asm__ __volatile__ ("stmxcsr %0" : "=m" (mxcsr)); 373 374 omask = ~(control | (mxcsr >> SSE_MASK_SHIFT)) & FE_ALL_EXCEPT; 375 control &= ~mask; 376 __asm__ __volatile__ ("fldcw %0" : : "m" (control)); 377 378 mxcsr &= ~(mask << SSE_MASK_SHIFT); 379 __asm__ __volatile__ ("ldmxcsr %0" : : "m" (mxcsr)); 380 381 return (omask); 382 } 383 384 int 385 fedisableexcept(int mask) 386 { 387 unsigned int mxcsr, omask; 388 unsigned short control; 389 390 mask &= FE_ALL_EXCEPT; 391 392 __asm__ __volatile__ ("fnstcw %0" : "=m" (control)); 393 __asm__ __volatile__ ("stmxcsr %0" : "=m" (mxcsr)); 394 395 omask = ~(control | (mxcsr >> SSE_MASK_SHIFT)) & FE_ALL_EXCEPT; 396 control |= mask; 397 __asm__ __volatile__ ("fldcw %0" : : "m" (control)); 398 399 mxcsr |= mask << SSE_MASK_SHIFT; 400 __asm__ __volatile__ ("ldmxcsr %0" : : "m" (mxcsr)); 401 402 return (omask); 403 } 404 405 int 406 fegetexcept(void) 407 { 408 unsigned short control; 409 410 /* 411 * We assume that the masks for the x87 and the SSE unit are 412 * the same. 413 */ 414 __asm__ __volatile__ ("fnstcw %0" : "=m" (control)); 415 416 return (~control & FE_ALL_EXCEPT); 417 } 418