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