1 /* $OpenBSD: fenv.c,v 1.6 2022/12/27 17:10:07 jmc 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 extern unsigned int __fpscr_values[2];
22
23 /*
24 * The following constant represents the default floating-point environment
25 * (that is, the one installed at program startup) and has type pointer to
26 * const-qualified fenv_t.
27 *
28 * It can be used as an argument to the functions within the <fenv.h> header
29 * that manage the floating-point environment, namely fesetenv() and
30 * feupdateenv().
31 */
32 fenv_t __fe_dfl_env = 0xc0000;
33
34 /*
35 * The feclearexcept() function clears the supported floating-point exceptions
36 * represented by `excepts'.
37 */
38 int
feclearexcept(int excepts)39 feclearexcept(int excepts)
40 {
41 unsigned int fpscr;
42
43 excepts &= FE_ALL_EXCEPT;
44
45 /* Store the current floating-point status and control register */
46 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
47
48 /* Clear the requested floating-point exceptions */
49 fpscr &= ~excepts;
50 __fpscr_values[0] &= ~excepts;
51 __fpscr_values[1] &= ~excepts;
52
53 /* Load the floating-point status and control register */
54 __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
55
56 return (0);
57 }
58 DEF_STD(feclearexcept);
59
60 /*
61 * The fegetexceptflag() function stores an implementation-defined
62 * representation of the states of the floating-point status flags indicated by
63 * the argument excepts in the object pointed to by the argument flagp.
64 */
65 int
fegetexceptflag(fexcept_t * flagp,int excepts)66 fegetexceptflag(fexcept_t *flagp, int excepts)
67 {
68 unsigned int fpscr;
69
70 excepts &= FE_ALL_EXCEPT;
71
72 /* Store the current floating-point status and control register */
73 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
74
75 /* Store the results in flagp */
76 *flagp = fpscr & excepts;
77
78 return (0);
79 }
80
81 /*
82 * The feraiseexcept() function raises the supported floating-point exceptions
83 * represented by the argument `excepts'.
84 */
85 int
feraiseexcept(int excepts)86 feraiseexcept(int excepts)
87 {
88 volatile double d;
89
90 excepts &= FE_ALL_EXCEPT;
91
92 /*
93 * With a compiler that supports the FENV_ACCESS pragma
94 * properly, simple expressions like '0.0 / 0.0' should
95 * be sufficient to generate traps. Unfortunately, we
96 * need to bring a volatile variable into the equation
97 * to prevent incorrect optimizations.
98 */
99 if (excepts & FE_INVALID) {
100 d = 0.0;
101 d = 0.0 / d;
102 }
103 if (excepts & FE_DIVBYZERO) {
104 d = 0.0;
105 d = 1.0 / d;
106 }
107 if (excepts & FE_OVERFLOW) {
108 d = 0x1.ffp1023;
109 d *= 2.0;
110 }
111 if (excepts & FE_UNDERFLOW) {
112 d = 0x1p-1022;
113 d /= 0x1p1023;
114 }
115 if (excepts & FE_INEXACT) {
116 d = 0x1p-1022;
117 d += 1.0;
118 }
119
120 return (0);
121 }
122 DEF_STD(feraiseexcept);
123
124 /*
125 * This function sets the floating-point status flags indicated by the argument
126 * `excepts' to the states stored in the object pointed to by `flagp'. It does
127 * NOT raise any floating-point exceptions, but only sets the state of the flags.
128 */
129 int
fesetexceptflag(const fexcept_t * flagp,int excepts)130 fesetexceptflag(const fexcept_t *flagp, int excepts)
131 {
132 unsigned int fpscr;
133
134 excepts &= FE_ALL_EXCEPT;
135
136 /* Store the current floating-point status and control register */
137 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
138
139 /* Set the requested status flags */
140 fpscr &= ~excepts;
141 __fpscr_values[0] &= ~excepts;
142 __fpscr_values[1] &= ~excepts;
143
144 fpscr |= *flagp & excepts;
145 __fpscr_values[0] |= *flagp & excepts;
146 __fpscr_values[1] |= *flagp & excepts;
147
148 /* Load the floating-point status and control register */
149 __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
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 unsigned int fpscr;
164
165 excepts &= FE_ALL_EXCEPT;
166
167 /* Store the current floating-point status and control register */
168 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
169
170 return (fpscr & excepts);
171 }
172 DEF_STD(fetestexcept);
173
174 /*
175 * The fegetround() function gets the current rounding direction.
176 */
177 int
fegetround(void)178 fegetround(void)
179 {
180 unsigned int fpscr;
181
182 /* Store the current floating-point status and control register */
183 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
184
185 return (fpscr & _ROUND_MASK);
186 }
187 DEF_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 */
194 int
fesetround(int round)195 fesetround(int round)
196 {
197 unsigned int fpscr;
198
199 /* Check whether requested rounding direction is supported */
200 if (round & ~_ROUND_MASK)
201 return (-1);
202
203 /* Store the current floating-point status and control register */
204 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
205
206 /* Set the rounding direction */
207 fpscr &= ~_ROUND_MASK;
208 __fpscr_values[0] &= ~_ROUND_MASK;
209 __fpscr_values[1] &= ~_ROUND_MASK;
210
211 fpscr |= round;
212 __fpscr_values[0] |= round;
213 __fpscr_values[1] |= round;
214
215 /* Load the floating-point status and control register */
216 __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
217
218 return (0);
219 }
220 DEF_STD(fesetround);
221
222 /*
223 * The fegetenv() function attempts to store the current floating-point
224 * environment in the object pointed to by envp.
225 */
226 int
fegetenv(fenv_t * envp)227 fegetenv(fenv_t *envp)
228 {
229 /* Store the current floating-point status and control register */
230 __asm__ volatile ("sts fpscr, %0" : "=r" (*envp));
231
232 return (0);
233 }
234 DEF_STD(fegetenv);
235
236 /*
237 * The feholdexcept() function saves the current floating-point environment
238 * in the object pointed to by envp, clears the floating-point status flags, and
239 * then installs a non-stop (continue on floating-point exceptions) mode, if
240 * available, for all floating-point exceptions.
241 */
242 int
feholdexcept(fenv_t * envp)243 feholdexcept(fenv_t *envp)
244 {
245 unsigned int fpscr;
246
247 /* Store the current floating-point status and control register */
248 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
249
250 *envp = fpscr;
251
252 /* Clear exception flags in FPSCR */
253 fpscr &= ~FE_ALL_EXCEPT;
254 __fpscr_values[0] &= ~FE_ALL_EXCEPT;
255 __fpscr_values[1] &= ~FE_ALL_EXCEPT;
256
257 /* Mask all exceptions */
258 fpscr &= ~(FE_ALL_EXCEPT << _MASK_SHIFT);
259 __fpscr_values[0] &= ~(FE_ALL_EXCEPT << _MASK_SHIFT);
260 __fpscr_values[1] &= ~(FE_ALL_EXCEPT << _MASK_SHIFT);
261
262 /* Load the floating-point status and control register */
263 __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
264
265 return (0);
266 }
267 DEF_STD(feholdexcept);
268
269 /*
270 * The fesetenv() function attempts to establish the floating-point environment
271 * represented by the object pointed to by envp. The argument `envp' points
272 * to an object set by a call to fegetenv() or feholdexcept(), or equal a
273 * floating-point environment macro. The fesetenv() function does not raise
274 * floating-point exceptions, but only installs the state of the floating-point
275 * status flags represented through its argument.
276 */
277 int
fesetenv(const fenv_t * envp)278 fesetenv(const fenv_t *envp)
279 {
280 unsigned int fpscr;
281
282 /* Store the current floating-point status and control register */
283 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
284
285 /* Set the requested flags */
286 fpscr &= ~((FE_ALL_EXCEPT << _MASK_SHIFT) | _ROUND_MASK |
287 FE_ALL_EXCEPT);
288 __fpscr_values[0] &= ~((FE_ALL_EXCEPT << _MASK_SHIFT) | _ROUND_MASK |
289 FE_ALL_EXCEPT);
290 __fpscr_values[1] &= ~((FE_ALL_EXCEPT << _MASK_SHIFT) | _ROUND_MASK |
291 FE_ALL_EXCEPT);
292
293 fpscr |= *envp & ((FE_ALL_EXCEPT << _MASK_SHIFT) | _ROUND_MASK |
294 FE_ALL_EXCEPT);
295 __fpscr_values[0] |= *envp & ((FE_ALL_EXCEPT << _MASK_SHIFT) |
296 _ROUND_MASK | FE_ALL_EXCEPT);
297 __fpscr_values[1] |= *envp & ((FE_ALL_EXCEPT << _MASK_SHIFT) |
298 _ROUND_MASK | FE_ALL_EXCEPT);
299
300 /* Load the floating-point status and control register */
301 __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
302
303 return (0);
304 }
305 DEF_STD(fesetenv);
306
307 /*
308 * The feupdateenv() function saves the currently raised floating-point
309 * exceptions in its automatic storage, installs the floating-point environment
310 * represented by the object pointed to by `envp', and then raises the saved
311 * floating-point exceptions. The argument `envp' shall point to an object set
312 * by a call to feholdexcept() or fegetenv(), or equal a floating-point
313 * environment macro.
314 */
315 int
feupdateenv(const fenv_t * envp)316 feupdateenv(const fenv_t *envp)
317 {
318 unsigned int fpscr;
319
320 /* Store the current floating-point status and control register */
321 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
322
323 /* Install new floating-point environment */
324 fesetenv(envp);
325
326 /* Raise any previously accumulated exceptions */
327 feraiseexcept(fpscr);
328
329 return (0);
330 }
331 DEF_STD(feupdateenv);
332
333 /*
334 * The following functions are extensions to the standard
335 */
336 int
feenableexcept(int mask)337 feenableexcept(int mask)
338 {
339 unsigned int fpscr, omask;
340
341 mask &= FE_ALL_EXCEPT;
342
343 /* Store the current floating-point status and control register */
344 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
345
346 omask = (fpscr >> _MASK_SHIFT) & FE_ALL_EXCEPT;
347 fpscr |= mask << _MASK_SHIFT;
348 __fpscr_values[0] |= mask << _MASK_SHIFT;
349 __fpscr_values[1] |= mask << _MASK_SHIFT;
350
351 /* Load the floating-point status and control register */
352 __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
353
354 return (omask);
355
356 }
357
358 int
fedisableexcept(int mask)359 fedisableexcept(int mask)
360 {
361 unsigned int fpscr, omask;
362
363 mask &= FE_ALL_EXCEPT;
364
365 /* Store the current floating-point status and control register */
366 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
367
368 omask = (fpscr >> _MASK_SHIFT) & FE_ALL_EXCEPT;
369 fpscr &= ~(mask << _MASK_SHIFT);
370 __fpscr_values[0] &= ~(mask << _MASK_SHIFT);
371 __fpscr_values[1] &= ~(mask << _MASK_SHIFT);
372
373 /* Load the floating-point status and control register */
374 __asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
375
376 return (omask);
377 }
378
379 int
fegetexcept(void)380 fegetexcept(void)
381 {
382 unsigned int fpscr;
383
384 /* Store the current floating-point status and control register */
385 __asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
386
387 return ((fpscr >> _MASK_SHIFT) & FE_ALL_EXCEPT);
388 }
389