xref: /minix3/lib/libm/arch/sparc/fenv.c (revision 84d9c625bfea59e274550651111ae9edfdc40fbd)
1*84d9c625SLionel Sambuc /*	$NetBSD: fenv.c,v 1.1 2011/05/20 21:42:49 nakayama Exp $	*/
2*84d9c625SLionel Sambuc 
3*84d9c625SLionel Sambuc /*-
4*84d9c625SLionel Sambuc  * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG>
5*84d9c625SLionel Sambuc  * All rights reserved.
6*84d9c625SLionel Sambuc  *
7*84d9c625SLionel Sambuc  * Redistribution and use in source and binary forms, with or without
8*84d9c625SLionel Sambuc  * modification, are permitted provided that the following conditions
9*84d9c625SLionel Sambuc  * are met:
10*84d9c625SLionel Sambuc  * 1. Redistributions of source code must retain the above copyright
11*84d9c625SLionel Sambuc  *    notice, this list of conditions and the following disclaimer.
12*84d9c625SLionel Sambuc  * 2. Redistributions in binary form must reproduce the above copyright
13*84d9c625SLionel Sambuc  *    notice, this list of conditions and the following disclaimer in the
14*84d9c625SLionel Sambuc  *    documentation and/or other materials provided with the distribution.
15*84d9c625SLionel Sambuc  *
16*84d9c625SLionel Sambuc  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*84d9c625SLionel Sambuc  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*84d9c625SLionel Sambuc  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*84d9c625SLionel Sambuc  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*84d9c625SLionel Sambuc  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*84d9c625SLionel Sambuc  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*84d9c625SLionel Sambuc  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*84d9c625SLionel Sambuc  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*84d9c625SLionel Sambuc  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*84d9c625SLionel Sambuc  */
26*84d9c625SLionel Sambuc #include <sys/cdefs.h>
27*84d9c625SLionel Sambuc __RCSID("$NetBSD: fenv.c,v 1.1 2011/05/20 21:42:49 nakayama Exp $");
28*84d9c625SLionel Sambuc 
29*84d9c625SLionel Sambuc #include <assert.h>
30*84d9c625SLionel Sambuc #include <fenv.h>
31*84d9c625SLionel Sambuc 
32*84d9c625SLionel Sambuc /* Load floating-point state register (32bits) */
33*84d9c625SLionel Sambuc #define	__ldfsr(__r)	__asm__	__volatile__		\
34*84d9c625SLionel Sambuc 	("ld %0, %%fsr" : : "m" (__r))
35*84d9c625SLionel Sambuc 
36*84d9c625SLionel Sambuc /* Save floating-point state register (32bits) */
37*84d9c625SLionel Sambuc #define	__stfsr(__r)	__asm__	__volatile__		\
38*84d9c625SLionel Sambuc 	("st %%fsr, %0" : "=m" (*(__r)))
39*84d9c625SLionel Sambuc 
40*84d9c625SLionel Sambuc /*
41*84d9c625SLionel Sambuc  * The feclearexcept() function clears the supported floating-point exceptions
42*84d9c625SLionel Sambuc  * represented by `excepts'.
43*84d9c625SLionel Sambuc  */
44*84d9c625SLionel Sambuc int
feclearexcept(int excepts)45*84d9c625SLionel Sambuc feclearexcept(int excepts)
46*84d9c625SLionel Sambuc {
47*84d9c625SLionel Sambuc 	fexcept_t r;
48*84d9c625SLionel Sambuc 	int ex;
49*84d9c625SLionel Sambuc 
50*84d9c625SLionel Sambuc 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
51*84d9c625SLionel Sambuc 
52*84d9c625SLionel Sambuc 	ex = excepts & FE_ALL_EXCEPT;
53*84d9c625SLionel Sambuc 
54*84d9c625SLionel Sambuc 	__stfsr(&r);
55*84d9c625SLionel Sambuc 	r &= ~ex;
56*84d9c625SLionel Sambuc 	__ldfsr(r);
57*84d9c625SLionel Sambuc 
58*84d9c625SLionel Sambuc 	/* Success */
59*84d9c625SLionel Sambuc 	return 0;
60*84d9c625SLionel Sambuc }
61*84d9c625SLionel Sambuc 
62*84d9c625SLionel Sambuc /*
63*84d9c625SLionel Sambuc  * The fegetexceptflag() function stores an implementation-defined
64*84d9c625SLionel Sambuc  * representation of the states of the floating-point status flags indicated
65*84d9c625SLionel Sambuc  * by the argument excepts in the object pointed to by the argument flagp.
66*84d9c625SLionel Sambuc  */
67*84d9c625SLionel Sambuc int
fegetexceptflag(fexcept_t * flagp,int excepts)68*84d9c625SLionel Sambuc fegetexceptflag(fexcept_t *flagp, int excepts)
69*84d9c625SLionel Sambuc {
70*84d9c625SLionel Sambuc 	fexcept_t r;
71*84d9c625SLionel Sambuc 	int ex;
72*84d9c625SLionel Sambuc 
73*84d9c625SLionel Sambuc 	_DIAGASSERT(flagp != NULL);
74*84d9c625SLionel Sambuc 	_DIAGASSERT((excepts & ~_FE_ALL_EXCEPT) == 0);
75*84d9c625SLionel Sambuc 
76*84d9c625SLionel Sambuc 	ex = excepts & FE_ALL_EXCEPT;
77*84d9c625SLionel Sambuc 
78*84d9c625SLionel Sambuc 	__stfsr(&r);
79*84d9c625SLionel Sambuc 	*flagp = r & ex;
80*84d9c625SLionel Sambuc 
81*84d9c625SLionel Sambuc 	/* Success */
82*84d9c625SLionel Sambuc 	return 0;
83*84d9c625SLionel Sambuc }
84*84d9c625SLionel Sambuc 
85*84d9c625SLionel Sambuc 
86*84d9c625SLionel Sambuc /*
87*84d9c625SLionel Sambuc  * This function sets the floating-point status flags indicated by the argument
88*84d9c625SLionel Sambuc  * `excepts' to the states stored in the object pointed to by `flagp'. It does
89*84d9c625SLionel Sambuc  * NOT raise any floating-point exceptions, but only sets the state of the flags.
90*84d9c625SLionel Sambuc  */
91*84d9c625SLionel Sambuc int
fesetexceptflag(const fexcept_t * flagp,int excepts)92*84d9c625SLionel Sambuc fesetexceptflag(const fexcept_t *flagp, int excepts)
93*84d9c625SLionel Sambuc {
94*84d9c625SLionel Sambuc 	fexcept_t r;
95*84d9c625SLionel Sambuc 	int ex;
96*84d9c625SLionel Sambuc 
97*84d9c625SLionel Sambuc 	_DIAGASSERT(flagp != NULL);
98*84d9c625SLionel Sambuc 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
99*84d9c625SLionel Sambuc 
100*84d9c625SLionel Sambuc 	ex = excepts & FE_ALL_EXCEPT;
101*84d9c625SLionel Sambuc 
102*84d9c625SLionel Sambuc 	__stfsr(&r);
103*84d9c625SLionel Sambuc 	r &= ~ex;
104*84d9c625SLionel Sambuc 	r |= *flagp & ex;
105*84d9c625SLionel Sambuc 	__ldfsr(r);
106*84d9c625SLionel Sambuc 
107*84d9c625SLionel Sambuc 	/* Success */
108*84d9c625SLionel Sambuc 	return 0;
109*84d9c625SLionel Sambuc }
110*84d9c625SLionel Sambuc 
111*84d9c625SLionel Sambuc /*
112*84d9c625SLionel Sambuc  * The feraiseexcept() function raises the supported floating-point exceptions
113*84d9c625SLionel Sambuc  * represented by the argument `excepts'.
114*84d9c625SLionel Sambuc  *
115*84d9c625SLionel Sambuc  * The order in which these floating-point exceptions are raised is unspecified
116*84d9c625SLionel Sambuc  * (by the standard).
117*84d9c625SLionel Sambuc  */
118*84d9c625SLionel Sambuc int
feraiseexcept(int excepts)119*84d9c625SLionel Sambuc feraiseexcept(int excepts)
120*84d9c625SLionel Sambuc {
121*84d9c625SLionel Sambuc 	volatile double d;
122*84d9c625SLionel Sambuc 	int ex;
123*84d9c625SLionel Sambuc 
124*84d9c625SLionel Sambuc 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
125*84d9c625SLionel Sambuc 
126*84d9c625SLionel Sambuc 	ex = excepts & FE_ALL_EXCEPT;
127*84d9c625SLionel Sambuc 
128*84d9c625SLionel Sambuc 	/*
129*84d9c625SLionel Sambuc 	 * With a compiler that supports the FENV_ACCESS pragma properly, simple
130*84d9c625SLionel Sambuc 	 * expressions like '0.0 / 0.0' should be sufficient to generate traps.
131*84d9c625SLionel Sambuc 	 * Unfortunately, we need to bring a volatile variable into the equation
132*84d9c625SLionel Sambuc 	 * to prevent incorrect optimizations.
133*84d9c625SLionel Sambuc 	 */
134*84d9c625SLionel Sambuc 	if (ex & FE_INVALID) {
135*84d9c625SLionel Sambuc 		d = 0.0;
136*84d9c625SLionel Sambuc 		d = 0.0 / d;
137*84d9c625SLionel Sambuc 	}
138*84d9c625SLionel Sambuc 	if (ex & FE_DIVBYZERO) {
139*84d9c625SLionel Sambuc 		d = 0.0;
140*84d9c625SLionel Sambuc 		d = 1.0 / d;
141*84d9c625SLionel Sambuc 	}
142*84d9c625SLionel Sambuc 	if (ex & FE_OVERFLOW) {
143*84d9c625SLionel Sambuc 		d = 0x1.ffp1023;
144*84d9c625SLionel Sambuc 		d *= 2.0;
145*84d9c625SLionel Sambuc 	}
146*84d9c625SLionel Sambuc 	if (ex & FE_UNDERFLOW) {
147*84d9c625SLionel Sambuc 		d = 0x1p-1022;
148*84d9c625SLionel Sambuc 		d /= 0x1p1023;
149*84d9c625SLionel Sambuc 	}
150*84d9c625SLionel Sambuc 	if (ex & FE_INEXACT) {
151*84d9c625SLionel Sambuc 		d = 0x1p-1022;
152*84d9c625SLionel Sambuc 		d += 1.0;
153*84d9c625SLionel Sambuc 	}
154*84d9c625SLionel Sambuc 
155*84d9c625SLionel Sambuc 	/* Success */
156*84d9c625SLionel Sambuc 	return 0;
157*84d9c625SLionel Sambuc }
158*84d9c625SLionel Sambuc 
159*84d9c625SLionel Sambuc /*
160*84d9c625SLionel Sambuc  * The fetestexcept() function determines which of a specified subset of the
161*84d9c625SLionel Sambuc  * floating-point exception flags are currently set. The `excepts' argument
162*84d9c625SLionel Sambuc  * specifies the floating-point status flags to be queried.
163*84d9c625SLionel Sambuc  */
164*84d9c625SLionel Sambuc int
fetestexcept(int excepts)165*84d9c625SLionel Sambuc fetestexcept(int excepts)
166*84d9c625SLionel Sambuc {
167*84d9c625SLionel Sambuc 	fexcept_t r;
168*84d9c625SLionel Sambuc 
169*84d9c625SLionel Sambuc 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
170*84d9c625SLionel Sambuc 
171*84d9c625SLionel Sambuc 	__stfsr(&r);
172*84d9c625SLionel Sambuc 
173*84d9c625SLionel Sambuc 	return r & (excepts & FE_ALL_EXCEPT);
174*84d9c625SLionel Sambuc }
175*84d9c625SLionel Sambuc 
176*84d9c625SLionel Sambuc /*
177*84d9c625SLionel Sambuc  * The fegetround() function gets the current rounding direction.
178*84d9c625SLionel Sambuc  */
179*84d9c625SLionel Sambuc int
fegetround(void)180*84d9c625SLionel Sambuc fegetround(void)
181*84d9c625SLionel Sambuc {
182*84d9c625SLionel Sambuc 	fenv_t r;
183*84d9c625SLionel Sambuc 
184*84d9c625SLionel Sambuc 	__stfsr(&r);
185*84d9c625SLionel Sambuc 
186*84d9c625SLionel Sambuc 	return (r >> _ROUND_SHIFT) & _ROUND_MASK;
187*84d9c625SLionel Sambuc }
188*84d9c625SLionel Sambuc 
189*84d9c625SLionel Sambuc /*
190*84d9c625SLionel Sambuc  * The fesetround() function establishes the rounding direction represented by
191*84d9c625SLionel Sambuc  * its argument `round'. If the argument is not equal to the value of a rounding
192*84d9c625SLionel Sambuc  * direction macro, the rounding direction is not changed.
193*84d9c625SLionel Sambuc  */
194*84d9c625SLionel Sambuc int
fesetround(int round)195*84d9c625SLionel Sambuc fesetround(int round)
196*84d9c625SLionel Sambuc {
197*84d9c625SLionel Sambuc 	fenv_t r;
198*84d9c625SLionel Sambuc 
199*84d9c625SLionel Sambuc 	_DIAGASSERT((round & ~_ROUND_MASK) == 0);
200*84d9c625SLionel Sambuc 	if (round & ~_ROUND_MASK)
201*84d9c625SLionel Sambuc 		return -1;
202*84d9c625SLionel Sambuc 
203*84d9c625SLionel Sambuc 	__stfsr(&r);
204*84d9c625SLionel Sambuc 	r &= ~(_ROUND_MASK << _ROUND_SHIFT);
205*84d9c625SLionel Sambuc 	r |= round << _ROUND_SHIFT;
206*84d9c625SLionel Sambuc 	__ldfsr(r);
207*84d9c625SLionel Sambuc 
208*84d9c625SLionel Sambuc 	/* Success */
209*84d9c625SLionel Sambuc 	return 0;
210*84d9c625SLionel Sambuc }
211*84d9c625SLionel Sambuc 
212*84d9c625SLionel Sambuc /*
213*84d9c625SLionel Sambuc  * The fegetenv() function attempts to store the current floating-point
214*84d9c625SLionel Sambuc  * environment in the object pointed to by envp.
215*84d9c625SLionel Sambuc  */
216*84d9c625SLionel Sambuc int
fegetenv(fenv_t * envp)217*84d9c625SLionel Sambuc fegetenv(fenv_t *envp)
218*84d9c625SLionel Sambuc {
219*84d9c625SLionel Sambuc 	_DIAGASSERT(envp != NULL);
220*84d9c625SLionel Sambuc 
221*84d9c625SLionel Sambuc 	__stfsr(envp);
222*84d9c625SLionel Sambuc 
223*84d9c625SLionel Sambuc 	/* Success */
224*84d9c625SLionel Sambuc 	return 0;
225*84d9c625SLionel Sambuc }
226*84d9c625SLionel Sambuc 
227*84d9c625SLionel Sambuc 
228*84d9c625SLionel Sambuc /*
229*84d9c625SLionel Sambuc  * The feholdexcept() function saves the current floating-point environment
230*84d9c625SLionel Sambuc  * in the object pointed to by envp, clears the floating-point status flags, and
231*84d9c625SLionel Sambuc  * then installs a non-stop (continue on floating-point exceptions) mode, if
232*84d9c625SLionel Sambuc  * available, for all floating-point exceptions.
233*84d9c625SLionel Sambuc  */
234*84d9c625SLionel Sambuc int
feholdexcept(fenv_t * envp)235*84d9c625SLionel Sambuc feholdexcept(fenv_t *envp)
236*84d9c625SLionel Sambuc {
237*84d9c625SLionel Sambuc 	fenv_t r;
238*84d9c625SLionel Sambuc 
239*84d9c625SLionel Sambuc 	_DIAGASSERT(envp != NULL);
240*84d9c625SLionel Sambuc 
241*84d9c625SLionel Sambuc 	__stfsr(&r);
242*84d9c625SLionel Sambuc 	*envp = r;
243*84d9c625SLionel Sambuc 	r &= ~(FE_ALL_EXCEPT | _ENABLE_MASK);
244*84d9c625SLionel Sambuc 	__ldfsr(r);
245*84d9c625SLionel Sambuc 
246*84d9c625SLionel Sambuc 	/* Success */
247*84d9c625SLionel Sambuc 	return 0;
248*84d9c625SLionel Sambuc }
249*84d9c625SLionel Sambuc 
250*84d9c625SLionel Sambuc /*
251*84d9c625SLionel Sambuc  * The fesetenv() function attempts to establish the floating-point environment
252*84d9c625SLionel Sambuc  * represented by the object pointed to by envp. The argument `envp' points
253*84d9c625SLionel Sambuc  * to an object set by a call to fegetenv() or feholdexcept(), or equal a
254*84d9c625SLionel Sambuc  * floating-point environment macro. The fesetenv() function does not raise
255*84d9c625SLionel Sambuc  * floating-point exceptions, but only installs the state of the floating-point
256*84d9c625SLionel Sambuc  * status flags represented through its argument.
257*84d9c625SLionel Sambuc  */
258*84d9c625SLionel Sambuc int
fesetenv(const fenv_t * envp)259*84d9c625SLionel Sambuc fesetenv(const fenv_t *envp)
260*84d9c625SLionel Sambuc {
261*84d9c625SLionel Sambuc 	_DIAGASSERT(envp != NULL);
262*84d9c625SLionel Sambuc 
263*84d9c625SLionel Sambuc 	__ldfsr(*envp);
264*84d9c625SLionel Sambuc 
265*84d9c625SLionel Sambuc 	/* Success */
266*84d9c625SLionel Sambuc 	return 0;
267*84d9c625SLionel Sambuc }
268*84d9c625SLionel Sambuc 
269*84d9c625SLionel Sambuc 
270*84d9c625SLionel Sambuc /*
271*84d9c625SLionel Sambuc  * The feupdateenv() function saves the currently raised floating-point
272*84d9c625SLionel Sambuc  * exceptions in its automatic storage, installs the floating-point environment
273*84d9c625SLionel Sambuc  * represented by the object pointed to by `envp', and then raises the saved
274*84d9c625SLionel Sambuc  * floating-point exceptions. The argument `envp' shall point to an object set
275*84d9c625SLionel Sambuc  * by a call to feholdexcept() or fegetenv(), or equal a floating-point
276*84d9c625SLionel Sambuc  * environment macro.
277*84d9c625SLionel Sambuc  */
278*84d9c625SLionel Sambuc int
feupdateenv(const fenv_t * envp)279*84d9c625SLionel Sambuc feupdateenv(const fenv_t *envp)
280*84d9c625SLionel Sambuc {
281*84d9c625SLionel Sambuc 	fexcept_t r;
282*84d9c625SLionel Sambuc 
283*84d9c625SLionel Sambuc 	_DIAGASSERT(envp != NULL);
284*84d9c625SLionel Sambuc 
285*84d9c625SLionel Sambuc 	__stfsr(&r);
286*84d9c625SLionel Sambuc 	__ldfsr(*envp);
287*84d9c625SLionel Sambuc 
288*84d9c625SLionel Sambuc 	_DIAGASSERT((r & ~FE_ALL_EXCEPT) == 0);
289*84d9c625SLionel Sambuc 	feraiseexcept(r & FE_ALL_EXCEPT);
290*84d9c625SLionel Sambuc 
291*84d9c625SLionel Sambuc 	/* Success */
292*84d9c625SLionel Sambuc 	return 0;
293*84d9c625SLionel Sambuc }
294*84d9c625SLionel Sambuc 
295*84d9c625SLionel Sambuc /*
296*84d9c625SLionel Sambuc  * The following functions are extentions to the standard
297*84d9c625SLionel Sambuc  */
298*84d9c625SLionel Sambuc int
feenableexcept(int mask)299*84d9c625SLionel Sambuc feenableexcept(int mask)
300*84d9c625SLionel Sambuc {
301*84d9c625SLionel Sambuc 	fenv_t old_r, new_r;
302*84d9c625SLionel Sambuc 
303*84d9c625SLionel Sambuc 	__stfsr(&old_r);
304*84d9c625SLionel Sambuc 	new_r = old_r | ((mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT);
305*84d9c625SLionel Sambuc 	__ldfsr(new_r);
306*84d9c625SLionel Sambuc 
307*84d9c625SLionel Sambuc 	return (old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT;
308*84d9c625SLionel Sambuc }
309*84d9c625SLionel Sambuc 
310*84d9c625SLionel Sambuc int
fedisableexcept(int mask)311*84d9c625SLionel Sambuc fedisableexcept(int mask)
312*84d9c625SLionel Sambuc {
313*84d9c625SLionel Sambuc 	fenv_t old_r, new_r;
314*84d9c625SLionel Sambuc 
315*84d9c625SLionel Sambuc 	__stfsr(&old_r);
316*84d9c625SLionel Sambuc 	new_r = old_r & ~((mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT);
317*84d9c625SLionel Sambuc 	__ldfsr(new_r);
318*84d9c625SLionel Sambuc 
319*84d9c625SLionel Sambuc 	return (old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT;
320*84d9c625SLionel Sambuc }
321*84d9c625SLionel Sambuc 
322*84d9c625SLionel Sambuc int
fegetexcept(void)323*84d9c625SLionel Sambuc fegetexcept(void)
324*84d9c625SLionel Sambuc {
325*84d9c625SLionel Sambuc 	fenv_t r;
326*84d9c625SLionel Sambuc 
327*84d9c625SLionel Sambuc 	__stfsr(&r);
328*84d9c625SLionel Sambuc 	return (r & _ENABLE_MASK) >> _FPUSW_SHIFT;
329*84d9c625SLionel Sambuc }
330