[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