1*2c53affbSjmc /* $OpenBSD: fenv.c,v 1.8 2022/12/27 17:10:07 jmc Exp $ */
2d6f349c8Smartynas
3d6f349c8Smartynas /*
4d6f349c8Smartynas * Copyright (c) 2011 Martynas Venckus <martynas@openbsd.org>
5d6f349c8Smartynas *
6d6f349c8Smartynas * Permission to use, copy, modify, and distribute this software for any
7d6f349c8Smartynas * purpose with or without fee is hereby granted, provided that the above
8d6f349c8Smartynas * copyright notice and this permission notice appear in all copies.
9d6f349c8Smartynas *
10d6f349c8Smartynas * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11d6f349c8Smartynas * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d6f349c8Smartynas * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13d6f349c8Smartynas * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14d6f349c8Smartynas * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15d6f349c8Smartynas * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16d6f349c8Smartynas * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17d6f349c8Smartynas */
18d6f349c8Smartynas
19d6f349c8Smartynas #include <fenv.h>
20d6f349c8Smartynas
21d6f349c8Smartynas /*
22d6f349c8Smartynas * The following constant represents the default floating-point environment
23d6f349c8Smartynas * (that is, the one installed at program startup) and has type pointer to
24d6f349c8Smartynas * const-qualified fenv_t.
25d6f349c8Smartynas *
26d6f349c8Smartynas * It can be used as an argument to the functions within the <fenv.h> header
27d6f349c8Smartynas * that manage the floating-point environment, namely fesetenv() and
28d6f349c8Smartynas * feupdateenv().
29d6f349c8Smartynas */
30d6f349c8Smartynas fenv_t __fe_dfl_env = {
31d6f349c8Smartynas 0x00000000, /* Control register */
32d6f349c8Smartynas 0x00000000 /* Status register */
33d6f349c8Smartynas };
34d6f349c8Smartynas
35d6f349c8Smartynas /*
36d6f349c8Smartynas * The feclearexcept() function clears the supported floating-point exceptions
37d6f349c8Smartynas * represented by `excepts'.
38d6f349c8Smartynas */
39d6f349c8Smartynas int
feclearexcept(int excepts)40d6f349c8Smartynas feclearexcept(int excepts)
41d6f349c8Smartynas {
42d6f349c8Smartynas unsigned int fpsr;
43d6f349c8Smartynas
44d6f349c8Smartynas excepts &= FE_ALL_EXCEPT;
45d6f349c8Smartynas
46d6f349c8Smartynas /* Store the current floating-point status register */
47b5aa3b33Sguenther __asm__ volatile ("fldcr %0, %%fcr62" : "=r" (fpsr));
48d6f349c8Smartynas
49d6f349c8Smartynas /* Clear the requested floating-point exceptions */
50d6f349c8Smartynas fpsr &= ~excepts;
51d6f349c8Smartynas
52d6f349c8Smartynas /* Load the floating-point status register */
53b5aa3b33Sguenther __asm__ volatile ("fstcr %0, %%fcr62" : : "r" (fpsr));
54d6f349c8Smartynas
55d6f349c8Smartynas return (0);
56d6f349c8Smartynas }
572f2c0062Sguenther DEF_STD(feclearexcept);
58d6f349c8Smartynas
59d6f349c8Smartynas /*
60d6f349c8Smartynas * The fegetexceptflag() function stores an implementation-defined
61d6f349c8Smartynas * representation of the states of the floating-point status flags indicated by
62d6f349c8Smartynas * the argument excepts in the object pointed to by the argument flagp.
63d6f349c8Smartynas */
64d6f349c8Smartynas int
fegetexceptflag(fexcept_t * flagp,int excepts)65d6f349c8Smartynas fegetexceptflag(fexcept_t *flagp, int excepts)
66d6f349c8Smartynas {
67d6f349c8Smartynas unsigned int fpsr;
68d6f349c8Smartynas
69d6f349c8Smartynas excepts &= FE_ALL_EXCEPT;
70d6f349c8Smartynas
71d6f349c8Smartynas /* Store the current floating-point status register */
72b5aa3b33Sguenther __asm__ volatile ("fldcr %0, %%fcr62" : "=r" (fpsr));
73d6f349c8Smartynas
74d6f349c8Smartynas /* Store the results in flagp */
75d6f349c8Smartynas *flagp = fpsr & excepts;
76d6f349c8Smartynas
77d6f349c8Smartynas return (0);
78d6f349c8Smartynas }
79d6f349c8Smartynas
80d6f349c8Smartynas /*
81d6f349c8Smartynas * The feraiseexcept() function raises the supported floating-point exceptions
82d6f349c8Smartynas * represented by the argument `excepts'.
83d6f349c8Smartynas */
84d6f349c8Smartynas int
feraiseexcept(int excepts)85d6f349c8Smartynas feraiseexcept(int excepts)
86d6f349c8Smartynas {
87d6f349c8Smartynas volatile double d;
88d6f349c8Smartynas
89d6f349c8Smartynas excepts &= FE_ALL_EXCEPT;
90d6f349c8Smartynas
91d6f349c8Smartynas /*
92d6f349c8Smartynas * With a compiler that supports the FENV_ACCESS pragma
93d6f349c8Smartynas * properly, simple expressions like '0.0 / 0.0' should
94d6f349c8Smartynas * be sufficient to generate traps. Unfortunately, we
95d6f349c8Smartynas * need to bring a volatile variable into the equation
96d6f349c8Smartynas * to prevent incorrect optimizations.
97d6f349c8Smartynas */
98d6f349c8Smartynas if (excepts & FE_INVALID) {
99d6f349c8Smartynas d = 0.0;
100d6f349c8Smartynas d = 0.0 / d;
101d6f349c8Smartynas }
102d6f349c8Smartynas if (excepts & FE_DIVBYZERO) {
103d6f349c8Smartynas d = 0.0;
104d6f349c8Smartynas d = 1.0 / d;
105d6f349c8Smartynas }
106d6f349c8Smartynas if (excepts & FE_OVERFLOW) {
107d6f349c8Smartynas d = 0x1.ffp1023;
108d6f349c8Smartynas d *= 2.0;
109d6f349c8Smartynas }
110d6f349c8Smartynas if (excepts & FE_UNDERFLOW) {
1111241b6e5Smartynas d = 0x1p1023;
1121241b6e5Smartynas d = 0x1p-1022 / d;
113d6f349c8Smartynas }
114d6f349c8Smartynas if (excepts & FE_INEXACT) {
115d6f349c8Smartynas d = 0x1p-1022;
116d6f349c8Smartynas d += 1.0;
117d6f349c8Smartynas }
118d6f349c8Smartynas return (0);
119d6f349c8Smartynas }
1202f2c0062Sguenther DEF_STD(feraiseexcept);
121d6f349c8Smartynas
122d6f349c8Smartynas /*
123d6f349c8Smartynas * This function sets the floating-point status flags indicated by the argument
124d6f349c8Smartynas * `excepts' to the states stored in the object pointed to by `flagp'. It does
125d6f349c8Smartynas * NOT raise any floating-point exceptions, but only sets the state of the flags.
126d6f349c8Smartynas */
127d6f349c8Smartynas int
fesetexceptflag(const fexcept_t * flagp,int excepts)128d6f349c8Smartynas fesetexceptflag(const fexcept_t *flagp, int excepts)
129d6f349c8Smartynas {
130d6f349c8Smartynas unsigned int fpsr;
131d6f349c8Smartynas
132d6f349c8Smartynas excepts &= FE_ALL_EXCEPT;
133d6f349c8Smartynas
134d6f349c8Smartynas /* Store the current floating-point status register */
135b5aa3b33Sguenther __asm__ volatile ("fldcr %0, %%fcr62" : "=r" (fpsr));
136d6f349c8Smartynas
137d6f349c8Smartynas /* Set the requested status flags */
138d6f349c8Smartynas fpsr &= ~excepts;
139d6f349c8Smartynas fpsr |= *flagp & excepts;
140d6f349c8Smartynas
141d6f349c8Smartynas /* Load the floating-point status register */
142b5aa3b33Sguenther __asm__ volatile ("fstcr %0, %%fcr62" : : "r" (fpsr));
143d6f349c8Smartynas
144d6f349c8Smartynas return (0);
145d6f349c8Smartynas }
1462f2c0062Sguenther DEF_STD(fesetexceptflag);
147d6f349c8Smartynas
148d6f349c8Smartynas /*
149d6f349c8Smartynas * The fetestexcept() function determines which of a specified subset of the
150d6f349c8Smartynas * floating-point exception flags are currently set. The `excepts' argument
151d6f349c8Smartynas * specifies the floating-point status flags to be queried.
152d6f349c8Smartynas */
153d6f349c8Smartynas int
fetestexcept(int excepts)154d6f349c8Smartynas fetestexcept(int excepts)
155d6f349c8Smartynas {
156d6f349c8Smartynas unsigned int fpsr;
157d6f349c8Smartynas
158d6f349c8Smartynas excepts &= FE_ALL_EXCEPT;
159d6f349c8Smartynas
160d6f349c8Smartynas /* Store the current floating-point status register */
161b5aa3b33Sguenther __asm__ volatile ("fldcr %0, %%fcr62" : "=r" (fpsr));
162d6f349c8Smartynas
163d6f349c8Smartynas return (fpsr & excepts);
164d6f349c8Smartynas }
1652f2c0062Sguenther DEF_STD(fetestexcept);
166d6f349c8Smartynas
167d6f349c8Smartynas /*
168d6f349c8Smartynas * The fegetround() function gets the current rounding direction.
169d6f349c8Smartynas */
170d6f349c8Smartynas int
fegetround(void)171d6f349c8Smartynas fegetround(void)
172d6f349c8Smartynas {
173d6f349c8Smartynas unsigned int fpcr;
174d6f349c8Smartynas
175d6f349c8Smartynas /* Store the current floating-point control register */
176b5aa3b33Sguenther __asm__ volatile ("fldcr %0, %%fcr63" : "=r" (fpcr));
177d6f349c8Smartynas
178d6f349c8Smartynas return (fpcr & _ROUND_MASK);
179d6f349c8Smartynas }
1802f2c0062Sguenther DEF_STD(fegetround);
181d6f349c8Smartynas
182d6f349c8Smartynas /*
183d6f349c8Smartynas * The fesetround() function establishes the rounding direction represented by
184d6f349c8Smartynas * its argument `round'. If the argument is not equal to the value of a rounding
185d6f349c8Smartynas * direction macro, the rounding direction is not changed.
186d6f349c8Smartynas */
187d6f349c8Smartynas int
fesetround(int round)188d6f349c8Smartynas fesetround(int round)
189d6f349c8Smartynas {
190d6f349c8Smartynas unsigned int fpcr;
191d6f349c8Smartynas
192d6f349c8Smartynas /* Check whether requested rounding direction is supported */
193d6f349c8Smartynas if (round & ~_ROUND_MASK)
194d6f349c8Smartynas return (-1);
195d6f349c8Smartynas
196d6f349c8Smartynas /* Store the current floating-point control register */
197b5aa3b33Sguenther __asm__ volatile ("fldcr %0, %%fcr63" : "=r" (fpcr));
198d6f349c8Smartynas
199d6f349c8Smartynas /* Set the rounding direction */
200d6f349c8Smartynas fpcr &= ~_ROUND_MASK;
201d6f349c8Smartynas fpcr |= round;
202d6f349c8Smartynas
203d6f349c8Smartynas /* Load the floating-point control register */
204b5aa3b33Sguenther __asm__ volatile ("fstcr %0, %%fcr63" : : "r" (fpcr));
205d6f349c8Smartynas
206d6f349c8Smartynas return (0);
207d6f349c8Smartynas }
2082f2c0062Sguenther DEF_STD(fesetround);
209d6f349c8Smartynas
210d6f349c8Smartynas /*
211d6f349c8Smartynas * The fegetenv() function attempts to store the current floating-point
212d6f349c8Smartynas * environment in the object pointed to by envp.
213d6f349c8Smartynas */
214d6f349c8Smartynas int
fegetenv(fenv_t * envp)215d6f349c8Smartynas fegetenv(fenv_t *envp)
216d6f349c8Smartynas {
217d6f349c8Smartynas /* Store the current floating-point control and status registers */
218b5aa3b33Sguenther __asm__ volatile ("fldcr %0, %%fcr63" : "=r" (envp->__control));
219b5aa3b33Sguenther __asm__ volatile ("fldcr %0, %%fcr62" : "=r" (envp->__status));
220d6f349c8Smartynas
221d6f349c8Smartynas return (0);
222d6f349c8Smartynas }
2232f2c0062Sguenther DEF_STD(fegetenv);
224d6f349c8Smartynas
225d6f349c8Smartynas /*
226d6f349c8Smartynas * The feholdexcept() function saves the current floating-point environment
227d6f349c8Smartynas * in the object pointed to by envp, clears the floating-point status flags, and
228d6f349c8Smartynas * then installs a non-stop (continue on floating-point exceptions) mode, if
229d6f349c8Smartynas * available, for all floating-point exceptions.
230d6f349c8Smartynas */
231d6f349c8Smartynas int
feholdexcept(fenv_t * envp)232d6f349c8Smartynas feholdexcept(fenv_t *envp)
233d6f349c8Smartynas {
234d6f349c8Smartynas unsigned int fpsr, fpcr;
235d6f349c8Smartynas
236d6f349c8Smartynas /* Store the current floating-point control and status registers */
237b5aa3b33Sguenther __asm__ volatile ("fldcr %0, %%fcr63" : "=r" (envp->__control));
238b5aa3b33Sguenther __asm__ volatile ("fldcr %0, %%fcr62" : "=r" (envp->__status));
239d6f349c8Smartynas
240d6f349c8Smartynas /* Clear exception flags in FPSR */
241d6f349c8Smartynas fpsr = envp->__status;
242d6f349c8Smartynas fpsr &= ~FE_ALL_EXCEPT;
243b5aa3b33Sguenther __asm__ volatile ("fstcr %0, %%fcr62" : : "r" (fpsr));
244d6f349c8Smartynas
245d6f349c8Smartynas /* Mask all exceptions */
246d6f349c8Smartynas fpcr = envp->__control;
247d6f349c8Smartynas fpcr &= ~FE_ALL_EXCEPT;
248b5aa3b33Sguenther __asm__ volatile ("fstcr %0, %%fcr63" : : "r" (fpcr));
249d6f349c8Smartynas
250d6f349c8Smartynas return (0);
251d6f349c8Smartynas }
2522f2c0062Sguenther DEF_STD(feholdexcept);
253d6f349c8Smartynas
254d6f349c8Smartynas /*
255d6f349c8Smartynas * The fesetenv() function attempts to establish the floating-point environment
256d6f349c8Smartynas * represented by the object pointed to by envp. The argument `envp' points
257d6f349c8Smartynas * to an object set by a call to fegetenv() or feholdexcept(), or equal a
258d6f349c8Smartynas * floating-point environment macro. The fesetenv() function does not raise
259d6f349c8Smartynas * floating-point exceptions, but only installs the state of the floating-point
260d6f349c8Smartynas * status flags represented through its argument.
261d6f349c8Smartynas */
262d6f349c8Smartynas int
fesetenv(const fenv_t * envp)263d6f349c8Smartynas fesetenv(const fenv_t *envp)
264d6f349c8Smartynas {
265d6f349c8Smartynas fenv_t fenv;
266d6f349c8Smartynas
267d6f349c8Smartynas /* Store the current floating-point control and status registers */
268b5aa3b33Sguenther __asm__ volatile ("fldcr %0, %%fcr63" : "=r" (fenv.__control));
269b5aa3b33Sguenther __asm__ volatile ("fldcr %0, %%fcr62" : "=r" (fenv.__status));
270d6f349c8Smartynas
271d6f349c8Smartynas /* Set the requested control flags */
272d6f349c8Smartynas fenv.__control &= ~(FE_ALL_EXCEPT | _ROUND_MASK);
273d6f349c8Smartynas fenv.__control |= envp->__control & (FE_ALL_EXCEPT | _ROUND_MASK);
274d6f349c8Smartynas
275d6f349c8Smartynas /* Set the requested status flags */
276d6f349c8Smartynas fenv.__status &= ~FE_ALL_EXCEPT;
277d6f349c8Smartynas fenv.__status |= envp->__status & FE_ALL_EXCEPT;
278d6f349c8Smartynas
279d6f349c8Smartynas /* Load the floating-point control and status registers */
280b5aa3b33Sguenther __asm__ volatile ("fstcr %0, %%fcr63" : : "r" (fenv.__control));
281b5aa3b33Sguenther __asm__ volatile ("fstcr %0, %%fcr62" : : "r" (fenv.__status));
282d6f349c8Smartynas
283d6f349c8Smartynas return (0);
284d6f349c8Smartynas }
2852f2c0062Sguenther DEF_STD(fesetenv);
286d6f349c8Smartynas
287d6f349c8Smartynas /*
288d6f349c8Smartynas * The feupdateenv() function saves the currently raised floating-point
289d6f349c8Smartynas * exceptions in its automatic storage, installs the floating-point environment
290d6f349c8Smartynas * represented by the object pointed to by `envp', and then raises the saved
291d6f349c8Smartynas * floating-point exceptions. The argument `envp' shall point to an object set
292d6f349c8Smartynas * by a call to feholdexcept() or fegetenv(), or equal a floating-point
293d6f349c8Smartynas * environment macro.
294d6f349c8Smartynas */
295d6f349c8Smartynas int
feupdateenv(const fenv_t * envp)296d6f349c8Smartynas feupdateenv(const fenv_t *envp)
297d6f349c8Smartynas {
298d6f349c8Smartynas unsigned int fpsr;
299d6f349c8Smartynas
300d6f349c8Smartynas /* Store the current floating-point status register */
301b5aa3b33Sguenther __asm__ volatile ("fldcr %0, %%fcr62" : "=r" (fpsr));
302d6f349c8Smartynas
303d6f349c8Smartynas /* Install new floating-point environment */
304d6f349c8Smartynas fesetenv(envp);
305d6f349c8Smartynas
306d6f349c8Smartynas /* Raise any previously accumulated exceptions */
307d6f349c8Smartynas feraiseexcept(fpsr);
308d6f349c8Smartynas
309d6f349c8Smartynas return (0);
310d6f349c8Smartynas }
3112f2c0062Sguenther DEF_STD(feupdateenv);
312d6f349c8Smartynas
313d6f349c8Smartynas /*
314*2c53affbSjmc * The following functions are extensions to the standard
315d6f349c8Smartynas */
316d6f349c8Smartynas int
feenableexcept(int mask)317d6f349c8Smartynas feenableexcept(int mask)
318d6f349c8Smartynas {
319d6f349c8Smartynas unsigned int fpcr, omask;
320d6f349c8Smartynas
321d6f349c8Smartynas mask &= FE_ALL_EXCEPT;
322d6f349c8Smartynas
323d6f349c8Smartynas /* Store the current floating-point control register */
324b5aa3b33Sguenther __asm__ volatile ("fldcr %0, %%fcr63" : "=r" (fpcr));
325d6f349c8Smartynas
326d6f349c8Smartynas omask = fpcr & FE_ALL_EXCEPT;
327d6f349c8Smartynas fpcr |= mask;
328d6f349c8Smartynas
329d6f349c8Smartynas /* Load the floating-point control register */
330b5aa3b33Sguenther __asm__ volatile ("fstcr %0, %%fcr63" : : "r" (fpcr));
331d6f349c8Smartynas
332d6f349c8Smartynas return (omask);
333d6f349c8Smartynas
334d6f349c8Smartynas }
335d6f349c8Smartynas
336d6f349c8Smartynas int
fedisableexcept(int mask)337d6f349c8Smartynas fedisableexcept(int mask)
338d6f349c8Smartynas {
339d6f349c8Smartynas unsigned int fpcr, omask;
340d6f349c8Smartynas
341d6f349c8Smartynas mask &= FE_ALL_EXCEPT;
342d6f349c8Smartynas
343d6f349c8Smartynas /* Store the current floating-point control register */
344b5aa3b33Sguenther __asm__ volatile ("fldcr %0, %%fcr63" : "=r" (fpcr));
345d6f349c8Smartynas
346d6f349c8Smartynas omask = fpcr & FE_ALL_EXCEPT;
347d6f349c8Smartynas fpcr &= ~mask;
348d6f349c8Smartynas
349d6f349c8Smartynas /* Load the floating-point control register */
350b5aa3b33Sguenther __asm__ volatile ("fstcr %0, %%fcr63" : : "r" (fpcr));
351d6f349c8Smartynas
352d6f349c8Smartynas return (omask);
353d6f349c8Smartynas }
354d6f349c8Smartynas
355d6f349c8Smartynas int
fegetexcept(void)356d6f349c8Smartynas fegetexcept(void)
357d6f349c8Smartynas {
358d6f349c8Smartynas unsigned int fpcr;
359d6f349c8Smartynas
360d6f349c8Smartynas /* Store the current floating-point control register */
361b5aa3b33Sguenther __asm__ volatile ("fldcr %0, %%fcr63" : "=r" (fpcr));
362d6f349c8Smartynas
363d6f349c8Smartynas return (fpcr & FE_ALL_EXCEPT);
364d6f349c8Smartynas }
365