xref: /minix3/lib/libm/arch/arm/fenv.c (revision 84d9c625bfea59e274550651111ae9edfdc40fbd)
1*84d9c625SLionel Sambuc /*-
2*84d9c625SLionel Sambuc  * Copyright (c) 2013 The NetBSD Foundation, Inc.
3*84d9c625SLionel Sambuc  * All rights reserved.
4*84d9c625SLionel Sambuc  *
5*84d9c625SLionel Sambuc  * This code is derived from software contributed to The NetBSD Foundation
6*84d9c625SLionel Sambuc  * by Matt Thomas of 3am Software Foundry.
7*84d9c625SLionel Sambuc  *
8*84d9c625SLionel Sambuc  * Redistribution and use in source and binary forms, with or without
9*84d9c625SLionel Sambuc  * modification, are permitted provided that the following conditions
10*84d9c625SLionel Sambuc  * are met:
11*84d9c625SLionel Sambuc  * 1. Redistributions of source code must retain the above copyright
12*84d9c625SLionel Sambuc  *    notice, this list of conditions and the following disclaimer.
13*84d9c625SLionel Sambuc  * 2. Redistributions in binary form must reproduce the above copyright
14*84d9c625SLionel Sambuc  *    notice, this list of conditions and the following disclaimer in the
15*84d9c625SLionel Sambuc  *    documentation and/or other materials provided with the distribution.
16*84d9c625SLionel Sambuc  *
17*84d9c625SLionel Sambuc  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18*84d9c625SLionel Sambuc  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19*84d9c625SLionel Sambuc  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20*84d9c625SLionel Sambuc  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21*84d9c625SLionel Sambuc  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22*84d9c625SLionel Sambuc  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23*84d9c625SLionel Sambuc  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24*84d9c625SLionel Sambuc  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25*84d9c625SLionel Sambuc  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26*84d9c625SLionel Sambuc  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27*84d9c625SLionel Sambuc  * POSSIBILITY OF SUCH DAMAGE.
28*84d9c625SLionel Sambuc  */
29*84d9c625SLionel Sambuc 
30*84d9c625SLionel Sambuc #include <sys/cdefs.h>
31*84d9c625SLionel Sambuc __RCSID("$NetBSD: fenv.c,v 1.3 2013/05/01 04:04:54 matt Exp $");
32*84d9c625SLionel Sambuc 
33*84d9c625SLionel Sambuc #include <sys/types.h>
34*84d9c625SLionel Sambuc #include <assert.h>
35*84d9c625SLionel Sambuc #include <fenv.h>
36*84d9c625SLionel Sambuc #include <string.h>
37*84d9c625SLionel Sambuc #include <unistd.h>
38*84d9c625SLionel Sambuc #include <inttypes.h>
39*84d9c625SLionel Sambuc 
40*84d9c625SLionel Sambuc #ifdef __SOFTFP__
41*84d9c625SLionel Sambuc #include <ieeefp.h>
42*84d9c625SLionel Sambuc #include <sys/signal.h>
43*84d9c625SLionel Sambuc #include <sys/siginfo.h>
44*84d9c625SLionel Sambuc #else
45*84d9c625SLionel Sambuc #include <arm/armreg.h>
46*84d9c625SLionel Sambuc #endif
47*84d9c625SLionel Sambuc 
48*84d9c625SLionel Sambuc #include <arm/vfpreg.h>
49*84d9c625SLionel Sambuc 
50*84d9c625SLionel Sambuc const fenv_t __fe_dfl_env = VFP_FPSCR_FZ|VFP_FPSCR_DN|VFP_FPSCR_RN;
51*84d9c625SLionel Sambuc 
52*84d9c625SLionel Sambuc /*
53*84d9c625SLionel Sambuc  * The feclearexcept() function shall attempt to clear the supported
54*84d9c625SLionel Sambuc  * floating-point exceptions represented by excepts.
55*84d9c625SLionel Sambuc  */
56*84d9c625SLionel Sambuc int
57*84d9c625SLionel Sambuc feclearexcept(int excepts)
58*84d9c625SLionel Sambuc {
59*84d9c625SLionel Sambuc #ifndef lint
60*84d9c625SLionel Sambuc 	_DIAGASSERT((except & ~FE_EXCEPT_ALL) == 0);
61*84d9c625SLionel Sambuc #endif
62*84d9c625SLionel Sambuc #ifdef __SOFTFP__
63*84d9c625SLionel Sambuc 	fpsetsticky(fpgetsticky() & ~excepts);
64*84d9c625SLionel Sambuc 	return 0;
65*84d9c625SLionel Sambuc #else
66*84d9c625SLionel Sambuc 	int tmp = armreg_fpscr_read() & ~__SHIFTIN(excepts, VFP_FPSCR_CSUM);
67*84d9c625SLionel Sambuc 	armreg_fpscr_write(tmp);
68*84d9c625SLionel Sambuc 	return 0;
69*84d9c625SLionel Sambuc #endif
70*84d9c625SLionel Sambuc }
71*84d9c625SLionel Sambuc 
72*84d9c625SLionel Sambuc /*
73*84d9c625SLionel Sambuc  * The fegetexceptflag() function shall attempt to store an
74*84d9c625SLionel Sambuc  * implementation-defined representation of the states of the floating-point
75*84d9c625SLionel Sambuc  * status flags indicated by the argument excepts in the object pointed to by
76*84d9c625SLionel Sambuc  * the argument flagp.
77*84d9c625SLionel Sambuc  */
78*84d9c625SLionel Sambuc int
79*84d9c625SLionel Sambuc fegetexceptflag(fexcept_t *flagp, int excepts)
80*84d9c625SLionel Sambuc {
81*84d9c625SLionel Sambuc 	_DIAGASSERT((except & ~FE_EXCEPT_ALL) == 0);
82*84d9c625SLionel Sambuc #ifdef __SOFTFP__
83*84d9c625SLionel Sambuc 	*flagp = fpgetsticky() & excepts;
84*84d9c625SLionel Sambuc #else
85*84d9c625SLionel Sambuc 	*flagp = __SHIFTOUT(armreg_fpscr_read(), VFP_FPSCR_CSUM) & excepts;
86*84d9c625SLionel Sambuc #endif
87*84d9c625SLionel Sambuc 	return 0;
88*84d9c625SLionel Sambuc }
89*84d9c625SLionel Sambuc 
90*84d9c625SLionel Sambuc /*
91*84d9c625SLionel Sambuc  * The feraiseexcept() function shall attempt to raise the supported
92*84d9c625SLionel Sambuc  * floating-point exceptions represented by the argument excepts. The order
93*84d9c625SLionel Sambuc  * in which these floating-point exceptions are raised is unspecified.
94*84d9c625SLionel Sambuc  */
95*84d9c625SLionel Sambuc int
96*84d9c625SLionel Sambuc feraiseexcept(int excepts)
97*84d9c625SLionel Sambuc {
98*84d9c625SLionel Sambuc #ifndef lint
99*84d9c625SLionel Sambuc 	_DIAGASSERT((except & ~FE_EXCEPT_ALL) == 0);
100*84d9c625SLionel Sambuc #endif
101*84d9c625SLionel Sambuc #if !defined(__minix) /* LSC: No sigqueueinfo on Minix. */
102*84d9c625SLionel Sambuc #ifdef __SOFTFP__
103*84d9c625SLionel Sambuc 	excepts &= fpgetsticky();
104*84d9c625SLionel Sambuc 
105*84d9c625SLionel Sambuc 	if (excepts) {
106*84d9c625SLionel Sambuc 		siginfo_t info;
107*84d9c625SLionel Sambuc 		memset(&info, 0, sizeof info);
108*84d9c625SLionel Sambuc 		info.si_signo = SIGFPE;
109*84d9c625SLionel Sambuc 		info.si_pid = getpid();
110*84d9c625SLionel Sambuc 		info.si_uid = geteuid();
111*84d9c625SLionel Sambuc 		if (excepts & FE_UNDERFLOW)
112*84d9c625SLionel Sambuc 			info.si_code = FPE_FLTUND;
113*84d9c625SLionel Sambuc 		else if (excepts & FE_OVERFLOW)
114*84d9c625SLionel Sambuc 			info.si_code = FPE_FLTOVF;
115*84d9c625SLionel Sambuc 		else if (excepts & FE_DIVBYZERO)
116*84d9c625SLionel Sambuc 			info.si_code = FPE_FLTDIV;
117*84d9c625SLionel Sambuc 		else if (excepts & FE_INVALID)
118*84d9c625SLionel Sambuc 			info.si_code = FPE_FLTINV;
119*84d9c625SLionel Sambuc 		else if (excepts & FE_INEXACT)
120*84d9c625SLionel Sambuc 			info.si_code = FPE_FLTRES;
121*84d9c625SLionel Sambuc 		sigqueueinfo(getpid(), &info);
122*84d9c625SLionel Sambuc 	}
123*84d9c625SLionel Sambuc #else
124*84d9c625SLionel Sambuc 	int fpscr = armreg_fpscr_read();
125*84d9c625SLionel Sambuc 	fpscr = (fpscr & ~VFP_FPSCR_ESUM) | __SHIFTIN(excepts, VFP_FPSCR_ESUM);
126*84d9c625SLionel Sambuc 	armreg_fpscr_write(fpscr);
127*84d9c625SLionel Sambuc #endif
128*84d9c625SLionel Sambuc #endif /* !defined(__minix) */
129*84d9c625SLionel Sambuc 	return 0;
130*84d9c625SLionel Sambuc }
131*84d9c625SLionel Sambuc 
132*84d9c625SLionel Sambuc /*
133*84d9c625SLionel Sambuc  * The fesetexceptflag() function shall attempt to set the floating-point
134*84d9c625SLionel Sambuc  * status flags indicated by the argument excepts to the states stored in the
135*84d9c625SLionel Sambuc  * object pointed to by flagp. The value pointed to by flagp shall have been
136*84d9c625SLionel Sambuc  * set by a previous call to fegetexceptflag() whose second argument
137*84d9c625SLionel Sambuc  * represented at least those floating-point exceptions represented by the
138*84d9c625SLionel Sambuc  * argument excepts. This function does not raise floating-point exceptions,
139*84d9c625SLionel Sambuc  * but only sets the state of the flags.
140*84d9c625SLionel Sambuc  */
141*84d9c625SLionel Sambuc int
142*84d9c625SLionel Sambuc fesetexceptflag(const fexcept_t *flagp, int excepts)
143*84d9c625SLionel Sambuc {
144*84d9c625SLionel Sambuc #ifndef lint
145*84d9c625SLionel Sambuc 	_DIAGASSERT((except & ~FE_EXCEPT_ALL) == 0);
146*84d9c625SLionel Sambuc #endif
147*84d9c625SLionel Sambuc #ifdef __SOFTFP__
148*84d9c625SLionel Sambuc 	fpsetsticky((fpgetsticky() & ~excepts) | (excepts & *flagp));
149*84d9c625SLionel Sambuc #else
150*84d9c625SLionel Sambuc 	int fpscr = armreg_fpscr_read();
151*84d9c625SLionel Sambuc 	fpscr &= ~__SHIFTIN(excepts, VFP_FPSCR_CSUM);
152*84d9c625SLionel Sambuc 	fpscr |= __SHIFTIN((*flagp & excepts), VFP_FPSCR_CSUM);
153*84d9c625SLionel Sambuc 	armreg_fpscr_write(fpscr);
154*84d9c625SLionel Sambuc #endif
155*84d9c625SLionel Sambuc 	return 0;
156*84d9c625SLionel Sambuc }
157*84d9c625SLionel Sambuc 
158*84d9c625SLionel Sambuc /*
159*84d9c625SLionel Sambuc  * The fetestexcept() function shall determine which of a specified subset of
160*84d9c625SLionel Sambuc  * the floating-point exception flags are currently set. The excepts argument
161*84d9c625SLionel Sambuc  * specifies the floating-point status flags to be queried.
162*84d9c625SLionel Sambuc  */
163*84d9c625SLionel Sambuc int
164*84d9c625SLionel Sambuc fetestexcept(int excepts)
165*84d9c625SLionel Sambuc {
166*84d9c625SLionel Sambuc 	_DIAGASSERT((except & ~FE_EXCEPT_ALL) == 0);
167*84d9c625SLionel Sambuc #ifdef __SOFTFP__
168*84d9c625SLionel Sambuc 	return fpgetsticky() & excepts;
169*84d9c625SLionel Sambuc #else
170*84d9c625SLionel Sambuc 	return __SHIFTOUT(armreg_fpscr_read(), VFP_FPSCR_CSUM) & excepts;
171*84d9c625SLionel Sambuc #endif
172*84d9c625SLionel Sambuc }
173*84d9c625SLionel Sambuc 
174*84d9c625SLionel Sambuc /*
175*84d9c625SLionel Sambuc  * The fegetround() function shall get the current rounding direction.
176*84d9c625SLionel Sambuc  */
177*84d9c625SLionel Sambuc int
178*84d9c625SLionel Sambuc fegetround(void)
179*84d9c625SLionel Sambuc {
180*84d9c625SLionel Sambuc #ifdef __SOFTFP__
181*84d9c625SLionel Sambuc 	return fpgetround();
182*84d9c625SLionel Sambuc #else
183*84d9c625SLionel Sambuc 	return __SHIFTOUT(armreg_fpscr_read(), VFP_FPSCR_RMODE);
184*84d9c625SLionel Sambuc #endif
185*84d9c625SLionel Sambuc }
186*84d9c625SLionel Sambuc 
187*84d9c625SLionel Sambuc /*
188*84d9c625SLionel Sambuc  * The fesetround() function shall establish the rounding direction represented
189*84d9c625SLionel Sambuc  * by its argument round. If the argument is not equal to the value of a
190*84d9c625SLionel Sambuc  * rounding direction macro, the rounding direction is not changed.
191*84d9c625SLionel Sambuc  */
192*84d9c625SLionel Sambuc int
193*84d9c625SLionel Sambuc fesetround(int round)
194*84d9c625SLionel Sambuc {
195*84d9c625SLionel Sambuc #ifndef lint
196*84d9c625SLionel Sambuc 	_DIAGASSERT(!(round & ~__SHIFTOUT(VFP_FPSCR_RMODE, VFP_FPSCR_RMODE)));
197*84d9c625SLionel Sambuc #endif
198*84d9c625SLionel Sambuc #ifdef __SOFTFP__
199*84d9c625SLionel Sambuc 	(void)fpsetround(round);
200*84d9c625SLionel Sambuc #else
201*84d9c625SLionel Sambuc 	int fpscr = armreg_fpscr_read() & ~VFP_FPSCR_RMODE;
202*84d9c625SLionel Sambuc 	fpscr |= __SHIFTIN(round, VFP_FPSCR_RMODE);
203*84d9c625SLionel Sambuc 	armreg_fpscr_write(fpscr);
204*84d9c625SLionel Sambuc #endif
205*84d9c625SLionel Sambuc 	return 0;
206*84d9c625SLionel Sambuc }
207*84d9c625SLionel Sambuc 
208*84d9c625SLionel Sambuc /*
209*84d9c625SLionel Sambuc  * The fegetenv() function shall attempt to store the current floating-point
210*84d9c625SLionel Sambuc  * environment in the object pointed to by envp.
211*84d9c625SLionel Sambuc  */
212*84d9c625SLionel Sambuc int
213*84d9c625SLionel Sambuc fegetenv(fenv_t *envp)
214*84d9c625SLionel Sambuc {
215*84d9c625SLionel Sambuc #ifdef __SOFTFP__
216*84d9c625SLionel Sambuc 	*envp = __SHIFTIN(fpgetround(), VFP_FPSCR_RMODE)
217*84d9c625SLionel Sambuc 	    | __SHIFTIN(fpgetmask(), VFP_FPSCR_ESUM)
218*84d9c625SLionel Sambuc 	    | __SHIFTIN(fpgetsticky(), VFP_FPSCR_CSUM);
219*84d9c625SLionel Sambuc #else
220*84d9c625SLionel Sambuc 	*envp = armreg_fpscr_read();
221*84d9c625SLionel Sambuc #endif
222*84d9c625SLionel Sambuc 	return 0;
223*84d9c625SLionel Sambuc }
224*84d9c625SLionel Sambuc 
225*84d9c625SLionel Sambuc /*
226*84d9c625SLionel Sambuc  * The feholdexcept() function shall save the current floating-point
227*84d9c625SLionel Sambuc  * environment in the object pointed to by envp, clear the floating-point
228*84d9c625SLionel Sambuc  * status flags, and then install a non-stop (continue on floating-point
229*84d9c625SLionel Sambuc  * exceptions) mode, if available, for all floating-point exceptions.
230*84d9c625SLionel Sambuc  */
231*84d9c625SLionel Sambuc int
232*84d9c625SLionel Sambuc feholdexcept(fenv_t *envp)
233*84d9c625SLionel Sambuc {
234*84d9c625SLionel Sambuc #ifdef __SOFTFP__
235*84d9c625SLionel Sambuc 	*envp = __SHIFTIN(fpgetround(), VFP_FPSCR_RMODE)
236*84d9c625SLionel Sambuc 	    | __SHIFTIN(fpgetmask(), VFP_FPSCR_ESUM)
237*84d9c625SLionel Sambuc 	    | __SHIFTIN(fpgetsticky(), VFP_FPSCR_CSUM);
238*84d9c625SLionel Sambuc 	fpsetmask(0);
239*84d9c625SLionel Sambuc 	fpsetsticky(0);
240*84d9c625SLionel Sambuc #else
241*84d9c625SLionel Sambuc 	*envp = armreg_fpscr_read();
242*84d9c625SLionel Sambuc 	armreg_fpscr_write((*envp) & ~(VFP_FPSCR_ESUM|VFP_FPSCR_CSUM));
243*84d9c625SLionel Sambuc #endif
244*84d9c625SLionel Sambuc 	return 0;
245*84d9c625SLionel Sambuc }
246*84d9c625SLionel Sambuc 
247*84d9c625SLionel Sambuc /*
248*84d9c625SLionel Sambuc  * The fesetenv() function shall attempt to establish the floating-point
249*84d9c625SLionel Sambuc  * environment represented by the object pointed to by envp. The fesetenv()
250*84d9c625SLionel Sambuc  * function does not raise floating-point exceptions, but only installs the
251*84d9c625SLionel Sambuc  * state of the floating-point status flags represented through its argument.
252*84d9c625SLionel Sambuc  */
253*84d9c625SLionel Sambuc int
254*84d9c625SLionel Sambuc fesetenv(const fenv_t *envp)
255*84d9c625SLionel Sambuc {
256*84d9c625SLionel Sambuc #ifdef __SOFTFP__
257*84d9c625SLionel Sambuc 	(void)fpsetround(__SHIFTIN(*envp, VFP_FPSCR_RMODE));
258*84d9c625SLionel Sambuc 	(void)fpsetmask(__SHIFTOUT(*envp, VFP_FPSCR_ESUM));
259*84d9c625SLionel Sambuc 	(void)fpsetsticky(__SHIFTOUT(*envp, VFP_FPSCR_CSUM));
260*84d9c625SLionel Sambuc #else
261*84d9c625SLionel Sambuc 	armreg_fpscr_write(*envp);
262*84d9c625SLionel Sambuc #endif
263*84d9c625SLionel Sambuc 	return 0;
264*84d9c625SLionel Sambuc }
265*84d9c625SLionel Sambuc 
266*84d9c625SLionel Sambuc /*
267*84d9c625SLionel Sambuc  * The feupdateenv() function shall attempt to save the currently raised
268*84d9c625SLionel Sambuc  * floating-point exceptions in its automatic storage, attempt to install the
269*84d9c625SLionel Sambuc  * floating-point environment represented by the object pointed to by envp,
270*84d9c625SLionel Sambuc  * and then attempt to raise the saved floating-point exceptions.
271*84d9c625SLionel Sambuc  */
272*84d9c625SLionel Sambuc int
273*84d9c625SLionel Sambuc feupdateenv(const fenv_t *envp)
274*84d9c625SLionel Sambuc {
275*84d9c625SLionel Sambuc #ifndef lint
276*84d9c625SLionel Sambuc 	_DIAGASSERT(envp != NULL);
277*84d9c625SLionel Sambuc #endif
278*84d9c625SLionel Sambuc #ifdef __SOFTFP__
279*84d9c625SLionel Sambuc 	(void)fpsetround(__SHIFTIN(*envp, VFP_FPSCR_RMODE));
280*84d9c625SLionel Sambuc 	(void)fpsetmask(fpgetmask() | __SHIFTOUT(*envp, VFP_FPSCR_ESUM));
281*84d9c625SLionel Sambuc 	(void)fpsetsticky(fpgetsticky() | __SHIFTOUT(*envp, VFP_FPSCR_CSUM));
282*84d9c625SLionel Sambuc #else
283*84d9c625SLionel Sambuc 	int fpscr = armreg_fpscr_read() & ~(VFP_FPSCR_ESUM|VFP_FPSCR_CSUM);
284*84d9c625SLionel Sambuc 	fpscr |= *envp;
285*84d9c625SLionel Sambuc 	armreg_fpscr_write(fpscr);
286*84d9c625SLionel Sambuc #endif
287*84d9c625SLionel Sambuc 
288*84d9c625SLionel Sambuc 	/* Success */
289*84d9c625SLionel Sambuc 	return 0;
290*84d9c625SLionel Sambuc }
291