1 /* Functions for saving the floating-point exception status flags.
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>/{fgetexcptflg.c,fsetexcptflg.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)
31 /* On most OSes, fexcept_t is binary-equivalent to an 'unsigned short'.
32 On NetBSD, OpenBSD, Solaris, Cygwin, MSVC, Android/x86_64, Minix, fexcept_t
33 is equivalent to an 'unsigned int'.
34 A simple C cast does the necessary conversion. */
37 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
39 exceptions
&= FE_ALL_EXCEPT
;
41 unsigned int desired_flags
= (unsigned int) *saved_flags
;
44 exceptions
= exceptions_to_x86hardware (exceptions
);
45 desired_flags
= exceptions_to_x86hardware (desired_flags
);
47 /* Modify the flags in the SSE unit. */
48 unsigned int mxcsr
, orig_mxcsr
;
49 _FPU_GETSSECW (orig_mxcsr
);
50 mxcsr
= orig_mxcsr
^ ((orig_mxcsr
^ desired_flags
) & exceptions
);
51 if (mxcsr
!= orig_mxcsr
)
52 _FPU_SETSSECW (mxcsr
);
56 /* The flags can be set in the 387 unit or in the SSE unit.
57 When we need to clear a flag, we need to do so in both units,
58 due to the way fetestexcept() is implemented.
59 When we need to set a flag, it is sufficient to do it in the SSE unit,
60 because that is guaranteed to not trap. However, on i386 CPUs that have
61 only a 387 unit, set the flags in the 387, as long as this cannot trap. */
65 /* Modify the flags in the SSE unit. */
66 unsigned int mxcsr
, orig_mxcsr
;
67 _FPU_GETSSECW (orig_mxcsr
);
68 mxcsr
= orig_mxcsr
^ ((orig_mxcsr
^ desired_flags
) & exceptions
);
69 if (mxcsr
!= orig_mxcsr
)
70 _FPU_SETSSECW (mxcsr
);
72 /* Modify the flags in the 387 unit, but only by clearing bits, not by
75 __asm__
__volatile__ ("fnstenv %0" : "=m" (*&env
));
76 /* Note: fnstenv masks all floating-point exceptions until the fldenv
78 env
.__status_word
&= ~ (exceptions
& ~desired_flags
);
79 __asm__
__volatile__ ("fldenv %0" : : "m" (*&env
));
81 # if !(defined __x86_64__ || defined _M_X64)
84 /* Modify the flags in the 387 unit. */
86 __asm__
__volatile__ ("fnstenv %0" : "=m" (*&env
));
87 /* Note: fnstenv masks all floating-point exceptions until the fldenv
89 env
.__status_word
^= ((env
.__status_word
^ desired_flags
) & exceptions
);
90 if ((~env
.__control_word
) & env
.__status_word
& exceptions
)
92 /* Setting the exception flags may trigger a trap (at the next
93 floating-point instruction, but that does not matter).
94 ISO C 23 § 7.6.4.5 does not allow it. */
95 __asm__
__volatile__ ("fldcw %0" : : "m" (*&env
.__control_word
));
98 __asm__
__volatile__ ("fldenv %0" : : "m" (*&env
));
105 # elif defined __aarch64__ /* arm64 */
107 /* On Linux, NetBSD, and Android, fexcept_t is binary-equivalent to
109 On macOS, fexcept_t is binary-equivalent to an 'unsigned short'.
110 On FreeBSD and OpenBSD, fexcept_t is binary-equivalent to an 'unsigned long'.
111 A simple C cast does the necessary conversion. */
114 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
116 unsigned long desired_flags
= (unsigned long) *saved_flags
;
117 unsigned long fpsr
, orig_fpsr
;
118 _FPU_GETFPSR (orig_fpsr
);
119 fpsr
= orig_fpsr
^ ((orig_fpsr
^ desired_flags
) & FE_ALL_EXCEPT
& exceptions
);
120 if (fpsr
!= orig_fpsr
)
122 /* Possibly some new exception flags have been set. But just storing them
123 does not cause a trap to be executed (when traps are enabled). */
127 # elif defined __arm__
129 /* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */
132 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
138 unsigned int desired_flags
= (unsigned int) *saved_flags
;
139 unsigned int fpscr
, orig_fpscr
;
140 _FPU_GETCW (orig_fpscr
);
141 fpscr
= orig_fpscr
^ ((orig_fpscr
^ desired_flags
) & FE_ALL_EXCEPT
& exceptions
);
142 if (fpscr
!= orig_fpscr
)
148 # elif defined __alpha
150 /* On all OSes except NetBSD and OpenBSD, fexcept_t is binary-equivalent to
152 On NetBSD, it is equivalent to an 'unsigned short'.
153 On OpenBSD, it is equivalent to an 'unsigned int'.
154 A simple C cast does the necessary conversion. */
157 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
159 unsigned long desired_flags
= (unsigned long) *saved_flags
;
160 unsigned long swcr
, orig_swcr
;
161 orig_swcr
= __ieee_get_fp_control ();
162 swcr
= orig_swcr
^ ((orig_swcr
^ desired_flags
) & FE_ALL_EXCEPT
& exceptions
);
163 if (swcr
!= orig_swcr
)
164 __ieee_set_fp_control (swcr
);
165 /* Possibly some new exception flags have been set. But just storing them
166 does not cause a trap to be executed (when traps are enabled). */
170 # elif defined __hppa
172 /* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */
175 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
177 unsigned int desired_flags
= (unsigned int) *saved_flags
;
178 union { unsigned long long fpreg
; unsigned int halfreg
[2]; } s
;
179 /* Get the current status word. */
180 __asm__
__volatile__ ("fstd %%fr0,0(%1)" : "=m" (s
.fpreg
) : "r" (&s
.fpreg
) : "%r0");
181 unsigned int old_halfreg0
= s
.halfreg
[0];
182 /* Modify all the relevant bits. */
183 s
.halfreg
[0] ^= (s
.halfreg
[0] ^ (desired_flags
<< 27)) & ((FE_ALL_EXCEPT
& exceptions
) << 27);
184 if (s
.halfreg
[0] != old_halfreg0
)
186 /* Store the new status word. */
187 __asm__
__volatile__ ("fldd 0(%0),%%fr0" : : "r" (&s
.fpreg
), "m" (s
.fpreg
) : "%r0");
189 /* Possibly some new exception flags have been set. But just storing them
190 does not cause a trap to be executed (when traps are enabled). */
194 # elif defined __ia64__
196 /* On all OSes except NetBSD, fexcept_t is binary-equivalent to
198 On NetBSD, it is equivalent to an 'unsigned short'.
199 A simple C cast does the necessary conversion. */
202 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
204 unsigned long desired_flags
= (unsigned long) *saved_flags
;
205 unsigned long fpsr
, orig_fpsr
;
206 _FPU_GETCW (orig_fpsr
);
207 fpsr
= orig_fpsr
^ ((orig_fpsr
^ (desired_flags
<< 13)) & ((FE_ALL_EXCEPT
& exceptions
) << 13));
208 if (fpsr
!= orig_fpsr
)
210 /* Possibly some new exception flags have been set. But just storing them
211 does not cause a trap to be executed (when traps are enabled). */
215 # elif defined __m68k__
217 /* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */
220 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
222 unsigned int desired_flags
= (unsigned int) *saved_flags
;
223 unsigned int fpsr
, orig_fpsr
;
224 _FPU_GETFPSR (orig_fpsr
);
225 fpsr
= orig_fpsr
^ ((orig_fpsr
^ desired_flags
) & FE_ALL_EXCEPT
& exceptions
);
226 if (fpsr
!= orig_fpsr
)
228 /* Possibly some new exception flags have been set. But just storing them
229 does not cause a trap to be executed (when traps are enabled). */
233 # elif defined __mips__
235 /* On all OSes except NetBSD and OpenBSD, fexcept_t is binary-equivalent to
237 On NetBSD and OpenBSD, it is equivalent to an 'unsigned int'.
238 A simple C cast does the necessary conversion. */
241 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
243 unsigned int desired_flags
= (unsigned int) *saved_flags
;
244 unsigned int fcsr
, orig_fcsr
;
245 _FPU_GETCW (orig_fcsr
);
246 fcsr
= orig_fcsr
^ ((orig_fcsr
^ desired_flags
) & FE_ALL_EXCEPT
& exceptions
);
247 if (fcsr
!= orig_fcsr
)
252 # elif defined __loongarch__
254 /* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */
257 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
259 unsigned int desired_flags
= (unsigned int) *saved_flags
;
260 unsigned int fcsr
, orig_fcsr
;
261 _FPU_GETCW (orig_fcsr
);
262 fcsr
= orig_fcsr
^ ((orig_fcsr
^ desired_flags
) & FE_ALL_EXCEPT
& exceptions
);
263 if (fcsr
!= orig_fcsr
)
265 /* Possibly some new exception flags have been set. But just storing them
266 does not cause a trap to be executed (when traps are enabled). */
270 # elif defined __powerpc__
272 /* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */
275 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
277 /* The hardware does not support setting an exception flag without triggering
278 a trap, except through the "Ignore Exceptions Mode", bits FE0 and FE1 of
279 the MSR register set to zero, that can be obtained through a system call:
280 - On Linux and NetBSD: prctl (PR_SET_FPEXC, PR_FP_EXC_DISABLED);
281 - On AIX: fp_trap (FP_TRAP_OFF);
282 But that is not what we need here, as it would have a persistent effect on
284 exceptions
&= FE_ALL_EXCEPT
;
286 unsigned int desired_flags
= (unsigned int) *saved_flags
;
287 desired_flags
&= exceptions
;
289 union { unsigned long long u
; double f
; } memenv
, orig_memenv
;
290 _FPU_GETCW_AS_DOUBLE (memenv
.f
);
291 orig_memenv
= memenv
;
293 /* Instead of clearing FE_INVALID (= bit 29), we need to clear the
295 memenv
.u
&= ~ (exceptions
& FE_INVALID
296 ? (exceptions
& ~FE_INVALID
) | 0x01F80700U
298 /* Instead of setting FE_INVALID (= bit 29), we need to set one of the
299 individual bits: bit 10 or, if that does not work, bit 24. */
300 memenv
.u
|= (desired_flags
& FE_INVALID
301 ? (desired_flags
& ~FE_INVALID
) | (1U << 10)
304 if (!(memenv
.u
== orig_memenv
.u
))
306 if (memenv
.u
& (exceptions
>> 22))
308 /* Setting the exception flags may trigger a trap.
309 ISO C 23 § 7.6.4.5 does not allow it. */
312 _FPU_SETCW_AS_DOUBLE (memenv
.f
);
313 if (desired_flags
& FE_INVALID
)
316 _FPU_GETCW_AS_DOUBLE (memenv
.f
);
317 if ((memenv
.u
& FE_INVALID
) == 0)
319 memenv
.u
|= (1U << 24);
320 _FPU_SETCW_AS_DOUBLE (memenv
.f
);
328 # elif defined __riscv
330 /* On all OSes except FreeBSD, fexcept_t is binary-equivalent to
332 On FreeBSD, it is equivalent to an 'unsigned long'.
333 A simple C cast does the necessary conversion. */
336 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
338 # if 1 /* both should be equivalent */
339 exceptions
&= FE_ALL_EXCEPT
;
341 unsigned int desired_flags
= (unsigned int) *saved_flags
;
342 desired_flags
&= exceptions
;
344 __asm__
__volatile__ ("csrc fflags, %0" : : "r" (exceptions
));
345 __asm__
__volatile__ ("csrs fflags, %0" : : "r" (desired_flags
));
347 unsigned int desired_flags
= (unsigned int) *saved_flags
;
348 unsigned int fcsr
, orig_fcsr
;
349 __asm__
__volatile__ ("frflags %0" : "=r" (orig_fcsr
)); /* same as "csrr %0, fflags" */
350 fcsr
= orig_fcsr
^ ((orig_fcsr
^ desired_flags
) & FE_ALL_EXCEPT
& exceptions
);
351 if (fcsr
!= orig_fcsr
)
352 __asm__
__volatile__ ("fsflags %0" : : "r" (fcsr
)); /* same as "csrw fflags, %0" */
358 # elif defined __s390__ || defined __s390x__
360 /* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */
363 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
365 unsigned int desired_flags
= (unsigned int) *saved_flags
;
366 unsigned int fpc
, orig_fpc
;
367 _FPU_GETCW (orig_fpc
);
368 # if FE_INEXACT == 8 /* glibc compatible FE_* values */
369 fpc
= orig_fpc
^ ((orig_fpc
^ (desired_flags
<< 16)) & ((FE_ALL_EXCEPT
& exceptions
) << 16));
370 if ((fpc
& 0x00000300) == 0)
371 /* Clear the corresponding bits of the "data exception code". */
372 fpc
&= ~((FE_ALL_EXCEPT
& exceptions
) << 8);
373 # else /* musl libc compatible FE_* values */
374 fpc
= orig_fpc
^ ((orig_fpc
^ desired_flags
) & FE_ALL_EXCEPT
& exceptions
);
375 if ((fpc
& 0x00000300) == 0)
376 /* Clear the corresponding bits of the "data exception code". */
377 fpc
&= ~((FE_ALL_EXCEPT
& exceptions
) >> 8);
381 /* Possibly some new exception flags have been set. But just storing them
382 does not cause a trap to be executed (when traps are enabled). */
386 # elif defined __sh__
388 /* On glibc, fexcept_t is binary-equivalent to an 'unsigned short'.
389 On all other OSes, fexcept_t is binary-equivalent to an 'unsigned int'.
390 A simple C cast does the necessary conversion. */
393 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
395 unsigned int desired_flags
= (unsigned int) *saved_flags
;
396 unsigned int fpscr
, orig_fpscr
;
397 _FPU_GETCW (orig_fpscr
);
398 fpscr
= orig_fpscr
^ ((orig_fpscr
^ desired_flags
) & FE_ALL_EXCEPT
& exceptions
);
399 if (fpscr
!= orig_fpscr
)
401 /* Possibly some new exception flags have been set. But just storing them
402 does not cause a trap to be executed (when traps are enabled). */
406 # elif defined __sparc
408 /* On all OSes except Solaris, fexcept_t is binary-equivalent to an 'unsigned long'.
409 On Solaris, fexcept_t is an 'int'.
410 A simple C cast does the necessary conversion. */
413 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
415 unsigned long desired_flags
= (unsigned long) *saved_flags
;
416 unsigned long fsr
, orig_fsr
;
417 _FPU_GETCW (orig_fsr
);
418 # if FE_INEXACT == 32 /* glibc compatible FE_* values */
419 fsr
= orig_fsr
^ ((orig_fsr
^ desired_flags
) & FE_ALL_EXCEPT
& exceptions
);
420 # else /* Solaris compatible FE_* values */
421 fsr
= orig_fsr
^ ((orig_fsr
^ (desired_flags
<< 5)) & ((FE_ALL_EXCEPT
& exceptions
) << 5));
425 /* Possibly some new exception flags have been set. But just storing them
426 does not cause a trap to be executed (when traps are enabled). */
432 # if defined __GNUC__ || defined __clang__
433 # warning "Unknown CPU / architecture. Please report your platform and compiler to <bug-gnulib@gnu.org>."
435 # define NEED_FALLBACK 1
441 /* The compiler does not support __asm__ statements or equivalent
444 # if (defined __sun || __GLIBC__ >= 2) && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)) && defined __SUNPRO_C
445 /* Solaris/i386, Solaris/x86_64, glibc/i386, glibc/x86_64, with SunPRO C. */
447 /* On these Solaris platforms, fpsetsticky cannot be used here, because it may
448 generate traps (since fpsetsticky calls _putsw, which modifies the control
449 word of the 387 unit). Instead, we need to modify only the flags in the SSE
452 /* Accessors for the mxcsr register. Fortunately, the SunPRO C compiler
453 supports a poor form of 'asm'. */
456 getssecw (unsigned int *mxcsr_p
)
458 # if defined __x86_64__ || defined _M_X64
459 asm ("stmxcsr (%rdi)");
461 /* The compiler generates a stack frame. Therefore the first argument is in
462 8(%ebp), not in 4(%esp). */
463 asm ("movl 8(%ebp),%eax");
464 asm ("stmxcsr (%eax)");
469 setssecw (unsigned int const *mxcsr_p
)
471 # if defined __x86_64__ || defined _M_X64
472 asm ("ldmxcsr (%rdi)");
474 /* The compiler generates a stack frame. Therefore the first argument is in
475 8(%ebp), not in 4(%esp). */
476 asm ("movl 8(%ebp),%eax");
477 asm ("ldmxcsr (%eax)");
482 /* Clears flags in the 387 unit. */
484 mask387cw (unsigned short mask
)
486 # if defined __x86_64__ || defined _M_X64
487 asm ("fnstenv -32(%rsp)");
488 asm ("andw %di,-28(%rsp)");
489 asm ("fldenv -32(%rsp)");
491 /* The compiler generates a stack frame. Therefore the first argument is in
492 8(%ebp), not in 4(%esp). */
493 asm ("movl 8(%ebp),%eax");
494 asm ("fnstenv -32(%esp)");
495 asm ("andw %ax,-28(%esp)");
496 asm ("fldenv -32(%esp)");
502 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
504 exceptions
&= FE_ALL_EXCEPT
;
505 unsigned int desired_flags
= (unsigned int) *saved_flags
;
507 /* Modify the flags in the SSE unit. */
508 unsigned int mxcsr
, orig_mxcsr
;
509 getssecw (&orig_mxcsr
);
510 mxcsr
= orig_mxcsr
^ ((orig_mxcsr
^ desired_flags
) & exceptions
);
511 if (mxcsr
!= orig_mxcsr
)
515 /* Modify the flags in the 387 unit, but only by clearing bits, not by
517 mask387cw (~ (exceptions
& ~desired_flags
));
523 # elif HAVE_FPSETSTICKY
524 /* FreeBSD ≥ 3.1, NetBSD ≥ 1.1, OpenBSD, IRIX, Solaris, Minix ≥ 3.2. */
526 /* Get fpgetsticky, fpsetsticky. */
528 /* The type is called 'fp_except_t' on FreeBSD, but 'fp_except' on
529 all other systems. */
530 # if !defined __FreeBSD__
531 # define fp_except_t fp_except
535 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
537 unsigned long desired_flags
= (unsigned long) *saved_flags
;
538 fp_except_t flags
, orig_flags
;
539 orig_flags
= fpgetsticky ();
540 flags
= orig_flags
^ ((orig_fsr
^ desired_flags
) & FE_ALL_EXCEPT
& exceptions
);
541 if (flags
!= orig_flags
)
543 /* Possibly some new exception flags have been set. But just storing them
544 does not cause a trap to be executed (when traps are enabled). */
548 # elif defined _AIX && defined __powerpc__ /* AIX */
556 <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-clr-flag-fp-set-flag-fp-read-flag-fp-swap-flag-subroutine> */
559 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)
561 exceptions
&= FE_ALL_EXCEPT
;
563 unsigned int desired_flags
= (unsigned int) *saved_flags
;
564 int exceptions_to_clear
= exceptions
& ~desired_flags
;
565 int exceptions_to_set
= exceptions
& desired_flags
;
567 fpflag_t orig_flags
= fp_read_flag ();
568 /* In addition to clearing FE_INVALID (= bit 29), we also need to clear the
570 fpflag_t f_to_clear
=
571 exceptions_to_fpflag (exceptions_to_clear
)
572 | (exceptions_to_clear
& FE_INVALID
? 0x01F80700U
: 0);
573 /* Instead of setting FE_INVALID (= bit 29), we need to set one of the
574 individual bits: bit 10 or, if that does not work, bit 24. */
576 (exceptions_to_set
& FE_INVALID
577 ? exceptions_to_fpflag (exceptions_to_set
& ~FE_INVALID
) | (1U << 10)
578 : exceptions_to_fpflag (exceptions_to_set
));
580 fp_clr_flag (f_to_clear
);
583 if ((fegetexcept_impl () & exceptions
) != 0)
585 /* Setting the exception flags may trigger a trap.
586 ISO C 23 § 7.6.4.5 does not allow it. */
589 fp_set_flag (f_to_set
);
590 if (exceptions
& FE_INVALID
)
593 if ((fp_read_flag () & FP_INVALID
) == 0)
594 fp_set_flag (1U << 24);
603 # define NEED_FALLBACK 1
611 /* A dummy fallback. */
614 fesetexceptflag (fexcept_t
const *saved_flags
, int exceptions
)