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