1*f9faf20aSandvar /* $NetBSD: fenv.c,v 1.4 2021/09/03 21:54:59 andvar Exp $ */
217b8402aSchristos
317b8402aSchristos /*-
417b8402aSchristos * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG>
517b8402aSchristos * All rights reserved.
617b8402aSchristos *
717b8402aSchristos * Redistribution and use in source and binary forms, with or without
817b8402aSchristos * modification, are permitted provided that the following conditions
917b8402aSchristos * are met:
1017b8402aSchristos * 1. Redistributions of source code must retain the above copyright
1117b8402aSchristos * notice, this list of conditions and the following disclaimer.
1217b8402aSchristos * 2. Redistributions in binary form must reproduce the above copyright
1317b8402aSchristos * notice, this list of conditions and the following disclaimer in the
1417b8402aSchristos * documentation and/or other materials provided with the distribution.
1517b8402aSchristos *
1617b8402aSchristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1717b8402aSchristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1817b8402aSchristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1917b8402aSchristos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2017b8402aSchristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2117b8402aSchristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2217b8402aSchristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2317b8402aSchristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2417b8402aSchristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2517b8402aSchristos */
2617b8402aSchristos #include <sys/cdefs.h>
27*f9faf20aSandvar __RCSID("$NetBSD: fenv.c,v 1.4 2021/09/03 21:54:59 andvar Exp $");
287e30e943Schs
297e30e943Schs #include "namespace.h"
3017b8402aSchristos
3117b8402aSchristos #include <assert.h>
3217b8402aSchristos #include <fenv.h>
3317b8402aSchristos
347e30e943Schs #ifdef __weak_alias
__weak_alias(feclearexcept,_feclearexcept)357e30e943Schs __weak_alias(feclearexcept,_feclearexcept)
367e30e943Schs __weak_alias(fedisableexcept,_fedisableexcept)
377e30e943Schs __weak_alias(feenableexcept,_feenableexcept)
387e30e943Schs __weak_alias(fegetenv,_fegetenv)
397e30e943Schs __weak_alias(fegetexcept,_fegetexcept)
407e30e943Schs __weak_alias(fegetexceptflag,_fegetexceptflag)
417e30e943Schs __weak_alias(fegetround,_fegetround)
427e30e943Schs __weak_alias(feholdexcept,_feholdexcept)
437e30e943Schs __weak_alias(feraiseexcept,_feraiseexcept)
447e30e943Schs __weak_alias(fesetenv,_fesetenv)
457e30e943Schs __weak_alias(fesetexceptflag,_fesetexceptflag)
467e30e943Schs __weak_alias(fesetround,_fesetround)
477e30e943Schs __weak_alias(fetestexcept,_fetestexcept)
487e30e943Schs __weak_alias(feupdateenv,_feupdateenv)
497e30e943Schs #endif
507e30e943Schs
5188e42b60Snakayama #ifdef __arch64__
5288e42b60Snakayama
5317b8402aSchristos /* Load floating-point state register (all 64bits) */
5417b8402aSchristos #define __ldxfsr(__r) __asm__ __volatile__ \
5517b8402aSchristos ("ldx %0, %%fsr" : : "m" (__r))
5617b8402aSchristos
5717b8402aSchristos /* Save floating-point state register (all 64bits) */
5817b8402aSchristos #define __stxfsr(__r) __asm__ __volatile__ \
5917b8402aSchristos ("stx %%fsr, %0" : "=m" (*(__r)))
6017b8402aSchristos
6188e42b60Snakayama #else /* !__arch64__ */
6288e42b60Snakayama
6388e42b60Snakayama /* Load floating-point state register (32bits) */
6488e42b60Snakayama #define __ldxfsr(__r) __asm__ __volatile__ \
6588e42b60Snakayama ("ld %0, %%fsr" : : "m" (__r))
6688e42b60Snakayama
6788e42b60Snakayama /* Save floating-point state register (32bits) */
6888e42b60Snakayama #define __stxfsr(__r) __asm__ __volatile__ \
6988e42b60Snakayama ("st %%fsr, %0" : "=m" (*(__r)))
7088e42b60Snakayama
7188e42b60Snakayama #endif /* __arch64__ */
7288e42b60Snakayama
7317b8402aSchristos /*
7417b8402aSchristos * The feclearexcept() function clears the supported floating-point exceptions
7517b8402aSchristos * represented by `excepts'.
7617b8402aSchristos */
7717b8402aSchristos int
7817b8402aSchristos feclearexcept(int excepts)
7917b8402aSchristos {
8017b8402aSchristos fexcept_t r;
8117b8402aSchristos int ex;
8217b8402aSchristos
8317b8402aSchristos _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
8417b8402aSchristos
8517b8402aSchristos ex = excepts & FE_ALL_EXCEPT;
8617b8402aSchristos
8717b8402aSchristos __stxfsr(&r);
8817b8402aSchristos r &= ~ex;
8917b8402aSchristos __ldxfsr(r);
9017b8402aSchristos
9117b8402aSchristos /* Success */
9217b8402aSchristos return 0;
9317b8402aSchristos }
9417b8402aSchristos
9517b8402aSchristos /*
9617b8402aSchristos * The fegetexceptflag() function stores an implementation-defined
9717b8402aSchristos * representation of the states of the floating-point status flags indicated
9817b8402aSchristos * by the argument excepts in the object pointed to by the argument flagp.
9917b8402aSchristos */
10017b8402aSchristos int
fegetexceptflag(fexcept_t * flagp,int excepts)10117b8402aSchristos fegetexceptflag(fexcept_t *flagp, int excepts)
10217b8402aSchristos {
10317b8402aSchristos fexcept_t r;
10417b8402aSchristos int ex;
10517b8402aSchristos
10617b8402aSchristos _DIAGASSERT(flagp != NULL);
10717b8402aSchristos _DIAGASSERT((excepts & ~_FE_ALL_EXCEPT) == 0);
10817b8402aSchristos
10917b8402aSchristos ex = excepts & FE_ALL_EXCEPT;
11017b8402aSchristos
11117b8402aSchristos __stxfsr(&r);
11217b8402aSchristos *flagp = r & ex;
11317b8402aSchristos
11417b8402aSchristos /* Success */
11517b8402aSchristos return 0;
11617b8402aSchristos }
11717b8402aSchristos
11817b8402aSchristos
11917b8402aSchristos /*
12017b8402aSchristos * This function sets the floating-point status flags indicated by the argument
12117b8402aSchristos * `excepts' to the states stored in the object pointed to by `flagp'. It does
12217b8402aSchristos * NOT raise any floating-point exceptions, but only sets the state of the flags.
12317b8402aSchristos */
12417b8402aSchristos int
fesetexceptflag(const fexcept_t * flagp,int excepts)12517b8402aSchristos fesetexceptflag(const fexcept_t *flagp, int excepts)
12617b8402aSchristos {
12717b8402aSchristos fexcept_t r;
12817b8402aSchristos int ex;
12917b8402aSchristos
13017b8402aSchristos _DIAGASSERT(flagp != NULL);
13117b8402aSchristos _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
13217b8402aSchristos
13317b8402aSchristos ex = excepts & FE_ALL_EXCEPT;
13417b8402aSchristos
13517b8402aSchristos __stxfsr(&r);
13617b8402aSchristos r &= ~ex;
13717b8402aSchristos r |= *flagp & ex;
13817b8402aSchristos __ldxfsr(r);
13917b8402aSchristos
14017b8402aSchristos /* Success */
14117b8402aSchristos return 0;
14217b8402aSchristos }
14317b8402aSchristos
14417b8402aSchristos /*
14517b8402aSchristos * The feraiseexcept() function raises the supported floating-point exceptions
14617b8402aSchristos * represented by the argument `excepts'.
14717b8402aSchristos *
14817b8402aSchristos * The order in which these floating-point exceptions are raised is unspecified
14917b8402aSchristos * (by the standard).
15017b8402aSchristos */
15117b8402aSchristos int
feraiseexcept(int excepts)15217b8402aSchristos feraiseexcept(int excepts)
15317b8402aSchristos {
15417b8402aSchristos volatile double d;
15517b8402aSchristos int ex;
15617b8402aSchristos
15717b8402aSchristos _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
15817b8402aSchristos
15917b8402aSchristos ex = excepts & FE_ALL_EXCEPT;
16017b8402aSchristos
16117b8402aSchristos /*
16217b8402aSchristos * With a compiler that supports the FENV_ACCESS pragma properly, simple
16317b8402aSchristos * expressions like '0.0 / 0.0' should be sufficient to generate traps.
16417b8402aSchristos * Unfortunately, we need to bring a volatile variable into the equation
16517b8402aSchristos * to prevent incorrect optimizations.
16617b8402aSchristos */
16717b8402aSchristos if (ex & FE_INVALID) {
16817b8402aSchristos d = 0.0;
16917b8402aSchristos d = 0.0 / d;
17017b8402aSchristos }
17117b8402aSchristos if (ex & FE_DIVBYZERO) {
17217b8402aSchristos d = 0.0;
17317b8402aSchristos d = 1.0 / d;
17417b8402aSchristos }
17517b8402aSchristos if (ex & FE_OVERFLOW) {
17617b8402aSchristos d = 0x1.ffp1023;
17717b8402aSchristos d *= 2.0;
17817b8402aSchristos }
17917b8402aSchristos if (ex & FE_UNDERFLOW) {
18017b8402aSchristos d = 0x1p-1022;
18117b8402aSchristos d /= 0x1p1023;
18217b8402aSchristos }
18317b8402aSchristos if (ex & FE_INEXACT) {
18417b8402aSchristos d = 0x1p-1022;
18517b8402aSchristos d += 1.0;
18617b8402aSchristos }
18717b8402aSchristos
18817b8402aSchristos /* Success */
18917b8402aSchristos return 0;
19017b8402aSchristos }
19117b8402aSchristos
19217b8402aSchristos /*
19317b8402aSchristos * The fetestexcept() function determines which of a specified subset of the
19417b8402aSchristos * floating-point exception flags are currently set. The `excepts' argument
19517b8402aSchristos * specifies the floating-point status flags to be queried.
19617b8402aSchristos */
19717b8402aSchristos int
fetestexcept(int excepts)19817b8402aSchristos fetestexcept(int excepts)
19917b8402aSchristos {
20017b8402aSchristos fexcept_t r;
20117b8402aSchristos
20217b8402aSchristos _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
20317b8402aSchristos
20417b8402aSchristos __stxfsr(&r);
20517b8402aSchristos
20617b8402aSchristos return r & (excepts & FE_ALL_EXCEPT);
20717b8402aSchristos }
20817b8402aSchristos
20917b8402aSchristos /*
21017b8402aSchristos * The fegetround() function gets the current rounding direction.
21117b8402aSchristos */
21217b8402aSchristos int
fegetround(void)21317b8402aSchristos fegetround(void)
21417b8402aSchristos {
21517b8402aSchristos fenv_t r;
21617b8402aSchristos
21717b8402aSchristos __stxfsr(&r);
21817b8402aSchristos
21917b8402aSchristos return (r >> _ROUND_SHIFT) & _ROUND_MASK;
22017b8402aSchristos }
22117b8402aSchristos
22217b8402aSchristos /*
22317b8402aSchristos * The fesetround() function establishes the rounding direction represented by
22417b8402aSchristos * its argument `round'. If the argument is not equal to the value of a rounding
22517b8402aSchristos * direction macro, the rounding direction is not changed.
22617b8402aSchristos */
22717b8402aSchristos int
fesetround(int round)22817b8402aSchristos fesetround(int round)
22917b8402aSchristos {
23017b8402aSchristos fenv_t r;
23117b8402aSchristos
23217b8402aSchristos _DIAGASSERT((round & ~_ROUND_MASK) == 0);
23317b8402aSchristos if (round & ~_ROUND_MASK)
23417b8402aSchristos return -1;
23517b8402aSchristos
23617b8402aSchristos __stxfsr(&r);
23717b8402aSchristos r &= ~(_ROUND_MASK << _ROUND_SHIFT);
23817b8402aSchristos r |= round << _ROUND_SHIFT;
23917b8402aSchristos __ldxfsr(r);
24017b8402aSchristos
24117b8402aSchristos /* Success */
24217b8402aSchristos return 0;
24317b8402aSchristos }
24417b8402aSchristos
24517b8402aSchristos /*
24617b8402aSchristos * The fegetenv() function attempts to store the current floating-point
24717b8402aSchristos * environment in the object pointed to by envp.
24817b8402aSchristos */
24917b8402aSchristos int
fegetenv(fenv_t * envp)25017b8402aSchristos fegetenv(fenv_t *envp)
25117b8402aSchristos {
25217b8402aSchristos _DIAGASSERT(envp != NULL);
25317b8402aSchristos
25417b8402aSchristos __stxfsr(envp);
25517b8402aSchristos
25617b8402aSchristos /* Success */
25717b8402aSchristos return 0;
25817b8402aSchristos }
25917b8402aSchristos
26017b8402aSchristos
26117b8402aSchristos /*
26217b8402aSchristos * The feholdexcept() function saves the current floating-point environment
26317b8402aSchristos * in the object pointed to by envp, clears the floating-point status flags, and
26417b8402aSchristos * then installs a non-stop (continue on floating-point exceptions) mode, if
26517b8402aSchristos * available, for all floating-point exceptions.
26617b8402aSchristos */
26717b8402aSchristos int
feholdexcept(fenv_t * envp)26817b8402aSchristos feholdexcept(fenv_t *envp)
26917b8402aSchristos {
27017b8402aSchristos fenv_t r;
27117b8402aSchristos
27217b8402aSchristos _DIAGASSERT(envp != NULL);
27317b8402aSchristos
27417b8402aSchristos __stxfsr(&r);
27517b8402aSchristos *envp = r;
27617b8402aSchristos r &= ~(FE_ALL_EXCEPT | _ENABLE_MASK);
27717b8402aSchristos __ldxfsr(r);
27817b8402aSchristos
27917b8402aSchristos /* Success */
28017b8402aSchristos return 0;
28117b8402aSchristos }
28217b8402aSchristos
28317b8402aSchristos /*
28417b8402aSchristos * The fesetenv() function attempts to establish the floating-point environment
28517b8402aSchristos * represented by the object pointed to by envp. The argument `envp' points
28617b8402aSchristos * to an object set by a call to fegetenv() or feholdexcept(), or equal a
28717b8402aSchristos * floating-point environment macro. The fesetenv() function does not raise
28817b8402aSchristos * floating-point exceptions, but only installs the state of the floating-point
28917b8402aSchristos * status flags represented through its argument.
29017b8402aSchristos */
29117b8402aSchristos int
fesetenv(const fenv_t * envp)29217b8402aSchristos fesetenv(const fenv_t *envp)
29317b8402aSchristos {
29417b8402aSchristos _DIAGASSERT(envp != NULL);
29517b8402aSchristos
29617b8402aSchristos __ldxfsr(*envp);
29717b8402aSchristos
29817b8402aSchristos /* Success */
29917b8402aSchristos return 0;
30017b8402aSchristos }
30117b8402aSchristos
30217b8402aSchristos
30317b8402aSchristos /*
30417b8402aSchristos * The feupdateenv() function saves the currently raised floating-point
30517b8402aSchristos * exceptions in its automatic storage, installs the floating-point environment
30617b8402aSchristos * represented by the object pointed to by `envp', and then raises the saved
30717b8402aSchristos * floating-point exceptions. The argument `envp' shall point to an object set
30817b8402aSchristos * by a call to feholdexcept() or fegetenv(), or equal a floating-point
30917b8402aSchristos * environment macro.
31017b8402aSchristos */
31117b8402aSchristos int
feupdateenv(const fenv_t * envp)31217b8402aSchristos feupdateenv(const fenv_t *envp)
31317b8402aSchristos {
31417b8402aSchristos fexcept_t r;
31517b8402aSchristos
31617b8402aSchristos _DIAGASSERT(envp != NULL);
31717b8402aSchristos
31817b8402aSchristos __stxfsr(&r);
31917b8402aSchristos __ldxfsr(*envp);
32017b8402aSchristos
32117b8402aSchristos _DIAGASSERT((r & ~FE_ALL_EXCEPT) == 0);
32217b8402aSchristos feraiseexcept(r & FE_ALL_EXCEPT);
32317b8402aSchristos
32417b8402aSchristos /* Success */
32517b8402aSchristos return 0;
32617b8402aSchristos }
32717b8402aSchristos
32817b8402aSchristos /*
329*f9faf20aSandvar * The following functions are extensions to the standard
33017b8402aSchristos */
33117b8402aSchristos int
feenableexcept(int mask)33217b8402aSchristos feenableexcept(int mask)
33317b8402aSchristos {
33417b8402aSchristos fenv_t old_r, new_r;
33517b8402aSchristos
33617b8402aSchristos __stxfsr(&old_r);
33717b8402aSchristos new_r = old_r | ((mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT);
33817b8402aSchristos __ldxfsr(new_r);
33917b8402aSchristos
34017b8402aSchristos return (old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT;
34117b8402aSchristos }
34217b8402aSchristos
34317b8402aSchristos int
fedisableexcept(int mask)34417b8402aSchristos fedisableexcept(int mask)
34517b8402aSchristos {
34617b8402aSchristos fenv_t old_r, new_r;
34717b8402aSchristos
34817b8402aSchristos __stxfsr(&old_r);
34917b8402aSchristos new_r = old_r & ~((mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT);
35017b8402aSchristos __ldxfsr(new_r);
35117b8402aSchristos
35217b8402aSchristos return (old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT;
35317b8402aSchristos }
35417b8402aSchristos
35517b8402aSchristos int
fegetexcept(void)35617b8402aSchristos fegetexcept(void)
35717b8402aSchristos {
35817b8402aSchristos fenv_t r;
35917b8402aSchristos
36017b8402aSchristos __stxfsr(&r);
36117b8402aSchristos return (r & _ENABLE_MASK) >> _FPUSW_SHIFT;
36217b8402aSchristos }
363