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}. */
25 #include "fenv-private.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
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)
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
56 fegetenv (fenv_t
*envp
)
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
);
64 /* AIX's original fegetenv() sets envp->trapstate = 0. */
65 envp
->trapstate
= FE_ALL_EXCEPT
& ((unsigned int) memenv
.u
<< 22);
70 fesetenv (fenv_t
const *envp
)
73 /* Invoke the original fesetenv(), and additionally restore the exception trap
74 bits (fpscr bits 7..3). */
75 if (fesetenv (envp
) != 0)
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
)
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
);
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; }
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.
122 struct { unsigned short __control, __status; unsigned int __mxcsr; ... }.
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
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
;
164 _FPU_GETSTAT (fstat
);
165 _FPU_GETSSECW (mxcsr
);
167 (((unsigned int) (fctrl
^ 0x3F) & ~0xE0C0U
) << 16)
168 | ((unsigned int) fstat
| (mxcsr
& 0x3F));
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
177 __asm__
__volatile__ ("fldcw %0" : : "m" (((x86_387_fenv_t
*) envp
)->__control_word
));
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
;
188 # if defined __GLIBC__
189 ((x86_387_fenv_t
*) envp
)->more
[1] = mxcsr
;
191 ((x86_387_fenv_t
*) envp
)->__status_word
|= mxcsr
& 0x3F;
193 ((x86_387_fenv_t
*) envp
)->__reserved1
= mxcsr
>> 16;
194 ((x86_387_fenv_t
*) envp
)->__reserved2
= mxcsr
& 0xFFFF;
203 fesetenv (fenv_t
const *envp
)
205 # if defined _MSC_VER
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);
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
;
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
)
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
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
)
273 env_mxcsr
= 0x3F << 7;
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. */
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
295 env387
.__control_word
= env_fctrl
;
296 env387
.__status_word
&= ~0x3F;
297 __asm__
__volatile__ ("fldenv %0" : : "m" (*&env387
));
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
)
313 env_mxcsr
= 0x3F << 7;
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);
323 # if defined __GLIBC__
324 env_mxcsr
= ((x86_387_fenv_t
const *) envp
)->more
[1];
326 env_mxcsr
= env_fstat
& 0x3F;
328 env_mxcsr
= (((x86_387_fenv_t
const *) envp
)->__reserved1
<< 16)
329 | ((x86_387_fenv_t
const *) envp
)->__reserved2
;
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
343 env387
.__control_word
= env_fctrl
;
344 env387
.__status_word
= env_fstat
;
345 __asm__
__volatile__ ("fldenv %0" : : "m" (*&env387
));
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
)
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
;
375 ((unsigned int *) envp
)[0] = fpcr
;
376 ((unsigned int *) envp
)[1] = fpsr
;
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
;
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];
406 fpcr
= ((unsigned int const *) envp
)[0];
407 fpsr
= ((unsigned int const *) envp
)[1];
410 if (fpcr
!= orig_fpcr
)
412 if (fpsr
!= orig_fpsr
)
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
)
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;
435 * (unsigned int *) envp
= fpscr
;
442 fesetenv (fenv_t
const *envp
)
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;
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
)
463 | (* (unsigned int const *) envp
& ((3U << 22) | (FE_ALL_EXCEPT
<< 8) | FE_ALL_EXCEPT
))
466 if (((fpscr
^ orig_fpscr
) & ~0xF0000000) != 0)
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 ();
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
;
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 */
501 fesetenv (fenv_t
const *envp
)
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
;
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);
517 env
= * (unsigned long const *) envp
;
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
)
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
);
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
;
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
;
562 unsigned int env
= * (unsigned int const *) envp
;
563 fpstatus
= (orig_fpstatus
& ~0xF800061FU
) | (env
& 0xF800061FU
);
565 if (fpstatus
!= orig_fpstatus
)
566 _FPU_SETCW (fpstatus
);
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
)
582 * (unsigned long *) envp
= fpsr
;
587 fesetenv (fenv_t
const *envp
)
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
)
594 sf0 = 0b0000000001100, sf1 = 0b0000001001110, sf2 = sf3 = 0000001001100
595 i.e. precision control = 80-bits "extended",
596 rounding direction = FE_TONEAREST,
598 exceptions trap bits = all 1, i.e. all masked. */
599 env
= 0x0009804C0270033FUL
;
601 env
= * (unsigned long const *) envp
;
602 unsigned long fpsr
= env
;
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
)
623 _FPU_GETFPIAR (fpiar
);
624 ((unsigned int *) envp
)[0] = fpcr
;
625 ((unsigned int *) envp
)[1] = fpsr
;
626 ((unsigned int *) envp
)[2] = fpiar
;
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. */
639 /* Default: exception flags = 0. */
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
);
660 _FPU_SETFPIAR (fpiar
);
664 # elif defined __mips__
666 /* On all OSes, fenv_t is binary-equivalent to an 'unsigned int'. */
669 fegetenv (fenv_t
*envp
)
673 * (unsigned int *) envp
= fcsr
;
678 fesetenv (fenv_t
const *envp
)
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. */
688 env
= * (unsigned int const *) envp
;
689 unsigned int fcsr
= env
;
694 # elif defined __loongarch__
696 /* On all OSes, fenv_t is binary-equivalent to an 'unsigned int'. */
699 fegetenv (fenv_t
*envp
)
703 * (unsigned int *) envp
= fcsr
;
708 fesetenv (fenv_t
const *envp
)
711 if (envp
== FE_DFL_ENV
)
712 /* Default: exception flags = 0, rounding direction = FE_TONEAREST,
713 exceptions trap bits = 0. */
716 env
= * (unsigned int const *) envp
;
717 unsigned int fcsr
= env
;
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>
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
;
741 _FPU_GETCW_AS_DOUBLE (*envp
);
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
;
756 # if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__
757 memenv
.u
= * (unsigned int const *) envp
;
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
);
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
);
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
)
795 # if defined __FreeBSD__
796 * (unsigned long *) envp
= fcsr
;
798 * (unsigned int *) envp
= fcsr
;
804 fesetenv (fenv_t
const *envp
)
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. */
813 # if defined __FreeBSD__
814 env
= * (unsigned long const *) envp
;
816 env
= * (unsigned int const *) envp
;
818 unsigned int fcsr
= env
;
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
)
833 * (unsigned int *) envp
= fpc
;
838 fesetenv (fenv_t
const *envp
)
841 if (envp
== FE_DFL_ENV
)
842 /* Default: exceptions trap bits = 0, exception flags = 0,
843 rounding direction = FE_TONEAREST. */
846 env
= * (unsigned int const *) envp
;
847 unsigned int fpc
= env
;
852 # elif defined __sh__
854 /* On all OSes, fenv_t is binary-equivalent to an 'unsigned int'. */
857 fegetenv (fenv_t
*envp
)
861 * (unsigned int *) envp
= fpscr
;
866 fesetenv (fenv_t
const *envp
)
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. */
876 env
= * (unsigned int const *) envp
;
877 unsigned int fpscr
= env
;
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
)
893 fex_getexcepthandler (&envp
->__handlers
, FEX_ALL
);
896 * (unsigned long *) envp
= fsr
;
902 fesetenv (fenv_t
const *envp
)
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. */
916 fex_setexcepthandler (&envp
->__handlers
, FEX_ALL
);
919 fsr
= * (unsigned long const *) envp
;
928 # if defined __GNUC__ || defined __clang__
929 # warning "Unknown CPU / architecture. Please report your platform and compiler to <bug-gnulib@gnu.org>."
931 # define NEED_FALLBACK 1
937 /* The compiler does not support __asm__ statements or equivalent
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. */
949 getfctrl (unsigned short *fctrl_p
)
951 # if defined __x86_64__ || defined _M_X64
952 asm ("fnstcw (%rdi)");
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)");
962 getfstat (unsigned short *fstat_p
)
964 # if defined __x86_64__ || defined _M_X64
965 asm ("fnstsw (%rdi)");
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)");
975 getenv387 (x86_387_fenv_t
*env387_p
)
977 # if defined __x86_64__ || defined _M_X64
978 asm ("fnstenv (%rdi)");
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)");
988 setenv387 (x86_387_fenv_t
const *env387_p
)
990 # if defined __x86_64__ || defined _M_X64
991 asm ("fldenv (%rdi)");
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)");
1000 /* Accessors for the mxcsr register. */
1003 getssecw (unsigned int *mxcsr_p
)
1005 # if defined __x86_64__ || defined _M_X64
1006 asm ("stmxcsr (%rdi)");
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)");
1016 setssecw (unsigned int const *mxcsr_p
)
1018 # if defined __x86_64__ || defined _M_X64
1019 asm ("ldmxcsr (%rdi)");
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)");
1029 fegetenv (fenv_t
*envp
)
1031 fex_getexcepthandler (&envp
->__handlers
, FEX_ALL
);
1033 unsigned short fctrl
;
1034 unsigned short fstat
;
1040 (((unsigned int) (fctrl
^ 0x3F) & ~0xE0C0U
) << 16)
1041 | ((unsigned int) fstat
| (mxcsr
& 0x3F));
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
)
1056 env_mxcsr
= 0x3F << 7;
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. */
1070 mxcsr
= (mxcsr
& ~0x3F) | env_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
1078 env387
.__control_word
= env_fctrl
;
1079 env387
.__status_word
&= ~0x3F;
1080 setenv387 (&env387
);
1085 #elif defined __sun && defined __sparc && defined __SUNPRO_C
1086 /* Solaris/sparc. */
1088 /* Accessors for the fsr register. */
1091 getfsr (unsigned long *fsr_p
)
1093 # if defined __sparcv9 || defined __arch64__ /* sparc64 */
1094 asm ("stx %fsr,[%i0]");
1096 asm ("st %fsr,[%i0]");
1101 setfsr (unsigned long const *fsr_p
)
1103 # if defined __sparcv9 || defined __arch64__ /* sparc64 */
1104 asm ("ldx [%i0],%fsr");
1106 asm ("ld [%i0],%fsr");
1111 fegetenv (fenv_t
*envp
)
1115 fex_getexcepthandler (&envp
->__handlers
, FEX_ALL
);
1121 fesetenv (fenv_t
const *envp
)
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. */
1134 fex_setexcepthandler (&envp
->__handlers
, FEX_ALL
);
1143 # define NEED_FALLBACK 1
1151 /* A dummy fallback. */
1154 fegetenv (fenv_t
*envp
)
1160 fesetenv (fenv_t
const *envp
)