1130144Sdas/*-
2143769Sdas * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG>
3130144Sdas * All rights reserved.
4130144Sdas *
5130144Sdas * Redistribution and use in source and binary forms, with or without
6130144Sdas * modification, are permitted provided that the following conditions
7130144Sdas * are met:
8130144Sdas * 1. Redistributions of source code must retain the above copyright
9130144Sdas *    notice, this list of conditions and the following disclaimer.
10130144Sdas * 2. Redistributions in binary form must reproduce the above copyright
11130144Sdas *    notice, this list of conditions and the following disclaimer in the
12130144Sdas *    documentation and/or other materials provided with the distribution.
13130144Sdas *
14130144Sdas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15130144Sdas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16130144Sdas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17130144Sdas * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18130144Sdas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19130144Sdas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20130144Sdas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21130144Sdas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22130144Sdas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23130144Sdas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24130144Sdas * SUCH DAMAGE.
25130144Sdas *
26130144Sdas * $FreeBSD: stable/11/lib/msun/i387/fenv.c 334771 2018-06-07 09:03:42Z dim $
27130144Sdas */
28130144Sdas
29143769Sdas#include <sys/cdefs.h>
30130144Sdas#include <sys/types.h>
31130144Sdas#include <machine/npx.h>
32130144Sdas
33226218Sdas#define	__fenv_static
34226415Sdas#include "fenv.h"
35226218Sdas
36226218Sdas#ifdef __GNUC_GNU_INLINE__
37226218Sdas#error "This file must be compiled with C99 'inline' semantics"
38226218Sdas#endif
39226218Sdas
40130144Sdasconst fenv_t __fe_dfl_env = {
41143769Sdas	__INITIAL_NPXCW__,
42143769Sdas	0x0000,
43143769Sdas	0x0000,
44143769Sdas	0x1f80,
45130144Sdas	0xffffffff,
46130144Sdas	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
47130144Sdas	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff }
48130144Sdas};
49143769Sdas
50143769Sdasenum __sse_support __has_sse =
51143769Sdas#ifdef __SSE__
52143769Sdas	__SSE_YES;
53143769Sdas#else
54143769Sdas	__SSE_UNK;
55143769Sdas#endif
56143769Sdas
57143769Sdas#define	getfl(x)	__asm __volatile("pushfl\n\tpopl %0" : "=mr" (*(x)))
58143769Sdas#define	setfl(x)	__asm __volatile("pushl %0\n\tpopfl" : : "g" (x))
59143769Sdas#define	cpuid_dx(x)	__asm __volatile("pushl %%ebx\n\tmovl $1, %%eax\n\t"  \
60143769Sdas					 "cpuid\n\tpopl %%ebx"		      \
61143769Sdas					: "=d" (*(x)) : : "eax", "ecx")
62143769Sdas
63143769Sdas/*
64143769Sdas * Test for SSE support on this processor.  We need to do this because
65143769Sdas * we need to use ldmxcsr/stmxcsr to get correct results if any part
66143769Sdas * of the program was compiled to use SSE floating-point, but we can't
67143769Sdas * use SSE on older processors.
68143769Sdas */
69143769Sdasint
70143769Sdas__test_sse(void)
71143769Sdas{
72143769Sdas	int flag, nflag;
73143769Sdas	int dx_features;
74143769Sdas
75143769Sdas	/* Am I a 486? */
76143769Sdas	getfl(&flag);
77143769Sdas	nflag = flag ^ 0x200000;
78143769Sdas	setfl(nflag);
79143769Sdas	getfl(&nflag);
80143769Sdas	if (flag != nflag) {
81143769Sdas		/* Not a 486, so CPUID should work. */
82143769Sdas		cpuid_dx(&dx_features);
83143769Sdas		if (dx_features & 0x2000000) {
84143769Sdas			__has_sse = __SSE_YES;
85143769Sdas			return (1);
86143769Sdas		}
87143769Sdas	}
88143769Sdas	__has_sse = __SSE_NO;
89143769Sdas	return (0);
90143769Sdas}
91143769Sdas
92226218Sdasextern inline int feclearexcept(int __excepts);
93226218Sdasextern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts);
94226218Sdas
95143769Sdasint
96143769Sdasfesetexceptflag(const fexcept_t *flagp, int excepts)
97143769Sdas{
98143769Sdas	fenv_t env;
99203441Skib	__uint32_t mxcsr;
100143769Sdas
101143769Sdas	__fnstenv(&env);
102143769Sdas	env.__status &= ~excepts;
103143769Sdas	env.__status |= *flagp & excepts;
104334771Sdim	__fldenv(&env);
105143769Sdas
106143769Sdas	if (__HAS_SSE()) {
107143769Sdas		__stmxcsr(&mxcsr);
108143769Sdas		mxcsr &= ~excepts;
109143769Sdas		mxcsr |= *flagp & excepts;
110334771Sdim		__ldmxcsr(&mxcsr);
111143769Sdas	}
112143769Sdas
113143769Sdas	return (0);
114143769Sdas}
115143769Sdas
116143769Sdasint
117143769Sdasferaiseexcept(int excepts)
118143769Sdas{
119143769Sdas	fexcept_t ex = excepts;
120143769Sdas
121143769Sdas	fesetexceptflag(&ex, excepts);
122143769Sdas	__fwait();
123143769Sdas	return (0);
124143769Sdas}
125143769Sdas
126226218Sdasextern inline int fetestexcept(int __excepts);
127226218Sdasextern inline int fegetround(void);
128226218Sdasextern inline int fesetround(int __round);
129226218Sdas
130143769Sdasint
131143769Sdasfegetenv(fenv_t *envp)
132143769Sdas{
133203441Skib	__uint32_t mxcsr;
134143769Sdas
135165795Sdas	__fnstenv(envp);
136143769Sdas	/*
137165795Sdas	 * fnstenv masks all exceptions, so we need to restore
138165795Sdas	 * the old control word to avoid this side effect.
139143769Sdas	 */
140334771Sdim	__fldcw(&envp->__control);
141143769Sdas	if (__HAS_SSE()) {
142143769Sdas		__stmxcsr(&mxcsr);
143143769Sdas		__set_mxcsr(*envp, mxcsr);
144143769Sdas	}
145143769Sdas	return (0);
146143769Sdas}
147143769Sdas
148143769Sdasint
149143769Sdasfeholdexcept(fenv_t *envp)
150143769Sdas{
151203441Skib	__uint32_t mxcsr;
152143769Sdas
153143769Sdas	__fnstenv(envp);
154143769Sdas	__fnclex();
155143769Sdas	if (__HAS_SSE()) {
156143769Sdas		__stmxcsr(&mxcsr);
157143769Sdas		__set_mxcsr(*envp, mxcsr);
158143769Sdas		mxcsr &= ~FE_ALL_EXCEPT;
159143769Sdas		mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT;
160334771Sdim		__ldmxcsr(&mxcsr);
161143769Sdas	}
162143769Sdas	return (0);
163143769Sdas}
164143769Sdas
165226218Sdasextern inline int fesetenv(const fenv_t *__envp);
166226218Sdas
167143769Sdasint
168143769Sdasfeupdateenv(const fenv_t *envp)
169143769Sdas{
170203441Skib	__uint32_t mxcsr;
171203441Skib	__uint16_t status;
172143769Sdas
173143769Sdas	__fnstsw(&status);
174143769Sdas	if (__HAS_SSE())
175143769Sdas		__stmxcsr(&mxcsr);
176143769Sdas	else
177143769Sdas		mxcsr = 0;
178143769Sdas	fesetenv(envp);
179143769Sdas	feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT);
180143769Sdas	return (0);
181143769Sdas}
182143769Sdas
183143769Sdasint
184143769Sdas__feenableexcept(int mask)
185143769Sdas{
186203441Skib	__uint32_t mxcsr, omask;
187203441Skib	__uint16_t control;
188143769Sdas
189143769Sdas	mask &= FE_ALL_EXCEPT;
190143769Sdas	__fnstcw(&control);
191143769Sdas	if (__HAS_SSE())
192143769Sdas		__stmxcsr(&mxcsr);
193143769Sdas	else
194143769Sdas		mxcsr = 0;
195226594Sdas	omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
196143769Sdas	control &= ~mask;
197334771Sdim	__fldcw(&control);
198143769Sdas	if (__HAS_SSE()) {
199143769Sdas		mxcsr &= ~(mask << _SSE_EMASK_SHIFT);
200334771Sdim		__ldmxcsr(&mxcsr);
201143769Sdas	}
202226594Sdas	return (omask);
203143769Sdas}
204143769Sdas
205143769Sdasint
206143769Sdas__fedisableexcept(int mask)
207143769Sdas{
208203441Skib	__uint32_t mxcsr, omask;
209203441Skib	__uint16_t control;
210143769Sdas
211143769Sdas	mask &= FE_ALL_EXCEPT;
212143769Sdas	__fnstcw(&control);
213143769Sdas	if (__HAS_SSE())
214143769Sdas		__stmxcsr(&mxcsr);
215143769Sdas	else
216143769Sdas		mxcsr = 0;
217226594Sdas	omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
218143769Sdas	control |= mask;
219334771Sdim	__fldcw(&control);
220143769Sdas	if (__HAS_SSE()) {
221143769Sdas		mxcsr |= mask << _SSE_EMASK_SHIFT;
222334771Sdim		__ldmxcsr(&mxcsr);
223143769Sdas	}
224226594Sdas	return (omask);
225143769Sdas}
226226372Sdas
227226372Sdas__weak_reference(__feenableexcept, feenableexcept);
228226372Sdas__weak_reference(__fedisableexcept, fedisableexcept);
229