// If F18 is not built on one of the above host architecture, software
// flushing will be performed around host library calls if needed.
#endif
+
+#ifdef __clang__
+ // clang does not ensure that floating point environment flags are meaningful.
+ // It may perform optimizations that will impact the floating point
+ // environment. For instance, libc++ complex float tan and tanh compilation
+ // with clang -O2 introduces a division by zero on X86 in unused slots of xmm
+ // registers. Therefore, fetestexcept should not be used.
+ hardwareFlagsAreReliable_ = false;
+#endif
errno = 0;
if (fesetenv(¤tFenv_) != 0) {
common::die("Folding with host runtime: fesetenv() failed: %s",
"TiesAwayFromZero rounding mode is not available not available when folding constants with host runtime. Using TiesToEven instead."_en_US);
break;
}
+ flags_.clear();
errno = 0;
}
void HostFloatingPointEnvironment::CheckAndRestoreFloatingPointEnvironment(
FoldingContext &context) {
int errnoCapture{errno};
- 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);
+ if (hardwareFlagsAreReliable()) {
+ int exceptions{fetestexcept(FE_ALL_EXCEPT)};
+ 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);
+ }
}
- if (flags.empty()) {
+ if (flags_.empty()) {
if (errnoCapture == EDOM) {
- flags.set(RealFlag::InvalidArgument);
+ flags_.set(RealFlag::InvalidArgument);
}
if (errnoCapture == ERANGE) {
// can't distinguish over/underflow from errno
- flags.set(RealFlag::Overflow);
+ flags_.set(RealFlag::Overflow);
}
}
- if (!flags.empty()) {
- RealFlagWarnings(context, flags, "folding function with host runtime");
+ if (!flags_.empty()) {
+ RealFlagWarnings(context, flags_, "intrinsic function");
}
errno = 0;
if (fesetenv(&originalFenv_) != 0) {
public:
void SetUpHostFloatingPointEnvironment(FoldingContext &);
void CheckAndRestoreFloatingPointEnvironment(FoldingContext &);
- bool hasSubnormalFlushingHardwareControl() {
+ bool hasSubnormalFlushingHardwareControl() const {
return hasSubnormalFlushingHardwareControl_;
}
+ void SetFlag(RealFlag flag) { flags_.set(flag); }
+ bool hardwareFlagsAreReliable() const { return hardwareFlagsAreReliable_; }
private:
std::fenv_t originalFenv_;
std::fenv_t currentFenv_;
+ RealFlags flags_;
bool hasSubnormalFlushingHardwareControl_{false};
+ bool hardwareFlagsAreReliable_{true};
};
// Type mapping from F18 types to host types
if constexpr (host::HostTypeExists<TR, typename ArgInfo::Type...>()) {
host::HostFloatingPointEnvironment hostFPE;
hostFPE.SetUpHostFloatingPointEnvironment(context);
- host::HostType<TR> res{};
+ host::HostType<TR> hostResult{};
+ Scalar<TR> result{};
if (context.flushSubnormalsToZero() &&
!hostFPE.hasSubnormalFlushingHardwareControl()) {
- res = func(host::CastFortranToHost<typename ArgInfo::Type>(
+ hostResult = func(host::CastFortranToHost<typename ArgInfo::Type>(
Flusher<typename ArgInfo::Type>::FlushSubnormals(x))...);
- hostFPE.CheckAndRestoreFloatingPointEnvironment(context);
- return Flusher<TR>::FlushSubnormals(host::CastHostToFortran<TR>(res));
+ result = Flusher<TR>::FlushSubnormals(
+ host::CastHostToFortran<TR>(hostResult));
} else {
- res = func(host::CastFortranToHost<typename ArgInfo::Type>(x)...);
- hostFPE.CheckAndRestoreFloatingPointEnvironment(context);
- return host::CastHostToFortran<TR>(res);
+ hostResult =
+ func(host::CastFortranToHost<typename ArgInfo::Type>(x)...);
+ result = host::CastHostToFortran<TR>(hostResult);
}
+ if (!hostFPE.hardwareFlagsAreReliable()) {
+ CheckFloatingPointIssues(hostFPE, result);
+ }
+ hostFPE.CheckAndRestoreFloatingPointEnvironment(context);
+ return result;
} else {
common::die("Internal error: Host does not supports this function type."
"This should not have been called for folding");
}
}
static constexpr inline auto MakeScalarCallable() { return &scalarCallable; }
+
+ static void CheckFloatingPointIssues(
+ host::HostFloatingPointEnvironment &hostFPE, const Scalar<TR> &x) {
+ if constexpr (TR::category == TypeCategory::Complex ||
+ TR::category == TypeCategory::Real) {
+ if (x.IsNotANumber()) {
+ hostFPE.SetFlag(RealFlag::InvalidArgument);
+ } else if (x.IsInfinite()) {
+ hostFPE.SetFlag(RealFlag::Overflow);
+ }
+ }
+ }
};
template<typename TR, typename... ArgInfo>
!WARN: division by zero on division
real(4), parameter :: r4_ninf = -1._4/0._4
- !WARN: invalid argument on folding function with host runtime
+ !WARN: invalid argument on intrinsic function
real(4), parameter :: nan_r4_acos1 = acos(1.1)
TEST_ISNAN(nan_r4_acos1)
- !WARN: invalid argument on folding function with host runtime
+ !WARN: invalid argument on intrinsic function
real(4), parameter :: nan_r4_acos2 = acos(r4_pmax)
TEST_ISNAN(nan_r4_acos2)
- !WARN: invalid argument on folding function with host runtime
+ !WARN: invalid argument on intrinsic function
real(4), parameter :: nan_r4_acos3 = acos(r4_nmax)
TEST_ISNAN(nan_r4_acos3)
- !WARN: invalid argument on folding function with host runtime
+ !WARN: invalid argument on intrinsic function
real(4), parameter :: nan_r4_acos4 = acos(r4_ninf)
TEST_ISNAN(nan_r4_acos4)
- !WARN: invalid argument on folding function with host runtime
+ !WARN: invalid argument on intrinsic function
real(4), parameter :: nan_r4_acos5 = acos(r4_pinf)
TEST_ISNAN(nan_r4_acos5)
- ! No warnings expected for NaN propagation (quiet)
- real(4), parameter :: nan_r4_acos6 = acos(r4_nan)
- TEST_ISNAN(nan_r4_acos6)
- !WARN: overflow on folding function with host runtime
+ !WARN: overflow on intrinsic function
logical, parameter :: test_exp_overflow = exp(256._4).EQ.r4_pinf
end module