exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / fenv-except-tracking-clear.c
blob8a67d744ec41722f2cab9bdd6c4037f42963d211
1 /* Functions for tracking which floating-point exceptions have occurred.
2 Copyright (C) 1997-2024 Free Software Foundation, Inc.
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
9 This file is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Based on glibc/sysdeps/<cpu>/{fclrexcpt.c,fraiseexcpt.c,ftestexcept.c}
18 together with glibc/sysdeps/<cpu>/{fpu_control.h,fenv_private.h,fenv_libc.h}. */
20 #include <config.h>
22 /* Specification. */
23 #include <fenv.h>
25 #include "fenv-private.h"
27 #if defined __GNUC__ || defined __clang__ || defined _MSC_VER
29 # if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)
31 int
32 feclearexcept (int exceptions)
34 exceptions &= FE_ALL_EXCEPT;
36 # if defined _MSC_VER
38 exceptions = exceptions_to_x86hardware (exceptions);
40 /* Clear the bits only in the SSE unit. */
41 unsigned int mxcsr, orig_mxcsr;
42 _FPU_GETSSECW (orig_mxcsr);
43 mxcsr = orig_mxcsr & ~exceptions;
44 if (mxcsr != orig_mxcsr)
45 _FPU_SETSSECW (mxcsr);
47 # else
49 /* Clear the bits in the 387 unit. */
50 x86_387_fenv_t env;
51 __asm__ __volatile__ ("fnstenv %0" : "=m" (*&env));
52 /* Note: fnstenv masks all floating-point exceptions until the fldenv
53 below. */
54 env.__status_word &= ~exceptions;
55 __asm__ __volatile__ ("fldenv %0" : : "m" (*&env));
57 if (CPU_HAS_SSE ())
59 /* Clear the bits in the SSE unit as well. */
60 unsigned int mxcsr, orig_mxcsr;
61 _FPU_GETSSECW (orig_mxcsr);
62 mxcsr = orig_mxcsr & ~exceptions;
63 if (mxcsr != orig_mxcsr)
64 _FPU_SETSSECW (mxcsr);
67 # endif
69 return 0;
72 # elif defined __aarch64__ /* arm64 */
74 int
75 feclearexcept (int exceptions)
77 exceptions &= FE_ALL_EXCEPT;
79 unsigned long fpsr, orig_fpsr;
80 _FPU_GETFPSR (orig_fpsr);
81 fpsr = orig_fpsr & ~exceptions;
82 if (fpsr != orig_fpsr)
83 _FPU_SETFPSR (fpsr);
85 return 0;
88 # elif defined __arm__
90 int
91 feclearexcept (int exceptions)
93 exceptions &= FE_ALL_EXCEPT;
95 # ifdef __SOFTFP__
96 if (exceptions != 0)
97 return -1;
98 # else
99 unsigned int fpscr, orig_fpscr;
100 _FPU_GETCW (orig_fpscr);
101 fpscr = orig_fpscr & ~exceptions;
102 if (fpscr != orig_fpscr)
103 _FPU_SETCW (fpscr);
104 # endif
105 return 0;
108 # elif defined __alpha
111 feclearexcept (int exceptions)
113 exceptions &= FE_ALL_EXCEPT;
115 unsigned long swcr, orig_swcr;
116 orig_swcr = __ieee_get_fp_control ();
117 swcr = orig_swcr & ~exceptions;
118 if (swcr != orig_swcr)
119 __ieee_set_fp_control (swcr);
121 return 0;
124 # elif defined __hppa
127 feclearexcept (int exceptions)
129 exceptions &= FE_ALL_EXCEPT;
131 union { unsigned long long fpreg; unsigned int halfreg[2]; } s;
132 /* Get the current status word. */
133 __asm__ __volatile__ ("fstd %%fr0,0(%1)" : "=m" (s.fpreg) : "r" (&s.fpreg) : "%r0");
134 unsigned int old_halfreg0 = s.halfreg[0];
135 /* Clear all the relevant bits. */
136 s.halfreg[0] &= ~ ((unsigned int) exceptions << 27);
137 if (s.halfreg[0] != old_halfreg0)
139 /* Store the new status word. */
140 __asm__ __volatile__ ("fldd 0(%0),%%fr0" : : "r" (&s.fpreg), "m" (s.fpreg) : "%r0");
143 return 0;
146 # elif defined __ia64__
149 feclearexcept (int exceptions)
151 exceptions &= FE_ALL_EXCEPT;
153 unsigned long fpsr, orig_fpsr;
154 _FPU_GETCW (orig_fpsr);
155 fpsr = orig_fpsr & ~ (unsigned long) (exceptions << 13);
156 if (fpsr != orig_fpsr)
157 _FPU_SETCW (fpsr);
159 return 0;
162 # elif defined __m68k__
165 feclearexcept (int exceptions)
167 exceptions &= FE_ALL_EXCEPT;
169 unsigned int fpsr, orig_fpsr;
170 _FPU_GETFPSR (orig_fpsr);
171 fpsr = orig_fpsr & ~ exceptions;
172 if (fpsr != orig_fpsr)
173 _FPU_SETFPSR (fpsr);
175 return 0;
178 # elif defined __mips__
181 feclearexcept (int exceptions)
183 exceptions &= FE_ALL_EXCEPT;
185 /* Clear also the cause bits. If the cause bit is not cleared, the next
186 CTC instruction (just below) will re-generate the exception. */
187 unsigned int fcsr, orig_fcsr;
188 _FPU_GETCW (orig_fcsr);
189 fcsr = orig_fcsr & ~ ((exceptions << 10) | exceptions);
190 if (fcsr != orig_fcsr)
191 _FPU_SETCW (fcsr);
193 return 0;
196 # elif defined __loongarch__
199 feclearexcept (int exceptions)
201 exceptions &= FE_ALL_EXCEPT;
203 /* Clear also the cause bits. If the cause bit is not cleared, the next
204 CTC instruction (just below) will re-generate the exception. */
205 unsigned int fcsr, orig_fcsr;
206 _FPU_GETCW (orig_fcsr);
207 fcsr = orig_fcsr & ~ ((exceptions << 8) | exceptions);
208 if (fcsr != orig_fcsr)
209 _FPU_SETCW (fcsr);
211 return 0;
214 # elif defined __powerpc__
217 feclearexcept (int exceptions)
219 exceptions &= FE_ALL_EXCEPT;
221 union { unsigned long long u; double f; } memenv, orig_memenv;
222 _FPU_GETCW_AS_DOUBLE (memenv.f);
223 orig_memenv = memenv;
225 /* Instead of clearing FE_INVALID (= bit 29), we need to clear the
226 individual bits. */
227 memenv.u &= ~ (exceptions & FE_INVALID
228 ? (exceptions & ~FE_INVALID) | 0x01F80700U
229 : exceptions);
231 if (!(memenv.u == orig_memenv.u))
232 _FPU_SETCW_AS_DOUBLE (memenv.f);
234 return 0;
237 # elif defined __riscv
240 feclearexcept (int exceptions)
242 exceptions &= FE_ALL_EXCEPT;
243 __asm__ __volatile__ ("csrc fflags, %0" : : "r" (exceptions));
244 return 0;
247 # elif defined __s390__ || defined __s390x__
250 feclearexcept (int exceptions)
252 exceptions &= FE_ALL_EXCEPT;
254 unsigned int fpc, orig_fpc;
255 _FPU_GETCW (orig_fpc);
256 # if FE_INEXACT == 8 /* glibc compatible FE_* values */
257 fpc = orig_fpc & ~(exceptions << 16);
258 if ((fpc & 0x00000300) == 0)
259 fpc &= ~(exceptions << 8);
260 # else /* musl libc compatible FE_* values */
261 fpc = orig_fpc & ~exceptions;
262 if ((fpc & 0x00000300) == 0)
263 fpc &= ~(exceptions >> 8);
264 # endif
265 if (fpc != orig_fpc)
266 _FPU_SETCW (fpc);
268 return 0;
271 # elif defined __sh__
274 feclearexcept (int exceptions)
276 exceptions &= FE_ALL_EXCEPT;
278 unsigned int fpscr, orig_fpscr;
279 _FPU_GETCW (orig_fpscr);
280 fpscr = orig_fpscr & ~exceptions;
281 if (fpscr != orig_fpscr)
282 _FPU_SETCW (fpscr);
284 return 0;
287 # elif defined __sparc
290 feclearexcept (int exceptions)
292 exceptions &= FE_ALL_EXCEPT;
294 unsigned long fsr, orig_fsr;
295 _FPU_GETCW (orig_fsr);
296 # if FE_INEXACT == 32 /* glibc compatible FE_* values */
297 fsr = orig_fsr & ~exceptions;
298 # else /* Solaris compatible FE_* values */
299 fsr = orig_fsr & ~(exceptions << 5);
300 # endif
301 if (fsr != orig_fsr)
302 _FPU_SETCW (fsr);
304 return 0;
307 # else
309 # if defined __GNUC__ || defined __clang__
310 # warning "Unknown CPU / architecture. Please report your platform and compiler to <bug-gnulib@gnu.org>."
311 # endif
312 # define NEED_FALLBACK 1
314 # endif
316 #else
318 /* The compiler does not support __asm__ statements or equivalent
319 intrinsics. */
321 # if HAVE_FPSETSTICKY
322 /* FreeBSD ≥ 3.1, NetBSD ≥ 1.1, OpenBSD, IRIX, Solaris, Minix ≥ 3.2. */
324 /* Get fpgetsticky, fpsetsticky. */
325 # include <ieeefp.h>
326 /* The type is called 'fp_except_t' on FreeBSD, but 'fp_except' on
327 all other systems. */
328 # if !defined __FreeBSD__
329 # define fp_except_t fp_except
330 # endif
333 feclearexcept (int exceptions)
335 exceptions &= FE_ALL_EXCEPT;
337 fp_except_t flags, orig_flags;
338 orig_flags = fpgetsticky ();
339 flags = orig_flags & ~exceptions;
340 if (flags != orig_flags)
341 fpsetsticky (flags);
343 return 0;
346 # elif defined _AIX && defined __powerpc__ /* AIX */
348 # include <float.h>
349 # include <fpxcp.h>
351 /* Documentation:
352 <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-clr-flag-fp-set-flag-fp-read-flag-fp-swap-flag-subroutine> */
355 feclearexcept (int exceptions)
357 exceptions &= FE_ALL_EXCEPT;
359 /* In addition to clearing FE_INVALID (= bit 29), we also need to clear the
360 individual bits. */
361 fpflag_t f_to_clear =
362 exceptions_to_fpflag (exceptions)
363 | (exceptions & FE_INVALID ? 0x01F80700U : 0);
364 if (f_to_clear != 0)
365 fp_clr_flag (f_to_clear);
367 return 0;
370 # else
372 # define NEED_FALLBACK 1
374 # endif
376 #endif
378 #if NEED_FALLBACK
380 /* A dummy fallback. */
383 feclearexcept (int exceptions)
385 exceptions &= FE_ALL_EXCEPT;
386 if (exceptions != 0)
387 return -1;
388 return 0;
391 #endif