exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / fenv-except-tracking-raise.c
blobf8ed9bc3358efd956c9ce88419a5db5628a9503a
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 _GL_UNUSED static void
28 generic_feraiseexcept (int exceptions)
30 /* First: invalid exception. */
31 if (exceptions & FE_INVALID)
33 double volatile a;
34 _GL_UNUSED double volatile b;
35 a = 0; b = a / a;
37 /* Next: division by zero. */
38 if (exceptions & FE_DIVBYZERO)
40 double volatile a, b;
41 _GL_UNUSED double volatile c;
42 a = 1; b = 0; c = a / b;
44 /* Next: overflow. */
45 if (exceptions & FE_OVERFLOW)
47 double volatile a;
48 _GL_UNUSED double volatile b;
49 a = 1e200; b = a * a;
51 /* Next: underflow. */
52 if (exceptions & FE_UNDERFLOW)
54 double volatile a;
55 _GL_UNUSED double volatile b;
56 a = 1e-200; b = a * a;
58 /* Last: inexact. */
59 if (exceptions & FE_INEXACT)
61 double volatile a, b;
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)
71 int
72 feraiseexcept (int exceptions)
74 # if defined _MSC_VER
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);
80 # else
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)
91 double volatile a;
92 _GL_UNUSED double volatile b;
93 a = 0; b = a / a;
95 /* Next: division by zero. */
96 if (exceptions & FE_DIVBYZERO)
98 double volatile a, b;
99 _GL_UNUSED double volatile c;
100 a = 1; b = 0; c = a / b;
103 else
105 /* The general case. */
107 /* Set the bits in the 387 unit. */
108 x86_387_fenv_t env;
109 __asm__ __volatile__ ("fnstenv %0" : "=m" (*&env));
110 /* Note: fnstenv masks all floating-point exceptions until the fldenv
111 below. */
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");
119 # endif
120 return 0;
123 # elif defined __aarch64__ /* arm64 */
126 feraiseexcept (int exceptions)
128 # if 0
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)
137 _FPU_SETFPSR (fpsr);
138 # else
139 /* This is how glibc does it.
140 The drawback is that when FE_OVERFLOW is raised, FE_INEXACT is raised
141 with it. */
142 generic_feraiseexcept (exceptions);
143 # endif
144 return 0;
147 # elif defined __arm__
150 feraiseexcept (int exceptions)
152 # ifdef __SOFTFP__
153 exceptions &= FE_ALL_EXCEPT;
155 if (exceptions != 0)
156 return -1;
157 # else
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)
174 _FPU_SETCW (fpscr);
175 _FPU_GETCW (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)
185 _FPU_SETCW (fpscr);
186 _FPU_GETCW (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)
196 _FPU_SETCW (fpscr);
197 _FPU_GETCW (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)
207 _FPU_SETCW (fpscr);
208 _FPU_GETCW (fpscr);
211 /* Last: inexact. */
212 if (exceptions & FE_INEXACT)
214 _FPU_GETCW (orig_fpscr);
215 fpscr = orig_fpscr | FE_INEXACT;
216 if (fpscr != orig_fpscr)
218 _FPU_SETCW (fpscr);
219 _FPU_GETCW (fpscr);
222 # endif
223 return 0;
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);
236 return 0;
238 # endif
240 # elif defined __hppa
243 feraiseexcept (int exceptions)
245 generic_feraiseexcept (exceptions);
246 return 0;
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);
259 return 0;
262 # elif defined __m68k__
265 feraiseexcept (int exceptions)
267 generic_feraiseexcept (exceptions);
268 return 0;
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)
285 _FPU_SETCW (fcsr);
287 return 0;
290 # elif defined __loongarch__
293 feraiseexcept (int exceptions)
295 # if 0
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)
307 _FPU_SETCW (fcsr);
308 # else
309 /* This is how glibc does it.
310 The drawback is that when FE_OVERFLOW is raised, FE_INEXACT is raised
311 with it. */
312 generic_feraiseexcept (exceptions);
313 # endif
314 return 0;
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)
332 : exceptions);
334 if (!(memenv.u == orig_memenv.u))
336 _FPU_SETCW_AS_DOUBLE (memenv.f);
337 if (exceptions & FE_INVALID)
339 /* Did it work? */
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);
349 return 0;
352 # elif defined __riscv
355 feraiseexcept (int exceptions)
357 exceptions &= FE_ALL_EXCEPT;
358 __asm__ __volatile__ ("csrs fflags, %0" : : "r" (exceptions));
359 return 0;
362 # elif defined __s390__ || defined __s390x__
365 feraiseexcept (int exceptions)
367 generic_feraiseexcept (exceptions);
368 return 0;
371 # elif defined __sh__
374 feraiseexcept (int exceptions)
376 # if 0
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)
385 _FPU_SETCW (fpscr);
386 # else
387 /* This is how glibc does it.
388 The drawback is that when FE_OVERFLOW is raised, FE_INEXACT is raised
389 with it. */
390 generic_feraiseexcept (exceptions);
391 # endif
392 return 0;
395 # elif defined __sparc
398 feraiseexcept (int exceptions)
400 # if 0
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);
411 # endif
412 if (fsr != orig_fsr)
413 _FPU_SETCW (fsr);
414 # else
415 /* This is how glibc does it.
416 The drawback is that when FE_OVERFLOW is raised, FE_INEXACT is raised
417 with it. */
418 generic_feraiseexcept (exceptions);
419 # endif
420 return 0;
423 # else
425 # if defined __GNUC__ || defined __clang__
426 # warning "Unknown CPU / architecture. Please report your platform and compiler to <bug-gnulib@gnu.org>."
427 # endif
428 # define NEED_FALLBACK 1
430 # endif
432 #else
434 /* The compiler does not support __asm__ statements or equivalent
435 intrinsics. */
437 # define NEED_FALLBACK 1
439 #endif
441 #if NEED_FALLBACK
443 /* A fallback that should work everywhere. */
446 feraiseexcept (int exceptions)
448 generic_feraiseexcept (exceptions);
449 return 0;
452 #endif