[flang] Fix 482: do not check floating point environment flags with clang
authorJean Perier <jperier@nvidia.com>
Thu, 6 Jun 2019 10:24:35 +0000 (03:24 -0700)
committerJean Perier <jperier@nvidia.com>
Thu, 6 Jun 2019 10:24:35 +0000 (03:24 -0700)
Original-commit: flang-compiler/f18@fc423c151654055e50c3fad563be4e0a6afd7348
Reviewed-on: https://github.com/flang-compiler/f18/pull/483
Tree-same-pre-rewrite: false

flang/lib/evaluate/host.cc
flang/lib/evaluate/host.h
flang/lib/evaluate/intrinsics-library-templates.h
flang/test/evaluate/folding04.f90

index f2cc383..e41e01d 100644 (file)
@@ -66,6 +66,15 @@ void HostFloatingPointEnvironment::SetUpHostFloatingPointEnvironment(
   // 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(&currentFenv_) != 0) {
     common::die("Folding with host runtime: fesetenv() failed: %s",
@@ -83,41 +92,43 @@ void HostFloatingPointEnvironment::SetUpHostFloatingPointEnvironment(
         "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) {
index 7dca52f..fe7dcf5 100644 (file)
@@ -39,14 +39,18 @@ class HostFloatingPointEnvironment {
 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
index 1a10fec..7bc1e67 100644 (file)
@@ -88,24 +88,42 @@ template<typename TR, typename... ArgInfo> struct CallableHostWrapper {
     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>
index 9ca5503..4bd2961 100644 (file)
@@ -30,26 +30,23 @@ module real_tests
   !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