1 | #include "fp-testing.h" |
2 | #include "llvm/Support/Errno.h" |
3 | #include <cstdio> |
4 | #include <cstdlib> |
5 | #include <cstring> |
6 | #if __x86_64__ |
7 | #include <xmmintrin.h> |
8 | #endif |
9 | |
10 | using Fortran::common::RoundingMode; |
11 | using Fortran::evaluate::RealFlag; |
12 | |
13 | ScopedHostFloatingPointEnvironment::ScopedHostFloatingPointEnvironment( |
14 | #if __x86_64__ |
15 | bool treatSubnormalOperandsAsZero, bool flushSubnormalResultsToZero |
16 | #else |
17 | bool, bool |
18 | #endif |
19 | ) { |
20 | errno = 0; |
21 | if (feholdexcept(envp: &originalFenv_) != 0) { |
22 | std::fprintf(stderr, format: "feholdexcept() failed: %s\n" , |
23 | llvm::sys::StrError(errno).c_str()); |
24 | std::abort(); |
25 | } |
26 | fenv_t currentFenv; |
27 | if (fegetenv(envp: ¤tFenv) != 0) { |
28 | std::fprintf( |
29 | stderr, format: "fegetenv() failed: %s\n" , llvm::sys::StrError(errno).c_str()); |
30 | std::abort(); |
31 | } |
32 | |
33 | #if __x86_64__ |
34 | originalMxcsr = _mm_getcsr(); |
35 | unsigned int currentMxcsr{originalMxcsr}; |
36 | if (treatSubnormalOperandsAsZero) { |
37 | currentMxcsr |= 0x0040; |
38 | } else { |
39 | currentMxcsr &= ~0x0040; |
40 | } |
41 | if (flushSubnormalResultsToZero) { |
42 | currentMxcsr |= 0x8000; |
43 | } else { |
44 | currentMxcsr &= ~0x8000; |
45 | } |
46 | #else |
47 | // TODO others |
48 | #endif |
49 | errno = 0; |
50 | if (fesetenv(envp: ¤tFenv) != 0) { |
51 | std::fprintf( |
52 | stderr, format: "fesetenv() failed: %s\n" , llvm::sys::StrError(errno).c_str()); |
53 | std::abort(); |
54 | } |
55 | #if __x86_64__ |
56 | _mm_setcsr(i: currentMxcsr); |
57 | #endif |
58 | } |
59 | |
60 | ScopedHostFloatingPointEnvironment::~ScopedHostFloatingPointEnvironment() { |
61 | errno = 0; |
62 | if (fesetenv(envp: &originalFenv_) != 0) { |
63 | std::fprintf( |
64 | stderr, format: "fesetenv() failed: %s\n" , llvm::sys::StrError(errno).c_str()); |
65 | std::abort(); |
66 | } |
67 | #if __x86_64__ |
68 | _mm_setcsr(i: originalMxcsr); |
69 | #endif |
70 | } |
71 | |
72 | void ScopedHostFloatingPointEnvironment::ClearFlags() const { |
73 | feclearexcept(FE_ALL_EXCEPT); |
74 | } |
75 | |
76 | RealFlags ScopedHostFloatingPointEnvironment::CurrentFlags() { |
77 | int exceptions = fetestexcept(FE_ALL_EXCEPT); |
78 | RealFlags flags; |
79 | if (exceptions & FE_INVALID) { |
80 | flags.set(RealFlag::InvalidArgument); |
81 | } |
82 | if (exceptions & FE_DIVBYZERO) { |
83 | flags.set(RealFlag::DivideByZero); |
84 | } |
85 | if (exceptions & FE_OVERFLOW) { |
86 | flags.set(RealFlag::Overflow); |
87 | } |
88 | if (exceptions & FE_UNDERFLOW) { |
89 | flags.set(RealFlag::Underflow); |
90 | } |
91 | if (exceptions & FE_INEXACT) { |
92 | flags.set(RealFlag::Inexact); |
93 | } |
94 | return flags; |
95 | } |
96 | |
97 | void ScopedHostFloatingPointEnvironment::SetRounding(Rounding rounding) { |
98 | switch (rounding.mode) { |
99 | case RoundingMode::TiesToEven: |
100 | fesetround(FE_TONEAREST); |
101 | break; |
102 | case RoundingMode::ToZero: |
103 | fesetround(FE_TOWARDZERO); |
104 | break; |
105 | case RoundingMode::Up: |
106 | fesetround(FE_UPWARD); |
107 | break; |
108 | case RoundingMode::Down: |
109 | fesetround(FE_DOWNWARD); |
110 | break; |
111 | case RoundingMode::TiesAwayFromZero: |
112 | std::fprintf(stderr, format: "SetRounding: TiesAwayFromZero not available" ); |
113 | std::abort(); |
114 | break; |
115 | } |
116 | } |
117 | |