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