powerpc: Use faster means to access FPSCR when possible in some cases
[external/glibc.git] / sysdeps / powerpc / fpu / fenv_libc.h
1 /* Internal libc stuff for floating point environment routines.
2    Copyright (C) 1997-2019 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <http://www.gnu.org/licenses/>.  */
18
19 #ifndef _FENV_LIBC_H
20 #define _FENV_LIBC_H    1
21
22 #include <fenv.h>
23 #include <ldsodefs.h>
24 #include <sysdep.h>
25
26 extern const fenv_t *__fe_nomask_env_priv (void);
27
28 extern const fenv_t *__fe_mask_env (void) attribute_hidden;
29
30 /* The sticky bits in the FPSCR indicating exceptions have occurred.  */
31 #define FPSCR_STICKY_BITS ((FE_ALL_EXCEPT | FE_ALL_INVALID) & ~FE_INVALID)
32
33 /* Equivalent to fegetenv, but returns a fenv_t instead of taking a
34    pointer.  */
35 #define fegetenv_register() __builtin_mffs()
36
37 /* Equivalent to fegetenv_register, but only returns bits for
38    status, exception enables, and mode.  */
39
40 #define fegetenv_status_ISA300()                                        \
41   ({register double __fr;                                               \
42     __asm__ __volatile__ (                                              \
43       ".machine push; .machine \"power9\"; mffsl %0; .machine pop"      \
44       : "=f" (__fr));                                                   \
45     __fr;                                                               \
46   })
47
48 #ifdef _ARCH_PWR9
49 # define fegetenv_status() fegetenv_status_ISA300()
50 #else
51 # define fegetenv_status()                                              \
52   (__glibc_likely (__builtin_cpu_supports ("arch_3_00"))                \
53    ? fegetenv_status_ISA300()                                           \
54    : fegetenv_register()                                                \
55   )
56 #endif
57
58 /* Equivalent to fesetenv, but takes a fenv_t instead of a pointer.  */
59 #define fesetenv_register(env) \
60         do { \
61           double d = (env); \
62           if(GLRO(dl_hwcap) & PPC_FEATURE_HAS_DFP) \
63             asm volatile (".machine push; " \
64                           ".machine \"power6\"; " \
65                           "mtfsf 0xff,%0,1,0; " \
66                           ".machine pop" : : "f" (d)); \
67           else \
68             __builtin_mtfsf (0xff, d); \
69         } while(0)
70
71 /* This very handy macro:
72    - Sets the rounding mode to 'round to nearest';
73    - Sets the processor into IEEE mode; and
74    - Prevents exceptions from being raised for inexact results.
75    These things happen to be exactly what you need for typical elementary
76    functions.  */
77 #define relax_fenv_state() \
78         do { \
79            if (GLRO(dl_hwcap) & PPC_FEATURE_HAS_DFP) \
80              asm volatile (".machine push; .machine \"power6\"; " \
81                   "mtfsfi 7,0,1; .machine pop"); \
82            asm volatile ("mtfsfi 7,0"); \
83         } while(0)
84
85 /* Set/clear a particular FPSCR bit (for instance,
86    reset_fpscr_bit(FPSCR_VE);
87    prevents INVALID exceptions from being raised).  */
88 #define set_fpscr_bit(x) asm volatile ("mtfsb1 %0" : : "i"(x))
89 #define reset_fpscr_bit(x) asm volatile ("mtfsb0 %0" : : "i"(x))
90
91 typedef union
92 {
93   fenv_t fenv;
94   unsigned long long l;
95 } fenv_union_t;
96
97
98 static inline int
99 __fesetround_inline (int round)
100 {
101   if ((unsigned int) round < 2)
102     {
103        asm volatile ("mtfsb0 30");
104        if ((unsigned int) round == 0)
105          asm volatile ("mtfsb0 31");
106        else
107          asm volatile ("mtfsb1 31");
108     }
109   else
110     {
111        asm volatile ("mtfsb1 30");
112        if ((unsigned int) round == 2)
113          asm volatile ("mtfsb0 31");
114        else
115          asm volatile ("mtfsb1 31");
116     }
117
118   return 0;
119 }
120
121 /* Same as __fesetround_inline, however without runtime check to use DFP
122    mtfsfi syntax (as relax_fenv_state) or if round value is valid.  */
123 static inline void
124 __fesetround_inline_nocheck (const int round)
125 {
126   asm volatile ("mtfsfi 7,%0" : : "i" (round));
127 }
128
129 /* Definitions of all the FPSCR bit numbers */
130 enum {
131   FPSCR_FX = 0,    /* exception summary */
132   FPSCR_FEX,       /* enabled exception summary */
133   FPSCR_VX,        /* invalid operation summary */
134   FPSCR_OX,        /* overflow */
135   FPSCR_UX,        /* underflow */
136   FPSCR_ZX,        /* zero divide */
137   FPSCR_XX,        /* inexact */
138   FPSCR_VXSNAN,    /* invalid operation for sNaN */
139   FPSCR_VXISI,     /* invalid operation for Inf-Inf */
140   FPSCR_VXIDI,     /* invalid operation for Inf/Inf */
141   FPSCR_VXZDZ,     /* invalid operation for 0/0 */
142   FPSCR_VXIMZ,     /* invalid operation for Inf*0 */
143   FPSCR_VXVC,      /* invalid operation for invalid compare */
144   FPSCR_FR,        /* fraction rounded [fraction was incremented by round] */
145   FPSCR_FI,        /* fraction inexact */
146   FPSCR_FPRF_C,    /* result class descriptor */
147   FPSCR_FPRF_FL,   /* result less than (usually, less than 0) */
148   FPSCR_FPRF_FG,   /* result greater than */
149   FPSCR_FPRF_FE,   /* result equal to */
150   FPSCR_FPRF_FU,   /* result unordered */
151   FPSCR_20,        /* reserved */
152   FPSCR_VXSOFT,    /* invalid operation set by software */
153   FPSCR_VXSQRT,    /* invalid operation for square root */
154   FPSCR_VXCVI,     /* invalid operation for invalid integer convert */
155   FPSCR_VE,        /* invalid operation exception enable */
156   FPSCR_OE,        /* overflow exception enable */
157   FPSCR_UE,        /* underflow exception enable */
158   FPSCR_ZE,        /* zero divide exception enable */
159   FPSCR_XE,        /* inexact exception enable */
160 #ifdef _ARCH_PWR6
161   FPSCR_29,        /* Reserved in ISA 2.05  */
162 #else
163   FPSCR_NI         /* non-IEEE mode (typically, no denormalised numbers) */
164 #endif /* _ARCH_PWR6 */
165   /* the remaining two least-significant bits keep the rounding mode */
166 };
167
168 static inline int
169 fenv_reg_to_exceptions (unsigned long long l)
170 {
171   int result = 0;
172   if (l & (1 << (31 - FPSCR_XE)))
173     result |= FE_INEXACT;
174   if (l & (1 << (31 - FPSCR_ZE)))
175     result |= FE_DIVBYZERO;
176   if (l & (1 << (31 - FPSCR_UE)))
177     result |= FE_UNDERFLOW;
178   if (l & (1 << (31 - FPSCR_OE)))
179     result |= FE_OVERFLOW;
180   if (l & (1 << (31 - FPSCR_VE)))
181     result |= FE_INVALID;
182   return result;
183 }
184
185 #ifdef _ARCH_PWR6
186   /* Not supported in ISA 2.05.  Provided for source compat only.  */
187 # define FPSCR_NI 29
188 #endif /* _ARCH_PWR6 */
189
190 /* This operation (i) sets the appropriate FPSCR bits for its
191    parameter, (ii) converts sNaN to the corresponding qNaN, and (iii)
192    otherwise passes its parameter through unchanged (in particular, -0
193    and +0 stay as they were).  The `obvious' way to do this is optimised
194    out by gcc.  */
195 #define f_wash(x) \
196    ({ double d; asm volatile ("fmul %0,%1,%2" \
197                               : "=f"(d) \
198                               : "f" (x), "f"((float)1.0)); d; })
199 #define f_washf(x) \
200    ({ float f; asm volatile ("fmuls %0,%1,%2" \
201                              : "=f"(f) \
202                              : "f" (x), "f"((float)1.0)); f; })
203
204 #endif /* fenv_libc.h */