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