xref: /openbsd-src/lib/libm/arch/amd64/fenv.c (revision 2c53affbcc0119d6480b86c18f2790523b6a0aad)
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