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>/fesetexcept.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 fesetexcept (int exceptions
)
34 exceptions
&= FE_ALL_EXCEPT
;
37 exceptions
= exceptions_to_x86hardware (exceptions
);
39 /* Set the flags in the SSE unit. */
40 unsigned int mxcsr
, orig_mxcsr
;
41 _FPU_GETSSECW (orig_mxcsr
);
42 mxcsr
= orig_mxcsr
| exceptions
;
43 if (mxcsr
!= orig_mxcsr
)
44 _FPU_SETSSECW (mxcsr
);
48 /* We can set the flags in the 387 unit or in the SSE unit.
49 Either works, due to the way fetestexcept() is implemented.
50 Choose the simplest approach. */
51 # if defined __x86_64__ || defined _M_X64
52 /* Set the flags in the SSE unit. */
53 unsigned int mxcsr
, orig_mxcsr
;
54 _FPU_GETSSECW (orig_mxcsr
);
55 mxcsr
= orig_mxcsr
| exceptions
;
56 if (mxcsr
!= orig_mxcsr
)
57 _FPU_SETSSECW (mxcsr
);
61 /* Set the flags in the SSE unit. */
62 unsigned int mxcsr
, orig_mxcsr
;
63 _FPU_GETSSECW (orig_mxcsr
);
64 mxcsr
= orig_mxcsr
| exceptions
;
65 if (mxcsr
!= orig_mxcsr
)
66 _FPU_SETSSECW (mxcsr
);
70 /* Set the flags in the 387 unit. */
72 __asm__
__volatile__ ("fnstenv %0" : "=m" (*&env
));
73 /* Note: fnstenv masks all floating-point exceptions until the fldenv
75 env
.__status_word
|= exceptions
;
76 if ((~env
.__control_word
) & exceptions
)
78 /* Setting the exception flags may trigger a trap (at the next
79 floating-point instruction, but that does not matter).
80 ISO C 23 § 7.6.4.4 does not allow it. */
81 __asm__
__volatile__ ("fldcw %0" : : "m" (*&env
.__control_word
));
84 __asm__
__volatile__ ("fldenv %0" : : "m" (*&env
));
92 # elif defined __aarch64__ /* arm64 */
95 fesetexcept (int exceptions
)
97 exceptions
&= FE_ALL_EXCEPT
;
99 unsigned long fpsr
, orig_fpsr
;
100 _FPU_GETFPSR (orig_fpsr
);
101 fpsr
= orig_fpsr
| exceptions
;
102 if (fpsr
!= orig_fpsr
)
108 # elif defined __arm__
111 fesetexcept (int exceptions
)
113 exceptions
&= FE_ALL_EXCEPT
;
119 unsigned int fpscr
, orig_fpscr
;
120 _FPU_GETCW (orig_fpscr
);
121 fpscr
= orig_fpscr
| exceptions
;
122 if (fpscr
!= orig_fpscr
)
128 # elif defined __alpha
131 fesetexcept (int exceptions
)
133 exceptions
&= FE_ALL_EXCEPT
;
135 unsigned long swcr
, orig_swcr
;
136 orig_swcr
= __ieee_get_fp_control ();
137 swcr
= orig_swcr
| exceptions
;
138 if (swcr
!= orig_swcr
)
139 __ieee_set_fp_control (swcr
);
144 # elif defined __hppa
147 fesetexcept (int exceptions
)
149 exceptions
&= FE_ALL_EXCEPT
;
151 union { unsigned long long fpreg
; unsigned int halfreg
[2]; } s
;
152 /* Get the current status word. */
153 __asm__
__volatile__ ("fstd %%fr0,0(%1)" : "=m" (s
.fpreg
) : "r" (&s
.fpreg
) : "%r0");
154 unsigned int old_halfreg0
= s
.halfreg
[0];
155 /* Clear all the relevant bits. */
156 s
.halfreg
[0] |= ((unsigned int) exceptions
<< 27);
157 if (s
.halfreg
[0] != old_halfreg0
)
159 /* Store the new status word. */
160 __asm__
__volatile__ ("fldd 0(%0),%%fr0" : : "r" (&s
.fpreg
), "m" (s
.fpreg
) : "%r0");
166 # elif defined __ia64__
169 fesetexcept (int exceptions
)
171 exceptions
&= FE_ALL_EXCEPT
;
173 unsigned long fpsr
, orig_fpsr
;
174 _FPU_GETCW (orig_fpsr
);
175 fpsr
= orig_fpsr
| (unsigned long) (exceptions
<< 13);
176 if (fpsr
!= orig_fpsr
)
182 # elif defined __m68k__
185 fesetexcept (int exceptions
)
187 exceptions
&= FE_ALL_EXCEPT
;
189 unsigned int fpsr
, orig_fpsr
;
190 _FPU_GETFPSR (orig_fpsr
);
191 fpsr
= orig_fpsr
| exceptions
;
192 if (fpsr
!= orig_fpsr
)
198 # elif defined __mips__
201 fesetexcept (int exceptions
)
203 exceptions
&= FE_ALL_EXCEPT
;
205 unsigned int fcsr
, orig_fcsr
;
206 _FPU_GETCW (orig_fcsr
);
207 fcsr
= orig_fcsr
| exceptions
;
208 if (fcsr
!= orig_fcsr
)
214 # elif defined __loongarch__
217 fesetexcept (int exceptions
)
219 exceptions
&= FE_ALL_EXCEPT
;
221 unsigned int fcsr
, orig_fcsr
;
222 _FPU_GETCW (orig_fcsr
);
223 fcsr
= orig_fcsr
| exceptions
;
224 if (fcsr
!= orig_fcsr
)
230 # elif defined __powerpc__
233 fesetexcept (int exceptions
)
235 /* The hardware does not support setting an exception flag without triggering
236 a trap, except through the "Ignore Exceptions Mode", bits FE0 and FE1 of
237 the MSR register set to zero, that can be obtained through a system call:
238 - On Linux and NetBSD: prctl (PR_SET_FPEXC, PR_FP_EXC_DISABLED);
239 - On AIX: fp_trap (FP_TRAP_OFF);
240 But that is not what we need here, as it would have a persistent effect on
242 exceptions
&= FE_ALL_EXCEPT
;
244 union { unsigned long long u
; double f
; } memenv
, orig_memenv
;
245 _FPU_GETCW_AS_DOUBLE (memenv
.f
);
246 orig_memenv
= memenv
;
248 /* Instead of setting FE_INVALID (= bit 29), we need to set one of the
249 individual bits: bit 10 or, if that does not work, bit 24. */
250 memenv
.u
|= (exceptions
& FE_INVALID
251 ? (exceptions
& ~FE_INVALID
) | (1U << 10)
254 if (!(memenv
.u
== orig_memenv
.u
))
256 if (memenv
.u
& (exceptions
>> 22))
258 /* Setting the exception flags may trigger a trap.
259 ISO C 23 § 7.6.4.4 does not allow it. */
262 _FPU_SETCW_AS_DOUBLE (memenv
.f
);
263 if (exceptions
& FE_INVALID
)
266 _FPU_GETCW_AS_DOUBLE (memenv
.f
);
267 if ((memenv
.u
& FE_INVALID
) == 0)
269 memenv
.u
|= (1U << 24);
270 _FPU_SETCW_AS_DOUBLE (memenv
.f
);
278 # elif defined __riscv
281 fesetexcept (int exceptions
)
283 /* This is identical to feraiseexcept(), because the hardware does not
284 support trapping on floating-point exceptions. */
285 exceptions
&= FE_ALL_EXCEPT
;
286 __asm__
__volatile__ ("csrs fflags, %0" : : "r" (exceptions
));
290 # elif defined __s390__ || defined __s390x__
293 fesetexcept (int exceptions
)
295 exceptions
&= FE_ALL_EXCEPT
;
297 unsigned int fpc
, orig_fpc
;
298 _FPU_GETCW (orig_fpc
);
299 # if FE_INEXACT == 8 /* glibc compatible FE_* values */
300 fpc
= orig_fpc
| (exceptions
<< 16);
301 # else /* musl libc compatible FE_* values */
302 fpc
= orig_fpc
| exceptions
;
310 # elif defined __sh__
313 fesetexcept (int exceptions
)
315 exceptions
&= FE_ALL_EXCEPT
;
317 unsigned int fpscr
, orig_fpscr
;
318 _FPU_GETCW (orig_fpscr
);
319 fpscr
= orig_fpscr
| exceptions
;
320 if (fpscr
!= orig_fpscr
)
326 # elif defined __sparc
329 fesetexcept (int exceptions
)
331 exceptions
&= FE_ALL_EXCEPT
;
333 unsigned long fsr
, orig_fsr
;
334 _FPU_GETCW (orig_fsr
);
335 # if FE_INEXACT == 32 /* glibc compatible FE_* values */
336 fsr
= orig_fsr
| exceptions
;
337 # else /* Solaris compatible FE_* values */
338 fsr
= orig_fsr
| (exceptions
<< 5);
348 # if defined __GNUC__ || defined __clang__
349 # warning "Unknown CPU / architecture. Please report your platform and compiler to <bug-gnulib@gnu.org>."
351 # define NEED_FALLBACK 1
357 /* The compiler does not support __asm__ statements or equivalent
360 # if defined __sun && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)) && defined __SUNPRO_C
361 /* Solaris/i386, Solaris/x86_64. */
363 /* On these platforms, fpsetsticky cannot be used here, because it may generate
364 traps (since fpsetsticky calls _putsw, which modifies the control word of the
365 387 unit). Instead, we need to modify only the flags in the SSE unit. */
367 /* Accessors for the mxcsr register. Fortunately, the Solaris cc supports a
368 poor form of 'asm'. */
371 getssecw (unsigned int *mxcsr_p
)
373 # if defined __x86_64__ || defined _M_X64
374 asm ("stmxcsr (%rdi)");
376 /* The compiler generates a stack frame. Therefore the first argument is in
377 8(%ebp), not in 4(%esp). */
378 asm ("movl 8(%ebp),%eax");
379 asm ("stmxcsr (%eax)");
384 setssecw (unsigned int const *mxcsr_p
)
386 # if defined __x86_64__ || defined _M_X64
387 asm ("ldmxcsr (%rdi)");
389 /* The compiler generates a stack frame. Therefore the first argument is in
390 8(%ebp), not in 4(%esp). */
391 asm ("movl 8(%ebp),%eax");
392 asm ("ldmxcsr (%eax)");
397 fesetexcept (int exceptions
)
399 exceptions
&= FE_ALL_EXCEPT
;
401 /* Set the flags in the SSE unit. */
402 unsigned int mxcsr
, orig_mxcsr
;
403 getssecw (&orig_mxcsr
);
404 mxcsr
= orig_mxcsr
| exceptions
;
405 if (mxcsr
!= orig_mxcsr
)
411 # elif HAVE_FPSETSTICKY
412 /* FreeBSD ≥ 3.1, NetBSD ≥ 1.1, OpenBSD, IRIX, Solaris, Minix ≥ 3.2. */
414 /* Get fpgetsticky, fpsetsticky. */
416 /* The type is called 'fp_except_t' on FreeBSD, but 'fp_except' on
417 all other systems. */
418 # if !defined __FreeBSD__
419 # define fp_except_t fp_except
423 fesetexcept (int exceptions
)
425 exceptions
&= FE_ALL_EXCEPT
;
427 fp_except_t flags
, orig_flags
;
428 orig_flags
= fpgetsticky ();
429 flags
= orig_flags
| exceptions
;
430 if (flags
!= orig_flags
)
436 # elif defined _AIX && defined __powerpc__ /* AIX */
444 <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-clr-flag-fp-set-flag-fp-read-flag-fp-swap-flag-subroutine> */
447 fesetexcept (int exceptions
)
449 exceptions
&= FE_ALL_EXCEPT
;
451 /* Instead of setting FE_INVALID (= bit 29), we need to set one of the
452 individual bits: bit 10 or, if that does not work, bit 24. */
454 (exceptions
& FE_INVALID
455 ? exceptions_to_fpflag (exceptions
& ~FE_INVALID
) | (1U << 10)
456 : exceptions_to_fpflag (exceptions
));
459 if ((fegetexcept_impl () & exceptions
) != 0)
461 /* Setting the exception flags may trigger a trap.
462 ISO C 23 § 7.6.4.4 does not allow it. */
465 fp_set_flag (f_to_set
);
466 if (exceptions
& FE_INVALID
)
469 if ((fp_read_flag () & FP_INVALID
) == 0)
470 fp_set_flag (1U << 24);
479 # define NEED_FALLBACK 1
487 /* A dummy fallback. */
490 fesetexcept (int exceptions
)
492 exceptions
&= FE_ALL_EXCEPT
;