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