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