[flang] Implement MIN and MAX folding

* Use Extremum<T> FoldOperation to fold MIN and MAX
* Fix Extremum<T> FolOperation
    * For character, the length is the one of the longest argument.
      Define and use `CharacterUtils<Kind>::Resize` helper to do this.
    * For array of all types, Extremum<T> with Ordering::Less was
      behaving like Ordering::Greater. This is because the default
      `ApplyElementwise` for `Operation` was selected and it then
       called the Extremum<T> constructor without the ordering
       argument (which was an optional defaulted to Greater).
      Define a specific handler for Extremum<T> and make the ordering
      argument mandatory to prevent this kind of bug to pass
      f18 compilation in the futur.
* Fix intrinsic.cc for MIN and MAX
    * When provided with two arguments, `Match` was adding an empty
      3rd optional actual argument. Later code working on min and
      max was not expecting this and failing. The fix prevent this
      empty argument to be created by changing the initial size of
      `actualForDummy` to actually be the number of dummies that do
      not have `Optionality::Repeats`

This commit fixes issue flang-compiler/f18#677.

Original-commit: flang-compiler/f18@acb62f240b
Reviewed-on: https://github.com/flang-compiler/f18/pull/803
This commit is contained in:
Jean Perier 2019-10-31 08:03:16 -07:00
parent f991c76521
commit 572de7c7b0
8 changed files with 103 additions and 15 deletions

View File

@ -70,6 +70,17 @@ public:
return str;
}
// Resize adds spaces on the right if the new size is bigger than the
// original, or by trimming the rightmost characters otherwise.
static Character Resize(const Character &str, std::size_t newLength) {
auto oldLength{str.length()};
if (newLength > oldLength) {
return str + Character(newLength - oldLength, Space());
} else {
return str.substr(0, newLength);
}
}
private:
// Following helpers assume that character encodings contain ASCII
static constexpr CharT Space() { return 0x20; }

View File

@ -53,7 +53,7 @@ Expr<Type<TypeCategory::Character, KIND>>::LEN() const {
if (auto llen{c.left().LEN()}) {
if (auto rlen{c.right().LEN()}) {
return Expr<SubscriptInteger>{Extremum<SubscriptInteger>{
*std::move(llen), *std::move(rlen)}};
Ordering::Greater, *std::move(llen), *std::move(rlen)}};
}
}
return std::nullopt;

View File

