1*642e4c0cSmiod /* $OpenBSD: fenv.c,v 1.7 2023/01/27 16:43:33 miod Exp $ */
289cfafb2Smartynas
389cfafb2Smartynas /*
489cfafb2Smartynas * Copyright (c) 2011 Martynas Venckus <martynas@openbsd.org>
589cfafb2Smartynas *
689cfafb2Smartynas * Permission to use, copy, modify, and distribute this software for any
789cfafb2Smartynas * purpose with or without fee is hereby granted, provided that the above
889cfafb2Smartynas * copyright notice and this permission notice appear in all copies.
989cfafb2Smartynas *
1089cfafb2Smartynas * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1189cfafb2Smartynas * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1289cfafb2Smartynas * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1389cfafb2Smartynas * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1489cfafb2Smartynas * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1589cfafb2Smartynas * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1689cfafb2Smartynas * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1789cfafb2Smartynas */
1889cfafb2Smartynas
1989cfafb2Smartynas #include <fenv.h>
2089cfafb2Smartynas
21d6f349c8Smartynas union u {
22d6f349c8Smartynas unsigned long long fpsr;
23d6f349c8Smartynas unsigned int bits[2];
24d6f349c8Smartynas };
25d6f349c8Smartynas
2689cfafb2Smartynas /*
2789cfafb2Smartynas * The following constant represents the default floating-point environment
2889cfafb2Smartynas * (that is, the one installed at program startup) and has type pointer to
2989cfafb2Smartynas * const-qualified fenv_t.
3089cfafb2Smartynas *
3189cfafb2Smartynas * It can be used as an argument to the functions within the <fenv.h> header
3289cfafb2Smartynas * that manage the floating-point environment, namely fesetenv() and
3389cfafb2Smartynas * feupdateenv().
3489cfafb2Smartynas */
3589cfafb2Smartynas fenv_t __fe_dfl_env = 0;
3689cfafb2Smartynas
3789cfafb2Smartynas /*
3889cfafb2Smartynas * The feclearexcept() function clears the supported floating-point exceptions
3989cfafb2Smartynas * represented by `excepts'.
4089cfafb2Smartynas */
4189cfafb2Smartynas int
feclearexcept(int excepts)4289cfafb2Smartynas feclearexcept(int excepts)
4389cfafb2Smartynas {
44d6f349c8Smartynas volatile union u u;
4589cfafb2Smartynas
4689cfafb2Smartynas excepts &= FE_ALL_EXCEPT;
4789cfafb2Smartynas
4889cfafb2Smartynas /* Store the current floating-point status register */
49b5aa3b33Sguenther __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
50d6f349c8Smartynas "r" (&u.fpsr));
5189cfafb2Smartynas
5289cfafb2Smartynas /* Clear the requested floating-point exceptions */
53d6f349c8Smartynas u.bits[0] &= ~(excepts << _MASK_SHIFT);
5489cfafb2Smartynas
5589cfafb2Smartynas /* Load the floating-point status register */
56*642e4c0cSmiod __asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr), "m" (u.fpsr));
5789cfafb2Smartynas
5889cfafb2Smartynas return (0);
5989cfafb2Smartynas }
602f2c0062Sguenther DEF_STD(feclearexcept);
6189cfafb2Smartynas
6289cfafb2Smartynas /*
6389cfafb2Smartynas * The fegetexceptflag() function stores an implementation-defined
6489cfafb2Smartynas * representation of the states of the floating-point status flags indicated by
6589cfafb2Smartynas * the argument excepts in the object pointed to by the argument flagp.
6689cfafb2Smartynas */
6789cfafb2Smartynas int
fegetexceptflag(fexcept_t * flagp,int excepts)6889cfafb2Smartynas fegetexceptflag(fexcept_t *flagp, int excepts)
6989cfafb2Smartynas {
70d6f349c8Smartynas volatile union u u;
7189cfafb2Smartynas
7289cfafb2Smartynas excepts &= FE_ALL_EXCEPT;
7389cfafb2Smartynas
7489cfafb2Smartynas /* Store the current floating-point status register */
75b5aa3b33Sguenther __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
76d6f349c8Smartynas "r" (&u.fpsr));
7789cfafb2Smartynas
7889cfafb2Smartynas /* Store the results in flagp */
79d6f349c8Smartynas *flagp = (u.bits[0] >> _MASK_SHIFT) & excepts;
8089cfafb2Smartynas
8189cfafb2Smartynas return (0);
8289cfafb2Smartynas }
8389cfafb2Smartynas
8489cfafb2Smartynas /*
8589cfafb2Smartynas * The feraiseexcept() function raises the supported floating-point exceptions
8689cfafb2Smartynas * represented by the argument `excepts'.
8789cfafb2Smartynas */
8889cfafb2Smartynas int
feraiseexcept(int excepts)8989cfafb2Smartynas feraiseexcept(int excepts)
9089cfafb2Smartynas {
9189cfafb2Smartynas volatile double d;
9289cfafb2Smartynas
9389cfafb2Smartynas excepts &= FE_ALL_EXCEPT;
9489cfafb2Smartynas
9589cfafb2Smartynas /*
9689cfafb2Smartynas * With a compiler that supports the FENV_ACCESS pragma
9789cfafb2Smartynas * properly, simple expressions like '0.0 / 0.0' should
9889cfafb2Smartynas * be sufficient to generate traps. Unfortunately, we
9989cfafb2Smartynas * need to bring a volatile variable into the equation
10089cfafb2Smartynas * to prevent incorrect optimizations.
10189cfafb2Smartynas */
10289cfafb2Smartynas if (excepts & FE_INVALID) {
10389cfafb2Smartynas d = 0.0;
10489cfafb2Smartynas d = 0.0 / d;
10589cfafb2Smartynas }
10689cfafb2Smartynas if (excepts & FE_DIVBYZERO) {
10789cfafb2Smartynas d = 0.0;
10889cfafb2Smartynas d = 1.0 / d;
10989cfafb2Smartynas }
11089cfafb2Smartynas if (excepts & FE_OVERFLOW) {
11189cfafb2Smartynas d = 0x1.ffp1023;
11289cfafb2Smartynas d *= 2.0;
11389cfafb2Smartynas }
11489cfafb2Smartynas if (excepts & FE_UNDERFLOW) {
11589cfafb2Smartynas d = 0x1p-1022;
11689cfafb2Smartynas d /= 0x1p1023;
11789cfafb2Smartynas }
11889cfafb2Smartynas if (excepts & FE_INEXACT) {
11989cfafb2Smartynas d = 0x1p-1022;
12089cfafb2Smartynas d += 1.0;
12189cfafb2Smartynas }
122b5aa3b33Sguenther __asm__ volatile ("fldd 0(%%sr0,%%sp), %0" : "=f" (d));
12389cfafb2Smartynas
12489cfafb2Smartynas return (0);
12589cfafb2Smartynas }
1262f2c0062Sguenther DEF_STD(feraiseexcept);
12789cfafb2Smartynas
12889cfafb2Smartynas /*
12989cfafb2Smartynas * This function sets the floating-point status flags indicated by the argument
13089cfafb2Smartynas * `excepts' to the states stored in the object pointed to by `flagp'. It does
13189cfafb2Smartynas * NOT raise any floating-point exceptions, but only sets the state of the flags.
13289cfafb2Smartynas */
13389cfafb2Smartynas int
fesetexceptflag(const fexcept_t * flagp,int excepts)13489cfafb2Smartynas fesetexceptflag(const fexcept_t *flagp, int excepts)
13589cfafb2Smartynas {
136d6f349c8Smartynas volatile union u u;
13789cfafb2Smartynas
13889cfafb2Smartynas excepts &= FE_ALL_EXCEPT;
13989cfafb2Smartynas
14089cfafb2Smartynas /* Store the current floating-point status register */
141b5aa3b33Sguenther __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
142d6f349c8Smartynas "r" (&u.fpsr));
14389cfafb2Smartynas
14489cfafb2Smartynas /* Set the requested status flags */
145d6f349c8Smartynas u.bits[0] &= ~(excepts << _MASK_SHIFT);
146d6f349c8Smartynas u.bits[0] |= (*flagp & excepts) << _MASK_SHIFT;
14789cfafb2Smartynas
14889cfafb2Smartynas /* Load the floating-point status register */
149*642e4c0cSmiod __asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr), "m" (u.fpsr));
15089cfafb2Smartynas
15189cfafb2Smartynas return (0);
15289cfafb2Smartynas }
1532f2c0062Sguenther DEF_STD(fesetexceptflag);
15489cfafb2Smartynas
15589cfafb2Smartynas /*
15689cfafb2Smartynas * The fetestexcept() function determines which of a specified subset of the
15789cfafb2Smartynas * floating-point exception flags are currently set. The `excepts' argument
15889cfafb2Smartynas * specifies the floating-point status flags to be queried.
15989cfafb2Smartynas */
16089cfafb2Smartynas int
fetestexcept(int excepts)16189cfafb2Smartynas fetestexcept(int excepts)
16289cfafb2Smartynas {
163d6f349c8Smartynas volatile union u u;
16489cfafb2Smartynas
16589cfafb2Smartynas excepts &= FE_ALL_EXCEPT;
16689cfafb2Smartynas
16789cfafb2Smartynas /* Store the current floating-point status register */
168b5aa3b33Sguenther __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
169d6f349c8Smartynas "r" (&u.fpsr));
17089cfafb2Smartynas
171d6f349c8Smartynas return ((u.bits[0] >> _MASK_SHIFT) & excepts);
17289cfafb2Smartynas }
1732f2c0062Sguenther DEF_STD(fetestexcept);
17489cfafb2Smartynas
17589cfafb2Smartynas /*
17689cfafb2Smartynas * The fegetround() function gets the current rounding direction.
17789cfafb2Smartynas */
17889cfafb2Smartynas int
fegetround(void)17989cfafb2Smartynas fegetround(void)
18089cfafb2Smartynas {
181d6f349c8Smartynas volatile union u u;
18289cfafb2Smartynas
183d6f349c8Smartynas /* Store the current floating-point status register */
184b5aa3b33Sguenther __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
185d6f349c8Smartynas "r" (&u.fpsr));
18689cfafb2Smartynas
187d6f349c8Smartynas return (u.bits[0] & _ROUND_MASK);
18889cfafb2Smartynas }
1892f2c0062Sguenther DEF_STD(fegetround);
19089cfafb2Smartynas
19189cfafb2Smartynas /*
19289cfafb2Smartynas * The fesetround() function establishes the rounding direction represented by
19389cfafb2Smartynas * its argument `round'. If the argument is not equal to the value of a rounding
19489cfafb2Smartynas * direction macro, the rounding direction is not changed.
19589cfafb2Smartynas */
19689cfafb2Smartynas int
fesetround(int round)19789cfafb2Smartynas fesetround(int round)
19889cfafb2Smartynas {
199d6f349c8Smartynas volatile union u u;
20089cfafb2Smartynas
20189cfafb2Smartynas /* Check whether requested rounding direction is supported */
20289cfafb2Smartynas if (round & ~_ROUND_MASK)
20389cfafb2Smartynas return (-1);
20489cfafb2Smartynas
20589cfafb2Smartynas /* Store the current floating-point status register */
206b5aa3b33Sguenther __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
207d6f349c8Smartynas "r" (&u.fpsr));
20889cfafb2Smartynas
209d6f349c8Smartynas /* Set the rounding direction */
210d6f349c8Smartynas u.bits[0] &= ~_ROUND_MASK;
211d6f349c8Smartynas u.bits[0] |= round;
21289cfafb2Smartynas
21389cfafb2Smartynas /* Load the floating-point status register */
214*642e4c0cSmiod __asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr), "m"(u.fpsr));
21589cfafb2Smartynas
21689cfafb2Smartynas return (0);
21789cfafb2Smartynas }
2182f2c0062Sguenther DEF_STD(fesetround);
21989cfafb2Smartynas
22089cfafb2Smartynas /*
22189cfafb2Smartynas * The fegetenv() function attempts to store the current floating-point
22289cfafb2Smartynas * environment in the object pointed to by envp.
22389cfafb2Smartynas */
22489cfafb2Smartynas int
fegetenv(fenv_t * envp)22589cfafb2Smartynas fegetenv(fenv_t *envp)
22689cfafb2Smartynas {
227d6f349c8Smartynas volatile union u u;
22889cfafb2Smartynas
22989cfafb2Smartynas /* Store the current floating-point status register */
230b5aa3b33Sguenther __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
231d6f349c8Smartynas "r" (&u.fpsr));
23289cfafb2Smartynas
233d6f349c8Smartynas *envp = u.bits[0];
23489cfafb2Smartynas
23589cfafb2Smartynas return (0);
23689cfafb2Smartynas }
2372f2c0062Sguenther DEF_STD(fegetenv);
23889cfafb2Smartynas
23989cfafb2Smartynas /*
24089cfafb2Smartynas * The feholdexcept() function saves the current floating-point environment
24189cfafb2Smartynas * in the object pointed to by envp, clears the floating-point status flags, and
24289cfafb2Smartynas * then installs a non-stop (continue on floating-point exceptions) mode, if
24389cfafb2Smartynas * available, for all floating-point exceptions.
24489cfafb2Smartynas */
24589cfafb2Smartynas int
feholdexcept(fenv_t * envp)24689cfafb2Smartynas feholdexcept(fenv_t *envp)
24789cfafb2Smartynas {
248d6f349c8Smartynas volatile union u u;
24989cfafb2Smartynas
25089cfafb2Smartynas /* Store the current floating-point status register */
251b5aa3b33Sguenther __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
252d6f349c8Smartynas "r" (&u.fpsr));
25389cfafb2Smartynas
254d6f349c8Smartynas *envp = u.bits[0];
25589cfafb2Smartynas
25689cfafb2Smartynas /* Clear exception flags in FPSR */
257d6f349c8Smartynas u.bits[0] &= ~(FE_ALL_EXCEPT << _MASK_SHIFT);
25889cfafb2Smartynas
25989cfafb2Smartynas /* Mask all exceptions */
260d6f349c8Smartynas u.bits[0] &= ~FE_ALL_EXCEPT;
261*642e4c0cSmiod __asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr), "m"(u.fpsr));
26289cfafb2Smartynas
26389cfafb2Smartynas return (0);
26489cfafb2Smartynas }
2652f2c0062Sguenther DEF_STD(feholdexcept);
26689cfafb2Smartynas
26789cfafb2Smartynas /*
26889cfafb2Smartynas * The fesetenv() function attempts to establish the floating-point environment
26989cfafb2Smartynas * represented by the object pointed to by envp. The argument `envp' points
27089cfafb2Smartynas * to an object set by a call to fegetenv() or feholdexcept(), or equal a
27189cfafb2Smartynas * floating-point environment macro. The fesetenv() function does not raise
27289cfafb2Smartynas * floating-point exceptions, but only installs the state of the floating-point
27389cfafb2Smartynas * status flags represented through its argument.
27489cfafb2Smartynas */
27589cfafb2Smartynas int
fesetenv(const fenv_t * envp)27689cfafb2Smartynas fesetenv(const fenv_t *envp)
27789cfafb2Smartynas {
278d6f349c8Smartynas volatile union u u;
27989cfafb2Smartynas
28089cfafb2Smartynas /* Store the current floating-point status register */
281b5aa3b33Sguenther __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
282d6f349c8Smartynas "r" (&u.fpsr));
28389cfafb2Smartynas
28489cfafb2Smartynas /* Set the requested flags */
285d6f349c8Smartynas u.bits[0] &= ~(FE_ALL_EXCEPT | _ROUND_MASK |
286d6f349c8Smartynas (FE_ALL_EXCEPT << _MASK_SHIFT));
287d6f349c8Smartynas u.bits[0] |= *envp & (FE_ALL_EXCEPT | _ROUND_MASK |
288d6f349c8Smartynas (FE_ALL_EXCEPT << _MASK_SHIFT));
28989cfafb2Smartynas
29089cfafb2Smartynas /* Load the floating-point status register */
291*642e4c0cSmiod __asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr), "m"(u.fpsr));
29289cfafb2Smartynas
29389cfafb2Smartynas return (0);
29489cfafb2Smartynas }
2952f2c0062Sguenther DEF_STD(fesetenv);
29689cfafb2Smartynas
29789cfafb2Smartynas /*
29889cfafb2Smartynas * The feupdateenv() function saves the currently raised floating-point
29989cfafb2Smartynas * exceptions in its automatic storage, installs the floating-point environment
30089cfafb2Smartynas * represented by the object pointed to by `envp', and then raises the saved
30189cfafb2Smartynas * floating-point exceptions. The argument `envp' shall point to an object set
30289cfafb2Smartynas * by a call to feholdexcept() or fegetenv(), or equal a floating-point
30389cfafb2Smartynas * environment macro.
30489cfafb2Smartynas */
30589cfafb2Smartynas int
feupdateenv(const fenv_t * envp)30689cfafb2Smartynas feupdateenv(const fenv_t *envp)
30789cfafb2Smartynas {
308d6f349c8Smartynas volatile union u u;
30989cfafb2Smartynas
31089cfafb2Smartynas /* Store the current floating-point status register */
311b5aa3b33Sguenther __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
312d6f349c8Smartynas "r" (&u.fpsr));
31389cfafb2Smartynas
31489cfafb2Smartynas /* Install new floating-point environment */
31589cfafb2Smartynas fesetenv(envp);
31689cfafb2Smartynas
31789cfafb2Smartynas /* Raise any previously accumulated exceptions */
318d6f349c8Smartynas feraiseexcept(u.bits[0] >> _MASK_SHIFT);
31989cfafb2Smartynas
32089cfafb2Smartynas return (0);
32189cfafb2Smartynas }
3222f2c0062Sguenther DEF_STD(feupdateenv);
32389cfafb2Smartynas
32489cfafb2Smartynas /*
3252c53affbSjmc * The following functions are extensions to the standard
32689cfafb2Smartynas */
32789cfafb2Smartynas int
feenableexcept(int mask)32889cfafb2Smartynas feenableexcept(int mask)
32989cfafb2Smartynas {
330d6f349c8Smartynas volatile union u u;
33189cfafb2Smartynas unsigned int omask;
33289cfafb2Smartynas
33389cfafb2Smartynas mask &= FE_ALL_EXCEPT;
33489cfafb2Smartynas
33589cfafb2Smartynas /* Store the current floating-point status register */
336b5aa3b33Sguenther __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
337d6f349c8Smartynas "r" (&u.fpsr));
33889cfafb2Smartynas
339d6f349c8Smartynas omask = u.bits[0] & FE_ALL_EXCEPT;
340d6f349c8Smartynas u.bits[0] |= mask;
34189cfafb2Smartynas
34289cfafb2Smartynas /* Load the floating-point status register */
343*642e4c0cSmiod __asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr), "m"(u.fpsr));
34489cfafb2Smartynas
34589cfafb2Smartynas return (omask);
34689cfafb2Smartynas
34789cfafb2Smartynas }
34889cfafb2Smartynas
34989cfafb2Smartynas int
fedisableexcept(int mask)35089cfafb2Smartynas fedisableexcept(int mask)
35189cfafb2Smartynas {
352d6f349c8Smartynas volatile union u u;
35389cfafb2Smartynas unsigned int omask;
35489cfafb2Smartynas
35589cfafb2Smartynas mask &= FE_ALL_EXCEPT;
35689cfafb2Smartynas
35789cfafb2Smartynas /* Store the current floating-point status register */
358b5aa3b33Sguenther __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
359d6f349c8Smartynas "r" (&u.fpsr));
36089cfafb2Smartynas
361d6f349c8Smartynas omask = u.bits[0] & FE_ALL_EXCEPT;
362d6f349c8Smartynas u.bits[0] &= ~mask;
36389cfafb2Smartynas
36489cfafb2Smartynas /* Load the floating-point status register */
365*642e4c0cSmiod __asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr), "m"(u.fpsr));
36689cfafb2Smartynas
36789cfafb2Smartynas return (omask);
36889cfafb2Smartynas }
36989cfafb2Smartynas
37089cfafb2Smartynas int
fegetexcept(void)37189cfafb2Smartynas fegetexcept(void)
37289cfafb2Smartynas {
373d6f349c8Smartynas volatile union u u;
37489cfafb2Smartynas
37589cfafb2Smartynas /* Store the current floating-point status register */
376b5aa3b33Sguenther __asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
377d6f349c8Smartynas "r" (&u.fpsr));
37889cfafb2Smartynas
379d6f349c8Smartynas return (u.bits[0] & FE_ALL_EXCEPT);
38089cfafb2Smartynas }
381