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 union u {
22 unsigned long long fpscr;
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 union u u;
45 excepts &= FE_ALL_EXCEPT;
46
47 /* Store the current floating-point status and control register */
48 __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
49
50 /* Clear the requested floating-point exceptions */
51 u.bits[1] &= ~excepts;
52 if (excepts & FE_INVALID)
53 u.bits[1] &= ~_FE_INVALID_ALL;
54
55 /* Load the floating-point status and control register */
56 __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
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 union u u;
71
72 excepts &= FE_ALL_EXCEPT;
73
74 /* Store the current floating-point status and control register */
75 __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
76
77 /* Store the results in flagp */
78 *flagp = u.bits[1] & excepts;
79
80 return (0);
81 }
82
83 /*
84 * The feraiseexcept() function raises the supported floating-point exceptions
85 * represented by the argument `excepts'.
86 */
87 int
feraiseexcept(int excepts)88 feraiseexcept(int excepts)
89 {
90 excepts &= FE_ALL_EXCEPT;
91
92 fesetexceptflag((fexcept_t *)&excepts, excepts);
93
94 return (0);
95 }
96 DEF_STD(feraiseexcept);
97
98 /*
99 * This function sets the floating-point status flags indicated by the argument
100 * `excepts' to the states stored in the object pointed to by `flagp'. It does
101 * NOT raise any floating-point exceptions, but only sets the state of the flags.
102 */
103 int
fesetexceptflag(const fexcept_t * flagp,int excepts)104 fesetexceptflag(const fexcept_t *flagp, int excepts)
105 {
106 union u u;
107
108 excepts &= FE_ALL_EXCEPT;
109
110 /* Store the current floating-point status and control register */
111 __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
112
113 /* Set the requested status flags */
114 u.bits[1] &= ~excepts;
115 u.bits[1] |= *flagp & excepts;
116 if (excepts & FE_INVALID) {
117 if (*flagp & FE_INVALID)
118 u.bits[1] |= _FE_INVALID_SOFT;
119 else
120 u.bits[1] &= ~_FE_INVALID_ALL;
121 }
122
123 /* Load the floating-point status and control register */
124 __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
125
126 return (0);
127 }
128 DEF_STD(fesetexceptflag);
129
130 /*
131 * The fetestexcept() function determines which of a specified subset of the
132 * floating-point exception flags are currently set. The `excepts' argument
133 * specifies the floating-point status flags to be queried.
134 */
135 int
fetestexcept(int excepts)136 fetestexcept(int excepts)
137 {
138 union u u;
139
140 excepts &= FE_ALL_EXCEPT;
141
142 /* Store the current floating-point status and control register */
143 __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
144
145 return (u.bits[1] & excepts);
146 }
147 DEF_STD(fetestexcept);
148
149 /*
150 * The fegetround() function gets the current rounding direction.
151 */
152 int
fegetround(void)153 fegetround(void)
154 {
155 union u u;
156
157 /* Store the current floating-point status and control register */
158 __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
159
160 return (u.bits[1] & _ROUND_MASK);
161 }
162 DEF_STD(fegetround);
163
164 /*
165 * The fesetround() function establishes the rounding direction represented by
166 * its argument `round'. If the argument is not equal to the value of a rounding
167 * direction macro, the rounding direction is not changed.
168 */
169 int
fesetround(int round)170 fesetround(int round)
171 {
172 union u u;
173
174 /* Check whether requested rounding direction is supported */
175 if (round & ~_ROUND_MASK)
176 return (-1);
177
178 /* Store the current floating-point status and control register */
179 __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
180
181 /* Set the rounding direction */
182 u.bits[1] &= ~_ROUND_MASK;
183 u.bits[1] |= round;
184
185 /* Load the floating-point status and control register */
186 __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
187
188 return (0);
189 }
190 DEF_STD(fesetround);
191
192 /*
193 * The fegetenv() function attempts to store the current floating-point
194 * environment in the object pointed to by envp.
195 */
196 int
fegetenv(fenv_t * envp)197 fegetenv(fenv_t *envp)
198 {
199 union u u;
200
201 /* Store the current floating-point status and control register */
202 __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
203
204 *envp = u.bits[1];
205
206 return (0);
207 }
208 DEF_STD(fegetenv);
209
210 /*
211 * The feholdexcept() function saves the current floating-point environment
212 * in the object pointed to by envp, clears the floating-point status flags, and
213 * then installs a non-stop (continue on floating-point exceptions) mode, if
214 * available, for all floating-point exceptions.
215 */
216 int
feholdexcept(fenv_t * envp)217 feholdexcept(fenv_t *envp)
218 {
219 union u u;
220
221 /* Store the current floating-point status and control register */
222 __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
223
224 *envp = u.bits[1];
225
226 /* Clear exception flags in FPSCR */
227 u.bits[1] &= ~(FE_ALL_EXCEPT | _FE_INVALID_ALL);
228
229 /* Mask all exceptions */
230 u.bits[1] &= ~(FE_ALL_EXCEPT >> _MASK_SHIFT);
231 __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
232
233 return (0);
234 }
235 DEF_STD(feholdexcept);
236
237 /*
238 * The fesetenv() function attempts to establish the floating-point environment
239 * represented by the object pointed to by envp. The argument `envp' points
240 * to an object set by a call to fegetenv() or feholdexcept(), or equal a
241 * floating-point environment macro. The fesetenv() function does not raise
242 * floating-point exceptions, but only installs the state of the floating-point
243 * status flags represented through its argument.
244 */
245 int
fesetenv(const fenv_t * envp)246 fesetenv(const fenv_t *envp)
247 {
248 union u u;
249
250 u.bits[0] = 0;
251 u.bits[1] = *envp;
252
253 /* Load the floating-point status and control register */
254 __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
255
256 return (0);
257 }
258 DEF_STD(fesetenv);
259
260 /*
261 * The feupdateenv() function saves the currently raised floating-point
262 * exceptions in its automatic storage, installs the floating-point environment
263 * represented by the object pointed to by `envp', and then raises the saved
264 * floating-point exceptions. The argument `envp' shall point to an object set
265 * by a call to feholdexcept() or fegetenv(), or equal a floating-point
266 * environment macro.
267 */
268 int
feupdateenv(const fenv_t * envp)269 feupdateenv(const fenv_t *envp)
270 {
271 union u u;
272
273 /* Store the current floating-point status and control register */
274 __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
275
276 /* Install new floating-point environment */
277 fesetenv(envp);
278
279 /* Raise any previously accumulated exceptions */
280 feraiseexcept(u.bits[1]);
281
282 return (0);
283 }
284 DEF_STD(feupdateenv);
285
286 /*
287 * The following functions are extensions to the standard
288 */
289 int
feenableexcept(int mask)290 feenableexcept(int mask)
291 {
292 union u u;
293 unsigned int omask;
294
295 mask &= FE_ALL_EXCEPT;
296
297 /* Store the current floating-point status and control register */
298 __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
299
300 omask = (u.bits[1] << _MASK_SHIFT) & FE_ALL_EXCEPT;
301 u.bits[1] |= mask >> _MASK_SHIFT;
302
303 /* Load the floating-point status and control register */
304 __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
305
306 return (omask);
307
308 }
309
310 int
fedisableexcept(int mask)311 fedisableexcept(int mask)
312 {
313 union u u;
314 unsigned int omask;
315
316 mask &= FE_ALL_EXCEPT;
317
318 /* Store the current floating-point status and control register */
319 __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
320
321 omask = (u.bits[1] << _MASK_SHIFT) & FE_ALL_EXCEPT;
322 u.bits[1] &= ~(mask >> _MASK_SHIFT);
323
324 /* Load the floating-point status and control register */
325 __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
326
327 return (omask);
328 }
329
330 int
fegetexcept(void)331 fegetexcept(void)
332 {
333 union u u;
334
335 /* Store the current floating-point status and control register */
336 __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
337
338 return ((u.bits[1] << _MASK_SHIFT) & FE_ALL_EXCEPT);
339 }
340