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