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