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