xref: /relibc/openlibm/i387/fenv.c (revision adb103acec06f5f4e58c22a3b050d09429c216eb)
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: src/lib/msun/i387/fenv.c,v 1.8 2011/10/21 06:25:31 das Exp $
27  */
28 
29 #include <include/cdefs-compat.h>
30 #include <include/types-compat.h>
31 #ifdef __WIN32__
32 #include <i387/bsd_npx.h>
33 #else
34 #include <machine/npx.h>
35 #endif
36 
37 #define	__fenv_static
38 #include "fenv.h"
39 
40 #ifdef __GNUC_GNU_INLINE__
41 #error "This file must be compiled with C99 'inline' semantics"
42 #endif
43 
44 const fenv_t __fe_dfl_env = {
45 	__INITIAL_NPXCW__,
46 	0x0000,
47 	0x0000,
48 	0x1f80,
49 	0xffffffff,
50 	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
51 	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff }
52 };
53 
54 enum __sse_support __has_sse =
55 #ifdef __SSE__
56 	__SSE_YES;
57 #else
58 	__SSE_UNK;
59 #endif
60 
61 #define	getfl(x)	__asm __volatile("pushfl\n\tpopl %0" : "=mr" (*(x)))
62 #define	setfl(x)	__asm __volatile("pushl %0\n\tpopfl" : : "g" (x))
63 #define	cpuid_dx(x)	__asm __volatile("pushl %%ebx\n\tmovl $1, %%eax\n\t"  \
64 					 "cpuid\n\tpopl %%ebx"		      \
65 					: "=d" (*(x)) : : "eax", "ecx")
66 
67 /*
68  * Test for SSE support on this processor.  We need to do this because
69  * we need to use ldmxcsr/stmxcsr to get correct results if any part
70  * of the program was compiled to use SSE floating-point, but we can't
71  * use SSE on older processors.
72  */
73 int
74 __test_sse(void)
75 {
76 	int flag, nflag;
77 	int dx_features;
78 
79 	/* Am I a 486? */
80 	getfl(&flag);
81 	nflag = flag ^ 0x200000;
82 	setfl(nflag);
83 	getfl(&nflag);
84 	if (flag != nflag) {
85 		/* Not a 486, so CPUID should work. */
86 		cpuid_dx(&dx_features);
87 		if (dx_features & 0x2000000) {
88 			__has_sse = __SSE_YES;
89 			return (1);
90 		}
91 	}
92 	__has_sse = __SSE_NO;
93 	return (0);
94 }
95 
96 extern inline int feclearexcept(int __excepts);
97 extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts);
98 
99 int
100 fesetexceptflag(const fexcept_t *flagp, int excepts)
101 {
102 	fenv_t env;
103 	uint32_t mxcsr;
104 
105 	__fnstenv(&env);
106 	env.__status &= ~excepts;
107 	env.__status |= *flagp & excepts;
108 	__fldenv(env);
109 
110 	if (__HAS_SSE()) {
111 		__stmxcsr(&mxcsr);
112 		mxcsr &= ~excepts;
113 		mxcsr |= *flagp & excepts;
114 		__ldmxcsr(mxcsr);
115 	}
116 
117 	return (0);
118 }
119 
120 int
121 feraiseexcept(int excepts)
122 {
123 	fexcept_t ex = excepts;
124 
125 	fesetexceptflag(&ex, excepts);
126 	__fwait();
127 	return (0);
128 }
129 
130 extern inline int fetestexcept(int __excepts);
131 extern inline int fegetround(void);
132 extern inline int fesetround(int __round);
133 
134 int
135 fegetenv(fenv_t *envp)
136 {
137 	uint32_t mxcsr;
138 
139 	__fnstenv(envp);
140 	/*
141 	 * fnstenv masks all exceptions, so we need to restore
142 	 * the old control word to avoid this side effect.
143 	 */
144 	__fldcw(envp->__control);
145 	if (__HAS_SSE()) {
146 		__stmxcsr(&mxcsr);
147 		__set_mxcsr(*envp, mxcsr);
148 	}
149 	return (0);
150 }
151 
152 int
153 feholdexcept(fenv_t *envp)
154 {
155 	uint32_t mxcsr;
156 
157 	__fnstenv(envp);
158 	__fnclex();
159 	if (__HAS_SSE()) {
160 		__stmxcsr(&mxcsr);
161 		__set_mxcsr(*envp, mxcsr);
162 		mxcsr &= ~FE_ALL_EXCEPT;
163 		mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT;
164 		__ldmxcsr(mxcsr);
165 	}
166 	return (0);
167 }
168 
169 extern inline int fesetenv(const fenv_t *__envp);
170 
171 int
172 feupdateenv(const fenv_t *envp)
173 {
174 	uint32_t mxcsr;
175 	uint16_t status;
176 
177 	__fnstsw(&status);
178 	if (__HAS_SSE())
179 		__stmxcsr(&mxcsr);
180 	else
181 		mxcsr = 0;
182 	fesetenv(envp);
183 	feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT);
184 	return (0);
185 }
186 
187 int
188 __feenableexcept(int mask)
189 {
190 	uint32_t mxcsr, omask;
191 	uint16_t control;
192 
193 	mask &= FE_ALL_EXCEPT;
194 	__fnstcw(&control);
195 	if (__HAS_SSE())
196 		__stmxcsr(&mxcsr);
197 	else
198 		mxcsr = 0;
199 	omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
200 	control &= ~mask;
201 	__fldcw(control);
202 	if (__HAS_SSE()) {
203 		mxcsr &= ~(mask << _SSE_EMASK_SHIFT);
204 		__ldmxcsr(mxcsr);
205 	}
206 	return (omask);
207 }
208 
209 int
210 __fedisableexcept(int mask)
211 {
212 	uint32_t mxcsr, omask;
213 	uint16_t control;
214 
215 	mask &= FE_ALL_EXCEPT;
216 	__fnstcw(&control);
217 	if (__HAS_SSE())
218 		__stmxcsr(&mxcsr);
219 	else
220 		mxcsr = 0;
221 	omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
222 	control |= mask;
223 	__fldcw(control);
224 	if (__HAS_SSE()) {
225 		mxcsr |= mask << _SSE_EMASK_SHIFT;
226 		__ldmxcsr(mxcsr);
227 	}
228 	return (omask);
229 }
230 
231 __weak_reference(__feenableexcept, feenableexcept);
232 __weak_reference(__fedisableexcept, fedisableexcept);
233