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