xref: /openbsd/lib/libm/arch/alpha/fenv.c (revision 5dea098c)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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