@ -347,11 +347,9 @@ template<typename A> struct Extremum : public Operation<Extremum<A>, A, A, A> {
using Operand = A;
using Base = Operation<Extremum, A, A, A>;
CLASS_BOILERPLATE(Extremum)
Extremum(const Expr<Operand> &x, const Expr<Operand> &y,
Ordering ord = Ordering::Greater)
Extremum(Ordering ord, const Expr<Operand> &x, const Expr<Operand> &y)
: Base{x, y}, ordering{ord} {}
Extremum(
Expr<Operand> &&x, Expr<Operand> &&y, Ordering ord = Ordering::Greater)
Extremum(Ordering ord, Expr<Operand> &&x, Expr<Operand> &&y)
: Base{std::move(x), std::move(y)}, ordering{ord} {}
const char *Prefix() const {

View File

@ -526,6 +526,26 @@ Expr<Type<TypeCategory::Integer, KIND>> UBOUND(FoldingContext &context,
return Expr<T>{std::move(funcRef)};
}
template<typename T>
Expr<T> FoldMINorMAX(
FoldingContext &context, FunctionRef<T> &&funcRef, Ordering order) {
std::vector<Constant<T> *> constantArgs;
for (auto &arg : funcRef.arguments()) {
if (auto *cst{FoldConvertedArg<T>(context, arg)}) {
constantArgs.push_back(cst);
} else {
return Expr<T>(std::move(funcRef));
}
}
CHECK(constantArgs.size() > 0);
Expr<T> result{std::move(*constantArgs[0])};
for (std::size_t i{1}; i < constantArgs.size(); ++i) {
Extremum<T> extremum{order, result, Expr<T>{std::move(*constantArgs[i])}};
result = FoldOperation(context, std::move(extremum));
}
return result;
}
template<typename T>
Expr<T> FoldOperation(FoldingContext &context, FunctionRef<T> &&funcRef) {
ActualArguments &args{funcRef.arguments()};
@ -751,6 +771,8 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
ScalarFunc<T, Int4>([&fptr](const Scalar<Int4> &places) -> Scalar<T> {
return fptr(static_cast<int>(places.ToInt64()));
}));
} else if (name == "max") {
return FoldMINorMAX(context, std::move(funcRef), Ordering::Greater);
} else if (name == "maxexponent") {
if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
return std::visit(
@ -772,6 +794,8 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
},
sx->u);
}
} else if (name == "min") {
return FoldMINorMAX(context, std::move(funcRef), Ordering::Less);
} else if (name == "precision") {
if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
return Expr<T>{std::visit(
@ -1013,6 +1037,10 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
return Expr<T>{Scalar<T>::EPSILON()};
} else if (name == "huge") {
return Expr<T>{Scalar<T>::HUGE()};
} else if (name == "max") {
return FoldMINorMAX(context, std::move(funcRef), Ordering::Greater);
} else if (name == "min") {
return FoldMINorMAX(context, std::move(funcRef), Ordering::Less);
} else if (name == "real") {
if (auto *expr{args[0].value().UnwrapExpr()}) {
return ToReal<KIND>(context, std::move(*expr));
@ -1174,6 +1202,10 @@ Expr<Type<TypeCategory::Character, KIND>> FoldIntrinsicFunction(
} else if (name == "adjustr") {
return FoldElementalIntrinsic<T, T>(
context, std::move(funcRef), CharacterUtils<KIND>::ADJUSTR);
} else if (name == "max") {
return FoldMINorMAX(context, std::move(funcRef), Ordering::Greater);
} else if (name == "min") {
return FoldMINorMAX(context, std::move(funcRef), Ordering::Less);
} else if (name == "new_line") {
return Expr<T>{Constant<T>{CharacterUtils<KIND>::NEW_LINE()}};
}
@ -2330,7 +2362,11 @@ Expr<T> FoldOperation(FoldingContext &context, RealToIntPower<T> &&x) {
template<typename T>
Expr<T> FoldOperation(FoldingContext &context, Extremum<T> &&x) {
if (auto array{ApplyElementwise(context, x)}) {
if (auto array{ApplyElementwise(context, x,
std::function<Expr<T>(Expr<T> &&, Expr<T> &&)>{[=](Expr<T> &&l,
Expr<T> &&r) {
return Expr<T>{Extremum<T>{x.ordering, std::move(l), std::move(r)}};
}})}) {
return *array;
}
if (auto folded{OperandsAreConstants(x)}) {
@ -2345,9 +2381,16 @@ Expr<T> FoldOperation(FoldingContext &context, Extremum<T> &&x) {
return Expr<T>{Constant<T>{folded->first}};
}
} else {
if (x.ordering == Compare(folded->first, folded->second)) {
return Expr<T>{Constant<T>{folded->first}};
}
static_assert(T::category == TypeCategory::Character);
// Result of MIN and MAX on character has the length of
// the longest argument.
auto maxLen{std::max(folded->first.length(), folded->second.length())};
bool isFirst{x.ordering == Compare(folded->first, folded->second)};
auto res{isFirst ? std::move(folded->first) : std::move(folded->second)};
res = res.length() == maxLen
? std::move(res)
: CharacterUtils<T::kind>::Resize(res, maxLen);
return Expr<T>{Constant<T>{std::move(res)}};
}
return Expr<T>{Constant<T>{folded->second}};
}

View File

@ -976,7 +976,6 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
dummy[dummyArgPatterns].keyword != nullptr;
++dummyArgPatterns) {
}
std::vector<ActualArgument *> actualForDummy(dummyArgPatterns, nullptr);
// MAX and MIN (and others that map to them) allow their last argument to
// be repeated indefinitely. The actualForDummy vector is sized
// and null-initialized to the non-repeated dummy argument count,
@ -984,6 +983,9 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
// when this flag is set.
bool repeatLastDummy{dummyArgPatterns > 0 &&
dummy[dummyArgPatterns - 1].optionality == Optionality::repeats};
std::size_t nonRepeatedDummies{
repeatLastDummy ? dummyArgPatterns - 1 : dummyArgPatterns};
std::vector<ActualArgument *> actualForDummy(nonRepeatedDummies, nullptr);
int missingActualArguments{0};
for (std::optional<ActualArgument> &arg : arguments) {
if (!arg.has_value()) {
@ -997,7 +999,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
}
bool found{false};
int slot{missingActualArguments};
for (std::size_t j{0}; j < dummyArgPatterns && !found; ++j) {
for (std::size_t j{0}; j < nonRepeatedDummies && !found; ++j) {
if (arg->keyword.has_value()) {
found = *arg->keyword == dummy[j].keyword;
if (found) {

View File

@ -137,7 +137,7 @@ static ExtentExpr ComputeTripCount(FoldingContext &context, ExtentExpr &&lower,
(std::move(upper) - std::move(lower) + std::move(strideCopy)) /
std::move(stride)};
ExtentExpr extent{
Extremum<ExtentType>{std::move(span), ExtentExpr{0}, Ordering::Greater}};
Extremum<ExtentType>{Ordering::Greater, std::move(span), ExtentExpr{0}}};
return Fold(context, std::move(extent));
}

View File

@ -310,9 +310,9 @@ std::optional<Expr<SubscriptInteger>> DataRef::LEN() const {
std::optional<Expr<SubscriptInteger>> Substring::LEN() const {
if (auto top{upper()}) {
return AsExpr(
Extremum<SubscriptInteger>{AsExpr(Constant<SubscriptInteger>{0}),
*std::move(top) - lower() + AsExpr(Constant<SubscriptInteger>{1})});
return AsExpr(Extremum<SubscriptInteger>{Ordering::Greater,
AsExpr(Constant<SubscriptInteger>{0}),
*std::move(top) - lower() + AsExpr(Constant<SubscriptInteger>{1})});
} else {
return std::nullopt;
}

View File

@ -100,4 +100,38 @@ module m
logical, parameter :: test_pow3 = (2**4).EQ.(16)
logical, parameter :: test_pow4 = (7**5).EQ.(16807)
! test MIN and MAX
real, parameter :: x1 = -35., x2= -35.05, x3=0., x4=35.05, x5=35.
real, parameter :: res_max_r = max(x1, x2, x3, x4, x5)
real, parameter :: res_min_r = min(x1, x2, x3, x4, x5)
logical, parameter :: test_max_r = res_max_r.EQ.x4
logical, parameter :: test_min_r = res_min_r.EQ.x2
logical, parameter :: test_min_i = min(-3, 3).EQ.-3
logical, parameter :: test_max_i = max(-3, 3).EQ.3
integer, parameter :: i1 = 35, i2= 36, i3=0, i4=-35, i5=-36
integer, parameter :: res_max_i = max(i1, i2, i3, i4, i5)
integer, parameter :: res_min_i = min(i1, i2, i3, i4, i5)
logical, parameter :: test_max_i2 = res_max_i.EQ.i2
logical, parameter :: test_min_i2 = res_min_i.EQ.i5
character(*), parameter :: c1 = "elephant", c2="elevator"
character(*), parameter :: c3 = "excalibur", c4="z", c5="epsilon"
character(*), parameter :: res_max_c = max(c1, c2, c3, c4, c5)
character(*), parameter :: res_min_c = min(c1, c2, c3, c4, c5)
! length of result is length of longest arguments!
character(len(c3)), parameter :: exp_min = c1
character(len(c3)), parameter :: exp_max = c4
logical, parameter :: test_max_c_1 = res_max_c.EQ.exp_max
logical, parameter :: test_max_c_2 = res_max_c.NE.c4
logical, parameter :: test_max_c_3 = len(res_max_c).EQ.len(c3)
logical, parameter :: test_min_c_1 = res_min_c.NE.c1
logical, parameter :: test_min_c_2 = res_min_c.EQ.exp_min
logical, parameter :: test_min_c_3 = len(res_min_c).EQ.len(c3)
integer, parameter :: x1a(*) = [1, 12, 3, 14]
integer, parameter :: x2a(*) = [11, 2, 13, 4]
logical, parameter :: test_max_a1 = all(max(x1a, x2a).EQ.[11, 12, 13, 14])
logical, parameter :: test_min_a1 = all(min(x1a, x2a).EQ.[1, 2, 3, 4])
end module