1/*	$OpenBSD: fenv.c,v 1.7 2023/01/27 16:43:33 miod Exp $	*/
2
3/*
4 * Copyright (c) 2011 Martynas Venckus <martynas@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <fenv.h>
20
21union u {
22	unsigned long long fpsr;
23	unsigned int bits[2];
24};
25
26/*
27 * The following constant represents the default floating-point environment
28 * (that is, the one installed at program startup) and has type pointer to
29 * const-qualified fenv_t.
30 *
31 * It can be used as an argument to the functions within the <fenv.h> header
32 * that manage the floating-point environment, namely fesetenv() and
33 * feupdateenv().
34 */
35fenv_t __fe_dfl_env = 0;
36
37/*
38 * The feclearexcept() function clears the supported floating-point exceptions
39 * represented by `excepts'.
40 */
41int
42feclearexcept(int excepts)
43{
44	volatile union u u;
45
46	excepts &= FE_ALL_EXCEPT;
47
48	/* Store the current floating-point status register */
49	__asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
50	    "r" (&u.fpsr));
51
52	/* Clear the requested floating-point exceptions */
53	u.bits[0] &= ~(excepts << _MASK_SHIFT);
54
55	/* Load the floating-point status register */
56	__asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr), "m" (u.fpsr));
57
58	return (0);
59}
60DEF_STD(feclearexcept);
61
62/*
63 * The fegetexceptflag() function stores an implementation-defined
64 * representation of the states of the floating-point status flags indicated by
65 * the argument excepts in the object pointed to by the argument flagp.
66 */
67int
68fegetexceptflag(fexcept_t *flagp, int excepts)
69{
70	volatile union u u;
71
72	excepts &= FE_ALL_EXCEPT;
73
74	/* Store the current floating-point status register */
75	__asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
76	    "r" (&u.fpsr));
77
78	/* Store the results in flagp */
79	*flagp = (u.bits[0] >> _MASK_SHIFT) & excepts;
80
81	return (0);
82}
83
84/*
85 * The feraiseexcept() function raises the supported floating-point exceptions
86 * represented by the argument `excepts'.
87 */
88int
89feraiseexcept(int excepts)
90{
91	volatile double d;
92
93	excepts &= FE_ALL_EXCEPT;
94
95	/*
96	 * With a compiler that supports the FENV_ACCESS pragma
97	 * properly, simple expressions like '0.0 / 0.0' should
98	 * be sufficient to generate traps.  Unfortunately, we
99	 * need to bring a volatile variable into the equation
100	 * to prevent incorrect optimizations.
101	 */
102	if (excepts & FE_INVALID) {
103		d = 0.0;
104		d = 0.0 / d;
105	}
106	if (excepts & FE_DIVBYZERO) {
107		d = 0.0;
108		d = 1.0 / d;
109	}
110	if (excepts & FE_OVERFLOW) {
111		d = 0x1.ffp1023;
112		d *= 2.0;
113	}
114	if (excepts & FE_UNDERFLOW) {
115		d = 0x1p-1022;
116		d /= 0x1p1023;
117	}
118	if (excepts & FE_INEXACT) {
119		d = 0x1p-1022;
120		d += 1.0;
121	}
122	__asm__ volatile ("fldd 0(%%sr0,%%sp), %0" : "=f" (d));
123
124	return (0);
125}
126DEF_STD(feraiseexcept);
127
128/*
129 * This function sets the floating-point status flags indicated by the argument
130 * `excepts' to the states stored in the object pointed to by `flagp'. It does
131 * NOT raise any floating-point exceptions, but only sets the state of the flags.
132 */
133int
134fesetexceptflag(const fexcept_t *flagp, int excepts)
135{
136	volatile union u u;
137
138	excepts &= FE_ALL_EXCEPT;
139
140	/* Store the current floating-point status register */
141	__asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
142	    "r" (&u.fpsr));
143
144	/* Set the requested status flags */
145	u.bits[0] &= ~(excepts << _MASK_SHIFT);
146	u.bits[0] |= (*flagp & excepts) << _MASK_SHIFT;
147
148	/* Load the floating-point status register */
149	__asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr), "m" (u.fpsr));
150
151	return (0);
152}
153DEF_STD(fesetexceptflag);
154
155/*
156 * The fetestexcept() function determines which of a specified subset of the
157 * floating-point exception flags are currently set. The `excepts' argument
158 * specifies the floating-point status flags to be queried.
159 */
160int
161fetestexcept(int excepts)
162{
163	volatile union u u;
164
165	excepts &= FE_ALL_EXCEPT;
166
167	/* Store the current floating-point status register */
168	__asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
169	    "r" (&u.fpsr));
170
171	return ((u.bits[0] >> _MASK_SHIFT) & excepts);
172}
173DEF_STD(fetestexcept);
174
175/*
176 * The fegetround() function gets the current rounding direction.
177 */
178int
179fegetround(void)
180{
181	volatile union u u;
182
183	/* Store the current floating-point status register */
184	__asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
185	    "r" (&u.fpsr));
186
187	return (u.bits[0] & _ROUND_MASK);
188}
189DEF_STD(fegetround);
190
191/*
192 * The fesetround() function establishes the rounding direction represented by
193 * its argument `round'. If the argument is not equal to the value of a rounding
194 * direction macro, the rounding direction is not changed.
195 */
196int
197fesetround(int round)
198{
199	volatile union u u;
200
201	/* Check whether requested rounding direction is supported */
202	if (round & ~_ROUND_MASK)
203		return (-1);
204
205	/* Store the current floating-point status register */
206	__asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
207	    "r" (&u.fpsr));
208
209	/* Set the rounding direction */
210	u.bits[0] &= ~_ROUND_MASK;
211	u.bits[0] |= round;
212
213	/* Load the floating-point status register */
214	__asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr), "m"(u.fpsr));
215
216	return (0);
217}
218DEF_STD(fesetround);
219
220/*
221 * The fegetenv() function attempts to store the current floating-point
222 * environment in the object pointed to by envp.
223 */
224int
225fegetenv(fenv_t *envp)
226{
227	volatile union u u;
228
229	/* Store the current floating-point status register */
230	__asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
231	    "r" (&u.fpsr));
232
233	*envp = u.bits[0];
234
235	return (0);
236}
237DEF_STD(fegetenv);
238
239/*
240 * The feholdexcept() function saves the current floating-point environment
241 * in the object pointed to by envp, clears the floating-point status flags, and
242 * then installs a non-stop (continue on floating-point exceptions) mode, if
243 * available, for all floating-point exceptions.
244 */
245int
246feholdexcept(fenv_t *envp)
247{
248	volatile union u u;
249
250	/* Store the current floating-point status register */
251	__asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
252	    "r" (&u.fpsr));
253
254	*envp = u.bits[0];
255
256	/* Clear exception flags in FPSR */
257	u.bits[0] &= ~(FE_ALL_EXCEPT << _MASK_SHIFT);
258
259	/* Mask all exceptions */
260	u.bits[0] &= ~FE_ALL_EXCEPT;
261	__asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr), "m"(u.fpsr));
262
263	return (0);
264}
265DEF_STD(feholdexcept);
266
267/*
268 * The fesetenv() function attempts to establish the floating-point environment
269 * represented by the object pointed to by envp. The argument `envp' points
270 * to an object set by a call to fegetenv() or feholdexcept(), or equal a
271 * floating-point environment macro. The fesetenv() function does not raise
272 * floating-point exceptions, but only installs the state of the floating-point
273 * status flags represented through its argument.
274 */
275int
276fesetenv(const fenv_t *envp)
277{
278	volatile union u u;
279
280	/* Store the current floating-point status register */
281	__asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
282	    "r" (&u.fpsr));
283
284	/* Set the requested flags */
285	u.bits[0] &= ~(FE_ALL_EXCEPT | _ROUND_MASK |
286	    (FE_ALL_EXCEPT << _MASK_SHIFT));
287	u.bits[0] |= *envp & (FE_ALL_EXCEPT | _ROUND_MASK |
288	    (FE_ALL_EXCEPT << _MASK_SHIFT));
289
290	/* Load the floating-point status register */
291	__asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr), "m"(u.fpsr));
292
293	return (0);
294}
295DEF_STD(fesetenv);
296
297/*
298 * The feupdateenv() function saves the currently raised floating-point
299 * exceptions in its automatic storage, installs the floating-point environment
300 * represented by the object pointed to by `envp', and then raises the saved
301 * floating-point exceptions. The argument `envp' shall point to an object set
302 * by a call to feholdexcept() or fegetenv(), or equal a floating-point
303 * environment macro.
304 */
305int
306feupdateenv(const fenv_t *envp)
307{
308	volatile union u u;
309
310	/* Store the current floating-point status register */
311	__asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
312	    "r" (&u.fpsr));
313
314	/* Install new floating-point environment */
315	fesetenv(envp);
316
317	/* Raise any previously accumulated exceptions */
318	feraiseexcept(u.bits[0] >> _MASK_SHIFT);
319
320	return (0);
321}
322DEF_STD(feupdateenv);
323
324/*
325 * The following functions are extensions to the standard
326 */
327int
328feenableexcept(int mask)
329{
330	volatile union u u;
331	unsigned int omask;
332
333	mask &= FE_ALL_EXCEPT;
334
335	/* Store the current floating-point status register */
336	__asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
337	    "r" (&u.fpsr));
338
339	omask = u.bits[0] & FE_ALL_EXCEPT;
340	u.bits[0] |= mask;
341
342	/* Load the floating-point status register */
343	__asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr), "m"(u.fpsr));
344
345	return (omask);
346
347}
348
349int
350fedisableexcept(int mask)
351{
352	volatile union u u;
353	unsigned int omask;
354
355	mask &= FE_ALL_EXCEPT;
356
357	/* Store the current floating-point status register */
358	__asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
359	    "r" (&u.fpsr));
360
361	omask = u.bits[0] & FE_ALL_EXCEPT;
362	u.bits[0] &= ~mask;
363
364	/* Load the floating-point status register */
365	__asm__ volatile ("fldd 0(%0), %%fr0" : : "r" (&u.fpsr), "m"(u.fpsr));
366
367	return (omask);
368}
369
370int
371fegetexcept(void)
372{
373	volatile union u u;
374
375	/* Store the current floating-point status register */
376	__asm__ volatile ("fstd %%fr0, 0(%1)" : "=m" (u.fpsr) :
377	    "r" (&u.fpsr));
378
379	return (u.bits[0] & FE_ALL_EXCEPT);
380}
381