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
21 union 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 */
35 fenv_t __fe_dfl_env = 0;
36
37 /*
38 * The feclearexcept() function clears the supported floating-point exceptions
39 * represented by `excepts'.
40 */
41 int
feclearexcept(int excepts)42 feclearexcept(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 }
60 DEF_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 */
67 int
fegetexceptflag(fexcept_t * flagp,int excepts)68 fegetexceptflag(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 */
88 int
feraiseexcept(int excepts)89 feraiseexcept(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 }
126 DEF_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 */
133 int
fesetexceptflag(const fexcept_t * flagp,int excepts)134 fesetexceptflag(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 }
153 DEF_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 */
160 int
fetestexcept(int excepts)161 fetestexcept(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 }
173 DEF_STD(fetestexcept);
174
175 /*
176 * The fegetround() function gets the current rounding direction.
177 */
178 int
fegetround(void)179 fegetround(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 }
189 DEF_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 */
196 int
fesetround(int round)197 fesetround(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 }
218 DEF_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 */
224 int
fegetenv(fenv_t * envp)225 fegetenv(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 }
237 DEF_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 */
245 int
feholdexcept(fenv_t * envp)246 feholdexcept(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 }
265 DEF_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 */
275 int
fesetenv(const fenv_t * envp)276 fesetenv(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 }
295 DEF_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 */
305 int
feupdateenv(const fenv_t * envp)306 feupdateenv(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 }
322 DEF_STD(feupdateenv);
323
324 /*
325 * The following functions are extensions to the standard
326 */
327 int
feenableexcept(int mask)328 feenableexcept(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
349 int
fedisableexcept(int mask)350 fedisableexcept(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
370 int
fegetexcept(void)371 fegetexcept(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