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(&currentFenv) != 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(&currentFenv) != 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