[flang-commits] [flang] 9489699 - [flang] Fold IS_CONTIGUOUS() to .FALSE. when it is known to be

Peter Klausler via flang-commits flang-commits at lists.llvm.org
Fri Sep 23 14:16:41 PDT 2022


Author: Peter Klausler
Date: 2022-09-23T14:16:26-07:00
New Revision: 94896994386d2a6a9e7bc310de83ee1491f194ef

URL: https://github.com/llvm/llvm-project/commit/94896994386d2a6a9e7bc310de83ee1491f194ef
DIFF: https://github.com/llvm/llvm-project/commit/94896994386d2a6a9e7bc310de83ee1491f194ef.diff

LOG: [flang] Fold IS_CONTIGUOUS() to .FALSE. when it is known to be

At present, IS_CONTIGUOUS() can only either fold to .TRUE. or
remain unknown.  The underlying analysis, however, is capable
of returning a tri-state result (true, false, or unknown).
Extend and expose it to folding so that IS_CONTIGUOUS() can
fold to .FALSE. as well as to .TRUE. when contiguity is
known.

Differential Revision: https://reviews.llvm.org/D134466

Added: 
    

Modified: 
    flang/include/flang/Evaluate/check-expression.h
    flang/include/flang/Evaluate/variable.h
    flang/lib/Evaluate/check-expression.cpp
    flang/lib/Evaluate/fold-logical.cpp
    flang/lib/Evaluate/variable.cpp
    flang/test/Semantics/assign03.f90

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Evaluate/check-expression.h b/flang/include/flang/Evaluate/check-expression.h
index 810a8c9c7f7d..31c8adacd5f3 100644
--- a/flang/include/flang/Evaluate/check-expression.h
+++ b/flang/include/flang/Evaluate/check-expression.h
@@ -95,10 +95,15 @@ extern template void CheckSpecificationExpr(
     const std::optional<Expr<SubscriptInteger>> &x, const semantics::Scope &,
     FoldingContext &);
 
