[flang] Begin shape analysis

Original-commit: flang-compiler/f18@888166c97e
Reviewed-on: https://github.com/flang-compiler/f18/pull/386
Tree-same-pre-rewrite: false
This commit is contained in:
peter klausler 2019-04-01 15:22:45 -07:00
parent 89a219488c
commit 47f8713854
9 changed files with 365 additions and 55 deletions

View File

@ -28,6 +28,7 @@ add_library(FortranEvaluate
intrinsics-library.cc
logical.cc
real.cc
shape.cc
static-data.cc
tools.cc
type.cc

View File

@ -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());

View File

@ -533,12 +533,15 @@ private:
Power<Result>, Extremum<Result>>;
using Indices = std::conditional_t<KIND == ImpliedDoIndex::Result::kind,
std::tuple<ImpliedDoIndex>, std::tuple<>>;
using DescriptorInquiries =
std::conditional_t<KIND == DescriptorInquiry::Result::kind,
std::tuple<DescriptorInquiry>, std::tuple<>>;
using Others = std::tuple<Constant<Result>, ArrayConstructor<Result>,
TypeParamInquiry<KIND>, Designator<Result>, FunctionRef<Result>>;
public:
common::TupleToVariant<
common::CombineTuples<Operations, Conversions, Indices, Others>>
common::TupleToVariant<common::CombineTuples<Operations, Conversions, Indices,
DescriptorInquiries, Others>>
u;
};

View File

@ -113,16 +113,19 @@ ArrayRef FoldOperation(FoldingContext &context, ArrayRef &&arrayRef) {
}
CoarrayRef FoldOperation(FoldingContext &context, CoarrayRef &&coarrayRef) {
auto base{coarrayRef.base()};
std::vector<Expr<SubscriptInteger>> subscript, cosubscript;
for (Expr<SubscriptInteger> 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<Expr<SubscriptInteger>> cosubscript;
for (Expr<SubscriptInteger> 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<Expr<SomeInteger>> stat{coarrayRef.stat()}) {
folded.set_stat(Fold(context, std::move(*stat)));
}

View File

@ -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);
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 << ')';
}
}
char separator{'('};
for (const auto &ss : subscript_) {
EmitVar(o << separator, ss);
separator = ',';
}
if (separator == ',') {
o << ')';
}
separator = '[';
char separator{'['};
for (const auto &css : cosubscript_) {
EmitVar(o << separator, css);
separator = ',';
@ -566,6 +572,28 @@ std::ostream &Designator<T>::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

118
flang/lib/evaluate/shape.cc Normal file
View File

@ -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<Shape> GetShape(
const semantics::Symbol &symbol, const Component *component) {
if (const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
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<SubscriptInteger>{1});
} else if (component != nullptr) {
result.emplace_back(Expr<SubscriptInteger>{DescriptorInquiry{
*component, DescriptorInquiry::Field::Extent, dimension}});
} else {
result.emplace_back(Expr<SubscriptInteger>{DescriptorInquiry{
symbol, DescriptorInquiry::Field::Extent, dimension}});
}
++dimension;
}
return result;
} else {
return std::nullopt;
}
}
std::optional<Shape> 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<SubscriptInteger>{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<Shape> 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<Shape> GetShape(const CoarrayRef &); // TODO pmk
std::optional<Shape> GetShape(const DataRef &dataRef) {
return std::visit([](const auto &x) { return GetShape(x); }, dataRef.u);
}
std::optional<Shape> GetShape(const Substring &substring) {
if (const auto *dataRef{substring.GetParentIf<DataRef>()}) {
return GetShape(*dataRef);
} else {
return std::nullopt;
}
}
std::optional<Shape> GetShape(const ComplexPart &part) {
return GetShape(part.complex());
}
}

View File

