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