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