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