1/*	$OpenBSD: fenv.c,v 1.7 2022/12/27 17:10:07 jmc Exp $	*/
2/*	$NetBSD: fenv.c,v 1.1 2011/01/31 00:19:33 christos Exp $	*/
3
4/*-
5 * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 */
27
28#include <fenv.h>
29
30/*
31 * The following constant represents the default floating-point environment
32 * (that is, the one installed at program startup) and has type pointer to
33 * const-qualified fenv_t.
34 *
35 * It can be used as an argument to the functions within the <fenv.h> header
36 * that manage the floating-point environment, namely fesetenv() and
37 * feupdateenv().
38 */
39fenv_t __fe_dfl_env = 0;
40
41/*
42 * The feclearexcept() function clears the supported floating-point exceptions
43 * represented by `excepts'.
44 */
45int
46feclearexcept(int excepts)
47{
48	fexcept_t r;
49
50	excepts &= FE_ALL_EXCEPT;
51
52	/* Save floating-point state register */
53	__asm__ volatile ("stx %%fsr, %0" : "=m" (r));
54
55	r &= ~excepts;
56
57	/* Load floating-point state register */
58	__asm__ volatile ("ldx %0, %%fsr" : : "m" (r));
59
60	return 0;
61}
62DEF_STD(feclearexcept);
63
64/*
65 * The fegetexceptflag() function stores an implementation-defined
66 * representation of the states of the floating-point status flags indicated
67 * by the argument excepts in the object pointed to by the argument flagp.
68 */
69int
70fegetexceptflag(fexcept_t *flagp, int excepts)
71{
72	fexcept_t r;
73
74	excepts &= FE_ALL_EXCEPT;
75
76	/* Save floating-point state register */
77	__asm__ volatile ("stx %%fsr, %0" : "=m" (r));
78
79	*flagp = r & excepts;
80
81	return 0;
82}
83
84
85/*
86 * This function sets the floating-point status flags indicated by the argument
87 * `excepts' to the states stored in the object pointed to by `flagp'. It does
88 * NOT raise any floating-point exceptions, but only sets the state of the flags.
89 */
90int
91fesetexceptflag(const fexcept_t *flagp, int excepts)
92{
93	fexcept_t r;
94
95	excepts &= FE_ALL_EXCEPT;
96
97	/* Save floating-point state register */
98	__asm__ volatile ("stx %%fsr, %0" : "=m" (r));
99
100	r &= ~excepts;
101	r |= *flagp & excepts;
102
103	/* Load floating-point state register */
104	__asm__ volatile ("ldx %0, %%fsr" : : "m" (r));
105
106	return 0;
107}
108DEF_STD(fesetexceptflag);
109
110/*
111 * The feraiseexcept() function raises the supported floating-point exceptions
112 * represented by the argument `excepts'.
113 *
114 * The order in which these floating-point exceptions are raised is unspecified
115 * (by the standard).
116 */
117int
118feraiseexcept(int excepts)
119{
120	volatile double d;
121
122	excepts &= FE_ALL_EXCEPT;
123
124	/*
125	 * With a compiler that supports the FENV_ACCESS pragma properly, simple
126	 * expressions like '0.0 / 0.0' should be sufficient to generate traps.
127	 * Unfortunately, we need to bring a volatile variable into the equation
128	 * to prevent incorrect optimizations.
129	 */
130	if (excepts & FE_INVALID) {
131		d = 0.0;
132		d = 0.0 / d;
133	}
134	if (excepts & FE_DIVBYZERO) {
135		d = 0.0;
136		d = 1.0 / d;
137	}
138	if (excepts & FE_OVERFLOW) {
139		d = 0x1.ffp1023;
140		d *= 2.0;
141	}
142	if (excepts & FE_UNDERFLOW) {
143		d = 0x1p-1022;
144		d /= 0x1p1023;
145	}
146	if (excepts & FE_INEXACT) {
147		d = 0x1p-1022;
148		d += 1.0;
149	}
150
151	return 0;
152}
153DEF_STD(feraiseexcept);
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	fexcept_t r;
164
165	excepts &= FE_ALL_EXCEPT;
166
167	/* Save floating-point state register */
168	__asm__ volatile ("stx %%fsr, %0" : "=m" (r));
169
170	return r & excepts;
171}
172DEF_STD(fetestexcept);
173
174/*
175 * The fegetround() function gets the current rounding direction.
176 */
177int
178fegetround(void)
179{
180	fenv_t r;
181
182	/* Save floating-point state register */
183	__asm__ volatile ("stx %%fsr, %0" : "=m" (r));
184
185	return (r >> _ROUND_SHIFT) & _ROUND_MASK;
186}
187DEF_STD(fegetround);
188
189/*
190 * The fesetround() function establishes the rounding direction represented by
191 * its argument `round'. If the argument is not equal to the value of a rounding
192 * direction macro, the rounding direction is not changed.
193 */
194int
195fesetround(int round)
196{
197	fenv_t r;
198
199	if (round & ~_ROUND_MASK)
200		return -1;
201
202	/* Save floating-point state register */
203	__asm__ volatile ("stx %%fsr, %0" : "=m" (r));
204
205	r &= ~(_ROUND_MASK << _ROUND_SHIFT);
206	r |= round << _ROUND_SHIFT;
207
208	/* Load floating-point state register */
209	__asm__ volatile ("ldx %0, %%fsr" : : "m" (r));
210
211	return 0;
212}
213DEF_STD(fesetround);
214
215/*
216 * The fegetenv() function attempts to store the current floating-point
217 * environment in the object pointed to by envp.
218 */
219int
220fegetenv(fenv_t *envp)
221{
222	/* Save floating-point state register */
223	__asm__ volatile ("stx %%fsr, %0" : "=m" (*envp));
224
225	return 0;
226}
227DEF_STD(fegetenv);
228
229
230/*
231 * The feholdexcept() function saves the current floating-point environment
232 * in the object pointed to by envp, clears the floating-point status flags, and
233 * then installs a non-stop (continue on floating-point exceptions) mode, if
234 * available, for all floating-point exceptions.
235 */
236int
237feholdexcept(fenv_t *envp)
238{
239	fenv_t r;
240
241	/* Save floating-point state register */
242	__asm__ volatile ("stx %%fsr, %0" : "=m" (r));
243
244	*envp = r;
245	r &= ~(FE_ALL_EXCEPT | (FE_ALL_EXCEPT << _MASK_SHIFT));
246
247	/* Load floating-point state register */
248	__asm__ volatile ("ldx %0, %%fsr" : : "m" (r));
249
250	return 0;
251}
252DEF_STD(feholdexcept);
253
254/*
255 * The fesetenv() function attempts to establish the floating-point environment
256 * represented by the object pointed to by envp. The argument `envp' points
257 * to an object set by a call to fegetenv() or feholdexcept(), or equal a
258 * floating-point environment macro. The fesetenv() function does not raise
259 * floating-point exceptions, but only installs the state of the floating-point
260 * status flags represented through its argument.
261 */
262int
263fesetenv(const fenv_t *envp)
264{
265	/* Load floating-point state register */
266	__asm__ volatile ("ldx %0, %%fsr" : : "m" (*envp));
267
268	return 0;
269}
270DEF_STD(fesetenv);
271
272
273/*
274 * The feupdateenv() function saves the currently raised floating-point
275 * exceptions in its automatic storage, installs the floating-point environment
276 * represented by the object pointed to by `envp', and then raises the saved
277 * floating-point exceptions. The argument `envp' shall point to an object set
278 * by a call to feholdexcept() or fegetenv(), or equal a floating-point
279 * environment macro.
280 */
281int
282feupdateenv(const fenv_t *envp)
283{
284	fexcept_t r;
285
286	/* Save floating-point state register */
287	__asm__ volatile ("stx %%fsr, %0" : "=m" (r));
288
289	/* Load floating-point state register */
290	__asm__ volatile ("ldx %0, %%fsr" : : "m" (*envp));
291
292	feraiseexcept(r & FE_ALL_EXCEPT);
293
294	return 0;
295}
296DEF_STD(feupdateenv);
297
298/*
299 * The following functions are extensions to the standard
300 */
301int
302feenableexcept(int mask)
303{
304	fenv_t old_r, new_r;
305
306	mask &= FE_ALL_EXCEPT;
307
308	/* Save floating-point state register */
309	__asm__ volatile ("stx %%fsr, %0" : "=m" (old_r));
310
311	new_r = old_r | (mask << _MASK_SHIFT);
312
313	/* Load floating-point state register */
314	__asm__ volatile ("ldx %0, %%fsr" : : "m" (new_r));
315
316	return (old_r >> _MASK_SHIFT) & FE_ALL_EXCEPT;
317}
318
319int
320fedisableexcept(int mask)
321{
322	fenv_t old_r, new_r;
323
324	mask &= FE_ALL_EXCEPT;
325
326	/* Save floating-point state register */
327	__asm__ volatile ("stx %%fsr, %0" : "=m" (old_r));
328
329	new_r = old_r & ~(mask << _MASK_SHIFT);
330
331	/* Load floating-point state register */
332	__asm__ volatile ("ldx %0, %%fsr" : : "m" (new_r));
333
334	return (old_r >> _MASK_SHIFT) & FE_ALL_EXCEPT;
335}
336
337int
338fegetexcept(void)
339{
340	fenv_t r;
341
342	/* Save floating-point state register */
343	__asm__ volatile ("stx %%fsr, %0" : "=m" (r));
344
345	return (r & (FE_ALL_EXCEPT << _MASK_SHIFT)) >> _MASK_SHIFT;
346}
347