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