xref: /openbsd/lib/libm/arch/sparc64/fenv.c (revision d6f349c8)
1 /*	$OpenBSD: fenv.c,v 1.3 2011/04/28 17:34:23 martynas Exp $	*/
2 /*	$NetBSD: fenv.c,v 1.1 2011/01/31 00:19:33 christos Exp $	*/
3 
4 /*-
5  * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  */
27 #include <sys/cdefs.h>
28 
29 #include <fenv.h>
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 = 0;
41 
42 /*
43  * The feclearexcept() function clears the supported floating-point exceptions
44  * represented by `excepts'.
45  */
46 int
47 feclearexcept(int excepts)
48 {
49 	fexcept_t r;
50 
51 	excepts &= FE_ALL_EXCEPT;
52 
53 	/* Save floating-point state register */
54 	__asm__ __volatile__ ("stx %%fsr, %0" : "=m" (r));
55 
56 	r &= ~excepts;
57 
58 	/* Load floating-point state register */
59 	__asm__ __volatile__ ("ldx %0, %%fsr" : : "m" (r));
60 
61 	return 0;
62 }
63 
64 /*
65  * The fegetexceptflag() function stores an implementation-defined
66  * representation of the states of the floating-point status flags indicated
67  * by the argument excepts in the object pointed to by the argument flagp.
68  */
69 int
70 fegetexceptflag(fexcept_t *flagp, int excepts)
71 {
72 	fexcept_t r;
73 
74 	excepts &= FE_ALL_EXCEPT;
75 
76 	/* Save floating-point state register */
77 	__asm__ __volatile__ ("stx %%fsr, %0" : "=m" (r));
78 
79 	*flagp = r & excepts;
80 
81 	return 0;
82 }
83 
84 
85 /*
86  * This function sets the floating-point status flags indicated by the argument
87  * `excepts' to the states stored in the object pointed to by `flagp'. It does
88  * NOT raise any floating-point exceptions, but only sets the state of the flags.
89  */
90 int
91 fesetexceptflag(const fexcept_t *flagp, int excepts)
92 {
93 	fexcept_t r;
94 
95 	excepts &= FE_ALL_EXCEPT;
96 
97 	/* Save floating-point state register */
98 	__asm__ __volatile__ ("stx %%fsr, %0" : "=m" (r));
99 
100 	r &= ~excepts;
101 	r |= *flagp & excepts;
102 
103 	/* Load floating-point state register */
104 	__asm__ __volatile__ ("ldx %0, %%fsr" : : "m" (r));
105 
106 	return 0;
107 }
108 
109 /*
110  * The feraiseexcept() function raises the supported floating-point exceptions
111  * represented by the argument `excepts'.
112  *
113  * The order in which these floating-point exceptions are raised is unspecified
114  * (by the standard).
115  */
116 int
117 feraiseexcept(int excepts)
118 {
119 	volatile double d;
120 
121 	excepts &= FE_ALL_EXCEPT;
122 
123 	/*
124 	 * With a compiler that supports the FENV_ACCESS pragma properly, simple
125 	 * expressions like '0.0 / 0.0' should be sufficient to generate traps.
126 	 * Unfortunately, we need to bring a volatile variable into the equation
127 	 * to prevent incorrect optimizations.
128 	 */
129 	if (excepts & FE_INVALID) {
130 		d = 0.0;
131 		d = 0.0 / d;
132 	}
133 	if (excepts & FE_DIVBYZERO) {
134 		d = 0.0;
135 		d = 1.0 / d;
136 	}
137 	if (excepts & FE_OVERFLOW) {
138 		d = 0x1.ffp1023;
139 		d *= 2.0;
140 	}
141 	if (excepts & FE_UNDERFLOW) {
142 		d = 0x1p-1022;
143 		d /= 0x1p1023;
144 	}
145 	if (excepts & FE_INEXACT) {
146 		d = 0x1p-1022;
147 		d += 1.0;
148 	}
149 
150 	return 0;
151 }
152 
153 /*
154  * The fetestexcept() function determines which of a specified subset of the
155  * floating-point exception flags are currently set. The `excepts' argument
156  * specifies the floating-point status flags to be queried.
157  */
158 int
159 fetestexcept(int excepts)
160 {
161 	fexcept_t r;
162 
163 	excepts &= FE_ALL_EXCEPT;
164 
165 	/* Save floating-point state register */
166 	__asm__ __volatile__ ("stx %%fsr, %0" : "=m" (r));
167 
168 	return r & excepts;
169 }
170 
171 /*
172  * The fegetround() function gets the current rounding direction.
173  */
174 int
175 fegetround(void)
176 {
177 	fenv_t r;
178 
179 	/* Save floating-point state register */
180 	__asm__ __volatile__ ("stx %%fsr, %0" : "=m" (r));
181 
182 	return (r >> _ROUND_SHIFT) & _ROUND_MASK;
183 }
184 
185 /*
186  * The fesetround() function establishes the rounding direction represented by
187  * its argument `round'. If the argument is not equal to the value of a rounding
188  * direction macro, the rounding direction is not changed.
189  */
190 int
191 fesetround(int round)
192 {
193 	fenv_t r;
194 
195 	if (round & ~_ROUND_MASK)
196 		return -1;
197 
198 	/* Save floating-point state register */
199 	__asm__ __volatile__ ("stx %%fsr, %0" : "=m" (r));
200 
201 	r &= ~(_ROUND_MASK << _ROUND_SHIFT);
202 	r |= round << _ROUND_SHIFT;
203 
204 	/* Load floating-point state register */
205 	__asm__ __volatile__ ("ldx %0, %%fsr" : : "m" (r));
206 
207 	return 0;
208 }
209 
210 /*
211  * The fegetenv() function attempts to store the current floating-point
212  * environment in the object pointed to by envp.
213  */
214 int
215 fegetenv(fenv_t *envp)
216 {
217 	/* Save floating-point state register */
218 	__asm__ __volatile__ ("stx %%fsr, %0" : "=m" (*envp));
219 
220 	return 0;
221 }
222 
223 
224 /*
225  * The feholdexcept() function saves the current floating-point environment
226  * in the object pointed to by envp, clears the floating-point status flags, and
227  * then installs a non-stop (continue on floating-point exceptions) mode, if
228  * available, for all floating-point exceptions.
229  */
230 int
231 feholdexcept(fenv_t *envp)
232 {
233 	fenv_t r;
234 
235 	/* Save floating-point state register */
236 	__asm__ __volatile__ ("stx %%fsr, %0" : "=m" (r));
237 
238 	*envp = r;
239 	r &= ~(FE_ALL_EXCEPT | (FE_ALL_EXCEPT << _MASK_SHIFT));
240 
241 	/* Load floating-point state register */
242 	__asm__ __volatile__ ("ldx %0, %%fsr" : : "m" (r));
243 
244 	return 0;
245 }
246 
247 /*
248  * The fesetenv() function attempts to establish the floating-point environment
249  * represented by the object pointed to by envp. The argument `envp' points
250  * to an object set by a call to fegetenv() or feholdexcept(), or equal a
251  * floating-point environment macro. The fesetenv() function does not raise
252  * floating-point exceptions, but only installs the state of the floating-point
253  * status flags represented through its argument.
254  */
255 int
256 fesetenv(const fenv_t *envp)
257 {
258 	/* Load floating-point state register */
259 	__asm__ __volatile__ ("ldx %0, %%fsr" : : "m" (*envp));
260 
261 	return 0;
262 }
263 
264 
265 /*
266  * The feupdateenv() function saves the currently raised floating-point
267  * exceptions in its automatic storage, installs the floating-point environment
268  * represented by the object pointed to by `envp', and then raises the saved
269  * floating-point exceptions. The argument `envp' shall point to an object set
270  * by a call to feholdexcept() or fegetenv(), or equal a floating-point
271  * environment macro.
272  */
273 int
274 feupdateenv(const fenv_t *envp)
275 {
276 	fexcept_t r;
277 
278 	/* Save floating-point state register */
279 	__asm__ __volatile__ ("stx %%fsr, %0" : "=m" (r));
280 
281 	/* Load floating-point state register */
282 	__asm__ __volatile__ ("ldx %0, %%fsr" : : "m" (*envp));
283 
284 	feraiseexcept(r & FE_ALL_EXCEPT);
285 
286 	return 0;
287 }
288 
289 /*
290  * The following functions are extentions to the standard
291  */
292 int
293 feenableexcept(int mask)
294 {
295 	fenv_t old_r, new_r;
296 
297 	mask &= FE_ALL_EXCEPT;
298 
299 	/* Save floating-point state register */
300 	__asm__ __volatile__ ("stx %%fsr, %0" : "=m" (old_r));
301 
302 	new_r = old_r | (mask << _MASK_SHIFT);
303 
304 	/* Load floating-point state register */
305 	__asm__ __volatile__ ("ldx %0, %%fsr" : : "m" (new_r));
306 
307 	return (old_r >> _MASK_SHIFT) & FE_ALL_EXCEPT;
308 }
309 
310 int
311 fedisableexcept(int mask)
312 {
313 	fenv_t old_r, new_r;
314 
315 	mask &= FE_ALL_EXCEPT;
316 
317 	/* Save floating-point state register */
318 	__asm__ __volatile__ ("stx %%fsr, %0" : "=m" (old_r));
319 
320 	new_r = old_r & ~(mask << _MASK_SHIFT);
321 
322 	/* Load floating-point state register */
323 	__asm__ __volatile__ ("ldx %0, %%fsr" : : "m" (new_r));
324 
325 	return (old_r >> _MASK_SHIFT) & FE_ALL_EXCEPT;
326 }
327 
328 int
329 fegetexcept(void)
330 {
331 	fenv_t r;
332 
333 	/* Save floating-point state register */
334 	__asm__ __volatile__ ("stx %%fsr, %0" : "=m" (r));
335 
336 	return (r & (FE_ALL_EXCEPT << _MASK_SHIFT)) >> _MASK_SHIFT;
337 }
338