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