fenv.c revision 225736
1/*-
2 * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: stable/9/lib/msun/i387/fenv.c 203441 2010-02-03 20:23:47Z kib $
27 */
28
29#include <sys/cdefs.h>
30#include <sys/types.h>
31#include <machine/npx.h>
32#include "fenv.h"
33
34const fenv_t __fe_dfl_env = {
35	__INITIAL_NPXCW__,
36	0x0000,
37	0x0000,
38	0x1f80,
39	0xffffffff,
40	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
41	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff }
42};
43
44enum __sse_support __has_sse =
45#ifdef __SSE__
46	__SSE_YES;
47#else
48	__SSE_UNK;
49#endif
50
51#define	getfl(x)	__asm __volatile("pushfl\n\tpopl %0" : "=mr" (*(x)))
52#define	setfl(x)	__asm __volatile("pushl %0\n\tpopfl" : : "g" (x))
53#define	cpuid_dx(x)	__asm __volatile("pushl %%ebx\n\tmovl $1, %%eax\n\t"  \
54					 "cpuid\n\tpopl %%ebx"		      \
55					: "=d" (*(x)) : : "eax", "ecx")
56
57/*
58 * Test for SSE support on this processor.  We need to do this because
59 * we need to use ldmxcsr/stmxcsr to get correct results if any part
60 * of the program was compiled to use SSE floating-point, but we can't
61 * use SSE on older processors.
62 */
63int
64__test_sse(void)
65{
66	int flag, nflag;
67	int dx_features;
68
69	/* Am I a 486? */
70	getfl(&flag);
71	nflag = flag ^ 0x200000;
72	setfl(nflag);
73	getfl(&nflag);
74	if (flag != nflag) {
75		/* Not a 486, so CPUID should work. */
76		cpuid_dx(&dx_features);
77		if (dx_features & 0x2000000) {
78			__has_sse = __SSE_YES;
79			return (1);
80		}
81	}
82	__has_sse = __SSE_NO;
83	return (0);
84}
85
86int
87fesetexceptflag(const fexcept_t *flagp, int excepts)
88{
89	fenv_t env;
90	__uint32_t mxcsr;
91
92	__fnstenv(&env);
93	env.__status &= ~excepts;
94	env.__status |= *flagp & excepts;
95	__fldenv(env);
96
97	if (__HAS_SSE()) {
98		__stmxcsr(&mxcsr);
99		mxcsr &= ~excepts;
100		mxcsr |= *flagp & excepts;
101		__ldmxcsr(mxcsr);
102	}
103
104	return (0);
105}
106
107int
108feraiseexcept(int excepts)
109{
110	fexcept_t ex = excepts;
111
112	fesetexceptflag(&ex, excepts);
113	__fwait();
114	return (0);
115}
116
117int
118fegetenv(fenv_t *envp)
119{
120	__uint32_t mxcsr;
121
122	__fnstenv(envp);
123	/*
124	 * fnstenv masks all exceptions, so we need to restore
125	 * the old control word to avoid this side effect.
126	 */
127	__fldcw(envp->__control);
128	if (__HAS_SSE()) {
129		__stmxcsr(&mxcsr);
130		__set_mxcsr(*envp, mxcsr);
131	}
132	return (0);
133}
134
135int
136feholdexcept(fenv_t *envp)
137{
138	__uint32_t mxcsr;
139
140	__fnstenv(envp);
141	__fnclex();
142	if (__HAS_SSE()) {
143		__stmxcsr(&mxcsr);
144		__set_mxcsr(*envp, mxcsr);
145		mxcsr &= ~FE_ALL_EXCEPT;
146		mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT;
147		__ldmxcsr(mxcsr);
148	}
149	return (0);
150}
151
152int
153feupdateenv(const fenv_t *envp)
154{
155	__uint32_t mxcsr;
156	__uint16_t status;
157
158	__fnstsw(&status);
159	if (__HAS_SSE())
160		__stmxcsr(&mxcsr);
161	else
162		mxcsr = 0;
163	fesetenv(envp);
164	feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT);
165	return (0);
166}
167
168int
169__feenableexcept(int mask)
170{
171	__uint32_t mxcsr, omask;
172	__uint16_t control;
173
174	mask &= FE_ALL_EXCEPT;
175	__fnstcw(&control);
176	if (__HAS_SSE())
177		__stmxcsr(&mxcsr);
178	else
179		mxcsr = 0;
180	omask = (control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
181	control &= ~mask;
182	__fldcw(control);
183	if (__HAS_SSE()) {
184		mxcsr &= ~(mask << _SSE_EMASK_SHIFT);
185		__ldmxcsr(mxcsr);
186	}
187	return (~omask);
188}
189
190int
191__fedisableexcept(int mask)
192{
193	__uint32_t mxcsr, omask;
194	__uint16_t control;
195
196	mask &= FE_ALL_EXCEPT;
197	__fnstcw(&control);
198	if (__HAS_SSE())
199		__stmxcsr(&mxcsr);
200	else
201		mxcsr = 0;
202	omask = (control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
203	control |= mask;
204	__fldcw(control);
205	if (__HAS_SSE()) {
206		mxcsr |= mask << _SSE_EMASK_SHIFT;
207		__ldmxcsr(mxcsr);
208	}
209	return (~omask);
210}
211
212__weak_reference(__feenableexcept, feenableexcept);
213__weak_reference(__fedisableexcept, fedisableexcept);
214