forked from OSchip/llvm-project
[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:
parent
f991c76521
commit
572de7c7b0
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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}};
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue