[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.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// PFT (Pre-FIR Tree) interface.
//
//===----------------------------------------------------------------------===//
#ifndef FORTRAN_LOWER_PFT_BUILDER_H_
#define FORTRAN_LOWER_PFT_BUILDER_H_
#ifndef FORTRAN_LOWER_PFTBUILDER_H
#define FORTRAN_LOWER_PFTBUILDER_H
#include "flang/Common/reference.h"
#include "flang/Common/template.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.
/// It is named Pre-FIR Tree (PFT) to underline it has no other usage than
/// 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 mlir {
class Block;
}
namespace Fortran::lower {
namespace Fortran {
namespace semantics {
class SemanticsContext;
class Scope;
} // namespace semantics
namespace lower {
namespace pft {
struct Evaluation;
@ -40,40 +40,56 @@ struct FunctionLikeUnit;
// 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
// 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 {
template <typename A>
ParentType(A &parent) : p{&parent} {}
const std::variant<Program *, ModuleLikeUnit *, FunctionLikeUnit *,
Evaluation *>
p;
};
/// Provide a variant like container that can hold references. It can hold
/// constant or mutable references. It is used in the other classes to provide
/// union of const references to parse-tree nodes.
template <bool isConst, typename... A>
class ReferenceVariantBase {
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
/// control flow. These annotations to parse-tree nodes are later used to
/// build the control flow graph when lowering to FIR.
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.
};
ReferenceVariantBase() = delete;
template <typename B>
ReferenceVariantBase(B &b) : u{Ref<B>{b}} {}
/// Compiler-generated jump
///
/// This is used to convert implicit control-flow edges to explicit form in the
/// decorated PFT
struct CGJump {
CGJump(Evaluation &to) : target{to} {}
Evaluation &target;
template <typename B>
constexpr BaseType<B> &get() const {
return std::get<Ref<B>> > (u).get();
}
template <typename B>
constexpr BaseType<B> *getIf() const {
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
@ -95,15 +111,6 @@ using ActionStmts = std::tuple<
using OtherStmts = std::tuple<parser::FormatStmt, parser::EntryStmt,
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<
parser::AssociateStmt, parser::EndAssociateStmt, parser::BlockStmt,
parser::EndBlockStmt, parser::SelectCaseStmt, parser::CaseStmt,
@ -115,257 +122,342 @@ using ConstructStmts = std::tuple<
parser::MaskedElsewhereStmt, parser::ElsewhereStmt, parser::EndWhereStmt,
parser::ForallConstructStmt, parser::EndForallStmt>;
template <typename A>
constexpr static bool isActionStmt{common::HasMember<A, ActionStmts>};
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>;
using Directives =
std::tuple<parser::CompilerDirective, parser::OpenMPConstruct,
parser::OmpEndLoopDirective>;
template <typename A>
constexpr static bool isConstruct{common::HasMember<A, Constructs>};
static constexpr bool isActionStmt{common::HasMember<A, ActionStmts>};
template <typename A>
constexpr static bool isConstructStmt{common::HasMember<A, ConstructStmts>};
static constexpr bool isOtherStmt{common::HasMember<A, OtherStmts>};
template <typename A>
constexpr static bool isOtherStmt{common::HasMember<A, OtherStmts>};
static constexpr bool isConstructStmt{common::HasMember<A, ConstructStmts>};
template <typename A>
constexpr static bool isGenerated{std::is_same_v<A, CGJump>};
static constexpr bool isConstruct{common::HasMember<A, Constructs>};
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,
parser::SubroutineSubprogram,
parser::SeparateModuleSubprogram>>};
/// Function-like units can contains lists of evaluations. These can be
/// (simple) statements or constructs, where a construct contains its own
/// evaluations.
struct Evaluation {
using EvalTuple = common::CombineTuples<ActionStmts, OtherStmts, Constructs,
ConstructStmts>;
using LabelSet = llvm::SmallSet<parser::Label, 5>;
using SymbolRef = common::Reference<const semantics::Symbol>;
using SymbolLabelMap = llvm::DenseMap<SymbolRef, LabelSet>;
/// Hide non-nullable pointers to the parse-tree node.
template <typename A>
using MakeRefType = const A *const;
using EvalVariant =
common::CombineVariants<common::MapTemplate<MakeRefType, EvalTuple>,
std::variant<CGJump>>;
template <typename A>
constexpr auto visit(A visitor) const {
return std::visit(common::visitors{
[&](const auto *p) { return visitor(*p); },
[&](auto &r) { return visitor(r); },
},
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);
}
template <typename A>
struct MakeReferenceVariantHelper {};
template <typename... A>
struct MakeReferenceVariantHelper<std::variant<A...>> {
using type = ReferenceVariant<A...>;
};
template <typename... A>
struct MakeReferenceVariantHelper<std::tuple<A...>> {
using type = ReferenceVariant<A...>;
};
template <typename A>
using MakeReferenceVariant = typename MakeReferenceVariantHelper<A>::type;
Evaluation() = delete;
Evaluation(const Evaluation &) = delete;
Evaluation(Evaluation &&) = default;
using EvaluationTuple =
common::CombineTuples<ActionStmts, OtherStmts, ConstructStmts, Constructs,
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
template <typename A>
Evaluation(const A &a, const ParentType &p, const parser::CharBlock &pos,
const std::optional<parser::Label> &lab)
: u{&a}, parent{p}, pos{pos}, lab{lab} {}
/// Compiler-generated jump
Evaluation(const CGJump &jump, const ParentType &p)
: u{jump}, parent{p}, cfg{CFGAnnotation::Goto} {}
Evaluation(const A &a, const ParentVariant &parentVariant,
const parser::CharBlock &position,
const std::optional<parser::Label> &label)
: EvaluationVariant{a},
parentVariant{parentVariant}, position{position}, label{label} {}
/// Construct ctor
template <typename A>
Evaluation(const A &a, const ParentType &parent) : u{&a}, parent{parent} {
static_assert(pft::isConstruct<A>, "must be a construct");
Evaluation(const A &a, const ParentVariant &parentVariant)
: 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{
[](auto &r) {
using T = std::decay_t<decltype(r)>;
return isActionStmt<T> || isGenerated<T>;
},
});
[](auto &r) { return pft::isActionStmt<std::decay_t<decltype(r)>>; }});
}
constexpr bool isStmt() const {
constexpr bool isOtherStmt() const {
return visit(common::visitors{
[](auto &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;
},
});
[](auto &r) { return pft::isOtherStmt<std::decay_t<decltype(r)>>; }});
}
constexpr bool isConstruct() const { return !isStmt(); }
/// Set the type of originating control flow type for this evaluation.
void setCFG(CFGAnnotation a, Evaluation *cstr) {
cfg = a;
setBranches(cstr);
constexpr bool isConstructStmt() const {
return visit(common::visitors{[](auto &r) {
return pft::isConstructStmt<std::decay_t<decltype(r)>>;
}});
}
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)
bool isControlOrigin() const { return cfg != CFGAnnotation::None; }
/// Return FunctionLikeUnit to which this evaluation
/// 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 isControlTarget() const { return isTarget; }
bool lowerAsStructured() const;
bool lowerAsUnstructured() const;
/// Set the containsBranches flag iff this evaluation (a construct) contains
/// control flow
void setBranches() { containsBranches = true; }
// FIR generation looks primarily at PFT statement (leaf) nodes. So members
// such as lexicalSuccessor and the various block fields are only applicable
// 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() {
auto *evals{subs.get()};
if (isStmt() && !evals) {
return nullptr;
}
if (isConstruct() && evals) {
return evals;
}
llvm_unreachable("evaluation subs is inconsistent");
return nullptr;
}
/// Set that the construct `cstr` (if not a nullptr) has branches.
static void setBranches(Evaluation *cstr) {
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
ParentVariant parentVariant;
parser::CharBlock position{};
std::optional<parser::Label> label{};
std::unique_ptr<EvaluationList> evaluationList; // nested evaluations
Evaluation *parentConstruct{nullptr}; // set for nodes below the top level
Evaluation *lexicalSuccessor{nullptr}; // set for ActionStmt, ConstructStmt
Evaluation *controlSuccessor{nullptr}; // set for some statements
Evaluation *constructExit{nullptr}; // set for constructs
bool isNewBlock{false}; // evaluation begins a new basic block
bool isUnstructured{false}; // evaluation has unstructured control flow
bool skip{false}; // evaluation has been processed in advance
class mlir::Block *block{nullptr}; // isNewBlock block
llvm::SmallVector<mlir::Block *, 1> localBlocks{}; // construct local blocks
int printIndex{0}; // (ActionStmt, ConstructStmt) evaluation index for dumps
};
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.
/// These units can be function like, module like, or block data
struct ProgramUnit {
/// These units can be function like, module like, or block data.
struct ProgramUnit : ProgramVariant {
template <typename A>
ProgramUnit(const A &ptr, const ParentType &parent)
: p{&ptr}, parent{parent} {}
ProgramUnit(const A &p, const ParentVariant &parentVariant)
: ProgramVariant{p}, parentVariant{parentVariant} {}
ProgramUnit(ProgramUnit &&) = default;
ProgramUnit(const ProgramUnit &) = delete;
const std::variant<
const parser::MainProgram *, const parser::FunctionSubprogram *,
const parser::SubroutineSubprogram *, const parser::Module *,
const parser::Submodule *, const parser::SeparateModuleSubprogram *,
const parser::BlockData *>
p;
ParentType parent;
ParentVariant parentVariant;
};
/// Function-like units have similar structure. They all can contain executable
/// statements as well as other function-like units (internal procedures and
/// function statements).
/// A variable captures an object to be created per the declaration part of a
/// function like unit.
///
/// 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 {
// wrapper statements for function-like syntactic structures
using FunctionStatement =
std::variant<const parser::Statement<parser::ProgramStmt> *,
const parser::Statement<parser::EndProgramStmt> *,
const parser::Statement<parser::FunctionStmt> *,
const parser::Statement<parser::EndFunctionStmt> *,
const parser::Statement<parser::SubroutineStmt> *,
const parser::Statement<parser::EndSubroutineStmt> *,
const parser::Statement<parser::MpSubprogramStmt> *,
const parser::Statement<parser::EndMpSubprogramStmt> *>;
ReferenceVariant<parser::Statement<parser::ProgramStmt>,
parser::Statement<parser::EndProgramStmt>,
parser::Statement<parser::FunctionStmt>,
parser::Statement<parser::EndFunctionStmt>,
parser::Statement<parser::SubroutineStmt>,
parser::Statement<parser::EndSubroutineStmt>,
parser::Statement<parser::MpSubprogramStmt>,
parser::Statement<parser::EndMpSubprogramStmt>>;
FunctionLikeUnit(const parser::MainProgram &f, const ParentType &parent);
FunctionLikeUnit(const parser::FunctionSubprogram &f,
const ParentType &parent);
FunctionLikeUnit(const parser::SubroutineSubprogram &f,
const ParentType &parent);
FunctionLikeUnit(const parser::SeparateModuleSubprogram &f,
const ParentType &parent);
FunctionLikeUnit(
const parser::MainProgram &f, const ParentVariant &parentVariant,
const Fortran::semantics::SemanticsContext &semanticsContext);
FunctionLikeUnit(
const parser::FunctionSubprogram &f, const ParentVariant &parentVariant,
const Fortran::semantics::SemanticsContext &semanticsContext);
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(const FunctionLikeUnit &) = delete;
bool isMainProgram() {
return std::holds_alternative<
const parser::Statement<parser::EndProgramStmt> *>(endStmt);
void processSymbolTable(const Fortran::semantics::Scope &);
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
std::optional<FunctionStatement> beginStmt;
FunctionStatement endStmt;
EvaluationCollection evals; // statements
std::list<FunctionLikeUnit> funcs; // internal procedures
private:
template <typename A>
const A *getA() {
if (beginStmt) {
if (auto p =
std::get_if<const parser::Statement<A> *>(&beginStmt.value()))
return &(*p)->statement;
}
return nullptr;
}
EvaluationList evaluationList;
LabelEvalMap labelEvaluationMap;
SymbolLabelMap assignSymbolLabelMap;
std::list<FunctionLikeUnit> nestedFunctions;
/// Symbol associated to this FunctionLikeUnit.
/// Null if the FunctionLikeUnit is an anonymous program.
/// The symbol has MainProgramDetails for named programs, otherwise it has
/// SubprogramDetails.
const semantics::Symbol *symbol{nullptr};
/// Terminal basic block (if any)
mlir::Block *finalBlock{};
std::vector<std::vector<Variable>> varList;
};
/// Module-like units have similar structure. They all can contain a list of
/// function-like units.
/// Module-like units contain a list of function-like units.
struct ModuleLikeUnit : public ProgramUnit {
// wrapper statements for module-like syntactic structures
using ModuleStatement =
std::variant<const parser::Statement<parser::ModuleStmt> *,
const parser::Statement<parser::EndModuleStmt> *,
const parser::Statement<parser::SubmoduleStmt> *,
const parser::Statement<parser::EndSubmoduleStmt> *>;
ReferenceVariant<parser::Statement<parser::ModuleStmt>,
parser::Statement<parser::EndModuleStmt>,
parser::Statement<parser::SubmoduleStmt>,
parser::Statement<parser::EndSubmoduleStmt>>;
ModuleLikeUnit(const parser::Module &m, const ParentType &parent);
ModuleLikeUnit(const parser::Submodule &m, const ParentType &parent);
ModuleLikeUnit(const parser::Module &m, const ParentVariant &parentVariant);
ModuleLikeUnit(const parser::Submodule &m,
const ParentVariant &parentVariant);
~ModuleLikeUnit() = default;
ModuleLikeUnit(ModuleLikeUnit &&) = default;
ModuleLikeUnit(const ModuleLikeUnit &) = delete;
ModuleStatement beginStmt;
ModuleStatement endStmt;
std::list<FunctionLikeUnit> funcs;
std::list<FunctionLikeUnit> nestedFunctions;
};
struct BlockDataUnit : public ProgramUnit {
BlockDataUnit(const parser::BlockData &bd, const ParentType &parent);
BlockDataUnit(const parser::BlockData &bd,
const ParentVariant &parentVariant);
BlockDataUnit(BlockDataUnit &&) = default;
BlockDataUnit(const BlockDataUnit &) = delete;
};
/// A Program is the top-level PFT
/// A Program is the top-level root of the PFT.
struct Program {
using Units = std::variant<FunctionLikeUnit, ModuleLikeUnit, BlockDataUnit>;
@ -375,23 +467,31 @@ struct Program {
std::list<Units> &getUnits() { return units; }
/// LLVM dump method on a Program.
void dump();
private:
std::list<Units> units;
};
} // namespace pft
/// Create an PFT from the parse tree
std::unique_ptr<pft::Program> createPFT(const parser::Program &root);
/// Decorate the PFT with control flow annotations
/// Create a PFT (Pre-FIR Tree) from the parse tree.
///
/// The PFT must be decorated with control-flow annotations to prepare it for
/// use in generating a CFG-like structure.
void annotateControl(pft::Program &);
/// A PFT is a light weight tree over the parse tree that is used to create FIR.
/// The PFT captures pointers back into the parse tree, so the parse tree must
/// 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/enum-set.h"
#include "flang/Common/reference.h"
#include "llvm/ADT/DenseMapInfo.h"
#include <array>
#include <list>
#include <optional>
@ -760,4 +761,30 @@ inline bool operator<(MutableSymbolRef x, MutableSymbolRef y) {
using SymbolSet = std::set<SymbolRef>;
} // 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_

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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