Welcome to mirror list, hosted at ThFree Co, Russian Federation.

fenv.cc « cygwin « winsup - cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 5443681f983e32ac664e133f15285342b5a426e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
/* fenv.cc

   Copyright 2010, 2011, 2012 Red Hat, Inc.

This file is part of Cygwin.

This software is a copyrighted work licensed under the terms of the
Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
details. */

#include "winsup.h"
#include "fenv.h"
#include "errno.h"
#include "wincap.h"
#include <string.h>

/*  Mask and shift amount for rounding bits.  */
#define FE_CW_ROUND_MASK	(0x0c00)
#define FE_CW_ROUND_SHIFT	(10)
/*  Same, for SSE MXCSR.  */
#define FE_MXCSR_ROUND_MASK	(0x6000)
#define FE_MXCSR_ROUND_SHIFT	(13)

/*  Mask and shift amount for precision bits.  */
#define FE_CW_PREC_MASK		(0x0300)
#define FE_CW_PREC_SHIFT	(8)

/*  In x87, exception status bits and mask bits occupy
   corresponding bit positions in the status and control
   registers, respectively.  In SSE, they are both located
   in the control-and-status register, with the status bits
   corresponding to the x87 positions, and the mask bits
   shifted by this amount to the left.  */
#define FE_SSE_EXCEPT_MASK_SHIFT (7)

/* These are writable so we can initialise them at startup.  */
static fenv_t fe_dfl_env;
static fenv_t fe_nomask_env;

/* These pointers provide the outside world with read-only access to them.  */
const fenv_t *_fe_dfl_env = &fe_dfl_env;
const fenv_t *_fe_nomask_env = &fe_nomask_env;

/*  Although Cygwin assumes i686 or above (hence SSE available) these
   days, and the compiler feels free to use it (depending on compile-
   time flags of course), we should avoid needlessly breaking any
   purely integer mode apps (or apps compiled with -mno-sse), so we
   only manage SSE state in this fenv module if we detect that SSE
   instructions are available at runtime.  If we didn't do this, all
   applications run on older machines would bomb out with an invalid
   instruction exception right at startup; let's not be *that* WJM!  */
static bool use_sse = false;

/*  This function enables traps for each of the exceptions as indicated
   by the parameter except. The individual exceptions are described in
   [ ... glibc manual xref elided ...]. Only the specified exceptions are
   enabled, the status of the other exceptions is not changed.
    The function returns the previous enabled exceptions in case the
   operation was successful, -1 otherwise.  */
int
feenableexcept (int excepts)
{
  unsigned short cw, old_cw;
  unsigned int mxcsr = 0;

  if (excepts & ~FE_ALL_EXCEPT)
    return -1;

  /* Get control words.  */
  __asm__ volatile ("fnstcw %0" : "=m" (old_cw) : );
  if (use_sse)
    __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );

  /* Enable exceptions by clearing mask bits.  */
  cw = old_cw & ~excepts;
  mxcsr &= ~(excepts << FE_SSE_EXCEPT_MASK_SHIFT);

  /* Store updated control words.  */
  __asm__ volatile ("fldcw %0" :: "m" (cw));
  if (use_sse)
    __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));

  /* Return old value.  We assume SSE and x87 stay in sync.  Note that
     we are returning a mask of enabled exceptions, which is the opposite
     of the flags in the register, which are set to disable (mask) their
     related exceptions.  */
  return (~old_cw) & FE_ALL_EXCEPT;
}

/*  This function disables traps for each of the exceptions as indicated
   by the parameter except. The individual exceptions are described in
   [ ... glibc manual xref elided ...]. Only the specified exceptions are
   disabled, the status of the other exceptions is not changed.
    The function returns the previous enabled exceptions in case the
   operation was successful, -1 otherwise.  */
