1/* $OpenBSD: fenv.c,v 1.6 2022/12/27 17:10:07 jmc 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 21extern unsigned int __fpscr_values[2]; 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 */ 32fenv_t __fe_dfl_env = 0xc0000; 33 34/* 35 * The feclearexcept() function clears the supported floating-point exceptions 36 * represented by `excepts'. 37 */ 38int 39feclearexcept(int excepts) 40{ 41 unsigned int fpscr; 42 43 excepts &= FE_ALL_EXCEPT; 44 45 /* Store the current floating-point status and control register */ 46 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr)); 47 48 /* Clear the requested floating-point exceptions */ 49 fpscr &= ~excepts; 50 __fpscr_values[0] &= ~excepts; 51 __fpscr_values[1] &= ~excepts; 52 53 /* Load the floating-point status and control register */ 54 __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr)); 55 56 return (0); 57} 58DEF_STD(feclearexcept); 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 */ 65int 66fegetexceptflag(fexcept_t *flagp, int excepts) 67{ 68 unsigned int fpscr; 69 70 excepts &= FE_ALL_EXCEPT; 71 72 /* Store the current floating-point status and control register */ 73 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr)); 74 75 /* Store the results in flagp */ 76 *flagp = fpscr & 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 */ 85int 86feraiseexcept(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 = 0x1p-1022; 113 d /= 0x1p1023; 114 } 115 if (excepts & FE_INEXACT) { 116 d = 0x1p-1022; 117 d += 1.0; 118 } 119 120 return (0); 121} 122DEF_STD(feraiseexcept); 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 */ 129int 130fesetexceptflag(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} 153DEF_STD(fesetexceptflag); 154 155/* 156 * The fetestexcept() function determines which of a specified subset of the 157 * floating-point exception flags are currently set. The `excepts' argument 158 * specifies the floating-point status flags to be queried. 159 */ 160int 161fetestexcept(int excepts) 162{ 163 unsigned int fpscr; 164 165 excepts &= FE_ALL_EXCEPT; 166 167 /* Store the current floating-point status and control register */ 168 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr)); 169 170 return (fpscr & excepts); 171} 172DEF_STD(fetestexcept); 173 174/* 175 * The fegetround() function gets the current rounding direction. 176 */ 177int 178fegetround(void) 179{ 180 unsigned int fpscr; 181 182 /* Store the current floating-point status and control register */ 183 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr)); 184 185 return (fpscr & _ROUND_MASK); 186} 187DEF_STD(fegetround); 188 189/* 190 * The fesetround() function establishes the rounding direction represented by 191 * its argument `round'. If the argument is not equal to the value of a rounding 192 * direction macro, the rounding direction is not changed. 193 */ 194int 195fesetround(int round) 196{ 197 unsigned int fpscr; 198 199 /* Check whether requested rounding direction is supported */ 200 if (round & ~_ROUND_MASK) 201 return (-1); 202 203 /* Store the current floating-point status and control register */ 204 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr)); 205 206 /* Set the rounding direction */ 207 fpscr &= ~_ROUND_MASK; 208 __fpscr_values[0] &= ~_ROUND_MASK; 209 __fpscr_values[1] &= ~_ROUND_MASK; 210 211 fpscr |= round; 212 __fpscr_values[0] |= round; 213 __fpscr_values[1] |= round; 214 215 /* Load the floating-point status and control register */ 216 __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr)); 217 218 return (0); 219} 220DEF_STD(fesetround); 221 222/* 223 * The fegetenv() function attempts to store the current floating-point 224 * environment in the object pointed to by envp. 225 */ 226int 227fegetenv(fenv_t *envp) 228{ 229 /* Store the current floating-point status and control register */ 230 __asm__ volatile ("sts fpscr, %0" : "=r" (*envp)); 231 232 return (0); 233} 234DEF_STD(fegetenv); 235 236/* 237 * The feholdexcept() function saves the current floating-point environment 238 * in the object pointed to by envp, clears the floating-point status flags, and 239 * then installs a non-stop (continue on floating-point exceptions) mode, if 240 * available, for all floating-point exceptions. 241 */ 242int 243feholdexcept(fenv_t *envp) 244{ 245 unsigned int fpscr; 246 247 /* Store the current floating-point status and control register */ 248 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr)); 249 250 *envp = fpscr; 251 252 /* Clear exception flags in FPSCR */ 253 fpscr &= ~FE_ALL_EXCEPT; 254 __fpscr_values[0] &= ~FE_ALL_EXCEPT; 255 __fpscr_values[1] &= ~FE_ALL_EXCEPT; 256 257 /* Mask all exceptions */ 258 fpscr &= ~(FE_ALL_EXCEPT << _MASK_SHIFT); 259 __fpscr_values[0] &= ~(FE_ALL_EXCEPT << _MASK_SHIFT); 260 __fpscr_values[1] &= ~(FE_ALL_EXCEPT << _MASK_SHIFT); 261 262 /* Load the floating-point status and control register */ 263 __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr)); 264 265 return (0); 266} 267DEF_STD(feholdexcept); 268 269/* 270 * The fesetenv() function attempts to establish the floating-point environment 271 * represented by the object pointed to by envp. The argument `envp' points 272 * to an object set by a call to fegetenv() or feholdexcept(), or equal a 273 * floating-point environment macro. The fesetenv() function does not raise 274 * floating-point exceptions, but only installs the state of the floating-point 275 * status flags represented through its argument. 276 */ 277int 278fesetenv(const fenv_t *envp) 279{ 280 unsigned int fpscr; 281 282 /* Store the current floating-point status and control register */ 283 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr)); 284 285 /* Set the requested flags */ 286 fpscr &= ~((FE_ALL_EXCEPT << _MASK_SHIFT) | _ROUND_MASK | 287 FE_ALL_EXCEPT); 288 __fpscr_values[0] &= ~((FE_ALL_EXCEPT << _MASK_SHIFT) | _ROUND_MASK | 289 FE_ALL_EXCEPT); 290 __fpscr_values[1] &= ~((FE_ALL_EXCEPT << _MASK_SHIFT) | _ROUND_MASK | 291 FE_ALL_EXCEPT); 292 293 fpscr |= *envp & ((FE_ALL_EXCEPT << _MASK_SHIFT) | _ROUND_MASK | 294 FE_ALL_EXCEPT); 295 __fpscr_values[0] |= *envp & ((FE_ALL_EXCEPT << _MASK_SHIFT) | 296 _ROUND_MASK | FE_ALL_EXCEPT); 297 __fpscr_values[1] |= *envp & ((FE_ALL_EXCEPT << _MASK_SHIFT) | 298 _ROUND_MASK | FE_ALL_EXCEPT); 299 300 /* Load the floating-point status and control register */ 301 __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr)); 302 303 return (0); 304} 305DEF_STD(fesetenv); 306 307/* 308 * The feupdateenv() function saves the currently raised floating-point 309 * exceptions in its automatic storage, installs the floating-point environment 310 * represented by the object pointed to by `envp', and then raises the saved 311 * floating-point exceptions. The argument `envp' shall point to an object set 312 * by a call to feholdexcept() or fegetenv(), or equal a floating-point 313 * environment macro. 314 */ 315int 316feupdateenv(const fenv_t *envp) 317{ 318 unsigned int fpscr; 319 320 /* Store the current floating-point status and control register */ 321 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr)); 322 323 /* Install new floating-point environment */ 324 fesetenv(envp); 325 326 /* Raise any previously accumulated exceptions */ 327 feraiseexcept(fpscr); 328 329 return (0); 330} 331DEF_STD(feupdateenv); 332 333/* 334 * The following functions are extensions to the standard 335 */ 336int 337feenableexcept(int mask) 338{ 339 unsigned int fpscr, omask; 340 341 mask &= FE_ALL_EXCEPT; 342 343 /* Store the current floating-point status and control register */ 344 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr)); 345 346 omask = (fpscr >> _MASK_SHIFT) & FE_ALL_EXCEPT; 347 fpscr |= mask << _MASK_SHIFT; 348 __fpscr_values[0] |= mask << _MASK_SHIFT; 349 __fpscr_values[1] |= mask << _MASK_SHIFT; 350 351 /* Load the floating-point status and control register */ 352 __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr)); 353 354 return (omask); 355 356} 357 358int 359fedisableexcept(int mask) 360{ 361 unsigned int fpscr, omask; 362 363 mask &= FE_ALL_EXCEPT; 364 365 /* Store the current floating-point status and control register */ 366 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr)); 367 368 omask = (fpscr >> _MASK_SHIFT) & FE_ALL_EXCEPT; 369 fpscr &= ~(mask << _MASK_SHIFT); 370 __fpscr_values[0] &= ~(mask << _MASK_SHIFT); 371 __fpscr_values[1] &= ~(mask << _MASK_SHIFT); 372 373 /* Load the floating-point status and control register */ 374 __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr)); 375 376 return (omask); 377} 378 379int 380fegetexcept(void) 381{ 382 unsigned int fpscr; 383 384 /* Store the current floating-point status and control register */ 385 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr)); 386 387 return ((fpscr >> _MASK_SHIFT) & FE_ALL_EXCEPT); 388} 389