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