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