3 Copyright 2010, 2011 Red Hat, Inc.
5 This file is part of Cygwin.
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
17 /* Mask and shift amount for rounding bits. */
18 #define FE_CW_ROUND_MASK (0x0c00)
19 #define FE_CW_ROUND_SHIFT (10)
20 /* Same, for SSE MXCSR. */
21 #define FE_MXCSR_ROUND_MASK (0x6000)
22 #define FE_MXCSR_ROUND_SHIFT (13)
24 /* Mask and shift amount for precision bits. */
25 #define FE_CW_PREC_MASK (0x0300)
26 #define FE_CW_PREC_SHIFT (8)
28 /* In x87, exception status bits and mask bits occupy
29 corresponding bit positions in the status and control
30 registers, respectively. In SSE, they are both located
31 in the control-and-status register, with the status bits
32 corresponding to the x87 positions, and the mask bits
33 shifted by this amount to the left. */
34 #define FE_SSE_EXCEPT_MASK_SHIFT (7)
36 /* These are writable so we can initialise them at startup. */
37 static fenv_t fe_dfl_env;
38 static fenv_t fe_nomask_env;
40 /* These pointers provide the outside world with read-only access to them. */
41 const fenv_t *_fe_dfl_env = &fe_dfl_env;
42 const fenv_t *_fe_nomask_env = &fe_nomask_env;
44 /* Although Cygwin assumes i686 or above (hence SSE available) these
45 days, and the compiler feels free to use it (depending on compile-
46 time flags of course), we should avoid needlessly breaking any
47 purely integer mode apps (or apps compiled with -mno-sse), so we
48 only manage SSE state in this fenv module if we detect that SSE
49 instructions are available at runtime. If we didn't do this, all
50 applications run on older machines would bomb out with an invalid
51 instruction exception right at startup; let's not be *that* WJM! */
52 static bool use_sse = false;
54 /* This function enables traps for each of the exceptions as indicated
55 by the parameter except. The individual exceptions are described in
56 [ ... glibc manual xref elided ...]. Only the specified exceptions are
57 enabled, the status of the other exceptions is not changed.
58 The function returns the previous enabled exceptions in case the
59 operation was successful, -1 otherwise. */
61 feenableexcept (int excepts)
63 unsigned short cw, old_cw;
64 unsigned int mxcsr = 0;
66 if (excepts & ~FE_ALL_EXCEPT)
69 /* Get control words. */
70 __asm__ volatile ("fnstcw %0" : "=m" (old_cw) : );
72 __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
74 /* Enable exceptions by clearing mask bits. */
75 cw = old_cw & ~excepts;
76 mxcsr &= ~(excepts << FE_SSE_EXCEPT_MASK_SHIFT);
78 /* Store updated control words. */
79 __asm__ volatile ("fldcw %0" :: "m" (cw));
81 __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));
83 /* Return old value. We assume SSE and x87 stay in sync. Note that
84 we are returning a mask of enabled exceptions, which is the opposite
85 of the flags in the register, which are set to disable (mask) their
86 related exceptions. */
87 return (~old_cw) & FE_ALL_EXCEPT;
90 /* This function disables traps for each of the exceptions as indicated
91 by the parameter except. The individual exceptions are described in
92 [ ... glibc manual xref elided ...]. Only the specified exceptions are
93 disabled, the status of the other exceptions is not changed.
94 The function returns the previous enabled exceptions in case the
95 operation was successful, -1 otherwise. */
97 fedisableexcept (int excepts)
99 unsigned short cw, old_cw;
100 unsigned int mxcsr = 0;
102 if (excepts & ~FE_ALL_EXCEPT)
105 /* Get control words. */
106 __asm__ volatile ("fnstcw %0" : "=m" (old_cw) : );
108 __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
110 /* Disable exceptions by setting mask bits. */
111 cw = old_cw | excepts;
112 mxcsr |= (excepts << FE_SSE_EXCEPT_MASK_SHIFT);
114 /* Store updated control words. */
115 __asm__ volatile ("fldcw %0" :: "m" (cw));
117 __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));
119 /* Return old value. We assume SSE and x87 stay in sync. Note that
120 we are returning a mask of enabled exceptions, which is the opposite
121 of the flags in the register, which are set to disable (mask) their
122 related exceptions. */
123 return (~old_cw) & FE_ALL_EXCEPT;
126 /* This function returns a bitmask of all currently enabled exceptions. It
127 returns -1 in case of failure. */
133 /* Get control word. We assume SSE and x87 stay in sync. */
134 __asm__ volatile ("fnstcw %0" : "=m" (cw) : );
136 /* Exception is *dis*abled when mask bit is set. */
137 return (~cw) & FE_ALL_EXCEPT;
140 /* Store the floating-point environment in the variable pointed to by envp.
141 The function returns zero in case the operation was successful, a non-zero
144 fegetenv (fenv_t *envp)
146 __asm__ volatile ("fnstenv %0" : "=m" (envp->_fpu) : );
148 __asm__ volatile ("stmxcsr %0" : "=m" (envp->_sse_mxcsr) : );
152 /* Store the current floating-point environment in the object pointed to
153 by envp. Then clear all exception flags, and set the FPU to trap no
154 exceptions. Not all FPUs support trapping no exceptions; if feholdexcept
155 cannot set this mode, it returns nonzero value. If it succeeds, it
158 feholdexcept (fenv_t *envp)
162 mxcsr = envp->_sse_mxcsr & ~FE_ALL_EXCEPT;
164 __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));
165 __asm__ volatile ("fnclex");
166 fedisableexcept (FE_ALL_EXCEPT);
170 /* Set the floating-point environment to that described by envp. The
171 function returns zero in case the operation was successful, a non-zero
174 fesetenv (const fenv_t *envp)
176 __asm__ volatile ("fldenv %0" :: "m" (envp->_fpu) );
178 __asm__ volatile ("ldmxcsr %0" :: "m" (envp->_sse_mxcsr));
182 /* Like fesetenv, this function sets the floating-point environment to
183 that described by envp. However, if any exceptions were flagged in the
184 status word before feupdateenv was called, they remain flagged after
185 the call. In other words, after feupdateenv is called, the status
186 word is the bitwise OR of the previous status word and the one saved
187 in envp. The function returns zero in case the operation was successful,
188 a non-zero value otherwise. */
190 feupdateenv (const fenv_t *envp)
193 unsigned int mxcsr = 0;
196 /* Don't want to modify *envp, but want to update environment atomically,
197 so take a copy and merge the existing exceptions into it. */
198 memcpy (&envcopy, envp, sizeof *envp);
199 __asm__ volatile ("fnstsw %0" : "=m" (sw) : );
201 __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
202 envcopy._fpu._fpu_sw |= (sw & FE_ALL_EXCEPT);
203 envcopy._sse_mxcsr |= (mxcsr & FE_ALL_EXCEPT);
205 return fesetenv (&envcopy);
208 /* This function clears all of the supported exception flags indicated by
209 excepts. The function returns zero in case the operation was successful,
210 a non-zero value otherwise. */
212 feclearexcept (int excepts)
216 if (excepts & ~FE_ALL_EXCEPT)
219 /* Need to save/restore whole environment to modify status word. */
222 /* Mask undesired bits out. */
223 fenv._fpu._fpu_sw &= ~excepts;
224 fenv._sse_mxcsr &= ~excepts;
226 /* Set back into FPU state. */
227 return fesetenv (&fenv);
230 /* This function raises the supported exceptions indicated by
231 excepts. If more than one exception bit in excepts is set the order
232 in which the exceptions are raised is undefined except that overflow
233 (FE_OVERFLOW) or underflow (FE_UNDERFLOW) are raised before inexact
234 (FE_INEXACT). Whether for overflow or underflow the inexact exception
235 is also raised is also implementation dependent. The function returns
236 zero in case the operation was successful, a non-zero value otherwise. */
238 feraiseexcept (int excepts)
242 if (excepts & ~FE_ALL_EXCEPT)
245 /* Need to save/restore whole environment to modify status word. */
246 __asm__ volatile ("fnstenv %0" : "=m" (fenv) : );
248 /* Set desired exception bits. */
249 fenv._fpu._fpu_sw |= excepts;
251 /* Set back into FPU state. */
252 __asm__ volatile ("fldenv %0" :: "m" (fenv));
254 /* And trigger them - whichever are unmasked. */
255 __asm__ volatile ("fwait");
260 /* Test whether the exception flags indicated by the parameter except
261 are currently set. If any of them are, a nonzero value is returned
262 which specifies which exceptions are set. Otherwise the result is zero. */
264 fetestexcept (int excepts)
267 unsigned int mxcsr = 0;
269 if (excepts & ~FE_ALL_EXCEPT)
272 /* Get status registers. */
273 __asm__ volatile ("fnstsw %0" : "=m" (sw) : );
275 __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
277 /* Mask undesired bits out and return result. */
278 return (sw | mxcsr) & excepts;
280 /* This function stores in the variable pointed to by flagp an
281 implementation-defined value representing the current setting of the
282 exception flags indicated by excepts. The function returns zero in
283 case the operation was successful, a non-zero value otherwise. */
285 fegetexceptflag (fexcept_t *flagp, int excepts)
288 unsigned int mxcsr = 0;
290 if (excepts & ~FE_ALL_EXCEPT)
293 /* Get status registers. */
294 __asm__ volatile ("fnstsw %0" : "=m" (sw) : );
296 __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
298 /* Mask undesired bits out and set result struct. */
299 flagp->_fpu_exceptions = (sw & excepts);
300 flagp->_sse_exceptions = (mxcsr & excepts);
305 /* This function restores the flags for the exceptions indicated by
306 excepts to the values stored in the variable pointed to by flagp. */
308 fesetexceptflag (const fexcept_t *flagp, int excepts)
312 if (excepts & ~FE_ALL_EXCEPT)
315 /* Need to save/restore whole environment to modify status word. */
318 /* Set/Clear desired exception bits. */
319 fenv._fpu._fpu_sw &= ~excepts;
320 fenv._fpu._fpu_sw |= (excepts & flagp->_fpu_exceptions);
321 fenv._sse_mxcsr &= ~excepts;
322 fenv._sse_mxcsr |= (excepts & flagp->_sse_exceptions);
324 /* Set back into FPU state. */
325 return fesetenv (&fenv);
328 /* Returns the currently selected rounding mode, represented by one of the
329 values of the defined rounding mode macros. */
335 /* Get control word. We assume SSE and x87 stay in sync. */
336 __asm__ volatile ("fnstcw %0" : "=m" (cw) : );
338 return (cw & FE_CW_ROUND_MASK) >> FE_CW_ROUND_SHIFT;
341 /* Changes the currently selected rounding mode to round. If round does
342 not correspond to one of the supported rounding modes nothing is changed.
343 fesetround returns zero if it changed the rounding mode, a nonzero value
344 if the mode is not supported. */
346 fesetround (int round)
349 unsigned int mxcsr = 0;
351 /* Will succeed for any valid value of the input parameter. */
352 if (round & ~(FE_CW_ROUND_MASK >> FE_CW_PREC_SHIFT))
355 /* Get control words. */
356 __asm__ volatile ("fnstcw %0" : "=m" (cw) : );
358 __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
361 cw &= ~FE_CW_ROUND_MASK;
362 cw |= (round << FE_CW_ROUND_SHIFT);
363 mxcsr &= ~FE_MXCSR_ROUND_MASK;
364 mxcsr |= (round << FE_MXCSR_ROUND_SHIFT);
366 /* Set back into FPU state. */
367 __asm__ volatile ("fldcw %0" :: "m" (cw));
369 __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));
371 /* Indicate success. */
375 /* Returns the currently selected precision, represented by one of the
376 values of the defined precision macros. */
382 /* Get control word. */
383 __asm__ volatile ("fnstcw %0" : "=m" (cw) : );
385 return (cw & FE_CW_PREC_MASK) >> FE_CW_PREC_SHIFT;
388 /* Changes the currently selected precision to prec. If prec does not
389 correspond to one of the supported rounding modes nothing is changed.
390 fesetprec returns zero if it changed the precision, or a nonzero value
391 if the mode is not supported. */
397 /* Will succeed for any valid value of the input parameter. */
398 if (prec & ~(FE_CW_PREC_MASK >> FE_CW_PREC_SHIFT) || prec == FE_RESERVEDPREC)
401 /* Get control word. */
402 __asm__ volatile ("fnstcw %0" : "=m" (cw) : );
405 cw &= ~FE_CW_PREC_MASK;
406 cw |= (prec << FE_CW_PREC_SHIFT);
408 /* Set back into FPU state. */
409 __asm__ volatile ("fldcw %0" :: "m" (cw));
411 /* Indicate success. */
415 /* Set up the FPU and SSE environment at the start of execution. */
419 unsigned int edx, eax, mxcsr;
421 /* Check for presence of SSE: invoke CPUID #1, check EDX bit 25. */
423 __asm__ volatile ("cpuid" : "=d" (edx), "+a" (eax) :: "%ecx", "%ebx");
424 /* If this flag isn't set, or if the OS doesn't support SSE (NT4, at least
425 up to SP4) we'll avoid trying to execute any SSE. */
426 if ((edx & (1 << 25)) != 0)
429 /* Reset FPU: extended prec, all exceptions cleared and masked off. */
430 __asm__ volatile ("fninit");
431 /* The default cw value, 0x37f, is rounding mode zero. The MXCSR has
432 no precision control, so the only thing to do is set the exception
434 mxcsr = FE_ALL_EXCEPT << FE_SSE_EXCEPT_MASK_SHIFT;
436 __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));
438 /* Setup unmasked environment. */
439 feenableexcept (FE_ALL_EXCEPT);
440 fegetenv (&fe_nomask_env);
442 /* Restore default exception masking (all masked). */
443 fedisableexcept (FE_ALL_EXCEPT);
445 /* Finally cache state as default environment. */
446 fegetenv (&fe_dfl_env);