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
ScopedHostFloatingPointEnvironment(bool treatSubnormalOperandsAsZero,bool flushSubnormalResultsToZero)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(&originalFenv_) != 0) {
22 std::fprintf(stderr, "feholdexcept() failed: %s\n",
23 llvm::sys::StrError(errno).c_str());
24 std::abort();
25 }
26 fenv_t currentFenv;
27 if (fegetenv(¤tFenv) != 0) {
28 std::fprintf(
29 stderr, "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(¤tFenv) != 0) {
51 std::fprintf(
52 stderr, "fesetenv() failed: %s\n", llvm::sys::StrError(errno).c_str());
53 std::abort();
54 }
55 #if __x86_64__
56 _mm_setcsr(currentMxcsr);
57 #endif
58 }
59
~ScopedHostFloatingPointEnvironment()60 ScopedHostFloatingPointEnvironment::~ScopedHostFloatingPointEnvironment() {
61 errno = 0;
62 if (fesetenv(&originalFenv_) != 0) {
63 std::fprintf(
64 stderr, "fesetenv() failed: %s\n", llvm::sys::StrError(errno).c_str());
65 std::abort();
66 }
67 #if __x86_64__
68 _mm_setcsr(originalMxcsr);
69 #endif
70 }
71
ClearFlags() const72 void ScopedHostFloatingPointEnvironment::ClearFlags() const {
73 feclearexcept(FE_ALL_EXCEPT);
74 }
75
CurrentFlags()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
SetRounding(Rounding rounding)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, "SetRounding: TiesAwayFromZero not available");
113 std::abort();
114 break;
115 }
116 }
117