1 /* $NetBSD: fenv.c,v 1.3 2017/03/22 23:11:09 chs 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.3 2017/03/22 23:11:09 chs 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 #ifdef __arch64__ 52 53 /* Load floating-point state register (all 64bits) */ 54 #define __ldxfsr(__r) __asm__ __volatile__ \ 55 ("ldx %0, %%fsr" : : "m" (__r)) 56 57 /* Save floating-point state register (all 64bits) */ 58 #define __stxfsr(__r) __asm__ __volatile__ \ 59 ("stx %%fsr, %0" : "=m" (*(__r))) 60 61 #else /* !__arch64__ */ 62 63 /* Load floating-point state register (32bits) */ 64 #define __ldxfsr(__r) __asm__ __volatile__ \ 65 ("ld %0, %%fsr" : : "m" (__r)) 66 67 /* Save floating-point state register (32bits) */ 68 #define __stxfsr(__r) __asm__ __volatile__ \ 69 ("st %%fsr, %0" : "=m" (*(__r))) 70 71 #endif /* __arch64__ */ 72 73 /* 74 * The feclearexcept() function clears the supported floating-point exceptions 75 * represented by `excepts'. 76 */ 77 int 78 feclearexcept(int excepts) 79 { 80 fexcept_t r; 81 int ex; 82 83 _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0); 84 85 ex = excepts & FE_ALL_EXCEPT; 86 87 __stxfsr(&r); 88 r &= ~ex; 89 __ldxfsr(r); 90 91 /* Success */ 92 return 0; 93 } 94 95 /* 96 * The fegetexceptflag() function stores an implementation-defined 97 * representation of the states of the floating-point status flags indicated 98 * by the argument excepts in the object pointed to by the argument flagp. 99 */ 100 int 101 fegetexceptflag(fexcept_t *flagp, int excepts) 102 { 103 fexcept_t r; 104 int ex; 105 106 _DIAGASSERT(flagp != NULL); 107 _DIAGASSERT((excepts & ~_FE_ALL_EXCEPT) == 0); 108 109 ex = excepts & FE_ALL_EXCEPT; 110 111 __stxfsr(&r); 112 *flagp = r & ex; 113 114 /* Success */ 115 return 0; 116 } 117 118 119 /* 120 * This function sets the floating-point status flags indicated by the argument 121 * `excepts' to the states stored in the object pointed to by `flagp'. It does 122 * NOT raise any floating-point exceptions, but only sets the state of the flags. 123 */ 124 int 125 fesetexceptflag(const fexcept_t *flagp, int excepts) 126 { 127 fexcept_t r; 128 int ex; 129 130 _DIAGASSERT(flagp != NULL); 131 _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0); 132 133 ex = excepts & FE_ALL_EXCEPT; 134 135 __stxfsr(&r); 136 r &= ~ex; 137 r |= *flagp & ex; 138 __ldxfsr(r); 139 140 /* Success */ 141 return 0; 142 } 143 144 /* 145 * The feraiseexcept() function raises the supported floating-point exceptions 146 * represented by the argument `excepts'. 147 * 148 * The order in which these floating-point exceptions are raised is unspecified 149 * (by the standard). 150 */ 151 int 152 feraiseexcept(int excepts) 153 { 154 volatile double d; 155 int ex; 156 157 _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0); 158 159 ex = excepts & FE_ALL_EXCEPT; 160 161 /* 162 * With a compiler that supports the FENV_ACCESS pragma properly, simple 163 * expressions like '0.0 / 0.0' should be sufficient to generate traps. 164 * Unfortunately, we need to bring a volatile variable into the equation 165 * to prevent incorrect optimizations. 166 */ 167 if (ex & FE_INVALID) { 168 d = 0.0; 169 d = 0.0 / d; 170 } 171 if (ex & FE_DIVBYZERO) { 172 d = 0.0; 173 d = 1.0 / d; 174 } 175 if (ex & FE_OVERFLOW) { 176 d = 0x1.ffp1023; 177 d *= 2.0; 178 } 179 if (ex & FE_UNDERFLOW) { 180 d = 0x1p-1022; 181 d /= 0x1p1023; 182 } 183 if (ex & FE_INEXACT) { 184 d = 0x1p-1022; 185 d += 1.0; 186 } 187 188 /* Success */ 189 return 0; 190 } 191 192 /* 193 * The fetestexcept() function determines which of a specified subset of the 194 * floating-point exception flags are currently set. The `excepts' argument 195 * specifies the floating-point status flags to be queried. 196 */ 197 int 198 fetestexcept(int excepts) 199 { 200 fexcept_t r; 201 202 _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0); 203 204 __stxfsr(&r); 205 206 return r & (excepts & FE_ALL_EXCEPT); 207 } 208 209 /* 210 * The fegetround() function gets the current rounding direction. 211 */ 212 int 213 fegetround(void) 214 { 215 fenv_t r; 216 217 __stxfsr(&r); 218 219 return (r >> _ROUND_SHIFT) & _ROUND_MASK; 220 } 221 222 /* 223 * The fesetround() function establishes the rounding direction represented by 224 * its argument `round'. If the argument is not equal to the value of a rounding 225 * direction macro, the rounding direction is not changed. 226 */ 227 int 228 fesetround(int round) 229 { 230 fenv_t r; 231 232 _DIAGASSERT((round & ~_ROUND_MASK) == 0); 233 if (round & ~_ROUND_MASK) 234 return -1; 235 236 __stxfsr(&r); 237 r &= ~(_ROUND_MASK << _ROUND_SHIFT); 238 r |= round << _ROUND_SHIFT; 239 __ldxfsr(r); 240 241 /* Success */ 242 return 0; 243 } 244 245 /* 246 * The fegetenv() function attempts to store the current floating-point 247 * environment in the object pointed to by envp. 248 */ 249 int 250 fegetenv(fenv_t *envp) 251 { 252 _DIAGASSERT(envp != NULL); 253 254 __stxfsr(envp); 255 256 /* Success */ 257 return 0; 258 } 259 260 261 /* 262 * The feholdexcept() function saves the current floating-point environment 263 * in the object pointed to by envp, clears the floating-point status flags, and 264 * then installs a non-stop (continue on floating-point exceptions) mode, if 265 * available, for all floating-point exceptions. 266 */ 267 int 268 feholdexcept(fenv_t *envp) 269 { 270 fenv_t r; 271 272 _DIAGASSERT(envp != NULL); 273 274 __stxfsr(&r); 275 *envp = r; 276 r &= ~(FE_ALL_EXCEPT | _ENABLE_MASK); 277 __ldxfsr(r); 278 279 /* Success */ 280 return 0; 281 } 282 283 /* 284 * The fesetenv() function attempts to establish the floating-point environment 285 * represented by the object pointed to by envp. The argument `envp' points 286 * to an object set by a call to fegetenv() or feholdexcept(), or equal a 287 * floating-point environment macro. The fesetenv() function does not raise 288 * floating-point exceptions, but only installs the state of the floating-point 289 * status flags represented through its argument. 290 */ 291 int 292 fesetenv(const fenv_t *envp) 293 { 294 _DIAGASSERT(envp != NULL); 295 296 __ldxfsr(*envp); 297 298 /* Success */ 299 return 0; 300 } 301 302 303 /* 304 * The feupdateenv() function saves the currently raised floating-point 305 * exceptions in its automatic storage, installs the floating-point environment 306 * represented by the object pointed to by `envp', and then raises the saved 307 * floating-point exceptions. The argument `envp' shall point to an object set 308 * by a call to feholdexcept() or fegetenv(), or equal a floating-point 309 * environment macro. 310 */ 311 int 312 feupdateenv(const fenv_t *envp) 313 { 314 fexcept_t r; 315 316 _DIAGASSERT(envp != NULL); 317 318 __stxfsr(&r); 319 __ldxfsr(*envp); 320 321 _DIAGASSERT((r & ~FE_ALL_EXCEPT) == 0); 322 feraiseexcept(r & FE_ALL_EXCEPT); 323 324 /* Success */ 325 return 0; 326 } 327 328 /* 329 * The following functions are extentions to the standard 330 */ 331 int 332 feenableexcept(int mask) 333 { 334 fenv_t old_r, new_r; 335 336 __stxfsr(&old_r); 337 new_r = old_r | ((mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT); 338 __ldxfsr(new_r); 339 340 return (old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT; 341 } 342 343 int 344 fedisableexcept(int mask) 345 { 346 fenv_t old_r, new_r; 347 348 __stxfsr(&old_r); 349 new_r = old_r & ~((mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT); 350 __ldxfsr(new_r); 351 352 return (old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT; 353 } 354 355 int 356 fegetexcept(void) 357 { 358 fenv_t r; 359 360 __stxfsr(&r); 361 return (r & _ENABLE_MASK) >> _FPUSW_SHIFT; 362 } 363