1*f9faf20aSandvar /* $NetBSD: fenv.c,v 1.3 2021/09/03 21:54:59 andvar Exp $ */
288e42b60Snakayama
388e42b60Snakayama /*-
488e42b60Snakayama * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG>
588e42b60Snakayama * All rights reserved.
688e42b60Snakayama *
788e42b60Snakayama * Redistribution and use in source and binary forms, with or without
888e42b60Snakayama * modification, are permitted provided that the following conditions
988e42b60Snakayama * are met:
1088e42b60Snakayama * 1. Redistributions of source code must retain the above copyright
1188e42b60Snakayama * notice, this list of conditions and the following disclaimer.
1288e42b60Snakayama * 2. Redistributions in binary form must reproduce the above copyright
1388e42b60Snakayama * notice, this list of conditions and the following disclaimer in the
1488e42b60Snakayama * documentation and/or other materials provided with the distribution.
1588e42b60Snakayama *
1688e42b60Snakayama * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1788e42b60Snakayama * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1888e42b60Snakayama * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1988e42b60Snakayama * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2088e42b60Snakayama * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2188e42b60Snakayama * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2288e42b60Snakayama * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2388e42b60Snakayama * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2488e42b60Snakayama * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2588e42b60Snakayama */
2688e42b60Snakayama #include <sys/cdefs.h>
27*f9faf20aSandvar __RCSID("$NetBSD: fenv.c,v 1.3 2021/09/03 21:54:59 andvar Exp $");
287e30e943Schs
297e30e943Schs #include "namespace.h"
3088e42b60Snakayama
3188e42b60Snakayama #include <assert.h>
3288e42b60Snakayama #include <fenv.h>
3388e42b60Snakayama
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 /* Load floating-point state register (32bits) */
5288e42b60Snakayama #define __ldfsr(__r) __asm__ __volatile__ \
5388e42b60Snakayama ("ld %0, %%fsr" : : "m" (__r))
5488e42b60Snakayama
5588e42b60Snakayama /* Save floating-point state register (32bits) */
5688e42b60Snakayama #define __stfsr(__r) __asm__ __volatile__ \
5788e42b60Snakayama ("st %%fsr, %0" : "=m" (*(__r)))
5888e42b60Snakayama
5988e42b60Snakayama /*
6088e42b60Snakayama * The feclearexcept() function clears the supported floating-point exceptions
6188e42b60Snakayama * represented by `excepts'.
6288e42b60Snakayama */
6388e42b60Snakayama int
6488e42b60Snakayama feclearexcept(int excepts)
6588e42b60Snakayama {
6688e42b60Snakayama fexcept_t r;
6788e42b60Snakayama int ex;
6888e42b60Snakayama
6988e42b60Snakayama _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
7088e42b60Snakayama
7188e42b60Snakayama ex = excepts & FE_ALL_EXCEPT;
7288e42b60Snakayama
7388e42b60Snakayama __stfsr(&r);
7488e42b60Snakayama r &= ~ex;
7588e42b60Snakayama __ldfsr(r);
7688e42b60Snakayama
7788e42b60Snakayama /* Success */
7888e42b60Snakayama return 0;
7988e42b60Snakayama }
8088e42b60Snakayama
8188e42b60Snakayama /*
8288e42b60Snakayama * The fegetexceptflag() function stores an implementation-defined
8388e42b60Snakayama * representation of the states of the floating-point status flags indicated
8488e42b60Snakayama * by the argument excepts in the object pointed to by the argument flagp.
8588e42b60Snakayama */
8688e42b60Snakayama int
fegetexceptflag(fexcept_t * flagp,int excepts)8788e42b60Snakayama fegetexceptflag(fexcept_t *flagp, int excepts)
8888e42b60Snakayama {
8988e42b60Snakayama fexcept_t r;
9088e42b60Snakayama int ex;
9188e42b60Snakayama
9288e42b60Snakayama _DIAGASSERT(flagp != NULL);
9388e42b60Snakayama _DIAGASSERT((excepts & ~_FE_ALL_EXCEPT) == 0);
9488e42b60Snakayama
9588e42b60Snakayama ex = excepts & FE_ALL_EXCEPT;
9688e42b60Snakayama
9788e42b60Snakayama __stfsr(&r);
9888e42b60Snakayama *flagp = r & ex;
9988e42b60Snakayama
10088e42b60Snakayama /* Success */
10188e42b60Snakayama return 0;
10288e42b60Snakayama }
10388e42b60Snakayama
10488e42b60Snakayama
10588e42b60Snakayama /*
10688e42b60Snakayama * This function sets the floating-point status flags indicated by the argument
10788e42b60Snakayama * `excepts' to the states stored in the object pointed to by `flagp'. It does
10888e42b60Snakayama * NOT raise any floating-point exceptions, but only sets the state of the flags.
10988e42b60Snakayama */
11088e42b60Snakayama int
fesetexceptflag(const fexcept_t * flagp,int excepts)11188e42b60Snakayama fesetexceptflag(const fexcept_t *flagp, int excepts)
11288e42b60Snakayama {
11388e42b60Snakayama fexcept_t r;
11488e42b60Snakayama int ex;
11588e42b60Snakayama
11688e42b60Snakayama _DIAGASSERT(flagp != NULL);
11788e42b60Snakayama _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
11888e42b60Snakayama
11988e42b60Snakayama ex = excepts & FE_ALL_EXCEPT;
12088e42b60Snakayama
12188e42b60Snakayama __stfsr(&r);
12288e42b60Snakayama r &= ~ex;
12388e42b60Snakayama r |= *flagp & ex;
12488e42b60Snakayama __ldfsr(r);
12588e42b60Snakayama
12688e42b60Snakayama /* Success */
12788e42b60Snakayama return 0;
12888e42b60Snakayama }
12988e42b60Snakayama
13088e42b60Snakayama /*
13188e42b60Snakayama * The feraiseexcept() function raises the supported floating-point exceptions
13288e42b60Snakayama * represented by the argument `excepts'.
13388e42b60Snakayama *
13488e42b60Snakayama * The order in which these floating-point exceptions are raised is unspecified
13588e42b60Snakayama * (by the standard).
13688e42b60Snakayama */
13788e42b60Snakayama int
feraiseexcept(int excepts)13888e42b60Snakayama feraiseexcept(int excepts)
13988e42b60Snakayama {
14088e42b60Snakayama volatile double d;
14188e42b60Snakayama int ex;
14288e42b60Snakayama
14388e42b60Snakayama _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
14488e42b60Snakayama
14588e42b60Snakayama ex = excepts & FE_ALL_EXCEPT;
14688e42b60Snakayama
14788e42b60Snakayama /*
14888e42b60Snakayama * With a compiler that supports the FENV_ACCESS pragma properly, simple
14988e42b60Snakayama * expressions like '0.0 / 0.0' should be sufficient to generate traps.
15088e42b60Snakayama * Unfortunately, we need to bring a volatile variable into the equation
15188e42b60Snakayama * to prevent incorrect optimizations.
15288e42b60Snakayama */
15388e42b60Snakayama if (ex & FE_INVALID) {
15488e42b60Snakayama d = 0.0;
15588e42b60Snakayama d = 0.0 / d;
15688e42b60Snakayama }
15788e42b60Snakayama if (ex & FE_DIVBYZERO) {
15888e42b60Snakayama d = 0.0;
15988e42b60Snakayama d = 1.0 / d;
16088e42b60Snakayama }
16188e42b60Snakayama if (ex & FE_OVERFLOW) {
16288e42b60Snakayama d = 0x1.ffp1023;
16388e42b60Snakayama d *= 2.0;
16488e42b60Snakayama }
16588e42b60Snakayama if (ex & FE_UNDERFLOW) {
16688e42b60Snakayama d = 0x1p-1022;
16788e42b60Snakayama d /= 0x1p1023;
16888e42b60Snakayama }
16988e42b60Snakayama if (ex & FE_INEXACT) {
17088e42b60Snakayama d = 0x1p-1022;
17188e42b60Snakayama d += 1.0;
17288e42b60Snakayama }
17388e42b60Snakayama
17488e42b60Snakayama /* Success */
17588e42b60Snakayama return 0;
17688e42b60Snakayama }
17788e42b60Snakayama
17888e42b60Snakayama /*
17988e42b60Snakayama * The fetestexcept() function determines which of a specified subset of the
18088e42b60Snakayama * floating-point exception flags are currently set. The `excepts' argument
18188e42b60Snakayama * specifies the floating-point status flags to be queried.
18288e42b60Snakayama */
18388e42b60Snakayama int
fetestexcept(int excepts)18488e42b60Snakayama fetestexcept(int excepts)
18588e42b60Snakayama {
18688e42b60Snakayama fexcept_t r;
18788e42b60Snakayama
18888e42b60Snakayama _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
18988e42b60Snakayama
19088e42b60Snakayama __stfsr(&r);
19188e42b60Snakayama
19288e42b60Snakayama return r & (excepts & FE_ALL_EXCEPT);
19388e42b60Snakayama }
19488e42b60Snakayama
19588e42b60Snakayama /*
19688e42b60Snakayama * The fegetround() function gets the current rounding direction.
19788e42b60Snakayama */
19888e42b60Snakayama int
fegetround(void)19988e42b60Snakayama fegetround(void)
20088e42b60Snakayama {
20188e42b60Snakayama fenv_t r;
20288e42b60Snakayama
20388e42b60Snakayama __stfsr(&r);
20488e42b60Snakayama
20588e42b60Snakayama return (r >> _ROUND_SHIFT) & _ROUND_MASK;
20688e42b60Snakayama }
20788e42b60Snakayama
20888e42b60Snakayama /*
20988e42b60Snakayama * The fesetround() function establishes the rounding direction represented by
21088e42b60Snakayama * its argument `round'. If the argument is not equal to the value of a rounding
21188e42b60Snakayama * direction macro, the rounding direction is not changed.
21288e42b60Snakayama */
21388e42b60Snakayama int
fesetround(int round)21488e42b60Snakayama fesetround(int round)
21588e42b60Snakayama {
21688e42b60Snakayama fenv_t r;
21788e42b60Snakayama
21888e42b60Snakayama _DIAGASSERT((round & ~_ROUND_MASK) == 0);
21988e42b60Snakayama if (round & ~_ROUND_MASK)
22088e42b60Snakayama return -1;
22188e42b60Snakayama
22288e42b60Snakayama __stfsr(&r);
22388e42b60Snakayama r &= ~(_ROUND_MASK << _ROUND_SHIFT);
22488e42b60Snakayama r |= round << _ROUND_SHIFT;
22588e42b60Snakayama __ldfsr(r);
22688e42b60Snakayama
22788e42b60Snakayama /* Success */
22888e42b60Snakayama return 0;
22988e42b60Snakayama }
23088e42b60Snakayama
23188e42b60Snakayama /*
23288e42b60Snakayama * The fegetenv() function attempts to store the current floating-point
23388e42b60Snakayama * environment in the object pointed to by envp.
23488e42b60Snakayama */
23588e42b60Snakayama int
fegetenv(fenv_t * envp)23688e42b60Snakayama fegetenv(fenv_t *envp)
23788e42b60Snakayama {
23888e42b60Snakayama _DIAGASSERT(envp != NULL);
23988e42b60Snakayama
24088e42b60Snakayama __stfsr(envp);
24188e42b60Snakayama
24288e42b60Snakayama /* Success */
24388e42b60Snakayama return 0;
24488e42b60Snakayama }
24588e42b60Snakayama
24688e42b60Snakayama
24788e42b60Snakayama /*
24888e42b60Snakayama * The feholdexcept() function saves the current floating-point environment
24988e42b60Snakayama * in the object pointed to by envp, clears the floating-point status flags, and
25088e42b60Snakayama * then installs a non-stop (continue on floating-point exceptions) mode, if
25188e42b60Snakayama * available, for all floating-point exceptions.
25288e42b60Snakayama */
25388e42b60Snakayama int
feholdexcept(fenv_t * envp)25488e42b60Snakayama feholdexcept(fenv_t *envp)
25588e42b60Snakayama {
25688e42b60Snakayama fenv_t r;
25788e42b60Snakayama
25888e42b60Snakayama _DIAGASSERT(envp != NULL);
25988e42b60Snakayama
26088e42b60Snakayama __stfsr(&r);
26188e42b60Snakayama *envp = r;
26288e42b60Snakayama r &= ~(FE_ALL_EXCEPT | _ENABLE_MASK);
26388e42b60Snakayama __ldfsr(r);
26488e42b60Snakayama
26588e42b60Snakayama /* Success */
26688e42b60Snakayama return 0;
26788e42b60Snakayama }
26888e42b60Snakayama
26988e42b60Snakayama /*
27088e42b60Snakayama * The fesetenv() function attempts to establish the floating-point environment
27188e42b60Snakayama * represented by the object pointed to by envp. The argument `envp' points
27288e42b60Snakayama * to an object set by a call to fegetenv() or feholdexcept(), or equal a
27388e42b60Snakayama * floating-point environment macro. The fesetenv() function does not raise
27488e42b60Snakayama * floating-point exceptions, but only installs the state of the floating-point
27588e42b60Snakayama * status flags represented through its argument.
27688e42b60Snakayama */
27788e42b60Snakayama int
fesetenv(const fenv_t * envp)27888e42b60Snakayama fesetenv(const fenv_t *envp)
27988e42b60Snakayama {
28088e42b60Snakayama _DIAGASSERT(envp != NULL);
28188e42b60Snakayama
28288e42b60Snakayama __ldfsr(*envp);
28388e42b60Snakayama
28488e42b60Snakayama /* Success */
28588e42b60Snakayama return 0;
28688e42b60Snakayama }
28788e42b60Snakayama
28888e42b60Snakayama
28988e42b60Snakayama /*
29088e42b60Snakayama * The feupdateenv() function saves the currently raised floating-point
29188e42b60Snakayama * exceptions in its automatic storage, installs the floating-point environment
29288e42b60Snakayama * represented by the object pointed to by `envp', and then raises the saved
29388e42b60Snakayama * floating-point exceptions. The argument `envp' shall point to an object set
29488e42b60Snakayama * by a call to feholdexcept() or fegetenv(), or equal a floating-point
29588e42b60Snakayama * environment macro.
29688e42b60Snakayama */
29788e42b60Snakayama int
feupdateenv(const fenv_t * envp)29888e42b60Snakayama feupdateenv(const fenv_t *envp)
29988e42b60Snakayama {
30088e42b60Snakayama fexcept_t r;
30188e42b60Snakayama
30288e42b60Snakayama _DIAGASSERT(envp != NULL);
30388e42b60Snakayama
30488e42b60Snakayama __stfsr(&r);
30588e42b60Snakayama __ldfsr(*envp);
30688e42b60Snakayama
30788e42b60Snakayama _DIAGASSERT((r & ~FE_ALL_EXCEPT) == 0);
30888e42b60Snakayama feraiseexcept(r & FE_ALL_EXCEPT);
30988e42b60Snakayama
31088e42b60Snakayama /* Success */
31188e42b60Snakayama return 0;
31288e42b60Snakayama }
31388e42b60Snakayama
31488e42b60Snakayama /*
315*f9faf20aSandvar * The following functions are extensions to the standard
31688e42b60Snakayama */
31788e42b60Snakayama int
feenableexcept(int mask)31888e42b60Snakayama feenableexcept(int mask)
31988e42b60Snakayama {
32088e42b60Snakayama fenv_t old_r, new_r;
32188e42b60Snakayama
32288e42b60Snakayama __stfsr(&old_r);
32388e42b60Snakayama new_r = old_r | ((mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT);
32488e42b60Snakayama __ldfsr(new_r);
32588e42b60Snakayama
32688e42b60Snakayama return (old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT;
32788e42b60Snakayama }
32888e42b60Snakayama
32988e42b60Snakayama int
fedisableexcept(int mask)33088e42b60Snakayama fedisableexcept(int mask)
33188e42b60Snakayama {
33288e42b60Snakayama fenv_t old_r, new_r;
33388e42b60Snakayama
33488e42b60Snakayama __stfsr(&old_r);
33588e42b60Snakayama new_r = old_r & ~((mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT);
33688e42b60Snakayama __ldfsr(new_r);
33788e42b60Snakayama
33888e42b60Snakayama return (old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT;
33988e42b60Snakayama }
34088e42b60Snakayama
34188e42b60Snakayama int
fegetexcept(void)34288e42b60Snakayama fegetexcept(void)
34388e42b60Snakayama {
34488e42b60Snakayama fenv_t r;
34588e42b60Snakayama
34688e42b60Snakayama __stfsr(&r);
34788e42b60Snakayama return (r & _ENABLE_MASK) >> _FPUSW_SHIFT;
34888e42b60Snakayama }
349