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