int
fedisableexcept (int excepts)
{
  unsigned short cw, old_cw;
  unsigned int mxcsr = 0;

  if (excepts & ~FE_ALL_EXCEPT)
    return -1;

  /* Get control words.  */
  __asm__ volatile ("fnstcw %0" : "=m" (old_cw) : );
  if (use_sse)
    __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );

  /* Disable exceptions by setting mask bits.  */
  cw = old_cw | excepts;
  mxcsr |= (excepts << FE_SSE_EXCEPT_MASK_SHIFT);

  /* Store updated control words.  */
  __asm__ volatile ("fldcw %0" :: "m" (cw));
  if (use_sse)
    __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));

  /* Return old value.  We assume SSE and x87 stay in sync.  Note that
     we are returning a mask of enabled exceptions, which is the opposite
     of the flags in the register, which are set to disable (mask) their
     related exceptions.  */
  return (~old_cw) & FE_ALL_EXCEPT;
}

/*  This function returns a bitmask of all currently enabled exceptions. It
   returns -1 in case of failure.  */
int
fegetexcept (void)
{
  unsigned short cw;

  /* Get control word.  We assume SSE and x87 stay in sync.  */
  __asm__ volatile ("fnstcw %0" : "=m" (cw) : );

  /* Exception is *dis*abled when mask bit is set.  */
  return (~cw) & FE_ALL_EXCEPT;
}

/*  Store the floating-point environment in the variable pointed to by envp.
   The function returns zero in case the operation was successful, a non-zero
   value otherwise.  */
int
fegetenv (fenv_t *envp)
{
  __asm__ volatile ("fnstenv %0" : "=m" (envp->_fpu) : );
  if (use_sse)
    __asm__ volatile ("stmxcsr %0" : "=m" (envp->_sse_mxcsr) : );
  return 0;
}

/*  Store the current floating-point environment in the object pointed to
   by envp. Then clear all exception flags, and set the FPU to trap no
   exceptions.  Not all FPUs support trapping no exceptions; if feholdexcept
   cannot set this mode, it returns nonzero value.  If it succeeds, it
   returns zero.  */
int
feholdexcept (fenv_t *envp)
{
  unsigned int mxcsr;
  fegetenv (envp);
  mxcsr = envp->_sse_mxcsr & ~FE_ALL_EXCEPT;
  if (use_sse)
    __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));
  __asm__ volatile ("fnclex");
  fedisableexcept (FE_ALL_EXCEPT);
  return 0;
}

/*  Set the floating-point environment to that described by envp.  The
   function returns zero in case the operation was successful, a non-zero
   value otherwise.  */
int
fesetenv (const fenv_t *envp)
{
  __asm__ volatile ("fldenv %0" :: "m" (envp->_fpu) );
  if (use_sse)
    __asm__ volatile ("ldmxcsr %0" :: "m" (envp->_sse_mxcsr));
  return 0;
}

/*  Like fesetenv, this function sets the floating-point environment to
   that described by envp. However, if any exceptions were flagged in the
   status word before feupdateenv was called, they remain flagged after
   the call.  In other words, after feupdateenv is called, the status
   word is the bitwise OR of the previous status word and the one saved
   in envp.  The function returns zero in case the operation was successful,
   a non-zero value otherwise.  */
int
feupdateenv (const fenv_t *envp)
{
  fenv_t envcopy;
  unsigned int mxcsr = 0;
  unsigned short sw;

  /* Don't want to modify *envp, but want to update environment atomically,
     so take a copy and merge the existing exceptions into it.  */
  memcpy (&envcopy, envp, sizeof *envp);
  __asm__ volatile ("fnstsw %0" : "=m" (sw) : );
  if (use_sse)
    __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );
  envcopy._fpu._fpu_sw |= (sw & FE_ALL_EXCEPT);
  envcopy._sse_mxcsr |= (mxcsr & FE_ALL_EXCEPT);

  return fesetenv (&envcopy);
}

/*  This function clears all of the supported exception flags indicated by
   excepts.  The function returns zero in case the operation was successful,
   a non-zero value otherwise.  */
