1/*	$NetBSD: fenv.c,v 1.4 2021/09/03 21:54:59 andvar Exp $	*/
2
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 */
26#include <sys/cdefs.h>
27__RCSID("$NetBSD: fenv.c,v 1.4 2021/09/03 21:54:59 andvar Exp $");
28
29#include "namespace.h"
30
31#include <assert.h>
32#include <fenv.h>
33
34#ifdef __weak_alias
35__weak_alias(feclearexcept,_feclearexcept)
36__weak_alias(fedisableexcept,_fedisableexcept)
37__weak_alias(feenableexcept,_feenableexcept)
38__weak_alias(fegetenv,_fegetenv)
39__weak_alias(fegetexcept,_fegetexcept)
40__weak_alias(fegetexceptflag,_fegetexceptflag)
41__weak_alias(fegetround,_fegetround)
42__weak_alias(feholdexcept,_feholdexcept)
43__weak_alias(feraiseexcept,_feraiseexcept)
44__weak_alias(fesetenv,_fesetenv)
45__weak_alias(fesetexceptflag,_fesetexceptflag)
46__weak_alias(fesetround,_fesetround)
47__weak_alias(fetestexcept,_fetestexcept)
48__weak_alias(feupdateenv,_feupdateenv)
49#endif
50
51#ifdef __arch64__
52
53/* Load floating-point state register (all 64bits) */
54#define	__ldxfsr(__r)	__asm__	__volatile__		\
55	("ldx %0, %%fsr" : : "m" (__r))
56
57/* Save floating-point state register (all 64bits) */
58#define	__stxfsr(__r)	__asm__	__volatile__		\
59	("stx %%fsr, %0" : "=m" (*(__r)))
60
61#else /* !__arch64__ */
62
63/* Load floating-point state register (32bits) */
64#define	__ldxfsr(__r)	__asm__	__volatile__		\
65	("ld %0, %%fsr" : : "m" (__r))
66
67/* Save floating-point state register (32bits) */
68#define	__stxfsr(__r)	__asm__	__volatile__		\
69	("st %%fsr, %0" : "=m" (*(__r)))
70
71#endif /* __arch64__ */
72
73/*
74 * The feclearexcept() function clears the supported floating-point exceptions
75 * represented by `excepts'.
76 */
77int
78feclearexcept(int excepts)
79{
80	fexcept_t r;
81	int ex;
82
83	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
84
85	ex = excepts & FE_ALL_EXCEPT;
86
87	__stxfsr(&r);
88	r &= ~ex;
89	__ldxfsr(r);
90
91	/* Success */
92	return 0;
93}
94
95/*
96 * The fegetexceptflag() function stores an implementation-defined
97 * representation of the states of the floating-point status flags indicated
98 * by the argument excepts in the object pointed to by the argument flagp.
99 */
100int
101fegetexceptflag(fexcept_t *flagp, int excepts)
102{
103	fexcept_t r;
104	int ex;
105
106	_DIAGASSERT(flagp != NULL);
107	_DIAGASSERT((excepts & ~_FE_ALL_EXCEPT) == 0);
108
109	ex = excepts & FE_ALL_EXCEPT;
110
111	__stxfsr(&r);
112	*flagp = r & ex;
113
114	/* Success */
115	return 0;
116}
117
118
119/*
120 * This function sets the floating-point status flags indicated by the argument
121 * `excepts' to the states stored in the object pointed to by `flagp'. It does
122 * NOT raise any floating-point exceptions, but only sets the state of the flags.
123 */
124int
125fesetexceptflag(const fexcept_t *flagp, int excepts)
126{
127	fexcept_t r;
128	int ex;
129
130	_DIAGASSERT(flagp != NULL);
131	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
132
133	ex = excepts & FE_ALL_EXCEPT;
134
135	__stxfsr(&r);
136	r &= ~ex;
137	r |= *flagp & ex;
138	__ldxfsr(r);
139
140	/* Success */
141	return 0;
142}
143
144/*
145 * The feraiseexcept() function raises the supported floating-point exceptions
146 * represented by the argument `excepts'.
147 *
148 * The order in which these floating-point exceptions are raised is unspecified
149 * (by the standard).
150 */
151int
152feraiseexcept(int excepts)
153{
154	volatile double d;
155	int ex;
156
157	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
158
159	ex = excepts & FE_ALL_EXCEPT;
160
161	/*
162	 * With a compiler that supports the FENV_ACCESS pragma properly, simple
163	 * expressions like '0.0 / 0.0' should be sufficient to generate traps.
164	 * Unfortunately, we need to bring a volatile variable into the equation
165	 * to prevent incorrect optimizations.
166	 */
167	if (ex & FE_INVALID) {
168		d = 0.0;
169		d = 0.0 / d;
170	}
171	if (ex & FE_DIVBYZERO) {
172		d = 0.0;
173		d = 1.0 / d;
174	}
175	if (ex & FE_OVERFLOW) {
176		d = 0x1.ffp1023;
177		d *= 2.0;
178	}
179	if (ex & FE_UNDERFLOW) {
180		d = 0x1p-1022;
181		d /= 0x1p1023;
182	}
183	if (ex & FE_INEXACT) {
184		d = 0x1p-1022;
185		d += 1.0;
186	}
187
188	/* Success */
189	return 0;
190}
191
192/*
193 * The fetestexcept() function determines which of a specified subset of the
194 * floating-point exception flags are currently set. The `excepts' argument
195 * specifies the floating-point status flags to be queried.
196 */
197int
198fetestexcept(int excepts)
199{
200	fexcept_t r;
201
202	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
203
204	__stxfsr(&r);
205
206	return r & (excepts & FE_ALL_EXCEPT);
207}
208
209/*
210 * The fegetround() function gets the current rounding direction.
211 */
212int
213fegetround(void)
214{
215	fenv_t r;
216
217	__stxfsr(&r);
218
219	return (r >> _ROUND_SHIFT) & _ROUND_MASK;
220}
221
222/*
223 * The fesetround() function establishes the rounding direction represented by
224 * its argument `round'. If the argument is not equal to the value of a rounding
225 * direction macro, the rounding direction is not changed.
226 */
227int
228fesetround(int round)
229{
230	fenv_t r;
231
232	_DIAGASSERT((round & ~_ROUND_MASK) == 0);
233	if (round & ~_ROUND_MASK)
234		return -1;
235
236	__stxfsr(&r);
237	r &= ~(_ROUND_MASK << _ROUND_SHIFT);
238	r |= round << _ROUND_SHIFT;
239	__ldxfsr(r);
240
241	/* Success */
242	return 0;
243}
244
245/*
246 * The fegetenv() function attempts to store the current floating-point
247 * environment in the object pointed to by envp.
248 */
249int
250fegetenv(fenv_t *envp)
251{
252	_DIAGASSERT(envp != NULL);
253
254	__stxfsr(envp);
255
256	/* Success */
257	return 0;
258}
259
260
261/*
262 * The feholdexcept() function saves the current floating-point environment
263 * in the object pointed to by envp, clears the floating-point status flags, and
264 * then installs a non-stop (continue on floating-point exceptions) mode, if
265 * available, for all floating-point exceptions.
266 */
267int
268feholdexcept(fenv_t *envp)
269{
270	fenv_t r;
271
272	_DIAGASSERT(envp != NULL);
273
274	__stxfsr(&r);
275	*envp = r;
276	r &= ~(FE_ALL_EXCEPT | _ENABLE_MASK);
277	__ldxfsr(r);
278
279	/* Success */
280	return 0;
281}
282
283/*
284 * The fesetenv() function attempts to establish the floating-point environment
285 * represented by the object pointed to by envp. The argument `envp' points
286 * to an object set by a call to fegetenv() or feholdexcept(), or equal a
287 * floating-point environment macro. The fesetenv() function does not raise
288 * floating-point exceptions, but only installs the state of the floating-point
289 * status flags represented through its argument.
290 */
291int
292fesetenv(const fenv_t *envp)
293{
294	_DIAGASSERT(envp != NULL);
295
296	__ldxfsr(*envp);
297
298	/* Success */
299	return 0;
300}
301
302
303/*
304 * The feupdateenv() function saves the currently raised floating-point
305 * exceptions in its automatic storage, installs the floating-point environment
306 * represented by the object pointed to by `envp', and then raises the saved
307 * floating-point exceptions. The argument `envp' shall point to an object set
308 * by a call to feholdexcept() or fegetenv(), or equal a floating-point
309 * environment macro.
310 */
311int
312feupdateenv(const fenv_t *envp)
313{
314	fexcept_t r;
315
316	_DIAGASSERT(envp != NULL);
317
318	__stxfsr(&r);
319	__ldxfsr(*envp);
320
321	_DIAGASSERT((r & ~FE_ALL_EXCEPT) == 0);
322	feraiseexcept(r & FE_ALL_EXCEPT);
323
324	/* Success */
325	return 0;
326}
327
328/*
329 * The following functions are extensions to the standard
330 */
331int
332feenableexcept(int mask)
333{
334	fenv_t old_r, new_r;
335
336	__stxfsr(&old_r);
337	new_r = old_r | ((mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT);
338	__ldxfsr(new_r);
339
340	return (old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT;
341}
342
343int
344fedisableexcept(int mask)
345{
346	fenv_t old_r, new_r;
347
348	__stxfsr(&old_r);
349	new_r = old_r & ~((mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT);
350	__ldxfsr(new_r);
351
352	return (old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT;
353}
354
355int
356fegetexcept(void)
357{
358	fenv_t r;
359
360	__stxfsr(&r);
361	return (r & _ENABLE_MASK) >> _FPUSW_SHIFT;
362}
363