OSDN Git Service

Initial checkin of text Corinna sent to cygwin-announce.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / fenv.cc
1 /* fenv.cc
2
3    Copyright 2010, 2011 Red Hat, Inc.
4
5 This file is part of Cygwin.
6
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
9 details. */
10
11 #include "winsup.h"
12 #include "fenv.h"
13 #include "errno.h"
14 #include "wincap.h"
15 #include <string.h>
16
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)
23
24 /*  Mask and shift amount for precision bits.  */
25 #define FE_CW_PREC_MASK         (0x0300)
26 #define FE_CW_PREC_SHIFT        (8)
27
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)
35
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;
39
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;
43
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;
53
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.  */
60 int
61 feenableexcept (int excepts)
62 {
63   unsigned short cw, old_cw;
64   unsigned int mxcsr = 0;
65
66   if (excepts & ~FE_ALL_EXCEPT)
67     return -1;
68
69   /* Get control words.  */
70   __asm__ volatile ("fnstcw %0" : "=m" (old_cw) : );
71   if (use_sse)
72     __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
73
74   /* Enable exceptions by clearing mask bits.  */
75   cw = old_cw & ~excepts;
76   mxcsr &= ~(excepts << FE_SSE_EXCEPT_MASK_SHIFT);
77
78   /* Store updated control words.  */
79   __asm__ volatile ("fldcw %0" :: "m" (cw));
80   if (use_sse)
81     __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));
82
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;
88 }
89
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.  */
96 int
97 fedisableexcept (int excepts)
98 {
99   unsigned short cw, old_cw;
100   unsigned int mxcsr = 0;
101
102   if (excepts & ~FE_ALL_EXCEPT)
103     return -1;
104
105   /* Get control words.  */
106   __asm__ volatile ("fnstcw %0" : "=m" (old_cw) : );
107   if (use_sse)
108     __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
109
110   /* Disable exceptions by setting mask bits.  */
111   cw = old_cw | excepts;
112   mxcsr |= (excepts << FE_SSE_EXCEPT_MASK_SHIFT);
113
114   /* Store updated control words.  */
115   __asm__ volatile ("fldcw %0" :: "m" (cw));
116   if (use_sse)
117     __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));
118
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;
124 }
125
126 /*  This function returns a bitmask of all currently enabled exceptions. It
127    returns -1 in case of failure.  */
128 int
129 fegetexcept (void)
130 {
131   unsigned short cw;
132
133   /* Get control word.  We assume SSE and x87 stay in sync.  */
134   __asm__ volatile ("fnstcw %0" : "=m" (cw) : );
135
136   /* Exception is *dis*abled when mask bit is set.  */
137   return (~cw) & FE_ALL_EXCEPT;
138 }
139
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
142    value otherwise.  */
143 int
144 fegetenv (fenv_t *envp)
145 {
146   __asm__ volatile ("fnstenv %0" : "=m" (envp->_fpu) : );
147   if (use_sse)
148     __asm__ volatile ("stmxcsr %0" : "=m" (envp->_sse_mxcsr) : );
149   return 0;
150 }
151
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
156    returns zero.  */
157 int
158 feholdexcept (fenv_t *envp)
159 {
160   unsigned int mxcsr;
161   fegetenv (envp);
162   mxcsr = envp->_sse_mxcsr & ~FE_ALL_EXCEPT;
163   if (use_sse)
164     __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));
165   __asm__ volatile ("fnclex");
166   fedisableexcept (FE_ALL_EXCEPT);
167   return 0;
168 }
169
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
172    value otherwise.  */
173 int
174 fesetenv (const fenv_t *envp)
175 {
176   __asm__ volatile ("fldenv %0" :: "m" (envp->_fpu) );
177   if (use_sse)
178     __asm__ volatile ("ldmxcsr %0" :: "m" (envp->_sse_mxcsr));
179   return 0;
180 }
181
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.  */
189 int
190 feupdateenv (const fenv_t *envp)
191 {
192   fenv_t envcopy;
193   unsigned int mxcsr = 0;
194   unsigned short sw;
195
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) : );
200   if (use_sse)
201     __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
202   envcopy._fpu._fpu_sw |= (sw & FE_ALL_EXCEPT);
203   envcopy._sse_mxcsr |= (mxcsr & FE_ALL_EXCEPT);
204
205   return fesetenv (&envcopy);
206 }
207
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.  */
211 int
212 feclearexcept (int excepts)
213 {
214   fenv_t fenv;
215
216   if (excepts & ~FE_ALL_EXCEPT)
217     return EINVAL;
218
219   /* Need to save/restore whole environment to modify status word.  */
220   fegetenv (&fenv);
221
222   /* Mask undesired bits out.  */
223   fenv._fpu._fpu_sw &= ~excepts;
224   fenv._sse_mxcsr &= ~excepts;
225
226   /* Set back into FPU state.  */
227   return fesetenv (&fenv);
228 }
229
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.  */
237 int
238 feraiseexcept (int excepts)
239 {
240   fenv_t fenv;
241
242   if (excepts & ~FE_ALL_EXCEPT)
243     return EINVAL;
244
245   /* Need to save/restore whole environment to modify status word.  */
246   __asm__ volatile ("fnstenv %0" : "=m" (fenv) : );
247
248   /* Set desired exception bits.  */
249   fenv._fpu._fpu_sw |= excepts;
250
251   /* Set back into FPU state.  */
252   __asm__ volatile ("fldenv %0" :: "m" (fenv));
253
254   /* And trigger them - whichever are unmasked.  */
255   __asm__ volatile ("fwait");
256
257   return 0;
258 }
259
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.  */
263 int
264 fetestexcept (int excepts)
265 {
266   unsigned short sw;
267   unsigned int mxcsr = 0;
268
269   if (excepts & ~FE_ALL_EXCEPT)
270     return EINVAL;
271
272   /* Get status registers.  */
273   __asm__ volatile ("fnstsw %0" : "=m" (sw) : );
274   if (use_sse)
275     __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
276
277   /* Mask undesired bits out and return result.  */
278   return (sw | mxcsr) & excepts;
279 }
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.  */
284 int
285 fegetexceptflag (fexcept_t *flagp, int excepts)
286 {
287   unsigned short sw;
288   unsigned int mxcsr = 0;
289
290   if (excepts & ~FE_ALL_EXCEPT)
291     return EINVAL;
292
293   /* Get status registers.  */
294   __asm__ volatile ("fnstsw %0" : "=m" (sw) : );
295   if (use_sse)
296     __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
297
298   /* Mask undesired bits out and set result struct.  */
299   flagp->_fpu_exceptions = (sw & excepts);
300   flagp->_sse_exceptions = (mxcsr & excepts);
301
302   return 0;
303 }
304
305 /*  This function restores the flags for the exceptions indicated by
306    excepts to the values stored in the variable pointed to by flagp.  */
307 int
308 fesetexceptflag (const fexcept_t *flagp, int excepts)
309 {
310   fenv_t fenv;
311
312   if (excepts & ~FE_ALL_EXCEPT)
313     return EINVAL;
314
315   /* Need to save/restore whole environment to modify status word.  */
316   fegetenv (&fenv);
317
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);
323
324   /* Set back into FPU state.  */
325   return fesetenv (&fenv);
326 }
327
328 /*  Returns the currently selected rounding mode, represented by one of the
329    values of the defined rounding mode macros.  */
330 int
331 fegetround (void)
332 {
333   unsigned short cw;
334
335   /* Get control word.  We assume SSE and x87 stay in sync.  */
336   __asm__ volatile ("fnstcw %0" : "=m" (cw) : );
337
338   return (cw & FE_CW_ROUND_MASK) >> FE_CW_ROUND_SHIFT;
339 }
340
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.  */
345 int
346 fesetround (int round)
347 {
348   unsigned short cw;
349   unsigned int mxcsr = 0;
350
351   /* Will succeed for any valid value of the input parameter.  */
352   if (round & ~(FE_CW_ROUND_MASK >> FE_CW_PREC_SHIFT))
353     return EINVAL;
354
355   /* Get control words.  */
356   __asm__ volatile ("fnstcw %0" : "=m" (cw) : );
357   if (use_sse)
358     __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
359
360   /* Twiddle bits.  */
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);
365
366   /* Set back into FPU state.  */
367   __asm__ volatile ("fldcw %0" :: "m" (cw));
368   if (use_sse)
369     __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));
370
371   /* Indicate success.  */
372   return 0;
373 }
374
375 /*  Returns the currently selected precision, represented by one of the
376    values of the defined precision macros.  */
377 int
378 fegetprec (void)
379 {
380   unsigned short cw;
381
382   /* Get control word.  */
383   __asm__ volatile ("fnstcw %0" : "=m" (cw) : );
384
385   return (cw & FE_CW_PREC_MASK) >> FE_CW_PREC_SHIFT;
386 }
387
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.  */
392 int
393 fesetprec (int prec)
394 {
395   unsigned short cw;
396
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)
399     return EINVAL;
400
401   /* Get control word.  */
402   __asm__ volatile ("fnstcw %0" : "=m" (cw) : );
403
404   /* Twiddle bits.  */
405   cw &= ~FE_CW_PREC_MASK;
406   cw |= (prec << FE_CW_PREC_SHIFT);
407
408   /* Set back into FPU state.  */
409   __asm__ volatile ("fldcw %0" :: "m" (cw));
410
411   /* Indicate success.  */
412   return 0;
413 }
414
415 /*  Set up the FPU and SSE environment at the start of execution.  */
416 void
417 _feinitialise (void)
418 {
419   unsigned int edx, eax, mxcsr;
420
421   /* Check for presence of SSE: invoke CPUID #1, check EDX bit 25.  */
422   eax = 1;
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)
427     use_sse = true;
428
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
433      mask bits.  */
434   mxcsr = FE_ALL_EXCEPT << FE_SSE_EXCEPT_MASK_SHIFT;
435   if (use_sse)
436     __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));
437
438   /* Setup unmasked environment.  */
439   feenableexcept (FE_ALL_EXCEPT);
440   fegetenv (&fe_nomask_env);
441
442   /* Restore default exception masking (all masked).  */
443   fedisableexcept (FE_ALL_EXCEPT);
444
445   /* Finally cache state as default environment. */
446   fegetenv (&fe_dfl_env);
447 }
448