1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD$
29 */
30
31#ifndef	_FENV_H_
32#define	_FENV_H_
33
34#include <stdint.h>
35#include <sys/cdefs.h>
36#include <sys/types.h>
37
38#ifndef	__fenv_static
39#define	__fenv_static	static
40#endif
41
42typedef	uint64_t	fenv_t;
43typedef	uint64_t	fexcept_t;
44
45/* Exception flags */
46#define	FE_INVALID	0x00000200
47#define	FE_DIVBYZERO	0x00000040
48#define	FE_OVERFLOW	0x00000100
49#define	FE_UNDERFLOW	0x00000080
50#define	FE_INEXACT	0x00000020
51#define	FE_ALL_EXCEPT	(FE_DIVBYZERO | FE_INEXACT | \
52			 FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW)
53
54/*
55 * Rounding modes
56 *
57 * We can't just use the hardware bit values here, because that would
58 * make FE_UPWARD and FE_DOWNWARD negative, which is not allowed.
59 */
60#define	FE_TONEAREST	0x0
61#define	FE_TOWARDZERO	0x1
62#define	FE_UPWARD	0x2
63#define	FE_DOWNWARD	0x3
64#define	_ROUND_MASK	(FE_TONEAREST | FE_DOWNWARD | \
65			 FE_UPWARD | FE_TOWARDZERO)
66#define	_ROUND_SHIFT	30
67
68__BEGIN_DECLS
69
70/* Default floating-point environment */
71extern const fenv_t	__fe_dfl_env;
72#define	FE_DFL_ENV	(&__fe_dfl_env)
73
74/* We need to be able to map status flag positions to mask flag positions */
75#define _FPUSW_SHIFT	18
76#define	_ENABLE_MASK	(FE_ALL_EXCEPT << _FPUSW_SHIFT)
77
78#define	__ldxfsr(__r)	__asm __volatile("ldx %0, %%fsr" : : "m" (__r))
79#define	__stxfsr(__r)	__asm __volatile("stx %%fsr, %0" : "=m" (*(__r)))
80
81__fenv_static __inline int
82feclearexcept(int __excepts)
83{
84	fexcept_t __r;
85
86	__stxfsr(&__r);
87	__r &= ~__excepts;
88	__ldxfsr(__r);
89	return (0);
90}
91
92__fenv_static inline int
93fegetexceptflag(fexcept_t *__flagp, int __excepts)
94{
95	fexcept_t __r;
96
97	__stxfsr(&__r);
98	*__flagp = __r & __excepts;
99	return (0);
100}
101
102__fenv_static inline int
103fesetexceptflag(const fexcept_t *__flagp, int __excepts)
104{
105	fexcept_t __r;
106
107	__stxfsr(&__r);
108	__r &= ~__excepts;
109	__r |= *__flagp & __excepts;
110	__ldxfsr(__r);
111	return (0);
112}
113
114/*
115 * It seems to be worthwhile to inline this function even when the
116 * arguments are not compile-time constants.  Perhaps this depends
117 * on the register window.
118 */
119__fenv_static inline int
120feraiseexcept(int __excepts)
121{
122	volatile double d;
123
124	/*
125	 * With a compiler that supports the FENV_ACCESS pragma
126	 * properly, simple expressions like '0.0 / 0.0' should
127	 * be sufficient to generate traps.  Unfortunately, we
128	 * need to bring a volatile variable into the equation
129	 * to prevent incorrect optimizations.
130	 */
131	if (__excepts & FE_INVALID) {
132		d = 0.0;
133		d = 0.0 / d;
134	}
135	if (__excepts & FE_DIVBYZERO) {
136		d = 0.0;
137		d = 1.0 / d;
138	}
139	if (__excepts & FE_OVERFLOW) {
140		d = 0x1.ffp1023;
141		d *= 2.0;
142	}
143	if (__excepts & FE_UNDERFLOW) {
144		d = 0x1p-1022;
145		d /= 0x1p1023;
146	}
147	if (__excepts & FE_INEXACT) {
148		d = 0x1p-1022;
149		d += 1.0;
150	}
151	return (0);
152}
153
154__fenv_static inline int
155fetestexcept(int __excepts)
156{
157	fexcept_t __r;
158
159	__stxfsr(&__r);
160	return (__r & __excepts);
161}
162
163__fenv_static inline int
164fegetround(void)
165{
166	fenv_t __r;
167
168	__stxfsr(&__r);
169	return ((__r >> _ROUND_SHIFT) & _ROUND_MASK);
170}
171
172__fenv_static inline int
173fesetround(int __round)
174{
175	fenv_t __r;
176
177	if (__round & ~_ROUND_MASK)
178		return (-1);
179	__stxfsr(&__r);
180	__r &= ~(_ROUND_MASK << _ROUND_SHIFT);
181	__r |= __round << _ROUND_SHIFT;
182	__ldxfsr(__r);
183	return (0);
184}
185
186__fenv_static inline int
187fegetenv(fenv_t *__envp)
188{
189
190	__stxfsr(__envp);
191	return (0);
192}
193
194__fenv_static inline int
195feholdexcept(fenv_t *__envp)
196{
197	fenv_t __r;
198
199	__stxfsr(&__r);
200	*__envp = __r;
201	__r &= ~(FE_ALL_EXCEPT | _ENABLE_MASK);
202	__ldxfsr(__r);
203	return (0);
204}
205
206__fenv_static inline int
207fesetenv(const fenv_t *__envp)
208{
209
210	__ldxfsr(*__envp);
211	return (0);
212}
213
214__fenv_static inline int
215feupdateenv(const fenv_t *__envp)
216{
217	fexcept_t __r;
218
219	__stxfsr(&__r);
220	__ldxfsr(*__envp);
221	feraiseexcept(__r & FE_ALL_EXCEPT);
222	return (0);
223}
224
225#if __BSD_VISIBLE
226
227/* We currently provide no external definitions of the functions below. */
228
229static inline int
230feenableexcept(int __mask)
231{
232	fenv_t __old_r, __new_r;
233
234	__stxfsr(&__old_r);
235	__new_r = __old_r | ((__mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT);
236	__ldxfsr(__new_r);
237	return ((__old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT);
238}
239
240static inline int
241fedisableexcept(int __mask)
242{
243	fenv_t __old_r, __new_r;
244
245	__stxfsr(&__old_r);
246	__new_r = __old_r & ~((__mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT);
247	__ldxfsr(__new_r);
248	return ((__old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT);
249}
250
251static inline int
252fegetexcept(void)
253{
254	fenv_t __r;
255
256	__stxfsr(&__r);
257	return ((__r & _ENABLE_MASK) >> _FPUSW_SHIFT);
258}
259
260#endif /* __BSD_VISIBLE */
261
262__END_DECLS
263
264#endif	/* !_FENV_H_ */
265