1 /* $OpenBSD: fenv.c,v 1.6 2022/12/27 17:10:07 jmc 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 /* 34 * The following constant represents the default floating-point environment 35 * (that is, the one installed at program startup) and has type pointer to 36 * const-qualified fenv_t. 37 * 38 * It can be used as an argument to the functions within the <fenv.h> header 39 * that manage the floating-point environment, namely fesetenv() and 40 * feupdateenv(). 41 * 42 * x87 fpu registers are 16bit wide. The upper bits, 31-16, are marked as 43 * RESERVED. 44 */ 45 fenv_t __fe_dfl_env = { 46 { 47 0xffff0000 | __INITIAL_NPXCW__, /* Control word register */ 48 0xffff0000, /* Status word register */ 49 0xffffffff, /* Tag word register */ 50 { 51 0x00000000, 52 0x00000000, 53 0x00000000, 54 0xffff0000 55 } 56 }, 57 __INITIAL_MXCSR__ /* MXCSR register */ 58 }; 59 60 61 /* 62 * The feclearexcept() function clears the supported floating-point exceptions 63 * represented by `excepts'. 64 */ 65 int 66 feclearexcept(int excepts) 67 { 68 fenv_t fenv; 69 unsigned int mxcsr; 70 71 excepts &= FE_ALL_EXCEPT; 72 73 /* Store the current x87 floating-point environment */ 74 __asm__ volatile ("fnstenv %0" : "=m" (fenv)); 75 76 /* Clear the requested floating-point exceptions */ 77 fenv.__x87.__status &= ~excepts; 78 79 /* Load the x87 floating-point environment */ 80 __asm__ volatile ("fldenv %0" : : "m" (fenv)); 81 82 /* Same for SSE environment */ 83 __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr)); 84 mxcsr &= ~excepts; 85 __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr)); 86 87 return (0); 88 } 89 DEF_STD(feclearexcept); 90 91 /* 92 * The fegetexceptflag() function stores an implementation-defined 93 * representation of the states of the floating-point status flags indicated by 94 * the argument excepts in the object pointed to by the argument flagp. 95 */ 96 int 97 fegetexceptflag(fexcept_t *flagp, int excepts) 98 { 99 unsigned short status; 100 unsigned int mxcsr; 101 102 excepts &= FE_ALL_EXCEPT; 103 104 /* Store the current x87 status register */ 105 __asm__ volatile ("fnstsw %0" : "=am" (status)); 106 107 /* Store the MXCSR register */ 108 __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr)); 109 110 /* Store the results in flagp */ 111 *flagp = (status | mxcsr) & excepts; 112 113 return (0); 114 } 115 116 /* 117 * The feraiseexcept() function raises the supported floating-point exceptions 118 * represented by the argument `excepts'. 119 * 120 * The standard explicitly allows us to execute an instruction that has the 121 * exception as a side effect, but we choose to manipulate the status register 122 * directly. 123 * 124 * The validation of input is being deferred to fesetexceptflag(). 125 */ 126 int 127 feraiseexcept(int excepts) 128 { 129 excepts &= FE_ALL_EXCEPT; 130 131 fesetexceptflag((fexcept_t *)&excepts, excepts); 132 __asm__ volatile ("fwait"); 133 134 return (0); 135 } 136 DEF_STD(feraiseexcept); 137 138 /* 139 * This function sets the floating-point status flags indicated by the argument 140 * `excepts' to the states stored in the object pointed to by `flagp'. It does 141 * NOT raise any floating-point exceptions, but only sets the state of the flags. 142 */ 143 int 144 fesetexceptflag(const fexcept_t *flagp, int excepts) 145 { 146 fenv_t fenv; 147 unsigned int mxcsr; 148 149 excepts &= FE_ALL_EXCEPT; 150 151 /* Store the current x87 floating-point environment */ 152 __asm__ volatile ("fnstenv %0" : "=m" (fenv)); 153 154 /* Set the requested status flags */ 155 fenv.__x87.__status &= ~excepts; 156 fenv.__x87.__status |= *flagp & excepts; 157 158 /* Load the x87 floating-point environment */ 159 __asm__ volatile ("fldenv %0" : : "m" (fenv)); 160 161 /* Same for SSE environment */ 162 __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr)); 163 mxcsr &= ~excepts; 164 mxcsr |= *flagp & excepts; 165 __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr)); 166 167 return (0); 168 } 169 DEF_STD(fesetexceptflag); 170 171 /* 172 * The fetestexcept() function determines which of a specified subset of the 173 * floating-point exception flags are currently set. The `excepts' argument 174 * specifies the floating-point status flags to be queried. 175 */ 176 int 177 fetestexcept(int excepts) 178 { 179 unsigned short status; 180 unsigned int mxcsr; 181 182 excepts &= FE_ALL_EXCEPT; 183 184 /* Store the current x87 status register */ 185 __asm__ volatile ("fnstsw %0" : "=am" (status)); 186 187 /* Store the MXCSR register state */ 188 __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr)); 189 190 return ((status | mxcsr) & excepts); 191 } 192 DEF_STD(fetestexcept); 193 194 /* 195 * The fegetround() function gets the current rounding direction. 196 */ 197 int 198 fegetround(void) 199 { 200 unsigned short control; 201 202 /* 203 * We assume that the x87 and the SSE unit agree on the 204 * rounding mode. Reading the control word on the x87 turns 205 * out to be about 5 times faster than reading it on the SSE 206 * unit on an Opteron 244. 207 */ 208 __asm__ volatile ("fnstcw %0" : "=m" (control)); 209 210 return (control & _X87_ROUND_MASK); 211 } 212 DEF_STD(fegetround); 213 214 /* 215 * The fesetround() function establishes the rounding direction represented by 216 * its argument `round'. If the argument is not equal to the value of a rounding 217 * direction macro, the rounding direction is not changed. 218 */ 219 int 220 fesetround(int round) 221 { 222 unsigned short control; 223 unsigned int mxcsr; 224 225 /* Check whether requested rounding direction is supported */ 226 if (round & ~_X87_ROUND_MASK) 227 return (-1); 228 229 /* Store the current x87 control word register */ 230 __asm__ volatile ("fnstcw %0" : "=m" (control)); 231 232 /* Set the rounding direction */ 233 control &= ~_X87_ROUND_MASK; 234 control |= round; 235 236 /* Load the x87 control word register */ 237 __asm__ volatile ("fldcw %0" : : "m" (control)); 238 239 /* Same for the SSE environment */ 240 __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr)); 241 mxcsr &= ~(_X87_ROUND_MASK << _SSE_ROUND_SHIFT); 242 mxcsr |= round << _SSE_ROUND_SHIFT; 243 __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr)); 244 245 return (0); 246 } 247 DEF_STD(fesetround); 248 249 /* 250 * The fegetenv() function attempts to store the current floating-point 251 * environment in the object pointed to by envp. 252 */ 253 int 254 fegetenv(fenv_t *envp) 255 { 256 /* Store the current x87 floating-point environment */ 257 __asm__ volatile ("fnstenv %0" : "=m" (*envp)); 258 259 /* Store the MXCSR register state */ 260 __asm__ volatile ("stmxcsr %0" : "=m" (envp->__mxcsr)); 261 262 /* 263 * When an FNSTENV instruction is executed, all pending exceptions are 264 * essentially lost (either the x87 FPU status register is cleared or 265 * all exceptions are masked). 266 * 267 * 8.6 X87 FPU EXCEPTION SYNCHRONIZATION - 268 * Intel(R) 64 and IA-32 Architectures Softare Developer's Manual - Vol1 269 */ 270 __asm__ volatile ("fldcw %0" : : "m" (envp->__x87.__control)); 271 272 return (0); 273 } 274 DEF_STD(fegetenv); 275 276 /* 277 * The feholdexcept() function saves the current floating-point environment 278 * in the object pointed to by envp, clears the floating-point status flags, and 279 * then installs a non-stop (continue on floating-point exceptions) mode, if 280 * available, for all floating-point exceptions. 281 */ 282 int 283 feholdexcept(fenv_t *envp) 284 { 285 unsigned int mxcsr; 286 287 /* Store the current x87 floating-point environment */ 288 __asm__ volatile ("fnstenv %0" : "=m" (*envp)); 289 290 /* Clear all exception flags in FPU */ 291 __asm__ volatile ("fnclex"); 292 293 /* Store the MXCSR register state */ 294 __asm__ volatile ("stmxcsr %0" : "=m" (envp->__mxcsr)); 295 296 /* Clear exception flags in MXCSR */ 297 mxcsr = envp->__mxcsr; 298 mxcsr &= ~FE_ALL_EXCEPT; 299 300 /* Mask all exceptions */ 301 mxcsr |= FE_ALL_EXCEPT << _SSE_MASK_SHIFT; 302 303 /* Store the MXCSR register */ 304 __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr)); 305 306 return (0); 307 } 308 DEF_STD(feholdexcept); 309 310 /* 311 * The fesetenv() function attempts to establish the floating-point environment 312 * represented by the object pointed to by envp. The argument `envp' points 313 * to an object set by a call to fegetenv() or feholdexcept(), or equal a 314 * floating-point environment macro. The fesetenv() function does not raise 315 * floating-point exceptions, but only installs the state of the floating-point 316 * status flags represented through its argument. 317 */ 318 int 319 fesetenv(const fenv_t *envp) 320 { 321 /* Load the x87 floating-point environment */ 322 __asm__ volatile ("fldenv %0" : : "m" (*envp)); 323 324 /* Store the MXCSR register */ 325 __asm__ volatile ("ldmxcsr %0" : : "m" (envp->__mxcsr)); 326 327 return (0); 328 } 329 DEF_STD(fesetenv); 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 DEF_STD(feupdateenv); 360 361 /* 362 * The following functions are extensions to the standard 363 */ 364 int 365 feenableexcept(int mask) 366 { 367 unsigned int mxcsr, omask; 368 unsigned short control; 369 370 mask &= FE_ALL_EXCEPT; 371 372 __asm__ volatile ("fnstcw %0" : "=m" (control)); 373 __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr)); 374 375 omask = ~(control | (mxcsr >> _SSE_MASK_SHIFT)) & FE_ALL_EXCEPT; 376 control &= ~mask; 377 __asm__ volatile ("fldcw %0" : : "m" (control)); 378 379 mxcsr &= ~(mask << _SSE_MASK_SHIFT); 380 __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr)); 381 382 return (omask); 383 } 384 385 int 386 fedisableexcept(int mask) 387 { 388 unsigned int mxcsr, omask; 389 unsigned short control; 390 391 mask &= FE_ALL_EXCEPT; 392 393 __asm__ volatile ("fnstcw %0" : "=m" (control)); 394 __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr)); 395 396 omask = ~(control | (mxcsr >> _SSE_MASK_SHIFT)) & FE_ALL_EXCEPT; 397 control |= mask; 398 __asm__ volatile ("fldcw %0" : : "m" (control)); 399 400 mxcsr |= mask << _SSE_MASK_SHIFT; 401 __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr)); 402 403 return (omask); 404 } 405 406 int 407 fegetexcept(void) 408 { 409 unsigned short control; 410 411 /* 412 * We assume that the masks for the x87 and the SSE unit are 413 * the same. 414 */ 415 __asm__ volatile ("fnstcw %0" : "=m" (control)); 416 417 return (~control & FE_ALL_EXCEPT); 418 } 419