@ -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 <optional>
#include <variant>
namespace Fortran::evaluate {
using Extent = std::optional<Expr<SubscriptInteger>>;
using Shape = std::vector<Extent>;
template<typename A> std::optional<Shape> GetShape(const A &) {
return std::nullopt;
}
template<typename T> std::optional<Shape> GetShape(const Expr<T> &);
template<typename A, bool COPY>
std::optional<Shape> GetShape(const common::Indirection<A, COPY> &p) {
return GetShape(p.value());
}
template<typename A> std::optional<Shape> GetShape(const std::optional<A> &x) {
if (x.has_value()) {
return GetShape(*x);
} else {
return std::nullopt;
}
}
template<typename... A>
std::optional<Shape> GetShape(const std::variant<A...> &u) {
return std::visit([](const auto &x) { return GetShape(x); }, u);
}
std::optional<Shape> GetShape(
const semantics::Symbol &, const Component * = nullptr);
std::optional<Shape> GetShape(const DataRef &);
std::optional<Shape> GetShape(const ComplexPart &);
std::optional<Shape> GetShape(const Substring &);
std::optional<Shape> GetShape(const Component &);
std::optional<Shape> GetShape(const ArrayRef &);
std::optional<Shape> GetShape(const CoarrayRef &);
template<typename T>
std::optional<Shape> GetShape(const Designator<T> &designator) {
return std::visit([](const auto &x) { return GetShape(x); }, designator.u);
}
template<typename T> std::optional<Shape> GetShape(const Expr<T> &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_

View File

@ -69,12 +69,14 @@ bool Triplet::IsStrideOne() const {
}
}
CoarrayRef::CoarrayRef(std::vector<const Symbol *> &&c,
std::vector<Expr<SubscriptInteger>> &&ss,
std::vector<Expr<SubscriptInteger>> &&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<Expr<SubscriptInteger>> &&css)
: baseDataRef_{std::move(base)}, cosubscript_(std::move(css)) {
CHECK(!baseDataRef_.empty());
}
std::optional<Expr<SomeInteger>> CoarrayRef::stat() const {
@ -106,6 +108,14 @@ CoarrayRef &CoarrayRef::set_team(Expr<SomeInteger> &&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<Expr<SubscriptInteger>> &lower,
std::optional<Expr<SubscriptInteger>> &upper) {
if (lower.has_value()) {
@ -213,6 +223,31 @@ std::optional<Expr<SomeCharacter>> 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<Component>(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<SubscriptInteger> SymbolLEN(const Symbol &sym) {
return AsExpr(Constant<SubscriptInteger>{0}); // TODO
@ -243,7 +278,7 @@ Expr<SubscriptInteger> ArrayRef::LEN() const {
}
Expr<SubscriptInteger> CoarrayRef::LEN() const {
return SymbolLEN(*base_.back());
return SymbolLEN(GetLastSymbol());
}
Expr<SubscriptInteger> 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
}

View File

@ -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:
CLASS_BOILERPLATE(CoarrayRef)
CoarrayRef(std::vector<const Symbol *> &&,
std::vector<Expr<SubscriptInteger>> &&,
std::vector<Expr<SubscriptInteger>> &&);
struct BasePartRef {
explicit BasePartRef(const Symbol &s) : symbol{&s} {}
BasePartRef(const Symbol &s, std::vector<Expr<SubscriptInteger>> &&ss)
: symbol{&s}, subscript{std::move(ss)} {}
CLASS_BOILERPLATE(BasePartRef)
bool operator==(const BasePartRef &) const;
const Symbol *symbol;
std::vector<Expr<SubscriptInteger>> subscript;
};
using BaseDataRef = std::vector<BasePartRef>;
const std::vector<const Symbol *> &base() const { return base_; }
const std::vector<Expr<SubscriptInteger>> &subscript() const {
return subscript_;
}
CLASS_BOILERPLATE(CoarrayRef)
CoarrayRef(BaseDataRef &&, std::vector<Expr<SubscriptInteger>> &&);
const BaseDataRef &baseDataRef() const { return baseDataRef_; }
BaseDataRef &baseDataRef() { return baseDataRef_; }
const std::vector<Expr<SubscriptInteger>> &cosubscript() const {
return cosubscript_;
}
std::vector<Expr<SubscriptInteger>> &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<SomeInteger> &&, 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<SubscriptInteger> LEN() const;
bool operator==(const CoarrayRef &) const;
std::ostream &AsFortran(std::ostream &) const;
private:
std::vector<const Symbol *> base_;
std::vector<Expr<SubscriptInteger>> subscript_, cosubscript_;
BaseDataRef baseDataRef_;
std::vector<Expr<SubscriptInteger>> cosubscript_;
std::optional<common::CopyableIndirection<Expr<SomeInteger>>> stat_, team_;
bool teamIsTeamNumber_{false}; // false: TEAM=, true: TEAM_NUMBER=
};
@ -375,9 +388,35 @@ template<typename T> struct Variable {
std::variant<Designator<Result>, FunctionRef<Result>> 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_