1 /* $OpenBSD: fenv.c,v 1.4 2014/04/18 15:09:52 guenther Exp $ */ 2 3 /* 4 * Copyright (c) 2011 Martynas Venckus <martynas@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <fenv.h> 20 21 union u { 22 unsigned long long fpsr; 23 unsigned int bits[2]; 24 }; 25 26 /* 27 * The following constant represents the default floating-point environment 28 * (that is, the one installed at program startup) and has type pointer to 29 * const-qualified fenv_t. 30 * 31 * It can be used as an argument to the functions within the <fenv.h> header 32 * that manage the floating-point environment, namely fesetenv() and 33 * feupdateenv(). 34 */ 35 fenv_t __fe_dfl_env = 0; 36 37 /* 38 * The feclearexcept() function clears the supported floating-point exceptions 39 * represented by `excepts'. 40 */ 41 int 42 feclearexcept(int excepts) 43 { 44 volatile union u u; 45 46 excepts &= FE_ALL_EXCEPT; 47 48 /* Store the current floating-point status register */ 49 __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) : 50 "r" (&u.fpsr)); 51 52 /* Clear the requested floating-point exceptions */ 53 u.bits[0] &= ~(excepts << _MASK_SHIFT); 54 55 /* Load the floating-point status register */ 56 __asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr)); 57 58 return (0); 59 } 60 61 /* 62 * The fegetexceptflag() function stores an implementation-defined 63 * representation of the states of the floating-point status flags indicated by 64 * the argument excepts in the object pointed to by the argument flagp. 65 */ 66 int 67 fegetexceptflag(fexcept_t *flagp, int excepts) 68 { 69 volatile union u u; 70 71 excepts &= FE_ALL_EXCEPT; 72 73 /* Store the current floating-point status register */ 74 __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) : 75 "r" (&u.fpsr)); 76 77 /* Store the results in flagp */ 78 *flagp = (u.bits[0] >> _MASK_SHIFT) & excepts; 79 80 return (0); 81 } 82 83 /* 84 * The feraiseexcept() function raises the supported floating-point exceptions 85 * represented by the argument `excepts'. 86 */ 87 int 88 feraiseexcept(int excepts) 89 { 90 volatile double d; 91 92 excepts &= FE_ALL_EXCEPT; 93 94 /* 95 * With a compiler that supports the FENV_ACCESS pragma 96 * properly, simple expressions like '0.0 / 0.0' should 97 * be sufficient to generate traps. Unfortunately, we 98 * need to bring a volatile variable into the equation 99 * to prevent incorrect optimizations. 100 */ 101 if (excepts & FE_INVALID) { 102 d = 0.0; 103 d = 0.0 / d; 104 } 105 if (excepts & FE_DIVBYZERO) { 106 d = 0.0; 107 d = 1.0 / d; 108 } 109 if (excepts & FE_OVERFLOW) { 110 d = 0x1.ffp1023; 111 d *= 2.0; 112 } 113 if (excepts & FE_UNDERFLOW) { 114 d = 0x1p-1022; 115 d /= 0x1p1023; 116 } 117 if (excepts & FE_INEXACT) { 118 d = 0x1p-1022; 119 d += 1.0; 120 } 121 __asm__ volatile ("fldd 0(%%sr0,%%sp), %0" : "=f" (d)); 122 123 return (0); 124 } 125 126 /* 127 * This function sets the floating-point status flags indicated by the argument 128 * `excepts' to the states stored in the object pointed to by `flagp'. It does 129 * NOT raise any floating-point exceptions, but only sets the state of the flags. 130 */ 131 int 132 fesetexceptflag(const fexcept_t *flagp, int excepts) 133 { 134 volatile union u u; 135 136 excepts &= FE_ALL_EXCEPT; 137 138 /* Store the current floating-point status register */ 139 __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) : 140 "r" (&u.fpsr)); 141 142 /* Set the requested status flags */ 143 u.bits[0] &= ~(excepts << _MASK_SHIFT); 144 u.bits[0] |= (*flagp & excepts) << _MASK_SHIFT; 145 146 /* Load the floating-point status register */ 147 __asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr)); 148 149 return (0); 150 } 151 152 /* 153 * The fetestexcept() function determines which of a specified subset of the 154 * floating-point exception flags are currently set. The `excepts' argument 155 * specifies the floating-point status flags to be queried. 156 */ 157 int 158 fetestexcept(int excepts) 159 { 160 volatile union u u; 161 162 excepts &= FE_ALL_EXCEPT; 163 164 /* Store the current floating-point status register */ 165 __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) : 166 "r" (&u.fpsr)); 167 168 return ((u.bits[0] >> _MASK_SHIFT) & excepts); 169 } 170 171 /* 172 * The fegetround() function gets the current rounding direction. 173 */ 174 int 175 fegetround(void) 176 { 177 volatile union u u; 178 179 /* Store the current floating-point status register */ 180 __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) : 181 "r" (&u.fpsr)); 182 183 return (u.bits[0] & _ROUND_MASK); 184 } 185 186 /* 187 * The fesetround() function establishes the rounding direction represented by 188 * its argument `round'. If the argument is not equal to the value of a rounding 189 * direction macro, the rounding direction is not changed. 190 */ 191 int 192 fesetround(int round) 193 { 194 volatile union u u; 195 196 /* Check whether requested rounding direction is supported */ 197 if (round & ~_ROUND_MASK) 198 return (-1); 199 200 /* Store the current floating-point status register */ 201 __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) : 202 "r" (&u.fpsr)); 203 204 /* Set the rounding direction */ 205 u.bits[0] &= ~_ROUND_MASK; 206 u.bits[0] |= round; 207 208 /* Load the floating-point status register */ 209 __asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr)); 210 211 return (0); 212 } 213 214 /* 215 * The fegetenv() function attempts to store the current floating-point 216 * environment in the object pointed to by envp. 217 */ 218 int 219 fegetenv(fenv_t *envp) 220 { 221 volatile union u u; 222 223 /* Store the current floating-point status register */ 224 __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) : 225 "r" (&u.fpsr)); 226 227 *envp = u.bits[0]; 228 229 return (0); 230 } 231 232 /* 233 * The feholdexcept() function saves the current floating-point environment 234 * in the object pointed to by envp, clears the floating-point status flags, and 235 * then installs a non-stop (continue on floating-point exceptions) mode, if 236 * available, for all floating-point exceptions. 237 */ 238 int 239 feholdexcept(fenv_t *envp) 240 { 241 volatile union u u; 242 243 /* Store the current floating-point status register */ 244 __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) : 245 "r" (&u.fpsr)); 246 247 *envp = u.bits[0]; 248 249 /* Clear exception flags in FPSR */ 250 u.bits[0] &= ~(FE_ALL_EXCEPT << _MASK_SHIFT); 251 252 /* Mask all exceptions */ 253 u.bits[0] &= ~FE_ALL_EXCEPT; 254 __asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr)); 255 256 return (0); 257 } 258 259 /* 260 * The fesetenv() function attempts to establish the floating-point environment 261 * represented by the object pointed to by envp. The argument `envp' points 262 * to an object set by a call to fegetenv() or feholdexcept(), or equal a 263 * floating-point environment macro. The fesetenv() function does not raise 264 * floating-point exceptions, but only installs the state of the floating-point 265 * status flags represented through its argument. 266 */ 267 int 268 fesetenv(const fenv_t *envp) 269 { 270 volatile union u u; 271 272 /* Store the current floating-point status register */ 273 __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) : 274 "r" (&u.fpsr)); 275 276 /* Set the requested flags */ 277 u.bits[0] &= ~(FE_ALL_EXCEPT | _ROUND_MASK | 278 (FE_ALL_EXCEPT << _MASK_SHIFT)); 279 u.bits[0] |= *envp & (FE_ALL_EXCEPT | _ROUND_MASK | 280 (FE_ALL_EXCEPT << _MASK_SHIFT)); 281 282 /* Load the floating-point status register */ 283 __asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr)); 284 285 return (0); 286 } 287 288 /* 289 * The feupdateenv() function saves the currently raised floating-point 290 * exceptions in its automatic storage, installs the floating-point environment 291 * represented by the object pointed to by `envp', and then raises the saved 292 * floating-point exceptions. The argument `envp' shall point to an object set 293 * by a call to feholdexcept() or fegetenv(), or equal a floating-point 294 * environment macro. 295 */ 296 int 297 feupdateenv(const fenv_t *envp) 298 { 299 volatile union u u; 300 301 /* Store the current floating-point status register */ 302 __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) : 303 "r" (&u.fpsr)); 304 305 /* Install new floating-point environment */ 306 fesetenv(envp); 307 308 /* Raise any previously accumulated exceptions */ 309 feraiseexcept(u.bits[0] >> _MASK_SHIFT); 310 311 return (0); 312 } 313 314 /* 315 * The following functions are extentions to the standard 316 */ 317 int 318 feenableexcept(int mask) 319 { 320 volatile union u u; 321 unsigned int omask; 322 323 mask &= FE_ALL_EXCEPT; 324 325 /* Store the current floating-point status register */ 326 __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) : 327 "r" (&u.fpsr)); 328 329 omask = u.bits[0] & FE_ALL_EXCEPT; 330 u.bits[0] |= mask; 331 332 /* Load the floating-point status register */ 333 __asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr)); 334 335 return (omask); 336 337 } 338 339 int 340 fedisableexcept(int mask) 341 { 342 volatile union u u; 343 unsigned int omask; 344 345 mask &= FE_ALL_EXCEPT; 346 347 /* Store the current floating-point status register */ 348 __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) : 349 "r" (&u.fpsr)); 350 351 omask = u.bits[0] & FE_ALL_EXCEPT; 352 u.bits[0] &= ~mask; 353 354 /* Load the floating-point status register */ 355 __asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr)); 356 357 return (omask); 358 } 359 360 int 361 fegetexcept(void) 362 { 363 volatile union u u; 364 365 /* Store the current floating-point status register */ 366 __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) : 367 "r" (&u.fpsr)); 368 369 return (u.bits[0] & FE_ALL_EXCEPT); 370 } 371