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