xref: /openbsd-src/lib/libm/arch/alpha/fenv.c (revision 2c53affbcc0119d6480b86c18f2790523b6a0aad)
1*2c53affbSjmc /*	$OpenBSD: fenv.c,v 1.5 2022/12/27 17:10:07 jmc Exp $	*/
2499accfeSmartynas 
3499accfeSmartynas /*
4499accfeSmartynas  * Copyright (c) 2011 Martynas Venckus <martynas@openbsd.org>
5499accfeSmartynas  *
6499accfeSmartynas  * Permission to use, copy, modify, and distribute this software for any
7499accfeSmartynas  * purpose with or without fee is hereby granted, provided that the above
8499accfeSmartynas  * copyright notice and this permission notice appear in all copies.
9499accfeSmartynas  *
10499accfeSmartynas  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11499accfeSmartynas  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12499accfeSmartynas  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13499accfeSmartynas  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14499accfeSmartynas  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15499accfeSmartynas  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16499accfeSmartynas  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17499accfeSmartynas  */
18499accfeSmartynas 
19499accfeSmartynas #include <sys/types.h>
20499accfeSmartynas #include <machine/sysarch.h>
21499accfeSmartynas 
22499accfeSmartynas #include <fenv.h>
23499accfeSmartynas 
24499accfeSmartynas /*
252f2c0062Sguenther  * Call sysarch(2) via its alias in the reserved namespace:
262f2c0062Sguenther  * _thread_sys_sysarch()
272f2c0062Sguenther  */
282f2c0062Sguenther typeof(sysarch) sysarch asm("_thread_sys_sysarch");
292f2c0062Sguenther 
302f2c0062Sguenther 
312f2c0062Sguenther /*
32499accfeSmartynas  * The following constant represents the default floating-point environment
33499accfeSmartynas  * (that is, the one installed at program startup) and has type pointer to
34499accfeSmartynas  * const-qualified fenv_t.
35499accfeSmartynas  *
36499accfeSmartynas  * It can be used as an argument to the functions within the <fenv.h> header
37499accfeSmartynas  * that manage the floating-point environment, namely fesetenv() and
38499accfeSmartynas  * feupdateenv().
39499accfeSmartynas  */
40499accfeSmartynas fenv_t __fe_dfl_env = {
41499accfeSmartynas 	0,
42499accfeSmartynas 	0,
43499accfeSmartynas 	FE_TONEAREST
44499accfeSmartynas };
45499accfeSmartynas 
46499accfeSmartynas /*
47499accfeSmartynas  * The feclearexcept() function clears the supported floating-point exceptions
48499accfeSmartynas  * represented by `excepts'.
49499accfeSmartynas  */
50499accfeSmartynas int
feclearexcept(int excepts)51499accfeSmartynas feclearexcept(int excepts)
52499accfeSmartynas {
53499accfeSmartynas 	unsigned int fpsticky;
54499accfeSmartynas 	struct alpha_fp_except_args a;
55499accfeSmartynas 
56499accfeSmartynas 	excepts &= FE_ALL_EXCEPT;
57499accfeSmartynas 
58499accfeSmartynas 	/* Store the current floating-point sticky flags */
59499accfeSmartynas 	fpsticky = sysarch(ALPHA_FPGETSTICKY, 0L);
60499accfeSmartynas 
61499accfeSmartynas 	/* Clear the requested floating-point exceptions */
62499accfeSmartynas 	fpsticky &= ~excepts;
63499accfeSmartynas 
64499accfeSmartynas 	/* Load the floating-point sticky flags */
65499accfeSmartynas 	a.mask = fpsticky;
66499accfeSmartynas 	sysarch(ALPHA_FPSETSTICKY, &a);
67499accfeSmartynas 
68499accfeSmartynas 	return (0);
69499accfeSmartynas }
702f2c0062Sguenther DEF_STD(feclearexcept);
71499accfeSmartynas 
72499accfeSmartynas /*
73499accfeSmartynas  * The fegetexceptflag() function stores an implementation-defined
74499accfeSmartynas  * representation of the states of the floating-point status flags indicated by
75499accfeSmartynas  * the argument excepts in the object pointed to by the argument flagp.
76499accfeSmartynas  */
77499accfeSmartynas int
fegetexceptflag(fexcept_t * flagp,int excepts)78499accfeSmartynas fegetexceptflag(fexcept_t *flagp, int excepts)
79499accfeSmartynas {
80499accfeSmartynas 	unsigned int fpsticky;
81499accfeSmartynas 
82499accfeSmartynas 	excepts &= FE_ALL_EXCEPT;
83499accfeSmartynas 
84499accfeSmartynas 	/* Store the current floating-point sticky flags */
85499accfeSmartynas 	fpsticky = sysarch(ALPHA_FPGETSTICKY, 0L);
86499accfeSmartynas 
87499accfeSmartynas 	/* Store the results in flagp */
88499accfeSmartynas 	*flagp = fpsticky & excepts;
89499accfeSmartynas 
90499accfeSmartynas 	return (0);
91499accfeSmartynas }
92499accfeSmartynas 
93499accfeSmartynas /*
94499accfeSmartynas  * The feraiseexcept() function raises the supported floating-point exceptions
95499accfeSmartynas  * represented by the argument `excepts'.
96499accfeSmartynas  */
97499accfeSmartynas int
feraiseexcept(int excepts)98499accfeSmartynas feraiseexcept(int excepts)
99499accfeSmartynas {
100499accfeSmartynas 	excepts &= FE_ALL_EXCEPT;
101499accfeSmartynas 
102499accfeSmartynas 	fesetexceptflag((fexcept_t *)&excepts, excepts);
103499accfeSmartynas 
104499accfeSmartynas 	return (0);
105499accfeSmartynas }
1062f2c0062Sguenther DEF_STD(feraiseexcept);
107499accfeSmartynas 
108499accfeSmartynas /*
109499accfeSmartynas  * This function sets the floating-point status flags indicated by the argument
110499accfeSmartynas  * `excepts' to the states stored in the object pointed to by `flagp'. It does
111499accfeSmartynas  * NOT raise any floating-point exceptions, but only sets the state of the flags.
112499accfeSmartynas  */
113499accfeSmartynas int
fesetexceptflag(const fexcept_t * flagp,int excepts)114499accfeSmartynas fesetexceptflag(const fexcept_t *flagp, int excepts)
115499accfeSmartynas {
116499accfeSmartynas 	unsigned int fpsticky;
117499accfeSmartynas 	struct alpha_fp_except_args a;
118499accfeSmartynas 
119499accfeSmartynas 	excepts &= FE_ALL_EXCEPT;
120499accfeSmartynas 
121499accfeSmartynas 	/* Store the current floating-point sticky flags */
122499accfeSmartynas 	fpsticky = sysarch(ALPHA_FPGETSTICKY, 0L);
123499accfeSmartynas 
124499accfeSmartynas 	/* Set the requested status flags */
125499accfeSmartynas 	fpsticky &= ~excepts;
126499accfeSmartynas 	fpsticky |= *flagp & excepts;
127499accfeSmartynas 
128499accfeSmartynas 	/* Load the floating-point sticky flags */
129499accfeSmartynas 	a.mask = fpsticky;
130499accfeSmartynas 	sysarch(ALPHA_FPSETSTICKY, &a);
131499accfeSmartynas 
132499accfeSmartynas 	return (0);
133499accfeSmartynas }
1342f2c0062Sguenther DEF_STD(fesetexceptflag);
135499accfeSmartynas 
136499accfeSmartynas /*
137499accfeSmartynas  * The fetestexcept() function determines which of a specified subset of the
138499accfeSmartynas  * floating-point exception flags are currently set. The `excepts' argument
139499accfeSmartynas  * specifies the floating-point status flags to be queried.
140499accfeSmartynas  */
141499accfeSmartynas int
fetestexcept(int excepts)142499accfeSmartynas fetestexcept(int excepts)
143499accfeSmartynas {
144499accfeSmartynas 	unsigned int fpsticky;
145499accfeSmartynas 
146499accfeSmartynas 	excepts &= FE_ALL_EXCEPT;
147499accfeSmartynas 
148499accfeSmartynas 	/* Store the current floating-point sticky flags */
149499accfeSmartynas 	fpsticky = sysarch(ALPHA_FPGETSTICKY, 0L);
150499accfeSmartynas 
151499accfeSmartynas 	return (fpsticky & excepts);
152499accfeSmartynas }
1532f2c0062Sguenther DEF_STD(fetestexcept);
154499accfeSmartynas 
155499accfeSmartynas /*
156499accfeSmartynas  * The fegetround() function gets the current rounding direction.
157499accfeSmartynas  */
158499accfeSmartynas int
fegetround(void)159499accfeSmartynas fegetround(void)
160499accfeSmartynas {
161499accfeSmartynas 	unsigned long fpcr;
162499accfeSmartynas 
163499accfeSmartynas 	/* Store the current floating-point control register */
164b5aa3b33Sguenther 	__asm__ volatile ("trapb");
165b5aa3b33Sguenther 	__asm__ volatile ("mf_fpcr %0" : "=f" (fpcr));
166b5aa3b33Sguenther 	__asm__ volatile ("trapb");
167499accfeSmartynas 
168499accfeSmartynas 	return ((fpcr >> _ROUND_SHIFT) & _ROUND_MASK);
169499accfeSmartynas }
1702f2c0062Sguenther DEF_STD(fegetround);
171499accfeSmartynas 
172499accfeSmartynas /*
173499accfeSmartynas  * The fesetround() function establishes the rounding direction represented by
174499accfeSmartynas  * its argument `round'. If the argument is not equal to the value of a rounding
175499accfeSmartynas  * direction macro, the rounding direction is not changed.
176499accfeSmartynas  */
177499accfeSmartynas int
fesetround(int round)178499accfeSmartynas fesetround(int round)
179499accfeSmartynas {
180499accfeSmartynas 	unsigned long fpcr;
181499accfeSmartynas 
182499accfeSmartynas 	/* Check whether requested rounding direction is supported */
183499accfeSmartynas 	if (round & ~_ROUND_MASK)
184499accfeSmartynas 		return (-1);
185499accfeSmartynas 
186499accfeSmartynas 	/* Store the current floating-point control register */
187b5aa3b33Sguenther 	__asm__ volatile ("trapb");
188b5aa3b33Sguenther 	__asm__ volatile ("mf_fpcr %0" : "=f" (fpcr));
189b5aa3b33Sguenther 	__asm__ volatile ("trapb");
190499accfeSmartynas 
191d6f349c8Smartynas 	/* Set the rounding direction */
192499accfeSmartynas 	fpcr &= ~((unsigned long)_ROUND_MASK << _ROUND_SHIFT);
193499accfeSmartynas 	fpcr |= (unsigned long)round << _ROUND_SHIFT;
194499accfeSmartynas 
195499accfeSmartynas 	/* Load the floating-point control register */
196b5aa3b33Sguenther 	__asm__ volatile ("trapb");
197b5aa3b33Sguenther 	__asm__ volatile ("mt_fpcr %0" : : "f" (fpcr));
198b5aa3b33Sguenther 	__asm__ volatile ("trapb");
199499accfeSmartynas 
200499accfeSmartynas 	return (0);
201499accfeSmartynas }
2022f2c0062Sguenther DEF_STD(fesetround);
203499accfeSmartynas 
204499accfeSmartynas /*
205499accfeSmartynas  * The fegetenv() function attempts to store the current floating-point
206499accfeSmartynas  * environment in the object pointed to by envp.
207499accfeSmartynas  */
208499accfeSmartynas int
fegetenv(fenv_t * envp)209499accfeSmartynas fegetenv(fenv_t *envp)
210499accfeSmartynas {
211499accfeSmartynas 	unsigned long fpcr;
212499accfeSmartynas 
213499accfeSmartynas 	/* Store the current floating-point sticky flags */
214d6f349c8Smartynas 	envp->__sticky = sysarch(ALPHA_FPGETSTICKY, 0L) & FE_ALL_EXCEPT;
215499accfeSmartynas 
216499accfeSmartynas 	/* Store the current floating-point masks */
217d6f349c8Smartynas 	envp->__mask = sysarch(ALPHA_FPGETMASK, 0L) & FE_ALL_EXCEPT;
218499accfeSmartynas 
219499accfeSmartynas 	/* Store the current floating-point control register */
220b5aa3b33Sguenther 	__asm__ volatile ("trapb");
221b5aa3b33Sguenther 	__asm__ volatile ("mf_fpcr %0" : "=f" (fpcr));
222b5aa3b33Sguenther 	__asm__ volatile ("trapb");
223499accfeSmartynas 	envp->__round = (fpcr >> _ROUND_SHIFT) & _ROUND_MASK;
224499accfeSmartynas 
225499accfeSmartynas 	return (0);
226499accfeSmartynas }
2272f2c0062Sguenther DEF_STD(fegetenv);
228499accfeSmartynas 
229499accfeSmartynas /*
230499accfeSmartynas  * The feholdexcept() function saves the current floating-point environment
231499accfeSmartynas  * in the object pointed to by envp, clears the floating-point status flags, and
232499accfeSmartynas  * then installs a non-stop (continue on floating-point exceptions) mode, if
233499accfeSmartynas  * available, for all floating-point exceptions.
234499accfeSmartynas  */
235499accfeSmartynas int
feholdexcept(fenv_t * envp)236499accfeSmartynas feholdexcept(fenv_t *envp)
237499accfeSmartynas {
238499accfeSmartynas 	struct alpha_fp_except_args a;
239499accfeSmartynas 
240499accfeSmartynas 	/* Store the current floating-point environment */
241499accfeSmartynas 	fegetenv(envp);
242499accfeSmartynas 
243499accfeSmartynas 	/* Clear exception flags */
244d6f349c8Smartynas 	a.mask = 0;
245499accfeSmartynas 	sysarch(ALPHA_FPSETSTICKY, &a);
246499accfeSmartynas 
247499accfeSmartynas 	/* Mask all exceptions */
248d6f349c8Smartynas 	a.mask = 0;
249499accfeSmartynas 	sysarch(ALPHA_FPSETMASK, &a);
250499accfeSmartynas 
251499accfeSmartynas 	return (0);
252499accfeSmartynas }
2532f2c0062Sguenther DEF_STD(feholdexcept);
254499accfeSmartynas 
255499accfeSmartynas /*
256499accfeSmartynas  * The fesetenv() function attempts to establish the floating-point environment
257499accfeSmartynas  * represented by the object pointed to by envp. The argument `envp' points
258499accfeSmartynas  * to an object set by a call to fegetenv() or feholdexcept(), or equal a
259499accfeSmartynas  * floating-point environment macro. The fesetenv() function does not raise
260499accfeSmartynas  * floating-point exceptions, but only installs the state of the floating-point
261499accfeSmartynas  * status flags represented through its argument.
262499accfeSmartynas  */
263499accfeSmartynas int
fesetenv(const fenv_t * envp)264499accfeSmartynas fesetenv(const fenv_t *envp)
265499accfeSmartynas {
266499accfeSmartynas 	unsigned long fpcr;
267499accfeSmartynas 	struct alpha_fp_except_args a;
268499accfeSmartynas 
269499accfeSmartynas 	/* Load the floating-point sticky flags */
270d6f349c8Smartynas 	a.mask = envp->__sticky & FE_ALL_EXCEPT;
271499accfeSmartynas 	sysarch(ALPHA_FPSETSTICKY, &a);
272499accfeSmartynas 
273499accfeSmartynas 	/* Load the floating-point masks */
274499accfeSmartynas 	a.mask = envp->__mask & FE_ALL_EXCEPT;
275499accfeSmartynas 	sysarch(ALPHA_FPSETMASK, &a);
276499accfeSmartynas 
277499accfeSmartynas 	/* Store the current floating-point control register */
278b5aa3b33Sguenther 	__asm__ volatile ("trapb");
279b5aa3b33Sguenther 	__asm__ volatile ("mf_fpcr %0" : "=f" (fpcr));
280b5aa3b33Sguenther 	__asm__ volatile ("trapb");
281499accfeSmartynas 
282d6f349c8Smartynas 	/* Set the requested flags */
283499accfeSmartynas 	fpcr &= ~((unsigned long)_ROUND_MASK << _ROUND_SHIFT);
284499accfeSmartynas 	fpcr |= ((unsigned long)envp->__round & _ROUND_MASK) << _ROUND_SHIFT;
285499accfeSmartynas 
286499accfeSmartynas 	/* Load the floating-point control register */
287b5aa3b33Sguenther 	__asm__ volatile ("trapb");
288b5aa3b33Sguenther 	__asm__ volatile ("mt_fpcr %0" : : "f" (fpcr));
289b5aa3b33Sguenther 	__asm__ volatile ("trapb");
290499accfeSmartynas 
291499accfeSmartynas 	return (0);
292499accfeSmartynas }
2932f2c0062Sguenther DEF_STD(fesetenv);
294499accfeSmartynas 
295499accfeSmartynas /*
296499accfeSmartynas  * The feupdateenv() function saves the currently raised floating-point
297499accfeSmartynas  * exceptions in its automatic storage, installs the floating-point environment
298499accfeSmartynas  * represented by the object pointed to by `envp', and then raises the saved
299499accfeSmartynas  * floating-point exceptions. The argument `envp' shall point to an object set
300499accfeSmartynas  * by a call to feholdexcept() or fegetenv(), or equal a floating-point
301499accfeSmartynas  * environment macro.
302499accfeSmartynas  */
303499accfeSmartynas int
feupdateenv(const fenv_t * envp)304499accfeSmartynas feupdateenv(const fenv_t *envp)
305499accfeSmartynas {
306499accfeSmartynas 	unsigned int fpsticky;
307499accfeSmartynas 
308499accfeSmartynas 	/* Store the current floating-point sticky flags */
309499accfeSmartynas 	fpsticky = sysarch(ALPHA_FPGETSTICKY, 0L);
310499accfeSmartynas 
311499accfeSmartynas 	/* Install new floating-point environment */
312499accfeSmartynas 	fesetenv(envp);
313499accfeSmartynas 
314499accfeSmartynas 	/* Raise any previously accumulated exceptions */
315499accfeSmartynas 	feraiseexcept(fpsticky);
316499accfeSmartynas 
317499accfeSmartynas 	return (0);
318499accfeSmartynas }
3192f2c0062Sguenther DEF_STD(feupdateenv);
320499accfeSmartynas 
321499accfeSmartynas /*
322*2c53affbSjmc  * The following functions are extensions to the standard
323499accfeSmartynas  */
324499accfeSmartynas int
feenableexcept(int mask)325499accfeSmartynas feenableexcept(int mask)
326499accfeSmartynas {
327499accfeSmartynas 	unsigned int fpmask, omask;
328499accfeSmartynas 	struct alpha_fp_except_args a;
329499accfeSmartynas 
330499accfeSmartynas 	mask &= FE_ALL_EXCEPT;
331499accfeSmartynas 
332499accfeSmartynas 	/* Store the current floating-point masks */
333499accfeSmartynas 	fpmask = sysarch(ALPHA_FPGETMASK, 0L);
334499accfeSmartynas 
335499accfeSmartynas 	omask = fpmask & FE_ALL_EXCEPT;
336499accfeSmartynas 	fpmask |= mask;
337499accfeSmartynas 
338499accfeSmartynas 	/* Load the floating-point masks */
339499accfeSmartynas 	a.mask = fpmask;
340499accfeSmartynas 	sysarch(ALPHA_FPSETMASK, &a);
341499accfeSmartynas 
342499accfeSmartynas 	return (omask);
343499accfeSmartynas 
344499accfeSmartynas }
345499accfeSmartynas 
346499accfeSmartynas int
fedisableexcept(int mask)347499accfeSmartynas fedisableexcept(int mask)
348499accfeSmartynas {
349499accfeSmartynas 	unsigned int fpmask, omask;
350499accfeSmartynas 	struct alpha_fp_except_args a;
351499accfeSmartynas 
352499accfeSmartynas 	mask &= FE_ALL_EXCEPT;
353499accfeSmartynas 
354499accfeSmartynas 	/* Store the current floating-point masks */
355499accfeSmartynas 	fpmask = sysarch(ALPHA_FPGETMASK, 0L);
356499accfeSmartynas 
357499accfeSmartynas 	omask = fpmask & FE_ALL_EXCEPT;
358499accfeSmartynas 	fpmask &= ~mask;
359499accfeSmartynas 
360499accfeSmartynas 	/* Load the floating-point masks */
361499accfeSmartynas 	a.mask = fpmask;
362499accfeSmartynas 	sysarch(ALPHA_FPSETMASK, &a);
363499accfeSmartynas 
364499accfeSmartynas 	return (omask);
365499accfeSmartynas }
366499accfeSmartynas 
367499accfeSmartynas int
fegetexcept(void)368499accfeSmartynas fegetexcept(void)
369499accfeSmartynas {
370499accfeSmartynas 	unsigned int fpmask;
371499accfeSmartynas 
372499accfeSmartynas 	/* Store the current floating-point masks */
373499accfeSmartynas 	fpmask = sysarch(ALPHA_FPGETMASK, 0L);
374499accfeSmartynas 
375499accfeSmartynas 	return (fpmask & FE_ALL_EXCEPT);
376499accfeSmartynas }
377