int
feclearexcept (int excepts)
{
  fenv_t fenv;

  if (excepts & ~FE_ALL_EXCEPT)
    return EINVAL;

  /* Need to save/restore whole environment to modify status word.  */
  fegetenv (&fenv);

  /* Mask undesired bits out.  */
  fenv._fpu._fpu_sw &= ~excepts;
  fenv._sse_mxcsr &= ~excepts;

  /* Set back into FPU state.  */
  return fesetenv (&fenv);
}

/*  This function raises the supported exceptions indicated by
   excepts.  If more than one exception bit in excepts is set the order
   in which the exceptions are raised is undefined except that overflow
   (FE_OVERFLOW) or underflow (FE_UNDERFLOW) are raised before inexact
   (FE_INEXACT). Whether for overflow or underflow the inexact exception
   is also raised is also implementation dependent.  The function returns
   zero in case the operation was successful, a non-zero value otherwise.  */
int
feraiseexcept (int excepts)
{
  fenv_t fenv;

  if (excepts & ~FE_ALL_EXCEPT)
    return EINVAL;

  /* Need to save/restore whole environment to modify status word.  */
  __asm__ volatile ("fnstenv %0" : "=m" (fenv) : );

  /* Set desired exception bits.  */
  fenv._fpu._fpu_sw |= excepts;

  /* Set back into FPU state.  */
  __asm__ volatile ("fldenv %0" :: "m" (fenv));

  /* And trigger them - whichever are unmasked.  */
  __asm__ volatile ("fwait");

  return 0;
}

/*  Test whether the exception flags indicated by the parameter except
   are currently set. If any of them are, a nonzero value is returned
   which specifies which exceptions are set. Otherwise the result is zero.  */
int
fetestexcept (int excepts)
{
  unsigned short sw;
  unsigned int mxcsr = 0;

  if (excepts & ~FE_ALL_EXCEPT)
    return EINVAL;

  /* Get status registers.  */
  __asm__ volatile ("fnstsw %0" : "=m" (sw) : );
  if (use_sse)
    __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );

  /* Mask undesired bits out and return result.  */
  return (sw | mxcsr) & excepts;
}
/*  This function stores in the variable pointed to by flagp an
   implementation-defined value representing the current setting of the
   exception flags indicated by excepts.  The function returns zero in
   case the operation was successful, a non-zero value otherwise.  */
int
fegetexceptflag (fexcept_t *flagp, int excepts)
{
  unsigned short sw;
  unsigned int mxcsr = 0;

  if (excepts & ~FE_ALL_EXCEPT)
    return EINVAL;

  /* Get status registers.  */
  __asm__ volatile ("fnstsw %0" : "=m" (sw) : );
  if (use_sse)
    __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );

  /* Mask undesired bits out and set result struct.  */
  flagp->_fpu_exceptions = (sw & excepts);
  flagp->_sse_exceptions = (mxcsr & excepts);

  return 0;
}

/*  This function restores the flags for the exceptions indicated by
   excepts to the values stored in the variable pointed to by flagp.  */
int
fesetexceptflag (const fexcept_t *flagp, int excepts)
{
  fenv_t fenv;

  if (excepts & ~FE_ALL_EXCEPT)
    return EINVAL;

  /* Need to save/restore whole environment to modify status word.  */
  fegetenv (&fenv);

  /* Set/Clear desired exception bits.  */
  fenv._fpu._fpu_sw &= ~excepts;
  fenv._fpu._fpu_sw |= (excepts & flagp->_fpu_exceptions);
  fenv._sse_mxcsr &= ~excepts;
  fenv._sse_mxcsr |= (excepts & flagp->_sse_exceptions);

  /* Set back into FPU state.  */
  return fesetenv (&fenv);
}

/*  Returns the currently selected rounding mode, represented by one of the
   values of the defined rounding mode macros.  */
