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