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