exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / fenv-env.c
blob69784b254e248b80504ad4af287448b7e35148f0
1 /* Functions for controlling the floating-point environment as a whole.
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>/{fegetenv.c,fesetenv.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"
26 #include "verify.h"
28 /* The big problem of the fegetenv/fesetenv API is that it is not well-defined
29 which parts of the floating-point environment need to be restored by fesetenv
30 and which don't. For example:
31 - On x86_64 and i386, some platforms restore all the x86_387_fenv_t,
32 whereas others restore only the control word and the status words.
33 - On m68k, musl libc restores the fpiar register value, whereas glibc
34 doesn't.
35 Some platforms don't even do this consistently. For example, musl libc on
36 sh4 restores the precision mode bit (fpscr bit 19) during an fesetenv of a
37 previously saved environment, but not during an fesetenv (FE_DFL_ENV)
38 invocation. */
40 #if defined _AIX && defined __powerpc__ /* AIX */
42 /* On AIX, fenv_t is a struct { unsigned short rmode; unsigned int fpstat, trapstate; }. */
44 /* On AIX, the register fpscr is augmented with a 32-bit word named fpscrx
45 in thread-local storage. Therefore AIX does complex things behind the scene,
46 and its best to use the system-provided fegetenv() and fesetenv() functions
47 and just add the missing behaviour, namely saving and restoring the exception
48 trap bits. */
50 # include <float.h>
51 # include <fpxcp.h>
53 # include <fptrap.h>
55 int
56 fegetenv (fenv_t *envp)
57 #undef fegetenv
59 /* Invoke the original fegetenv(), and additionally save the exception trap
60 bits (fpscr bits 7..3). */
61 union { unsigned long long u; double f; } memenv;
62 _FPU_GETCW_AS_DOUBLE (memenv.f);
63 fegetenv (envp);
64 /* AIX's original fegetenv() sets envp->trapstate = 0. */
65 envp->trapstate = FE_ALL_EXCEPT & ((unsigned int) memenv.u << 22);
66 return 0;
69 int
70 fesetenv (fenv_t const *envp)
71 #undef fesetenv
73 /* Invoke the original fesetenv(), and additionally restore the exception trap
74 bits (fpscr bits 7..3). */
75 if (fesetenv (envp) != 0)
76 return -1;
77 union { unsigned long long u; double f; } orig_memenv, memenv;
78 _FPU_GETCW_AS_DOUBLE (orig_memenv.f);
79 unsigned int trapbits;
80 if (envp == FE_DFL_ENV)
81 trapbits = 0;
82 else
83 trapbits = (envp->trapstate & FE_ALL_EXCEPT) >> 22;
84 memenv.u = (orig_memenv.u & ~0x000000f8) | trapbits;
85 if (!(memenv.u == orig_memenv.u))
87 if ((orig_memenv.u & 0x000000f8) == 0 && (memenv.u & 0x000000f8) != 0)
89 /* Put the thread into precise trapping mode. */
90 /* Documentation: <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-trap-subroutine> */
91 fp_trap (FP_TRAP_SYNC);
93 _FPU_SETCW_AS_DOUBLE (memenv.f);
94 if ((orig_memenv.u & 0x000000f8) != 0 && (memenv.u & 0x000000f8) == 0)
96 /* Put the thread into no-trapping mode. */
97 /* Documentation: <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-trap-subroutine> */
98 fp_trap (FP_TRAP_OFF);
101 return 0;
104 #elif defined __GNUC__ || defined __clang__ || defined _MSC_VER
106 # if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)
108 /* On all OSes except MSVC, macOS, Solaris, fenv_t is binary-equivalent to
109 - either a x86_387_fenv_t (7 'unsigned int' words)
110 where mxcsr is stored:
111 - for glibc/i386: in __eip = more[1].
112 - for musl libc/i386: the lower 6 bits ORed into __status_word.
113 - for FreeBSD/i386, Haiku/i386, mingw/i386:
114 the upper 16 bits in __reserved1, the lower 16 bits in __reserved2.
115 - or a struct { x86_387_fenv_t 387; unsigned int mxcsr; }
116 On MSVC, it's a
117 struct { unsigned int _Fe_ctl, _Fe_stat; }, where
118 _Fe_ctl bits 9..8 are the rounding direction,
119 _Fe_ctl bits 4..0 are the exception trap bits (inverted),
120 _Fe_stat bits 4..0 are the exception flags.
121 On macOS, it's a
122 struct { unsigned short __control, __status; unsigned int __mxcsr; ... }.
123 On Solaris, it's a
124 struct { __fex_handler_t __handlers; unsigned long __fsr; }. */
126 /* Like FE_ALL_EXCEPT, except that it also includes FE_DENORMAL. */
127 # define _FE_ALL_EXCEPT (0x3F * FE_INVALID)
130 fegetenv (fenv_t *envp)
132 # if defined _MSC_VER
134 unsigned int mxcsr;
135 _FPU_GETSSECW (mxcsr);
136 envp->_Fe_ctl = ((mxcsr & 0x6000) >> 5) /* rounding direction */
137 /* exception trap bits (inverted): */
138 | (mxcsr & (1 << 7) ? FE_INVALID : 0)
139 | (mxcsr & (1 << 9) ? FE_DIVBYZERO : 0)
140 | (mxcsr & (1 << 10) ? FE_OVERFLOW : 0)
141 | (mxcsr & (1 << 11) ? FE_UNDERFLOW : 0)
142 | (mxcsr & (1 << 12) ? FE_INEXACT : 0);
143 envp->_Fe_stat = /* exception flags: */
144 (mxcsr & (1 << 0) ? FE_INVALID : 0)
145 | (mxcsr & (1 << 2) ? FE_DIVBYZERO : 0)
146 | (mxcsr & (1 << 3) ? FE_OVERFLOW : 0)
147 | (mxcsr & (1 << 4) ? FE_UNDERFLOW : 0)
148 | (mxcsr & (1 << 5) ? FE_INEXACT : 0);
150 # elif defined __APPLE__ && defined __MACH__ /* macOS */
152 _FPU_GETCW (envp->__control);
153 _FPU_GETSTAT (envp->__status);
154 _FPU_GETSSECW (envp->__mxcsr);
156 # elif defined __sun /* Solaris */
158 fex_getexcepthandler (&envp->__handlers, FEX_ALL);
160 unsigned short fctrl;
161 unsigned short fstat;
162 unsigned int mxcsr;
163 _FPU_GETCW (fctrl);
164 _FPU_GETSTAT (fstat);
165 _FPU_GETSSECW (mxcsr);
166 envp->__fsr =
167 (((unsigned int) (fctrl ^ 0x3F) & ~0xE0C0U) << 16)
168 | ((unsigned int) fstat | (mxcsr & 0x3F));
170 # else
172 verify (sizeof (fenv_t) >= sizeof (x86_387_fenv_t));
174 __asm__ __volatile__ ("fnstenv %0" : "=m" (* (x86_387_fenv_t *) envp));
175 /* Note: fnstenv masks all floating-point exceptions until the fldcw
176 below. */
177 __asm__ __volatile__ ("fldcw %0" : : "m" (((x86_387_fenv_t *) envp)->__control_word));
179 unsigned int mxcsr;
180 _FPU_GETSSECW (mxcsr);
181 if (sizeof (fenv_t) > sizeof (x86_387_fenv_t))
183 /* Store mxcsr in the struct field after the x86_387_fenv_t. */
184 * (unsigned int *) ((x86_387_fenv_t *) envp + 1) = mxcsr;
186 else
188 # if defined __GLIBC__
189 ((x86_387_fenv_t *) envp)->more[1] = mxcsr;
190 # elif MUSL_LIBC
191 ((x86_387_fenv_t *) envp)->__status_word |= mxcsr & 0x3F;
192 # else
193 ((x86_387_fenv_t *) envp)->__reserved1 = mxcsr >> 16;
194 ((x86_387_fenv_t *) envp)->__reserved2 = mxcsr & 0xFFFF;
195 # endif
198 # endif
199 return 0;
203 fesetenv (fenv_t const *envp)
205 # if defined _MSC_VER
207 unsigned int mxcsr;
208 _FPU_GETSSECW (mxcsr);
209 mxcsr &= ~(0x6000 | (0x3F << 7) | 0x3F);
210 /* On MSVC, FE_DFL_ENV is the address of a global variable.
211 Let's ignore this variable. */
212 if (envp == FE_DFL_ENV)
213 /* Default: rounding direction = FE_TONEAREST, exception trap bits = 0,
214 exception flags = 0. */
215 mxcsr = mxcsr | (0x3F << 7);
216 else
217 mxcsr = mxcsr
218 | ((envp->_Fe_ctl & 0x300) << 5) /* rounding direction */
219 /* exception trap bits (inverted): */
220 | (envp->_Fe_ctl & FE_INVALID ? 1 << 7 : 0)
221 | (envp->_Fe_ctl & FE_DIVBYZERO ? 1 << 9 : 0)
222 | (envp->_Fe_ctl & FE_OVERFLOW ? 1 << 10 : 0)
223 | (envp->_Fe_ctl & FE_UNDERFLOW ? 1 << 11 : 0)
224 | (envp->_Fe_ctl & FE_INEXACT ? 1 << 12 : 0)
225 /* exception flags: */
226 | (envp->_Fe_stat & FE_INVALID ? 1 << 0 : 0)
227 | (envp->_Fe_stat & FE_DIVBYZERO ? 1 << 2 : 0)
228 | (envp->_Fe_stat & FE_OVERFLOW ? 1 << 3 : 0)
229 | (envp->_Fe_stat & FE_UNDERFLOW ? 1 << 4 : 0)
230 | (envp->_Fe_stat & FE_INEXACT ? 1 << 5 : 0);
231 _FPU_SETSSECW (mxcsr);
233 # elif defined __APPLE__ && defined __MACH__ /* macOS */
235 unsigned short fctrl;
236 unsigned short fstat;
237 unsigned int mxcsr;
238 /* On macOS, FE_DFL_ENV is the address of a global variable;
239 no special code is needed in this case. */
240 if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
242 fctrl = 0x3F;
243 fstat = 0;
244 mxcsr = 0x3F << 7;
246 else
248 fctrl = envp->__control;
249 fstat = envp->__status;
250 mxcsr = envp->__mxcsr;
252 /* In the SSE unit. */
253 _FPU_SETSSECW (mxcsr);
255 /* In the 387 unit. */
256 x86_387_fenv_t env387;
257 __asm__ __volatile__ ("fnstenv %0" : "=m" (*&env387));
258 /* Note: fnstenv masks all floating-point exceptions until the fldenv
259 below. */
260 env387.__control_word = fctrl;
261 env387.__status_word = fstat;
262 __asm__ __volatile__ ("fldenv %0" : : "m" (*&env387));
264 # elif defined __sun /* Solaris */
266 unsigned short env_fctrl;
267 unsigned int env_mxcsr;
268 /* On Solaris, FE_DFL_ENV is the address of a global variable;
269 no special code is needed in this case. */
270 if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
272 env_fctrl = 0x3F;
273 env_mxcsr = 0x3F << 7;
275 else
277 fex_setexcepthandler (&envp->__handlers, FEX_ALL);
278 env_fctrl = (envp->__fsr >> 16) ^ 0x3F;
279 env_mxcsr = envp->__fsr & 0x3F;
282 /* Store the exception flags in mxcsr, not in env387.__status_word. */
284 /* In the SSE unit. */
285 unsigned int mxcsr;
286 _FPU_GETSSECW (mxcsr);
287 mxcsr = (mxcsr & ~0x3F) | env_mxcsr;
288 _FPU_SETSSECW (mxcsr);
290 /* In the 387 unit. */
291 x86_387_fenv_t env387;
292 __asm__ __volatile__ ("fnstenv %0" : "=m" (*&env387));
293 /* Note: fnstenv masks all floating-point exceptions until the fldenv
294 below. */
295 env387.__control_word = env_fctrl;
296 env387.__status_word &= ~0x3F;
297 __asm__ __volatile__ ("fldenv %0" : : "m" (*&env387));
299 # else
301 verify (sizeof (fenv_t) >= sizeof (x86_387_fenv_t));
303 unsigned short env_fctrl;
304 unsigned short env_fstat;
305 unsigned int env_mxcsr;
306 /* On *BSD, Solaris, Cygwin, Android, Haiku, Minix, FE_DFL_ENV is the address
307 of a global variable; no special code is needed in this case.
308 But on mingw, FE_DFL_ENV is NULL. */
309 if (envp == FE_DFL_ENV)
311 env_fctrl = 0x3F;
312 env_fstat = 0;
313 env_mxcsr = 0x3F << 7;
315 else
317 env_fctrl = ((x86_387_fenv_t const *) envp)->__control_word;
318 env_fstat = ((x86_387_fenv_t const *) envp)->__status_word;
319 if (sizeof (fenv_t) > sizeof (x86_387_fenv_t))
320 env_mxcsr = * (unsigned int const *) ((x86_387_fenv_t const *) envp + 1);
321 else
323 # if defined __GLIBC__
324 env_mxcsr = ((x86_387_fenv_t const *) envp)->more[1];
325 # elif MUSL_LIBC
326 env_mxcsr = env_fstat & 0x3F;
327 # else
328 env_mxcsr = (((x86_387_fenv_t const *) envp)->__reserved1 << 16)
329 | ((x86_387_fenv_t const *) envp)->__reserved2;
330 # endif
334 /* In the SSE unit. */
335 unsigned int mxcsr = env_mxcsr;
336 _FPU_SETSSECW (mxcsr);
338 /* In the 387 unit. */
339 x86_387_fenv_t env387;
340 __asm__ __volatile__ ("fnstenv %0" : "=m" (*&env387));
341 /* Note: fnstenv masks all floating-point exceptions until the fldenv
342 below. */
343 env387.__control_word = env_fctrl;
344 env387.__status_word = env_fstat;
345 __asm__ __volatile__ ("fldenv %0" : : "m" (*&env387));
347 # endif
349 return 0;
352 # elif defined __aarch64__ /* arm64 */
354 /* On all OSes except FreeBSD and macOS, fenv_t is binary-equivalent to a
355 struct { unsigned int fpcr, fpsr; }.
356 On FreeBSD, it's binary-equivalent to a
357 struct { unsigned int fpsr, fpcr; }, i.e. the fields are swapped.
358 On macOS, it's binary-equivalent to a
359 struct { unsigned long fpsr, fpcr; }. */
362 fegetenv (fenv_t *envp)
364 unsigned long fpcr;
365 unsigned long fpsr;
366 _FPU_GETCW (fpcr);
367 _FPU_GETFPSR (fpsr);
368 # if defined __APPLE__ && defined __MACH__
369 ((unsigned long *) envp)[0] = fpsr;
370 ((unsigned long *) envp)[1] = fpcr;
371 # elif defined __FreeBSD__
372 ((unsigned int *) envp)[0] = fpsr;
373 ((unsigned int *) envp)[1] = fpcr;
374 # else
375 ((unsigned int *) envp)[0] = fpcr;
376 ((unsigned int *) envp)[1] = fpsr;
377 # endif
378 return 0;
382 fesetenv (fenv_t const *envp)
384 unsigned long orig_fpcr, fpcr;
385 unsigned long orig_fpsr, fpsr;
386 _FPU_GETCW (orig_fpcr);
387 _FPU_GETFPSR (orig_fpsr);
388 /* On *BSD and Android, FE_DFL_ENV is the address of a global variable;
389 no special code is needed in this case. */
390 if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
392 /* Default: rounding direction = FE_TONEAREST, exceptions trap bits = 0. */
393 fpcr = (orig_fpcr & 0xFE0FE0F8U) | 0x00000000U;
394 /* Default: exception flags = 0. */
395 fpsr = (orig_fpsr & 0x0FFFFFE0U) | 0x00000000U;
397 else
399 # if defined __APPLE__ && defined __MACH__
400 fpsr = ((unsigned long const *) envp)[0];
401 fpcr = ((unsigned long const *) envp)[1];
402 # elif defined __FreeBSD__
403 fpsr = ((unsigned int const *) envp)[0];
404 fpcr = ((unsigned int const *) envp)[1];
405 # else
406 fpcr = ((unsigned int const *) envp)[0];
407 fpsr = ((unsigned int const *) envp)[1];
408 # endif
410 if (fpcr != orig_fpcr)
411 _FPU_SETCW (fpcr);
412 if (fpsr != orig_fpsr)
413 _FPU_SETFPSR (fpsr);
414 return 0;
417 # elif defined __arm__
419 /* On all OSes except OpenBSD, fenv_t is binary-equivalent to an 'unsigned int'.
420 On OpenBSD, it's a struct { unsigned int __sticky, __mask, __round; }. */
423 fegetenv (fenv_t *envp)
425 # ifdef __SOFTFP__
426 return -1;
427 # else
428 unsigned int fpscr;
429 _FPU_GETCW (fpscr);
430 # if defined __OpenBSD__
431 envp->__sticky = fpscr & FE_ALL_EXCEPT;
432 envp->__mask = (fpscr >> 8) & FE_ALL_EXCEPT;
433 envp->__round = (fpscr >> 22) & 3;
434 # else
435 * (unsigned int *) envp = fpscr;
436 # endif
437 return 0;
438 # endif
442 fesetenv (fenv_t const *envp)
444 # ifdef __SOFTFP__
445 return -1;
446 # else
447 unsigned int fpscr, orig_fpscr;
448 _FPU_GETCW (orig_fpscr);
449 /* On *BSD and Android, FE_DFL_ENV is the address of a global variable;
450 no special code is needed in this case. */
451 if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
452 /* Default: rounding direction = FE_TONEAREST, exception trap bits = 0,
453 exception flags = 0. */
454 fpscr = (orig_fpscr & 0x00086060) | 0x00000000;
455 else
456 /* Here, glibc ignores orig_fpscr entirely... */
457 fpscr = (orig_fpscr & ~((3U << 22) | (FE_ALL_EXCEPT << 8) | FE_ALL_EXCEPT))
458 # if defined __OpenBSD__
459 | ((envp->__round & 3U) << 22)
460 | ((envp->__mask & FE_ALL_EXCEPT) << 8)
461 | (envp->__sticky & FE_ALL_EXCEPT)
462 # else
463 | (* (unsigned int const *) envp & ((3U << 22) | (FE_ALL_EXCEPT << 8) | FE_ALL_EXCEPT))
464 # endif
466 if (((fpscr ^ orig_fpscr) & ~0xF0000000) != 0)
467 _FPU_SETCW (fpscr);
468 return 0;
469 # endif
472 # elif defined __alpha
474 /* On all OSes except OpenBSD, fenv_t is binary-equivalent to an 'unsigned long'.
475 On OpenBSD, it's a struct { unsigned int __sticky, __mask, __round; }. */
477 /* Like FE_ALL_EXCEPT, except that it also includes FE_DENORMAL. */
478 # define _FE_ALL_EXCEPT (0x3FUL * FE_INVALID)
481 fegetenv (fenv_t *envp)
483 unsigned long swcr = __ieee_get_fp_control ();
484 unsigned long fpcr;
485 _FPU_GETCW (fpcr);
486 # if defined __OpenBSD__
487 envp->__sticky = (swcr >> 17) & _FE_ALL_EXCEPT;
488 envp->__mask = (swcr >> 1) & _FE_ALL_EXCEPT;
489 envp->__round = (fpcr >> 58) & 0x3UL;
490 # else
491 * (unsigned long *) envp =
492 (fpcr & 0x0C00000000000000UL) /* rounding direction */
493 | (swcr & 0x007E307EUL); /* exception flags,
494 mapping of denormal inputs and outputs,
495 exception trap bits */
496 # endif
497 return 0;
501 fesetenv (fenv_t const *envp)
503 unsigned long env;
504 /* On *BSD, FE_DFL_ENV is the address of a global variable;
505 on glibc, it is a magic address. */
506 if (envp == FE_DFL_ENV)
507 /* Default: rounding direction = FE_TONEAREST, exception flags = 0,
508 mapping = 0, exception trap bits = 0. */
509 env = 0x0800000000000000UL;
510 else
512 # if defined __OpenBSD__
513 env = ((unsigned long) (envp->__round & 0x3UL) << 58)
514 | ((envp->__sticky & _FE_ALL_EXCEPT) << 17)
515 | ((envp->__mask & _FE_ALL_EXCEPT) << 1);
516 # else
517 env = * (unsigned long const *) envp;
518 # endif
521 /* Set the rounding direction. */
522 unsigned long fpcr, orig_fpcr;
523 _FPU_GETCW (orig_fpcr);
524 fpcr = (orig_fpcr & ~0x0C00000000000000UL) | (env & 0x0C00000000000000UL);
525 if (fpcr != orig_fpcr)
526 _FPU_SETCW (fpcr);
527 /* Set the exception flags, the mapping of denormal inputs and outputs, and
528 the exception trap bits. */
529 __ieee_set_fp_control (env & 0x007E307EUL);
531 return 0;
534 # elif defined __hppa
536 /* On all OSes except glibc, fenv_t is binary-equivalent to an 'unsigned int'.
537 On glibc, it's a struct { unsigned int __status_word, __exception[7]; },
538 of which only the __status_word field is used. */
541 fegetenv (fenv_t *envp)
543 unsigned int fpstatus;
544 _FPU_GETCW (fpstatus);
545 * (unsigned int *) envp = fpstatus;
546 return 0;
550 fesetenv (fenv_t const *envp)
552 unsigned int fpstatus, orig_fpstatus;
553 _FPU_GETCW (orig_fpstatus);
554 /* On *BSD, FE_DFL_ENV is the address of a global variable;
555 no special code is needed in this case. */
556 if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
557 /* Default: exception flags = 0, rounding direction = FE_TONEAREST,
558 exception trap bits = 0. */
559 fpstatus = 0x00000000U;
560 else
562 unsigned int env = * (unsigned int const *) envp;
563 fpstatus = (orig_fpstatus & ~0xF800061FU) | (env & 0xF800061FU);
565 if (fpstatus != orig_fpstatus)
566 _FPU_SETCW (fpstatus);
567 return 0;
570 # elif defined __ia64__
572 /* On all OSes, fenv_t is binary-equivalent to an 'unsigned long'. */
574 /* Like FE_ALL_EXCEPT, except that it also includes FE_DENORMAL. */
575 # define _FE_ALL_EXCEPT (0x3FUL * FE_INVALID)
578 fegetenv (fenv_t *envp)
580 unsigned long fpsr;
581 _FPU_GETCW (fpsr);
582 * (unsigned long *) envp = fpsr;
583 return 0;
587 fesetenv (fenv_t const *envp)
589 unsigned long env;
590 /* On NetBSD, FE_DFL_ENV is the address of a global variable;
591 on glibc, it is a magic address. */
592 if (envp == FE_DFL_ENV)
593 /* Default:
594 sf0 = 0b0000000001100, sf1 = 0b0000001001110, sf2 = sf3 = 0000001001100
595 i.e. precision control = 80-bits "extended",
596 rounding direction = FE_TONEAREST,
597 exception flags = 0,
598 exceptions trap bits = all 1, i.e. all masked. */
599 env = 0x0009804C0270033FUL;
600 else
601 env = * (unsigned long const *) envp;
602 unsigned long fpsr = env;
603 _FPU_SETCW (fpsr);
604 return 0;
607 # elif defined __m68k__
609 /* On all OSes, fenv_t is binary-equivalent to a struct
610 { unsigned int control_register, status_register, instruction_address; }. */
612 # define _FPU_GETFPIAR(cw) __asm__ __volatile__ ("fmove%.l %/fpiar, %0" : "=dm" (cw))
613 # define _FPU_SETFPIAR(cw) __asm__ __volatile__ ("fmove%.l %0, %/fpiar" : : "dm" (cw))
616 fegetenv (fenv_t *envp)
618 unsigned int fpcr;
619 unsigned int fpsr;
620 unsigned int fpiar;
621 _FPU_GETCW (fpcr);
622 _FPU_GETFPSR (fpsr);
623 _FPU_GETFPIAR (fpiar);
624 ((unsigned int *) envp)[0] = fpcr;
625 ((unsigned int *) envp)[1] = fpsr;
626 ((unsigned int *) envp)[2] = fpiar;
627 return 0;
631 fesetenv (fenv_t const *envp)
633 unsigned int env_fpcr;
634 unsigned int env_fpsr;
635 if (envp == FE_DFL_ENV)
637 /* Default: exceptions trap bits = 0, rounding direction = FE_TONEAREST. */
638 env_fpcr = 0;
639 /* Default: exception flags = 0. */
640 env_fpsr = 0;
642 else
644 /* No need to restore the instruction address here. */
645 env_fpcr = ((unsigned int const *) envp)[0];
646 env_fpsr = ((unsigned int const *) envp)[1];
648 unsigned int fpcr, orig_fpcr;
649 unsigned int fpsr, orig_fpsr;
650 unsigned int fpiar, orig_fpiar;
651 _FPU_GETCW (orig_fpcr);
652 _FPU_GETFPSR (orig_fpsr);
653 _FPU_GETFPIAR (orig_fpiar);
654 /* glibc uses 0x3E30U here. I think 0xFF30U is a better choice. */
655 fpcr = (orig_fpcr & ~0xFF30U) | (env_fpcr & 0xFF30U);
656 fpsr = (orig_fpsr & ~0x00F8U) | (env_fpsr & 0x00F8U);
657 fpiar = orig_fpiar;
658 _FPU_SETCW (fpcr);
659 _FPU_SETFPSR (fpsr);
660 _FPU_SETFPIAR (fpiar);
661 return 0;
664 # elif defined __mips__
666 /* On all OSes, fenv_t is binary-equivalent to an 'unsigned int'. */
669 fegetenv (fenv_t *envp)
671 unsigned int fcsr;
672 _FPU_GETCW (fcsr);
673 * (unsigned int *) envp = fcsr;
674 return 0;
678 fesetenv (fenv_t const *envp)
680 unsigned int env;
681 /* On *BSD, FE_DFL_ENV is the address of a global variable;
682 no special code is needed in this case. */
683 if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
684 /* Default: exceptions trap bits = 0, exception flags = 0,
685 rounding direction = FE_TONEAREST. */
686 env = 0x00000000U;
687 else
688 env = * (unsigned int const *) envp;
689 unsigned int fcsr = env;
690 _FPU_SETCW (fcsr);
691 return 0;
694 # elif defined __loongarch__
696 /* On all OSes, fenv_t is binary-equivalent to an 'unsigned int'. */
699 fegetenv (fenv_t *envp)
701 unsigned int fcsr;
702 _FPU_GETCW (fcsr);
703 * (unsigned int *) envp = fcsr;
704 return 0;
708 fesetenv (fenv_t const *envp)
710 unsigned int env;
711 if (envp == FE_DFL_ENV)
712 /* Default: exception flags = 0, rounding direction = FE_TONEAREST,
713 exceptions trap bits = 0. */
714 env = 0x00000000U;
715 else
716 env = * (unsigned int const *) envp;
717 unsigned int fcsr = env;
718 _FPU_SETCW (fcsr);
719 return 0;
722 # elif defined __powerpc__
724 /* On all OSes except *BSD and AIX, fenv_t is a 'double'.
725 On *BSD, it's an 'unsigned int'.
726 On AIX, it's a struct { unsigned short rmode; unsigned int fpstat, trapstate; }.
727 But AIX has already been dealt with above. */
729 # if defined __linux__
730 # include <sys/prctl.h>
731 # endif
734 fegetenv (fenv_t *envp)
736 # if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__
737 union { unsigned long long u; double f; } memenv;
738 _FPU_GETCW_AS_DOUBLE (memenv.f);
739 * (unsigned int *) envp = (unsigned int) memenv.u;
740 # else
741 _FPU_GETCW_AS_DOUBLE (*envp);
742 # endif
743 return 0;
747 fesetenv (fenv_t const *envp)
749 union { unsigned long long u; double f; } orig_memenv, memenv;
750 _FPU_GETCW_AS_DOUBLE (orig_memenv.f);
751 /* On glibc and *BSD, FE_DFL_ENV is the address of a global variable;
752 no special code is needed in this case. */
753 if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
754 memenv.u = 0xFFF8000000000000ULL;
755 else
756 # if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__
757 memenv.u = * (unsigned int const *) envp;
758 # else
759 memenv.f = *envp;
760 # endif
761 if (!(memenv.u == orig_memenv.u))
763 if ((orig_memenv.u & 0x000000f8) == 0 && (memenv.u & 0x000000f8) != 0)
765 /* Put the thread into precise trapping mode. */
766 # if defined __linux__ || defined __NetBSD__
767 prctl (PR_SET_FPEXC, PR_FP_EXC_PRECISE);
768 # endif
770 _FPU_SETCW_AS_DOUBLE (memenv.f);
771 if ((orig_memenv.u & 0x000000f8) != 0 && (memenv.u & 0x000000f8) == 0)
773 /* Put the thread into no-trapping mode. */
774 # if defined __linux__ || defined __NetBSD__
775 prctl (PR_SET_FPEXC, PR_FP_EXC_DISABLED);
776 # endif
779 return 0;
782 # elif defined __riscv
784 /* On all OSes except FreeBSD, fenv_t is binary-equivalent to an 'unsigned int'.
785 On FreeBSD, it's binary-equivalent to an 'unsigned long'. */
787 # define _FPU_GETCW(cw) __asm__ __volatile__ ("frcsr %0" : "=r" (cw))
788 # define _FPU_SETCW(cw) __asm__ __volatile__ ("fscsr %z0" : : "rJ" (cw))
791 fegetenv (fenv_t *envp)
793 unsigned int fcsr;
794 _FPU_GETCW (fcsr);
795 # if defined __FreeBSD__
796 * (unsigned long *) envp = fcsr;
797 # else
798 * (unsigned int *) envp = fcsr;
799 # endif
800 return 0;
804 fesetenv (fenv_t const *envp)
806 unsigned int env;
807 /* On *BSD and Android, FE_DFL_ENV is the address of a global variable;
808 no special code is needed in this case. */
809 if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
810 /* Default: rounding direction = FE_TONEAREST, exception flags = 0. */
811 env = 0x00000000U;
812 else
813 # if defined __FreeBSD__
814 env = * (unsigned long const *) envp;
815 # else
816 env = * (unsigned int const *) envp;
817 # endif
818 unsigned int fcsr = env;
819 _FPU_SETCW (fcsr);
820 return 0;
823 # elif defined __s390__ || defined __s390x__
825 /* On all OSes, fenv_t is binary-equivalent to a struct whose first (and only
826 relevant) field is an 'unsigned int'. */
829 fegetenv (fenv_t *envp)
831 unsigned int fpc;
832 _FPU_GETCW (fpc);
833 * (unsigned int *) envp = fpc;
834 return 0;
838 fesetenv (fenv_t const *envp)
840 unsigned int env;
841 if (envp == FE_DFL_ENV)
842 /* Default: exceptions trap bits = 0, exception flags = 0,
843 rounding direction = FE_TONEAREST. */
844 env = 0x00000000U;
845 else
846 env = * (unsigned int const *) envp;
847 unsigned int fpc = env;
848 _FPU_SETCW (fpc);
849 return 0;
852 # elif defined __sh__
854 /* On all OSes, fenv_t is binary-equivalent to an 'unsigned int'. */
857 fegetenv (fenv_t *envp)
859 unsigned int fpscr;
860 _FPU_GETCW (fpscr);
861 * (unsigned int *) envp = fpscr;
862 return 0;
866 fesetenv (fenv_t const *envp)
868 unsigned int env;
869 /* On OpenBSD, FE_DFL_ENV is the address of a global variable;
870 no special code is needed in this case. */
871 if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
872 /* Default: PR = 1, exceptions trap bits = 0, exception flags = 0,
873 rounding direction = FE_TONEAREST. */
874 env = 0x00080000U;
875 else
876 env = * (unsigned int const *) envp;
877 unsigned int fpscr = env;
878 _FPU_SETCW (fpscr);
879 return 0;
882 # elif defined __sparc
884 /* On all OSes except Solaris, fenv_t is binary-equivalent to an 'unsigned long'.
885 On Solaris, it's a struct { __fex_handler_t __handlers; unsigned long __fsr; }. */
888 fegetenv (fenv_t *envp)
890 unsigned long fsr;
891 _FPU_GETCW (fsr);
892 # if defined __sun
893 fex_getexcepthandler (&envp->__handlers, FEX_ALL);
894 envp->__fsr = fsr;
895 # else
896 * (unsigned long *) envp = fsr;
897 # endif
898 return 0;
902 fesetenv (fenv_t const *envp)
904 unsigned long fsr;
905 /* On *BSD and Solaris, FE_DFL_ENV is the address of a global variable;
906 no special code is needed in this case. */
907 if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
909 /* Default: rounding direction = FE_TONEAREST, exceptions trap bits = 0,
910 exception flags = 0. */
911 fsr = 0x00000000U;
913 else
915 # if defined __sun
916 fex_setexcepthandler (&envp->__handlers, FEX_ALL);
917 fsr = envp->__fsr;
918 # else
919 fsr = * (unsigned long const *) envp;
920 # endif
922 _FPU_SETCW (fsr);
923 return 0;
926 # else
928 # if defined __GNUC__ || defined __clang__
929 # warning "Unknown CPU / architecture. Please report your platform and compiler to <bug-gnulib@gnu.org>."
930 # endif
931 # define NEED_FALLBACK 1
933 # endif
935 #else
937 /* The compiler does not support __asm__ statements or equivalent
938 intrinsics. */
940 # if defined __sun && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)) && defined __SUNPRO_C
941 /* Solaris/i386, Solaris/x86_64. */
943 /* Like FE_ALL_EXCEPT, except that it also includes FE_DENORMAL. */
944 # define _FE_ALL_EXCEPT (0x3F * FE_INVALID)
946 /* Accessors for the 387 unit's special registers. */
948 static void
949 getfctrl (unsigned short *fctrl_p)
951 # if defined __x86_64__ || defined _M_X64
952 asm ("fnstcw (%rdi)");
953 # else
954 /* The compiler generates a stack frame. Therefore the first argument is in
955 8(%ebp), not in 4(%esp). */
956 asm ("movl 8(%ebp),%eax");
957 asm ("fnstcw (%eax)");
958 # endif
961 static void
962 getfstat (unsigned short *fstat_p)
964 # if defined __x86_64__ || defined _M_X64
965 asm ("fnstsw (%rdi)");
966 # else
967 /* The compiler generates a stack frame. Therefore the first argument is in
968 8(%ebp), not in 4(%esp). */
969 asm ("movl 8(%ebp),%eax");
970 asm ("fnstsw (%eax)");
971 # endif
974 static void
975 getenv387 (x86_387_fenv_t *env387_p)
977 # if defined __x86_64__ || defined _M_X64
978 asm ("fnstenv (%rdi)");
979 # else
980 /* The compiler generates a stack frame. Therefore the first argument is in
981 8(%ebp), not in 4(%esp). */
982 asm ("movl 8(%ebp),%eax");
983 asm ("fnstenv (%eax)");
984 # endif
987 static void
988 setenv387 (x86_387_fenv_t const *env387_p)
990 # if defined __x86_64__ || defined _M_X64
991 asm ("fldenv (%rdi)");
992 # else
993 /* The compiler generates a stack frame. Therefore the first argument is in
994 8(%ebp), not in 4(%esp). */
995 asm ("movl 8(%ebp),%eax");
996 asm ("fldenv (%eax)");
997 # endif
1000 /* Accessors for the mxcsr register. */
1002 static void
1003 getssecw (unsigned int *mxcsr_p)
1005 # if defined __x86_64__ || defined _M_X64
1006 asm ("stmxcsr (%rdi)");
1007 # else
1008 /* The compiler generates a stack frame. Therefore the first argument is in
1009 8(%ebp), not in 4(%esp). */
1010 asm ("movl 8(%ebp),%eax");
1011 asm ("stmxcsr (%eax)");
1012 # endif
1015 static void
1016 setssecw (unsigned int const *mxcsr_p)
1018 # if defined __x86_64__ || defined _M_X64
1019 asm ("ldmxcsr (%rdi)");
1020 # else
1021 /* The compiler generates a stack frame. Therefore the first argument is in
1022 8(%ebp), not in 4(%esp). */
1023 asm ("movl 8(%ebp),%eax");
1024 asm ("ldmxcsr (%eax)");
1025 # endif
1029 fegetenv (fenv_t *envp)
1031 fex_getexcepthandler (&envp->__handlers, FEX_ALL);
1033 unsigned short fctrl;
1034 unsigned short fstat;
1035 unsigned int mxcsr;
1036 getfctrl (&fctrl);
1037 getfstat (&fstat);
1038 getssecw (&mxcsr);
1039 envp->__fsr =
1040 (((unsigned int) (fctrl ^ 0x3F) & ~0xE0C0U) << 16)
1041 | ((unsigned int) fstat | (mxcsr & 0x3F));
1043 return 0;
1047 fesetenv (fenv_t const *envp)
1049 unsigned short env_fctrl;
1050 unsigned int env_mxcsr;
1051 /* On Solaris, FE_DFL_ENV is the address of a global variable;
1052 no special code is needed in this case. */
1053 if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
1055 env_fctrl = 0x3F;
1056 env_mxcsr = 0x3F << 7;
1058 else
1060 fex_setexcepthandler (&envp->__handlers, FEX_ALL);
1061 env_fctrl = (envp->__fsr >> 16) ^ 0x3F;
1062 env_mxcsr = envp->__fsr & 0x3F;
1065 /* Store the exception flags in mxcsr, not in env387.__status_word. */
1067 /* In the SSE unit. */
1068 unsigned int mxcsr;
1069 getssecw (&mxcsr);
1070 mxcsr = (mxcsr & ~0x3F) | env_mxcsr;
1071 setssecw (&mxcsr);
1073 /* In the 387 unit. */
1074 x86_387_fenv_t env387;
1075 getenv387 (&env387);
1076 /* Note: fnstenv masks all floating-point exceptions until the setenv387
1077 below. */
1078 env387.__control_word = env_fctrl;
1079 env387.__status_word &= ~0x3F;
1080 setenv387 (&env387);
1082 return 0;
1085 #elif defined __sun && defined __sparc && defined __SUNPRO_C
1086 /* Solaris/sparc. */
1088 /* Accessors for the fsr register. */
1090 static void
1091 getfsr (unsigned long *fsr_p)
1093 # if defined __sparcv9 || defined __arch64__ /* sparc64 */
1094 asm ("stx %fsr,[%i0]");
1095 # else
1096 asm ("st %fsr,[%i0]");
1097 # endif
1100 static void
1101 setfsr (unsigned long const *fsr_p)
1103 # if defined __sparcv9 || defined __arch64__ /* sparc64 */
1104 asm ("ldx [%i0],%fsr");
1105 # else
1106 asm ("ld [%i0],%fsr");
1107 # endif
1111 fegetenv (fenv_t *envp)
1113 unsigned long fsr;
1114 getfsr (&fsr);
1115 fex_getexcepthandler (&envp->__handlers, FEX_ALL);
1116 envp->__fsr = fsr;
1117 return 0;
1121 fesetenv (fenv_t const *envp)
1123 unsigned long fsr;
1124 /* On *BSD and Solaris, FE_DFL_ENV is the address of a global variable;
1125 no special code is needed in this case. */
1126 if (FE_DFL_ENV == (const fenv_t *) (-1) && envp == FE_DFL_ENV)
1128 /* Default: rounding direction = FE_TONEAREST, exceptions trap bits = 0,
1129 exception flags = 0. */
1130 fsr = 0x00000000U;
1132 else
1134 fex_setexcepthandler (&envp->__handlers, FEX_ALL);
1135 fsr = envp->__fsr;
1137 setfsr (&fsr);
1138 return 0;
1141 # else
1143 # define NEED_FALLBACK 1
1145 # endif
1147 #endif
1149 #if NEED_FALLBACK
1151 /* A dummy fallback. */
1154 fegetenv (fenv_t *envp)
1156 return -1;
1160 fesetenv (fenv_t const *envp)
1162 return -1;
1165 #endif