exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / fenv-except-state-get.c
blob1924689a91391160212c8a81c74d330009b54d52
1 /* Functions for saving the floating-point exception status flags.
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>/{fgetexcptflg.c,fsetexcptflg.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 #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. */
36 int
37 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
39 /* Just like fetestexcept. */
40 # if defined _MSC_VER
42 unsigned int mxcsr;
43 _FPU_GETSSECW (mxcsr);
44 *saved_flags = x86hardware_to_exceptions (mxcsr) & FE_ALL_EXCEPT & exceptions;
46 # else
48 unsigned short fstat;
49 _FPU_GETSTAT (fstat);
51 unsigned int mxcsr = 0;
52 if (CPU_HAS_SSE ())
54 /* Look at the flags in the SSE unit as well. */
55 _FPU_GETSSECW (mxcsr);
58 *saved_flags = x86hardware_to_exceptions (fstat | mxcsr)
59 & FE_ALL_EXCEPT & exceptions;
60 # endif
62 return 0;
65 # elif defined __aarch64__ /* arm64 */
67 /* On Linux, NetBSD, and Android, fexcept_t is binary-equivalent to
68 an 'unsigned int'.
69 On macOS, fexcept_t is binary-equivalent to an 'unsigned short'.
70 On FreeBSD and OpenBSD, fexcept_t is binary-equivalent to an 'unsigned long'.
71 A simple C cast does the necessary conversion. */
73 int
74 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
76 /* Just like fetestexcept. */
77 unsigned long fpsr;
78 _FPU_GETFPSR (fpsr);
79 *saved_flags = fpsr & FE_ALL_EXCEPT & exceptions;
80 return 0;
83 # elif defined __arm__
85 /* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */
87 int
88 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
90 /* Just like fetestexcept. */
91 # ifdef __SOFTFP__
92 return -1;
93 # else
94 unsigned int fpscr;
95 _FPU_GETCW (fpscr);
96 *saved_flags = fpscr & FE_ALL_EXCEPT & exceptions;
97 return 0;
98 # endif
101 # elif defined __alpha
103 /* On all OSes except NetBSD and OpenBSD, fexcept_t is binary-equivalent to
104 an 'unsigned long'.
105 On NetBSD, it is equivalent to an 'unsigned short'.
106 On OpenBSD, it is equivalent to an 'unsigned int'.
107 A simple C cast does the necessary conversion. */
110 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
112 /* Just like fetestexcept. */
113 unsigned long swcr = __ieee_get_fp_control ();
114 *saved_flags = swcr & FE_ALL_EXCEPT & exceptions;
115 return 0;
118 # elif defined __hppa
120 /* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */
123 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
125 /* Just like fetestexcept. */
126 union { unsigned long long fpreg; unsigned int halfreg[2]; } s;
127 /* Get the current status word. */
128 __asm__ __volatile__ ("fstd %%fr0,0(%1)" : "=m" (s.fpreg) : "r" (&s.fpreg) : "%r0");
129 *saved_flags = (s.halfreg[0] >> 27) & FE_ALL_EXCEPT & exceptions;
130 return 0;
133 # elif defined __ia64__
135 /* On all OSes except NetBSD, fexcept_t is binary-equivalent to
136 an 'unsigned long'.
137 On NetBSD, it is equivalent to an 'unsigned short'.
138 A simple C cast does the necessary conversion. */
141 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
143 /* Just like fetestexcept. */
144 unsigned long fpsr;
145 _FPU_GETCW (fpsr);
146 *saved_flags = (fpsr >> 13) & FE_ALL_EXCEPT & exceptions;
147 return 0;
150 # elif defined __m68k__
152 /* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */
155 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
157 /* Just like fetestexcept. */
158 unsigned int fpsr;
159 _FPU_GETFPSR (fpsr);
160 *saved_flags = fpsr & FE_ALL_EXCEPT & exceptions;
161 return 0;
164 # elif defined __mips__
166 /* On all OSes except NetBSD and OpenBSD, fexcept_t is binary-equivalent to
167 an 'unsigned short'.
168 On NetBSD and OpenBSD, it is equivalent to an 'unsigned int'.
169 A simple C cast does the necessary conversion. */
172 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
174 /* Just like fetestexcept. */
175 unsigned int fcsr;
176 _FPU_GETCW (fcsr);
177 *saved_flags = fcsr & FE_ALL_EXCEPT & exceptions;
178 return 0;
181 # elif defined __loongarch__
183 /* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */
186 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
188 /* Just like fetestexcept. */
189 unsigned int fcsr;
190 _FPU_GETCW (fcsr);
191 *saved_flags = fcsr & FE_ALL_EXCEPT & exceptions;
192 return 0;
195 # elif defined __powerpc__
197 /* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */
200 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
202 /* Just like fetestexcept. */
203 union { unsigned long long u; double f; } memenv;
204 _FPU_GETCW_AS_DOUBLE (memenv.f);
205 *saved_flags = memenv.u & FE_ALL_EXCEPT & exceptions;
206 return 0;
209 # elif defined __riscv
211 /* On all OSes except FreeBSD, fexcept_t is binary-equivalent to
212 an 'unsigned int'.
213 On FreeBSD, it is equivalent to an 'unsigned long'.
214 A simple C cast does the necessary conversion. */
217 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
219 /* Just like fetestexcept. */
220 unsigned int flags;
221 __asm__ __volatile__ ("frflags %0" : "=r" (flags)); /* same as "csrr %0, fflags" */
222 *saved_flags = flags & FE_ALL_EXCEPT & exceptions;
223 return 0;
226 # elif defined __s390__ || defined __s390x__
228 /* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'. */
231 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
233 /* Just like fetestexcept. */
234 unsigned int fpc;
235 _FPU_GETCW (fpc);
236 # if FE_INEXACT == 8 /* glibc compatible FE_* values */
237 *saved_flags = ((fpc >> 16) | ((fpc & 0x00000300) == 0 ? fpc >> 8 : 0))
238 & FE_ALL_EXCEPT & exceptions;
239 # else /* musl libc compatible FE_* values */
240 *saved_flags = (fpc | ((fpc & 0x00000300) == 0 ? fpc << 8 : 0))
241 & FE_ALL_EXCEPT & exceptions;
242 # endif
243 return 0;
246 # elif defined __sh__
248 /* On glibc, fexcept_t is binary-equivalent to an 'unsigned short'.
249 On all other OSes, fexcept_t is binary-equivalent to an 'unsigned int'.
250 A simple C cast does the necessary conversion. */
253 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
255 /* Just like fetestexcept. */
256 unsigned int fpscr;
257 _FPU_GETCW (fpscr);
258 *saved_flags = fpscr & FE_ALL_EXCEPT & exceptions;
259 return 0;
262 # elif defined __sparc
264 /* On all OSes except Solaris, fexcept_t is binary-equivalent to an 'unsigned long'.
265 On Solaris, fexcept_t is an 'int'.
266 A simple C cast does the necessary conversion. */
269 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
271 /* Just like fetestexcept. */
272 unsigned long fsr;
273 _FPU_GETCW (fsr);
274 # if FE_INEXACT == 32 /* glibc compatible FE_* values */
275 *saved_flags = fsr & FE_ALL_EXCEPT & exceptions;
276 # else /* Solaris compatible FE_* values */
277 *saved_flags = (fsr >> 5) & FE_ALL_EXCEPT & exceptions;
278 # endif
279 return 0;
282 # else
284 # if defined __GNUC__ || defined __clang__
285 # warning "Unknown CPU / architecture. Please report your platform and compiler to <bug-gnulib@gnu.org>."
286 # endif
287 # define NEED_FALLBACK 1
289 # endif
291 #else
293 /* The compiler does not support __asm__ statements or equivalent
294 intrinsics. */
296 # if HAVE_FPSETSTICKY
297 /* FreeBSD ≥ 3.1, NetBSD ≥ 1.1, OpenBSD, IRIX, Solaris, Minix ≥ 3.2. */
299 /* Get fpgetsticky, fpsetsticky. */
300 # include <ieeefp.h>
301 /* The type is called 'fp_except_t' on FreeBSD, but 'fp_except' on
302 all other systems. */
303 # if !defined __FreeBSD__
304 # define fp_except_t fp_except
305 # endif
308 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
310 /* Just like fetestexcept. */
311 fp_except_t flags = fpgetsticky ();
312 *saved_flags = flags & FE_ALL_EXCEPT & exceptions;
313 return 0;
316 # elif defined _AIX && defined __powerpc__ /* AIX */
318 # include <float.h>
319 # include <fpxcp.h>
321 /* Documentation:
322 <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-clr-flag-fp-set-flag-fp-read-flag-fp-swap-flag-subroutine> */
325 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
327 /* Just like fetestexcept. */
328 fpflag_t flags = fp_read_flag ();
329 *saved_flags = fpflag_to_exceptions (flags) & FE_ALL_EXCEPT & exceptions;
330 return 0;
333 # else
335 # define NEED_FALLBACK 1
337 # endif
339 #endif
341 #if NEED_FALLBACK
343 /* A dummy fallback. */
346 fegetexceptflag (fexcept_t *saved_flags, int exceptions)
348 /* Just like fetestexcept. */
349 *saved_flags = 0;
350 return 0;
353 #endif