From 94896994386d2a6a9e7bc310de83ee1491f194ef Mon Sep 17 00:00:00 2001 From: Peter Klausler Date: Wed, 21 Sep 2022 12:18:15 -0700 Subject: [PATCH] [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 --- flang/include/flang/Evaluate/check-expression.h | 11 ++- flang/include/flang/Evaluate/variable.h | 2 +- flang/lib/Evaluate/check-expression.cpp | 92 +++++++++++++------------ flang/lib/Evaluate/fold-logical.cpp | 4 +- flang/lib/Evaluate/variable.cpp | 4 +- flang/test/Semantics/assign03.f90 | 20 +++--- 6 files changed, 74 insertions(+), 59 deletions(-) diff --git a/flang/include/flang/Evaluate/check-expression.h b/flang/include/flang/Evaluate/check-expression.h index 810a8c9..31c8ada 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> &x, const semantics::Scope &, FoldingContext &); -// Simple contiguity (9.5.4) -template bool IsSimplyContiguous(const A &, FoldingContext &); -extern template bool IsSimplyContiguous( +// Contiguity & "simple contiguity" (9.5.4) +template +std::optional IsContiguous(const A &, FoldingContext &); +extern template std::optional IsContiguous( const Expr &, FoldingContext &); +template +bool IsSimplyContiguous(const A &x, FoldingContext &context) { + return IsContiguous(x, context).value_or(false); +} template bool IsErrorExpr(const A &); extern template bool IsErrorExpr(const Expr &); diff --git a/flang/include/flang/Evaluate/variable.h b/flang/include/flang/Evaluate/variable.h index dac51ed..b353050 100644 --- a/flang/include/flang/Evaluate/variable.h +++ b/flang/include/flang/Evaluate/variable.h @@ -174,7 +174,7 @@ public: Triplet &set_stride(Expr &&); bool operator==(const Triplet &) const; - bool IsStrideOne() const; + std::optional 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 814785c..4d5fd0b 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> &, const semantics::Scope &, FoldingContext &); -// IsSimplyContiguous() -- 9.5.4 -class IsSimplyContiguousHelper - : public AnyTraverse> { +// IsContiguous() -- 9.5.4 +class IsContiguousHelper + : public AnyTraverse> { public: using Result = std::optional; // tri-state - using Base = AnyTraverse; - explicit IsSimplyContiguousHelper(FoldingContext &c) - : Base{*this}, context_{c} {} + using Base = AnyTraverse; + explicit IsContiguousHelper(FoldingContext &c) : Base{*this}, context_{c} {} using Base::operator(); Result operator()(const semantics::Symbol &symbol) const { @@ -711,43 +710,48 @@ public: return true; } else if (semantics::IsPointer(ultimate) || semantics::IsAssumedShape(ultimate)) { - return false; + return std::nullopt; } else if (const auto *details{ ultimate.detailsIf()}) { 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 @@ public: 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 CheckSubscripts( - const std::vector &subscript) { + static std::optional CheckSubscripts( + const std::vector &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(&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 @@ private: } ++rank; } else if (anyTriplet || subscript[j].Rank() > 0) { - return std::nullopt; + return false; } } - return rank; + return true; } FoldingContext &context_; }; template -bool IsSimplyContiguous(const A &x, FoldingContext &context) { +std::optional 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 &, FoldingContext &); +template std::optional IsContiguous( + const Expr &, FoldingContext &); // IsErrorExpr() struct IsErrorExprHelper : public AnyTraverse { diff --git a/flang/lib/Evaluate/fold-logical.cpp b/flang/lib/Evaluate/fold-logical.cpp index 052fe62..8adf632 100644 --- a/flang/lib/Evaluate/fold-logical.cpp +++ b/flang/lib/Evaluate/fold-logical.cpp @@ -180,8 +180,8 @@ Expr> FoldIntrinsicFunction( } else if (name == "is_contiguous") { if (args.at(0)) { if (auto *expr{args[0]->UnwrapExpr()}) { - if (IsSimplyContiguous(*expr, context)) { - return Expr{true}; + if (auto contiguous{IsContiguous(*expr, context)}) { + return Expr{*contiguous}; } } } diff --git a/flang/lib/Evaluate/variable.cpp b/flang/lib/Evaluate/variable.cpp index 2de0de6..6ddc495 100644 --- a/flang/lib/Evaluate/variable.cpp +++ b/flang/lib/Evaluate/variable.cpp @@ -69,11 +69,11 @@ Triplet &Triplet::set_stride(Expr &&expr) { return *this; } -bool Triplet::IsStrideOne() const { +std::optional 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 46de668..3e48681 100644 --- a/flang/test/Semantics/assign03.f90 +++ b/flang/test/Semantics/assign03.f90 @@ -269,10 +269,10 @@ contains 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 @@ contains 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(..) -- 2.7.4