xref: /openbsd-src/lib/libm/arch/hppa/fenv.c (revision 642e4c0cb181538041ae2d033c37186e1a4d31dc)
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