xref: /netbsd-src/lib/libm/arch/sparc64/fenv.c (revision f9faf20aeffd68c90a9d1a577a17402726208619)
1*f9faf20aSandvar /*	$NetBSD: fenv.c,v 1.4 2021/09/03 21:54:59 andvar Exp $	*/
217b8402aSchristos 
317b8402aSchristos /*-
417b8402aSchristos  * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG>
517b8402aSchristos  * All rights reserved.
617b8402aSchristos  *
717b8402aSchristos  * Redistribution and use in source and binary forms, with or without
817b8402aSchristos  * modification, are permitted provided that the following conditions
917b8402aSchristos  * are met:
1017b8402aSchristos  * 1. Redistributions of source code must retain the above copyright
1117b8402aSchristos  *    notice, this list of conditions and the following disclaimer.
1217b8402aSchristos  * 2. Redistributions in binary form must reproduce the above copyright
1317b8402aSchristos  *    notice, this list of conditions and the following disclaimer in the
1417b8402aSchristos  *    documentation and/or other materials provided with the distribution.
1517b8402aSchristos  *
1617b8402aSchristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1717b8402aSchristos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1817b8402aSchristos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1917b8402aSchristos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2017b8402aSchristos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2117b8402aSchristos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2217b8402aSchristos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2317b8402aSchristos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2417b8402aSchristos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2517b8402aSchristos  */
2617b8402aSchristos #include <sys/cdefs.h>
27*f9faf20aSandvar __RCSID("$NetBSD: fenv.c,v 1.4 2021/09/03 21:54:59 andvar Exp $");
287e30e943Schs 
297e30e943Schs #include "namespace.h"
3017b8402aSchristos 
3117b8402aSchristos #include <assert.h>
3217b8402aSchristos #include <fenv.h>
3317b8402aSchristos 
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 #ifdef __arch64__
5288e42b60Snakayama 
5317b8402aSchristos /* Load floating-point state register (all 64bits) */
5417b8402aSchristos #define	__ldxfsr(__r)	__asm__	__volatile__		\
5517b8402aSchristos 	("ldx %0, %%fsr" : : "m" (__r))
5617b8402aSchristos 
5717b8402aSchristos /* Save floating-point state register (all 64bits) */
5817b8402aSchristos #define	__stxfsr(__r)	__asm__	__volatile__		\
5917b8402aSchristos 	("stx %%fsr, %0" : "=m" (*(__r)))
6017b8402aSchristos 
6188e42b60Snakayama #else /* !__arch64__ */
6288e42b60Snakayama 
6388e42b60Snakayama /* Load floating-point state register (32bits) */
6488e42b60Snakayama #define	__ldxfsr(__r)	__asm__	__volatile__		\
6588e42b60Snakayama 	("ld %0, %%fsr" : : "m" (__r))
6688e42b60Snakayama 
6788e42b60Snakayama /* Save floating-point state register (32bits) */
6888e42b60Snakayama #define	__stxfsr(__r)	__asm__	__volatile__		\
6988e42b60Snakayama 	("st %%fsr, %0" : "=m" (*(__r)))
7088e42b60Snakayama 
7188e42b60Snakayama #endif /* __arch64__ */
7288e42b60Snakayama 
7317b8402aSchristos /*
7417b8402aSchristos  * The feclearexcept() function clears the supported floating-point exceptions
7517b8402aSchristos  * represented by `excepts'.
7617b8402aSchristos  */
7717b8402aSchristos int
7817b8402aSchristos feclearexcept(int excepts)
7917b8402aSchristos {
8017b8402aSchristos 	fexcept_t r;
8117b8402aSchristos 	int ex;
8217b8402aSchristos 
8317b8402aSchristos 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
8417b8402aSchristos 
8517b8402aSchristos 	ex = excepts & FE_ALL_EXCEPT;
8617b8402aSchristos 
8717b8402aSchristos 	__stxfsr(&r);
8817b8402aSchristos 	r &= ~ex;
8917b8402aSchristos 	__ldxfsr(r);
9017b8402aSchristos 
9117b8402aSchristos 	/* Success */
9217b8402aSchristos 	return 0;
9317b8402aSchristos }
9417b8402aSchristos 
9517b8402aSchristos /*
9617b8402aSchristos  * The fegetexceptflag() function stores an implementation-defined
9717b8402aSchristos  * representation of the states of the floating-point status flags indicated
9817b8402aSchristos  * by the argument excepts in the object pointed to by the argument flagp.
9917b8402aSchristos  */
10017b8402aSchristos int
fegetexceptflag(fexcept_t * flagp,int excepts)10117b8402aSchristos fegetexceptflag(fexcept_t *flagp, int excepts)
10217b8402aSchristos {
10317b8402aSchristos 	fexcept_t r;
10417b8402aSchristos 	int ex;
10517b8402aSchristos 
10617b8402aSchristos 	_DIAGASSERT(flagp != NULL);
10717b8402aSchristos 	_DIAGASSERT((excepts & ~_FE_ALL_EXCEPT) == 0);
10817b8402aSchristos 
10917b8402aSchristos 	ex = excepts & FE_ALL_EXCEPT;
11017b8402aSchristos 
11117b8402aSchristos 	__stxfsr(&r);
11217b8402aSchristos 	*flagp = r & ex;
11317b8402aSchristos 
11417b8402aSchristos 	/* Success */
11517b8402aSchristos 	return 0;
11617b8402aSchristos }
11717b8402aSchristos 
11817b8402aSchristos 
11917b8402aSchristos /*
12017b8402aSchristos  * This function sets the floating-point status flags indicated by the argument
12117b8402aSchristos  * `excepts' to the states stored in the object pointed to by `flagp'. It does
12217b8402aSchristos  * NOT raise any floating-point exceptions, but only sets the state of the flags.
12317b8402aSchristos  */
12417b8402aSchristos int
fesetexceptflag(const fexcept_t * flagp,int excepts)12517b8402aSchristos fesetexceptflag(const fexcept_t *flagp, int excepts)
12617b8402aSchristos {
12717b8402aSchristos 	fexcept_t r;
12817b8402aSchristos 	int ex;
12917b8402aSchristos 
13017b8402aSchristos 	_DIAGASSERT(flagp != NULL);
13117b8402aSchristos 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
13217b8402aSchristos 
13317b8402aSchristos 	ex = excepts & FE_ALL_EXCEPT;
13417b8402aSchristos 
13517b8402aSchristos 	__stxfsr(&r);
13617b8402aSchristos 	r &= ~ex;
13717b8402aSchristos 	r |= *flagp & ex;
13817b8402aSchristos 	__ldxfsr(r);
13917b8402aSchristos 
14017b8402aSchristos 	/* Success */
14117b8402aSchristos 	return 0;
14217b8402aSchristos }
14317b8402aSchristos 
14417b8402aSchristos /*
14517b8402aSchristos  * The feraiseexcept() function raises the supported floating-point exceptions
14617b8402aSchristos  * represented by the argument `excepts'.
14717b8402aSchristos  *
14817b8402aSchristos  * The order in which these floating-point exceptions are raised is unspecified
14917b8402aSchristos  * (by the standard).
15017b8402aSchristos  */
15117b8402aSchristos int
feraiseexcept(int excepts)15217b8402aSchristos feraiseexcept(int excepts)
15317b8402aSchristos {
15417b8402aSchristos 	volatile double d;
15517b8402aSchristos 	int ex;
15617b8402aSchristos 
15717b8402aSchristos 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
15817b8402aSchristos 
15917b8402aSchristos 	ex = excepts & FE_ALL_EXCEPT;
16017b8402aSchristos 
16117b8402aSchristos 	/*
16217b8402aSchristos 	 * With a compiler that supports the FENV_ACCESS pragma properly, simple
16317b8402aSchristos 	 * expressions like '0.0 / 0.0' should be sufficient to generate traps.
16417b8402aSchristos 	 * Unfortunately, we need to bring a volatile variable into the equation
16517b8402aSchristos 	 * to prevent incorrect optimizations.
16617b8402aSchristos 	 */
16717b8402aSchristos 	if (ex & FE_INVALID) {
16817b8402aSchristos 		d = 0.0;
16917b8402aSchristos 		d = 0.0 / d;
17017b8402aSchristos 	}
17117b8402aSchristos 	if (ex & FE_DIVBYZERO) {
17217b8402aSchristos 		d = 0.0;
17317b8402aSchristos 		d = 1.0 / d;
17417b8402aSchristos 	}
17517b8402aSchristos 	if (ex & FE_OVERFLOW) {
17617b8402aSchristos 		d = 0x1.ffp1023;
17717b8402aSchristos 		d *= 2.0;
17817b8402aSchristos 	}
17917b8402aSchristos 	if (ex & FE_UNDERFLOW) {
18017b8402aSchristos 		d = 0x1p-1022;
18117b8402aSchristos 		d /= 0x1p1023;
18217b8402aSchristos 	}
18317b8402aSchristos 	if (ex & FE_INEXACT) {
18417b8402aSchristos 		d = 0x1p-1022;
18517b8402aSchristos 		d += 1.0;
18617b8402aSchristos 	}
18717b8402aSchristos 
18817b8402aSchristos 	/* Success */
18917b8402aSchristos 	return 0;
19017b8402aSchristos }
19117b8402aSchristos 
19217b8402aSchristos /*
19317b8402aSchristos  * The fetestexcept() function determines which of a specified subset of the
19417b8402aSchristos  * floating-point exception flags are currently set. The `excepts' argument
19517b8402aSchristos  * specifies the floating-point status flags to be queried.
19617b8402aSchristos  */
19717b8402aSchristos int
fetestexcept(int excepts)19817b8402aSchristos fetestexcept(int excepts)
19917b8402aSchristos {
20017b8402aSchristos 	fexcept_t r;
20117b8402aSchristos 
20217b8402aSchristos 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
20317b8402aSchristos 
20417b8402aSchristos 	__stxfsr(&r);
20517b8402aSchristos 
20617b8402aSchristos 	return r & (excepts & FE_ALL_EXCEPT);
20717b8402aSchristos }
20817b8402aSchristos 
20917b8402aSchristos /*
21017b8402aSchristos  * The fegetround() function gets the current rounding direction.
21117b8402aSchristos  */
21217b8402aSchristos int
fegetround(void)21317b8402aSchristos fegetround(void)
21417b8402aSchristos {
21517b8402aSchristos 	fenv_t r;
21617b8402aSchristos 
21717b8402aSchristos 	__stxfsr(&r);
21817b8402aSchristos 
21917b8402aSchristos 	return (r >> _ROUND_SHIFT) & _ROUND_MASK;
22017b8402aSchristos }
22117b8402aSchristos 
22217b8402aSchristos /*
22317b8402aSchristos  * The fesetround() function establishes the rounding direction represented by
22417b8402aSchristos  * its argument `round'. If the argument is not equal to the value of a rounding
22517b8402aSchristos  * direction macro, the rounding direction is not changed.
22617b8402aSchristos  */
22717b8402aSchristos int
fesetround(int round)22817b8402aSchristos fesetround(int round)
22917b8402aSchristos {
23017b8402aSchristos 	fenv_t r;
23117b8402aSchristos 
23217b8402aSchristos 	_DIAGASSERT((round & ~_ROUND_MASK) == 0);
23317b8402aSchristos 	if (round & ~_ROUND_MASK)
23417b8402aSchristos 		return -1;
23517b8402aSchristos 
23617b8402aSchristos 	__stxfsr(&r);
23717b8402aSchristos 	r &= ~(_ROUND_MASK << _ROUND_SHIFT);
23817b8402aSchristos 	r |= round << _ROUND_SHIFT;
23917b8402aSchristos 	__ldxfsr(r);
24017b8402aSchristos 
24117b8402aSchristos 	/* Success */
24217b8402aSchristos 	return 0;
24317b8402aSchristos }
24417b8402aSchristos 
24517b8402aSchristos /*
24617b8402aSchristos  * The fegetenv() function attempts to store the current floating-point
24717b8402aSchristos  * environment in the object pointed to by envp.
24817b8402aSchristos  */
24917b8402aSchristos int
fegetenv(fenv_t * envp)25017b8402aSchristos fegetenv(fenv_t *envp)
25117b8402aSchristos {
25217b8402aSchristos 	_DIAGASSERT(envp != NULL);
25317b8402aSchristos 
25417b8402aSchristos 	__stxfsr(envp);
25517b8402aSchristos 
25617b8402aSchristos 	/* Success */
25717b8402aSchristos 	return 0;
25817b8402aSchristos }
25917b8402aSchristos 
26017b8402aSchristos 
26117b8402aSchristos /*
26217b8402aSchristos  * The feholdexcept() function saves the current floating-point environment
26317b8402aSchristos  * in the object pointed to by envp, clears the floating-point status flags, and
26417b8402aSchristos  * then installs a non-stop (continue on floating-point exceptions) mode, if
26517b8402aSchristos  * available, for all floating-point exceptions.
26617b8402aSchristos  */
26717b8402aSchristos int
feholdexcept(fenv_t * envp)26817b8402aSchristos feholdexcept(fenv_t *envp)
26917b8402aSchristos {
27017b8402aSchristos 	fenv_t r;
27117b8402aSchristos 
27217b8402aSchristos 	_DIAGASSERT(envp != NULL);
27317b8402aSchristos 
27417b8402aSchristos 	__stxfsr(&r);
27517b8402aSchristos 	*envp = r;
27617b8402aSchristos 	r &= ~(FE_ALL_EXCEPT | _ENABLE_MASK);
27717b8402aSchristos 	__ldxfsr(r);
27817b8402aSchristos 
27917b8402aSchristos 	/* Success */
28017b8402aSchristos 	return 0;
28117b8402aSchristos }
28217b8402aSchristos 
28317b8402aSchristos /*
28417b8402aSchristos  * The fesetenv() function attempts to establish the floating-point environment
28517b8402aSchristos  * represented by the object pointed to by envp. The argument `envp' points
28617b8402aSchristos  * to an object set by a call to fegetenv() or feholdexcept(), or equal a
28717b8402aSchristos  * floating-point environment macro. The fesetenv() function does not raise
28817b8402aSchristos  * floating-point exceptions, but only installs the state of the floating-point
28917b8402aSchristos  * status flags represented through its argument.
29017b8402aSchristos  */
29117b8402aSchristos int
fesetenv(const fenv_t * envp)29217b8402aSchristos fesetenv(const fenv_t *envp)
29317b8402aSchristos {
29417b8402aSchristos 	_DIAGASSERT(envp != NULL);
29517b8402aSchristos 
29617b8402aSchristos 	__ldxfsr(*envp);
29717b8402aSchristos 
29817b8402aSchristos 	/* Success */
29917b8402aSchristos 	return 0;
30017b8402aSchristos }
30117b8402aSchristos 
30217b8402aSchristos 
30317b8402aSchristos /*
30417b8402aSchristos  * The feupdateenv() function saves the currently raised floating-point
30517b8402aSchristos  * exceptions in its automatic storage, installs the floating-point environment
30617b8402aSchristos  * represented by the object pointed to by `envp', and then raises the saved
30717b8402aSchristos  * floating-point exceptions. The argument `envp' shall point to an object set
30817b8402aSchristos  * by a call to feholdexcept() or fegetenv(), or equal a floating-point
30917b8402aSchristos  * environment macro.
31017b8402aSchristos  */
31117b8402aSchristos int
feupdateenv(const fenv_t * envp)31217b8402aSchristos feupdateenv(const fenv_t *envp)
31317b8402aSchristos {
31417b8402aSchristos 	fexcept_t r;
31517b8402aSchristos 
31617b8402aSchristos 	_DIAGASSERT(envp != NULL);
31717b8402aSchristos 
31817b8402aSchristos 	__stxfsr(&r);
31917b8402aSchristos 	__ldxfsr(*envp);
32017b8402aSchristos 
32117b8402aSchristos 	_DIAGASSERT((r & ~FE_ALL_EXCEPT) == 0);
32217b8402aSchristos 	feraiseexcept(r & FE_ALL_EXCEPT);
32317b8402aSchristos 
32417b8402aSchristos 	/* Success */
32517b8402aSchristos 	return 0;
32617b8402aSchristos }
32717b8402aSchristos 
32817b8402aSchristos /*
329*f9faf20aSandvar  * The following functions are extensions to the standard
33017b8402aSchristos  */
33117b8402aSchristos int
feenableexcept(int mask)33217b8402aSchristos feenableexcept(int mask)
33317b8402aSchristos {
33417b8402aSchristos 	fenv_t old_r, new_r;
33517b8402aSchristos 
33617b8402aSchristos 	__stxfsr(&old_r);
33717b8402aSchristos 	new_r = old_r | ((mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT);
33817b8402aSchristos 	__ldxfsr(new_r);
33917b8402aSchristos 
34017b8402aSchristos 	return (old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT;
34117b8402aSchristos }
34217b8402aSchristos 
34317b8402aSchristos int
fedisableexcept(int mask)34417b8402aSchristos fedisableexcept(int mask)
34517b8402aSchristos {
34617b8402aSchristos 	fenv_t old_r, new_r;
34717b8402aSchristos 
34817b8402aSchristos 	__stxfsr(&old_r);
34917b8402aSchristos 	new_r = old_r & ~((mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT);
35017b8402aSchristos 	__ldxfsr(new_r);
35117b8402aSchristos 
35217b8402aSchristos 	return (old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT;
35317b8402aSchristos }
35417b8402aSchristos 
35517b8402aSchristos int
fegetexcept(void)35617b8402aSchristos fegetexcept(void)
35717b8402aSchristos {
35817b8402aSchristos 	fenv_t r;
35917b8402aSchristos 
36017b8402aSchristos 	__stxfsr(&r);
36117b8402aSchristos 	return (r & _ENABLE_MASK) >> _FPUSW_SHIFT;
36217b8402aSchristos }
363