xref: /openbsd-src/lib/libm/arch/hppa/fenv.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: fenv.c,v 1.2 2011/04/28 17:34:23 martynas Exp $	*/
2 
3 /*
4  * Copyright (c) 2011 Martynas Venckus <martynas@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/cdefs.h>
20 
21 #include <fenv.h>
22 
23 union u {
24 	unsigned long long fpsr;
25 	unsigned int bits[2];
26 };
27 
28 /*
29  * The following constant represents the default floating-point environment
30  * (that is, the one installed at program startup) and has type pointer to
31  * const-qualified fenv_t.
32  *
33  * It can be used as an argument to the functions within the <fenv.h> header
34  * that manage the floating-point environment, namely fesetenv() and
35  * feupdateenv().
36  */
37 fenv_t __fe_dfl_env = 0;
38 
39 /*
40  * The feclearexcept() function clears the supported floating-point exceptions
41  * represented by `excepts'.
42  */
43 int
44 feclearexcept(int excepts)
45 {
46 	volatile union u u;
47 
48 	excepts &= FE_ALL_EXCEPT;
49 
50 	/* Store the current floating-point status register */
51 	__asm__ __volatile__ ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
52 	    "r" (&u.fpsr));
53 
54 	/* Clear the requested floating-point exceptions */
55 	u.bits[0] &= ~(excepts << _MASK_SHIFT);
56 
57 	/* Load the floating-point status register */
58 	__asm__ __volatile__ ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr));
59 
60 	return (0);
61 }
62 
63 /*
64  * The fegetexceptflag() function stores an implementation-defined
65  * representation of the states of the floating-point status flags indicated by
66  * the argument excepts in the object pointed to by the argument flagp.
67  */
68 int
69 fegetexceptflag(fexcept_t *flagp, int excepts)
70 {
71 	volatile union u u;
72 
73 	excepts &= FE_ALL_EXCEPT;
74 
75 	/* Store the current floating-point status register */
76 	__asm__ __volatile__ ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
77 	    "r" (&u.fpsr));
78 
79 	/* Store the results in flagp */
80 	*flagp = (u.bits[0] >> _MASK_SHIFT) & excepts;
81 
82 	return (0);
83 }
84 
85 /*
86  * The feraiseexcept() function raises the supported floating-point exceptions
87  * represented by the argument `excepts'.
88  */
89 int
90 feraiseexcept(int excepts)
91 {
92 	volatile double d;
93 
94 	excepts &= FE_ALL_EXCEPT;
95 
96 	/*
97 	 * With a compiler that supports the FENV_ACCESS pragma
98 	 * properly, simple expressions like '0.0 / 0.0' should
99 	 * be sufficient to generate traps.  Unfortunately, we
100 	 * need to bring a volatile variable into the equation
101 	 * to prevent incorrect optimizations.
102 	 */
103 	if (excepts & FE_INVALID) {
104 		d = 0.0;
105 		d = 0.0 / d;
106 	}
107 	if (excepts & FE_DIVBYZERO) {
108 		d = 0.0;
109 		d = 1.0 / d;
110 	}
111 	if (excepts & FE_OVERFLOW) {
112 		d = 0x1.ffp1023;
113 		d *= 2.0;
114 	}
115 	if (excepts & FE_UNDERFLOW) {
116 		d = 0x1p-1022;
117 		d /= 0x1p1023;
118 	}
119 	if (excepts & FE_INEXACT) {
120 		d = 0x1p-1022;
121 		d += 1.0;
122 	}
123 	__asm__ __volatile__ ("fldd 0(%%sr0,%%sp), %0" : "=f" (d));
124 
125 	return (0);
126 }
127 
128 /*
129  * This function sets the floating-point status flags indicated by the argument
130  * `excepts' to the states stored in the object pointed to by `flagp'. It does
131  * NOT raise any floating-point exceptions, but only sets the state of the flags.
132  */
133 int
134 fesetexceptflag(const fexcept_t *flagp, int excepts)
135 {
136 	volatile union u u;
137 
138 	excepts &= FE_ALL_EXCEPT;
139 
140 	/* Store the current floating-point status register */
141 	__asm__ __volatile__ ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
142 	    "r" (&u.fpsr));
143 
144 	/* Set the requested status flags */
145 	u.bits[0] &= ~(excepts << _MASK_SHIFT);
146 	u.bits[0] |= (*flagp & excepts) << _MASK_SHIFT;
147 
148 	/* Load the floating-point status register */
149 	__asm__ __volatile__ ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr));
150 
151 	return (0);
152 }
153 
154 /*
155  * The fetestexcept() function determines which of a specified subset of the
156  * floating-point exception flags are currently set. The `excepts' argument
157  * specifies the floating-point status flags to be queried.
158  */
159 int
160 fetestexcept(int excepts)
161 {
162 	volatile union u u;
163 
164 	excepts &= FE_ALL_EXCEPT;
165 
166 	/* Store the current floating-point status register */
167 	__asm__ __volatile__ ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
168 	    "r" (&u.fpsr));
169 
170 	return ((u.bits[0] >> _MASK_SHIFT) & excepts);
171 }
172 
173 /*
174  * The fegetround() function gets the current rounding direction.
175  */
176 int
177 fegetround(void)
178 {
179 	volatile union u u;
180 
181 	/* Store the current floating-point status register */
182 	__asm__ __volatile__ ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
183 	    "r" (&u.fpsr));
184 
185 	return (u.bits[0] & _ROUND_MASK);
186 }
187 
188 /*
189  * The fesetround() function establishes the rounding direction represented by
190  * its argument `round'. If the argument is not equal to the value of a rounding
191  * direction macro, the rounding direction is not changed.
192  */
193 int
194 fesetround(int round)
195 {
196 	volatile union u u;
197 
198 	/* Check whether requested rounding direction is supported */
199 	if (round & ~_ROUND_MASK)
200 		return (-1);
201 
202 	/* Store the current floating-point status register */
203 	__asm__ __volatile__ ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
204 	    "r" (&u.fpsr));
205 
206 	/* Set the rounding direction */
207 	u.bits[0] &= ~_ROUND_MASK;
208 	u.bits[0] |= round;
209 
210 	/* Load the floating-point status register */
211 	__asm__ __volatile__ ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr));
212 
213 	return (0);
214 }
215 
216 /*
217  * The fegetenv() function attempts to store the current floating-point
218  * environment in the object pointed to by envp.
219  */
220 int
221 fegetenv(fenv_t *envp)
222 {
223 	volatile union u u;
224 
225 	/* Store the current floating-point status register */
226 	__asm__ __volatile__ ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
227 	    "r" (&u.fpsr));
228 
229 	*envp = u.bits[0];
230 
231 	return (0);
232 }
233 
234 /*
235  * The feholdexcept() function saves the current floating-point environment
236  * in the object pointed to by envp, clears the floating-point status flags, and
237  * then installs a non-stop (continue on floating-point exceptions) mode, if
238  * available, for all floating-point exceptions.
239  */
240 int
241 feholdexcept(fenv_t *envp)
242 {
243 	volatile union u u;
244 
245 	/* Store the current floating-point status register */
246 	__asm__ __volatile__ ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
247 	    "r" (&u.fpsr));
248 
249 	*envp = u.bits[0];
250 
251 	/* Clear exception flags in FPSR */
252 	u.bits[0] &= ~(FE_ALL_EXCEPT << _MASK_SHIFT);
253 
254 	/* Mask all exceptions */
255 	u.bits[0] &= ~FE_ALL_EXCEPT;
256 	__asm__ __volatile__ ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr));
257 
258 	return (0);
259 }
260 
261 /*
262  * The fesetenv() function attempts to establish the floating-point environment
263  * represented by the object pointed to by envp. The argument `envp' points
264  * to an object set by a call to fegetenv() or feholdexcept(), or equal a
265  * floating-point environment macro. The fesetenv() function does not raise
266  * floating-point exceptions, but only installs the state of the floating-point
267  * status flags represented through its argument.
268  */
269 int
270 fesetenv(const fenv_t *envp)
271 {
272 	volatile union u u;
273 
274 	/* Store the current floating-point status register */
275 	__asm__ __volatile__ ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
276 	    "r" (&u.fpsr));
277 
278 	/* Set the requested flags */
279 	u.bits[0] &= ~(FE_ALL_EXCEPT | _ROUND_MASK |
280 	    (FE_ALL_EXCEPT << _MASK_SHIFT));
281 	u.bits[0] |= *envp & (FE_ALL_EXCEPT | _ROUND_MASK |
282 	    (FE_ALL_EXCEPT << _MASK_SHIFT));
283 
284 	/* Load the floating-point status register */
285 	__asm__ __volatile__ ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr));
286 
287 	return (0);
288 }
289 
290 /*
291  * The feupdateenv() function saves the currently raised floating-point
292  * exceptions in its automatic storage, installs the floating-point environment
293  * represented by the object pointed to by `envp', and then raises the saved
294  * floating-point exceptions. The argument `envp' shall point to an object set
295  * by a call to feholdexcept() or fegetenv(), or equal a floating-point
296  * environment macro.
297  */
298 int
299 feupdateenv(const fenv_t *envp)
300 {
301 	volatile union u u;
302 
303 	/* Store the current floating-point status register */
304 	__asm__ __volatile__ ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
305 	    "r" (&u.fpsr));
306 
307 	/* Install new floating-point environment */
308 	fesetenv(envp);
309 
310 	/* Raise any previously accumulated exceptions */
311 	feraiseexcept(u.bits[0] >> _MASK_SHIFT);
312 
313 	return (0);
314 }
315 
316 /*
317  * The following functions are extentions to the standard
318  */
319 int
320 feenableexcept(int mask)
321 {
322 	volatile union u u;
323 	unsigned int omask;
324 
325 	mask &= FE_ALL_EXCEPT;
326 
327 	/* Store the current floating-point status register */
328 	__asm__ __volatile__ ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
329 	    "r" (&u.fpsr));
330 
331 	omask = u.bits[0] & FE_ALL_EXCEPT;
332 	u.bits[0] |= mask;
333 
334 	/* Load the floating-point status register */
335 	__asm__ __volatile__ ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr));
336 
337 	return (omask);
338 
339 }
340 
341 int
342 fedisableexcept(int mask)
343 {
344 	volatile union u u;
345 	unsigned int omask;
346 
347 	mask &= FE_ALL_EXCEPT;
348 
349 	/* Store the current floating-point status register */
350 	__asm__ __volatile__ ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
351 	    "r" (&u.fpsr));
352 
353 	omask = u.bits[0] & FE_ALL_EXCEPT;
354 	u.bits[0] &= ~mask;
355 
356 	/* Load the floating-point status register */
357 	__asm__ __volatile__ ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr));
358 
359 	return (omask);
360 }
361 
362 int
363 fegetexcept(void)
364 {
365 	volatile union u u;
366 
367 	/* Store the current floating-point status register */
368 	__asm__ __volatile__ ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
369 	    "r" (&u.fpsr));
370 
371 	return (u.bits[0] & FE_ALL_EXCEPT);
372 }
373