-// Simple contiguity (9.5.4)
-template <typename A> bool IsSimplyContiguous(const A &, FoldingContext &);
-extern template bool IsSimplyContiguous(
+// Contiguity & "simple contiguity" (9.5.4)
+template <typename A>
+std::optional<bool> IsContiguous(const A &, FoldingContext &);
+extern template std::optional<bool> IsContiguous(
     const Expr<SomeType> &, FoldingContext &);
+template <typename A>
+bool IsSimplyContiguous(const A &x, FoldingContext &context) {
+  return IsContiguous(x, context).value_or(false);
+}
 
 template <typename A> bool IsErrorExpr(const A &);
 extern template bool IsErrorExpr(const Expr<SomeType> &);

diff  --git a/flang/include/flang/Evaluate/variable.h b/flang/include/flang/Evaluate/variable.h
index dac51edf2f42..b353050b781b 100644
--- a/flang/include/flang/Evaluate/variable.h
+++ b/flang/include/flang/Evaluate/variable.h
@@ -174,7 +174,7 @@ class Triplet {
   Triplet &set_stride(Expr<SubscriptInteger> &&);
 
   bool operator==(const Triplet &) const;
-  bool IsStrideOne() const;
+  std::optional<bool> IsStrideOne() const;
   llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
 
 private:

diff  --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index 814785c79365..4d5fd0ba9fab 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -690,14 +690,13 @@ template void CheckSpecificationExpr(
     const std::optional<Expr<SubscriptInteger>> &, const semantics::Scope &,
     FoldingContext &);
 
-// IsSimplyContiguous() -- 9.5.4
-class IsSimplyContiguousHelper
-    : public AnyTraverse<IsSimplyContiguousHelper, std::optional<bool>> {
+// IsContiguous() -- 9.5.4
+class IsContiguousHelper
+    : public AnyTraverse<IsContiguousHelper, std::optional<bool>> {
 public:
   using Result = std::optional<bool>; // tri-state
-  using Base = AnyTraverse<IsSimplyContiguousHelper, Result>;
-  explicit IsSimplyContiguousHelper(FoldingContext &c)
-      : Base{*this}, context_{c} {}
+  using Base = AnyTraverse<IsContiguousHelper, Result>;
+  explicit IsContiguousHelper(FoldingContext &c) : Base{*this}, context_{c} {}
   using Base::operator();
 
   Result operator()(const semantics::Symbol &symbol) const {
@@ -711,43 +710,48 @@ class IsSimplyContiguousHelper
       return true;
     } else if (semantics::IsPointer(ultimate) ||
         semantics::IsAssumedShape(ultimate)) {
-      return false;
+      return std::nullopt;
     } else if (const auto *details{
                    ultimate.detailsIf<semantics::ObjectEntityDetails>()}) {
       return !details->IsAssumedRank();
-    } else if (auto assoc{Base::operator()(ultimate)}) {
-      return assoc;
     } else {
-      return false;
+      return Base::operator()(ultimate);
     }
   }
 
   Result operator()(const ArrayRef &x) const {
-    const auto &symbol{x.GetLastSymbol()};
-    if (!(*this)(symbol).has_value()) {
-      return false;
-    } else if (auto rank{CheckSubscripts(x.subscript())}) {
-      if (x.Rank() == 0) {
-        return true;
-      } else if (*rank > 0) {
-        // a(1)%b(:,:) is contiguous if an only if a(1)%b is contiguous.
-        return (*this)(x.base());
-      } else {
-        // a(:)%b(1,1) is not contiguous.
-        return false;
-      }
+    if (x.Rank() == 0) {
+      return true; // scalars considered contiguous
+    }
+    int subscriptRank{0};
+    auto subscripts{CheckSubscripts(x.subscript(), subscriptRank)};
+    if (!subscripts.value_or(false)) {
+      return subscripts; // subscripts not known to be contiguous
+    } else if (subscriptRank > 0) {
+      // a(1)%b(:,:) is contiguous if and only if a(1)%b is contiguous.
+      return (*this)(x.base());
     } else {
-      return false;
+      // a(:)%b(1,1) is (probably) not contiguous.
+      return std::nullopt;
     }
   }
   Result operator()(const CoarrayRef &x) const {
-    return CheckSubscripts(x.subscript()).has_value();
+    int rank{0};
+    return CheckSubscripts(x.subscript(), rank).has_value();
   }
   Result operator()(const Component &x) const {
-    return x.base().Rank() == 0 && (*this)(x.GetLastSymbol()).value_or(false);
+    if (x.base().Rank() == 0) {
+      return (*this)(x.GetLastSymbol());
+    } else {
+      // TODO could be true if base contiguous and this is only component, or
+      // if base has only one element?
+      return std::nullopt;
+    }
   }
-  Result operator()(const ComplexPart &) const { return false; }
-  Result operator()(const Substring &) const { return false; }
+  Result operator()(const ComplexPart &x) const {
+    return x.complex().Rank() == 0;
+  }
+  Result operator()(const Substring &) const { return std::nullopt; }
 
   Result operator()(const ProcedureRef &x) const {
     if (auto chars{
@@ -760,23 +764,25 @@ class IsSimplyContiguousHelper
                 characteristics::FunctionResult::Attr::Contiguous);
       }
     }
-    return false;
+    return std::nullopt;
   }
 
+  Result operator()(const NullPointer &) const { return true; }
+
 private:
-  // If the subscripts can possibly be on a simply-contiguous array reference,
-  // return the rank.
-  static std::optional<int> CheckSubscripts(
-      const std::vector<Subscript> &subscript) {
+  static std::optional<bool> CheckSubscripts(
+      const std::vector<Subscript> &subscript, int &rank) {
     bool anyTriplet{false};
-    int rank{0};
+    rank = 0;
     for (auto j{subscript.size()}; j-- > 0;) {
       if (const auto *triplet{std::get_if<Triplet>(&subscript[j].u)}) {
-        if (!triplet->IsStrideOne()) {
-          return std::nullopt;
+        auto isStride1{triplet->IsStrideOne()};
+        if (!isStride1.value_or(false)) {
+          return isStride1;
         } else if (anyTriplet) {
           if (triplet->lower() || triplet->upper()) {
-            // all triplets before the last one must be just ":"
+            // all triplets before the last one must be just ":" for
+            // simple contiguity
             return std::nullopt;
           }
         } else {
@@ -784,26 +790,26 @@ class IsSimplyContiguousHelper
         }
         ++rank;
       } else if (anyTriplet || subscript[j].Rank() > 0) {
-        return std::nullopt;
+        return false;
       }
     }
-    return rank;
+    return true;
   }
 
   FoldingContext &context_;
 };
 
 template <typename A>
-bool IsSimplyContiguous(const A &x, FoldingContext &context) {
+std::optional<bool> IsContiguous(const A &x, FoldingContext &context) {
   if (IsVariable(x)) {
-    auto known{IsSimplyContiguousHelper{context}(x)};
-    return known && *known;
+    return IsContiguousHelper{context}(x);
   } else {
     return true; // not a variable
   }
 }
 
-template bool IsSimplyContiguous(const Expr<SomeType> &, FoldingContext &);
+template std::optional<bool> IsContiguous(
+    const Expr<SomeType> &, FoldingContext &);
 
 // IsErrorExpr()
 struct IsErrorExprHelper : public AnyTraverse<IsErrorExprHelper, bool> {

diff  --git a/flang/lib/Evaluate/fold-logical.cpp b/flang/lib/Evaluate/fold-logical.cpp
index 052fe62bbd5d..8adf632c4c56 100644
--- a/flang/lib/Evaluate/fold-logical.cpp
+++ b/flang/lib/Evaluate/fold-logical.cpp
@@ -180,8 +180,8 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
   } else if (name == "is_contiguous") {
     if (args.at(0)) {
       if (auto *expr{args[0]->UnwrapExpr()}) {
-        if (IsSimplyContiguous(*expr, context)) {
-          return Expr<T>{true};
+        if (auto contiguous{IsContiguous(*expr, context)}) {
+          return Expr<T>{*contiguous};
         }
       }
     }

diff  --git a/flang/lib/Evaluate/variable.cpp b/flang/lib/Evaluate/variable.cpp
index 2de0de668d85..6ddc495a3f2c 100644
--- a/flang/lib/Evaluate/variable.cpp
+++ b/flang/lib/Evaluate/variable.cpp
@@ -69,11 +69,11 @@ Triplet &Triplet::set_stride(Expr<SubscriptInteger> &&expr) {
   return *this;
 }
 
-bool Triplet::IsStrideOne() const {
+std::optional<bool> Triplet::IsStrideOne() const {
   if (auto stride{ToInt64(stride_.value())}) {
     return stride == 1;
   } else {
-    return false;
+    return std::nullopt;
   }
 }
 

diff  --git a/flang/test/Semantics/assign03.f90 b/flang/test/Semantics/assign03.f90
index 46de668a706a..3e4868157721 100644
--- a/flang/test/Semantics/assign03.f90
+++ b/flang/test/Semantics/assign03.f90
@@ -269,10 +269,10 @@ subroutine s11
   end
 
   ! Check is_contiguous, which is usually the same as when pointer bounds
-  ! remapping is used. If it's not simply contiguous it's not constant so
-  ! an error is reported.
+  ! remapping is used.
   subroutine s12
     integer, pointer :: p(:)
+    integer, pointer, contiguous :: pc(:)
     type :: t
       integer :: a(4, 4)
       integer :: b
@@ -280,16 +280,20 @@ subroutine s12
     type(t), target :: x
     type(t), target :: y(10,10)
     integer :: v(10)
-    logical, parameter :: l1 = is_contiguous(x%a(:,:))
-    logical, parameter :: l2 = is_contiguous(y(1,1)%a(1,1))
+    logical(kind=merge(1,-1,is_contiguous(x%a(:,:)))) :: l1 ! known true
+    logical(kind=merge(1,-1,is_contiguous(y(1,1)%a(1,1)))) :: l2 ! known true
     !ERROR: Must be a constant value
-    logical, parameter :: l3 = is_contiguous(y(:,1)%a(1,1))
+    logical(kind=merge(-1,-2,is_contiguous(y(:,1)%a(1,1)))) :: l3 ! unknown
     !ERROR: Must be a constant value
-    logical, parameter :: l4 = is_contiguous(x%a(:,v))
+    logical(kind=merge(-1,-2,is_contiguous(y(:,1)%a(1,1)))) :: l4 ! unknown
+    logical(kind=merge(-1,1,is_contiguous(x%a(:,v)))) :: l5 ! known false
     !ERROR: Must be a constant value
-    logical, parameter :: l5 = is_contiguous(y(v,1)%a(1,1))
+    logical(kind=merge(-1,-2,is_contiguous(y(v,1)%a(1,1)))) :: l6 ! unknown
     !ERROR: Must be a constant value
-    logical, parameter :: l6 = is_contiguous(p(:))
+    logical(kind=merge(-1,-2,is_contiguous(p(:)))) :: l7 ! unknown
+    logical(kind=merge(1,-1,is_contiguous(pc(:)))) :: l8 ! known true
+    logical(kind=merge(-1,1,is_contiguous(pc(1:10:2)))) :: l9 ! known false
+    logical(kind=merge(-1,1,is_contiguous(pc(10:1:-1)))) :: l10 ! known false
   end
   subroutine test3(b)
     integer, intent(inout) :: b(..)


        


More information about the flang-commits mailing list