aboutsummaryrefslogtreecommitdiff
blob: 125745e04bb693f0482588b8ce454dc2129014a0 (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
/* Internal libc stuff for floating point environment routines.
   Copyright (C) 1997-2014 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */

#ifndef _FENV_LIBC_H
#define _FENV_LIBC_H	1

#include <fenv.h>
#include <ldsodefs.h>
#include <sysdep.h>

extern const fenv_t *__fe_nomask_env_priv (void);

extern const fenv_t *__fe_mask_env (void) attribute_hidden;

/* The sticky bits in the FPSCR indicating exceptions have occurred.  */
#define FPSCR_STICKY_BITS ((FE_ALL_EXCEPT | FE_ALL_INVALID) & ~FE_INVALID)

/* Equivalent to fegetenv, but returns a fenv_t instead of taking a
   pointer.  */
#define fegetenv_register() \
        ({ fenv_t env; asm volatile ("mffs %0" : "=f" (env)); env; })

/* Equivalent to fesetenv, but takes a fenv_t instead of a pointer.  */
#define fesetenv_register(env) \
	do { \
	  double d = (env); \
	  if(GLRO(dl_hwcap) & PPC_FEATURE_HAS_DFP) \
	    asm volatile (".machine push; " \
			  ".machine \"power6\"; " \
			  "mtfsf 0xff,%0,1,0; " \
			  ".machine pop" : : "f" (d)); \
	  else \
	    asm volatile ("mtfsf 0xff,%0" : : "f" (d)); \
	} while(0)

/* This very handy macro:
   - Sets the rounding mode to 'round to nearest';
   - Sets the processor into IEEE mode; and
   - Prevents exceptions from being raised for inexact results.
   These things happen to be exactly what you need for typical elementary
   functions.  */
#define relax_fenv_state() \
	do { \
	   if (GLRO(dl_hwcap) & PPC_FEATURE_HAS_DFP) \
	     asm (".machine push; .machine \"power6\"; " \
		  "mtfsfi 7,0,1; .machine pop"); \
	   asm ("mtfsfi 7,0"); \
	} while(0)

/* Set/clear a particular FPSCR bit (for instance,
   reset_fpscr_bit(FPSCR_VE);
   prevents INVALID exceptions from being raised).  */
#define set_fpscr_bit(x) asm volatile ("mtfsb1 %0" : : "i"(x))
#define reset_fpscr_bit(x) asm volatile ("mtfsb0 %0" : : "i"(x))

typedef union
{
  fenv_t fenv;
  unsigned long long l;
} fenv_union_t;


static inline int
__fesetround (int round)
{
  if ((unsigned int) round < 2)
    {
       asm volatile ("mtfsb0 30");
       if ((unsigned int) round == 0)
         asm volatile ("mtfsb0 31");
       else
         asm volatile ("mtfsb1 31");
    }
  else
    {
       asm volatile ("mtfsb1 30");
       if ((unsigned int) round == 2)
         asm volatile ("mtfsb0 31");
       else
         asm volatile ("mtfsb1 31");
    }

  return 0;
}

/* Definitions of all the FPSCR bit numbers */
enum {
  FPSCR_FX = 0,    /* exception summary */
  FPSCR_FEX,       /* enabled exception summary */
  FPSCR_VX,        /* invalid operation summary */
  FPSCR_OX,        /* overflow */
  FPSCR_UX,        /* underflow */
  FPSCR_ZX,        /* zero divide */
  FPSCR_XX,        /* inexact */
  FPSCR_VXSNAN,    /* invalid operation for sNaN */
  FPSCR_VXISI,     /* invalid operation for Inf-Inf */
  FPSCR_VXIDI,     /* invalid operation for Inf/Inf */
  FPSCR_VXZDZ,     /* invalid operation for 0/0 */
  FPSCR_VXIMZ,     /* invalid operation for Inf*0 */
  FPSCR_VXVC,      /* invalid operation for invalid compare */
  FPSCR_FR,        /* fraction rounded [fraction was incremented by round] */
  FPSCR_FI,        /* fraction inexact */
  FPSCR_FPRF_C,    /* result class descriptor */
  FPSCR_FPRF_FL,   /* result less than (usually, less than 0) */
  FPSCR_FPRF_FG,   /* result greater than */
  FPSCR_FPRF_FE,   /* result equal to */
  FPSCR_FPRF_FU,   /* result unordered */
  FPSCR_20,        /* reserved */
  FPSCR_VXSOFT,    /* invalid operation set by software */
  FPSCR_VXSQRT,    /* invalid operation for square root */
  FPSCR_VXCVI,     /* invalid operation for invalid integer convert */
  FPSCR_VE,        /* invalid operation exception enable */
  FPSCR_OE,        /* overflow exception enable */
  FPSCR_UE,        /* underflow exception enable */
  FPSCR_ZE,        /* zero divide exception enable */
  FPSCR_XE,        /* inexact exception enable */
#ifdef _ARCH_PWR6
  FPSCR_29,        /* Reserved in ISA 2.05  */
#else
  FPSCR_NI         /* non-IEEE mode (typically, no denormalised numbers) */
#endif /* _ARCH_PWR6 */
  /* the remaining two least-significant bits keep the rounding mode */
};

static inline int
fenv_reg_to_exceptions (unsigned long long l)
{
  int result = 0;
  if (l & (1 << (31 - FPSCR_XE)))
    result |= FE_INEXACT;
  if (l & (1 << (31 - FPSCR_ZE)))
    result |= FE_DIVBYZERO;
  if (l & (1 << (31 - FPSCR_UE)))
    result |= FE_UNDERFLOW;
  if (l & (1 << (31 - FPSCR_OE)))
    result |= FE_OVERFLOW;
  if (l & (1 << (31 - FPSCR_VE)))
    result |= FE_INVALID;
  return result;
}

#ifdef _ARCH_PWR6
  /* Not supported in ISA 2.05.  Provided for source compat only.  */
# define FPSCR_NI 29
#endif /* _ARCH_PWR6 */

/* This operation (i) sets the appropriate FPSCR bits for its
   parameter, (ii) converts sNaN to the corresponding qNaN, and (iii)
   otherwise passes its parameter through unchanged (in particular, -0
   and +0 stay as they were).  The `obvious' way to do this is optimised
   out by gcc.  */
#define f_wash(x) \
   ({ double d; asm volatile ("fmul %0,%1,%2" \
			      : "=f"(d) \
			      : "f" (x), "f"((float)1.0)); d; })
#define f_washf(x) \
   ({ float f; asm volatile ("fmuls %0,%1,%2" \
			     : "=f"(f) \
			     : "f" (x), "f"((float)1.0)); f; })

#endif /* fenv_libc.h */