int
fegetround (void)
{
  unsigned short cw;

  /* Get control word.  We assume SSE and x87 stay in sync.  */
  __asm__ volatile ("fnstcw %0" : "=m" (cw) : );

  return (cw & FE_CW_ROUND_MASK) >> FE_CW_ROUND_SHIFT;
}

/*  Changes the currently selected rounding mode to round. If round does
   not correspond to one of the supported rounding modes nothing is changed.
   fesetround returns zero if it changed the rounding mode, a nonzero value
   if the mode is not supported.  */
int
fesetround (int round)
{
  unsigned short cw;
  unsigned int mxcsr = 0;

  /* Will succeed for any valid value of the input parameter.  */
  if (round < FE_TONEAREST || round > FE_TOWARDZERO)
    return EINVAL;

  /* Get control words.  */
  __asm__ volatile ("fnstcw %0" : "=m" (cw) : );
  if (use_sse)
    __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr) : );

  /* Twiddle bits.  */
  cw &= ~FE_CW_ROUND_MASK;
  cw |= (round << FE_CW_ROUND_SHIFT);
  mxcsr &= ~FE_MXCSR_ROUND_MASK;
  mxcsr |= (round << FE_MXCSR_ROUND_SHIFT);

  /* Set back into FPU state.  */
  __asm__ volatile ("fldcw %0" :: "m" (cw));
  if (use_sse)
    __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));

  /* Indicate success.  */
  return 0;
}

/*  Returns the currently selected precision, represented by one of the
   values of the defined precision macros.  */
int
fegetprec (void)
{
  unsigned short cw;

  /* Get control word.  */
  __asm__ volatile ("fnstcw %0" : "=m" (cw) : );

  return (cw & FE_CW_PREC_MASK) >> FE_CW_PREC_SHIFT;
}

/*  Changes the currently selected precision to prec. If prec does not
   correspond to one of the supported rounding modes nothing is changed.
   fesetprec returns zero if it changed the precision, or a nonzero value
   if the mode is not supported.  */
int
fesetprec (int prec)
{
  unsigned short cw;

  /* Will succeed for any valid value of the input parameter.  */
  if (prec < FE_SINGLEPREC || prec > FE_EXTENDEDPREC)
    return EINVAL;

  /* Get control word.  */
  __asm__ volatile ("fnstcw %0" : "=m" (cw) : );

  /* Twiddle bits.  */
  cw &= ~FE_CW_PREC_MASK;
  cw |= (prec << FE_CW_PREC_SHIFT);

  /* Set back into FPU state.  */
  __asm__ volatile ("fldcw %0" :: "m" (cw));

  /* Indicate success.  */
  return 0;
}

/*  Set up the FPU and SSE environment at the start of execution.  */
void
_feinitialise (void)
{
  unsigned int edx, eax, mxcsr;

  /* Check for presence of SSE: invoke CPUID #1, check EDX bit 25.  */
  eax = 1;
  __asm__ volatile ("cpuid" : "=d" (edx), "+a" (eax) :: "%ecx", "%ebx");
  /* If this flag isn't set we'll avoid trying to execute any SSE.  */
  if ((edx & (1 << 25)) != 0)
    use_sse = true;

  /* Reset FPU: extended prec, all exceptions cleared and masked off.  */
  __asm__ volatile ("fninit");
  /* The default cw value, 0x37f, is rounding mode zero.  The MXCSR has
     no precision control, so the only thing to do is set the exception
     mask bits.  */
  mxcsr = FE_ALL_EXCEPT << FE_SSE_EXCEPT_MASK_SHIFT;
  if (use_sse)
    __asm__ volatile ("ldmxcsr %0" :: "m" (mxcsr));

  /* Setup unmasked environment.  */
  feenableexcept (FE_ALL_EXCEPT);
  fegetenv (&fe_nomask_env);

  /* Restore default exception masking (all masked).  */
  fedisableexcept (FE_ALL_EXCEPT);

  /* Finally cache state as default environment. */
  fegetenv (&fe_dfl_env);
}