[flang-commits] [flang] [flang] Fix folding edge cases with IEEE_NEXT_{UP/DOWN/AFTER} & NEAREST (PR #101424)

via flang-commits flang-commits at lists.llvm.org
Wed Jul 31 15:21:58 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-semantics

Author: Peter Klausler (klausler)

<details>
<summary>Changes</summary>

The generation of 80-bit x87 floating-point infinities was incorrect in Normalize(), the comparison for IEEE_NEXT_AFTER needs to use the most precise type of its arguments, and we don't need to warn about overflows from +/-HUGE() to infinity.  Warnings about NaN arguments remain in place, and enabled by default, as their usage may or may not be portable, and their appearance in a real code seems most likely to signify an earlier error.

---
Full diff: https://github.com/llvm/llvm-project/pull/101424.diff


3 Files Affected:

- (modified) flang/lib/Evaluate/fold-real.cpp (+20-30) 
- (modified) flang/lib/Evaluate/real.cpp (+16-5) 
- (modified) flang/test/Evaluate/fold-nearest.f90 (+4-11) 


``````````diff
diff --git a/flang/lib/Evaluate/fold-real.cpp b/flang/lib/Evaluate/fold-real.cpp
index 2dd08a7d1a6e1..15a9bcf6dbd85 100644
--- a/flang/lib/Evaluate/fold-real.cpp
+++ b/flang/lib/Evaluate/fold-real.cpp
@@ -359,28 +359,27 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
             using TS = ResultType<decltype(sVal)>;
             bool badSConst{false};
             if (auto sConst{GetScalarConstantValue<TS>(sVal)}; sConst &&
-                sConst->IsZero() &&
+                (sConst->IsZero() || sConst->IsNotANumber()) &&
                 context.languageFeatures().ShouldWarn(
                     common::UsageWarning::FoldingValueChecks)) {
-              context.messages().Say("NEAREST: S argument is zero"_warn_en_US);
+              context.messages().Say("NEAREST: S argument is %s"_warn_en_US,
+                  sConst->IsZero() ? "zero" : "NaN");
               badSConst = true;
             }
             return FoldElementalIntrinsic<T, T, TS>(context, std::move(funcRef),
                 ScalarFunc<T, T, TS>([&](const Scalar<T> &x,
                                          const Scalar<TS> &s) -> Scalar<T> {
-                  if (!badSConst && s.IsZero() &&
+                  if (!badSConst && (s.IsZero() || s.IsNotANumber()) &&
                       context.languageFeatures().ShouldWarn(
                           common::UsageWarning::FoldingValueChecks)) {
                     context.messages().Say(
-                        "NEAREST: S argument is zero"_warn_en_US);
+                        "NEAREST: S argument is %s"_warn_en_US,
+                        s.IsZero() ? "zero" : "NaN");
                   }
                   auto result{x.NEAREST(!s.IsNegative())};
                   if (context.languageFeatures().ShouldWarn(
                           common::UsageWarning::FoldingException)) {
-                    if (result.flags.test(RealFlag::Overflow)) {
-                      context.messages().Say(
-                          "NEAREST intrinsic folding overflow"_warn_en_US);
-                    } else if (result.flags.test(RealFlag::InvalidArgument)) {
+                    if (result.flags.test(RealFlag::InvalidArgument)) {
                       context.messages().Say(
                           "NEAREST intrinsic folding: bad argument"_warn_en_US);
                     }
@@ -469,32 +468,26 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
             return FoldElementalIntrinsic<T, T, TY>(context, std::move(funcRef),
                 ScalarFunc<T, T, TY>([&](const Scalar<T> &x,
                                          const Scalar<TY> &y) -> Scalar<T> {
-                  bool upward{true};
-                  switch (x.Compare(Scalar<T>::Convert(y).value)) {
+                  bool reverseCompare{
+                      Scalar<T>::binaryPrecision < Scalar<TY>::binaryPrecision};
+                  switch (reverseCompare
+                          ? y.Compare(Scalar<TY>::Convert(x).value)
+                          : x.Compare(Scalar<T>::Convert(y).value)) {
                   case Relation::Unordered:
                     if (context.languageFeatures().ShouldWarn(
                             common::UsageWarning::FoldingValueChecks)) {
                       context.messages().Say(
-                          "IEEE_NEXT_AFTER intrinsic folding: bad argument"_warn_en_US);
+                          "IEEE_NEXT_AFTER intrinsic folding: arguments are unordered"_warn_en_US);
                     }
-                    return x;
+                    return x.NotANumber();
                   case Relation::Equal:
-                    return x;
-                  case Relation::Less:
-                    upward = true;
                     break;
+                  case Relation::Less:
+                    return x.NEAREST(!reverseCompare).value;
                   case Relation::Greater:
-                    upward = false;
-                    break;
-                  }
-                  auto result{x.NEAREST(upward)};
-                  if (result.flags.test(RealFlag::Overflow) &&
-                      context.languageFeatures().ShouldWarn(
-                          common::UsageWarning::FoldingException)) {
-                    context.messages().Say(
-                        "IEEE_NEXT_AFTER intrinsic folding overflow"_warn_en_US);
+                    return x.NEAREST(reverseCompare).value;
                   }
-                  return result.value;
+                  return x; // dodge bogus "missing return" GCC warning
                 }));
           },
           yExpr->u);
@@ -508,12 +501,9 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
           auto result{x.NEAREST(upward)};
           if (context.languageFeatures().ShouldWarn(
                   common::UsageWarning::FoldingException)) {
-            if (result.flags.test(RealFlag::Overflow)) {
-              context.messages().Say(
-                  "%s intrinsic folding overflow"_warn_en_US, iName);
-            } else if (result.flags.test(RealFlag::InvalidArgument)) {
+            if (result.flags.test(RealFlag::InvalidArgument)) {
               context.messages().Say(
-                  "%s intrinsic folding: bad argument"_warn_en_US, iName);
+                  "%s intrinsic folding: argument is NaN"_warn_en_US, iName);
             }
           }
           return result.value;
diff --git a/flang/lib/Evaluate/real.cpp b/flang/lib/Evaluate/real.cpp
index a5f8070c684fe..642490646dcaf 100644
--- a/flang/lib/Evaluate/real.cpp
+++ b/flang/lib/Evaluate/real.cpp
@@ -358,10 +358,15 @@ ValueWithRealFlags<Real<W, P>> Real<W, P>::NEAREST(bool upward) const {
         }
       }
     }
-    result.flags = result.value.Normalize(isNegative, expo, nearest);
-  } else if (IsInfinite() && upward == isNegative) {
-    result.value = isNegative ? HUGE().Negate() : HUGE(); // largest mag finite
-  } else {
+    result.value.Normalize(isNegative, expo, nearest);
+  } else if (IsInfinite()) {
+    if (upward == isNegative) {
+      result.value =
+          isNegative ? HUGE().Negate() : HUGE(); // largest mag finite
+    } else {
+      result.value = *this;
+    }
+  } else { // NaN
     result.flags.set(RealFlag::InvalidArgument);
     result.value = *this;
   }
@@ -526,10 +531,16 @@ RealFlags Real<W, P>::Normalize(bool negative, int exponent,
         (rounding.mode == common::RoundingMode::Up && !negative) ||
         (rounding.mode == common::RoundingMode::Down && negative)) {
       word_ = Word{maxExponent}.SHIFTL(significandBits); // Inf
+      if constexpr (!isImplicitMSB) {
+        word_ = word_.IBSET(significandBits - 1);
+      }
     } else {
       // directed rounding: round to largest finite value rather than infinity
       // (x86 does this, not sure whether it's standard behavior)
-      word_ = Word{word_.MASKR(word_.bits - 1)}.IBCLR(significandBits);
+      word_ = Word{word_.MASKR(word_.bits - 1)};
+      if constexpr (isImplicitMSB) {
+        word_ = word_.IBCLR(significandBits);
+      }
     }
     if (negative) {
       word_ = word_.IBSET(bits - 1);
diff --git a/flang/test/Evaluate/fold-nearest.f90 b/flang/test/Evaluate/fold-nearest.f90
index 41853a699d8e9..7ca7757f79664 100644
--- a/flang/test/Evaluate/fold-nearest.f90
+++ b/flang/test/Evaluate/fold-nearest.f90
@@ -6,11 +6,8 @@ module m1
   logical, parameter :: test_2 = nearest(minSubnormal, -1.) == 0
   logical, parameter :: test_3 = nearest(1., 1.) == 1.0000001
   logical, parameter :: test_4 = nearest(1.0000001, -1.) == 1
-  !WARN: warning: NEAREST intrinsic folding overflow
   real, parameter :: inf = nearest(huge(1.), 1.)
-  !WARN: warning: NEAREST intrinsic folding: bad argument
   logical, parameter :: test_5 = nearest(inf, 1.) == inf
-  !WARN: warning: NEAREST intrinsic folding: bad argument
   logical, parameter :: test_6 = nearest(-inf, -1.) == -inf
   logical, parameter :: test_7 = nearest(1.9999999, 1.) == 2.
   logical, parameter :: test_8 = nearest(2., -1.) == 1.9999999
@@ -59,10 +56,10 @@ module m2
   logical, parameter :: test_12 = ieee_next_after(1., 1.) == 1.
   !WARN: warning: invalid argument on division
   real, parameter :: nan = 0. / 0.
-  !WARN: warning: IEEE_NEXT_AFTER intrinsic folding: bad argument
+  !WARN: warning: IEEE_NEXT_AFTER intrinsic folding: arguments are unordered
   real, parameter :: x13 = ieee_next_after(nan, nan)
   logical, parameter :: test_13 = .not. (x13 == x13)
-  !WARN: warning: IEEE_NEXT_AFTER intrinsic folding: bad argument
+  !WARN: warning: IEEE_NEXT_AFTER intrinsic folding: arguments are unordered
   real, parameter :: x14 = ieee_next_after(nan, 0.)
   logical, parameter :: test_14 = .not. (x14 == x14)
 end module
@@ -77,24 +74,20 @@ module m3
   logical, parameter :: test_4 = ieee_next_down(1.0000000000000002d0) == 1.d0
   !WARN: warning: division by zero
   real(kind(0.d0)), parameter :: inf = 1.d0 / 0.d0
-  !WARN: warning: IEEE_NEXT_UP intrinsic folding overflow
   logical, parameter :: test_5 = ieee_next_up(huge(0.d0)) == inf
-  !WARN: warning: IEEE_NEXT_DOWN intrinsic folding overflow
   logical, parameter :: test_6 = ieee_next_down(-huge(0.d0)) == -inf
-  !WARN: warning: IEEE_NEXT_UP intrinsic folding: bad argument
   logical, parameter :: test_7 = ieee_next_up(inf) == inf
   logical, parameter :: test_8 = ieee_next_down(inf) == h
   logical, parameter :: test_9 = ieee_next_up(-inf) == -h
-  !WARN: warning: IEEE_NEXT_DOWN intrinsic folding: bad argument
   logical, parameter :: test_10 = ieee_next_down(-inf) == -inf
   logical, parameter :: test_11 = ieee_next_up(1.9999999999999997d0) == 2.d0
   logical, parameter :: test_12 = ieee_next_down(2.d0) == 1.9999999999999997d0
   !WARN: warning: invalid argument on division
   real(kind(0.d0)), parameter :: nan = 0.d0 / 0.d0
-  !WARN: warning: IEEE_NEXT_UP intrinsic folding: bad argument
+  !WARN: warning: IEEE_NEXT_UP intrinsic folding: argument is NaN
   real(kind(0.d0)), parameter :: x13 = ieee_next_up(nan)
   logical, parameter :: test_13 = .not. (x13 == x13)
-  !WARN: warning: IEEE_NEXT_DOWN intrinsic folding: bad argument
+  !WARN: warning: IEEE_NEXT_DOWN intrinsic folding: argument is NaN
   real(kind(0.d0)), parameter :: x14 = ieee_next_down(nan)
   logical, parameter :: test_14 = .not. (x14 == x14)
 end module

``````````

</details>


https://github.com/llvm/llvm-project/pull/101424


More information about the flang-commits mailing list