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.2 2005/03/17 22:21:46 das Exp $
27 */
28
29 #include <sys/cdefs.h>
30 #include <sys/types.h>
31 #include "fenv.h"
32
33 #define ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | FE_UPWARD | FE_TOWARDZERO)
34
35 /*
36 * The hardware default control word for i387's and later coprocessors is
37 * 0x37F, giving:
38 *
39 * round to nearest
40 * 64-bit precision
41 * all exceptions masked.
42 *
43 * We modify the affine mode bit and precision bits in this to give:
44 *
45 * affine mode for 287's (if they work at all) (1 in bitfield 1<<12)
46 * 53-bit precision (2 in bitfield 3<<8)
47 *
48 * 64-bit precision often gives bad results with high level languages
49 * because it makes the results of calculations depend on whether
50 * intermediate values are stored in memory or in FPU registers.
51 */
52 #define __INITIAL_NPXCW__ 0x127F
53 #define __INITIAL_MXCSR__ 0x1F80
54
55 /*
56 * As compared to the x87 control word, the SSE unit's control word
57 * has the rounding control bits offset by 3 and the exception mask
58 * bits offset by 7.
59 */
60 #define _SSE_ROUND_SHIFT 3
61 #define _SSE_EMASK_SHIFT 7
62
63 const fenv_t __fe_dfl_env = {
64 __INITIAL_NPXCW__, /*__control*/
65 0x0000, /*__mxcsr_hi*/
66 0x0000, /*__status*/
67 0x1f80, /*__mxcsr_lo*/
68 0xffffffff, /*__tag*/
69 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
70 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff } /*__other*/
71 };
72
73 #define __fldcw(__cw) __asm volatile("fldcw %0" : : "m" (__cw))
74 #define __fldenv(__env) __asm volatile("fldenv %0" : : "m" (__env))
75 #define __fldenvx(__env) __asm volatile("fldenv %0" : : "m" (__env) \
76 : "st", "st(1)", "st(2)", "st(3)", "st(4)", \
77 "st(5)", "st(6)", "st(7)")
78 #define __fnclex() __asm volatile("fnclex")
79 #define __fnstenv(__env) __asm volatile("fnstenv %0" : "=m" (*(__env)))
80 #define __fnstcw(__cw) __asm volatile("fnstcw %0" : "=m" (*(__cw)))
81 #define __fnstsw(__sw) __asm volatile("fnstsw %0" : "=am" (*(__sw)))
82 #define __fwait() __asm volatile("fwait")
83 #define __ldmxcsr(__csr) __asm volatile("ldmxcsr %0" : : "m" (__csr))
84 #define __stmxcsr(__csr) __asm volatile("stmxcsr %0" : "=m" (*(__csr)))
85
86 /* After testing for SSE support once, we cache the result in __has_sse. */
87 enum __sse_support { __SSE_YES, __SSE_NO, __SSE_UNK };
88 #ifdef __SSE__
89 #define __HAS_SSE() 1
90 #else
91 #define __HAS_SSE() (__has_sse == __SSE_YES || \
92 (__has_sse == __SSE_UNK && __test_sse()))
93 #endif
94
95 enum __sse_support __has_sse =
96 #ifdef __SSE__
97 __SSE_YES;
98 #else
99 __SSE_UNK;
100 #endif
101
102 #ifndef __SSE__
103 #define getfl(x) __asm volatile("pushfl\n\tpopl %0" : "=mr" (*(x)))
104 #define setfl(x) __asm volatile("pushl %0\n\tpopfl" : : "g" (x))
105 #define cpuid_dx(x) __asm volatile("pushl %%ebx\n\tmovl $1, %%eax\n\t" \
106 "cpuid\n\tpopl %%ebx" \
107 : "=d" (*(x)) : : "eax", "ecx")
108
109 /*
110 * Test for SSE support on this processor. We need to do this because
111 * we need to use ldmxcsr/stmxcsr to get correct results if any part
112 * of the program was compiled to use SSE floating-point, but we can't
113 * use SSE on older processors.
114 */
115 int
__test_sse(void)116 __test_sse(void)
117 {
118 int flag, nflag;
119 int dx_features;
120
121 /* Am I a 486? */
122 getfl(&flag);
123 nflag = flag ^ 0x200000;
124 setfl(nflag);
125 getfl(&nflag);
126 if (flag != nflag) {
127 /* Not a 486, so CPUID should work. */
128 cpuid_dx(&dx_features);
129 if (dx_features & 0x2000000) {
130 __has_sse = __SSE_YES;
131 return (1);
132 }
133 }
134 __has_sse = __SSE_NO;
135 return (0);
136 }
137 #endif /* __SSE__ */
138
139 int
fesetexceptflag(const fexcept_t * flagp,int excepts)140 fesetexceptflag(const fexcept_t *flagp, int excepts)
141 {
142 fenv_t env;
143 __uint32_t mxcsr;
144
145 excepts &= FE_ALL_EXCEPT;
146 if (excepts) { /* Do nothing if excepts is 0 */
147 __fnstenv(&env);
148 env.__status &= ~excepts;
149 env.__status |= *flagp & excepts;
150 __fnclex();
151 __fldenv(env);
152 if (__HAS_SSE()) {
153 __stmxcsr(&mxcsr);
154 mxcsr &= ~excepts;
155 mxcsr |= *flagp & excepts;
156 __ldmxcsr(mxcsr);
157 }
158 }
159
160 return (0);
161 }
162
163 int
feraiseexcept(int excepts)164 feraiseexcept(int excepts)
165 {
166 fexcept_t ex = excepts;
167
168 fesetexceptflag(&ex, excepts);
169 __fwait();
170 return (0);
171 }
172
173 int
fegetenv(fenv_t * envp)174 fegetenv(fenv_t *envp)
175 {
176 __uint32_t mxcsr;
177
178 __fnstenv(envp);
179 /*
180 * fnstenv masks all exceptions, so we need to restore
181 * the old control word to avoid this side effect.
182 */
183 __fldcw(envp->__control);
184 if (__HAS_SSE()) {
185 __stmxcsr(&mxcsr);
186 envp->__mxcsr_hi = mxcsr >> 16;
187 envp->__mxcsr_lo = mxcsr & 0xffff;
188 }
189 return (0);
190 }
191
192 int
feholdexcept(fenv_t * envp)193 feholdexcept(fenv_t *envp)
194 {
195 __uint32_t mxcsr;
196 fenv_t env;
197
198 __fnstenv(&env);
199 *envp = env;
200 env.__status &= ~FE_ALL_EXCEPT;
201 env.__control |= FE_ALL_EXCEPT;
202 __fnclex();
203 __fldenv(env);
204 if (__HAS_SSE()) {
205 __stmxcsr(&mxcsr);
206 envp->__mxcsr_hi = mxcsr >> 16;
207 envp->__mxcsr_lo = mxcsr & 0xffff;
208 mxcsr &= ~FE_ALL_EXCEPT;
209 mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT;
210 __ldmxcsr(mxcsr);
211 }
212 return (0);
213 }
214
215 int
feupdateenv(const fenv_t * envp)216 feupdateenv(const fenv_t *envp)
217 {
218 __uint32_t mxcsr;
219 __uint16_t status;
220
221 __fnstsw(&status);
222 if (__HAS_SSE()) {
223 __stmxcsr(&mxcsr);
224 } else {
225 mxcsr = 0;
226 }
227 fesetenv(envp);
228 feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT);
229 return (0);
230 }
231
232 int
feenableexcept(int mask)233 feenableexcept(int mask)
234 {
235 __uint32_t mxcsr;
236 __uint16_t control, omask;
237
238 mask &= FE_ALL_EXCEPT;
239 __fnstcw(&control);
240 if (__HAS_SSE()) {
241 __stmxcsr(&mxcsr);
242 } else {
243 mxcsr = 0;
244 }
245 omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
246 if (mask) {
247 control &= ~mask;
248 __fldcw(control);
249 if (__HAS_SSE()) {
250 mxcsr &= ~(mask << _SSE_EMASK_SHIFT);
251 __ldmxcsr(mxcsr);
252 }
253 }
254 return (omask);
255 }
256
257 int
fedisableexcept(int mask)258 fedisableexcept(int mask)
259 {
260 __uint32_t mxcsr;
261 __uint16_t control, omask;
262
263 mask &= FE_ALL_EXCEPT;
264 __fnstcw(&control);
265 if (__HAS_SSE()) {
266 __stmxcsr(&mxcsr);
267 } else {
268 mxcsr = 0;
269 }
270 omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
271 if (mask) {
272 control |= mask;
273 __fldcw(control);
274 if (__HAS_SSE()) {
275 mxcsr |= mask << _SSE_EMASK_SHIFT;
276 __ldmxcsr(mxcsr);
277 }
278 }
279 return (omask);
280 }
281
282 int
feclearexcept(int excepts)283 feclearexcept(int excepts)
284 {
285 fenv_t env;
286 __uint32_t mxcsr;
287
288 excepts &= FE_ALL_EXCEPT;
289 if (excepts) { /* Do nothing if excepts is 0 */
290 __fnstenv(&env);
291 env.__status &= ~excepts;
292 __fnclex();
293 __fldenv(env);
294 if (__HAS_SSE()) {
295 __stmxcsr(&mxcsr);
296 mxcsr &= ~excepts;
297 __ldmxcsr(mxcsr);
298 }
299 }
300 return (0);
301 }
302
303 int
fegetexceptflag(fexcept_t * flagp,int excepts)304 fegetexceptflag(fexcept_t *flagp, int excepts)
305 {
306 __uint32_t mxcsr;
307 __uint16_t status;
308
309 excepts &= FE_ALL_EXCEPT;
310 __fnstsw(&status);
311 if (__HAS_SSE()) {
312 __stmxcsr(&mxcsr);
313 } else {
314 mxcsr = 0;
315 }
316 *flagp = (status | mxcsr) & excepts;
317 return (0);
318 }
319
320 int
fetestexcept(int excepts)321 fetestexcept(int excepts)
322 {
323 __uint32_t mxcsr;
324 __uint16_t status;
325
326 excepts &= FE_ALL_EXCEPT;
327 if (excepts) { /* Do nothing if excepts is 0 */
328 __fnstsw(&status);
329 if (__HAS_SSE()) {
330 __stmxcsr(&mxcsr);
331 } else {
332 mxcsr = 0;
333 }
334 return ((status | mxcsr) & excepts);
335 }
336 return (0);
337 }
338
339 int
fegetround(void)340 fegetround(void)
341 {
342 __uint16_t control;
343
344 /*
345 * We assume that the x87 and the SSE unit agree on the
346 * rounding mode. Reading the control word on the x87 turns
347 * out to be about 5 times faster than reading it on the SSE
348 * unit on an Opteron 244.
349 */
350 __fnstcw(&control);
351 return (control & ROUND_MASK);
352 }
353
354 int
fesetround(int round)355 fesetround(int round)
356 {
357 __uint32_t mxcsr;
358 __uint16_t control;
359
360 if (round & ~ROUND_MASK) {
361 return (-1);
362 } else {
363 __fnstcw(&control);
364 control &= ~ROUND_MASK;
365 control |= round;
366 __fldcw(control);
367 if (__HAS_SSE()) {
368 __stmxcsr(&mxcsr);
369 mxcsr &= ~(ROUND_MASK << _SSE_ROUND_SHIFT);
370 mxcsr |= round << _SSE_ROUND_SHIFT;
371 __ldmxcsr(mxcsr);
372 }
373 return (0);
374 }
375 }
376
377 int
fesetenv(const fenv_t * envp)378 fesetenv(const fenv_t *envp)
379 {
380 fenv_t env = *envp;
381 __uint32_t mxcsr;
382
383 mxcsr = (env.__mxcsr_hi << 16) | (env.__mxcsr_lo);
384 env.__mxcsr_hi = 0xffff;
385 env.__mxcsr_lo = 0xffff;
386 /*
387 * XXX Using fldenvx() instead of fldenv() tells the compiler that this
388 * instruction clobbers the i387 register stack. This happens because
389 * we restore the tag word from the saved environment. Normally, this
390 * would happen anyway and we wouldn't care, because the ABI allows
391 * function calls to clobber the i387 regs. However, fesetenv() is
392 * inlined, so we need to be more careful.
393 */
394 __fldenvx(env);
395 if (__HAS_SSE()) {
396 __ldmxcsr(mxcsr);
397 }
398 return (0);
399 }
400
401 int
fegetexcept(void)402 fegetexcept(void)
403 {
404 __uint16_t control;
405
406 /*
407 * We assume that the masks for the x87 and the SSE unit are
408 * the same.
409 */
410 __fnstcw(&control);
411 return (~control & FE_ALL_EXCEPT);
412 }
413