1*2c53affbSjmc /* $OpenBSD: fenv.c,v 1.6 2022/12/27 17:10:07 jmc Exp $ */
2602592deSmartynas /* $NetBSD: fenv.c,v 1.1 2010/07/31 21:47:53 joerg Exp $ */
3602592deSmartynas
4602592deSmartynas /*-
5602592deSmartynas * Copyright (c) 2004-2005 David Schultz <das (at) FreeBSD.ORG>
6602592deSmartynas * All rights reserved.
7602592deSmartynas *
8602592deSmartynas * Redistribution and use in source and binary forms, with or without
9602592deSmartynas * modification, are permitted provided that the following conditions
10602592deSmartynas * are met:
11602592deSmartynas * 1. Redistributions of source code must retain the above copyright
12602592deSmartynas * notice, this list of conditions and the following disclaimer.
13602592deSmartynas * 2. Redistributions in binary form must reproduce the above copyright
14602592deSmartynas * notice, this list of conditions and the following disclaimer in the
15602592deSmartynas * documentation and/or other materials provided with the distribution.
16602592deSmartynas *
17602592deSmartynas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18602592deSmartynas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19602592deSmartynas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20602592deSmartynas * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21602592deSmartynas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22602592deSmartynas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23602592deSmartynas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24602592deSmartynas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25602592deSmartynas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26602592deSmartynas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27602592deSmartynas * SUCH DAMAGE.
28602592deSmartynas */
29602592deSmartynas
30602592deSmartynas #include <fenv.h>
314a39ccd0Sderaadt #include <machine/fpu.h>
32602592deSmartynas
33602592deSmartynas /*
34602592deSmartynas * The following constant represents the default floating-point environment
35602592deSmartynas * (that is, the one installed at program startup) and has type pointer to
36602592deSmartynas * const-qualified fenv_t.
37602592deSmartynas *
38602592deSmartynas * It can be used as an argument to the functions within the <fenv.h> header
39602592deSmartynas * that manage the floating-point environment, namely fesetenv() and
40602592deSmartynas * feupdateenv().
41602592deSmartynas *
42602592deSmartynas * x87 fpu registers are 16bit wide. The upper bits, 31-16, are marked as
43602592deSmartynas * RESERVED.
44602592deSmartynas */
45602592deSmartynas fenv_t __fe_dfl_env = {
46602592deSmartynas {
47602592deSmartynas 0xffff0000 | __INITIAL_NPXCW__, /* Control word register */
48602592deSmartynas 0xffff0000, /* Status word register */
49602592deSmartynas 0xffffffff, /* Tag word register */
50602592deSmartynas {
51602592deSmartynas 0x00000000,
52602592deSmartynas 0x00000000,
53602592deSmartynas 0x00000000,
54d6f349c8Smartynas 0xffff0000
55d6f349c8Smartynas }
56602592deSmartynas },
57602592deSmartynas __INITIAL_MXCSR__ /* MXCSR register */
58602592deSmartynas };
59602592deSmartynas
60602592deSmartynas
61602592deSmartynas /*
62602592deSmartynas * The feclearexcept() function clears the supported floating-point exceptions
63602592deSmartynas * represented by `excepts'.
64602592deSmartynas */
65602592deSmartynas int
feclearexcept(int excepts)66602592deSmartynas feclearexcept(int excepts)
67602592deSmartynas {
68602592deSmartynas fenv_t fenv;
69602592deSmartynas unsigned int mxcsr;
70602592deSmartynas
71602592deSmartynas excepts &= FE_ALL_EXCEPT;
72602592deSmartynas
73602592deSmartynas /* Store the current x87 floating-point environment */
74b5aa3b33Sguenther __asm__ volatile ("fnstenv %0" : "=m" (fenv));
75602592deSmartynas
76602592deSmartynas /* Clear the requested floating-point exceptions */
77602592deSmartynas fenv.__x87.__status &= ~excepts;
78602592deSmartynas
79*2c53affbSjmc /* Load the x87 floating-point environment */
80b5aa3b33Sguenther __asm__ volatile ("fldenv %0" : : "m" (fenv));
81602592deSmartynas
82602592deSmartynas /* Same for SSE environment */
83b5aa3b33Sguenther __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
84602592deSmartynas mxcsr &= ~excepts;
85b5aa3b33Sguenther __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr));
86602592deSmartynas
87602592deSmartynas return (0);
88602592deSmartynas }
892f2c0062Sguenther DEF_STD(feclearexcept);
90602592deSmartynas
91602592deSmartynas /*
92602592deSmartynas * The fegetexceptflag() function stores an implementation-defined
93602592deSmartynas * representation of the states of the floating-point status flags indicated by
94602592deSmartynas * the argument excepts in the object pointed to by the argument flagp.
95602592deSmartynas */
96602592deSmartynas int
fegetexceptflag(fexcept_t * flagp,int excepts)97602592deSmartynas fegetexceptflag(fexcept_t *flagp, int excepts)
98602592deSmartynas {
99d6f349c8Smartynas unsigned short status;
100602592deSmartynas unsigned int mxcsr;
101602592deSmartynas
102602592deSmartynas excepts &= FE_ALL_EXCEPT;
103602592deSmartynas
104602592deSmartynas /* Store the current x87 status register */
105b5aa3b33Sguenther __asm__ volatile ("fnstsw %0" : "=am" (status));
106602592deSmartynas
107602592deSmartynas /* Store the MXCSR register */
108b5aa3b33Sguenther __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
109602592deSmartynas
110602592deSmartynas /* Store the results in flagp */
111d6f349c8Smartynas *flagp = (status | mxcsr) & excepts;
112602592deSmartynas
113602592deSmartynas return (0);
114602592deSmartynas }
115602592deSmartynas
116602592deSmartynas /*
117602592deSmartynas * The feraiseexcept() function raises the supported floating-point exceptions
118602592deSmartynas * represented by the argument `excepts'.
119602592deSmartynas *
120602592deSmartynas * The standard explicitly allows us to execute an instruction that has the
121602592deSmartynas * exception as a side effect, but we choose to manipulate the status register
122602592deSmartynas * directly.
123602592deSmartynas *
124602592deSmartynas * The validation of input is being deferred to fesetexceptflag().
125602592deSmartynas */
126602592deSmartynas int
feraiseexcept(int excepts)127602592deSmartynas feraiseexcept(int excepts)
128602592deSmartynas {
129602592deSmartynas excepts &= FE_ALL_EXCEPT;
130602592deSmartynas
131602592deSmartynas fesetexceptflag((fexcept_t *)&excepts, excepts);
132b5aa3b33Sguenther __asm__ volatile ("fwait");
133602592deSmartynas
134602592deSmartynas return (0);
135602592deSmartynas }
1362f2c0062Sguenther DEF_STD(feraiseexcept);
137602592deSmartynas
138602592deSmartynas /*
139602592deSmartynas * This function sets the floating-point status flags indicated by the argument
140602592deSmartynas * `excepts' to the states stored in the object pointed to by `flagp'. It does
141602592deSmartynas * NOT raise any floating-point exceptions, but only sets the state of the flags.
142602592deSmartynas */
143602592deSmartynas int
fesetexceptflag(const fexcept_t * flagp,int excepts)144602592deSmartynas fesetexceptflag(const fexcept_t *flagp, int excepts)
145602592deSmartynas {
146602592deSmartynas fenv_t fenv;
147602592deSmartynas unsigned int mxcsr;
148602592deSmartynas
149602592deSmartynas excepts &= FE_ALL_EXCEPT;
150602592deSmartynas
151602592deSmartynas /* Store the current x87 floating-point environment */
152b5aa3b33Sguenther __asm__ volatile ("fnstenv %0" : "=m" (fenv));
153602592deSmartynas
154602592deSmartynas /* Set the requested status flags */
155602592deSmartynas fenv.__x87.__status &= ~excepts;
156602592deSmartynas fenv.__x87.__status |= *flagp & excepts;
157602592deSmartynas
158*2c53affbSjmc /* Load the x87 floating-point environment */
159b5aa3b33Sguenther __asm__ volatile ("fldenv %0" : : "m" (fenv));
160602592deSmartynas
161602592deSmartynas /* Same for SSE environment */
162b5aa3b33Sguenther __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
163602592deSmartynas mxcsr &= ~excepts;
164602592deSmartynas mxcsr |= *flagp & excepts;
165b5aa3b33Sguenther __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr));
166602592deSmartynas
167602592deSmartynas return (0);
168602592deSmartynas }
1692f2c0062Sguenther DEF_STD(fesetexceptflag);
170602592deSmartynas
171602592deSmartynas /*
172602592deSmartynas * The fetestexcept() function determines which of a specified subset of the
173602592deSmartynas * floating-point exception flags are currently set. The `excepts' argument
174602592deSmartynas * specifies the floating-point status flags to be queried.
175602592deSmartynas */
176602592deSmartynas int
fetestexcept(int excepts)177602592deSmartynas fetestexcept(int excepts)
178602592deSmartynas {
179d6f349c8Smartynas unsigned short status;
180602592deSmartynas unsigned int mxcsr;
181602592deSmartynas
182602592deSmartynas excepts &= FE_ALL_EXCEPT;
183602592deSmartynas
184602592deSmartynas /* Store the current x87 status register */
185b5aa3b33Sguenther __asm__ volatile ("fnstsw %0" : "=am" (status));
186602592deSmartynas
187602592deSmartynas /* Store the MXCSR register state */
188b5aa3b33Sguenther __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
189602592deSmartynas
190d6f349c8Smartynas return ((status | mxcsr) & excepts);
191602592deSmartynas }
1922f2c0062Sguenther DEF_STD(fetestexcept);
193602592deSmartynas
194602592deSmartynas /*
195602592deSmartynas * The fegetround() function gets the current rounding direction.
196602592deSmartynas */
197602592deSmartynas int
fegetround(void)198602592deSmartynas fegetround(void)
199602592deSmartynas {
200602592deSmartynas unsigned short control;
201602592deSmartynas
202602592deSmartynas /*
203d6f349c8Smartynas * We assume that the x87 and the SSE unit agree on the
204d6f349c8Smartynas * rounding mode. Reading the control word on the x87 turns
205d6f349c8Smartynas * out to be about 5 times faster than reading it on the SSE
206d6f349c8Smartynas * unit on an Opteron 244.
207602592deSmartynas */
208b5aa3b33Sguenther __asm__ volatile ("fnstcw %0" : "=m" (control));
209602592deSmartynas
210602592deSmartynas return (control & _X87_ROUND_MASK);
211602592deSmartynas }
2122f2c0062Sguenther DEF_STD(fegetround);
213602592deSmartynas
214602592deSmartynas /*
215602592deSmartynas * The fesetround() function establishes the rounding direction represented by
216602592deSmartynas * its argument `round'. If the argument is not equal to the value of a rounding
217602592deSmartynas * direction macro, the rounding direction is not changed.
218602592deSmartynas */
219602592deSmartynas int
fesetround(int round)220602592deSmartynas fesetround(int round)
221602592deSmartynas {
222602592deSmartynas unsigned short control;
223602592deSmartynas unsigned int mxcsr;
224602592deSmartynas
225602592deSmartynas /* Check whether requested rounding direction is supported */
226602592deSmartynas if (round & ~_X87_ROUND_MASK)
227602592deSmartynas return (-1);
228602592deSmartynas
229602592deSmartynas /* Store the current x87 control word register */
230b5aa3b33Sguenther __asm__ volatile ("fnstcw %0" : "=m" (control));
231602592deSmartynas
232d6f349c8Smartynas /* Set the rounding direction */
233602592deSmartynas control &= ~_X87_ROUND_MASK;
234602592deSmartynas control |= round;
235602592deSmartynas
236602592deSmartynas /* Load the x87 control word register */
237b5aa3b33Sguenther __asm__ volatile ("fldcw %0" : : "m" (control));
238602592deSmartynas
239d6f349c8Smartynas /* Same for the SSE environment */
240b5aa3b33Sguenther __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
241d6f349c8Smartynas mxcsr &= ~(_X87_ROUND_MASK << _SSE_ROUND_SHIFT);
242d6f349c8Smartynas mxcsr |= round << _SSE_ROUND_SHIFT;
243b5aa3b33Sguenther __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr));
244602592deSmartynas
245602592deSmartynas return (0);
246602592deSmartynas }
2472f2c0062Sguenther DEF_STD(fesetround);
248602592deSmartynas
249602592deSmartynas /*
250602592deSmartynas * The fegetenv() function attempts to store the current floating-point
251602592deSmartynas * environment in the object pointed to by envp.
252602592deSmartynas */
253602592deSmartynas int
fegetenv(fenv_t * envp)254602592deSmartynas fegetenv(fenv_t *envp)
255602592deSmartynas {
256602592deSmartynas /* Store the current x87 floating-point environment */
257b5aa3b33Sguenther __asm__ volatile ("fnstenv %0" : "=m" (*envp));
258602592deSmartynas
259602592deSmartynas /* Store the MXCSR register state */
260b5aa3b33Sguenther __asm__ volatile ("stmxcsr %0" : "=m" (envp->__mxcsr));
261602592deSmartynas
262602592deSmartynas /*
263602592deSmartynas * When an FNSTENV instruction is executed, all pending exceptions are
264602592deSmartynas * essentially lost (either the x87 FPU status register is cleared or
265602592deSmartynas * all exceptions are masked).
266602592deSmartynas *
267602592deSmartynas * 8.6 X87 FPU EXCEPTION SYNCHRONIZATION -
268602592deSmartynas * Intel(R) 64 and IA-32 Architectures Softare Developer's Manual - Vol1
269602592deSmartynas */
270b5aa3b33Sguenther __asm__ volatile ("fldcw %0" : : "m" (envp->__x87.__control));
271602592deSmartynas
272602592deSmartynas return (0);
273602592deSmartynas }
2742f2c0062Sguenther DEF_STD(fegetenv);
275602592deSmartynas
276602592deSmartynas /*
277602592deSmartynas * The feholdexcept() function saves the current floating-point environment
278602592deSmartynas * in the object pointed to by envp, clears the floating-point status flags, and
279602592deSmartynas * then installs a non-stop (continue on floating-point exceptions) mode, if
280602592deSmartynas * available, for all floating-point exceptions.
281602592deSmartynas */
282602592deSmartynas int
feholdexcept(fenv_t * envp)283602592deSmartynas feholdexcept(fenv_t *envp)
284602592deSmartynas {
285602592deSmartynas unsigned int mxcsr;
286602592deSmartynas
287602592deSmartynas /* Store the current x87 floating-point environment */
288b5aa3b33Sguenther __asm__ volatile ("fnstenv %0" : "=m" (*envp));
289602592deSmartynas
290602592deSmartynas /* Clear all exception flags in FPU */
291b5aa3b33Sguenther __asm__ volatile ("fnclex");
292602592deSmartynas
293602592deSmartynas /* Store the MXCSR register state */
294b5aa3b33Sguenther __asm__ volatile ("stmxcsr %0" : "=m" (envp->__mxcsr));
295602592deSmartynas
296d6f349c8Smartynas /* Clear exception flags in MXCSR */
297602592deSmartynas mxcsr = envp->__mxcsr;
298602592deSmartynas mxcsr &= ~FE_ALL_EXCEPT;
299602592deSmartynas
300602592deSmartynas /* Mask all exceptions */
301d6f349c8Smartynas mxcsr |= FE_ALL_EXCEPT << _SSE_MASK_SHIFT;
302602592deSmartynas
303d6f349c8Smartynas /* Store the MXCSR register */
304b5aa3b33Sguenther __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr));
305602592deSmartynas
306602592deSmartynas return (0);
307602592deSmartynas }
3082f2c0062Sguenther DEF_STD(feholdexcept);
309602592deSmartynas
310602592deSmartynas /*
311602592deSmartynas * The fesetenv() function attempts to establish the floating-point environment
312602592deSmartynas * represented by the object pointed to by envp. The argument `envp' points
313602592deSmartynas * to an object set by a call to fegetenv() or feholdexcept(), or equal a
314602592deSmartynas * floating-point environment macro. The fesetenv() function does not raise
315602592deSmartynas * floating-point exceptions, but only installs the state of the floating-point
316602592deSmartynas * status flags represented through its argument.
317602592deSmartynas */
318602592deSmartynas int
fesetenv(const fenv_t * envp)319602592deSmartynas fesetenv(const fenv_t *envp)
320602592deSmartynas {
321*2c53affbSjmc /* Load the x87 floating-point environment */
322b5aa3b33Sguenther __asm__ volatile ("fldenv %0" : : "m" (*envp));
323602592deSmartynas
324602592deSmartynas /* Store the MXCSR register */
325b5aa3b33Sguenther __asm__ volatile ("ldmxcsr %0" : : "m" (envp->__mxcsr));
326602592deSmartynas
327602592deSmartynas return (0);
328602592deSmartynas }
3292f2c0062Sguenther DEF_STD(fesetenv);
330602592deSmartynas
331602592deSmartynas /*
332602592deSmartynas * The feupdateenv() function saves the currently raised floating-point
333602592deSmartynas * exceptions in its automatic storage, installs the floating-point environment
334602592deSmartynas * represented by the object pointed to by `envp', and then raises the saved
335602592deSmartynas * floating-point exceptions. The argument `envp' shall point to an object set
336602592deSmartynas * by a call to feholdexcept() or fegetenv(), or equal a floating-point
337602592deSmartynas * environment macro.
338602592deSmartynas */
339602592deSmartynas int
feupdateenv(const fenv_t * envp)340602592deSmartynas feupdateenv(const fenv_t *envp)
341602592deSmartynas {
342d6f349c8Smartynas unsigned short status;
343602592deSmartynas unsigned int mxcsr;
344602592deSmartynas
345602592deSmartynas /* Store the x87 status register */
346b5aa3b33Sguenther __asm__ volatile ("fnstsw %0" : "=am" (status));
347602592deSmartynas
348602592deSmartynas /* Store the MXCSR register */
349b5aa3b33Sguenther __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
350602592deSmartynas
351602592deSmartynas /* Install new floating-point environment */
352602592deSmartynas fesetenv(envp);
353602592deSmartynas
354602592deSmartynas /* Raise any previously accumulated exceptions */
355d6f349c8Smartynas feraiseexcept(status | mxcsr);
356602592deSmartynas
357602592deSmartynas return (0);
358602592deSmartynas }
3592f2c0062Sguenther DEF_STD(feupdateenv);
360602592deSmartynas
361602592deSmartynas /*
362*2c53affbSjmc * The following functions are extensions to the standard
363602592deSmartynas */
364602592deSmartynas int
feenableexcept(int mask)365602592deSmartynas feenableexcept(int mask)
366602592deSmartynas {
367602592deSmartynas unsigned int mxcsr, omask;
368602592deSmartynas unsigned short control;
369602592deSmartynas
370602592deSmartynas mask &= FE_ALL_EXCEPT;
371602592deSmartynas
372b5aa3b33Sguenther __asm__ volatile ("fnstcw %0" : "=m" (control));
373b5aa3b33Sguenther __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
374602592deSmartynas
375d6f349c8Smartynas omask = ~(control | (mxcsr >> _SSE_MASK_SHIFT)) & FE_ALL_EXCEPT;
376602592deSmartynas control &= ~mask;
377b5aa3b33Sguenther __asm__ volatile ("fldcw %0" : : "m" (control));
378602592deSmartynas
379d6f349c8Smartynas mxcsr &= ~(mask << _SSE_MASK_SHIFT);
380b5aa3b33Sguenther __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr));
381602592deSmartynas
382602592deSmartynas return (omask);
383602592deSmartynas }
384602592deSmartynas
385602592deSmartynas int
fedisableexcept(int mask)386602592deSmartynas fedisableexcept(int mask)
387602592deSmartynas {
388602592deSmartynas unsigned int mxcsr, omask;
389602592deSmartynas unsigned short control;
390602592deSmartynas
391602592deSmartynas mask &= FE_ALL_EXCEPT;
392602592deSmartynas
393b5aa3b33Sguenther __asm__ volatile ("fnstcw %0" : "=m" (control));
394b5aa3b33Sguenther __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
395602592deSmartynas
396d6f349c8Smartynas omask = ~(control | (mxcsr >> _SSE_MASK_SHIFT)) & FE_ALL_EXCEPT;
397602592deSmartynas control |= mask;
398b5aa3b33Sguenther __asm__ volatile ("fldcw %0" : : "m" (control));
399602592deSmartynas
400d6f349c8Smartynas mxcsr |= mask << _SSE_MASK_SHIFT;
401b5aa3b33Sguenther __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr));
402602592deSmartynas
403602592deSmartynas return (omask);
404602592deSmartynas }
405602592deSmartynas
406602592deSmartynas int
fegetexcept(void)407602592deSmartynas fegetexcept(void)
408602592deSmartynas {
409602592deSmartynas unsigned short control;
410602592deSmartynas
411602592deSmartynas /*
412602592deSmartynas * We assume that the masks for the x87 and the SSE unit are
413602592deSmartynas * the same.
414602592deSmartynas */
415b5aa3b33Sguenther __asm__ volatile ("fnstcw %0" : "=m" (control));
416602592deSmartynas
417602592deSmartynas return (~control & FE_ALL_EXCEPT);
418602592deSmartynas }
419