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