xref: /freebsd/lib/msun/i387/fenv.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
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/cdefs.h>
30 #include <sys/types.h>
31 #include <machine/npx.h>
32 
33 #define	__fenv_static
34 #include "fenv.h"
35 
36 #ifdef __GNUC_GNU_INLINE__
37 #error "This file must be compiled with C99 'inline' semantics"
38 #endif
39 
40 const fenv_t __fe_dfl_env = {
41 	__INITIAL_NPXCW__,
42 	0x0000,
43 	0x0000,
44 	0x1f80,
45 	0xffffffff,
46 	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
47 	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff }
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 extern inline int feclearexcept(int __excepts);
93 extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts);
94 
95 int
96 fesetexceptflag(const fexcept_t *flagp, int excepts)
97 {
98 	fenv_t env;
99 	__uint32_t mxcsr;
100 
101 	__fnstenv(&env);
102 	env.__status &= ~excepts;
103 	env.__status |= *flagp & excepts;
104 	__fldenv(&env);
105 
106 	if (__HAS_SSE()) {
107 		__stmxcsr(&mxcsr);
108 		mxcsr &= ~excepts;
109 		mxcsr |= *flagp & excepts;
110 		__ldmxcsr(&mxcsr);
111 	}
112 
113 	return (0);
114 }
115 
116 int
117 feraiseexcept(int excepts)
118 {
119 	fexcept_t ex = excepts;
120 
121 	fesetexceptflag(&ex, excepts);
122 	__fwait();
123 	return (0);
124 }
125 
126 extern inline int fetestexcept(int __excepts);
127 extern inline int fegetround(void);
128 extern inline int fesetround(int __round);
129 
130 int
131 fegetenv(fenv_t *envp)
132 {
133 	__uint32_t mxcsr;
134 
135 	__fnstenv(envp);
136 	/*
137 	 * fnstenv masks all exceptions, so we need to restore
138 	 * the old control word to avoid this side effect.
139 	 */
140 	__fldcw(&envp->__control);
141 	if (__HAS_SSE()) {
142 		__stmxcsr(&mxcsr);
143 		__set_mxcsr(*envp, mxcsr);
144 	}
145 	return (0);
146 }
147 
148 int
149 feholdexcept(fenv_t *envp)
150 {
151 	__uint32_t mxcsr;
152 
153 	__fnstenv(envp);
154 	__fnclex();
155 	if (__HAS_SSE()) {
156 		__stmxcsr(&mxcsr);
157 		__set_mxcsr(*envp, mxcsr);
158 		mxcsr &= ~FE_ALL_EXCEPT;
159 		mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT;
160 		__ldmxcsr(&mxcsr);
161 	}
162 	return (0);
163 }
164 
165 extern inline int fesetenv(const fenv_t *__envp);
166 
167 int
168 feupdateenv(const fenv_t *envp)
169 {
170 	__uint32_t mxcsr;
171 	__uint16_t status;
172 
173 	__fnstsw(&status);
174 	if (__HAS_SSE())
175 		__stmxcsr(&mxcsr);
176 	else
177 		mxcsr = 0;
178 	fesetenv(envp);
179 	feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT);
180 	return (0);
181 }
182 
183 int
184 __feenableexcept(int mask)
185 {
186 	__uint32_t mxcsr, omask;
187 	__uint16_t control;
188 
189 	mask &= FE_ALL_EXCEPT;
190 	__fnstcw(&control);
191 	if (__HAS_SSE())
192 		__stmxcsr(&mxcsr);
193 	else
194 		mxcsr = 0;
195 	omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
196 	control &= ~mask;
197 	__fldcw(&control);
198 	if (__HAS_SSE()) {
199 		mxcsr &= ~(mask << _SSE_EMASK_SHIFT);
200 		__ldmxcsr(&mxcsr);
201 	}
202 	return (omask);
203 }
204 
205 int
206 __fedisableexcept(int mask)
207 {
208 	__uint32_t mxcsr, omask;
209 	__uint16_t control;
210 
211 	mask &= FE_ALL_EXCEPT;
212 	__fnstcw(&control);
213 	if (__HAS_SSE())
214 		__stmxcsr(&mxcsr);
215 	else
216 		mxcsr = 0;
217 	omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
218 	control |= mask;
219 	__fldcw(&control);
220 	if (__HAS_SSE()) {
221 		mxcsr |= mask << _SSE_EMASK_SHIFT;
222 		__ldmxcsr(&mxcsr);
223 	}
224 	return (omask);
225 }
226 
227 __weak_reference(__feenableexcept, feenableexcept);
228 __weak_reference(__fedisableexcept, fedisableexcept);
229