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