1 /* $NetBSD: fenv.c,v 1.5 2021/09/03 21:54:59 andvar Exp $ */ 2 3 /*- 4 * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 */ 26 #include <sys/cdefs.h> 27 __RCSID("$NetBSD: fenv.c,v 1.5 2021/09/03 21:54:59 andvar Exp $"); 28 29 #include "namespace.h" 30 31 #include <assert.h> 32 #include <fenv.h> 33 34 #ifdef __weak_alias 35 __weak_alias(feclearexcept,_feclearexcept) 36 __weak_alias(fedisableexcept,_fedisableexcept) 37 __weak_alias(feenableexcept,_feenableexcept) 38 __weak_alias(fegetenv,_fegetenv) 39 __weak_alias(fegetexcept,_fegetexcept) 40 __weak_alias(fegetexceptflag,_fegetexceptflag) 41 __weak_alias(fegetround,_fegetround) 42 __weak_alias(feholdexcept,_feholdexcept) 43 __weak_alias(feraiseexcept,_feraiseexcept) 44 __weak_alias(fesetenv,_fesetenv) 45 __weak_alias(fesetexceptflag,_fesetexceptflag) 46 __weak_alias(fesetround,_fesetround) 47 __weak_alias(fetestexcept,_fetestexcept) 48 __weak_alias(feupdateenv,_feupdateenv) 49 #endif 50 51 /* 52 * Convert from exception flags (__BITS(27,32)) to exception enable bits 53 * (__BITS(5,0)) by right-shifting this much: 54 */ 55 #define FE_FLAGS_SHIFT 27 56 57 /* 58 * Mask all rounding mode bits 59 */ 60 #define FE_ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | \ 61 FE_UPWARD | FE_TOWARDZERO) 62 63 /* Load lower 32 bits from floating-point state register */ 64 static inline uint32_t 65 readfpsr(void) 66 { 67 uint32_t rv; 68 69 __asm__ __volatile__ ("fstw %%fr0, 0(%1)" : "=m" (rv) : "r"(&rv)); 70 return rv; 71 } 72 73 /* Save floating-point state register */ 74 static inline void 75 writefpsr(uint32_t val) 76 { 77 __asm__ __volatile__("fldw 0(%1),%%fr0" : : "m" (val), "r"(&val)); 78 } 79 80 /* 81 * The feclearexcept() function clears the supported floating-point exceptions 82 * represented by `excepts'. 83 */ 84 int 85 feclearexcept(int excepts) 86 { 87 fexcept_t r; 88 int ex; 89 90 _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0); 91 92 ex = (excepts & FE_ALL_EXCEPT) << FE_FLAGS_SHIFT; 93 94 r = readfpsr(); 95 r &= ~ex; 96 writefpsr(r); 97 98 /* Success */ 99 return 0; 100 } 101 102 /* 103 * The fegetexceptflag() function stores an implementation-defined 104 * representation of the states of the floating-point status flags indicated 105 * by the argument excepts in the object pointed to by the argument flagp. 106 */ 107 int 108 fegetexceptflag(fexcept_t *flagp, int excepts) 109 { 110 fexcept_t r; 111 int ex; 112 113 _DIAGASSERT(flagp != NULL); 114 _DIAGASSERT((excepts & ~_FE_ALL_EXCEPT) == 0); 115 116 ex = (excepts & FE_ALL_EXCEPT) << FE_FLAGS_SHIFT; 117 118 r = readfpsr(); 119 *flagp = (r & ex) >> FE_FLAGS_SHIFT; 120 121 /* Success */ 122 return 0; 123 } 124 125 126 /* 127 * This function sets the floating-point status flags indicated by the argument 128 * `excepts' to the states stored in the object pointed to by `flagp'. It does 129 * NOT raise any floating-point exceptions, but only sets the state of the flags. 130 */ 131 int 132 fesetexceptflag(const fexcept_t *flagp, int excepts) 133 { 134 fexcept_t r; 135 int ex; 136 137 _DIAGASSERT(flagp != NULL); 138 _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0); 139 140 ex = (excepts & FE_ALL_EXCEPT) << FE_FLAGS_SHIFT; 141 142 r = readfpsr(); 143 r &= ~ex; 144 r |= (*flagp << FE_FLAGS_SHIFT) & ex; 145 writefpsr(r); 146 147 /* Success */ 148 return 0; 149 } 150 151 /* 152 * The feraiseexcept() function raises the supported floating-point exceptions 153 * represented by the argument `excepts'. 154 * 155 * The order in which these floating-point exceptions are raised is unspecified 156 * (by the standard). 157 */ 158 int 159 feraiseexcept(int excepts) 160 { 161 volatile double d; 162 int ex; 163 164 _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0); 165 166 ex = excepts & FE_ALL_EXCEPT; 167 168 /* 169 * With a compiler that supports the FENV_ACCESS pragma properly, simple 170 * expressions like '0.0 / 0.0' should be sufficient to generate traps. 171 * Unfortunately, we need to bring a volatile variable into the equation 172 * to prevent incorrect optimizations. 173 */ 174 if (ex & FE_INVALID) { 175 d = 0.0; 176 d = 0.0 / d; 177 } 178 if (ex & FE_DIVBYZERO) { 179 d = 0.0; 180 d = 1.0 / d; 181 } 182 if (ex & FE_OVERFLOW) { 183 d = 0x1.ffp1023; 184 d *= 2.0; 185 } 186 if (ex & FE_UNDERFLOW) { 187 d = 0x1p-1022; 188 d /= 0x1p1023; 189 } 190 if (ex & FE_INEXACT) { 191 d = 0x1p-1022; 192 d += 1.0; 193 } 194 195 /* Success */ 196 return 0; 197 } 198 199 /* 200 * The fetestexcept() function determines which of a specified subset of the 201 * floating-point exception flags are currently set. The `excepts' argument 202 * specifies the floating-point status flags to be queried. 203 */ 204 int 205 fetestexcept(int excepts) 206 { 207 fexcept_t r; 208 209 _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0); 210 211 r = readfpsr(); 212 213 return (r >> FE_FLAGS_SHIFT) & (excepts & FE_ALL_EXCEPT); 214 } 215 216 /* 217 * The fegetround() function gets the current rounding direction. 218 */ 219 int 220 fegetround(void) 221 { 222 fenv_t r; 223 224 r = readfpsr(); 225 226 return r & FE_ROUND_MASK; 227 } 228 229 /* 230 * The fesetround() function establishes the rounding direction represented by 231 * its argument `round'. If the argument is not equal to the value of a rounding 232 * direction macro, the rounding direction is not changed. 233 */ 234 int 235 fesetround(int round) 236 { 237 fenv_t r; 238 239 _DIAGASSERT((round & ~FE_ROUND_MASK) == 0); 240 if (round & ~FE_ROUND_MASK) 241 return -1; 242 243 r = readfpsr(); 244 r &= ~FE_ROUND_MASK; 245 r |= round; 246 writefpsr(r); 247 248 /* Success */ 249 return 0; 250 } 251 252 /* 253 * The fegetenv() function attempts to store the current floating-point 254 * environment in the object pointed to by envp. 255 */ 256 int 257 fegetenv(fenv_t *envp) 258 { 259 _DIAGASSERT(envp != NULL); 260 261 *envp = readfpsr(); 262 263 /* Success */ 264 return 0; 265 } 266 267 268 /* 269 * The feholdexcept() function saves the current floating-point environment 270 * in the object pointed to by envp, clears the floating-point status flags, and 271 * then installs a non-stop (continue on floating-point exceptions) mode, if 272 * available, for all floating-point exceptions. 273 */ 274 int 275 feholdexcept(fenv_t *envp) 276 { 277 fenv_t r; 278 279 _DIAGASSERT(envp != NULL); 280 281 r = readfpsr(); 282 *envp = r; 283 r &= ~FE_ALL_EXCEPT; 284 writefpsr(r); 285 286 /* Success */ 287 return 0; 288 } 289 290 /* 291 * The fesetenv() function attempts to establish the floating-point environment 292 * represented by the object pointed to by envp. The argument `envp' points 293 * to an object set by a call to fegetenv() or feholdexcept(), or equal a 294 * floating-point environment macro. The fesetenv() function does not raise 295 * floating-point exceptions, but only installs the state of the floating-point 296 * status flags represented through its argument. 297 */ 298 int 299 fesetenv(const fenv_t *envp) 300 { 301 _DIAGASSERT(envp != NULL); 302 303 writefpsr(*envp); 304 305 /* Success */ 306 return 0; 307 } 308 309 310 /* 311 * The feupdateenv() function saves the currently raised floating-point 312 * exceptions in its automatic storage, installs the floating-point environment 313 * represented by the object pointed to by `envp', and then raises the saved 314 * floating-point exceptions. The argument `envp' shall point to an object set 315 * by a call to feholdexcept() or fegetenv(), or equal a floating-point 316 * environment macro. 317 */ 318 int 319 feupdateenv(const fenv_t *envp) 320 { 321 fexcept_t r; 322 323 _DIAGASSERT(envp != NULL); 324 325 r = readfpsr(); 326 writefpsr(*envp); 327 328 _DIAGASSERT((r & ~FE_ALL_EXCEPT) == 0); 329 feraiseexcept(r & FE_ALL_EXCEPT); 330 331 /* Success */ 332 return 0; 333 } 334 335 /* 336 * The following functions are extensions to the standard 337 */ 338 int 339 feenableexcept(int mask) 340 { 341 fenv_t old_r, new_r; 342 343 old_r = readfpsr(); 344 new_r = old_r | (mask & FE_ALL_EXCEPT); 345 writefpsr(new_r); 346 347 return old_r & FE_ALL_EXCEPT; 348 } 349 350 int 351 fedisableexcept(int mask) 352 { 353 fenv_t old_r, new_r; 354 355 old_r = readfpsr(); 356 new_r = old_r & ~(mask & FE_ALL_EXCEPT); 357 writefpsr(new_r); 358 359 return old_r & FE_ALL_EXCEPT; 360 } 361 362 int 363 fegetexcept(void) 364 { 365 fenv_t r; 366 367 r = readfpsr(); 368 return r & FE_ALL_EXCEPT; 369 } 370