The non-GTest library will be shared by unittests of Flang and Flang-RT. Promote it as a regular library for use by both projects. In the long term, we may want to convert these to regular GTest checks to avoid having multiple testing frameworks.
125 lines
3.1 KiB
C++
125 lines
3.1 KiB
C++
//===-- 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>
|
|
#include <cstring>
|
|
#if __x86_64__
|
|
#include <xmmintrin.h>
|
|
#endif
|
|
|
|
using Fortran::common::RealFlag;
|
|
using Fortran::common::RoundingMode;
|
|
|
|
ScopedHostFloatingPointEnvironment::ScopedHostFloatingPointEnvironment(
|
|
#if __x86_64__
|
|
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__
|
|
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__
|
|
_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;
|
|
}
|
|
}
|