xref: /openbsd-src/lib/libm/arch/powerpc/fenv.c (revision 2c53affbcc0119d6480b86c18f2790523b6a0aad)
1*2c53affbSjmc /*	$OpenBSD: fenv.c,v 1.6 2022/12/27 17:10:07 jmc Exp $	*/
20503db22Smartynas 
30503db22Smartynas /*
40503db22Smartynas  * Copyright (c) 2011 Martynas Venckus <martynas@openbsd.org>
50503db22Smartynas  *
60503db22Smartynas  * Permission to use, copy, modify, and distribute this software for any
70503db22Smartynas  * purpose with or without fee is hereby granted, provided that the above
80503db22Smartynas  * copyright notice and this permission notice appear in all copies.
90503db22Smartynas  *
100503db22Smartynas  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
110503db22Smartynas  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
120503db22Smartynas  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
130503db22Smartynas  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
140503db22Smartynas  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
150503db22Smartynas  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
160503db22Smartynas  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
170503db22Smartynas  */
180503db22Smartynas 
190503db22Smartynas #include <fenv.h>
200503db22Smartynas 
21d6f349c8Smartynas union u {
22d6f349c8Smartynas 	unsigned long long fpscr;
23d6f349c8Smartynas 	unsigned int bits[2];
24d6f349c8Smartynas };
25d6f349c8Smartynas 
260503db22Smartynas /*
270503db22Smartynas  * The following constant represents the default floating-point environment
280503db22Smartynas  * (that is, the one installed at program startup) and has type pointer to
290503db22Smartynas  * const-qualified fenv_t.
300503db22Smartynas  *
310503db22Smartynas  * It can be used as an argument to the functions within the <fenv.h> header
320503db22Smartynas  * that manage the floating-point environment, namely fesetenv() and
330503db22Smartynas  * feupdateenv().
340503db22Smartynas  */
350503db22Smartynas fenv_t __fe_dfl_env = 0;
360503db22Smartynas 
370503db22Smartynas /*
380503db22Smartynas  * The feclearexcept() function clears the supported floating-point exceptions
390503db22Smartynas  * represented by `excepts'.
400503db22Smartynas  */
410503db22Smartynas int
feclearexcept(int excepts)420503db22Smartynas feclearexcept(int excepts)
430503db22Smartynas {
44d6f349c8Smartynas 	union u u;
450503db22Smartynas 	excepts &= FE_ALL_EXCEPT;
460503db22Smartynas 
47d6f349c8Smartynas 	/* Store the current floating-point status and control register */
48b5aa3b33Sguenther 	__asm__ volatile ("mffs %0" : "=f" (u.fpscr));
490503db22Smartynas 
500503db22Smartynas 	/* Clear the requested floating-point exceptions */
51d6f349c8Smartynas 	u.bits[1] &= ~excepts;
520503db22Smartynas 	if (excepts & FE_INVALID)
53d6f349c8Smartynas 		u.bits[1] &= ~_FE_INVALID_ALL;
540503db22Smartynas 
55d6f349c8Smartynas 	/* Load the floating-point status and control register */
56b5aa3b33Sguenther 	__asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
570503db22Smartynas 
580503db22Smartynas 	return (0);
590503db22Smartynas }
602f2c0062Sguenther DEF_STD(feclearexcept);
610503db22Smartynas 
620503db22Smartynas /*
630503db22Smartynas  * The fegetexceptflag() function stores an implementation-defined
640503db22Smartynas  * representation of the states of the floating-point status flags indicated by
650503db22Smartynas  * the argument excepts in the object pointed to by the argument flagp.
660503db22Smartynas  */
670503db22Smartynas int
fegetexceptflag(fexcept_t * flagp,int excepts)680503db22Smartynas fegetexceptflag(fexcept_t *flagp, int excepts)
690503db22Smartynas {
70d6f349c8Smartynas 	union u u;
710503db22Smartynas 
720503db22Smartynas 	excepts &= FE_ALL_EXCEPT;
730503db22Smartynas 
74d6f349c8Smartynas 	/* Store the current floating-point status and control register */
75b5aa3b33Sguenther 	__asm__ volatile ("mffs %0" : "=f" (u.fpscr));
760503db22Smartynas 
770503db22Smartynas 	/* Store the results in flagp */
78d6f349c8Smartynas 	*flagp = u.bits[1] & excepts;
790503db22Smartynas 
800503db22Smartynas 	return (0);
810503db22Smartynas }
820503db22Smartynas 
830503db22Smartynas /*
840503db22Smartynas  * The feraiseexcept() function raises the supported floating-point exceptions
850503db22Smartynas  * represented by the argument `excepts'.
860503db22Smartynas  */
870503db22Smartynas int
feraiseexcept(int excepts)880503db22Smartynas feraiseexcept(int excepts)
890503db22Smartynas {
900503db22Smartynas 	excepts &= FE_ALL_EXCEPT;
910503db22Smartynas 
920503db22Smartynas 	fesetexceptflag((fexcept_t *)&excepts, excepts);
930503db22Smartynas 
940503db22Smartynas 	return (0);
950503db22Smartynas }
962f2c0062Sguenther DEF_STD(feraiseexcept);
970503db22Smartynas 
980503db22Smartynas /*
990503db22Smartynas  * This function sets the floating-point status flags indicated by the argument
1000503db22Smartynas  * `excepts' to the states stored in the object pointed to by `flagp'. It does
1010503db22Smartynas  * NOT raise any floating-point exceptions, but only sets the state of the flags.
1020503db22Smartynas  */
1030503db22Smartynas int
fesetexceptflag(const fexcept_t * flagp,int excepts)1040503db22Smartynas fesetexceptflag(const fexcept_t *flagp, int excepts)
1050503db22Smartynas {
106d6f349c8Smartynas 	union u u;
1070503db22Smartynas 
1080503db22Smartynas 	excepts &= FE_ALL_EXCEPT;
1090503db22Smartynas 
110d6f349c8Smartynas 	/* Store the current floating-point status and control register */
111b5aa3b33Sguenther 	__asm__ volatile ("mffs %0" : "=f" (u.fpscr));
1120503db22Smartynas 
1130503db22Smartynas 	/* Set the requested status flags */
114d6f349c8Smartynas 	u.bits[1] &= ~excepts;
115d6f349c8Smartynas 	u.bits[1] |= *flagp & excepts;
1160503db22Smartynas 	if (excepts & FE_INVALID) {
1170503db22Smartynas 		if (*flagp & FE_INVALID)
118d6f349c8Smartynas 			u.bits[1] |= _FE_INVALID_SOFT;
1190503db22Smartynas 		else
120d6f349c8Smartynas 			u.bits[1] &= ~_FE_INVALID_ALL;
1210503db22Smartynas 	}
1220503db22Smartynas 
123d6f349c8Smartynas 	/* Load the floating-point status and control register */
124b5aa3b33Sguenther 	__asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
1250503db22Smartynas 
1260503db22Smartynas 	return (0);
1270503db22Smartynas }
1282f2c0062Sguenther DEF_STD(fesetexceptflag);
1290503db22Smartynas 
1300503db22Smartynas /*
1310503db22Smartynas  * The fetestexcept() function determines which of a specified subset of the
1320503db22Smartynas  * floating-point exception flags are currently set. The `excepts' argument
1330503db22Smartynas  * specifies the floating-point status flags to be queried.
1340503db22Smartynas  */
1350503db22Smartynas int
fetestexcept(int excepts)1360503db22Smartynas fetestexcept(int excepts)
1370503db22Smartynas {
138d6f349c8Smartynas 	union u u;
1390503db22Smartynas 
1400503db22Smartynas 	excepts &= FE_ALL_EXCEPT;
1410503db22Smartynas 
142d6f349c8Smartynas 	/* Store the current floating-point status and control register */
143b5aa3b33Sguenther 	__asm__ volatile ("mffs %0" : "=f" (u.fpscr));
1440503db22Smartynas 
145d6f349c8Smartynas 	return (u.bits[1] & excepts);
1460503db22Smartynas }
1472f2c0062Sguenther DEF_STD(fetestexcept);
1480503db22Smartynas 
1490503db22Smartynas /*
1500503db22Smartynas  * The fegetround() function gets the current rounding direction.
1510503db22Smartynas  */
1520503db22Smartynas int
fegetround(void)1530503db22Smartynas fegetround(void)
1540503db22Smartynas {
155d6f349c8Smartynas 	union u u;
1560503db22Smartynas 
157d6f349c8Smartynas 	/* Store the current floating-point status and control register */
158b5aa3b33Sguenther 	__asm__ volatile ("mffs %0" : "=f" (u.fpscr));
1590503db22Smartynas 
160d6f349c8Smartynas 	return (u.bits[1] & _ROUND_MASK);
1610503db22Smartynas }
1622f2c0062Sguenther DEF_STD(fegetround);
1630503db22Smartynas 
1640503db22Smartynas /*
1650503db22Smartynas  * The fesetround() function establishes the rounding direction represented by
1660503db22Smartynas  * its argument `round'. If the argument is not equal to the value of a rounding
1670503db22Smartynas  * direction macro, the rounding direction is not changed.
1680503db22Smartynas  */
1690503db22Smartynas int
fesetround(int round)1700503db22Smartynas fesetround(int round)
1710503db22Smartynas {
172d6f349c8Smartynas 	union u u;
1730503db22Smartynas 
1740503db22Smartynas 	/* Check whether requested rounding direction is supported */
1750503db22Smartynas 	if (round & ~_ROUND_MASK)
1760503db22Smartynas 		return (-1);
1770503db22Smartynas 
178d6f349c8Smartynas 	/* Store the current floating-point status and control register */
179b5aa3b33Sguenther 	__asm__ volatile ("mffs %0" : "=f" (u.fpscr));
1800503db22Smartynas 
181d6f349c8Smartynas 	/* Set the rounding direction */
182d6f349c8Smartynas 	u.bits[1] &= ~_ROUND_MASK;
183d6f349c8Smartynas 	u.bits[1] |= round;
1840503db22Smartynas 
185d6f349c8Smartynas 	/* Load the floating-point status and control register */
186b5aa3b33Sguenther 	__asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
1870503db22Smartynas 
1880503db22Smartynas 	return (0);
1890503db22Smartynas }
1902f2c0062Sguenther DEF_STD(fesetround);
1910503db22Smartynas 
1920503db22Smartynas /*
1930503db22Smartynas  * The fegetenv() function attempts to store the current floating-point
1940503db22Smartynas  * environment in the object pointed to by envp.
1950503db22Smartynas  */
1960503db22Smartynas int
fegetenv(fenv_t * envp)1970503db22Smartynas fegetenv(fenv_t *envp)
1980503db22Smartynas {
199d6f349c8Smartynas 	union u u;
2000503db22Smartynas 
201d6f349c8Smartynas 	/* Store the current floating-point status and control register */
202b5aa3b33Sguenther 	__asm__ volatile ("mffs %0" : "=f" (u.fpscr));
2030503db22Smartynas 
204d6f349c8Smartynas 	*envp = u.bits[1];
2050503db22Smartynas 
2060503db22Smartynas 	return (0);
2070503db22Smartynas }
2082f2c0062Sguenther DEF_STD(fegetenv);
2090503db22Smartynas 
2100503db22Smartynas /*
2110503db22Smartynas  * The feholdexcept() function saves the current floating-point environment
2120503db22Smartynas  * in the object pointed to by envp, clears the floating-point status flags, and
2130503db22Smartynas  * then installs a non-stop (continue on floating-point exceptions) mode, if
2140503db22Smartynas  * available, for all floating-point exceptions.
2150503db22Smartynas  */
2160503db22Smartynas int
feholdexcept(fenv_t * envp)2170503db22Smartynas feholdexcept(fenv_t *envp)
2180503db22Smartynas {
219d6f349c8Smartynas 	union u u;
2200503db22Smartynas 
221d6f349c8Smartynas 	/* Store the current floating-point status and control register */
222b5aa3b33Sguenther 	__asm__ volatile ("mffs %0" : "=f" (u.fpscr));
2230503db22Smartynas 
224d6f349c8Smartynas 	*envp = u.bits[1];
2250503db22Smartynas 
2260503db22Smartynas 	/* Clear exception flags in FPSCR */
227d6f349c8Smartynas 	u.bits[1] &= ~(FE_ALL_EXCEPT | _FE_INVALID_ALL);
2280503db22Smartynas 
2290503db22Smartynas 	/* Mask all exceptions */
230d6f349c8Smartynas 	u.bits[1] &= ~(FE_ALL_EXCEPT >> _MASK_SHIFT);
231b5aa3b33Sguenther 	__asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
2320503db22Smartynas 
2330503db22Smartynas 	return (0);
2340503db22Smartynas }
2352f2c0062Sguenther DEF_STD(feholdexcept);
2360503db22Smartynas 
2370503db22Smartynas /*
2380503db22Smartynas  * The fesetenv() function attempts to establish the floating-point environment
2390503db22Smartynas  * represented by the object pointed to by envp. The argument `envp' points
2400503db22Smartynas  * to an object set by a call to fegetenv() or feholdexcept(), or equal a
2410503db22Smartynas  * floating-point environment macro. The fesetenv() function does not raise
2420503db22Smartynas  * floating-point exceptions, but only installs the state of the floating-point
2430503db22Smartynas  * status flags represented through its argument.
2440503db22Smartynas  */
2450503db22Smartynas int
fesetenv(const fenv_t * envp)2460503db22Smartynas fesetenv(const fenv_t *envp)
2470503db22Smartynas {
248d6f349c8Smartynas 	union u u;
2490503db22Smartynas 
250d6f349c8Smartynas 	u.bits[0] = 0;
251d6f349c8Smartynas 	u.bits[1] = *envp;
2520503db22Smartynas 
253d6f349c8Smartynas 	/* Load the floating-point status and control register */
254b5aa3b33Sguenther 	__asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
2550503db22Smartynas 
2560503db22Smartynas 	return (0);
2570503db22Smartynas }
2582f2c0062Sguenther DEF_STD(fesetenv);
2590503db22Smartynas 
2600503db22Smartynas /*
2610503db22Smartynas  * The feupdateenv() function saves the currently raised floating-point
2620503db22Smartynas  * exceptions in its automatic storage, installs the floating-point environment
2630503db22Smartynas  * represented by the object pointed to by `envp', and then raises the saved
2640503db22Smartynas  * floating-point exceptions. The argument `envp' shall point to an object set
2650503db22Smartynas  * by a call to feholdexcept() or fegetenv(), or equal a floating-point
2660503db22Smartynas  * environment macro.
2670503db22Smartynas  */
2680503db22Smartynas int
feupdateenv(const fenv_t * envp)2690503db22Smartynas feupdateenv(const fenv_t *envp)
2700503db22Smartynas {
271d6f349c8Smartynas 	union u u;
2720503db22Smartynas 
273d6f349c8Smartynas 	/* Store the current floating-point status and control register */
274b5aa3b33Sguenther 	__asm__ volatile ("mffs %0" : "=f" (u.fpscr));
2750503db22Smartynas 
2760503db22Smartynas 	/* Install new floating-point environment */
2770503db22Smartynas 	fesetenv(envp);
2780503db22Smartynas 
2790503db22Smartynas 	/* Raise any previously accumulated exceptions */
280d6f349c8Smartynas 	feraiseexcept(u.bits[1]);
2810503db22Smartynas 
2820503db22Smartynas 	return (0);
2830503db22Smartynas }
2842f2c0062Sguenther DEF_STD(feupdateenv);
2850503db22Smartynas 
2860503db22Smartynas /*
287*2c53affbSjmc  * The following functions are extensions to the standard
2880503db22Smartynas  */
2890503db22Smartynas int
feenableexcept(int mask)2900503db22Smartynas feenableexcept(int mask)
2910503db22Smartynas {
292d6f349c8Smartynas 	union u u;
2930503db22Smartynas 	unsigned int omask;
2940503db22Smartynas 
2950503db22Smartynas 	mask &= FE_ALL_EXCEPT;
2960503db22Smartynas 
297d6f349c8Smartynas 	/* Store the current floating-point status and control register */
298b5aa3b33Sguenther 	__asm__ volatile ("mffs %0" : "=f" (u.fpscr));
2990503db22Smartynas 
300d6f349c8Smartynas 	omask = (u.bits[1] << _MASK_SHIFT) & FE_ALL_EXCEPT;
301d6f349c8Smartynas 	u.bits[1] |= mask >> _MASK_SHIFT;
3020503db22Smartynas 
303d6f349c8Smartynas 	/* Load the floating-point status and control register */
304b5aa3b33Sguenther 	__asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
3050503db22Smartynas 
3060503db22Smartynas 	return (omask);
3070503db22Smartynas 
3080503db22Smartynas }
3090503db22Smartynas 
3100503db22Smartynas int
fedisableexcept(int mask)3110503db22Smartynas fedisableexcept(int mask)
3120503db22Smartynas {
313d6f349c8Smartynas 	union u u;
3140503db22Smartynas 	unsigned int omask;
3150503db22Smartynas 
3160503db22Smartynas 	mask &= FE_ALL_EXCEPT;
3170503db22Smartynas 
318d6f349c8Smartynas 	/* Store the current floating-point status and control register */
319b5aa3b33Sguenther 	__asm__ volatile ("mffs %0" : "=f" (u.fpscr));
3200503db22Smartynas 
321d6f349c8Smartynas 	omask = (u.bits[1] << _MASK_SHIFT) & FE_ALL_EXCEPT;
322d6f349c8Smartynas 	u.bits[1] &= ~(mask >> _MASK_SHIFT);
3230503db22Smartynas 
324d6f349c8Smartynas 	/* Load the floating-point status and control register */
325b5aa3b33Sguenther 	__asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
3260503db22Smartynas 
3270503db22Smartynas 	return (omask);
3280503db22Smartynas }
3290503db22Smartynas 
3300503db22Smartynas int
fegetexcept(void)3310503db22Smartynas fegetexcept(void)
3320503db22Smartynas {
333d6f349c8Smartynas 	union u u;
3340503db22Smartynas 
335d6f349c8Smartynas 	/* Store the current floating-point status and control register */
336b5aa3b33Sguenther 	__asm__ volatile ("mffs %0" : "=f" (u.fpscr));
3370503db22Smartynas 
338d6f349c8Smartynas 	return ((u.bits[1] << _MASK_SHIFT) & FE_ALL_EXCEPT);
3390503db22Smartynas }
340