Skip to content

Commit

Permalink
[flang] Fix folding edge cases with IEEE_NEXT_{UP/DOWN/AFTER} & NEARE…
Browse files Browse the repository at this point in the history
…ST (llvm#101424)

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.
  • Loading branch information
klausler authored Aug 2, 2024
1 parent c2a95ad commit 90617e9
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 46 deletions.
50 changes: 20 additions & 30 deletions flang/lib/Evaluate/fold-real.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down
21 changes: 16 additions & 5 deletions flang/lib/Evaluate/real.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down
15 changes: 4 additions & 11 deletions flang/test/Evaluate/fold-nearest.f90
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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

0 comments on commit 90617e9

Please sign in to comment.