[flang] Fold LGE/LGT/LLE/LLT intrinsic functions
Fold the legacy intrinsic functions LGE, LGT, LLE, & LLT
by rewriting them into character relational expressions and
then folding those. Also fix folding of comparisons of
character values of distinct lengths: the shorter value must
be padded with blanks. (This fix exposed some bad test cases,
which are also fixed.)
Differential Revision: https://reviews.llvm.org/D111843
GitOrigin-RevId: 6965a776ee192cb4c1a2618c270254fbf70879df
diff --git a/include/flang/Evaluate/common.h b/include/flang/Evaluate/common.h
index dd7cb96..e0e5cac 100644
--- a/include/flang/Evaluate/common.h
+++ b/include/flang/Evaluate/common.h
@@ -19,6 +19,7 @@
#include "flang/Parser/message.h"
#include <cinttypes>
#include <map>
+#include <string>
namespace Fortran::semantics {
class DerivedTypeSpec;
@@ -45,6 +46,26 @@
}
}
+template <typename CH>
+static constexpr Ordering Compare(
+ const std::basic_string<CH> &x, const std::basic_string<CH> &y) {
+ std::size_t xLen{x.size()}, yLen{y.size()};
+ using String = std::basic_string<CH>;
+ // Fortran CHARACTER comparison is defined with blank padding
+ // to extend a shorter operand.
+ if (xLen < yLen) {
+ return Compare(String{x}.append(yLen - xLen, CH{' '}), y);
+ } else if (xLen > yLen) {
+ return Compare(x, String{y}.append(xLen - yLen, CH{' '}));
+ } else if (x < y) {
+ return Ordering::Less;
+ } else if (x > y) {
+ return Ordering::Greater;
+ } else {
+ return Ordering::Equal;
+ }
+}
+
static constexpr Ordering Reverse(Ordering ordering) {
if (ordering == Ordering::Less) {
return Ordering::Greater;
diff --git a/include/flang/Evaluate/type.h b/include/flang/Evaluate/type.h
index 23fc6ca..dcc0527 100644
--- a/include/flang/Evaluate/type.h
+++ b/include/flang/Evaluate/type.h
@@ -52,6 +52,7 @@
using CInteger = Type<TypeCategory::Integer, 4>;
using LogicalResult = Type<TypeCategory::Logical, 4>;
using LargestReal = Type<TypeCategory::Real, 16>;
+using Ascii = Type<TypeCategory::Character, 1>;
// A predicate that is true when a kind value is a kind that could possibly
// be supported for an intrinsic type category on some target instruction
diff --git a/lib/Evaluate/fold-logical.cpp b/lib/Evaluate/fold-logical.cpp
index 1e11fec..586909d 100644
--- a/lib/Evaluate/fold-logical.cpp
+++ b/lib/Evaluate/fold-logical.cpp
@@ -106,6 +106,20 @@
}
}
}
+ } else if (name == "lge" || name == "lgt" || name == "lle" || name == "llt") {
+ // Rewrite LGE/LGT/LLE/LLT into ASCII character relations
+ auto *cx0{UnwrapExpr<Expr<SomeCharacter>>(args[0])};
+ auto *cx1{UnwrapExpr<Expr<SomeCharacter>>(args[1])};
+ if (cx0 && cx1) {
+ return Fold(context,
+ ConvertToType<T>(
+ PackageRelation(name == "lge" ? RelationalOperator::GE
+ : name == "lgt" ? RelationalOperator::GT
+ : name == "lle" ? RelationalOperator::LE
+ : RelationalOperator::LT,
+ ConvertToType<Ascii>(std::move(*cx0)),
+ ConvertToType<Ascii>(std::move(*cx1)))));
+ }
} else if (name == "logical") {
if (auto *expr{UnwrapExpr<Expr<SomeLogical>>(args[0])}) {
return Fold(context, ConvertToType<T>(std::move(*expr)));
@@ -126,7 +140,7 @@
return Expr<T>{true};
}
// TODO: btest, dot_product, is_iostat_end,
- // is_iostat_eor, lge, lgt, lle, llt, logical, matmul, out_of_range,
+ // is_iostat_eor, logical, matmul, out_of_range,
// parity, transfer
return Expr<T>{std::move(funcRef)};
}
diff --git a/lib/Semantics/check-io.h b/lib/Semantics/check-io.h
index 01bbcd9..44c01e8 100644
--- a/lib/Semantics/check-io.h
+++ b/lib/Semantics/check-io.h
@@ -86,8 +86,7 @@
StatusReplace, StatusScratch, DataList)
template <typename R, typename T> std::optional<R> GetConstExpr(const T &x) {
- using DefaultCharConstantType =
- evaluate::Type<common::TypeCategory::Character, 1>;
+ using DefaultCharConstantType = evaluate::Ascii;
if (const SomeExpr * expr{GetExpr(x)}) {
const auto foldExpr{
evaluate::Fold(context_.foldingContext(), common::Clone(*expr))};
diff --git a/lib/Semantics/data-to-inits.cpp b/lib/Semantics/data-to-inits.cpp
index 8fba39f..958184c 100644
--- a/lib/Semantics/data-to-inits.cpp
+++ b/lib/Semantics/data-to-inits.cpp
@@ -234,8 +234,8 @@
if (auto converted{evaluate::ConvertToType(type, SomeExpr{expr})}) {
return {std::make_pair(std::move(*converted), false)};
}
- if (std::optional<std::string> chValue{evaluate::GetScalarConstantValue<
- evaluate::Type<TypeCategory::Character, 1>>(expr)}) {
+ if (std::optional<std::string> chValue{
+ evaluate::GetScalarConstantValue<evaluate::Ascii>(expr)}) {
// Allow DATA initialization with Hollerith and kind=1 CHARACTER like
// (most) other Fortran compilers do. Pad on the right with spaces
// when short, truncate the right if long.
diff --git a/lib/Semantics/resolve-names.cpp b/lib/Semantics/resolve-names.cpp
index 21f0b4f..8ad0f5a 100644
--- a/lib/Semantics/resolve-names.cpp
+++ b/lib/Semantics/resolve-names.cpp
@@ -1576,8 +1576,8 @@
if (!attrs_ || !attrs_->test(Attr::BIND_C)) {
return;
}
- std::optional<std::string> label{evaluate::GetScalarConstantValue<
- evaluate::Type<TypeCategory::Character, 1>>(bindName_)};
+ std::optional<std::string> label{
+ evaluate::GetScalarConstantValue<evaluate::Ascii>(bindName_)};
// 18.9.2(2): discard leading and trailing blanks, ignore if all blank
if (label) {
auto first{label->find_first_not_of(" ")};
diff --git a/lib/Semantics/runtime-type-info.cpp b/lib/Semantics/runtime-type-info.cpp
index ddda7d1..bc6a889 100644
--- a/lib/Semantics/runtime-type-info.cpp
+++ b/lib/Semantics/runtime-type-info.cpp
@@ -637,7 +637,7 @@
object.set_type(scope.MakeCharacterType(
ParamValue{len, common::TypeParamAttr::Len}, KindExpr{1}));
}
- using Ascii = evaluate::Type<TypeCategory::Character, 1>;
+ using evaluate::Ascii;
using AsciiExpr = evaluate::Expr<Ascii>;
object.set_init(evaluate::AsGenericExpr(AsciiExpr{name}));
Symbol &symbol{*scope
diff --git a/test/Evaluate/fold-char-cmp.f90 b/test/Evaluate/fold-char-cmp.f90
new file mode 100644
index 0000000..6fe5c70
--- /dev/null
+++ b/test/Evaluate/fold-char-cmp.f90
@@ -0,0 +1,17 @@
+! RUN: %python %S/test_folding.py %s %flang_fc1
+! Tests folding of character comparisons
+module m1
+ logical, parameter :: cases(*) = &
+ [ "" == "", "" == " " &
+ , "aaa" == "aaa", "aaa" == "aaa ", "aaa" /= "aab" &
+ , "aaa" <= "aaa", .not. "aaa" < "aaa", "aaa" < "aab", "aaa" >= "aaa" &
+ , .not. "aaa" > "aaa", .not. "aaa" >= "aab" &
+ , 4_"aaa" == 4_"aaa", 4_"aaa" == 4_"aaa ", 4_"aaa" /= 4_"aab" &
+ , 4_"aaa" <= 4_"aaa", .not. 4_"aaa" < 4_"aaa", 4_"aaa" < 4_"aab", 4_"aaa" >= 4_"aaa" &
+ , .not. 4_"aaa" > 4_"aaa", .not. 4_"aaa" >= 4_"aab" &
+ , lle("aaa", "aaa"), .not. llt("aaa", "aaa"), llt("aaa", "aab"), lge("aaa", "aaa") &
+ , .not. lgt("aaa", "aaa"), .not. lge("aaa", "aab") &
+ , lle("", ""), .not. llt("", ""), lge("", ""), .not. lgt("", "") &
+ ]
+ logical, parameter :: test_cases = all(cases)
+end module
diff --git a/test/Evaluate/folding01.f90 b/test/Evaluate/folding01.f90
index cb7a9eb..cc3b17e 100644
--- a/test/Evaluate/folding01.f90
+++ b/test/Evaluate/folding01.f90
@@ -123,9 +123,7 @@
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)
@@ -137,5 +135,5 @@
logical, parameter :: test_not_zero = not(0).EQ.-1
logical, parameter :: test_not_neg_one = not(-1).EQ.0
logical, parameter :: test_not_array = all(not([5, 6, 7]).EQ.[-6, -7, -8])
-
+
end module
diff --git a/test/Evaluate/folding05.f90 b/test/Evaluate/folding05.f90
index 22c0290..4ace088 100644
--- a/test/Evaluate/folding05.f90
+++ b/test/Evaluate/folding05.f90
Binary files differ