From 47f871385448fb3be96a4c87f5f19112764d2a0c Mon Sep 17 00:00:00 2001 From: peter klausler Date: Mon, 1 Apr 2019 15:22:45 -0700 Subject: [PATCH] [flang] Begin shape analysis Original-commit: flang-compiler/f18@888166c97e83a244e183aac95e7a706b8d154ab9 Reviewed-on: https://github.com/flang-compiler/f18/pull/386 Tree-same-pre-rewrite: false --- flang/lib/evaluate/CMakeLists.txt | 1 + flang/lib/evaluate/descender.h | 15 +++-- flang/lib/evaluate/expression.h | 7 ++- flang/lib/evaluate/fold.cc | 15 +++-- flang/lib/evaluate/formatting.cc | 50 ++++++++++++---- flang/lib/evaluate/shape.cc | 118 ++++++++++++++++++++++++++++++++++++++ flang/lib/evaluate/shape.h | 80 ++++++++++++++++++++++++++ flang/lib/evaluate/variable.cc | 63 ++++++++++++++------ flang/lib/evaluate/variable.h | 69 +++++++++++++++++----- 9 files changed, 364 insertions(+), 54 deletions(-) create mode 100644 flang/lib/evaluate/shape.cc create mode 100644 flang/lib/evaluate/shape.h diff --git a/flang/lib/evaluate/CMakeLists.txt b/flang/lib/evaluate/CMakeLists.txt index f2faa17..591e361 100644 --- a/flang/lib/evaluate/CMakeLists.txt +++ b/flang/lib/evaluate/CMakeLists.txt @@ -28,6 +28,7 @@ add_library(FortranEvaluate intrinsics-library.cc logical.cc real.cc + shape.cc static-data.cc tools.cc type.cc diff --git a/flang/lib/evaluate/descender.h b/flang/lib/evaluate/descender.h index d4d8c25..42aed26 100644 --- a/flang/lib/evaluate/descender.h +++ b/flang/lib/evaluate/descender.h @@ -210,16 +210,23 @@ public: Visit(aref.subscript()); } + void Descend(const CoarrayRef::BasePartRef &part) { + Visit(*part.symbol); + Visit(part.subscript); + } + void Descend(CoarrayRef::BasePartRef &part) { + Visit(*part.symbol); + Visit(part.subscript); + } + void Descend(const CoarrayRef &caref) { - Visit(caref.base()); - Visit(caref.subscript()); + Visit(caref.baseDataRef()); Visit(caref.cosubscript()); Visit(caref.stat()); Visit(caref.team()); } void Descend(CoarrayRef &caref) { - Visit(caref.base()); - Visit(caref.subscript()); + Visit(caref.baseDataRef()); Visit(caref.cosubscript()); Visit(caref.stat()); Visit(caref.team()); diff --git a/flang/lib/evaluate/expression.h b/flang/lib/evaluate/expression.h index a803de0..0833d0c 100644 --- a/flang/lib/evaluate/expression.h +++ b/flang/lib/evaluate/expression.h @@ -533,12 +533,15 @@ private: Power, Extremum>; using Indices = std::conditional_t, std::tuple<>>; + using DescriptorInquiries = + std::conditional_t, std::tuple<>>; using Others = std::tuple, ArrayConstructor, TypeParamInquiry, Designator, FunctionRef>; public: - common::TupleToVariant< - common::CombineTuples> + common::TupleToVariant> u; }; diff --git a/flang/lib/evaluate/fold.cc b/flang/lib/evaluate/fold.cc index 9270415..df32195 100644 --- a/flang/lib/evaluate/fold.cc +++ b/flang/lib/evaluate/fold.cc @@ -113,16 +113,19 @@ ArrayRef FoldOperation(FoldingContext &context, ArrayRef &&arrayRef) { } CoarrayRef FoldOperation(FoldingContext &context, CoarrayRef &&coarrayRef) { - auto base{coarrayRef.base()}; - std::vector> subscript, cosubscript; - for (Expr x : coarrayRef.subscript()) { - subscript.emplace_back(Fold(context, std::move(x))); + CoarrayRef::BaseDataRef baseDataRef; + for (auto &&part : std::move(coarrayRef.baseDataRef())) { + baseDataRef.emplace_back(*coarrayRef.baseDataRef().front().symbol); + for (auto &&subscript : std::move(part.subscript)) { + baseDataRef.back().subscript.emplace_back( + Fold(context, std::move(subscript))); + } } + std::vector> cosubscript; for (Expr x : coarrayRef.cosubscript()) { cosubscript.emplace_back(Fold(context, std::move(x))); } - CoarrayRef folded{ - std::move(base), std::move(subscript), std::move(cosubscript)}; + CoarrayRef folded{std::move(baseDataRef), std::move(cosubscript)}; if (std::optional> stat{coarrayRef.stat()}) { folded.set_stat(Fold(context, std::move(*stat))); } diff --git a/flang/lib/evaluate/formatting.cc b/flang/lib/evaluate/formatting.cc index 8b5cb8e..9560e45c 100644 --- a/flang/lib/evaluate/formatting.cc +++ b/flang/lib/evaluate/formatting.cc @@ -510,18 +510,24 @@ std::ostream &ArrayRef::AsFortran(std::ostream &o) const { } std::ostream &CoarrayRef::AsFortran(std::ostream &o) const { - for (const Symbol *sym : base_) { - EmitVar(o, *sym); - } - char separator{'('}; - for (const auto &ss : subscript_) { - EmitVar(o << separator, ss); - separator = ','; - } - if (separator == ',') { - o << ')'; + bool first{true}; + for (const auto &part : baseDataRef_) { + if (first) { + first = false; + } else { + o << '%'; + } + EmitVar(o, *part.symbol); + char ch{'('}; + for (const auto &sscript : part.subscript) { + EmitVar(o << ch, sscript); + ch = ','; + } + if (ch == ',') { + o << ')'; + } } - separator = '['; + char separator{'['}; for (const auto &css : cosubscript_) { EmitVar(o << separator, css); separator = ','; @@ -566,6 +572,28 @@ std::ostream &Designator::AsFortran(std::ostream &o) const { return o; } +std::ostream &DescriptorInquiry::AsFortran(std::ostream &o) const { + switch (field_) { + case Field::LowerBound: o << "lbound("; break; + case Field::Extent: o << "%EXTENT("; break; + case Field::Stride: o << "%STRIDE("; break; + } + std::visit( + common::visitors{ + [&](const Symbol *sym) { + if (sym != nullptr) { + EmitVar(o, *sym); + } + }, + [&](const Component &comp) { EmitVar(o, comp); }, + }, + base_); + if (dimension_ > 0) { + o << ",dim=" << dimension_; + } + return o << ')'; +} + INSTANTIATE_CONSTANT_TEMPLATES INSTANTIATE_EXPRESSION_TEMPLATES INSTANTIATE_VARIABLE_TEMPLATES diff --git a/flang/lib/evaluate/shape.cc b/flang/lib/evaluate/shape.cc new file mode 100644 index 0000000..666846f --- /dev/null +++ b/flang/lib/evaluate/shape.cc @@ -0,0 +1,118 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "shape.h" +#include "fold.h" +#include "tools.h" +#include "../common/idioms.h" +#include "../semantics/symbol.h" + +namespace Fortran::evaluate { +std::optional GetShape( + const semantics::Symbol &symbol, const Component *component) { + if (const auto *details{symbol.detailsIf()}) { + Shape result; + int dimension{1}; + for (const auto &shapeSpec : details->shape()) { + if (shapeSpec.isExplicit()) { + result.emplace_back( + common::Clone(shapeSpec.ubound().GetExplicit().value()) - + common::Clone(shapeSpec.lbound().GetExplicit().value()) + + Expr{1}); + } else if (component != nullptr) { + result.emplace_back(Expr{DescriptorInquiry{ + *component, DescriptorInquiry::Field::Extent, dimension}}); + } else { + result.emplace_back(Expr{DescriptorInquiry{ + symbol, DescriptorInquiry::Field::Extent, dimension}}); + } + ++dimension; + } + return result; + } else { + return std::nullopt; + } +} + +std::optional GetShape(const Component &component) { + const Symbol &symbol{component.GetLastSymbol()}; + if (symbol.Rank() > 0) { + return GetShape(symbol, &component); + } else { + return GetShape(component.base()); + } +} +static Extent GetExtent(const Subscript &subscript) { + return std::visit( + common::visitors{ + [](const Triplet &triplet) -> Extent { + if (auto lower{triplet.lower()}) { + if (auto lowerValue{ToInt64(*lower)}) { + if (auto upper{triplet.upper()}) { + if (auto upperValue{ToInt64(*upper)}) { + if (auto strideValue{ToInt64(triplet.stride())}) { + if (*strideValue != 0) { + std::int64_t extent{ + (*upperValue - *lowerValue + *strideValue) / + *strideValue}; + return Expr{extent > 0 ? extent : 0}; + } + } + } + } + } + } + return std::nullopt; + }, + [](const IndirectSubscriptIntegerExpr &subs) -> Extent { + if (auto shape{GetShape(subs.value())}) { + if (shape->size() == 1) { + return std::move(shape->at(0)); + } + } + return std::nullopt; + }, + }, + subscript.u); +} +std::optional GetShape(const ArrayRef &arrayRef) { + int subscripts{arrayRef.size()}; + Shape shape; + for (int j = 0; j < subscripts; ++j) { + const Subscript &subscript{arrayRef.at(j)}; + if (subscript.Rank() > 0) { + shape.emplace_back(GetExtent(subscript)); + } + } + if (shape.empty()) { + return GetShape(arrayRef.base()); + } else { + return shape; + } +} +std::optional GetShape(const CoarrayRef &); // TODO pmk +std::optional GetShape(const DataRef &dataRef) { + return std::visit([](const auto &x) { return GetShape(x); }, dataRef.u); +} +std::optional GetShape(const Substring &substring) { + if (const auto *dataRef{substring.GetParentIf()}) { + return GetShape(*dataRef); + } else { + return std::nullopt; + } +} +std::optional GetShape(const ComplexPart &part) { + return GetShape(part.complex()); +} +} diff --git a/flang/lib/evaluate/shape.h b/flang/lib/evaluate/shape.h new file mode 100644 index 0000000..30e5e92 --- /dev/null +++ b/flang/lib/evaluate/shape.h @@ -0,0 +1,80 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// GetShape() analyzes an expression and determines its shape, if possible, +// representing the result as a vector of scalar integer expressions. + +#ifndef FORTRAN_EVALUATE_SHAPE_H_ +#define FORTRAN_EVALUATE_SHAPE_H_ + +#include "expression.h" +#include "type.h" +#include "../common/indirection.h" +#include +#include + +namespace Fortran::evaluate { + +using Extent = std::optional>; +using Shape = std::vector; + +template std::optional GetShape(const A &) { + return std::nullopt; +} + +template std::optional GetShape(const Expr &); + +template +std::optional GetShape(const common::Indirection &p) { + return GetShape(p.value()); +} + +template std::optional GetShape(const std::optional &x) { + if (x.has_value()) { + return GetShape(*x); + } else { + return std::nullopt; + } +} + +template +std::optional GetShape(const std::variant &u) { + return std::visit([](const auto &x) { return GetShape(x); }, u); +} + +std::optional GetShape( + const semantics::Symbol &, const Component * = nullptr); +std::optional GetShape(const DataRef &); +std::optional GetShape(const ComplexPart &); +std::optional GetShape(const Substring &); +std::optional GetShape(const Component &); +std::optional GetShape(const ArrayRef &); +std::optional GetShape(const CoarrayRef &); + +template +std::optional GetShape(const Designator &designator) { + return std::visit([](const auto &x) { return GetShape(x); }, designator.u); +} + +template std::optional GetShape(const Expr &expr) { + return std::visit( + common::visitors{ + [](const BOZLiteralConstant &) { return Shape{}; }, + [](const NullPointer &) { return std::nullopt; }, + [](const auto &x) { return GetShape(x); }, + }, + expr.u); +} +} +#endif // FORTRAN_EVALUATE_SHAPE_H_ diff --git a/flang/lib/evaluate/variable.cc b/flang/lib/evaluate/variable.cc index 7ec3b81..3165e35 100644 --- a/flang/lib/evaluate/variable.cc +++ b/flang/lib/evaluate/variable.cc @@ -69,12 +69,14 @@ bool Triplet::IsStrideOne() const { } } -CoarrayRef::CoarrayRef(std::vector &&c, - std::vector> &&ss, - std::vector> &&css) - : base_(std::move(c)), subscript_(std::move(ss)), - cosubscript_(std::move(css)) { - CHECK(!base_.empty()); +bool CoarrayRef::BasePartRef::operator==(const BasePartRef &that) const { + return symbol == that.symbol && subscript == that.subscript; +} + +CoarrayRef::CoarrayRef( + CoarrayRef::BaseDataRef &&base, std::vector> &&css) + : baseDataRef_{std::move(base)}, cosubscript_(std::move(css)) { + CHECK(!baseDataRef_.empty()); } std::optional> CoarrayRef::stat() const { @@ -106,6 +108,14 @@ CoarrayRef &CoarrayRef::set_team(Expr &&v, bool isTeamNumber) { return *this; } +const Symbol &CoarrayRef::GetFirstSymbol() const { + return *baseDataRef_.front().symbol; +} + +const Symbol &CoarrayRef::GetLastSymbol() const { + return *baseDataRef_.back().symbol; +} + void Substring::SetBounds(std::optional> &lower, std::optional> &upper) { if (lower.has_value()) { @@ -213,6 +223,31 @@ std::optional> Substring::Fold(FoldingContext &context) { return std::nullopt; } +DescriptorInquiry::DescriptorInquiry(const Symbol &symbol, Field field, int dim) + : base_{&symbol}, field_{field}, dimension_{dim} { + CHECK(IsDescriptor(symbol)); + CHECK(dim >= 1 && dim <= symbol.Rank()); +} +DescriptorInquiry::DescriptorInquiry( + Component &&component, Field field, int dim) + : base_{std::move(component)}, field_{field}, dimension_{dim} { + const Symbol &symbol{std::get(base_).GetLastSymbol()}; + CHECK(IsDescriptor(symbol)); + CHECK(dim >= 1 && dim <= symbol.Rank()); +} +DescriptorInquiry::DescriptorInquiry( + SymbolOrComponent &&x, Field field, int dim) + : base_{std::move(x)}, field_{field}, dimension_{dim} { + const Symbol *symbol{std::visit( + common::visitors{ + [](const Symbol *s) { return s; }, + [](Component &c) { return &c.GetLastSymbol(); }, + }, + base_)}; + CHECK(symbol != nullptr && IsDescriptor(*symbol)); + CHECK(dim >= 1 && dim <= symbol->Rank()); +} + // LEN() static Expr SymbolLEN(const Symbol &sym) { return AsExpr(Constant{0}); // TODO @@ -243,7 +278,7 @@ Expr ArrayRef::LEN() const { } Expr CoarrayRef::LEN() const { - return SymbolLEN(*base_.back()); + return SymbolLEN(GetLastSymbol()); } Expr DataRef::LEN() const { @@ -333,14 +368,6 @@ int ArrayRef::Rank() const { base_); } -int CoarrayRef::Rank() const { - int rank{0}; - for (const auto &expr : subscript_) { - rank += expr.Rank(); - } - return rank; -} - int DataRef::Rank() const { return std::visit( // g++ 7.2 emits bogus warnings here and below when common::visitors{} @@ -504,7 +531,7 @@ bool ArrayRef::operator==(const ArrayRef &that) const { return base_ == that.base_ && subscript_ == that.subscript_; } bool CoarrayRef::operator==(const CoarrayRef &that) const { - return base_ == that.base_ && subscript_ == that.subscript_ && + return baseDataRef_ == that.baseDataRef_ && cosubscript_ == that.cosubscript_ && stat_ == that.stat_ && team_ == that.team_ && teamIsTeamNumber_ == that.teamIsTeamNumber_; } @@ -518,6 +545,10 @@ bool ComplexPart::operator==(const ComplexPart &that) const { bool ProcedureRef::operator==(const ProcedureRef &that) const { return proc_ == that.proc_ && arguments_ == that.arguments_; } +bool DescriptorInquiry::operator==(const DescriptorInquiry &that) const { + return field_ == that.field_ && base_ == that.base_ && + dimension_ == that.dimension_; +} INSTANTIATE_VARIABLE_TEMPLATES } diff --git a/flang/lib/evaluate/variable.h b/flang/lib/evaluate/variable.h index 75f4672..94386d9 100644 --- a/flang/lib/evaluate/variable.h +++ b/flang/lib/evaluate/variable.h @@ -69,7 +69,7 @@ struct BaseObject { // R913 structure-component & C920: Defined to be a multi-part // data-ref whose last part has no subscripts (or image-selector, although // that isn't explicit in the document). Pointer and allocatable components -// are not explicitly indirected in this representation (TODO: yet?) +// are not explicitly indirected in this representation. // Complex components (%RE, %IM) are isolated below in ComplexPart. // (Type parameter inquiries look like component references but are distinct // constructs and not represented by this class.) @@ -204,22 +204,35 @@ private: // C824 severely limits the usage of derived types with coarray ultimate // components: they can't be pointers, allocatables, arrays, coarrays, or // function results. They can be components of other derived types. +// Although the F'2018 Standard never prohibits multiple image-selectors +// in the same data-ref or designator, nor the presence of an image-selector +// after a part-ref with rank, the constraints on the derived types that +// would have be involved make it impossible to declare an object that +// could be referenced in these ways. // C930 precludes having both TEAM= and TEAM_NUMBER=. // TODO C931 prohibits the use of a coindexed object as a stat-variable. class CoarrayRef { public: + struct BasePartRef { + explicit BasePartRef(const Symbol &s) : symbol{&s} {} + BasePartRef(const Symbol &s, std::vector> &&ss) + : symbol{&s}, subscript{std::move(ss)} {} + CLASS_BOILERPLATE(BasePartRef) + bool operator==(const BasePartRef &) const; + const Symbol *symbol; + std::vector> subscript; + }; + using BaseDataRef = std::vector; + CLASS_BOILERPLATE(CoarrayRef) - CoarrayRef(std::vector &&, - std::vector> &&, - std::vector> &&); + CoarrayRef(BaseDataRef &&, std::vector> &&); - const std::vector &base() const { return base_; } - const std::vector> &subscript() const { - return subscript_; - } + const BaseDataRef &baseDataRef() const { return baseDataRef_; } + BaseDataRef &baseDataRef() { return baseDataRef_; } const std::vector> &cosubscript() const { return cosubscript_; } + std::vector> &cosubscript() { return cosubscript_; } // These integral expressions for STAT= and TEAM= must be variables // (i.e., Designator or pointer-valued FunctionRef). @@ -229,16 +242,16 @@ public: bool teamIsTeamNumber() const { return teamIsTeamNumber_; } CoarrayRef &set_team(Expr &&, bool isTeamNumber = false); - int Rank() const; - const Symbol &GetFirstSymbol() const { return *base_.front(); } - const Symbol &GetLastSymbol() const { return *base_.back(); } + int Rank() const { return 0; } + const Symbol &GetFirstSymbol() const; + const Symbol &GetLastSymbol() const; Expr LEN() const; bool operator==(const CoarrayRef &) const; std::ostream &AsFortran(std::ostream &) const; private: - std::vector base_; - std::vector> subscript_, cosubscript_; + BaseDataRef baseDataRef_; + std::vector> cosubscript_; std::optional>> stat_, team_; bool teamIsTeamNumber_{false}; // false: TEAM=, true: TEAM_NUMBER= }; @@ -375,9 +388,35 @@ template struct Variable { std::variant, FunctionRef> u; }; +class DescriptorInquiry { +public: + using Result = SubscriptInteger; + ENUM_CLASS(Field, LowerBound, Extent, Stride) + // TODO: Also type parameters and/or CHARACTER length? + + CLASS_BOILERPLATE(DescriptorInquiry) + DescriptorInquiry(const Symbol &, Field, int); + DescriptorInquiry(Component &&, Field, int); + DescriptorInquiry(SymbolOrComponent &&, Field, int); + + SymbolOrComponent &base() { return base_; } + const SymbolOrComponent &base() const { return base_; } + Field field() const { return field_; } + int dimension() const { return dimension_; } + + static constexpr int Rank() { return 0; } // always scalar + bool operator==(const DescriptorInquiry &) const; + std::ostream &AsFortran(std::ostream &) const; + +private: + SymbolOrComponent base_{nullptr}; + Field field_; + int dimension_{0}; +}; + #define INSTANTIATE_VARIABLE_TEMPLATES \ - EXPAND_FOR_EACH_INTEGER_KIND( \ - TEMPLATE_INSTANTIATION, template class TypeParamInquiry, ) \ + EXPAND_FOR_EACH_INTEGER_KIND( \ + TEMPLATE_INSTANTIATION, template class TypeParamInquiry, ) \ FOR_EACH_SPECIFIC_TYPE(template class Designator, ) } #endif // FORTRAN_EVALUATE_VARIABLE_H_ -- 2.7.4