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