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