diff --git a/flang/lib/Evaluate/host.h b/flang/lib/Evaluate/host.h --- a/flang/lib/Evaluate/host.h +++ b/flang/lib/Evaluate/host.h @@ -41,7 +41,9 @@ private: std::fenv_t originalFenv_; - std::fenv_t currentFenv_; +#if __x86_64__ + unsigned int originalMxcsr; +#endif RealFlags flags_; bool hasSubnormalFlushingHardwareControl_{false}; bool hardwareFlagsAreReliable_{true}; diff --git a/flang/lib/Evaluate/host.cpp b/flang/lib/Evaluate/host.cpp --- a/flang/lib/Evaluate/host.cpp +++ b/flang/lib/Evaluate/host.cpp @@ -11,6 +11,9 @@ #include "flang/Common/idioms.h" #include "llvm/Support/Errno.h" #include +#if __x86_64__ +#include +#endif namespace Fortran::evaluate::host { using namespace Fortran::parser::literals; @@ -18,39 +21,42 @@ void HostFloatingPointEnvironment::SetUpHostFloatingPointEnvironment( FoldingContext &context) { errno = 0; + std::fenv_t currentFenv; if (feholdexcept(&originalFenv_) != 0) { common::die("Folding with host runtime: feholdexcept() failed: %s", llvm::sys::StrError(errno).c_str()); return; } - if (fegetenv(¤tFenv_) != 0) { + if (fegetenv(¤tFenv) != 0) { common::die("Folding with host runtime: fegetenv() failed: %s", llvm::sys::StrError(errno).c_str()); return; } #if __x86_64__ hasSubnormalFlushingHardwareControl_ = true; + originalMxcsr = _mm_getcsr(); + unsigned int currentMxcsr{originalMxcsr}; if (context.flushSubnormalsToZero()) { - currentFenv_.__mxcsr |= 0x8000; // result - currentFenv_.__mxcsr |= 0x0040; // operands + currentMxcsr |= 0x8000; + currentMxcsr |= 0x0040; } else { - currentFenv_.__mxcsr &= ~0x8000; // result - currentFenv_.__mxcsr &= ~0x0040; // operands + currentMxcsr &= ~0x8000; + currentMxcsr &= ~0x0040; } #elif defined(__aarch64__) #if defined(__GNU_LIBRARY__) hasSubnormalFlushingHardwareControl_ = true; if (context.flushSubnormalsToZero()) { - currentFenv_.__fpcr |= (1U << 24); // control register + currentFenv.__fpcr |= (1U << 24); // control register } else { - currentFenv_.__fpcr &= ~(1U << 24); // control register + currentFenv.__fpcr &= ~(1U << 24); // control register } #elif defined(__BIONIC__) hasSubnormalFlushingHardwareControl_ = true; if (context.flushSubnormalsToZero()) { - currentFenv_.__control |= (1U << 24); // control register + currentFenv.__control |= (1U << 24); // control register } else { - currentFenv_.__control &= ~(1U << 24); // control register + currentFenv.__control &= ~(1U << 24); // control register } #else // If F18 is built with other C libraries on AArch64, software flushing will @@ -70,11 +76,15 @@ hardwareFlagsAreReliable_ = false; #endif errno = 0; - if (fesetenv(¤tFenv_) != 0) { + if (fesetenv(¤tFenv) != 0) { common::die("Folding with host runtime: fesetenv() failed: %s", llvm::sys::StrError(errno).c_str()); return; } +#if __x86_64__ + _mm_setcsr(currentMxcsr); +#endif + switch (context.rounding().mode) { case common::RoundingMode::TiesToEven: fesetround(FE_TONEAREST); @@ -141,6 +151,10 @@ "Folding with host runtime: fesetenv() failed while restoring fenv: %s", llvm::sys::StrError(errno).c_str()); } +#if __x86_64__ + _mm_setcsr(originalMxcsr); +#endif + errno = 0; } } // namespace Fortran::evaluate::host diff --git a/flang/unittests/Evaluate/fp-testing.h b/flang/unittests/Evaluate/fp-testing.h --- a/flang/unittests/Evaluate/fp-testing.h +++ b/flang/unittests/Evaluate/fp-testing.h @@ -19,7 +19,9 @@ private: fenv_t originalFenv_; - fenv_t currentFenv_; +#if __x86_64__ + unsigned int originalMxcsr; +#endif }; #endif // FORTRAN_TEST_EVALUATE_FP_TESTING_H_ diff --git a/flang/unittests/Evaluate/fp-testing.cpp b/flang/unittests/Evaluate/fp-testing.cpp --- a/flang/unittests/Evaluate/fp-testing.cpp +++ b/flang/unittests/Evaluate/fp-testing.cpp @@ -3,6 +3,9 @@ #include #include #include +#if __x86_64__ +#include +#endif using Fortran::common::RoundingMode; using Fortran::evaluate::RealFlag; @@ -20,31 +23,38 @@ llvm::sys::StrError(errno).c_str()); std::abort(); } - if (fegetenv(¤tFenv_) != 0) { + fenv_t currentFenv; + if (fegetenv(¤tFenv) != 0) { std::fprintf( stderr, "fegetenv() failed: %s\n", llvm::sys::StrError(errno).c_str()); std::abort(); } + #if __x86_64__ + originalMxcsr = _mm_getcsr(); + unsigned int currentMxcsr{originalMxcsr}; if (treatSubnormalOperandsAsZero) { - currentFenv_.__mxcsr |= 0x0040; + currentMxcsr |= 0x0040; } else { - currentFenv_.__mxcsr &= ~0x0040; + currentMxcsr &= ~0x0040; } if (flushSubnormalResultsToZero) { - currentFenv_.__mxcsr |= 0x8000; + currentMxcsr |= 0x8000; } else { - currentFenv_.__mxcsr &= ~0x8000; + currentMxcsr &= ~0x8000; } #else // TODO others #endif errno = 0; - if (fesetenv(¤tFenv_) != 0) { + if (fesetenv(¤tFenv) != 0) { std::fprintf( stderr, "fesetenv() failed: %s\n", llvm::sys::StrError(errno).c_str()); std::abort(); } +#if __x86_64__ + _mm_setcsr(currentMxcsr); +#endif } ScopedHostFloatingPointEnvironment::~ScopedHostFloatingPointEnvironment() { @@ -54,6 +64,9 @@ stderr, "fesetenv() failed: %s\n", llvm::sys::StrError(errno).c_str()); std::abort(); } +#if __x86_64__ + _mm_setcsr(originalMxcsr); +#endif } void ScopedHostFloatingPointEnvironment::ClearFlags() const {