xref: /netbsd-src/lib/libm/arch/hppa/fenv.c (revision dce8d6f5042f8ec99d84a4e87373b78d2cbd2abb)
1 /*	$NetBSD: fenv.c,v 1.1 2014/12/27 16:54:02 martin 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.1 2014/12/27 16:54:02 martin Exp $");
28 
29 #include <assert.h>
30 #include <fenv.h>
31 
32 /*
33  * Convert from exception flags (__BITS(27,32)) to exception enable bits
34  * (__BITS(5,0)) by right-shifting this much:
35  */
36 #define	FE_FLAGS_SHIFT		27
37 
38 /*
39  * Mask all rounding mode bits
40  */
41 #define FE_ROUND_MASK	(FE_TONEAREST | FE_DOWNWARD | \
42 			FE_UPWARD | FE_TOWARDZERO)
43 
44 /*
45  * Our constants start at bit 0, while the fpsr bitfield starts at 9
46  */
47 #define	FE_ROUND_SHIFT	9
48 
49 /* Load lower 32 bits from floating-point state register */
50 static inline uint32_t
51 readfpsr(void)
52 {
53 	uint32_t rv;
54 
55 	__asm__	__volatile__ ("fstws	%%fr0, %0" : "=m"(rv));
56 	return rv;
57 }
58 
59 /* Save floating-point state register */
60 static inline void
61 writefpsr(uint32_t val)
62 {
63 	__asm__	__volatile__("fldws	%0,%%fr0" : : "m"(val));
64 }
65 
66 /*
67  * The feclearexcept() function clears the supported floating-point exceptions
68  * represented by `excepts'.
69  */
70 int
71 feclearexcept(int excepts)
72 {
73 	fexcept_t r;
74 	int ex;
75 
76 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
77 
78 	ex = (excepts & FE_ALL_EXCEPT) << FE_FLAGS_SHIFT;
79 
80 	r = readfpsr();
81 	r &= ~ex;
82 	writefpsr(r);
83 
84 	/* Success */
85 	return 0;
86 }
87 
88 /*
89  * The fegetexceptflag() function stores an implementation-defined
90  * representation of the states of the floating-point status flags indicated
91  * by the argument excepts in the object pointed to by the argument flagp.
92  */
93 int
94 fegetexceptflag(fexcept_t *flagp, int excepts)
95 {
96 	fexcept_t r;
97 	int ex;
98 
99 	_DIAGASSERT(flagp != NULL);
100 	_DIAGASSERT((excepts & ~_FE_ALL_EXCEPT) == 0);
101 
102 	ex = (excepts & FE_ALL_EXCEPT) << FE_FLAGS_SHIFT;
103 
104 	r = readfpsr();
105 	*flagp = (r & ex) >> FE_FLAGS_SHIFT;
106 
107 	/* Success */
108 	return 0;
109 }
110 
111 
112 /*
113  * This function sets the floating-point status flags indicated by the argument
114  * `excepts' to the states stored in the object pointed to by `flagp'. It does
115  * NOT raise any floating-point exceptions, but only sets the state of the flags.
116  */
117 int
118 fesetexceptflag(const fexcept_t *flagp, int excepts)
119 {
120 	fexcept_t r;
121 	int ex;
122 
123 	_DIAGASSERT(flagp != NULL);
124 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
125 
126 	ex = (excepts & FE_ALL_EXCEPT) << FE_FLAGS_SHIFT;
127 
128 	r = readfpsr();
129 	r &= ~ex;
130 	r |= (*flagp << FE_FLAGS_SHIFT) & ex;
131 	writefpsr(r);
132 
133 	/* Success */
134 	return 0;
135 }
136 
137 /*
138  * The feraiseexcept() function raises the supported floating-point exceptions
139  * represented by the argument `excepts'.
140  *
141  * The order in which these floating-point exceptions are raised is unspecified
142  * (by the standard).
143  */
144 int
145 feraiseexcept(int excepts)
146 {
147 	volatile double d;
148 	int ex;
149 
150 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
151 
152 	ex = excepts & FE_ALL_EXCEPT;
153 
154 	/*
155 	 * With a compiler that supports the FENV_ACCESS pragma properly, simple
156 	 * expressions like '0.0 / 0.0' should be sufficient to generate traps.
157 	 * Unfortunately, we need to bring a volatile variable into the equation
158 	 * to prevent incorrect optimizations.
159 	 */
160 	if (ex & FE_INVALID) {
161 		d = 0.0;
162 		d = 0.0 / d;
163 	}
164 	if (ex & FE_DIVBYZERO) {
165 		d = 0.0;
166 		d = 1.0 / d;
167 	}
168 	if (ex & FE_OVERFLOW) {
169 		d = 0x1.ffp1023;
170 		d *= 2.0;
171 	}
172 	if (ex & FE_UNDERFLOW) {
173 		d = 0x1p-1022;
174 		d /= 0x1p1023;
175 	}
176 	if (ex & FE_INEXACT) {
177 		d = 0x1p-1022;
178 		d += 1.0;
179 	}
180 
181 	/* Success */
182 	return 0;
183 }
184 
185 /*
186  * The fetestexcept() function determines which of a specified subset of the
187  * floating-point exception flags are currently set. The `excepts' argument
188  * specifies the floating-point status flags to be queried.
189  */
190 int
191 fetestexcept(int excepts)
192 {
193 	fexcept_t r;
194 
195 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
196 
197 	r = readfpsr();
198 
199 	return (r >> FE_FLAGS_SHIFT) & (excepts & FE_ALL_EXCEPT);
200 }
201 
202 /*
203  * The fegetround() function gets the current rounding direction.
204  */
205 int
206 fegetround(void)
207 {
208 	fenv_t r;
209 
210 	r = readfpsr();
211 
212 	return (r>>FE_ROUND_SHIFT) & FE_ROUND_MASK;
213 }
214 
215 /*
216  * The fesetround() function establishes the rounding direction represented by
217  * its argument `round'. If the argument is not equal to the value of a rounding
218  * direction macro, the rounding direction is not changed.
219  */
220 int
221 fesetround(int round)
222 {
223 	fenv_t r;
224 
225 	_DIAGASSERT((round & ~FE_ROUND_MASK) == 0);
226 	if (round & ~FE_ROUND_MASK)
227 		return -1;
228 
229 	r = readfpsr();
230 	r &= ~(FE_ROUND_MASK << FE_ROUND_SHIFT);
231 	r |= round << FE_ROUND_SHIFT;
232 	writefpsr(r);
233 
234 	/* Success */
235 	return 0;
236 }
237 
238 /*
239  * The fegetenv() function attempts to store the current floating-point
240  * environment in the object pointed to by envp.
241  */
242 int
243 fegetenv(fenv_t *envp)
244 {
245 	_DIAGASSERT(envp != NULL);
246 
247 	*envp = readfpsr();
248 
249 	/* Success */
250 	return 0;
251 }
252 
253 
254 /*
255  * The feholdexcept() function saves the current floating-point environment
256  * in the object pointed to by envp, clears the floating-point status flags, and
257  * then installs a non-stop (continue on floating-point exceptions) mode, if
258  * available, for all floating-point exceptions.
259  */
260 int
261 feholdexcept(fenv_t *envp)
262 {
263 	fenv_t r;
264 
265 	_DIAGASSERT(envp != NULL);
266 
267 	r = readfpsr();
268 	*envp = r;
269 	r &= ~FE_ALL_EXCEPT;
270 	writefpsr(r);
271 
272 	/* Success */
273 	return 0;
274 }
275 
276 /*
277  * The fesetenv() function attempts to establish the floating-point environment
278  * represented by the object pointed to by envp. The argument `envp' points
279  * to an object set by a call to fegetenv() or feholdexcept(), or equal a
280  * floating-point environment macro. The fesetenv() function does not raise
281  * floating-point exceptions, but only installs the state of the floating-point
282  * status flags represented through its argument.
283  */
284 int
285 fesetenv(const fenv_t *envp)
286 {
287 	_DIAGASSERT(envp != NULL);
288 
289 	writefpsr(*envp);
290 
291 	/* Success */
292 	return 0;
293 }
294 
295 
296 /*
297  * The feupdateenv() function saves the currently raised floating-point
298  * exceptions in its automatic storage, installs the floating-point environment
299  * represented by the object pointed to by `envp', and then raises the saved
300  * floating-point exceptions. The argument `envp' shall point to an object set
301  * by a call to feholdexcept() or fegetenv(), or equal a floating-point
302  * environment macro.
303  */
304 int
305 feupdateenv(const fenv_t *envp)
306 {
307 	fexcept_t r;
308 
309 	_DIAGASSERT(envp != NULL);
310 
311 	r = readfpsr();
312 	writefpsr(*envp);
313 
314 	_DIAGASSERT((r & ~FE_ALL_EXCEPT) == 0);
315 	feraiseexcept(r & FE_ALL_EXCEPT);
316 
317 	/* Success */
318 	return 0;
319 }
320 
321 /*
322  * The following functions are extentions to the standard
323  */
324 int
325 feenableexcept(int mask)
326 {
327 	fenv_t old_r, new_r;
328 
329 	old_r = readfpsr();
330 	new_r = old_r | (mask & FE_ALL_EXCEPT);
331 	writefpsr(new_r);
332 
333 	return old_r & FE_ALL_EXCEPT;
334 }
335 
336 int
337 fedisableexcept(int mask)
338 {
339 	fenv_t old_r, new_r;
340 
341 	old_r = readfpsr();
342 	new_r = old_r & ~(mask & FE_ALL_EXCEPT);
343 	writefpsr(new_r);
344 
345 	return old_r & FE_ALL_EXCEPT;
346 }
347 
348 int
349 fegetexcept(void)
350 {
351 	fenv_t r;
352 
353 	r = readfpsr();
354 	return r & FE_ALL_EXCEPT;
355 }
356