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 _GL_UNUSED
static void
28 generic_feraiseexcept (int exceptions
)
30 /* First: invalid exception. */
31 if (exceptions
& FE_INVALID
)
34 _GL_UNUSED
double volatile b
;
37 /* Next: division by zero. */
38 if (exceptions
& FE_DIVBYZERO
)
41 _GL_UNUSED
double volatile c
;
42 a
= 1; b
= 0; c
= a
/ b
;
45 if (exceptions
& FE_OVERFLOW
)
48 _GL_UNUSED
double volatile b
;
51 /* Next: underflow. */
52 if (exceptions
& FE_UNDERFLOW
)
55 _GL_UNUSED
double volatile b
;
56 a
= 1e-200; b
= a
* a
;
59 if (exceptions
& FE_INEXACT
)
62 _GL_UNUSED
double volatile c
;
63 a
= 1; b
= 3; c
= a
/ b
;
67 #if defined __GNUC__ || defined __clang__ || defined _MSC_VER
69 # if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)
72 feraiseexcept (int exceptions
)
76 /* Setting the exception flags only in the SSE unit (i.e. in the mxcsr
77 register) would not cause the hardware to trap on the exception. */
78 generic_feraiseexcept (exceptions
);
82 exceptions
&= FE_ALL_EXCEPT
;
84 if ((exceptions
& ~(FE_INVALID
| FE_DIVBYZERO
)) == 0)
86 /* Like generic_feraiseexcept (exceptions). */
87 /* This code is probably faster than the general code below. */
88 /* First: invalid exception. */
89 if (exceptions
& FE_INVALID
)
92 _GL_UNUSED
double volatile b
;
95 /* Next: division by zero. */
96 if (exceptions
& FE_DIVBYZERO
)
99 _GL_UNUSED
double volatile c
;
100 a
= 1; b
= 0; c
= a
/ b
;
105 /* The general case. */
107 /* Set the bits in the 387 unit. */
109 __asm__
__volatile__ ("fnstenv %0" : "=m" (*&env
));
110 /* Note: fnstenv masks all floating-point exceptions until the fldenv
112 env
.__status_word
|= exceptions
;
113 __asm__
__volatile__ ("fldenv %0" : : "m" (*&env
));
114 /* A trap (if enabled) is triggered only at the next floating-point
115 instruction. Force it to occur here. */
116 __asm__
__volatile__ ("fwait");
123 # elif defined __aarch64__ /* arm64 */
126 feraiseexcept (int exceptions
)
129 /* This would just set the flag bits and make fetestexcept() work as expected.
130 But it would not cause the hardware to trap on the exception. */
131 exceptions
&= FE_ALL_EXCEPT
;
133 unsigned long fpsr
, orig_fpsr
;
134 _FPU_GETFPSR (orig_fpsr
);
135 fpsr
= orig_fpsr
| exceptions
;
136 if (fpsr
!= orig_fpsr
)
139 /* This is how glibc does it.
140 The drawback is that when FE_OVERFLOW is raised, FE_INEXACT is raised
142 generic_feraiseexcept (exceptions
);
147 # elif defined __arm__
150 feraiseexcept (int exceptions
)
153 exceptions
&= FE_ALL_EXCEPT
;
158 /* Raise exceptions represented by EXCEPTIONS. But we must raise only
159 one signal at a time. It is important that if the overflow/underflow
160 exception and the inexact exception are given at the same time,
161 the overflow/underflow exception follows the inexact exception. After
162 each exception we read from the fpscr, to force the exception to be
163 raised immediately. */
164 /* XXX Probably this should do actual floating-point operations, like in
165 generic_feraiseexcept, not just setting flag bits in the fpscr. */
166 unsigned int fpscr
, orig_fpscr
;
167 /* First: invalid exception. */
168 if (exceptions
& FE_INVALID
)
170 _FPU_GETCW (orig_fpscr
);
171 fpscr
= orig_fpscr
| FE_INVALID
;
172 if (fpscr
!= orig_fpscr
)
178 /* Next: division by zero. */
179 if (exceptions
& FE_DIVBYZERO
)
181 _FPU_GETCW (orig_fpscr
);
182 fpscr
= orig_fpscr
| FE_DIVBYZERO
;
183 if (fpscr
!= orig_fpscr
)
189 /* Next: overflow. */
190 if (exceptions
& FE_OVERFLOW
)
192 _FPU_GETCW (orig_fpscr
);
193 fpscr
= orig_fpscr
| FE_OVERFLOW
;
194 if (fpscr
!= orig_fpscr
)
200 /* Next: underflow. */
201 if (exceptions
& FE_UNDERFLOW
)
203 _FPU_GETCW (orig_fpscr
);
204 fpscr
= orig_fpscr
| FE_UNDERFLOW
;
205 if (fpscr
!= orig_fpscr
)
212 if (exceptions
& FE_INEXACT
)
214 _FPU_GETCW (orig_fpscr
);
215 fpscr
= orig_fpscr
| FE_INEXACT
;
216 if (fpscr
!= orig_fpscr
)
226 # elif defined __alpha
228 /* Prefer the Linux system call when available.
229 See glibc/sysdeps/unix/sysv/linux/alpha/fraiseexcpt.S */
230 # if !defined __linux__
232 feraiseexcept (int exceptions
)
234 /* This implementation cannot raise FE_INEXACT. */
235 generic_feraiseexcept (exceptions
);
240 # elif defined __hppa
243 feraiseexcept (int exceptions
)
245 generic_feraiseexcept (exceptions
);
249 # elif defined __ia64__
252 feraiseexcept (int exceptions
)
254 /* Raise exceptions represented by EXCEPTIONS. But we must raise only
255 one signal at a time. It is important that if the overflow/underflow
256 exception and the inexact exception are given at the same time,
257 the overflow/underflow exception precedes the inexact exception. */
258 generic_feraiseexcept (exceptions
);
262 # elif defined __m68k__
265 feraiseexcept (int exceptions
)
267 generic_feraiseexcept (exceptions
);
271 # elif defined __mips__
274 feraiseexcept (int exceptions
)
276 exceptions
&= FE_ALL_EXCEPT
;
278 /* Set also the cause bits. The setting of the cause bits is what actually
279 causes the hardware to trap on the exception, if the corresponding enable
280 bit is set as well. */
281 unsigned int fcsr
, orig_fcsr
;
282 _FPU_GETCW (orig_fcsr
);
283 fcsr
= orig_fcsr
| ((exceptions
<< 10) | exceptions
);
284 if (fcsr
!= orig_fcsr
)
290 # elif defined __loongarch__
293 feraiseexcept (int exceptions
)
296 /* This would just set the flag bits and make fetestexcept() work as expected.
297 But it would not cause the hardware to trap on the exception. */
298 exceptions
&= FE_ALL_EXCEPT
;
300 /* Set also the cause bits. The setting of the cause bits is what actually
301 causes the hardware to trap on the exception, if the corresponding enable
302 bit is set as well. */
303 unsigned int fcsr
, orig_fcsr
;
304 _FPU_GETCW (orig_fcsr
);
305 fcsr
= orig_fcsr
| ((exceptions
<< 8) | exceptions
);
306 if (fcsr
!= orig_fcsr
)
309 /* This is how glibc does it.
310 The drawback is that when FE_OVERFLOW is raised, FE_INEXACT is raised
312 generic_feraiseexcept (exceptions
);
317 # elif defined __powerpc__
320 feraiseexcept (int exceptions
)
322 exceptions
&= FE_ALL_EXCEPT
;
324 union { unsigned long long u
; double f
; } memenv
, orig_memenv
;
325 _FPU_GETCW_AS_DOUBLE (memenv
.f
);
326 orig_memenv
= memenv
;
328 /* Instead of setting FE_INVALID (= bit 29), we need to set one of the
329 individual bits: bit 10 or, if that does not work, bit 24. */
330 memenv
.u
|= (exceptions
& FE_INVALID
331 ? (exceptions
& ~FE_INVALID
) | (1U << 10)
334 if (!(memenv
.u
== orig_memenv
.u
))
336 _FPU_SETCW_AS_DOUBLE (memenv
.f
);
337 if (exceptions
& FE_INVALID
)
340 _FPU_GETCW_AS_DOUBLE (memenv
.f
);
341 if ((memenv
.u
& FE_INVALID
) == 0)
343 memenv
.u
|= (1U << 24);
344 _FPU_SETCW_AS_DOUBLE (memenv
.f
);
352 # elif defined __riscv
355 feraiseexcept (int exceptions
)
357 exceptions
&= FE_ALL_EXCEPT
;
358 __asm__
__volatile__ ("csrs fflags, %0" : : "r" (exceptions
));
362 # elif defined __s390__ || defined __s390x__
365 feraiseexcept (int exceptions
)
367 generic_feraiseexcept (exceptions
);
371 # elif defined __sh__
374 feraiseexcept (int exceptions
)
377 /* This would just set the flag bits and make fetestexcept() work as expected.
378 But it would not cause the hardware to trap on the exception. */
379 exceptions
&= FE_ALL_EXCEPT
;
381 unsigned int fpscr
, orig_fpscr
;
382 _FPU_GETCW (orig_fpscr
);
383 fpscr
= orig_fpscr
| exceptions
;
384 if (fpscr
!= orig_fpscr
)
387 /* This is how glibc does it.
388 The drawback is that when FE_OVERFLOW is raised, FE_INEXACT is raised
390 generic_feraiseexcept (exceptions
);
395 # elif defined __sparc
398 feraiseexcept (int exceptions
)
401 /* This would just set the flag bits and make fetestexcept() work as expected.
402 But it would not cause the hardware to trap on the exception. */
403 exceptions
&= FE_ALL_EXCEPT
;
405 unsigned long fsr
, orig_fsr
;
406 _FPU_GETCW (orig_fsr
);
407 # if FE_INEXACT == 32 /* glibc compatible FE_* values */
408 fsr
= orig_fsr
| exceptions
;
409 # else /* Solaris compatible FE_* values */
410 fsr
= orig_fsr
| (exceptions
<< 5);
415 /* This is how glibc does it.
416 The drawback is that when FE_OVERFLOW is raised, FE_INEXACT is raised
418 generic_feraiseexcept (exceptions
);
425 # if defined __GNUC__ || defined __clang__
426 # warning "Unknown CPU / architecture. Please report your platform and compiler to <bug-gnulib@gnu.org>."
428 # define NEED_FALLBACK 1
434 /* The compiler does not support __asm__ statements or equivalent
437 # define NEED_FALLBACK 1
443 /* A fallback that should work everywhere. */
446 feraiseexcept (int exceptions
)
448 generic_feraiseexcept (exceptions
);