1 /* Optimized inline fenv.h functions for libm.  Generic version.
2    Copyright (C) 2011-2022 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    <https://www.gnu.org/licenses/>.  */
18 
19 #ifndef _FENV_PRIVATE_H
20 #define _FENV_PRIVATE_H 1
21 
22 #include <fenv.h>
23 #include <get-rounding-mode.h>
24 
25 /* The standards only specify one variant of the fenv.h interfaces.
26    But at least for some architectures we can be more efficient if we
27    know what operations are going to be performed.  Therefore we
28    define additional interfaces.  By default they refer to the normal
29    interfaces.  */
30 
31 static __always_inline void
default_libc_feholdexcept(fenv_t * e)32 default_libc_feholdexcept (fenv_t *e)
33 {
34   (void) __feholdexcept (e);
35 }
36 
37 #ifndef libc_feholdexcept
38 # define libc_feholdexcept  default_libc_feholdexcept
39 #endif
40 #ifndef libc_feholdexceptf
41 # define libc_feholdexceptf default_libc_feholdexcept
42 #endif
43 #ifndef libc_feholdexceptl
44 # define libc_feholdexceptl default_libc_feholdexcept
45 #endif
46 
47 static __always_inline void
default_libc_fesetround(int r)48 default_libc_fesetround (int r)
49 {
50   (void) __fesetround (r);
51 }
52 
53 #ifndef libc_fesetround
54 # define libc_fesetround  default_libc_fesetround
55 #endif
56 #ifndef libc_fesetroundf
57 # define libc_fesetroundf default_libc_fesetround
58 #endif
59 #ifndef libc_fesetroundl
60 # define libc_fesetroundl default_libc_fesetround
61 #endif
62 
63 static __always_inline void
default_libc_feholdexcept_setround(fenv_t * e,int r)64 default_libc_feholdexcept_setround (fenv_t *e, int r)
65 {
66   __feholdexcept (e);
67   __fesetround (r);
68 }
69 
70 #ifndef libc_feholdexcept_setround
71 # define libc_feholdexcept_setround  default_libc_feholdexcept_setround
72 #endif
73 #ifndef libc_feholdexcept_setroundf
74 # define libc_feholdexcept_setroundf default_libc_feholdexcept_setround
75 #endif
76 #ifndef libc_feholdexcept_setroundl
77 # define libc_feholdexcept_setroundl default_libc_feholdexcept_setround
78 #endif
79 
80 #ifndef libc_feholdsetround_53bit
81 # define libc_feholdsetround_53bit libc_feholdsetround
82 #endif
83 
84 #ifndef libc_fetestexcept
85 # define libc_fetestexcept  fetestexcept
86 #endif
87 #ifndef libc_fetestexceptf
88 # define libc_fetestexceptf fetestexcept
89 #endif
90 #ifndef libc_fetestexceptl
91 # define libc_fetestexceptl fetestexcept
92 #endif
93 
94 static __always_inline void
default_libc_fesetenv(fenv_t * e)95 default_libc_fesetenv (fenv_t *e)
96 {
97   (void) __fesetenv (e);
98 }
99 
100 #ifndef libc_fesetenv
101 # define libc_fesetenv  default_libc_fesetenv
102 #endif
103 #ifndef libc_fesetenvf
104 # define libc_fesetenvf default_libc_fesetenv
105 #endif
106 #ifndef libc_fesetenvl
107 # define libc_fesetenvl default_libc_fesetenv
108 #endif
109 
110 static __always_inline void
default_libc_feupdateenv(fenv_t * e)111 default_libc_feupdateenv (fenv_t *e)
112 {
113   (void) __feupdateenv (e);
114 }
115 
116 #ifndef libc_feupdateenv
117 # define libc_feupdateenv  default_libc_feupdateenv
118 #endif
119 #ifndef libc_feupdateenvf
120 # define libc_feupdateenvf default_libc_feupdateenv
121 #endif
122 #ifndef libc_feupdateenvl
123 # define libc_feupdateenvl default_libc_feupdateenv
124 #endif
125 
126 #ifndef libc_feresetround_53bit
127 # define libc_feresetround_53bit libc_feresetround
128 #endif
129 
130 static __always_inline int
default_libc_feupdateenv_test(fenv_t * e,int ex)131 default_libc_feupdateenv_test (fenv_t *e, int ex)
132 {
133   int ret = fetestexcept (ex);
134   __feupdateenv (e);
135   return ret;
136 }
137 
138 #ifndef libc_feupdateenv_test
139 # define libc_feupdateenv_test  default_libc_feupdateenv_test
140 #endif
141 #ifndef libc_feupdateenv_testf
142 # define libc_feupdateenv_testf default_libc_feupdateenv_test
143 #endif
144 #ifndef libc_feupdateenv_testl
145 # define libc_feupdateenv_testl default_libc_feupdateenv_test
146 #endif
147 
148 /* Save and set the rounding mode.  The use of fenv_t to store the old mode
149    allows a target-specific version of this function to avoid converting the
150    rounding mode from the fpu format.  By default we have no choice but to
151    manipulate the entire env.  */
152 
153 #ifndef libc_feholdsetround
154 # define libc_feholdsetround  libc_feholdexcept_setround
155 #endif
156 #ifndef libc_feholdsetroundf
157 # define libc_feholdsetroundf libc_feholdexcept_setroundf
158 #endif
159 #ifndef libc_feholdsetroundl
160 # define libc_feholdsetroundl libc_feholdexcept_setroundl
161 #endif
162 
163 /* ... and the reverse.  */
164 
165 #ifndef libc_feresetround
166 # define libc_feresetround  libc_feupdateenv
167 #endif
168 #ifndef libc_feresetroundf
169 # define libc_feresetroundf libc_feupdateenvf
170 #endif
171 #ifndef libc_feresetroundl
172 # define libc_feresetroundl libc_feupdateenvl
173 #endif
174 
175 /* ... and a version that also discards exceptions.  */
176 
177 #ifndef libc_feresetround_noex
178 # define libc_feresetround_noex  libc_fesetenv
179 #endif
180 #ifndef libc_feresetround_noexf
181 # define libc_feresetround_noexf libc_fesetenvf
182 #endif
183 #ifndef libc_feresetround_noexl
184 # define libc_feresetround_noexl libc_fesetenvl
185 #endif
186 
187 #ifndef HAVE_RM_CTX
188 # define HAVE_RM_CTX 0
189 #endif
190 
191 
192 /* Default implementation using standard fenv functions.
193    Avoid unnecessary rounding mode changes by first checking the
194    current rounding mode.  Note the use of __glibc_unlikely is
195    important for performance.  */
196 
197 static __always_inline void
default_libc_feholdsetround_ctx(struct rm_ctx * ctx,int round)198 default_libc_feholdsetround_ctx (struct rm_ctx *ctx, int round)
199 {
200   ctx->updated_status = false;
201 
202   /* Update rounding mode only if different.  */
203   if (__glibc_unlikely (round != get_rounding_mode ()))
204     {
205       ctx->updated_status = true;
206       __fegetenv (&ctx->env);
207       __fesetround (round);
208     }
209 }
210 
211 static __always_inline void
default_libc_feresetround_ctx(struct rm_ctx * ctx)212 default_libc_feresetround_ctx (struct rm_ctx *ctx)
213 {
214   /* Restore the rounding mode if updated.  */
215   if (__glibc_unlikely (ctx->updated_status))
216     __feupdateenv (&ctx->env);
217 }
218 
219 static __always_inline void
default_libc_feholdsetround_noex_ctx(struct rm_ctx * ctx,int round)220 default_libc_feholdsetround_noex_ctx (struct rm_ctx *ctx, int round)
221 {
222   /* Save exception flags and rounding mode, and disable exception
223      traps.  */
224   __feholdexcept (&ctx->env);
225 
226   /* Update rounding mode only if different.  */
227   if (__glibc_unlikely (round != get_rounding_mode ()))
228     __fesetround (round);
229 }
230 
231 static __always_inline void
default_libc_feresetround_noex_ctx(struct rm_ctx * ctx)232 default_libc_feresetround_noex_ctx (struct rm_ctx *ctx)
233 {
234   /* Restore exception flags and rounding mode.  */
235   __fesetenv (&ctx->env);
236 }
237 
238 #if HAVE_RM_CTX
239 /* Set/Restore Rounding Modes only when necessary.  If defined, these functions
240    set/restore floating point state only if the state needed within the lexical
241    block is different from the current state.  This saves a lot of time when
242    the floating point unit is much slower than the fixed point units.  */
243 
244 # ifndef libc_feholdsetround_noex_ctx
245 #   define libc_feholdsetround_noex_ctx  libc_feholdsetround_ctx
246 # endif
247 # ifndef libc_feholdsetround_noexf_ctx
248 #   define libc_feholdsetround_noexf_ctx libc_feholdsetroundf_ctx
249 # endif
250 # ifndef libc_feholdsetround_noexl_ctx
251 #   define libc_feholdsetround_noexl_ctx libc_feholdsetroundl_ctx
252 # endif
253 
254 # ifndef libc_feresetround_noex_ctx
255 #   define libc_feresetround_noex_ctx  libc_fesetenv_ctx
256 # endif
257 # ifndef libc_feresetround_noexf_ctx
258 #   define libc_feresetround_noexf_ctx libc_fesetenvf_ctx
259 # endif
260 # ifndef libc_feresetround_noexl_ctx
261 #   define libc_feresetround_noexl_ctx libc_fesetenvl_ctx
262 # endif
263 
264 #else
265 
266 # define libc_feholdsetround_ctx      default_libc_feholdsetround_ctx
267 # define libc_feresetround_ctx        default_libc_feresetround_ctx
268 # define libc_feholdsetround_noex_ctx default_libc_feholdsetround_noex_ctx
269 # define libc_feresetround_noex_ctx   default_libc_feresetround_noex_ctx
270 
271 # define libc_feholdsetroundf_ctx libc_feholdsetround_ctx
272 # define libc_feholdsetroundl_ctx libc_feholdsetround_ctx
273 # define libc_feresetroundf_ctx   libc_feresetround_ctx
274 # define libc_feresetroundl_ctx   libc_feresetround_ctx
275 
276 # define libc_feholdsetround_noexf_ctx libc_feholdsetround_noex_ctx
277 # define libc_feholdsetround_noexl_ctx libc_feholdsetround_noex_ctx
278 # define libc_feresetround_noexf_ctx   libc_feresetround_noex_ctx
279 # define libc_feresetround_noexl_ctx   libc_feresetround_noex_ctx
280 
281 #endif
282 
283 #ifndef libc_feholdsetround_53bit_ctx
284 #  define libc_feholdsetround_53bit_ctx libc_feholdsetround_ctx
285 #endif
286 #ifndef libc_feresetround_53bit_ctx
287 #  define libc_feresetround_53bit_ctx libc_feresetround_ctx
288 #endif
289 
290 #define SET_RESTORE_ROUND_GENERIC(RM,ROUNDFUNC,CLEANUPFUNC) \
291   struct rm_ctx ctx __attribute__((cleanup (CLEANUPFUNC ## _ctx))); \
292   ROUNDFUNC ## _ctx (&ctx, (RM))
293 
294 /* Set the rounding mode within a lexical block.  Restore the rounding mode to
295    the value at the start of the block.  The exception mode must be preserved.
296    Exceptions raised within the block must be set in the exception flags.
297    Non-stop mode may be enabled inside the block.  */
298 
299 #define SET_RESTORE_ROUND(RM) \
300   SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetround, libc_feresetround)
301 #define SET_RESTORE_ROUNDF(RM) \
302   SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetroundf, libc_feresetroundf)
303 #define SET_RESTORE_ROUNDL(RM) \
304   SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetroundl, libc_feresetroundl)
305 
306 /* Set the rounding mode within a lexical block.  Restore the rounding mode to
307    the value at the start of the block.  The exception mode must be preserved.
308    Exceptions raised within the block must be discarded, and exception flags
309    are restored to the value at the start of the block.
310    Non-stop mode must be enabled inside the block.  */
311 
312 #define SET_RESTORE_ROUND_NOEX(RM) \
313   SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetround_noex, \
314 			     libc_feresetround_noex)
315 #define SET_RESTORE_ROUND_NOEXF(RM) \
316   SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetround_noexf, \
317 			     libc_feresetround_noexf)
318 #define SET_RESTORE_ROUND_NOEXL(RM) \
319   SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetround_noexl, \
320 			     libc_feresetround_noexl)
321 
322 /* Like SET_RESTORE_ROUND, but also set rounding precision to 53 bits.  */
323 #define SET_RESTORE_ROUND_53BIT(RM) \
324   SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetround_53bit,	      \
325 			     libc_feresetround_53bit)
326 
327 #endif /* fenv_private.h.  */
328