| //===-- lib/Testing/fp-testing.cpp ------------------------------*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "flang/Testing/fp-testing.h" | |
| #include "llvm/Support/Errno.h" | |
| #include <cstdio> | |
| #include <cstdlib> | |
| #if __x86_64__ || _M_X64 | |
| #include <xmmintrin.h> | |
| #endif | |
| using Fortran::common::RealFlag; | |
| using Fortran::common::RoundingMode; | |
| ScopedHostFloatingPointEnvironment::ScopedHostFloatingPointEnvironment( | |
| #if __x86_64__ || _M_X64 | |
| bool treatSubnormalOperandsAsZero, bool flushSubnormalResultsToZero | |
| #else | |
| bool, bool | |
| #endif | |
| ) { | |
| errno = 0; | |
| if (feholdexcept(&originalFenv_) != 0) { | |
| std::fprintf(stderr, "feholdexcept() failed: %s\n", | |
| llvm::sys::StrError(errno).c_str()); | |
| std::abort(); | |
| } | |
| 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__ || _M_X64 | |
| originalMxcsr = _mm_getcsr(); | |
| unsigned int currentMxcsr{originalMxcsr}; | |
| if (treatSubnormalOperandsAsZero) { | |
| currentMxcsr |= 0x0040; | |
| } else { | |
| currentMxcsr &= ~0x0040; | |
| } | |
| if (flushSubnormalResultsToZero) { | |
| currentMxcsr |= 0x8000; | |
| } else { | |
| currentMxcsr &= ~0x8000; | |
| } | |
| #else | |
| // TODO others | |
| #endif | |
| errno = 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() { | |
| errno = 0; | |
| if (fesetenv(&originalFenv_) != 0) { | |
| std::fprintf( | |
| stderr, "fesetenv() failed: %s\n", llvm::sys::StrError(errno).c_str()); | |
| std::abort(); | |
| } | |
| #if __x86_64__ || _M_X64 | |
| _mm_setcsr(originalMxcsr); | |
| #endif | |
| } | |
| void ScopedHostFloatingPointEnvironment::ClearFlags() const { | |
| feclearexcept(FE_ALL_EXCEPT); | |
| } | |
| RealFlags ScopedHostFloatingPointEnvironment::CurrentFlags() { | |
| int exceptions = fetestexcept(FE_ALL_EXCEPT); | |
| RealFlags flags; | |
| if (exceptions & FE_INVALID) { | |
| flags.set(RealFlag::InvalidArgument); | |
| } | |
| if (exceptions & FE_DIVBYZERO) { | |
| flags.set(RealFlag::DivideByZero); | |
| } | |
| if (exceptions & FE_OVERFLOW) { | |
| flags.set(RealFlag::Overflow); | |
| } | |
| if (exceptions & FE_UNDERFLOW) { | |
| flags.set(RealFlag::Underflow); | |
| } | |
| if (exceptions & FE_INEXACT) { | |
| flags.set(RealFlag::Inexact); | |
| } | |
| return flags; | |
| } | |
| void ScopedHostFloatingPointEnvironment::SetRounding(Rounding rounding) { | |
| switch (rounding.mode) { | |
| case RoundingMode::TiesToEven: | |
| fesetround(FE_TONEAREST); | |
| break; | |
| case RoundingMode::ToZero: | |
| fesetround(FE_TOWARDZERO); | |
| break; | |
| case RoundingMode::Up: | |
| fesetround(FE_UPWARD); | |
| break; | |
| case RoundingMode::Down: | |
| fesetround(FE_DOWNWARD); | |
| break; | |
| case RoundingMode::TiesAwayFromZero: | |
| std::fprintf(stderr, "SetRounding: TiesAwayFromZero not available"); | |
| std::abort(); | |
| break; | |
| } | |
| } |