llvm-project/flang/lib/Lower/PFTBuilder.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

698 lines
25 KiB
C++
Raw Normal View History

//===-- lib/Lower/PFTBuilder.cc -------------------------------------------===//
[flang] Add Pre-FIR Tree structure to help lowering the parse-tree The Pre-FIR Tree structure is a transient data structure that is meant to be built from the parse tree just before lowering to FIR and that will be deleted just afterwards. It is not meant to perfrom optimization analysis and transformations. It only provides temporary information, such as label target information or parse tree parent nodes, that is meant to be used to lower the parse tree structure into FIR operations. A PFTBuilder class builds the Pre-Fir Tree from the parse-tree. A pretty printer is available to visualize this data structure. - Lit tests are added to: 1. that the PFT tree structure is as expected 2. that the PFT captures all intented nodes - Cmake changes: Prevent warnings inisde LLVM headers when compiling flang The issue is that some LLVM headers define functions where the usage of the parameters depend on environment ifdef. See for instance Size in: https://github.com/llvm/llvm-project/blob/5f940220bf9438e95ffa4a627ac1591be1e1ba6e/llvm/include/llvm/Support/Compiler.h#L574 Because flang is build with -Werror and -Wunused-parameter is default in clang, this may breaks build in some environments (like with clang9 on macos). A solution would be to add -Wno-unused-parameter to flang CmakLists.txt, but it is wished to keep this warning on flang sources for quality purposes. Fixing LLVM headers is not an easy task and `[[maybe_unused]]` is C++17 and cannot be used yet in LLVM headers. Hence, this fix simply silence warnings coming from LLVM headers by telling CMake they are to be considered as if they were system headers. - drone.io changes: remove llvm 6.0 from clang config in drone.io and link flang with libstdc++ instead of libc++ llvm-dev resolved to llvm-6.0 in clang builds on drone.io. llvm 6.0 too old. LLVM packages are linked with libstdc++ standard library whereas libc++ was used for flang. This caused link time failure when building clang. Change frone.io to build flang with libc++. Note: This commit does not reflect an actual work log, it is a feature based split of the changes done in the FIR experimental branch. The related work log can be found in the commits between: 864898cbe509d032abfe1172ec367dbd3dd92bc1 and 137c23da9c64cf90584cf81fd646053a69e91f63 Other changes come from https://github.com/flang-compiler/f18/pull/959 review. Original-commit: flang-compiler/f18@edb0943bca4b81689f320bda341040bf255d6e2e Reviewed-on: https://github.com/flang-compiler/f18/pull/959
2020-01-28 20:58:30 +08:00
//
// 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
//
//===----------------------------------------------------------------------===//
#include "flang/Lower/PFTBuilder.h"
#include "flang/Parser/dump-parse-tree.h"
#include "flang/Parser/parse-tree-visitor.h"
[flang] Add Pre-FIR Tree structure to help lowering the parse-tree The Pre-FIR Tree structure is a transient data structure that is meant to be built from the parse tree just before lowering to FIR and that will be deleted just afterwards. It is not meant to perfrom optimization analysis and transformations. It only provides temporary information, such as label target information or parse tree parent nodes, that is meant to be used to lower the parse tree structure into FIR operations. A PFTBuilder class builds the Pre-Fir Tree from the parse-tree. A pretty printer is available to visualize this data structure. - Lit tests are added to: 1. that the PFT tree structure is as expected 2. that the PFT captures all intented nodes - Cmake changes: Prevent warnings inisde LLVM headers when compiling flang The issue is that some LLVM headers define functions where the usage of the parameters depend on environment ifdef. See for instance Size in: https://github.com/llvm/llvm-project/blob/5f940220bf9438e95ffa4a627ac1591be1e1ba6e/llvm/include/llvm/Support/Compiler.h#L574 Because flang is build with -Werror and -Wunused-parameter is default in clang, this may breaks build in some environments (like with clang9 on macos). A solution would be to add -Wno-unused-parameter to flang CmakLists.txt, but it is wished to keep this warning on flang sources for quality purposes. Fixing LLVM headers is not an easy task and `[[maybe_unused]]` is C++17 and cannot be used yet in LLVM headers. Hence, this fix simply silence warnings coming from LLVM headers by telling CMake they are to be considered as if they were system headers. - drone.io changes: remove llvm 6.0 from clang config in drone.io and link flang with libstdc++ instead of libc++ llvm-dev resolved to llvm-6.0 in clang builds on drone.io. llvm 6.0 too old. LLVM packages are linked with libstdc++ standard library whereas libc++ was used for flang. This caused link time failure when building clang. Change frone.io to build flang with libc++. Note: This commit does not reflect an actual work log, it is a feature based split of the changes done in the FIR experimental branch. The related work log can be found in the commits between: 864898cbe509d032abfe1172ec367dbd3dd92bc1 and 137c23da9c64cf90584cf81fd646053a69e91f63 Other changes come from https://github.com/flang-compiler/f18/pull/959 review. Original-commit: flang-compiler/f18@edb0943bca4b81689f320bda341040bf255d6e2e Reviewed-on: https://github.com/flang-compiler/f18/pull/959
2020-01-28 20:58:30 +08:00
#include "llvm/ADT/DenseMap.h"
#include <algorithm>
#include <cassert>
#include <utility>
namespace Fortran::lower {
namespace {
/// Helpers to unveil parser node inside parser::Statement<>,
/// parser::UnlabeledStatement, and common::Indirection<>
template <typename A>
struct RemoveIndirectionHelper {
using Type = A;
static constexpr const Type &unwrap(const A &a) { return a; }
};
template <typename A>
struct RemoveIndirectionHelper<common::Indirection<A>> {
using Type = A;
static constexpr const Type &unwrap(const common::Indirection<A> &a) {
return a.value();
}
};
template <typename A>
const auto &removeIndirection(const A &a) {
return RemoveIndirectionHelper<A>::unwrap(a);
}
template <typename A>
struct UnwrapStmt {
static constexpr bool isStmt{false};
};
template <typename A>
struct UnwrapStmt<parser::Statement<A>> {
static constexpr bool isStmt{true};
using Type = typename RemoveIndirectionHelper<A>::Type;
constexpr UnwrapStmt(const parser::Statement<A> &a)
: unwrapped{removeIndirection(a.statement)}, pos{a.source}, lab{a.label} {
}
const Type &unwrapped;
parser::CharBlock pos;
std::optional<parser::Label> lab;
};
template <typename A>
struct UnwrapStmt<parser::UnlabeledStatement<A>> {
static constexpr bool isStmt{true};
using Type = typename RemoveIndirectionHelper<A>::Type;
constexpr UnwrapStmt(const parser::UnlabeledStatement<A> &a)
: unwrapped{removeIndirection(a.statement)}, pos{a.source} {}
const Type &unwrapped;
parser::CharBlock pos;
std::optional<parser::Label> lab;
};
/// The instantiation of a parse tree visitor (Pre and Post) is extremely
/// expensive in terms of compile and link time, so one goal here is to limit
/// the bridge to one such instantiation.
class PFTBuilder {
public:
PFTBuilder() : pgm{new pft::Program}, parents{*pgm.get()} {}
/// Get the result
std::unique_ptr<pft::Program> result() { return std::move(pgm); }
template <typename A>
constexpr bool Pre(const A &a) {
bool visit{true};
if constexpr (pft::isFunctionLike<A>) {
return enterFunc(a);
} else if constexpr (pft::isConstruct<A>) {
return enterConstruct(a);
} else if constexpr (UnwrapStmt<A>::isStmt) {
using T = typename UnwrapStmt<A>::Type;
// Node "a" being visited has one of the following types:
// Statement<T>, Statement<Indirection<T>, UnlabeledStatement<T>,
// or UnlabeledStatement<Indirection<T>>
auto stmt{UnwrapStmt<A>(a)};
if constexpr (pft::isConstructStmt<T> || pft::isOtherStmt<T>) {
addEval(pft::Evaluation{stmt.unwrapped, parents.back(), stmt.pos,
stmt.lab});
visit = false;
} else if constexpr (std::is_same_v<T, parser::ActionStmt>) {
addEval(makeEvalAction(stmt.unwrapped, stmt.pos, stmt.lab));
visit = false;
}
}
return visit;
}
template <typename A>
constexpr void Post(const A &) {
if constexpr (pft::isFunctionLike<A>) {
exitFunc();
} else if constexpr (pft::isConstruct<A>) {
exitConstruct();
}
}
// Module like
bool Pre(const parser::Module &node) { return enterModule(node); }
bool Pre(const parser::Submodule &node) { return enterModule(node); }
void Post(const parser::Module &) { exitModule(); }
void Post(const parser::Submodule &) { exitModule(); }
// Block data
bool Pre(const parser::BlockData &node) {
addUnit(pft::BlockDataUnit{node, parents.back()});
return false;
}
// Get rid of production wrapper
bool Pre(const parser::UnlabeledStatement<parser::ForallAssignmentStmt>
&statement) {
addEval(std::visit(
[&](const auto &x) {
return pft::Evaluation{x, parents.back(), statement.source, {}};
},
statement.statement.u));
return false;
}
bool Pre(const parser::Statement<parser::ForallAssignmentStmt> &statement) {
addEval(std::visit(
[&](const auto &x) {
return pft::Evaluation{x, parents.back(), statement.source,
statement.label};
},
statement.statement.u));
return false;
}
bool Pre(const parser::WhereBodyConstruct &whereBody) {
return std::visit(
common::visitors{
[&](const parser::Statement<parser::AssignmentStmt> &stmt) {
// Not caught as other AssignmentStmt because it is not
// wrapped in a parser::ActionStmt.
addEval(pft::Evaluation{stmt.statement, parents.back(),
stmt.source, stmt.label});
return false;
},
[&](const auto &) { return true; },
},
whereBody.u);
}
private:
// ActionStmt has a couple of non-conforming cases, which get handled
// explicitly here. The other cases use an Indirection, which we discard in
// the PFT.
pft::Evaluation makeEvalAction(const parser::ActionStmt &statement,
parser::CharBlock pos,
std::optional<parser::Label> lab) {
return std::visit(
common::visitors{
[&](const auto &x) {
return pft::Evaluation{removeIndirection(x), parents.back(), pos,
lab};
},
},
statement.u);
}
// When we enter a function-like structure, we want to build a new unit and
// set the builder's cursors to point to it.
template <typename A>
bool enterFunc(const A &func) {
auto &unit = addFunc(pft::FunctionLikeUnit{func, parents.back()});
funclist = &unit.funcs;
pushEval(&unit.evals);
parents.emplace_back(unit);
return true;
}
/// Make funclist to point to current parent function list if it exists.
void setFunctListToParentFuncs() {
if (!parents.empty()) {
std::visit(common::visitors{
[&](pft::FunctionLikeUnit *p) { funclist = &p->funcs; },
[&](pft::ModuleLikeUnit *p) { funclist = &p->funcs; },
[&](auto *) { funclist = nullptr; },
},
parents.back().p);
}
}
void exitFunc() {
popEval();
parents.pop_back();
setFunctListToParentFuncs();
}
// When we enter a construct structure, we want to build a new construct and
// set the builder's evaluation cursor to point to it.
template <typename A>
bool enterConstruct(const A &construct) {
auto &con = addEval(pft::Evaluation{construct, parents.back()});
con.subs.reset(new pft::EvaluationCollection);
pushEval(con.subs.get());
parents.emplace_back(con);
return true;
}
void exitConstruct() {
popEval();
parents.pop_back();
}
// When we enter a module structure, we want to build a new module and
// set the builder's function cursor to point to it.
template <typename A>
bool enterModule(const A &func) {
auto &unit = addUnit(pft::ModuleLikeUnit{func, parents.back()});
funclist = &unit.funcs;
parents.emplace_back(unit);
return true;
}
void exitModule() {
parents.pop_back();
setFunctListToParentFuncs();
}
template <typename A>
A &addUnit(A &&unit) {
pgm->getUnits().emplace_back(std::move(unit));
return std::get<A>(pgm->getUnits().back());
}
template <typename A>
A &addFunc(A &&func) {
if (funclist) {
funclist->emplace_back(std::move(func));
return funclist->back();
}
return addUnit(std::move(func));
}
/// move the Evaluation to the end of the current list
pft::Evaluation &addEval(pft::Evaluation &&eval) {
assert(funclist && "not in a function");
assert(evallist.size() > 0);
evallist.back()->emplace_back(std::move(eval));
return evallist.back()->back();
}
/// push a new list on the stack of Evaluation lists
void pushEval(pft::EvaluationCollection *eval) {
assert(funclist && "not in a function");
assert(eval && eval->empty() && "evaluation list isn't correct");
evallist.emplace_back(eval);
}
/// pop the current list and return to the last Evaluation list
void popEval() {
assert(funclist && "not in a function");
evallist.pop_back();
}
std::unique_ptr<pft::Program> pgm;
/// funclist points to FunctionLikeUnit::funcs list (resp.
/// ModuleLikeUnit::funcs) when building a FunctionLikeUnit (resp.
/// ModuleLikeUnit) to store internal procedures (resp. module procedures).
/// Otherwise (e.g. when building the top level Program), it is null.
std::list<pft::FunctionLikeUnit> *funclist{nullptr};
/// evallist is a stack of pointer to FunctionLikeUnit::evals (or
/// Evaluation::subs) that are being build.
std::vector<pft::EvaluationCollection *> evallist;
std::vector<pft::ParentType> parents;
};
template <typename Label, typename A>
constexpr bool hasLabel(const A &stmt) {
auto isLabel{
[](const auto &v) { return std::holds_alternative<Label>(v.u); }};
if constexpr (std::is_same_v<A, parser::ReadStmt> ||
std::is_same_v<A, parser::WriteStmt>) {
return std::any_of(std::begin(stmt.controls), std::end(stmt.controls),
isLabel);
}
if constexpr (std::is_same_v<A, parser::WaitStmt>) {
return std::any_of(std::begin(stmt.v), std::end(stmt.v), isLabel);
}
if constexpr (std::is_same_v<Label, parser::ErrLabel>) {
if constexpr (common::HasMember<
A, std::tuple<parser::OpenStmt, parser::CloseStmt,
parser::BackspaceStmt, parser::EndfileStmt,
parser::RewindStmt, parser::FlushStmt>>)
return std::any_of(std::begin(stmt.v), std::end(stmt.v), isLabel);
if constexpr (std::is_same_v<A, parser::InquireStmt>) {
const auto &specifiers{std::get<std::list<parser::InquireSpec>>(stmt.u)};
return std::any_of(std::begin(specifiers), std::end(specifiers), isLabel);
}
}
return false;
}
bool hasAltReturns(const parser::CallStmt &callStmt) {
const auto &args{std::get<std::list<parser::ActualArgSpec>>(callStmt.v.t)};
for (const auto &arg : args) {
const auto &actual{std::get<parser::ActualArg>(arg.t)};
if (std::holds_alternative<parser::AltReturnSpec>(actual.u))
return true;
}
return false;
}
/// Determine if `callStmt` has alternate returns and if so set `e` to be the
/// origin of a switch-like control flow
///
/// \param cstr points to the current construct. It may be null at the top-level
/// of a FunctionLikeUnit.
void altRet(pft::Evaluation &evaluation, const parser::CallStmt &callStmt,
pft::Evaluation *cstr) {
if (hasAltReturns(callStmt))
evaluation.setCFG(pft::CFGAnnotation::Switch, cstr);
}
/// \param cstr points to the current construct. It may be null at the top-level
/// of a FunctionLikeUnit.
void annotateEvalListCFG(pft::EvaluationCollection &evaluationCollection,
pft::Evaluation *cstr) {
bool nextIsTarget = false;
for (auto &eval : evaluationCollection) {
eval.isTarget = nextIsTarget;
nextIsTarget = false;
if (auto *subs{eval.getConstructEvals()}) {
annotateEvalListCFG(*subs, &eval);
// assume that the entry and exit are both possible branch targets
nextIsTarget = true;
}
if (eval.isActionOrGenerated() && eval.lab.has_value())
eval.isTarget = true;
eval.visit(common::visitors{
[&](const parser::CallStmt &statement) {
altRet(eval, statement, cstr);
},
[&](const parser::CycleStmt &) {
eval.setCFG(pft::CFGAnnotation::Goto, cstr);
},
[&](const parser::ExitStmt &) {
eval.setCFG(pft::CFGAnnotation::Goto, cstr);
},
[&](const parser::FailImageStmt &) {
eval.setCFG(pft::CFGAnnotation::Terminate, cstr);
},
[&](const parser::GotoStmt &) {
eval.setCFG(pft::CFGAnnotation::Goto, cstr);
},
[&](const parser::IfStmt &) {
eval.setCFG(pft::CFGAnnotation::CondGoto, cstr);
},
[&](const parser::ReturnStmt &) {
eval.setCFG(pft::CFGAnnotation::Return, cstr);
},
[&](const parser::StopStmt &) {
eval.setCFG(pft::CFGAnnotation::Terminate, cstr);
},
[&](const parser::ArithmeticIfStmt &) {
eval.setCFG(pft::CFGAnnotation::Switch, cstr);
},
[&](const parser::AssignedGotoStmt &) {
eval.setCFG(pft::CFGAnnotation::IndGoto, cstr);
},
[&](const parser::ComputedGotoStmt &) {
eval.setCFG(pft::CFGAnnotation::Switch, cstr);
},
[&](const parser::WhereStmt &) {
// fir.loop + fir.where around the next stmt
eval.isTarget = true;
eval.setCFG(pft::CFGAnnotation::Iterative, cstr);
},
[&](const parser::ForallStmt &) {
// fir.loop around the next stmt
eval.isTarget = true;
eval.setCFG(pft::CFGAnnotation::Iterative, cstr);
},
[&](pft::CGJump &) { eval.setCFG(pft::CFGAnnotation::Goto, cstr); },
[&](const parser::SelectCaseStmt &) {
eval.setCFG(pft::CFGAnnotation::Switch, cstr);
},
[&](const parser::NonLabelDoStmt &) {
eval.isTarget = true;
eval.setCFG(pft::CFGAnnotation::Iterative, cstr);
},
[&](const parser::EndDoStmt &) {
eval.isTarget = true;
eval.setCFG(pft::CFGAnnotation::Goto, cstr);
},
[&](const parser::IfThenStmt &) {
eval.setCFG(pft::CFGAnnotation::CondGoto, cstr);
},
[&](const parser::ElseIfStmt &) {
eval.setCFG(pft::CFGAnnotation::CondGoto, cstr);
},
[&](const parser::SelectRankStmt &) {
eval.setCFG(pft::CFGAnnotation::Switch, cstr);
},
[&](const parser::SelectTypeStmt &) {
eval.setCFG(pft::CFGAnnotation::Switch, cstr);
},
[&](const parser::WhereConstruct &) {
// mark the WHERE as if it were a DO loop
eval.isTarget = true;
eval.setCFG(pft::CFGAnnotation::Iterative, cstr);
},
[&](const parser::WhereConstructStmt &) {
eval.setCFG(pft::CFGAnnotation::CondGoto, cstr);
},
[&](const parser::MaskedElsewhereStmt &) {
eval.isTarget = true;
eval.setCFG(pft::CFGAnnotation::CondGoto, cstr);
},
[&](const parser::ForallConstructStmt &) {
eval.isTarget = true;
eval.setCFG(pft::CFGAnnotation::Iterative, cstr);
},
[&](const auto &stmt) {
// Handle statements with similar impact on control flow
using IoStmts = std::tuple<parser::BackspaceStmt, parser::CloseStmt,
parser::EndfileStmt, parser::FlushStmt,
parser::InquireStmt, parser::OpenStmt,
parser::ReadStmt, parser::RewindStmt,
parser::WaitStmt, parser::WriteStmt>;
using TargetStmts =
std::tuple<parser::EndAssociateStmt, parser::EndBlockStmt,
parser::CaseStmt, parser::EndSelectStmt,
parser::EndChangeTeamStmt, parser::EndCriticalStmt,
parser::ElseStmt, parser::EndIfStmt,
parser::SelectRankCaseStmt, parser::TypeGuardStmt,
parser::ElsewhereStmt, parser::EndWhereStmt,
parser::EndForallStmt>;
using DoNothingConstructStmts =
std::tuple<parser::BlockStmt, parser::AssociateStmt,
parser::CriticalStmt, parser::ChangeTeamStmt>;
using A = std::decay_t<decltype(stmt)>;
if constexpr (common::HasMember<A, IoStmts>) {
if (hasLabel<parser::ErrLabel>(stmt) ||
hasLabel<parser::EorLabel>(stmt) ||
hasLabel<parser::EndLabel>(stmt))
eval.setCFG(pft::CFGAnnotation::IoSwitch, cstr);
} else if constexpr (common::HasMember<A, TargetStmts>) {
eval.isTarget = true;
} else if constexpr (common::HasMember<A, DoNothingConstructStmts>) {
// Explicitly do nothing for these construct statements
} else {
static_assert(!pft::isConstructStmt<A>,
"All ConstructStmts impact on the control flow "
"should be explicitly handled");
}
/* else do nothing */
},
});
}
}
/// Annotate the PFT with CFG source decorations (see CFGAnnotation) and mark
/// potential branch targets
inline void annotateFuncCFG(pft::FunctionLikeUnit &functionLikeUnit) {
annotateEvalListCFG(functionLikeUnit.evals, nullptr);
for (auto &internalFunc : functionLikeUnit.funcs)
annotateFuncCFG(internalFunc);
}
class PFTDumper {
public:
void dumpPFT(llvm::raw_ostream &outputStream, pft::Program &pft) {
for (auto &unit : pft.getUnits()) {
std::visit(common::visitors{
[&](pft::BlockDataUnit &unit) {
outputStream << getNodeIndex(unit) << " ";
outputStream << "BlockData: ";
outputStream << "\nEndBlockData\n\n";
},
[&](pft::FunctionLikeUnit &func) {
dumpFunctionLikeUnit(outputStream, func);
},
[&](pft::ModuleLikeUnit &unit) {
dumpModuleLikeUnit(outputStream, unit);
},
},
unit);
}
resetIndexes();
}
llvm::StringRef evalName(pft::Evaluation &eval) {
return eval.visit(common::visitors{
[](const pft::CGJump) { return "CGJump"; },
[](const auto &parseTreeNode) {
return parser::ParseTreeDumper::GetNodeName(parseTreeNode);
},
});
}
void dumpEvalList(llvm::raw_ostream &outputStream,
pft::EvaluationCollection &evaluationCollection,
int indent = 1) {
static const std::string white{" ++"};
std::string indentString{white.substr(0, indent * 2)};
for (pft::Evaluation &eval : evaluationCollection) {
outputStream << indentString << getNodeIndex(eval) << " ";
llvm::StringRef name{evalName(eval)};
if (auto *subs{eval.getConstructEvals()}) {
outputStream << "<<" << name << ">>";
outputStream << "\n";
dumpEvalList(outputStream, *subs, indent + 1);
outputStream << indentString << "<<End" << name << ">>\n";
} else {
outputStream << name;
outputStream << ": " << eval.pos.ToString() + "\n";
}
}
}
void dumpFunctionLikeUnit(llvm::raw_ostream &outputStream,
pft::FunctionLikeUnit &functionLikeUnit) {
outputStream << getNodeIndex(functionLikeUnit) << " ";
llvm::StringRef unitKind{};
std::string name{};
std::string header{};
if (functionLikeUnit.beginStmt) {
std::visit(
common::visitors{
[&](const parser::Statement<parser::ProgramStmt> *statement) {
unitKind = "Program";
name = statement->statement.v.ToString();
},
[&](const parser::Statement<parser::FunctionStmt> *statement) {
unitKind = "Function";
name =
std::get<parser::Name>(statement->statement.t).ToString();
header = statement->source.ToString();
},
[&](const parser::Statement<parser::SubroutineStmt> *statement) {
unitKind = "Subroutine";
name =
std::get<parser::Name>(statement->statement.t).ToString();
header = statement->source.ToString();
},
[&](const parser::Statement<parser::MpSubprogramStmt>
*statement) {
unitKind = "MpSubprogram";
name = statement->statement.v.ToString();
header = statement->source.ToString();
},
[&](auto *) {},
},
*functionLikeUnit.beginStmt);
} else {
unitKind = "Program";
name = "<anonymous>";
}
outputStream << unitKind << ' ' << name;
if (header.size())
outputStream << ": " << header;
outputStream << '\n';
dumpEvalList(outputStream, functionLikeUnit.evals);
if (!functionLikeUnit.funcs.empty()) {
outputStream << "\nContains\n";
for (auto &func : functionLikeUnit.funcs)
dumpFunctionLikeUnit(outputStream, func);
outputStream << "EndContains\n";
}
outputStream << "End" << unitKind << ' ' << name << "\n\n";
}
void dumpModuleLikeUnit(llvm::raw_ostream &outputStream,
pft::ModuleLikeUnit &moduleLikeUnit) {
outputStream << getNodeIndex(moduleLikeUnit) << " ";
outputStream << "ModuleLike: ";
outputStream << "\nContains\n";
for (auto &func : moduleLikeUnit.funcs)
dumpFunctionLikeUnit(outputStream, func);
outputStream << "EndContains\nEndModuleLike\n\n";
}
template <typename T>
std::size_t getNodeIndex(const T &node) {
auto addr{static_cast<const void *>(&node)};
auto it{nodeIndexes.find(addr)};
if (it != nodeIndexes.end()) {
return it->second;
}
nodeIndexes.try_emplace(addr, nextIndex);
return nextIndex++;
}
std::size_t getNodeIndex(const pft::Program &) { return 0; }
void resetIndexes() {
nodeIndexes.clear();
nextIndex = 1;
}
private:
llvm::DenseMap<const void *, std::size_t> nodeIndexes;
std::size_t nextIndex{1}; // 0 is the root
};
template <typename A, typename T>
pft::FunctionLikeUnit::FunctionStatement getFunctionStmt(const T &func) {
return pft::FunctionLikeUnit::FunctionStatement{
&std::get<parser::Statement<A>>(func.t)};
}
template <typename A, typename T>
pft::ModuleLikeUnit::ModuleStatement getModuleStmt(const T &mod) {
return pft::ModuleLikeUnit::ModuleStatement{
&std::get<parser::Statement<A>>(mod.t)};
}
} // namespace
pft::FunctionLikeUnit::FunctionLikeUnit(const parser::MainProgram &func,
const pft::ParentType &parent)
: ProgramUnit{func, parent} {
auto &ps{
std::get<std::optional<parser::Statement<parser::ProgramStmt>>>(func.t)};
if (ps.has_value()) {
const parser::Statement<parser::ProgramStmt> &statement{ps.value()};
beginStmt = &statement;
}
endStmt = getFunctionStmt<parser::EndProgramStmt>(func);
}
pft::FunctionLikeUnit::FunctionLikeUnit(const parser::FunctionSubprogram &func,
const pft::ParentType &parent)
: ProgramUnit{func, parent},
beginStmt{getFunctionStmt<parser::FunctionStmt>(func)},
endStmt{getFunctionStmt<parser::EndFunctionStmt>(func)} {}
pft::FunctionLikeUnit::FunctionLikeUnit(
const parser::SubroutineSubprogram &func, const pft::ParentType &parent)
: ProgramUnit{func, parent},
beginStmt{getFunctionStmt<parser::SubroutineStmt>(func)},
endStmt{getFunctionStmt<parser::EndSubroutineStmt>(func)} {}
pft::FunctionLikeUnit::FunctionLikeUnit(
const parser::SeparateModuleSubprogram &func, const pft::ParentType &parent)
: ProgramUnit{func, parent},
beginStmt{getFunctionStmt<parser::MpSubprogramStmt>(func)},
endStmt{getFunctionStmt<parser::EndMpSubprogramStmt>(func)} {}
pft::ModuleLikeUnit::ModuleLikeUnit(const parser::Module &m,
const pft::ParentType &parent)
: ProgramUnit{m, parent}, beginStmt{getModuleStmt<parser::ModuleStmt>(m)},
endStmt{getModuleStmt<parser::EndModuleStmt>(m)} {}
pft::ModuleLikeUnit::ModuleLikeUnit(const parser::Submodule &m,
const pft::ParentType &parent)
: ProgramUnit{m, parent}, beginStmt{getModuleStmt<parser::SubmoduleStmt>(
m)},
endStmt{getModuleStmt<parser::EndSubmoduleStmt>(m)} {}
pft::BlockDataUnit::BlockDataUnit(const parser::BlockData &bd,
const pft::ParentType &parent)
: ProgramUnit{bd, parent} {}
std::unique_ptr<pft::Program> createPFT(const parser::Program &root) {
PFTBuilder walker;
Walk(root, walker);
return walker.result();
}
void annotateControl(pft::Program &pft) {
for (auto &unit : pft.getUnits()) {
std::visit(common::visitors{
[](pft::BlockDataUnit &) {},
[](pft::FunctionLikeUnit &func) { annotateFuncCFG(func); },
[](pft::ModuleLikeUnit &unit) {
for (auto &func : unit.funcs)
annotateFuncCFG(func);
},
},
unit);
}
}
/// Dump a PFT.
void dumpPFT(llvm::raw_ostream &outputStream, pft::Program &pft) {
PFTDumper{}.dumpPFT(outputStream, pft);
}
} // namespace Fortran::lower