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