[flang] Batch together the changes to the PFT intermediate data

structure for upstreaming to llvm-project.

These files have had many changes since they were originally upstreamed.
Some of the changes are cosmetic.  Most of the functional changes were
done to support the lowering of control-flow syntax from the front-end
parse trees to the FIR dialect.

This patch is meant to be a reviewable size. The functionality it
provides will be used by code yet to be upstreamed in lowering.

review comments:

[review D80449][NFC] make PFT ParentVariant a ReferenceVariant

ReferenceVariant had to be slightly updated to also support
non constant references (which is required for ParentType).

[review D80449] extend Variable implementation beyond a comment
This commit is contained in:
Eric Schweitz 2020-05-22 10:18:26 -07:00
parent e4d2037a5c
commit fbac9ce226
9 changed files with 1330 additions and 741 deletions

View file

@ -1,35 +1,35 @@
//===-- include/flang/Lower/PFTBuilder.h ------------------------*- C++ -*-===// //===-- Lower/PFTBuilder.h -- PFT builder -----------------------*- C++ -*-===//
// //
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information. // See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
//
// PFT (Pre-FIR Tree) interface.
//
//===----------------------------------------------------------------------===//
#ifndef FORTRAN_LOWER_PFT_BUILDER_H_ #ifndef FORTRAN_LOWER_PFTBUILDER_H
#define FORTRAN_LOWER_PFT_BUILDER_H_ #define FORTRAN_LOWER_PFTBUILDER_H
#include "flang/Common/reference.h"
#include "flang/Common/template.h" #include "flang/Common/template.h"
#include "flang/Parser/parse-tree.h" #include "flang/Parser/parse-tree.h"
#include <memory> #include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/Support/raw_ostream.h"
/// Build a light-weight tree over the parse-tree to help with lowering to FIR. namespace mlir {
/// It is named Pre-FIR Tree (PFT) to underline it has no other usage than class Block;
/// helping lowering to FIR.
/// The PFT will capture pointers back into the parse tree, so the parse tree
/// data structure may <em>not</em> be changed between the construction of the
/// PFT and all of its uses.
///
/// The PFT captures a structured view of the program. The program is a list of
/// units. Function like units will contain lists of evaluations. Evaluations
/// are either statements or constructs, where a construct contains a list of
/// evaluations. The resulting PFT structure can then be used to create FIR.
namespace llvm {
class raw_ostream;
} }
namespace Fortran::lower { namespace Fortran {
namespace semantics {
class SemanticsContext;
class Scope;
} // namespace semantics
namespace lower {
namespace pft { namespace pft {
struct Evaluation; struct Evaluation;
@ -40,40 +40,56 @@ struct FunctionLikeUnit;
// TODO: A collection of Evaluations can obviously be any of the container // TODO: A collection of Evaluations can obviously be any of the container
// types; leaving this as a std::list _for now_ because we reserve the right to // types; leaving this as a std::list _for now_ because we reserve the right to
// insert PFT nodes in any order in O(1) time. // insert PFT nodes in any order in O(1) time.
using EvaluationCollection = std::list<Evaluation>; using EvaluationList = std::list<Evaluation>;
using LabelEvalMap = llvm::DenseMap<Fortran::parser::Label, Evaluation *>;
struct ParentType { /// Provide a variant like container that can hold references. It can hold
template <typename A> /// constant or mutable references. It is used in the other classes to provide
ParentType(A &parent) : p{&parent} {} /// union of const references to parse-tree nodes.
const std::variant<Program *, ModuleLikeUnit *, FunctionLikeUnit *, template <bool isConst, typename... A>
Evaluation *> class ReferenceVariantBase {
p; public:
}; template <typename B>
using BaseType = std::conditional_t<isConst, const B, B>;
template <typename B>
using Ref = common::Reference<BaseType<B>>;
/// Flags to describe the impact of parse-trees nodes on the program ReferenceVariantBase() = delete;
/// control flow. These annotations to parse-tree nodes are later used to template <typename B>
/// build the control flow graph when lowering to FIR. ReferenceVariantBase(B &b) : u{Ref<B>{b}} {}
enum class CFGAnnotation {
None, // Node does not impact control flow.
Goto, // Node acts like a goto on the control flow.
CondGoto, // Node acts like a conditional goto on the control flow.
IndGoto, // Node acts like an indirect goto on the control flow.
IoSwitch, // Node is an IO statement with ERR, END, or EOR specifier.
Switch, // Node acts like a switch on the control flow.
Iterative, // Node creates iterations in the control flow.
FirStructuredOp, // Node is a structured loop.
Return, // Node triggers a return from the current procedure.
Terminate // Node terminates the program.
};
/// Compiler-generated jump template <typename B>
/// constexpr BaseType<B> &get() const {
/// This is used to convert implicit control-flow edges to explicit form in the return std::get<Ref<B>> > (u).get();
/// decorated PFT }
struct CGJump { template <typename B>
CGJump(Evaluation &to) : target{to} {} constexpr BaseType<B> *getIf() const {
Evaluation &target; auto *ptr = std::get_if<Ref<B>>(&u);
return ptr ? &ptr->get() : nullptr;
}
template <typename B>
constexpr bool isA() const {
return std::holds_alternative<Ref<B>>(u);
}
template <typename VISITOR>
constexpr auto visit(VISITOR &&visitor) const {
return std::visit(
common::visitors{[&visitor](auto ref) { return visitor(ref.get()); }},
u);
}
private:
std::variant<Ref<A>...> u;
}; };
template <typename... A>
using ReferenceVariant = ReferenceVariantBase<true, A...>;
template <typename... A>
using MutableReferenceVariant = ReferenceVariantBase<false, A...>;
/// ParentVariant is used to provide a reference to the unit a parse-tree node
/// belongs to. It is a variant of non-nullable pointers.
using ParentVariant = MutableReferenceVariant<Program, ModuleLikeUnit,
FunctionLikeUnit, Evaluation>;
/// Classify the parse-tree nodes from ExecutablePartConstruct /// Classify the parse-tree nodes from ExecutablePartConstruct
@ -95,15 +111,6 @@ using ActionStmts = std::tuple<
using OtherStmts = std::tuple<parser::FormatStmt, parser::EntryStmt, using OtherStmts = std::tuple<parser::FormatStmt, parser::EntryStmt,
parser::DataStmt, parser::NamelistStmt>; parser::DataStmt, parser::NamelistStmt>;
using Constructs =
std::tuple<parser::AssociateConstruct, parser::BlockConstruct,
parser::CaseConstruct, parser::ChangeTeamConstruct,
parser::CriticalConstruct, parser::DoConstruct,
parser::IfConstruct, parser::SelectRankConstruct,
parser::SelectTypeConstruct, parser::WhereConstruct,
parser::ForallConstruct, parser::CompilerDirective,
parser::OpenMPConstruct, parser::OmpEndLoopDirective>;
using ConstructStmts = std::tuple< using ConstructStmts = std::tuple<
parser::AssociateStmt, parser::EndAssociateStmt, parser::BlockStmt, parser::AssociateStmt, parser::EndAssociateStmt, parser::BlockStmt,
parser::EndBlockStmt, parser::SelectCaseStmt, parser::CaseStmt, parser::EndBlockStmt, parser::SelectCaseStmt, parser::CaseStmt,
@ -115,257 +122,342 @@ using ConstructStmts = std::tuple<
parser::MaskedElsewhereStmt, parser::ElsewhereStmt, parser::EndWhereStmt, parser::MaskedElsewhereStmt, parser::ElsewhereStmt, parser::EndWhereStmt,
parser::ForallConstructStmt, parser::EndForallStmt>; parser::ForallConstructStmt, parser::EndForallStmt>;
template <typename A> using Constructs =
constexpr static bool isActionStmt{common::HasMember<A, ActionStmts>}; std::tuple<parser::AssociateConstruct, parser::BlockConstruct,
parser::CaseConstruct, parser::ChangeTeamConstruct,
parser::CriticalConstruct, parser::DoConstruct,
parser::IfConstruct, parser::SelectRankConstruct,
parser::SelectTypeConstruct, parser::WhereConstruct,
parser::ForallConstruct>;
using Directives =
std::tuple<parser::CompilerDirective, parser::OpenMPConstruct,
parser::OmpEndLoopDirective>;
template <typename A> template <typename A>
constexpr static bool isConstruct{common::HasMember<A, Constructs>}; static constexpr bool isActionStmt{common::HasMember<A, ActionStmts>};
template <typename A> template <typename A>
constexpr static bool isConstructStmt{common::HasMember<A, ConstructStmts>}; static constexpr bool isOtherStmt{common::HasMember<A, OtherStmts>};
template <typename A> template <typename A>
constexpr static bool isOtherStmt{common::HasMember<A, OtherStmts>}; static constexpr bool isConstructStmt{common::HasMember<A, ConstructStmts>};
template <typename A> template <typename A>
constexpr static bool isGenerated{std::is_same_v<A, CGJump>}; static constexpr bool isConstruct{common::HasMember<A, Constructs>};
template <typename A> template <typename A>
constexpr static bool isFunctionLike{common::HasMember< static constexpr bool isDirective{common::HasMember<A, Directives>};
template <typename A>
static constexpr bool isIntermediateConstructStmt{common::HasMember<
A, std::tuple<parser::CaseStmt, parser::ElseIfStmt, parser::ElseStmt,
parser::SelectRankCaseStmt, parser::TypeGuardStmt>>};
template <typename A>
static constexpr bool isNopConstructStmt{common::HasMember<
A, std::tuple<parser::EndAssociateStmt, parser::CaseStmt,
parser::EndSelectStmt, parser::ElseIfStmt, parser::ElseStmt,
parser::EndIfStmt, parser::SelectRankCaseStmt,
parser::TypeGuardStmt>>};
template <typename A>
static constexpr bool isFunctionLike{common::HasMember<
A, std::tuple<parser::MainProgram, parser::FunctionSubprogram, A, std::tuple<parser::MainProgram, parser::FunctionSubprogram,
parser::SubroutineSubprogram, parser::SubroutineSubprogram,
parser::SeparateModuleSubprogram>>}; parser::SeparateModuleSubprogram>>};
/// Function-like units can contains lists of evaluations. These can be using LabelSet = llvm::SmallSet<parser::Label, 5>;
/// (simple) statements or constructs, where a construct contains its own using SymbolRef = common::Reference<const semantics::Symbol>;
/// evaluations. using SymbolLabelMap = llvm::DenseMap<SymbolRef, LabelSet>;
struct Evaluation {
using EvalTuple = common::CombineTuples<ActionStmts, OtherStmts, Constructs,
ConstructStmts>;
/// Hide non-nullable pointers to the parse-tree node. template <typename A>
template <typename A> struct MakeReferenceVariantHelper {};
using MakeRefType = const A *const; template <typename... A>
using EvalVariant = struct MakeReferenceVariantHelper<std::variant<A...>> {
common::CombineVariants<common::MapTemplate<MakeRefType, EvalTuple>, using type = ReferenceVariant<A...>;
std::variant<CGJump>>; };
template <typename A> template <typename... A>
constexpr auto visit(A visitor) const { struct MakeReferenceVariantHelper<std::tuple<A...>> {
return std::visit(common::visitors{ using type = ReferenceVariant<A...>;
[&](const auto *p) { return visitor(*p); }, };
[&](auto &r) { return visitor(r); }, template <typename A>
}, using MakeReferenceVariant = typename MakeReferenceVariantHelper<A>::type;
u);
}
template <typename A>
constexpr const A *getIf() const {
if constexpr (!std::is_same_v<A, CGJump>) {
if (auto *ptr{std::get_if<MakeRefType<A>>(&u)}) {
return *ptr;
}
} else {
return std::get_if<CGJump>(&u);
}
return nullptr;
}
template <typename A>
constexpr bool isA() const {
if constexpr (!std::is_same_v<A, CGJump>) {
return std::holds_alternative<MakeRefType<A>>(u);
}
return std::holds_alternative<CGJump>(u);
}
Evaluation() = delete; using EvaluationTuple =
Evaluation(const Evaluation &) = delete; common::CombineTuples<ActionStmts, OtherStmts, ConstructStmts, Constructs,
Evaluation(Evaluation &&) = default; Directives>;
/// Hide non-nullable pointers to the parse-tree node.
/// Build type std::variant<const A* const, const B* const, ...>
/// from EvaluationTuple type (std::tuple<A, B, ...>).
using EvaluationVariant = MakeReferenceVariant<EvaluationTuple>;
/// Function-like units contain lists of evaluations. These can be simple
/// statements or constructs, where a construct contains its own evaluations.
struct Evaluation : EvaluationVariant {
/// General ctor /// General ctor
template <typename A> template <typename A>
Evaluation(const A &a, const ParentType &p, const parser::CharBlock &pos, Evaluation(const A &a, const ParentVariant &parentVariant,
const std::optional<parser::Label> &lab) const parser::CharBlock &position,
: u{&a}, parent{p}, pos{pos}, lab{lab} {} const std::optional<parser::Label> &label)
: EvaluationVariant{a},
/// Compiler-generated jump parentVariant{parentVariant}, position{position}, label{label} {}
Evaluation(const CGJump &jump, const ParentType &p)
: u{jump}, parent{p}, cfg{CFGAnnotation::Goto} {}
/// Construct ctor /// Construct ctor
template <typename A> template <typename A>
Evaluation(const A &a, const ParentType &parent) : u{&a}, parent{parent} { Evaluation(const A &a, const ParentVariant &parentVariant)
static_assert(pft::isConstruct<A>, "must be a construct"); : EvaluationVariant{a}, parentVariant{parentVariant} {
static_assert(pft::isConstruct<A> || pft::isDirective<A>,
"must be a construct or directive");
} }
constexpr bool isActionOrGenerated() const { /// Evaluation classification predicates.
constexpr bool isActionStmt() const {
return visit(common::visitors{ return visit(common::visitors{
[](auto &r) { [](auto &r) { return pft::isActionStmt<std::decay_t<decltype(r)>>; }});
using T = std::decay_t<decltype(r)>;
return isActionStmt<T> || isGenerated<T>;
},
});
} }
constexpr bool isOtherStmt() const {
constexpr bool isStmt() const {
return visit(common::visitors{ return visit(common::visitors{
[](auto &r) { [](auto &r) { return pft::isOtherStmt<std::decay_t<decltype(r)>>; }});
using T = std::decay_t<decltype(r)>;
static constexpr bool isStmt{isActionStmt<T> || isOtherStmt<T> ||
isConstructStmt<T>};
static_assert(!(isStmt && pft::isConstruct<T>),
"statement classification is inconsistent");
return isStmt;
},
});
} }
constexpr bool isConstruct() const { return !isStmt(); } constexpr bool isConstructStmt() const {
return visit(common::visitors{[](auto &r) {
/// Set the type of originating control flow type for this evaluation. return pft::isConstructStmt<std::decay_t<decltype(r)>>;
void setCFG(CFGAnnotation a, Evaluation *cstr) { }});
cfg = a; }
setBranches(cstr); constexpr bool isConstruct() const {
return visit(common::visitors{
[](auto &r) { return pft::isConstruct<std::decay_t<decltype(r)>>; }});
}
constexpr bool isDirective() const {
return visit(common::visitors{
[](auto &r) { return pft::isDirective<std::decay_t<decltype(r)>>; }});
}
/// Return the predicate: "This is a non-initial, non-terminal construct
/// statement." For an IfConstruct, this is ElseIfStmt and ElseStmt.
constexpr bool isIntermediateConstructStmt() const {
return visit(common::visitors{[](auto &r) {
return pft::isIntermediateConstructStmt<std::decay_t<decltype(r)>>;
}});
}
constexpr bool isNopConstructStmt() const {
return visit(common::visitors{[](auto &r) {
return pft::isNopConstructStmt<std::decay_t<decltype(r)>>;
}});
} }
/// Is this evaluation a control-flow origin? (The PFT must be annotated) /// Return FunctionLikeUnit to which this evaluation
bool isControlOrigin() const { return cfg != CFGAnnotation::None; } /// belongs. Nullptr if it does not belong to such unit.
FunctionLikeUnit *getOwningProcedure() const;
/// Is this evaluation a control-flow target? (The PFT must be annotated) bool lowerAsStructured() const;
bool isControlTarget() const { return isTarget; } bool lowerAsUnstructured() const;
/// Set the containsBranches flag iff this evaluation (a construct) contains // FIR generation looks primarily at PFT statement (leaf) nodes. So members
/// control flow // such as lexicalSuccessor and the various block fields are only applicable
void setBranches() { containsBranches = true; } // to statement nodes. One exception is that an internal construct node is
// a convenient place for a constructExit link that applies to exits from any
// statement within the construct. The controlSuccessor member is used for
// nonlexical successors, such as linking to a GOTO target. For multiway
// branches, controlSuccessor is set to one of the targets (might as well be
// the first target). Successor and exit links always target statements.
//
// An unstructured construct is one that contains some form of goto. This
// is indicated by the isUnstructured member flag, which may be set on a
// statement and propagated to enclosing constructs. This distinction allows
// a structured IF or DO statement to be materialized with custom structured
// FIR operations. An unstructured statement is materialized as mlir
// operation sequences that include explicit branches.
//
// There are two mlir::Block members. The block member is set for statements
// that begin a new block. If a statement may have more than one associated
// block, this member must be the block that would be the target of a branch
// to the statement. The prime example of a statement that may have multiple
// associated blocks is NonLabelDoStmt, which may have a loop preheader block
// for loop initialization code, and always has a header block that is the
// target of the loop back edge. If the NonLabelDoStmt is a concurrent loop,
// there may be an arbitrary number of nested preheader, header, and mask
// blocks. Any such additional blocks in the localBlocks member are local
// to a construct and cannot be the target of an unstructured branch. For
// NonLabelDoStmt, the block member designates the preheader block, which may
// be absent if loop initialization code may be appended to a predecessor
// block. The primary loop header block is localBlocks[0], with additional
// DO CONCURRENT blocks at localBlocks[1], etc.
//
// The printIndex member is only set for statements. It is used for dumps
// and does not affect FIR generation. It may also be helpful for debugging.
EvaluationCollection *getConstructEvals() { ParentVariant parentVariant;
auto *evals{subs.get()}; parser::CharBlock position{};
if (isStmt() && !evals) { std::optional<parser::Label> label{};
return nullptr; std::unique_ptr<EvaluationList> evaluationList; // nested evaluations
} Evaluation *parentConstruct{nullptr}; // set for nodes below the top level
if (isConstruct() && evals) { Evaluation *lexicalSuccessor{nullptr}; // set for ActionStmt, ConstructStmt
return evals; Evaluation *controlSuccessor{nullptr}; // set for some statements
} Evaluation *constructExit{nullptr}; // set for constructs
llvm_unreachable("evaluation subs is inconsistent"); bool isNewBlock{false}; // evaluation begins a new basic block
return nullptr; bool isUnstructured{false}; // evaluation has unstructured control flow
} bool skip{false}; // evaluation has been processed in advance
class mlir::Block *block{nullptr}; // isNewBlock block
/// Set that the construct `cstr` (if not a nullptr) has branches. llvm::SmallVector<mlir::Block *, 1> localBlocks{}; // construct local blocks
static void setBranches(Evaluation *cstr) { int printIndex{0}; // (ActionStmt, ConstructStmt) evaluation index for dumps
if (cstr)
cstr->setBranches();
}
EvalVariant u;
ParentType parent;
parser::CharBlock pos;
std::optional<parser::Label> lab;
std::unique_ptr<EvaluationCollection> subs; // construct sub-statements
CFGAnnotation cfg{CFGAnnotation::None};
bool isTarget{false}; // this evaluation is a control target
bool containsBranches{false}; // construct contains branches
}; };
using ProgramVariant =
ReferenceVariant<parser::MainProgram, parser::FunctionSubprogram,
parser::SubroutineSubprogram, parser::Module,
parser::Submodule, parser::SeparateModuleSubprogram,
parser::BlockData>;
/// A program is a list of program units. /// A program is a list of program units.
/// These units can be function like, module like, or block data /// These units can be function like, module like, or block data.
struct ProgramUnit { struct ProgramUnit : ProgramVariant {
template <typename A> template <typename A>
ProgramUnit(const A &ptr, const ParentType &parent) ProgramUnit(const A &p, const ParentVariant &parentVariant)
: p{&ptr}, parent{parent} {} : ProgramVariant{p}, parentVariant{parentVariant} {}
ProgramUnit(ProgramUnit &&) = default; ProgramUnit(ProgramUnit &&) = default;
ProgramUnit(const ProgramUnit &) = delete; ProgramUnit(const ProgramUnit &) = delete;
const std::variant< ParentVariant parentVariant;
const parser::MainProgram *, const parser::FunctionSubprogram *,
const parser::SubroutineSubprogram *, const parser::Module *,
const parser::Submodule *, const parser::SeparateModuleSubprogram *,
const parser::BlockData *>
p;
ParentType parent;
}; };
/// Function-like units have similar structure. They all can contain executable /// A variable captures an object to be created per the declaration part of a
/// statements as well as other function-like units (internal procedures and /// function like unit.
/// function statements). ///
/// Properties can be applied by lowering. For example, a local array that is
/// known to be very large may be transformed into a heap allocated entity by
/// lowering. That decision would be tracked in its Variable instance.
struct Variable {
explicit Variable(const Fortran::semantics::Symbol &sym, bool global = false,
int depth = 0)
: sym{&sym}, depth{depth}, global{global} {}
const Fortran::semantics::Symbol &getSymbol() const { return *sym; }
bool isGlobal() const { return global; }
bool isHeapAlloc() const { return heapAlloc; }
bool isPointer() const { return pointer; }
bool isTarget() const { return target; }
int getDepth() const { return depth; }
void setHeapAlloc(bool to = true) { heapAlloc = to; }
void setPointer(bool to = true) { pointer = to; }
void setTarget(bool to = true) { target = to; }
private:
const Fortran::semantics::Symbol *sym;
int depth;
bool global;
bool heapAlloc{false}; // variable needs deallocation on exit
bool pointer{false};
bool target{false};
};
/// Function-like units may contain evaluations (executable statements) and
/// nested function-like units (internal procedures and function statements).
struct FunctionLikeUnit : public ProgramUnit { struct FunctionLikeUnit : public ProgramUnit {
// wrapper statements for function-like syntactic structures // wrapper statements for function-like syntactic structures
using FunctionStatement = using FunctionStatement =
std::variant<const parser::Statement<parser::ProgramStmt> *, ReferenceVariant<parser::Statement<parser::ProgramStmt>,
const parser::Statement<parser::EndProgramStmt> *, parser::Statement<parser::EndProgramStmt>,
const parser::Statement<parser::FunctionStmt> *, parser::Statement<parser::FunctionStmt>,
const parser::Statement<parser::EndFunctionStmt> *, parser::Statement<parser::EndFunctionStmt>,
const parser::Statement<parser::SubroutineStmt> *, parser::Statement<parser::SubroutineStmt>,
const parser::Statement<parser::EndSubroutineStmt> *, parser::Statement<parser::EndSubroutineStmt>,
const parser::Statement<parser::MpSubprogramStmt> *, parser::Statement<parser::MpSubprogramStmt>,
const parser::Statement<parser::EndMpSubprogramStmt> *>; parser::Statement<parser::EndMpSubprogramStmt>>;
FunctionLikeUnit(const parser::MainProgram &f, const ParentType &parent); FunctionLikeUnit(
FunctionLikeUnit(const parser::FunctionSubprogram &f, const parser::MainProgram &f, const ParentVariant &parentVariant,
const ParentType &parent); const Fortran::semantics::SemanticsContext &semanticsContext);
FunctionLikeUnit(const parser::SubroutineSubprogram &f, FunctionLikeUnit(
const ParentType &parent); const parser::FunctionSubprogram &f, const ParentVariant &parentVariant,
FunctionLikeUnit(const parser::SeparateModuleSubprogram &f, const Fortran::semantics::SemanticsContext &semanticsContext);
const ParentType &parent); FunctionLikeUnit(
const parser::SubroutineSubprogram &f, const ParentVariant &parentVariant,
const Fortran::semantics::SemanticsContext &semanticsContext);
FunctionLikeUnit(
const parser::SeparateModuleSubprogram &f,
const ParentVariant &parentVariant,
const Fortran::semantics::SemanticsContext &semanticsContext);
FunctionLikeUnit(FunctionLikeUnit &&) = default; FunctionLikeUnit(FunctionLikeUnit &&) = default;
FunctionLikeUnit(const FunctionLikeUnit &) = delete; FunctionLikeUnit(const FunctionLikeUnit &) = delete;
bool isMainProgram() { void processSymbolTable(const Fortran::semantics::Scope &);
return std::holds_alternative<
const parser::Statement<parser::EndProgramStmt> *>(endStmt); std::vector<Variable> getOrderedSymbolTable() { return varList[0]; }
bool isMainProgram() const {
return endStmt.isA<parser::Statement<parser::EndProgramStmt>>();
} }
const parser::FunctionStmt *getFunction() {
return getA<parser::FunctionStmt>(); /// Get the starting source location for this function like unit
parser::CharBlock getStartingSourceLoc() {
if (beginStmt)
return stmtSourceLoc(*beginStmt);
if (!evaluationList.empty())
return evaluationList.front().position;
return stmtSourceLoc(endStmt);
} }
const parser::SubroutineStmt *getSubroutine() {
return getA<parser::SubroutineStmt>(); /// Returns reference to the subprogram symbol of this FunctionLikeUnit.
/// Dies if the FunctionLikeUnit is not a subprogram.
const semantics::Symbol &getSubprogramSymbol() const {
assert(symbol && "not inside a procedure");
return *symbol;
} }
const parser::MpSubprogramStmt *getMPSubp() {
return getA<parser::MpSubprogramStmt>(); /// Helper to get location from FunctionLikeUnit begin/end statements.
static parser::CharBlock stmtSourceLoc(const FunctionStatement &stmt) {
return stmt.visit(common::visitors{[](const auto &x) { return x.source; }});
} }
/// Anonymous programs do not have a begin statement /// Anonymous programs do not have a begin statement
std::optional<FunctionStatement> beginStmt; std::optional<FunctionStatement> beginStmt;
FunctionStatement endStmt; FunctionStatement endStmt;
EvaluationCollection evals; // statements EvaluationList evaluationList;
std::list<FunctionLikeUnit> funcs; // internal procedures LabelEvalMap labelEvaluationMap;
SymbolLabelMap assignSymbolLabelMap;
private: std::list<FunctionLikeUnit> nestedFunctions;
template <typename A> /// Symbol associated to this FunctionLikeUnit.
const A *getA() { /// Null if the FunctionLikeUnit is an anonymous program.
if (beginStmt) { /// The symbol has MainProgramDetails for named programs, otherwise it has
if (auto p = /// SubprogramDetails.
std::get_if<const parser::Statement<A> *>(&beginStmt.value())) const semantics::Symbol *symbol{nullptr};
return &(*p)->statement; /// Terminal basic block (if any)
} mlir::Block *finalBlock{};
return nullptr; std::vector<std::vector<Variable>> varList;
}
}; };
/// Module-like units have similar structure. They all can contain a list of /// Module-like units contain a list of function-like units.
/// function-like units.
struct ModuleLikeUnit : public ProgramUnit { struct ModuleLikeUnit : public ProgramUnit {
// wrapper statements for module-like syntactic structures // wrapper statements for module-like syntactic structures
using ModuleStatement = using ModuleStatement =
std::variant<const parser::Statement<parser::ModuleStmt> *, ReferenceVariant<parser::Statement<parser::ModuleStmt>,
const parser::Statement<parser::EndModuleStmt> *, parser::Statement<parser::EndModuleStmt>,
const parser::Statement<parser::SubmoduleStmt> *, parser::Statement<parser::SubmoduleStmt>,
const parser::Statement<parser::EndSubmoduleStmt> *>; parser::Statement<parser::EndSubmoduleStmt>>;
ModuleLikeUnit(const parser::Module &m, const ParentType &parent); ModuleLikeUnit(const parser::Module &m, const ParentVariant &parentVariant);
ModuleLikeUnit(const parser::Submodule &m, const ParentType &parent); ModuleLikeUnit(const parser::Submodule &m,
const ParentVariant &parentVariant);
~ModuleLikeUnit() = default; ~ModuleLikeUnit() = default;
ModuleLikeUnit(ModuleLikeUnit &&) = default; ModuleLikeUnit(ModuleLikeUnit &&) = default;
ModuleLikeUnit(const ModuleLikeUnit &) = delete; ModuleLikeUnit(const ModuleLikeUnit &) = delete;
ModuleStatement beginStmt; ModuleStatement beginStmt;
ModuleStatement endStmt; ModuleStatement endStmt;
std::list<FunctionLikeUnit> funcs; std::list<FunctionLikeUnit> nestedFunctions;
}; };
struct BlockDataUnit : public ProgramUnit { struct BlockDataUnit : public ProgramUnit {
BlockDataUnit(const parser::BlockData &bd, const ParentType &parent); BlockDataUnit(const parser::BlockData &bd,
const ParentVariant &parentVariant);
BlockDataUnit(BlockDataUnit &&) = default; BlockDataUnit(BlockDataUnit &&) = default;
BlockDataUnit(const BlockDataUnit &) = delete; BlockDataUnit(const BlockDataUnit &) = delete;
}; };
/// A Program is the top-level PFT /// A Program is the top-level root of the PFT.
struct Program { struct Program {
using Units = std::variant<FunctionLikeUnit, ModuleLikeUnit, BlockDataUnit>; using Units = std::variant<FunctionLikeUnit, ModuleLikeUnit, BlockDataUnit>;
@ -375,23 +467,31 @@ struct Program {
std::list<Units> &getUnits() { return units; } std::list<Units> &getUnits() { return units; }
/// LLVM dump method on a Program.
void dump();
private: private:
std::list<Units> units; std::list<Units> units;
}; };
} // namespace pft } // namespace pft
/// Create an PFT from the parse tree /// Create a PFT (Pre-FIR Tree) from the parse tree.
std::unique_ptr<pft::Program> createPFT(const parser::Program &root);
/// Decorate the PFT with control flow annotations
/// ///
/// The PFT must be decorated with control-flow annotations to prepare it for /// A PFT is a light weight tree over the parse tree that is used to create FIR.
/// use in generating a CFG-like structure. /// The PFT captures pointers back into the parse tree, so the parse tree must
void annotateControl(pft::Program &); /// not be changed between the construction of the PFT and its last use. The
/// PFT captures a structured view of a program. A program is a list of units.
/// A function like unit contains a list of evaluations. An evaluation is
/// either a statement, or a construct with a nested list of evaluations.
std::unique_ptr<pft::Program>
createPFT(const parser::Program &root,
const Fortran::semantics::SemanticsContext &semanticsContext);
void dumpPFT(llvm::raw_ostream &o, pft::Program &); /// Dumper for displaying a PFT.
void dumpPFT(llvm::raw_ostream &outputStream, pft::Program &pft);
} // namespace Fortran::lower } // namespace lower
} // namespace Fortran
#endif // FORTRAN_LOWER_PFT_BUILDER_H_ #endif // FORTRAN_LOWER_PFTBUILDER_H

View file

@ -0,0 +1,31 @@
//===-- Lower/Utils.h -- utilities ------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef FORTRAN_LOWER_UTILS_H
#define FORTRAN_LOWER_UTILS_H
#include "flang/Common/indirection.h"
#include "flang/Parser/char-block.h"
#include "llvm/ADT/StringRef.h"
/// Convert an F18 CharBlock to an LLVM StringRef
inline llvm::StringRef toStringRef(const Fortran::parser::CharBlock &cb) {
return {cb.begin(), cb.size()};
}
/// Template helper to remove Fortran::common::Indirection wrappers.
template <typename A>
const A &removeIndirection(const A &a) {
return a;
}
template <typename A>
const A &removeIndirection(const Fortran::common::Indirection<A> &a) {
return a.value();
}
#endif // FORTRAN_LOWER_UTILS_H

View file

@ -13,6 +13,7 @@
#include "flang/Common/Fortran.h" #include "flang/Common/Fortran.h"
#include "flang/Common/enum-set.h" #include "flang/Common/enum-set.h"
#include "flang/Common/reference.h" #include "flang/Common/reference.h"
#include "llvm/ADT/DenseMapInfo.h"
#include <array> #include <array>
#include <list> #include <list>
#include <optional> #include <optional>
@ -760,4 +761,30 @@ inline bool operator<(MutableSymbolRef x, MutableSymbolRef y) {
using SymbolSet = std::set<SymbolRef>; using SymbolSet = std::set<SymbolRef>;
} // namespace Fortran::semantics } // namespace Fortran::semantics
// Define required info so that SymbolRef can be used inside llvm::DenseMap.
namespace llvm {
template <> struct DenseMapInfo<Fortran::semantics::SymbolRef> {
static inline Fortran::semantics::SymbolRef getEmptyKey() {
auto ptr = DenseMapInfo<const Fortran::semantics::Symbol *>::getEmptyKey();
return *reinterpret_cast<Fortran::semantics::SymbolRef *>(&ptr);
}
static inline Fortran::semantics::SymbolRef getTombstoneKey() {
auto ptr =
DenseMapInfo<const Fortran::semantics::Symbol *>::getTombstoneKey();
return *reinterpret_cast<Fortran::semantics::SymbolRef *>(&ptr);
}
static unsigned getHashValue(const Fortran::semantics::SymbolRef &sym) {
return DenseMapInfo<const Fortran::semantics::Symbol *>::getHashValue(
&sym.get());
}
static bool isEqual(const Fortran::semantics::SymbolRef &LHS,
const Fortran::semantics::SymbolRef &RHS) {
return LHS == RHS;
}
};
} // namespace llvm
#endif // FORTRAN_SEMANTICS_SYMBOL_H_ #endif // FORTRAN_SEMANTICS_SYMBOL_H_

File diff suppressed because it is too large Load diff

View file

@ -16,10 +16,10 @@ subroutine foo()
print *, "hello", i, j print *, "hello", i, j
! CHECK: EndDoStmt ! CHECK: EndDoStmt
end do end do
! CHECK: <<EndDoConstruct>> ! CHECK: <<End DoConstruct>>
! CHECK: EndDoStmt ! CHECK: EndDoStmt
end do end do
! CHECK: <<EndDoConstruct>> ! CHECK: <<End DoConstruct>>
end subroutine end subroutine
! CHECK: EndSubroutine foo ! CHECK: EndSubroutine foo
@ -102,7 +102,7 @@ contains
write (*, 11) "test: ", xdim, pressure write (*, 11) "test: ", xdim, pressure
! CHECK: EndIfStmt ! CHECK: EndIfStmt
end if end if
! CHECK: <<EndIfConstruct>> ! CHECK: <<End IfConstruct>>
end procedure end procedure
end submodule end submodule
! CHECK: EndModuleLike ! CHECK: EndModuleLike

View file

@ -27,10 +27,10 @@ program test_prog
print *, "hello", i, j print *, "hello", i, j
! CHECK: EndDoStmt ! CHECK: EndDoStmt
end do end do
! CHECK: <<EndDoConstruct>> ! CHECK: <<End DoConstruct>>
! CHECK: EndDoStmt ! CHECK: EndDoStmt
end do end do
! CHECK: <<EndDoConstruct>> ! CHECK: <<End DoConstruct>>
! CHECK: <<AssociateConstruct>> ! CHECK: <<AssociateConstruct>>
! CHECK: AssociateStmt ! CHECK: AssociateStmt
@ -39,9 +39,9 @@ program test_prog
allocate(x(k)) allocate(x(k))
! CHECK: EndAssociateStmt ! CHECK: EndAssociateStmt
end associate end associate
! CHECK: <<EndAssociateConstruct>> ! CHECK: <<End AssociateConstruct>>
! CHECK: <<BlockConstruct>> ! CHECK: <<BlockConstruct!>>
! CHECK: BlockStmt ! CHECK: BlockStmt
block block
integer :: k, l integer :: k, l
@ -52,7 +52,7 @@ program test_prog
k = size(p) k = size(p)
! CHECK: AssignmentStmt ! CHECK: AssignmentStmt
l = 1 l = 1
! CHECK: <<CaseConstruct>> ! CHECK: <<CaseConstruct!>>
! CHECK: SelectCaseStmt ! CHECK: SelectCaseStmt
select case (k) select case (k)
! CHECK: CaseStmt ! CHECK: CaseStmt
@ -76,13 +76,13 @@ program test_prog
print *, "-" print *, "-"
! CHECK: EndIfStmt ! CHECK: EndIfStmt
end if end if
! CHECK: <<EndIfConstruct>> ! CHECK: <<End IfConstruct>>
! CHECK: CaseStmt ! CHECK: CaseStmt
case (2:10) case (2:10)
! CHECK: CaseStmt ! CHECK: CaseStmt
case default case default
! Note: label-do-loop are canonicalized into do constructs ! Note: label-do-loop are canonicalized into do constructs
! CHECK: <<DoConstruct>> ! CHECK: <<DoConstruct!>>
! CHECK: NonLabelDoStmt ! CHECK: NonLabelDoStmt
do 22 while(l<=k) do 22 while(l<=k)
! CHECK: IfStmt ! CHECK: IfStmt
@ -90,15 +90,15 @@ program test_prog
! CHECK: CallStmt ! CHECK: CallStmt
22 call incr(l) 22 call incr(l)
! CHECK: EndDoStmt ! CHECK: EndDoStmt
! CHECK: <<EndDoConstruct>> ! CHECK: <<End DoConstruct!>>
! CHECK: CaseStmt ! CHECK: CaseStmt
case (100:) case (100:)
! CHECK: EndSelectStmt ! CHECK: EndSelectStmt
end select end select
! CHECK: <<EndCaseConstruct>> ! CHECK: <<End CaseConstruct!>>
! CHECK: EndBlockStmt ! CHECK: EndBlockStmt
end block end block
! CHECK: <<EndBlockConstruct>> ! CHECK: <<End BlockConstruct!>>
! CHECK-NOT: WhereConstruct ! CHECK-NOT: WhereConstruct
! CHECK: WhereStmt ! CHECK: WhereStmt
@ -118,14 +118,14 @@ program test_prog
! CHECK: AssignmentStmt ! CHECK: AssignmentStmt
y = y/2. y = y/2.
end where end where
! CHECK: <<EndWhereConstruct>> ! CHECK: <<End WhereConstruct>>
! CHECK: ElsewhereStmt ! CHECK: ElsewhereStmt
elsewhere elsewhere
! CHECK: AssignmentStmt ! CHECK: AssignmentStmt
x = x + 1. x = x + 1.
! CHECK: EndWhereStmt ! CHECK: EndWhereStmt
end where end where
! CHECK: <<EndWhereConstruct>> ! CHECK: <<End WhereConstruct>>
! CHECK-NOT: ForAllConstruct ! CHECK-NOT: ForAllConstruct
! CHECK: ForallStmt ! CHECK: ForallStmt
@ -138,7 +138,7 @@ program test_prog
x(i) = x(i) + y(10*i) x(i) = x(i) + y(10*i)
! CHECK: EndForallStmt ! CHECK: EndForallStmt
end forall end forall
! CHECK: <<EndForallConstruct>> ! CHECK: <<End ForallConstruct>>
! CHECK: DeallocateStmt ! CHECK: DeallocateStmt
deallocate(x) deallocate(x)
@ -157,7 +157,7 @@ contains
function foo(x) function foo(x)
real x(..) real x(..)
integer :: foo integer :: foo
! CHECK: <<SelectRankConstruct>> ! CHECK: <<SelectRankConstruct!>>
! CHECK: SelectRankStmt ! CHECK: SelectRankStmt
select rank(x) select rank(x)
! CHECK: SelectRankCaseStmt ! CHECK: SelectRankCaseStmt
@ -178,13 +178,13 @@ contains
foo = 2 foo = 2
! CHECK: EndSelectStmt ! CHECK: EndSelectStmt
end select end select
! CHECK: <<EndSelectRankConstruct>> ! CHECK: <<End SelectRankConstruct!>>
end function end function
! CHECK: Function bar ! CHECK: Function bar
function bar(x) function bar(x)
class(*) :: x class(*) :: x
! CHECK: <<SelectTypeConstruct>> ! CHECK: <<SelectTypeConstruct!>>
! CHECK: SelectTypeStmt ! CHECK: SelectTypeStmt
select type(x) select type(x)
! CHECK: TypeGuardStmt ! CHECK: TypeGuardStmt
@ -203,7 +203,7 @@ contains
bar = -1 bar = -1
! CHECK: EndSelectStmt ! CHECK: EndSelectStmt
end select end select
! CHECK: <<EndSelectTypeConstruct>> ! CHECK: <<End SelectTypeConstruct!>>
end function end function
! CHECK: Subroutine sub ! CHECK: Subroutine sub
@ -219,7 +219,7 @@ end module
! CHECK: Subroutine altreturn ! CHECK: Subroutine altreturn
subroutine altreturn(i, j, *, *) subroutine altreturn(i, j, *, *)
! CHECK: <<IfConstruct>> ! CHECK: <<IfConstruct!>>
if (i>j) then if (i>j) then
! CHECK: ReturnStmt ! CHECK: ReturnStmt
return 1 return 1
@ -227,7 +227,7 @@ subroutine altreturn(i, j, *, *)
! CHECK: ReturnStmt ! CHECK: ReturnStmt
return 2 return 2
end if end if
! CHECK: <<EndIfConstruct>> ! CHECK: <<End IfConstruct!>>
end subroutine end subroutine
@ -246,7 +246,7 @@ subroutine iostmts(filename, a, b, c)
! CHECK: OpenStmt ! CHECK: OpenStmt
open(10, FILE=filename) open(10, FILE=filename)
end if end if
! CHECK: <<EndIfConstruct>> ! CHECK: <<End IfConstruct>>
! CHECK: ReadStmt ! CHECK: ReadStmt
read(10, *) length read(10, *) length
! CHECK: RewindStmt ! CHECK: RewindStmt
@ -297,18 +297,18 @@ subroutine sub2()
5 j = j + 1 5 j = j + 1
6 i = i + j/2 6 i = i + j/2
! CHECK: <<DoConstruct>> ! CHECK: <<DoConstruct!>>
do1: do k=1,10 do1: do k=1,10
! CHECK: <<DoConstruct>> ! CHECK: <<DoConstruct!>>
do2: do l=5,20 do2: do l=5,20
! CHECK: CycleStmt ! CHECK: CycleStmt
cycle do1 cycle do1
! CHECK: ExitStmt ! CHECK: ExitStmt
exit do2 exit do2
end do do2 end do do2
! CHECK: <<EndDoConstruct>> ! CHECK: <<End DoConstruct!>>
end do do1 end do do1
! CHECK: <<EndDoConstruct>> ! CHECK: <<End DoConstruct!>>
! CHECK: PauseStmt ! CHECK: PauseStmt
pause 7 pause 7

View file

@ -20,10 +20,10 @@ program test_omp
print *, "in omp do" print *, "in omp do"
! CHECK: EndDoStmt ! CHECK: EndDoStmt
end do end do
! CHECK: <<EndDoConstruct>> ! CHECK: <<End DoConstruct>>
! CHECK: OmpEndLoopDirective ! CHECK: OmpEndLoopDirective
!$omp end do !$omp end do
! CHECK: <<EndOpenMPConstruct>> ! CHECK: <<End OpenMPConstruct>>
! CHECK: PrintStmt ! CHECK: PrintStmt
print *, "not in omp do" print *, "not in omp do"
@ -37,13 +37,13 @@ program test_omp
print *, "in omp do" print *, "in omp do"
! CHECK: EndDoStmt ! CHECK: EndDoStmt
end do end do
! CHECK: <<EndDoConstruct>> ! CHECK: <<End DoConstruct>>
! CHECK: <<EndOpenMPConstruct>> ! CHECK: <<End OpenMPConstruct>>
! CHECK-NOT: OmpEndLoopDirective ! CHECK-NOT: OmpEndLoopDirective
! CHECK: PrintStmt ! CHECK: PrintStmt
print *, "no in omp do" print *, "no in omp do"
!$omp end parallel !$omp end parallel
! CHECK: <<EndOpenMPConstruct>> ! CHECK: <<End OpenMPConstruct>>
! CHECK: PrintStmt ! CHECK: PrintStmt
print *, "sequential again" print *, "sequential again"
@ -53,7 +53,7 @@ program test_omp
! CHECK: PrintStmt ! CHECK: PrintStmt
print *, "in task" print *, "in task"
!$omp end task !$omp end task
! CHECK: <<EndOpenMPConstruct>> ! CHECK: <<End OpenMPConstruct>>
! CHECK: PrintStmt ! CHECK: PrintStmt
print *, "sequential again" print *, "sequential again"

View file

@ -16,7 +16,7 @@ Subroutine test_coarray
! CHECK: AssignmentStmt ! CHECK: AssignmentStmt
x = x[4, 1] x = x[4, 1]
end team end team
! CHECK: <<EndChangeTeamConstruct>> ! CHECK: <<End ChangeTeamConstruct>>
! CHECK: FormTeamStmt ! CHECK: FormTeamStmt
form team(1, t) form team(1, t)
@ -28,14 +28,14 @@ Subroutine test_coarray
! CHECK: EventWaitStmt ! CHECK: EventWaitStmt
event wait (done) event wait (done)
end if end if
! CHECK: <<EndIfConstruct>> ! CHECK: <<End IfConstruct>>
! CHECK: <<CriticalConstruct>> ! CHECK: <<CriticalConstruct>>
critical critical
! CHECK: AssignmentStmt ! CHECK: AssignmentStmt
counter[1] = counter[1] + 1 counter[1] = counter[1] + 1
end critical end critical
! CHECK: <<EndCriticalConstruct>> ! CHECK: <<End CriticalConstruct>>
! CHECK: LockStmt ! CHECK: LockStmt
lock(alock) lock(alock)
@ -59,12 +59,12 @@ Subroutine test_coarray
! CHECK: SyncImagesStmt ! CHECK: SyncImagesStmt
sync images(1) sync images(1)
end if end if
! CHECK: <<EndIfConstruct>> ! CHECK: <<End IfConstruct>>
! CHECK: <<IfConstruct>> ! CHECK: <<IfConstruct>>
if (y<0.) then if (y<0.) then
! CHECK: FailImageStmt ! CHECK: FailImageStmt
fail image fail image
end if end if
! CHECK: <<EndIfConstruct>> ! CHECK: <<End IfConstruct>>
end end

View file

@ -315,8 +315,7 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options,
return {}; return {};
} }
if (driver.dumpPreFirTree) { if (driver.dumpPreFirTree) {
if (auto ast{Fortran::lower::createPFT(parseTree)}) { if (auto ast{Fortran::lower::createPFT(parseTree, semanticsContext)}) {
Fortran::lower::annotateControl(*ast);
Fortran::lower::dumpPFT(llvm::outs(), *ast); Fortran::lower::dumpPFT(llvm::outs(), *ast);
} else { } else {
llvm::errs() << "Pre FIR Tree is NULL.\n"; llvm::errs() << "Pre FIR Tree is NULL.\n";