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