1 | #include <assert.h>
|
---|
2 | #include <fenv.h>
|
---|
3 | #include <stdio.h>
|
---|
4 |
|
---|
5 | typedef struct
|
---|
6 | {
|
---|
7 | unsigned short __control_word;
|
---|
8 | unsigned short __reserved1;
|
---|
9 | unsigned short __status_word;
|
---|
10 | unsigned short __reserved2;
|
---|
11 | unsigned int more[5];
|
---|
12 | }
|
---|
13 | x86_387_fenv_t;
|
---|
14 |
|
---|
15 | int
|
---|
16 | feenableexcept (int exceptions)
|
---|
17 | {
|
---|
18 | exceptions &= 0x3f;
|
---|
19 |
|
---|
20 | /* Enable the traps in the 387 unit. */
|
---|
21 | unsigned short fctrl, orig_fctrl;
|
---|
22 | __asm__ __volatile__ ("fnstcw %0" : "=m" (*&orig_fctrl));
|
---|
23 | fctrl = orig_fctrl & ~exceptions;
|
---|
24 | if (fctrl != orig_fctrl)
|
---|
25 | __asm__ __volatile__ ("fldcw %0" : : "m" (*&fctrl));
|
---|
26 |
|
---|
27 | if (1)
|
---|
28 | {
|
---|
29 | /* Enable the traps in the SSE unit as well. */
|
---|
30 | unsigned int mxcsr, orig_mxcsr;
|
---|
31 | __asm__ __volatile__ ("stmxcsr %0" : "=m" (*&orig_mxcsr));
|
---|
32 | mxcsr = orig_mxcsr & ~(exceptions << 7);
|
---|
33 | if (mxcsr != orig_mxcsr)
|
---|
34 | __asm__ __volatile__ ("ldmxcsr %0" : : "m" (*&mxcsr));
|
---|
35 | }
|
---|
36 |
|
---|
37 | return 0x3f & ~orig_fctrl;
|
---|
38 | }
|
---|
39 |
|
---|
40 | static int
|
---|
41 | sigfpe_on_invalid ()
|
---|
42 | {
|
---|
43 | /* Clear FE_INVALID exceptions from past operations. */
|
---|
44 | feclearexcept (FE_INVALID);
|
---|
45 |
|
---|
46 | /* An FE_INVALID exception shall trigger a SIGFPE signal.
|
---|
47 | This call may fail on arm, arm64, riscv64 CPUs.
|
---|
48 | Also, possibly a bug in glibc/sysdeps/m68k/fpu/feenablxcpt.c: it sets
|
---|
49 | only bit 13, but should better set bits 15, 14, 13 of the control
|
---|
50 | register together. See
|
---|
51 | <https://sourceware.org/bugzilla/show_bug.cgi?id=30993>. */
|
---|
52 | int ret = feenableexcept (FE_INVALID);
|
---|
53 | if (ret == -1)
|
---|
54 | return -1;
|
---|
55 |
|
---|
56 | return 0;
|
---|
57 | }
|
---|
58 |
|
---|
59 | /* Test that feupdateenv(), unlike fesetenv(), can trigger traps. */
|
---|
60 |
|
---|
61 | int
|
---|
62 | main ()
|
---|
63 | {
|
---|
64 | fenv_t env1;
|
---|
65 |
|
---|
66 | /* Get to a known initial state. */
|
---|
67 | assert (feclearexcept (0x3f) == 0);
|
---|
68 |
|
---|
69 | /* Enable trapping on FE_INVALID. */
|
---|
70 | if (sigfpe_on_invalid () < 0)
|
---|
71 | {
|
---|
72 | fputs ("Skipping test: trapping floating-point exceptions are not supported on this machine.\n", stderr);
|
---|
73 | return 77;
|
---|
74 | }
|
---|
75 |
|
---|
76 | /* Save the current environment in env1. */
|
---|
77 | assert (fegetenv (&env1) == 0);
|
---|
78 |
|
---|
79 | /* Go back to the default environment. */
|
---|
80 | assert (fesetenv (FE_DFL_ENV) == 0);
|
---|
81 |
|
---|
82 | /* Modify the current environment. */
|
---|
83 | assert (feraiseexcept (FE_INVALID) == 0);
|
---|
84 |
|
---|
85 | /* Go back to env1.
|
---|
86 | Since the exceptions flag FE_INVALID is currently set, and since
|
---|
87 | env1 has trapping on FE_INVALID enabled, this should trap. */
|
---|
88 | assert (feupdateenv (&env1) == 0);
|
---|
89 |
|
---|
90 | return 0;
|
---|
91 | }
|
---|