1*2c53affbSjmc /* $OpenBSD: fenv.c,v 1.6 2022/12/27 17:10:07 jmc Exp $ */
26e40b843Smartynas
36e40b843Smartynas /*
46e40b843Smartynas * Copyright (c) 2011 Martynas Venckus <martynas@openbsd.org>
56e40b843Smartynas *
66e40b843Smartynas * Permission to use, copy, modify, and distribute this software for any
76e40b843Smartynas * purpose with or without fee is hereby granted, provided that the above
86e40b843Smartynas * copyright notice and this permission notice appear in all copies.
96e40b843Smartynas *
106e40b843Smartynas * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
116e40b843Smartynas * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
126e40b843Smartynas * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
136e40b843Smartynas * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
146e40b843Smartynas * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
156e40b843Smartynas * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
166e40b843Smartynas * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
176e40b843Smartynas */
186e40b843Smartynas
196e40b843Smartynas #include <fenv.h>
206e40b843Smartynas
216e40b843Smartynas extern unsigned int __fpscr_values[2];
226e40b843Smartynas
236e40b843Smartynas /*
246e40b843Smartynas * The following constant represents the default floating-point environment
256e40b843Smartynas * (that is, the one installed at program startup) and has type pointer to
266e40b843Smartynas * const-qualified fenv_t.
276e40b843Smartynas *
286e40b843Smartynas * It can be used as an argument to the functions within the <fenv.h> header
296e40b843Smartynas * that manage the floating-point environment, namely fesetenv() and
306e40b843Smartynas * feupdateenv().
316e40b843Smartynas */
326e40b843Smartynas fenv_t __fe_dfl_env = 0xc0000;
336e40b843Smartynas
346e40b843Smartynas /*
356e40b843Smartynas * The feclearexcept() function clears the supported floating-point exceptions
366e40b843Smartynas * represented by `excepts'.
376e40b843Smartynas */
386e40b843Smartynas int
feclearexcept(int excepts)396e40b843Smartynas feclearexcept(int excepts)
406e40b843Smartynas {
416e40b843Smartynas unsigned int fpscr;
426e40b843Smartynas
436e40b843Smartynas excepts &= FE_ALL_EXCEPT;
446e40b843Smartynas
45d6f349c8Smartynas /* Store the current floating-point status and control register */
46b5aa3b33Sguenther __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
476e40b843Smartynas
486e40b843Smartynas /* Clear the requested floating-point exceptions */
496e40b843Smartynas fpscr &= ~excepts;
506e40b843Smartynas __fpscr_values[0] &= ~excepts;
516e40b843Smartynas __fpscr_values[1] &= ~excepts;
526e40b843Smartynas
53d6f349c8Smartynas /* Load the floating-point status and control register */
54b5aa3b33Sguenther __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
556e40b843Smartynas
566e40b843Smartynas return (0);
576e40b843Smartynas }
582f2c0062Sguenther DEF_STD(feclearexcept);
596e40b843Smartynas
606e40b843Smartynas /*
616e40b843Smartynas * The fegetexceptflag() function stores an implementation-defined
626e40b843Smartynas * representation of the states of the floating-point status flags indicated by
636e40b843Smartynas * the argument excepts in the object pointed to by the argument flagp.
646e40b843Smartynas */
656e40b843Smartynas int
fegetexceptflag(fexcept_t * flagp,int excepts)666e40b843Smartynas fegetexceptflag(fexcept_t *flagp, int excepts)
676e40b843Smartynas {
686e40b843Smartynas unsigned int fpscr;
696e40b843Smartynas
706e40b843Smartynas excepts &= FE_ALL_EXCEPT;
716e40b843Smartynas
72d6f349c8Smartynas /* Store the current floating-point status and control register */
73b5aa3b33Sguenther __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
746e40b843Smartynas
756e40b843Smartynas /* Store the results in flagp */
766e40b843Smartynas *flagp = fpscr & excepts;
776e40b843Smartynas
786e40b843Smartynas return (0);
796e40b843Smartynas }
806e40b843Smartynas
816e40b843Smartynas /*
826e40b843Smartynas * The feraiseexcept() function raises the supported floating-point exceptions
836e40b843Smartynas * represented by the argument `excepts'.
846e40b843Smartynas */
856e40b843Smartynas int
feraiseexcept(int excepts)866e40b843Smartynas feraiseexcept(int excepts)
876e40b843Smartynas {
886e40b843Smartynas volatile double d;
896e40b843Smartynas
906e40b843Smartynas excepts &= FE_ALL_EXCEPT;
916e40b843Smartynas
926e40b843Smartynas /*
936e40b843Smartynas * With a compiler that supports the FENV_ACCESS pragma
946e40b843Smartynas * properly, simple expressions like '0.0 / 0.0' should
956e40b843Smartynas * be sufficient to generate traps. Unfortunately, we
966e40b843Smartynas * need to bring a volatile variable into the equation
976e40b843Smartynas * to prevent incorrect optimizations.
986e40b843Smartynas */
996e40b843Smartynas if (excepts & FE_INVALID) {
1006e40b843Smartynas d = 0.0;
1016e40b843Smartynas d = 0.0 / d;
1026e40b843Smartynas }
1036e40b843Smartynas if (excepts & FE_DIVBYZERO) {
1046e40b843Smartynas d = 0.0;
1056e40b843Smartynas d = 1.0 / d;
1066e40b843Smartynas }
1076e40b843Smartynas if (excepts & FE_OVERFLOW) {
1086e40b843Smartynas d = 0x1.ffp1023;
1096e40b843Smartynas d *= 2.0;
1106e40b843Smartynas }
1116e40b843Smartynas if (excepts & FE_UNDERFLOW) {
1126e40b843Smartynas d = 0x1p-1022;
1136e40b843Smartynas d /= 0x1p1023;
1146e40b843Smartynas }
1156e40b843Smartynas if (excepts & FE_INEXACT) {
1166e40b843Smartynas d = 0x1p-1022;
1176e40b843Smartynas d += 1.0;
1186e40b843Smartynas }
1196e40b843Smartynas
1206e40b843Smartynas return (0);
1216e40b843Smartynas }
1222f2c0062Sguenther DEF_STD(feraiseexcept);
1236e40b843Smartynas
1246e40b843Smartynas /*
1256e40b843Smartynas * This function sets the floating-point status flags indicated by the argument
1266e40b843Smartynas * `excepts' to the states stored in the object pointed to by `flagp'. It does
1276e40b843Smartynas * NOT raise any floating-point exceptions, but only sets the state of the flags.
1286e40b843Smartynas */
1296e40b843Smartynas int
fesetexceptflag(const fexcept_t * flagp,int excepts)1306e40b843Smartynas fesetexceptflag(const fexcept_t *flagp, int excepts)
1316e40b843Smartynas {
1326e40b843Smartynas unsigned int fpscr;
1336e40b843Smartynas
1346e40b843Smartynas excepts &= FE_ALL_EXCEPT;
1356e40b843Smartynas
136d6f349c8Smartynas /* Store the current floating-point status and control register */
137b5aa3b33Sguenther __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
1386e40b843Smartynas
1396e40b843Smartynas /* Set the requested status flags */
1406e40b843Smartynas fpscr &= ~excepts;
1416e40b843Smartynas __fpscr_values[0] &= ~excepts;
1426e40b843Smartynas __fpscr_values[1] &= ~excepts;
1436e40b843Smartynas
1446e40b843Smartynas fpscr |= *flagp & excepts;
1456e40b843Smartynas __fpscr_values[0] |= *flagp & excepts;
1466e40b843Smartynas __fpscr_values[1] |= *flagp & excepts;
1476e40b843Smartynas
148d6f349c8Smartynas /* Load the floating-point status and control register */
149b5aa3b33Sguenther __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
1506e40b843Smartynas
1516e40b843Smartynas return (0);
1526e40b843Smartynas }
1532f2c0062Sguenther DEF_STD(fesetexceptflag);
1546e40b843Smartynas
1556e40b843Smartynas /*
1566e40b843Smartynas * The fetestexcept() function determines which of a specified subset of the
1576e40b843Smartynas * floating-point exception flags are currently set. The `excepts' argument
1586e40b843Smartynas * specifies the floating-point status flags to be queried.
1596e40b843Smartynas */
1606e40b843Smartynas int
fetestexcept(int excepts)1616e40b843Smartynas fetestexcept(int excepts)
1626e40b843Smartynas {
1636e40b843Smartynas unsigned int fpscr;
1646e40b843Smartynas
1656e40b843Smartynas excepts &= FE_ALL_EXCEPT;
1666e40b843Smartynas
167d6f349c8Smartynas /* Store the current floating-point status and control register */
168b5aa3b33Sguenther __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
1696e40b843Smartynas
1706e40b843Smartynas return (fpscr & excepts);
1716e40b843Smartynas }
1722f2c0062Sguenther DEF_STD(fetestexcept);
1736e40b843Smartynas
1746e40b843Smartynas /*
1756e40b843Smartynas * The fegetround() function gets the current rounding direction.
1766e40b843Smartynas */
1776e40b843Smartynas int
fegetround(void)1786e40b843Smartynas fegetround(void)
1796e40b843Smartynas {
1806e40b843Smartynas unsigned int fpscr;
1816e40b843Smartynas
182d6f349c8Smartynas /* Store the current floating-point status and control register */
183b5aa3b33Sguenther __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
1846e40b843Smartynas
1856e40b843Smartynas return (fpscr & _ROUND_MASK);
1866e40b843Smartynas }
1872f2c0062Sguenther DEF_STD(fegetround);
1886e40b843Smartynas
1896e40b843Smartynas /*
1906e40b843Smartynas * The fesetround() function establishes the rounding direction represented by
1916e40b843Smartynas * its argument `round'. If the argument is not equal to the value of a rounding
1926e40b843Smartynas * direction macro, the rounding direction is not changed.
1936e40b843Smartynas */
1946e40b843Smartynas int
fesetround(int round)1956e40b843Smartynas fesetround(int round)
1966e40b843Smartynas {
1976e40b843Smartynas unsigned int fpscr;
1986e40b843Smartynas
1996e40b843Smartynas /* Check whether requested rounding direction is supported */
2006e40b843Smartynas if (round & ~_ROUND_MASK)
2016e40b843Smartynas return (-1);
2026e40b843Smartynas
203d6f349c8Smartynas /* Store the current floating-point status and control register */
204b5aa3b33Sguenther __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
2056e40b843Smartynas
206d6f349c8Smartynas /* Set the rounding direction */
2076e40b843Smartynas fpscr &= ~_ROUND_MASK;
2086e40b843Smartynas __fpscr_values[0] &= ~_ROUND_MASK;
2096e40b843Smartynas __fpscr_values[1] &= ~_ROUND_MASK;
2106e40b843Smartynas
2116e40b843Smartynas fpscr |= round;
2126e40b843Smartynas __fpscr_values[0] |= round;
2136e40b843Smartynas __fpscr_values[1] |= round;
2146e40b843Smartynas
215d6f349c8Smartynas /* Load the floating-point status and control register */
216b5aa3b33Sguenther __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
2176e40b843Smartynas
2186e40b843Smartynas return (0);
2196e40b843Smartynas }
2202f2c0062Sguenther DEF_STD(fesetround);
2216e40b843Smartynas
2226e40b843Smartynas /*
2236e40b843Smartynas * The fegetenv() function attempts to store the current floating-point
2246e40b843Smartynas * environment in the object pointed to by envp.
2256e40b843Smartynas */
2266e40b843Smartynas int
fegetenv(fenv_t * envp)2276e40b843Smartynas fegetenv(fenv_t *envp)
2286e40b843Smartynas {
229d6f349c8Smartynas /* Store the current floating-point status and control register */
230b5aa3b33Sguenther __asm__ volatile ("sts fpscr, %0" : "=r" (*envp));
2316e40b843Smartynas
2326e40b843Smartynas return (0);
2336e40b843Smartynas }
2342f2c0062Sguenther DEF_STD(fegetenv);
2356e40b843Smartynas
2366e40b843Smartynas /*
2376e40b843Smartynas * The feholdexcept() function saves the current floating-point environment
2386e40b843Smartynas * in the object pointed to by envp, clears the floating-point status flags, and
2396e40b843Smartynas * then installs a non-stop (continue on floating-point exceptions) mode, if
2406e40b843Smartynas * available, for all floating-point exceptions.
2416e40b843Smartynas */
2426e40b843Smartynas int
feholdexcept(fenv_t * envp)2436e40b843Smartynas feholdexcept(fenv_t *envp)
2446e40b843Smartynas {
2456e40b843Smartynas unsigned int fpscr;
2466e40b843Smartynas
247d6f349c8Smartynas /* Store the current floating-point status and control register */
248b5aa3b33Sguenther __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
2496e40b843Smartynas
2506e40b843Smartynas *envp = fpscr;
2516e40b843Smartynas
252d6f349c8Smartynas /* Clear exception flags in FPSCR */
2536e40b843Smartynas fpscr &= ~FE_ALL_EXCEPT;
2546e40b843Smartynas __fpscr_values[0] &= ~FE_ALL_EXCEPT;
2556e40b843Smartynas __fpscr_values[1] &= ~FE_ALL_EXCEPT;
2566e40b843Smartynas
2576e40b843Smartynas /* Mask all exceptions */
258d6f349c8Smartynas fpscr &= ~(FE_ALL_EXCEPT << _MASK_SHIFT);
259d6f349c8Smartynas __fpscr_values[0] &= ~(FE_ALL_EXCEPT << _MASK_SHIFT);
260d6f349c8Smartynas __fpscr_values[1] &= ~(FE_ALL_EXCEPT << _MASK_SHIFT);
2616e40b843Smartynas
262d6f349c8Smartynas /* Load the floating-point status and control register */
263b5aa3b33Sguenther __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
2646e40b843Smartynas
2656e40b843Smartynas return (0);
2666e40b843Smartynas }
2672f2c0062Sguenther DEF_STD(feholdexcept);
2686e40b843Smartynas
2696e40b843Smartynas /*
2706e40b843Smartynas * The fesetenv() function attempts to establish the floating-point environment
2716e40b843Smartynas * represented by the object pointed to by envp. The argument `envp' points
2726e40b843Smartynas * to an object set by a call to fegetenv() or feholdexcept(), or equal a
2736e40b843Smartynas * floating-point environment macro. The fesetenv() function does not raise
2746e40b843Smartynas * floating-point exceptions, but only installs the state of the floating-point
2756e40b843Smartynas * status flags represented through its argument.
2766e40b843Smartynas */
2776e40b843Smartynas int
fesetenv(const fenv_t * envp)2786e40b843Smartynas fesetenv(const fenv_t *envp)
2796e40b843Smartynas {
2806e40b843Smartynas unsigned int fpscr;
2816e40b843Smartynas
282d6f349c8Smartynas /* Store the current floating-point status and control register */
283b5aa3b33Sguenther __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
2846e40b843Smartynas
2856e40b843Smartynas /* Set the requested flags */
286d6f349c8Smartynas fpscr &= ~((FE_ALL_EXCEPT << _MASK_SHIFT) | _ROUND_MASK |
2876e40b843Smartynas FE_ALL_EXCEPT);
288d6f349c8Smartynas __fpscr_values[0] &= ~((FE_ALL_EXCEPT << _MASK_SHIFT) | _ROUND_MASK |
2896e40b843Smartynas FE_ALL_EXCEPT);
290d6f349c8Smartynas __fpscr_values[1] &= ~((FE_ALL_EXCEPT << _MASK_SHIFT) | _ROUND_MASK |
2916e40b843Smartynas FE_ALL_EXCEPT);
2926e40b843Smartynas
293d6f349c8Smartynas fpscr |= *envp & ((FE_ALL_EXCEPT << _MASK_SHIFT) | _ROUND_MASK |
2946e40b843Smartynas FE_ALL_EXCEPT);
295d6f349c8Smartynas __fpscr_values[0] |= *envp & ((FE_ALL_EXCEPT << _MASK_SHIFT) |
2966e40b843Smartynas _ROUND_MASK | FE_ALL_EXCEPT);
297d6f349c8Smartynas __fpscr_values[1] |= *envp & ((FE_ALL_EXCEPT << _MASK_SHIFT) |
2986e40b843Smartynas _ROUND_MASK | FE_ALL_EXCEPT);
2996e40b843Smartynas
300d6f349c8Smartynas /* Load the floating-point status and control register */
301b5aa3b33Sguenther __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
3026e40b843Smartynas
3036e40b843Smartynas return (0);
3046e40b843Smartynas }
3052f2c0062Sguenther DEF_STD(fesetenv);
3066e40b843Smartynas
3076e40b843Smartynas /*
3086e40b843Smartynas * The feupdateenv() function saves the currently raised floating-point
3096e40b843Smartynas * exceptions in its automatic storage, installs the floating-point environment
3106e40b843Smartynas * represented by the object pointed to by `envp', and then raises the saved
3116e40b843Smartynas * floating-point exceptions. The argument `envp' shall point to an object set
3126e40b843Smartynas * by a call to feholdexcept() or fegetenv(), or equal a floating-point
3136e40b843Smartynas * environment macro.
3146e40b843Smartynas */
3156e40b843Smartynas int
feupdateenv(const fenv_t * envp)3166e40b843Smartynas feupdateenv(const fenv_t *envp)
3176e40b843Smartynas {
3186e40b843Smartynas unsigned int fpscr;
3196e40b843Smartynas
320d6f349c8Smartynas /* Store the current floating-point status and control register */
321b5aa3b33Sguenther __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
3226e40b843Smartynas
3236e40b843Smartynas /* Install new floating-point environment */
3246e40b843Smartynas fesetenv(envp);
3256e40b843Smartynas
3266e40b843Smartynas /* Raise any previously accumulated exceptions */
3276e40b843Smartynas feraiseexcept(fpscr);
3286e40b843Smartynas
3296e40b843Smartynas return (0);
3306e40b843Smartynas }
3312f2c0062Sguenther DEF_STD(feupdateenv);
3326e40b843Smartynas
3336e40b843Smartynas /*
334*2c53affbSjmc * The following functions are extensions to the standard
3356e40b843Smartynas */
3366e40b843Smartynas int
feenableexcept(int mask)3376e40b843Smartynas feenableexcept(int mask)
3386e40b843Smartynas {
3396e40b843Smartynas unsigned int fpscr, omask;
3406e40b843Smartynas
3416e40b843Smartynas mask &= FE_ALL_EXCEPT;
3426e40b843Smartynas
343d6f349c8Smartynas /* Store the current floating-point status and control register */
344b5aa3b33Sguenther __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
3456e40b843Smartynas
346d6f349c8Smartynas omask = (fpscr >> _MASK_SHIFT) & FE_ALL_EXCEPT;
347d6f349c8Smartynas fpscr |= mask << _MASK_SHIFT;
348d6f349c8Smartynas __fpscr_values[0] |= mask << _MASK_SHIFT;
349d6f349c8Smartynas __fpscr_values[1] |= mask << _MASK_SHIFT;
3506e40b843Smartynas
351d6f349c8Smartynas /* Load the floating-point status and control register */
352b5aa3b33Sguenther __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
3536e40b843Smartynas
3546e40b843Smartynas return (omask);
3556e40b843Smartynas
3566e40b843Smartynas }
3576e40b843Smartynas
3586e40b843Smartynas int
fedisableexcept(int mask)3596e40b843Smartynas fedisableexcept(int mask)
3606e40b843Smartynas {
3616e40b843Smartynas unsigned int fpscr, omask;
3626e40b843Smartynas
3636e40b843Smartynas mask &= FE_ALL_EXCEPT;
3646e40b843Smartynas
365d6f349c8Smartynas /* Store the current floating-point status and control register */
366b5aa3b33Sguenther __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
3676e40b843Smartynas
368d6f349c8Smartynas omask = (fpscr >> _MASK_SHIFT) & FE_ALL_EXCEPT;
369d6f349c8Smartynas fpscr &= ~(mask << _MASK_SHIFT);
370d6f349c8Smartynas __fpscr_values[0] &= ~(mask << _MASK_SHIFT);
371d6f349c8Smartynas __fpscr_values[1] &= ~(mask << _MASK_SHIFT);
3726e40b843Smartynas
373d6f349c8Smartynas /* Load the floating-point status and control register */
374b5aa3b33Sguenther __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
3756e40b843Smartynas
3766e40b843Smartynas return (omask);
3776e40b843Smartynas }
3786e40b843Smartynas
3796e40b843Smartynas int
fegetexcept(void)3806e40b843Smartynas fegetexcept(void)
3816e40b843Smartynas {
3826e40b843Smartynas unsigned int fpscr;
3836e40b843Smartynas
384d6f349c8Smartynas /* Store the current floating-point status and control register */
385b5aa3b33Sguenther __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
3866e40b843Smartynas
387d6f349c8Smartynas return ((fpscr >> _MASK_SHIFT) & FE_ALL_EXCEPT);
3886e40b843Smartynas }
389