1 /* Functions for tracking which floating-point exceptions have occurred.
2 Copyright (C) 1997-2025 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}. */
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)
32 feclearexcept (int exceptions
)
34 exceptions
&= FE_ALL_EXCEPT
;
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
);
49 /* Clear the bits in the 387 unit. */
51 __asm__
__volatile__ ("fnstenv %0" : "=m" (*&env
));
52 /* Note: fnstenv masks all floating-point exceptions until the fldenv
54 env
.__status_word
&= ~exceptions
;
55 __asm__
__volatile__ ("fldenv %0" : : "m" (*&env
));
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
);
72 # elif defined __aarch64__ /* arm64 */
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
)
88 # elif defined __arm__
91 feclearexcept (int exceptions
)
93 exceptions
&= FE_ALL_EXCEPT
;
99 unsigned int fpscr
, orig_fpscr
;
100 _FPU_GETCW (orig_fpscr
);
101 fpscr
= orig_fpscr
& ~exceptions
;
102 if (fpscr
!= orig_fpscr
)
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
);
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");
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
)
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
)
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
)
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
)
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
227 memenv
.u
&= ~ (exceptions
& FE_INVALID
228 ? (exceptions
& ~FE_INVALID
) | 0x01F80700U
231 if (!(memenv
.u
== orig_memenv
.u
))
232 _FPU_SETCW_AS_DOUBLE (memenv
.f
);
237 # elif defined __riscv
240 feclearexcept (int exceptions
)
242 exceptions
&= FE_ALL_EXCEPT
;
243 __asm__
__volatile__ ("csrc fflags, %0" : : "r" (exceptions
));
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);
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
)
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);
309 # if defined __GNUC__ || defined __clang__
310 # warning "Unknown CPU / architecture. Please report your platform and compiler to <bug-gnulib@gnu.org>."
312 # define NEED_FALLBACK 1
318 /* The compiler does not support __asm__ statements or equivalent
321 # if HAVE_FPSETSTICKY
322 /* FreeBSD ≥ 3.1, NetBSD ≥ 1.1, OpenBSD, IRIX, Solaris, Minix ≥ 3.2. */
324 /* Get fpgetsticky, fpsetsticky. */
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
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
)
346 # elif defined _AIX && defined __powerpc__ /* AIX */
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
361 fpflag_t f_to_clear
=
362 exceptions_to_fpflag (exceptions
)
363 | (exceptions
& FE_INVALID
? 0x01F80700U
: 0);
365 fp_clr_flag (f_to_clear
);
372 # define NEED_FALLBACK 1
380 /* A dummy fallback. */
383 feclearexcept (int exceptions
)
385 exceptions
&= FE_ALL_EXCEPT
;