Skip to content

Commit 90617e9

Browse files
authored
[flang] Fix folding edge cases with IEEE_NEXT_{UP/DOWN/AFTER} & NEAREST (#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.
1 parent c2a95ad commit 90617e9

File tree

3 files changed

+40
-46
lines changed

3 files changed

+40
-46
lines changed

flang/lib/Evaluate/fold-real.cpp

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -359,28 +359,27 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
359359
using TS = ResultType<decltype(sVal)>;
360360
bool badSConst{false};
361361
if (auto sConst{GetScalarConstantValue<TS>(sVal)}; sConst &&
362-
sConst->IsZero() &&
362+
(sConst->IsZero() || sConst->IsNotANumber()) &&
363363
context.languageFeatures().ShouldWarn(
364364
common::UsageWarning::FoldingValueChecks)) {
365-
context.messages().Say("NEAREST: S argument is zero"_warn_en_US);
365+
context.messages().Say("NEAREST: S argument is %s"_warn_en_US,
366+
sConst->IsZero() ? "zero" : "NaN");
366367
badSConst = true;
367368
}
368369
return FoldElementalIntrinsic<T, T, TS>(context, std::move(funcRef),
369370
ScalarFunc<T, T, TS>([&](const Scalar<T> &x,
370371
const Scalar<TS> &s) -> Scalar<T> {
371-
if (!badSConst && s.IsZero() &&
372+
if (!badSConst && (s.IsZero() || s.IsNotANumber()) &&
372373
context.languageFeatures().ShouldWarn(
373374
common::UsageWarning::FoldingValueChecks)) {
374375
context.messages().Say(
375-
"NEAREST: S argument is zero"_warn_en_US);
376+
"NEAREST: S argument is %s"_warn_en_US,
377+
s.IsZero() ? "zero" : "NaN");
376378
}
377379
auto result{x.NEAREST(!s.IsNegative())};
378380
if (context.languageFeatures().ShouldWarn(
379381
common::UsageWarning::FoldingException)) {
380-
if (result.flags.test(RealFlag::Overflow)) {
381-
context.messages().Say(
382-
"NEAREST intrinsic folding overflow"_warn_en_US);
383-
} else if (result.flags.test(RealFlag::InvalidArgument)) {
382+
if (result.flags.test(RealFlag::InvalidArgument)) {
384383
context.messages().Say(
385384
"NEAREST intrinsic folding: bad argument"_warn_en_US);
386385
}
@@ -469,32 +468,26 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
469468
return FoldElementalIntrinsic<T, T, TY>(context, std::move(funcRef),
470469
ScalarFunc<T, T, TY>([&](const Scalar<T> &x,
471470
const Scalar<TY> &y) -> Scalar<T> {
472-
bool upward{true};
473-
switch (x.Compare(Scalar<T>::Convert(y).value)) {
471+
bool reverseCompare{
472+
Scalar<T>::binaryPrecision < Scalar<TY>::binaryPrecision};
473+
switch (reverseCompare
474+
? y.Compare(Scalar<TY>::Convert(x).value)
475+
: x.Compare(Scalar<T>::Convert(y).value)) {
474476
case Relation::Unordered:
475477
if (context.languageFeatures().ShouldWarn(
476478
common::UsageWarning::FoldingValueChecks)) {
477479
context.messages().Say(
478-
"IEEE_NEXT_AFTER intrinsic folding: bad argument"_warn_en_US);
480+
"IEEE_NEXT_AFTER intrinsic folding: arguments are unordered"_warn_en_US);
479481
}
480-
return x;
482+
return x.NotANumber();
481483
case Relation::Equal:
482-
return x;
483-
case Relation::Less:
484-
upward = true;
485484
break;
485+
case Relation::Less:
486+
return x.NEAREST(!reverseCompare).value;
486487
case Relation::Greater:
487-
upward = false;
488-
break;
489-
}
490-
auto result{x.NEAREST(upward)};
491-
if (result.flags.test(RealFlag::Overflow) &&
492-
context.languageFeatures().ShouldWarn(
493-
common::UsageWarning::FoldingException)) {
494-
context.messages().Say(
495-
"IEEE_NEXT_AFTER intrinsic folding overflow"_warn_en_US);
488+
return x.NEAREST(reverseCompare).value;
496489
}
497-
return result.value;
490+
return x; // dodge bogus "missing return" GCC warning
498491
}));
499492
},
500493
yExpr->u);
@@ -508,12 +501,9 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
508501
auto result{x.NEAREST(upward)};
509502
if (context.languageFeatures().ShouldWarn(
510503
common::UsageWarning::FoldingException)) {
511-
if (result.flags.test(RealFlag::Overflow)) {
512-
context.messages().Say(
513-
"%s intrinsic folding overflow"_warn_en_US, iName);
514-
} else if (result.flags.test(RealFlag::InvalidArgument)) {
504+
if (result.flags.test(RealFlag::InvalidArgument)) {
515505
context.messages().Say(
516-
"%s intrinsic folding: bad argument"_warn_en_US, iName);
506+
"%s intrinsic folding: argument is NaN"_warn_en_US, iName);
517507
}
518508
}
519509
return result.value;

flang/lib/Evaluate/real.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -358,10 +358,15 @@ ValueWithRealFlags<Real<W, P>> Real<W, P>::NEAREST(bool upward) const {
358358
}
359359
}
360360
}
361-
result.flags = result.value.Normalize(isNegative, expo, nearest);
362-
} else if (IsInfinite() && upward == isNegative) {
363-
result.value = isNegative ? HUGE().Negate() : HUGE(); // largest mag finite
364-
} else {
361+
result.value.Normalize(isNegative, expo, nearest);
362+
} else if (IsInfinite()) {
363+
if (upward == isNegative) {
364+
result.value =
365+
isNegative ? HUGE().Negate() : HUGE(); // largest mag finite
366+
} else {
367+
result.value = *this;
368+
}
369+
} else { // NaN
365370
result.flags.set(RealFlag::InvalidArgument);
366371
result.value = *this;
367372
}
@@ -526,10 +531,16 @@ RealFlags Real<W, P>::Normalize(bool negative, int exponent,
526531
(rounding.mode == common::RoundingMode::Up && !negative) ||
527532
(rounding.mode == common::RoundingMode::Down && negative)) {
528533
word_ = Word{maxExponent}.SHIFTL(significandBits); // Inf
534+
if constexpr (!isImplicitMSB) {
535+
word_ = word_.IBSET(significandBits - 1);
536+
}
529537
} else {
530538
// directed rounding: round to largest finite value rather than infinity
531539
// (x86 does this, not sure whether it's standard behavior)
532-
word_ = Word{word_.MASKR(word_.bits - 1)}.IBCLR(significandBits);
540+
word_ = Word{word_.MASKR(word_.bits - 1)};
541+
if constexpr (isImplicitMSB) {
542+
word_ = word_.IBCLR(significandBits);
543+
}
533544
}
534545
if (negative) {
535546
word_ = word_.IBSET(bits - 1);

flang/test/Evaluate/fold-nearest.f90

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,8 @@ module m1
66
logical, parameter :: test_2 = nearest(minSubnormal, -1.) == 0
77
logical, parameter :: test_3 = nearest(1., 1.) == 1.0000001
88
logical, parameter :: test_4 = nearest(1.0000001, -1.) == 1
9-
!WARN: warning: NEAREST intrinsic folding overflow
109
real, parameter :: inf = nearest(huge(1.), 1.)
11-
!WARN: warning: NEAREST intrinsic folding: bad argument
1210
logical, parameter :: test_5 = nearest(inf, 1.) == inf
13-
!WARN: warning: NEAREST intrinsic folding: bad argument
1411
logical, parameter :: test_6 = nearest(-inf, -1.) == -inf
1512
logical, parameter :: test_7 = nearest(1.9999999, 1.) == 2.
1613
logical, parameter :: test_8 = nearest(2., -1.) == 1.9999999
@@ -59,10 +56,10 @@ module m2
5956
logical, parameter :: test_12 = ieee_next_after(1., 1.) == 1.
6057
!WARN: warning: invalid argument on division
6158
real, parameter :: nan = 0. / 0.
62-
!WARN: warning: IEEE_NEXT_AFTER intrinsic folding: bad argument
59+
!WARN: warning: IEEE_NEXT_AFTER intrinsic folding: arguments are unordered
6360
real, parameter :: x13 = ieee_next_after(nan, nan)
6461
logical, parameter :: test_13 = .not. (x13 == x13)
65-
!WARN: warning: IEEE_NEXT_AFTER intrinsic folding: bad argument
62+
!WARN: warning: IEEE_NEXT_AFTER intrinsic folding: arguments are unordered
6663
real, parameter :: x14 = ieee_next_after(nan, 0.)
6764
logical, parameter :: test_14 = .not. (x14 == x14)
6865
end module
@@ -77,24 +74,20 @@ module m3
7774
logical, parameter :: test_4 = ieee_next_down(1.0000000000000002d0) == 1.d0
7875
!WARN: warning: division by zero
7976
real(kind(0.d0)), parameter :: inf = 1.d0 / 0.d0
80-
!WARN: warning: IEEE_NEXT_UP intrinsic folding overflow
8177
logical, parameter :: test_5 = ieee_next_up(huge(0.d0)) == inf
82-
!WARN: warning: IEEE_NEXT_DOWN intrinsic folding overflow
8378
logical, parameter :: test_6 = ieee_next_down(-huge(0.d0)) == -inf
84-
!WARN: warning: IEEE_NEXT_UP intrinsic folding: bad argument
8579
logical, parameter :: test_7 = ieee_next_up(inf) == inf
8680
logical, parameter :: test_8 = ieee_next_down(inf) == h
8781
logical, parameter :: test_9 = ieee_next_up(-inf) == -h
88-
!WARN: warning: IEEE_NEXT_DOWN intrinsic folding: bad argument
8982
logical, parameter :: test_10 = ieee_next_down(-inf) == -inf
9083
logical, parameter :: test_11 = ieee_next_up(1.9999999999999997d0) == 2.d0
9184
logical, parameter :: test_12 = ieee_next_down(2.d0) == 1.9999999999999997d0
9285
!WARN: warning: invalid argument on division
9386
real(kind(0.d0)), parameter :: nan = 0.d0 / 0.d0
94-
!WARN: warning: IEEE_NEXT_UP intrinsic folding: bad argument
87+
!WARN: warning: IEEE_NEXT_UP intrinsic folding: argument is NaN
9588
real(kind(0.d0)), parameter :: x13 = ieee_next_up(nan)
9689
logical, parameter :: test_13 = .not. (x13 == x13)
97-
!WARN: warning: IEEE_NEXT_DOWN intrinsic folding: bad argument
90+
!WARN: warning: IEEE_NEXT_DOWN intrinsic folding: argument is NaN
9891
real(kind(0.d0)), parameter :: x14 = ieee_next_down(nan)
9992
logical, parameter :: test_14 = .not. (x14 == x14)
10093
end module

0 commit comments

Comments
 (0)