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