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