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