From 9e98210438c46b0f0d13b6a659463f1cfe7d4569 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Tue, 19 Mar 2019 16:29:43 -0700 Subject: [PATCH] [flang] refactor linear representation -> flattened Original-commit: flang-compiler/f18@4253e2484f15657bff75de5c22947be0c567645a Reviewed-on: https://github.com/flang-compiler/f18/pull/354 Tree-same-pre-rewrite: false --- flang/lib/FIR/CMakeLists.txt | 1 + flang/lib/FIR/afforestation.cc | 890 +++-------------------------------------- flang/lib/FIR/common.h | 4 + flang/lib/FIR/flattened.cc | 853 +++++++++++++++++++++++++++++++++++++++ flang/lib/FIR/flattened.h | 256 ++++++++++++ 5 files changed, 1178 insertions(+), 826 deletions(-) create mode 100644 flang/lib/FIR/flattened.cc create mode 100644 flang/lib/FIR/flattened.h diff --git a/flang/lib/FIR/CMakeLists.txt b/flang/lib/FIR/CMakeLists.txt index 18b360e..5f133db 100644 --- a/flang/lib/FIR/CMakeLists.txt +++ b/flang/lib/FIR/CMakeLists.txt @@ -17,6 +17,7 @@ add_library(FortranFIR afforestation.cc basicblock.cc builder.cc + flattened.cc graph-writer.cc procedure.cc program.cc diff --git a/flang/lib/FIR/afforestation.cc b/flang/lib/FIR/afforestation.cc index f30262f..8ba25cf 100644 --- a/flang/lib/FIR/afforestation.cc +++ b/flang/lib/FIR/afforestation.cc @@ -14,6 +14,7 @@ #include "afforestation.h" #include "builder.h" +#include "flattened.h" #include "mixin.h" #include "../evaluate/fold.h" #include "../evaluate/tools.h" @@ -21,7 +22,6 @@ #include "../semantics/expression.h" #include "llvm/ADT/SmallSet.h" #include "llvm/Support/FileSystem.h" -#include "llvm/Support/raw_ostream.h" namespace Fortran::FIR { namespace { @@ -30,489 +30,6 @@ Expression *ExprRef(const common::Indirection &a) { return a.value().typedExpr.get()->v; } -struct LinearOp; - -using LinearLabelRef = unsigned; -constexpr LinearLabelRef unspecifiedLabel{~0u}; - -llvm::raw_ostream *debugChannel; -llvm::raw_ostream &DebugChannel() { - return debugChannel ? *debugChannel : llvm::errs(); -} -void SetDebugChannel(llvm::raw_ostream *output) { debugChannel = output; } - -struct LinearLabelBuilder { - LinearLabelBuilder() : referenced(32), counter{0u} {} - LinearLabelRef getNext() { - LinearLabelRef next{counter++}; - auto cap{referenced.capacity()}; - if (cap < counter) { - referenced.reserve(2 * cap); - } - referenced[next] = false; - return next; - } - void setReferenced(LinearLabelRef label) { referenced[label] = true; } - bool isReferenced(LinearLabelRef label) const { return referenced[label]; } - std::vector referenced; - unsigned counter; -}; - -struct LinearLabel { - explicit LinearLabel(LinearLabelBuilder &builder) - : builder_{builder}, label_{builder.getNext()} {} - LinearLabel(const LinearLabel &that) - : builder_{that.builder_}, label_{that.label_} {} - LinearLabel &operator=(const LinearLabel &that) { - CHECK(&builder_ == &that.builder_); - label_ = that.label_; - return *this; - } - void setReferenced() const { builder_.setReferenced(label_); } - bool isReferenced() const { return builder_.isReferenced(label_); } - LinearLabelRef get() const { return label_; } - operator LinearLabelRef() const { return get(); } - -private: - LinearLabelBuilder &builder_; - LinearLabelRef label_; -}; - -struct LinearGoto { - struct LinearArtificial {}; - LinearGoto(LinearLabelRef dest) : u{LinearArtificial{}}, target{dest} {} - template - LinearGoto(const T &stmt, LinearLabelRef dest) : u{&stmt}, target{dest} {} - std::variant - u; - LinearLabelRef target; -}; - -struct LinearReturn - : public SumTypeCopyMixin { - SUM_TYPE_COPY_MIXIN(LinearReturn) - template LinearReturn(const T &stmt) : SumTypeCopyMixin{&stmt} {} -}; - -struct LinearConditionalGoto { - template - LinearConditionalGoto(const T &cond, LinearLabelRef tb, LinearLabelRef fb) - : u{&cond}, trueLabel{tb}, falseLabel{fb} {} - std::variant *, - const parser::Statement *, const parser::IfStmt *, - const parser::Statement *> - u; - LinearLabelRef trueLabel; - LinearLabelRef falseLabel; -}; - -struct LinearIndirectGoto { - LinearIndirectGoto( - const semantics::Symbol *symbol, std::vector &&labelRefs) - : symbol{symbol}, labelRefs{labelRefs} {} - const semantics::Symbol *symbol; - std::vector labelRefs; -}; - -struct LinearSwitchingIO { - template - LinearSwitchingIO(const T &io, LinearLabelRef next, - std::optional errLab, - std::optional eorLab = std::nullopt, - std::optional endLab = std::nullopt) - : u{&io}, next{next}, errLabel{errLab}, eorLabel{eorLab}, endLabel{endLab} { - } - std::variant - u; - LinearLabelRef next; - std::optional errLabel; - std::optional eorLabel; - std::optional endLabel; -}; - -struct LinearSwitch { - template - LinearSwitch(const T &sw, const std::vector &refs) - : u{&sw}, refs{refs} {} - std::variant - u; - const std::vector refs; -}; - -struct LinearAction { - LinearAction(const parser::Statement &stmt) : v{&stmt} {} - parser::CharBlock getSource() const { return v->source; } - - const parser::Statement *v; -}; - -#define WRAP(T) const parser::T * -#define CONSTRUCT_TYPES \ - WRAP(AssociateConstruct), WRAP(BlockConstruct), WRAP(CaseConstruct), \ - WRAP(ChangeTeamConstruct), WRAP(CriticalConstruct), WRAP(DoConstruct), \ - WRAP(IfConstruct), WRAP(SelectRankConstruct), WRAP(SelectTypeConstruct), \ - WRAP(WhereConstruct), WRAP(ForallConstruct), WRAP(CompilerDirective), \ - WRAP(OpenMPConstruct), WRAP(OpenMPEndLoopDirective) - -struct LinearBeginConstruct : public SumTypeCopyMixin { - SUM_TYPE_COPY_MIXIN(LinearBeginConstruct) - template - LinearBeginConstruct(const T &c) : SumTypeCopyMixin{&c} {} -}; -struct LinearEndConstruct : public SumTypeCopyMixin { - SUM_TYPE_COPY_MIXIN(LinearEndConstruct) - template LinearEndConstruct(const T &c) : SumTypeCopyMixin{&c} {} -}; - -struct LinearDoIncrement { - LinearDoIncrement(const parser::DoConstruct &stmt) : v{&stmt} {} - const parser::DoConstruct *v; -}; -struct LinearDoCompare { - LinearDoCompare(const parser::DoConstruct &stmt) : v{&stmt} {} - const parser::DoConstruct *v; -}; - -template -const char *GetConstructName(const CONSTRUCT &construct) { - return std::visit( - common::visitors{ - [](const parser::AssociateConstruct *) { return "ASSOCIATE"; }, - [](const parser::BlockConstruct *) { return "BLOCK"; }, - [](const parser::CaseConstruct *) { return "SELECT CASE"; }, - [](const parser::ChangeTeamConstruct *) { return "CHANGE TEAM"; }, - [](const parser::CriticalConstruct *) { return "CRITICAL"; }, - [](const parser::DoConstruct *) { return "DO"; }, - [](const parser::IfConstruct *) { return "IF"; }, - [](const parser::SelectRankConstruct *) { return "SELECT RANK"; }, - [](const parser::SelectTypeConstruct *) { return "SELECT TYPE"; }, - [](const parser::WhereConstruct *) { return "WHERE"; }, - [](const parser::ForallConstruct *) { return "FORALL"; }, - [](const parser::CompilerDirective *) { return ""; }, - [](const parser::OpenMPConstruct *) { return ""; }, - [](const parser::OpenMPEndLoopDirective *) { - return ""; - }}, - construct.u); -} - -struct AnalysisData { - std::map labelMap; - std::vector> - nameStack; - LinearLabelBuilder labelBuilder; - std::map> assignMap; -}; - -void AddAssign(AnalysisData &ad, const semantics::Symbol *symbol, - const parser::Label &label) { - ad.assignMap[symbol].insert(label); -} -std::vector GetAssign( - AnalysisData &ad, const semantics::Symbol *symbol) { - std::vector result; - for (auto lab : ad.assignMap[symbol]) { - result.emplace_back(lab); - } - return result; -} -LinearLabel BuildNewLabel(AnalysisData &ad) { - return LinearLabel{ad.labelBuilder}; -} -LinearLabel FetchLabel(AnalysisData &ad, const parser::Label &label) { - auto iter{ad.labelMap.find(label)}; - if (iter == ad.labelMap.end()) { - LinearLabel ll{ad.labelBuilder}; - ll.setReferenced(); - ad.labelMap.insert({label, ll}); - return ll; - } - return iter->second; -} -std::tuple FindStack( - const std::vector> &stack, - const parser::Name *key) { - for (auto iter{stack.rbegin()}, iend{stack.rend()}; iter != iend; ++iter) { - if (std::get<0>(*iter) == key) return *iter; - } - SEMANTICS_FAILED("construct name not on stack"); - return {}; -} - -template parser::Label GetErr(const T &stmt) { - if constexpr (std::is_same_v || - std::is_same_v) { - for (const auto &control : stmt.controls) { - if (std::holds_alternative(control.u)) { - return std::get(control.u).v; - } - } - } - if constexpr (std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v) { - for (const auto &spec : stmt.v) { - if (std::holds_alternative(spec.u)) { - return std::get(spec.u).v; - } - } - } - if constexpr (std::is_same_v) { - for (const auto &spec : std::get>(stmt.u)) { - if (std::holds_alternative(spec.u)) { - return std::get(spec.u).v; - } - } - } - return 0; -} - -template parser::Label GetEor(const T &stmt) { - if constexpr (std::is_same_v || - std::is_same_v) { - for (const auto &control : stmt.controls) { - if (std::holds_alternative(control.u)) { - return std::get(control.u).v; - } - } - } - if constexpr (std::is_same_v) { - for (const auto &waitSpec : stmt.v) { - if (std::holds_alternative(waitSpec.u)) { - return std::get(waitSpec.u).v; - } - } - } - return 0; -} - -template parser::Label GetEnd(const T &stmt) { - if constexpr (std::is_same_v || - std::is_same_v) { - for (const auto &control : stmt.controls) { - if (std::holds_alternative(control.u)) { - return std::get(control.u).v; - } - } - } - if constexpr (std::is_same_v) { - for (const auto &waitSpec : stmt.v) { - if (std::holds_alternative(waitSpec.u)) { - return std::get(waitSpec.u).v; - } - } - } - return 0; -} - -template -void errLabelSpec(const T &s, std::list &ops, - const parser::Statement &ec, AnalysisData &ad) { - if (auto errLab{GetErr(s)}) { - std::optional errRef{FetchLabel(ad, errLab).get()}; - LinearLabel next{BuildNewLabel(ad)}; - ops.emplace_back(LinearSwitchingIO{s, next, errRef}); - ops.emplace_back(next); - } else { - ops.emplace_back(LinearAction{ec}); - } -} - -template -void threeLabelSpec(const T &s, std::list &ops, - const parser::Statement &ec, AnalysisData &ad) { - auto errLab{GetErr(s)}; - auto eorLab{GetEor(s)}; - auto endLab{GetEnd(s)}; - if (errLab || eorLab || endLab) { - std::optional errRef{std::nullopt}; - if (errLab) errRef = FetchLabel(ad, errLab).get(); - std::optional eorRef{std::nullopt}; - if (eorLab) eorRef = FetchLabel(ad, eorLab).get(); - std::optional endRef{std::nullopt}; - if (endLab) endRef = FetchLabel(ad, endLab).get(); - LinearLabel next{BuildNewLabel(ad)}; - ops.emplace_back(LinearSwitchingIO{s, next, errRef, eorRef, endRef}); - ops.emplace_back(next); - } else { - ops.emplace_back(LinearAction{ec}); - } -} - -template -std::vector toLabelRef(AnalysisData &ad, const T &labels) { - std::vector result; - for (auto label : labels) { - result.emplace_back(FetchLabel(ad, label).get()); - } - CHECK(result.size() == labels.size()); - return result; -} - -bool hasAltReturns(const parser::CallStmt &callStmt) { - const auto &args{std::get>(callStmt.v.t)}; - for (const auto &arg : args) { - const auto &actual{std::get(arg.t)}; - if (std::holds_alternative(actual.u)) { - return true; - } - } - return false; -} -std::list getAltReturnLabels(const parser::Call &call) { - std::list result; - const auto &args{std::get>(call.t)}; - for (const auto &arg : args) { - const auto &actual{std::get(arg.t)}; - if (const auto *p{std::get_if(&actual.u)}) { - result.push_back(p->v); - } - } - return result; -} -LinearLabelRef NearestEnclosingDoConstruct(AnalysisData &ad) { - for (auto iterator{ad.nameStack.rbegin()}, endIterator{ad.nameStack.rend()}; - iterator != endIterator; ++iterator) { - auto labelReference{std::get<2>(*iterator)}; - if (labelReference != unspecifiedLabel) { - return labelReference; - } - } - SEMANTICS_FAILED("CYCLE|EXIT not in loop"); - return unspecifiedLabel; -} - -struct LinearOp : public SumTypeMixin { - template LinearOp(const T &thing) : SumTypeMixin{thing} {} - void dump() const; - - static void Build(std::list &ops, - const parser::Statement &ec, AnalysisData &ad) { - std::visit( - common::visitors{ - [&](const auto &s) { ops.emplace_back(LinearAction{ec}); }, - [&](const common::Indirection &s) { - if (hasAltReturns(s.value())) { - auto next{BuildNewLabel(ad)}; - auto labels{toLabelRef(ad, getAltReturnLabels(s.value().v))}; - labels.push_back(next); - ops.emplace_back(LinearSwitch{s.value(), std::move(labels)}); - ops.emplace_back(next); - } else { - ops.emplace_back(LinearAction{ec}); - } - }, - [&](const common::Indirection &s) { - AddAssign(ad, std::get(s.value().t).symbol, - std::get(s.value().t)); - ops.emplace_back(LinearAction{ec}); - }, - [&](const common::Indirection &s) { - ops.emplace_back(LinearGoto{s.value(), - s.value().v ? std::get<2>(FindStack( - ad.nameStack, &s.value().v.value())) - : NearestEnclosingDoConstruct(ad)}); - }, - [&](const common::Indirection &s) { - ops.emplace_back(LinearGoto{s.value(), - s.value().v ? std::get<1>(FindStack( - ad.nameStack, &s.value().v.value())) - : NearestEnclosingDoConstruct(ad)}); - }, - [&](const common::Indirection &s) { - ops.emplace_back( - LinearGoto{s.value(), FetchLabel(ad, s.value().v).get()}); - }, - [&](const parser::FailImageStmt &s) { - ops.emplace_back(LinearReturn{s}); - }, - [&](const common::Indirection &s) { - ops.emplace_back(LinearReturn{s.value()}); - }, - [&](const common::Indirection &s) { - ops.emplace_back(LinearAction{ec}); - ops.emplace_back(LinearReturn{s.value()}); - }, - [&](const common::Indirection &s) { - threeLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - threeLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - threeLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - errLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - errLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - errLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - errLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - errLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - errLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - errLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - auto next{BuildNewLabel(ad)}; - auto labels{toLabelRef( - ad, std::get>(s.value().t))}; - labels.push_back(next); - ops.emplace_back(LinearSwitch{s.value(), std::move(labels)}); - ops.emplace_back(next); - }, - [&](const common::Indirection &s) { - ops.emplace_back(LinearSwitch{s.value(), - toLabelRef(ad, - std::list{std::get<1>(s.value().t), - std::get<2>(s.value().t), - std::get<3>(s.value().t)})}); - }, - [&](const common::Indirection &s) { - ops.emplace_back( - LinearIndirectGoto{std::get(s.value().t).symbol, - toLabelRef(ad, - std::get>(s.value().t))}); - }, - [&](const common::Indirection &s) { - auto then{BuildNewLabel(ad)}; - auto endif{BuildNewLabel(ad)}; - ops.emplace_back(LinearConditionalGoto{s.value(), then, endif}); - ops.emplace_back(then); - ops.emplace_back(LinearAction{ec}); - ops.emplace_back(endif); - }, - }, - ec.statement.u); - } -}; - template const std::optional &GetSwitchAssociateName( const CT *selectConstruct) { @@ -526,301 +43,13 @@ void DumpSwitchWithSelector( /// auto selector{getSelector(construct)}; DebugChannel() << name << "("; // << selector.dump() } - -void LinearOp::dump() const { - std::visit( - common::visitors{ - [](const LinearLabel &t) { - DebugChannel() << "label: " << t.get() << '\n'; - }, - [](const LinearGoto &t) { - DebugChannel() << "goto " << t.target << '\n'; - }, - [](const LinearReturn &) { DebugChannel() << "return\n"; }, - [](const LinearConditionalGoto &t) { - DebugChannel() << "cbranch (?) " << t.trueLabel << ' ' - << t.falseLabel << '\n'; - }, - [](const LinearSwitchingIO &t) { - DebugChannel() << "io-op"; - if (t.errLabel) DebugChannel() << " ERR=" << t.errLabel.value(); - if (t.eorLabel) DebugChannel() << " EOR=" << t.eorLabel.value(); - if (t.endLabel) DebugChannel() << " END=" << t.endLabel.value(); - DebugChannel() << '\n'; - }, - [](const LinearSwitch &lswitch) { - DebugChannel() << "switch-"; - std::visit( - common::visitors{ - [](const parser::CaseConstruct *caseConstruct) { - DumpSwitchWithSelector(caseConstruct, "case"); - }, - [](const parser::SelectRankConstruct *selectRankConstruct) { - DumpSwitchWithSelector(selectRankConstruct, "rank"); - }, - [](const parser::SelectTypeConstruct *selectTypeConstruct) { - DumpSwitchWithSelector(selectTypeConstruct, "type"); - }, - [](const parser::ComputedGotoStmt *computedGotoStmt) { - DebugChannel() << "igoto(?"; - }, - [](const parser::ArithmeticIfStmt *arithmeticIfStmt) { - DebugChannel() << "<=>(?"; - }, - [](const parser::CallStmt *callStmt) { - DebugChannel() << "alt-return(?"; - }, - }, - lswitch.u); - DebugChannel() << ") [...]\n"; - }, - [](const LinearAction &t) { - DebugChannel() << "action: " << t.getSource().ToString() << '\n'; - }, - [](const LinearBeginConstruct &construct) { - DebugChannel() << "construct-" << GetConstructName(construct) - << " {\n"; - }, - [](const LinearDoIncrement &) { DebugChannel() << "do increment\n"; }, - [](const LinearDoCompare &) { DebugChannel() << "do compare\n"; }, - [](const LinearEndConstruct &construct) { - DebugChannel() << "} construct-" << GetConstructName(construct) - << "\n"; - }, - [](const LinearIndirectGoto &) { DebugChannel() << "igoto\n"; }, - }, - u); -} } // end namespace -struct ControlFlowAnalyzer { - explicit ControlFlowAnalyzer(std::list &ops, AnalysisData &ad) - : linearOps{ops}, ad{ad} {} - - LinearLabel buildNewLabel() { return BuildNewLabel(ad); } - LinearOp findLabel(const parser::Label &lab) { - auto iter{ad.labelMap.find(lab)}; - if (iter == ad.labelMap.end()) { - LinearLabel ll{ad.labelBuilder}; - ad.labelMap.insert({lab, ll}); - return {ll}; - } - return {iter->second}; - } - template constexpr bool Pre(const A &) { return true; } - template constexpr void Post(const A &) {} - template bool Pre(const parser::Statement &stmt) { - if (stmt.label) { - linearOps.emplace_back(findLabel(*stmt.label)); - } - if constexpr (std::is_same_v) { - LinearOp::Build(linearOps, stmt, ad); - } - return true; - } - - // named constructs - template bool linearConstruct(const T &construct) { - std::list ops; - LinearLabel label{buildNewLabel()}; - const parser::Name *name{getName(construct)}; - ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel); - ops.emplace_back(LinearBeginConstruct{construct}); - ControlFlowAnalyzer cfa{ops, ad}; - Walk(std::get(construct.t), cfa); - ops.emplace_back(label); - ops.emplace_back(LinearEndConstruct{construct}); - linearOps.splice(linearOps.end(), ops); - ad.nameStack.pop_back(); - return false; - } - bool Pre(const parser::AssociateConstruct &c) { return linearConstruct(c); } - bool Pre(const parser::ChangeTeamConstruct &c) { return linearConstruct(c); } - bool Pre(const parser::CriticalConstruct &c) { return linearConstruct(c); } - bool Pre(const parser::BlockConstruct &construct) { - std::list ops; - LinearLabel label{buildNewLabel()}; - const auto &optName{ - std::get>(construct.t) - .statement.v}; - const parser::Name *name{optName ? &*optName : nullptr}; - ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel); - ops.emplace_back(LinearBeginConstruct{construct}); - ControlFlowAnalyzer cfa{ops, ad}; - Walk(std::get(construct.t), cfa); - ops.emplace_back(LinearEndConstruct{construct}); - ops.emplace_back(label); - linearOps.splice(linearOps.end(), ops); - ad.nameStack.pop_back(); - return false; - } - - bool Pre(const parser::DoConstruct &construct) { - std::list ops; - LinearLabel backedgeLab{buildNewLabel()}; - LinearLabel incrementLab{buildNewLabel()}; - LinearLabel entryLab{buildNewLabel()}; - LinearLabel exitLab{buildNewLabel()}; - const parser::Name *name{getName(construct)}; - LinearLabelRef exitOpRef{GetLabelRef(exitLab)}; - ad.nameStack.emplace_back(name, exitOpRef, GetLabelRef(incrementLab)); - ops.emplace_back(LinearBeginConstruct{construct}); - ops.emplace_back(LinearGoto{GetLabelRef(backedgeLab)}); - ops.emplace_back(incrementLab); - ops.emplace_back(LinearDoIncrement{construct}); - ops.emplace_back(backedgeLab); - ops.emplace_back(LinearDoCompare{construct}); - ops.emplace_back(LinearConditionalGoto{ - std::get>(construct.t), - GetLabelRef(entryLab), exitOpRef}); - ops.push_back(entryLab); - ControlFlowAnalyzer cfa{ops, ad}; - Walk(std::get(construct.t), cfa); - ops.emplace_back(LinearGoto{GetLabelRef(incrementLab)}); - ops.emplace_back(LinearEndConstruct{construct}); - ops.emplace_back(exitLab); - linearOps.splice(linearOps.end(), ops); - ad.nameStack.pop_back(); - return false; - } - - bool Pre(const parser::IfConstruct &construct) { - std::list ops; - LinearLabel thenLab{buildNewLabel()}; - LinearLabel elseLab{buildNewLabel()}; - LinearLabel exitLab{buildNewLabel()}; - const parser::Name *name{getName(construct)}; - ad.nameStack.emplace_back(name, GetLabelRef(exitLab), unspecifiedLabel); - ops.emplace_back(LinearBeginConstruct{construct}); - ops.emplace_back(LinearConditionalGoto{ - std::get>(construct.t), - GetLabelRef(thenLab), GetLabelRef(elseLab)}); - ops.emplace_back(thenLab); - ControlFlowAnalyzer cfa{ops, ad}; - Walk(std::get(construct.t), cfa); - LinearLabelRef exitOpRef{GetLabelRef(exitLab)}; - ops.emplace_back(LinearGoto{exitOpRef}); - for (const auto &elseIfBlock : - std::get>(construct.t)) { - ops.emplace_back(elseLab); - LinearLabel newThenLab{buildNewLabel()}; - LinearLabel newElseLab{buildNewLabel()}; - ops.emplace_back(LinearConditionalGoto{ - std::get>(elseIfBlock.t), - GetLabelRef(newThenLab), GetLabelRef(newElseLab)}); - ops.emplace_back(newThenLab); - Walk(std::get(elseIfBlock.t), cfa); - ops.emplace_back(LinearGoto{exitOpRef}); - elseLab = newElseLab; - } - ops.emplace_back(elseLab); - if (const auto &optElseBlock{ - std::get>( - construct.t)}) { - Walk(std::get(optElseBlock->t), cfa); - } - ops.emplace_back(LinearGoto{exitOpRef}); - ops.emplace_back(exitLab); - ops.emplace_back(LinearEndConstruct{construct}); - linearOps.splice(linearOps.end(), ops); - ad.nameStack.pop_back(); - return false; - } - template, - parser::CaseConstruct::Case, - std::conditional_t, - parser::SelectRankConstruct::RankCase, - std::conditional_t, - parser::SelectTypeConstruct::TypeCase, void>>>> - bool Multiway(const A &construct) { - std::list ops; - LinearLabel exitLab{buildNewLabel()}; - const parser::Name *name{getName(construct)}; - ad.nameStack.emplace_back(name, GetLabelRef(exitLab), unspecifiedLabel); - ops.emplace_back(LinearBeginConstruct{construct}); - const auto N{std::get>(construct.t).size()}; - LinearLabelRef exitOpRef{GetLabelRef(exitLab)}; - if (N > 0) { - typename std::list::size_type i; - std::vector toLabels; - for (i = 0; i != N; ++i) { - toLabels.emplace_back(buildNewLabel()); - } - std::vector targets; - for (i = 0; i != N; ++i) { - targets.emplace_back(GetLabelRef(toLabels[i])); - } - ops.emplace_back(LinearSwitch{construct, targets}); - ControlFlowAnalyzer cfa{ops, ad}; - i = 0; - for (const auto &caseBlock : std::get>(construct.t)) { - ops.emplace_back(toLabels[i++]); - Walk(std::get(caseBlock.t), cfa); - ops.emplace_back(LinearGoto{exitOpRef}); - } - } - ops.emplace_back(exitLab); - ops.emplace_back(LinearEndConstruct{construct}); - linearOps.splice(linearOps.end(), ops); - ad.nameStack.pop_back(); - return false; - } - bool Pre(const parser::CaseConstruct &c) { return Multiway(c); } - bool Pre(const parser::SelectRankConstruct &c) { return Multiway(c); } - bool Pre(const parser::SelectTypeConstruct &c) { return Multiway(c); } - bool Pre(const parser::WhereConstruct &c) { - std::list ops; - LinearLabel label{buildNewLabel()}; - const parser::Name *name{getName(c)}; - ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel); - ops.emplace_back(LinearBeginConstruct{c}); - ControlFlowAnalyzer cfa{ops, ad}; - Walk(std::get>(c.t), cfa); - Walk( - std::get>(c.t), cfa); - Walk(std::get>(c.t), cfa); - ops.emplace_back(label); - ops.emplace_back(LinearEndConstruct{c}); - linearOps.splice(linearOps.end(), ops); - ad.nameStack.pop_back(); - return false; - } - bool Pre(const parser::ForallConstruct &construct) { - std::list ops; - LinearLabel label{buildNewLabel()}; - const parser::Name *name{getName(construct)}; - ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel); - ops.emplace_back(LinearBeginConstruct{construct}); - ControlFlowAnalyzer cfa{ops, ad}; - Walk(std::get>(construct.t), cfa); - ops.emplace_back(label); - ops.emplace_back(LinearEndConstruct{construct}); - linearOps.splice(linearOps.end(), ops); - ad.nameStack.pop_back(); - return false; - } - template const parser::Name *getName(const A &a) { - const auto &optName{std::get<0>(std::get<0>(a.t).statement.t)}; - return optName ? &*optName : nullptr; - } - LinearLabelRef GetLabelRef(const LinearLabel &label) { - label.setReferenced(); - return label; - } - LinearLabelRef GetLabelRef(const parser::Label &label) { - return FetchLabel(ad, label); - } - - std::list &linearOps; - AnalysisData &ad; -}; - template struct SwitchArgs { Value exp; - LinearLabelRef defLab; + flat::LabelRef defLab; std::vector values; - std::vector labels; + std::vector labels; }; using SwitchArguments = SwitchArgs; using SwitchCaseArguments = SwitchArgs; @@ -832,9 +61,9 @@ template bool IsDefault(const typename T::ValueType &valueType) { } template -void cleanupSwitchPairs(LinearLabelRef &defLab, +void cleanupSwitchPairs(flat::LabelRef &defLab, std::vector &values, - std::vector &labels) { + std::vector &labels) { CHECK(values.size() == labels.size()); for (std::size_t i{0}, len{values.size()}; i < len; ++i) { if (IsDefault(values[i])) { @@ -1020,7 +249,7 @@ static void CreateSwitchTypeHelper(FIRBuilder *builder, Value condition, class FortranIRLowering { public: - using LabelMapType = std::map; + using LabelMapType = std::map; using Closure = std::function; FortranIRLowering(semantics::SemanticsContext &sc, bool debugLinearIR) @@ -1063,21 +292,19 @@ public: auto *subp{fir_->getOrInsertProcedure(name, nullptr, {})}; builder_ = new FIRBuilder(*CreateBlock(subp->getLastRegion())); AnalysisData ad; +#if 0 ControlFlowAnalyzer linearize{linearOperations_, ad}; Walk(here, linearize); +#else + CreateFlatIR(here, linearOperations_, ad); +#endif if (debugLinearFIR_) { - dumpLinearRepresentation(); + dump(linearOperations_); } ConstructFIR(ad); DrawRemainingArcs(); Cleanup(); } - void dumpLinearRepresentation() const { - for (const auto &op : linearOperations_) { - op.dump(); - } - DebugChannel() << "--- END ---\n"; - } template Statement *BindArrayWithBoundSpecifier( @@ -1269,8 +496,8 @@ public: .statement.t)}; return builder_->CreateExpr(ExprRef(x.thing)); } - SwitchArguments ComposeSwitchArgs(const LinearSwitch &op) { - SwitchArguments result{NOTHING, unspecifiedLabel, {}, op.refs}; + SwitchArguments ComposeSwitchArgs(const flat::SwitchOp &op) { + SwitchArguments result{NOTHING, flat::unspecifiedLabel, {}, op.refs}; std::visit( common::visitors{ [&](const parser::ComputedGotoStmt *c) { @@ -1293,11 +520,11 @@ public: } SwitchCaseArguments ComposeSwitchCaseArguments( const parser::CaseConstruct *caseConstruct, - const std::vector &refs) { + const std::vector &refs) { auto &cases{ std::get>(caseConstruct->t)}; SwitchCaseArguments result{GetSwitchCaseSelector(caseConstruct), - unspecifiedLabel, populateSwitchValues(builder_, cases), + flat::unspecifiedLabel, populateSwitchValues(builder_, cases), std::move(refs)}; cleanupSwitchPairs( result.defLab, result.values, result.labels); @@ -1305,11 +532,11 @@ public: } SwitchRankArguments ComposeSwitchRankArguments( const parser::SelectRankConstruct *selectRankConstruct, - const std::vector &refs) { + const std::vector &refs) { auto &ranks{std::get>( selectRankConstruct->t)}; SwitchRankArguments result{GetSwitchRankSelector(selectRankConstruct), - unspecifiedLabel, populateSwitchValues(ranks), std::move(refs)}; + flat::unspecifiedLabel, populateSwitchValues(ranks), std::move(refs)}; if (auto &name{GetSwitchAssociateName( selectRankConstruct)}) { (void)name; // get rid of warning @@ -1321,11 +548,11 @@ public: } SwitchTypeArguments ComposeSwitchTypeArguments( const parser::SelectTypeConstruct *selectTypeConstruct, - const std::vector &refs) { + const std::vector &refs) { auto &types{std::get>( selectTypeConstruct->t)}; SwitchTypeArguments result{GetSwitchTypeSelector(selectTypeConstruct), - unspecifiedLabel, populateSwitchValues(types), std::move(refs)}; + flat::unspecifiedLabel, populateSwitchValues(types), std::move(refs)}; if (auto &name{GetSwitchAssociateName( selectTypeConstruct)}) { (void)name; // get rid of warning @@ -1583,11 +810,11 @@ public: [&](const common::Indirection &s) { auto *addr{builder_->CreateAddr( NameToExpression(std::get(s.value().t)))}; - auto *block{ - blockMap_ - .find(FetchLabel(ad, std::get(s.value().t)) - .get()) - ->second}; + auto *block{blockMap_ + .find(flat::FetchLabel( + ad, std::get(s.value().t)) + .get()) + ->second}; builder_->CreateStore(addr, block); }, [](const common::Indirection &) { @@ -1600,7 +827,7 @@ public: }, stmt.statement.u); } - void handleLinearAction(const LinearAction &action, AnalysisData &ad) { + void handleLinearAction(const flat::ActionOp &action, AnalysisData &ad) { handleActionStatement(ad, *action.v); } @@ -1631,14 +858,14 @@ public: } // do_var = do_var + e3 - void handleLinearDoIncrement(const LinearDoIncrement &inc) { + void handleLinearDoIncrement(const flat::DoIncrementOp &inc) { auto *info{GetBoundsInfo(inc)}; auto *var{builder_->CreateLoad(info->doVariable)}; builder_->CreateIncrement(var, info->stepExpr); } // (e3 > 0 && do_var <= e2) || (e3 < 0 && do_var >= e2) - void handleLinearDoCompare(const LinearDoCompare &cmp) { + void handleLinearDoCompare(const flat::DoCompareOp &cmp) { auto *info{GetBoundsInfo(cmp)}; auto *var{builder_->CreateLoad(info->doVariable)}; auto *cond{ @@ -1760,7 +987,7 @@ public: const auto &op{*iter}; std::visit( common::visitors{ - [&](const LinearLabel &linearLabel) { + [&](const flat::LabelOp &linearLabel) { auto *newBlock{CreateBlock(builder_->GetCurrentRegion())}; blockMap_.insert({linearLabel.get(), newBlock}); if (builder_->GetInsertionPoint()) { @@ -1768,17 +995,17 @@ public: } builder_->SetInsertionPoint(newBlock); }, - [&](const LinearGoto &linearGoto) { + [&](const flat::GotoOp &linearGoto) { CheckInsertionPoint(); AddOrQueueBranch(linearGoto.target); builder_->ClearInsertionPoint(); }, - [&](const LinearIndirectGoto &linearIGoto) { + [&](const flat::IndirectGotoOp &linearIGoto) { CheckInsertionPoint(); AddOrQueueIGoto(ad, linearIGoto.symbol, linearIGoto.labelRefs); builder_->ClearInsertionPoint(); }, - [&](const LinearReturn &linearReturn) { + [&](const flat::ReturnOp &linearReturn) { CheckInsertionPoint(); std::visit( common::visitors{ @@ -1807,7 +1034,7 @@ public: linearReturn.u); builder_->ClearInsertionPoint(); }, - [&](const LinearConditionalGoto &linearConditionalGoto) { + [&](const flat::ConditionalGotoOp &linearConditionalGoto) { CheckInsertionPoint(); std::visit( common::visitors{ @@ -1851,13 +1078,13 @@ public: linearConditionalGoto.u); builder_->ClearInsertionPoint(); }, - [&](const LinearSwitchingIO &linearIO) { + [&](const flat::SwitchIOOp &linearIO) { CheckInsertionPoint(); AddOrQueueSwitch( NOTHING, linearIO.next, {}, {}, CreateSwitchHelper); builder_->ClearInsertionPoint(); }, - [&](const LinearSwitch &linearSwitch) { + [&](const flat::SwitchOp &linearSwitch) { CheckInsertionPoint(); std::visit( common::visitors{ @@ -1893,19 +1120,19 @@ public: linearSwitch.u); builder_->ClearInsertionPoint(); }, - [&](const LinearAction &action) { + [&](const flat::ActionOp &action) { CheckInsertionPoint(); handleLinearAction(action, ad); }, - [&](const LinearDoIncrement &inc) { + [&](const flat::DoIncrementOp &inc) { CheckInsertionPoint(); handleLinearDoIncrement(inc); }, - [&](const LinearDoCompare &cmp) { + [&](const flat::DoCompareOp &cmp) { CheckInsertionPoint(); handleLinearDoCompare(cmp); }, - [&](const LinearBeginConstruct &linearConstruct) { + [&](const flat::BeginOp &linearConstruct) { std::visit( common::visitors{ [&](const parser::AssociateConstruct *construct) { @@ -1985,7 +1212,7 @@ public: std::visit( common::visitors{ [](const auto &) {}, - [&](const LinearLabel &linearLabel) { + [&](const flat::LabelOp &linearLabel) { blockMap_.insert({linearLabel.get(), builder_->GetInsertionPoint()}); ++iter; @@ -1993,7 +1220,7 @@ public: }, nextOp.u); }, - [&](const LinearEndConstruct &linearConstruct) { + [&](const flat::EndOp &linearConstruct) { std::visit( common::visitors{ [](const auto &) {}, @@ -2038,14 +1265,14 @@ public: builder_->SetInsertionPoint(CreateBlock(builder_->GetCurrentRegion())); } } - void AddOrQueueBranch(LinearLabelRef dest) { + void AddOrQueueBranch(flat::LabelRef dest) { auto iter{blockMap_.find(dest)}; if (iter != blockMap_.end()) { builder_->CreateBranch(iter->second); } else { using namespace std::placeholders; controlFlowEdgesToAdd_.emplace_back(std::bind( - [](FIRBuilder *builder, BasicBlock *block, LinearLabelRef dest, + [](FIRBuilder *builder, BasicBlock *block, flat::LabelRef dest, const LabelMapType &map) { builder->SetInsertionPoint(block); CHECK(map.find(dest) != map.end()); @@ -2054,8 +1281,8 @@ public: builder_, builder_->GetInsertionPoint(), dest, _1)); } } - void AddOrQueueCGoto(Statement *condition, LinearLabelRef trueBlock, - LinearLabelRef falseBlock) { + void AddOrQueueCGoto(Statement *condition, flat::LabelRef trueBlock, + flat::LabelRef falseBlock) { auto trueIter{blockMap_.find(trueBlock)}; auto falseIter{blockMap_.find(falseBlock)}; if (trueIter != blockMap_.end() && falseIter != blockMap_.end()) { @@ -2065,7 +1292,7 @@ public: using namespace std::placeholders; controlFlowEdgesToAdd_.emplace_back(std::bind( [](FIRBuilder *builder, BasicBlock *block, Statement *expr, - LinearLabelRef trueDest, LinearLabelRef falseDest, + flat::LabelRef trueDest, flat::LabelRef falseDest, const LabelMapType &map) { builder->SetInsertionPoint(block); CHECK(map.find(trueDest) != map.end()); @@ -2079,9 +1306,9 @@ public: } template - void AddOrQueueSwitch(Value condition, LinearLabelRef defaultLabel, + void AddOrQueueSwitch(Value condition, flat::LabelRef defaultLabel, const std::vector &values, - const std::vector &labels, F function) { + const std::vector &labels, F function) { auto defer{false}; auto defaultIter{blockMap_.find(defaultLabel)}; typename SWITCHTYPE::ValueSuccPairListType cases; @@ -2104,9 +1331,9 @@ public: using namespace std::placeholders; controlFlowEdgesToAdd_.emplace_back(std::bind( [](FIRBuilder *builder, BasicBlock *block, Value expr, - LinearLabelRef defaultDest, + flat::LabelRef defaultDest, const std::vector &values, - const std::vector &labels, F function, + const std::vector &labels, F function, const LabelMapType &map) { builder->SetInsertionPoint(block); typename SWITCHTYPE::ValueSuccPairListType cases; @@ -2129,8 +1356,8 @@ public: } void AddOrQueueIGoto(AnalysisData &ad, const semantics::Symbol *symbol, - const std::vector &labels) { - auto useLabels{labels.empty() ? GetAssign(ad, symbol) : labels}; + const std::vector &labels) { + auto useLabels{labels.empty() ? flat::GetAssign(ad, symbol) : labels}; auto defer{false}; IndirectBranchStmt::TargetListType blocks; for (auto lab : useLabels) { @@ -2146,7 +1373,7 @@ public: using namespace std::placeholders; controlFlowEdgesToAdd_.emplace_back(std::bind( [](FIRBuilder *builder, BasicBlock *block, Variable *variable, - const std::vector &fixme, + const std::vector &fixme, const LabelMapType &map) { builder->SetInsertionPoint(block); builder->CreateIndirectBr(variable, {}); // FIXME @@ -2176,7 +1403,7 @@ public: FIRBuilder *builder_{nullptr}; Program *fir_; - std::list linearOperations_; + std::list linearOperations_; std::list controlFlowEdgesToAdd_; std::map doMap_; LabelMapType blockMap_; @@ -2191,6 +1418,17 @@ Program *CreateFortranIR(const parser::Program &program, return converter.program(); } +// debug channel +llvm::raw_ostream *debugChannel; + +llvm::raw_ostream &DebugChannel() { + return debugChannel ? *debugChannel : llvm::errs(); +} + +static void SetDebugChannel(llvm::raw_ostream *output) { + debugChannel = output; +} + void SetDebugChannel(const std::string &filename) { std::error_code ec; SetDebugChannel( diff --git a/flang/lib/FIR/common.h b/flang/lib/FIR/common.h index 05d3664..332e209 100644 --- a/flang/lib/FIR/common.h +++ b/flang/lib/FIR/common.h @@ -22,6 +22,7 @@ #include "../evaluate/variable.h" #include "../parser/parse-tree.h" #include "../semantics/symbol.h" +#include "llvm/Support/raw_ostream.h" // Some useful, self-documenting macros for failure modes #define STRINGIFY(X) #X @@ -108,6 +109,9 @@ enum RuntimeCallType { }; using RuntimeCallArguments = CallArguments; + +llvm::raw_ostream &DebugChannel(); +void SetDebugChannel(const std::string &filename); } #endif // FORTRAN_FIR_COMMON_H_ diff --git a/flang/lib/FIR/flattened.cc b/flang/lib/FIR/flattened.cc new file mode 100644 index 0000000..c509726 --- /dev/null +++ b/flang/lib/FIR/flattened.cc @@ -0,0 +1,853 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "flattened.h" +#include "../parser/parse-tree-visitor.h" + +namespace Fortran::FIR { +namespace flat { + +LabelBuilder::LabelBuilder() : referenced(32), counter{0u} {} + +LabelRef LabelBuilder::getNext() { + LabelRef next{counter++}; + auto cap{referenced.capacity()}; + if (cap < counter) { + referenced.reserve(2 * cap); + } + referenced[next] = false; + return next; +} + +void LabelBuilder::setReferenced(LabelRef label) { referenced[label] = true; } + +bool LabelBuilder::isReferenced(LabelRef label) const { + return referenced[label]; +} + +LabelOp::LabelOp(LabelBuilder &builder) + : builder_{builder}, label_{builder.getNext()} {} + +LabelOp::LabelOp(const LabelOp &that) + : builder_{that.builder_}, label_{that.label_} {} + +LabelOp &LabelOp::operator=(const LabelOp &that) { + CHECK(&builder_ == &that.builder_); + label_ = that.label_; + return *this; +} + +void LabelOp::setReferenced() const { builder_.setReferenced(label_); } + +bool LabelOp::isReferenced() const { return builder_.isReferenced(label_); } + +void AddAssign(AnalysisData &ad, const semantics::Symbol *symbol, + const parser::Label &label) { + ad.assignMap[symbol].insert(label); +} + +std::vector GetAssign( + AnalysisData &ad, const semantics::Symbol *symbol) { + std::vector result; + for (auto lab : ad.assignMap[symbol]) { + result.emplace_back(lab); + } + return result; +} + +std::tuple FindStack( + const std::vector> + &stack, + const parser::Name *key) { + for (auto iter{stack.rbegin()}, iend{stack.rend()}; iter != iend; ++iter) { + if (std::get<0>(*iter) == key) { + return *iter; + } + } + SEMANTICS_FAILED("construct name not on stack"); + return {}; +} + +LabelOp FetchLabel(AnalysisData &ad, const parser::Label &label) { + auto iter{ad.labelMap.find(label)}; + if (iter == ad.labelMap.end()) { + LabelOp ll{ad.labelBuilder}; + ll.setReferenced(); + ad.labelMap.insert({label, ll}); + return ll; + } + return iter->second; +} + +LabelOp BuildNewLabel(AnalysisData &ad) { return LabelOp{ad.labelBuilder}; } + +template parser::Label GetErr(const T &stmt) { + if constexpr (std::is_same_v || + std::is_same_v) { + for (const auto &control : stmt.controls) { + if (std::holds_alternative(control.u)) { + return std::get(control.u).v; + } + } + } + if constexpr (std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) { + for (const auto &spec : stmt.v) { + if (std::holds_alternative(spec.u)) { + return std::get(spec.u).v; + } + } + } + if constexpr (std::is_same_v) { + for (const auto &spec : std::get>(stmt.u)) { + if (std::holds_alternative(spec.u)) { + return std::get(spec.u).v; + } + } + } + return 0; +} + +template parser::Label GetEor(const T &stmt) { + if constexpr (std::is_same_v || + std::is_same_v) { + for (const auto &control : stmt.controls) { + if (std::holds_alternative(control.u)) { + return std::get(control.u).v; + } + } + } + if constexpr (std::is_same_v) { + for (const auto &waitSpec : stmt.v) { + if (std::holds_alternative(waitSpec.u)) { + return std::get(waitSpec.u).v; + } + } + } + return 0; +} + +template parser::Label GetEnd(const T &stmt) { + if constexpr (std::is_same_v || + std::is_same_v) { + for (const auto &control : stmt.controls) { + if (std::holds_alternative(control.u)) { + return std::get(control.u).v; + } + } + } + if constexpr (std::is_same_v) { + for (const auto &waitSpec : stmt.v) { + if (std::holds_alternative(waitSpec.u)) { + return std::get(waitSpec.u).v; + } + } + } + return 0; +} + +template +void errLabelSpec(const A &s, std::list &ops, + const parser::Statement &ec, AnalysisData &ad) { + if (auto errLab{GetErr(s)}) { + std::optional errRef{FetchLabel(ad, errLab).get()}; + LabelOp next{BuildNewLabel(ad)}; + ops.emplace_back(SwitchIOOp{s, next, errRef}); + ops.emplace_back(next); + } else { + ops.emplace_back(ActionOp{ec}); + } +} + +template +void threeLabelSpec(const A &s, std::list &ops, + const parser::Statement &ec, AnalysisData &ad) { + auto errLab{GetErr(s)}; + auto eorLab{GetEor(s)}; + auto endLab{GetEnd(s)}; + if (errLab || eorLab || endLab) { + std::optional errRef; + if (errLab) { + errRef = FetchLabel(ad, errLab).get(); + } + std::optional eorRef; + if (eorLab) { + eorRef = FetchLabel(ad, eorLab).get(); + } + std::optional endRef; + if (endLab) { + endRef = FetchLabel(ad, endLab).get(); + } + auto next{BuildNewLabel(ad)}; + ops.emplace_back(SwitchIOOp{s, next, errRef, eorRef, endRef}); + ops.emplace_back(next); + } else { + ops.emplace_back(ActionOp{ec}); + } +} + +template +std::vector toLabelRef(AnalysisData &ad, const A &labels) { + std::vector result; + for (auto label : labels) { + result.emplace_back(FetchLabel(ad, label).get()); + } + CHECK(result.size() == labels.size()); + return result; +} + +bool hasAltReturns(const parser::CallStmt &callStmt) { + const auto &args{std::get>(callStmt.v.t)}; + for (const auto &arg : args) { + const auto &actual{std::get(arg.t)}; + if (std::holds_alternative(actual.u)) { + return true; + } + } + return false; +} + +std::list getAltReturnLabels(const parser::Call &call) { + std::list result; + const auto &args{std::get>(call.t)}; + for (const auto &arg : args) { + const auto &actual{std::get(arg.t)}; + if (const auto *p{std::get_if(&actual.u)}) { + result.push_back(p->v); + } + } + return result; +} + +LabelRef NearestEnclosingDoConstruct(AnalysisData &ad) { + for (auto iterator{ad.nameStack.rbegin()}, endIterator{ad.nameStack.rend()}; + iterator != endIterator; ++iterator) { + auto labelReference{std::get<2>(*iterator)}; + if (labelReference != unspecifiedLabel) { + return labelReference; + } + } + SEMANTICS_FAILED("CYCLE|EXIT not in loop"); + return unspecifiedLabel; +} + +template std::string GetSource(const A *s) { + return s->source.ToString(); +} + +template std::string GetSource(const B *s) { + return GetSource(&std::get>(s->t)); +} + +void LabelOp::dump() const { DebugChannel() << "label_" << get() << ":\n"; } + +void GotoOp::dump() const { + DebugChannel() << "\tgoto " << target << " [" + << std::visit( + common::visitors{ + [](const parser::CycleStmt *s) { return "CYCLE"s; }, + [](const parser::ExitStmt *s) { return "EXIT"s; }, + [](const parser::GotoStmt *s) { return "GOTO"s; }, + [](ArtificialJump) { return ""s; }, + }, + u) + << "]\n"; +} + +void ReturnOp::dump() const { + DebugChannel() + << "\treturn [" + << std::visit( + common::visitors{ + [](const parser::FailImageStmt *s) { return "FAIL IMAGE"s; }, + [](const parser::ReturnStmt *s) { return "RETURN"s; }, + [](const parser::StopStmt *s) { return "STOP"s; }, + }, + u) + << "]\n"; +} + +void ConditionalGotoOp::dump() const { + DebugChannel() + << "\tcbranch .T.:" << trueLabel << " .F.:" << falseLabel << " [" + << std::visit( + common::visitors{ + [](const parser::Statement *s) { + return GetSource(s); + }, + [](const parser::Statement *s) { + return GetSource(s); + }, + [](const parser::IfStmt *s) { return ""s; }, + [](const parser::Statement *s) { + return GetSource(s); + }, + }, + u) + << "]\n"; +} + +void SwitchIOOp::dump() const { + DebugChannel() << "\tio-call"; + if (errLabel.has_value()) { + DebugChannel() << " ERR:" << errLabel.value(); + } + if (eorLabel.has_value()) { + DebugChannel() << " EOR:" << eorLabel.value(); + } + if (endLabel.has_value()) { + DebugChannel() << " END:" << endLabel.value(); + } + DebugChannel() + << " [" + << std::visit( + common::visitors{ + [](const parser::ReadStmt *) { return "READ"s; }, + [](const parser::WriteStmt *) { return "WRITE"s; }, + [](const parser::WaitStmt *) { return "WAIT"s; }, + [](const parser::OpenStmt *) { return "OPEN"s; }, + [](const parser::CloseStmt *) { return "CLOSE"s; }, + [](const parser::BackspaceStmt *) { return "BACKSPACE"s; }, + [](const parser::EndfileStmt *) { return "ENDFILE"s; }, + [](const parser::RewindStmt *) { return "REWIND"s; }, + [](const parser::FlushStmt *) { return "FLUSH"s; }, + [](const parser::InquireStmt *) { return "INQUIRE"s; }, + }, + u) + << "]\n"; +} + +void SwitchOp::dump() const { + DebugChannel() + << "\tswitch [" + << std::visit( + common::visitors{ + [](const parser::CaseConstruct *c) { + return GetSource(c); + }, + [](const parser::SelectRankConstruct *c) { + return GetSource(c); + }, + [](const parser::SelectTypeConstruct *c) { + return GetSource(c); + }, + [](const parser::ComputedGotoStmt *) { + return "computed GOTO"s; + }, + [](const parser::ArithmeticIfStmt *) { + return "arithmetic IF"s; + }, + [](const parser::CallStmt *) { return "alt-return CALL"s; }, + }, + u) + << "]\n"; +} + +void ActionOp::dump() const { DebugChannel() << '\t' << GetSource(v) << '\n'; } + +void BeginOp::dump() const { + DebugChannel() + << "\t[" + << std::visit( + common::visitors{ + [](const parser::AssociateConstruct *c) { + return GetSource(c); + }, + [](const parser::BlockConstruct *c) { + return GetSource(c); + }, + [](const parser::CaseConstruct *c) { + return GetSource(c); + }, + [](const parser::ChangeTeamConstruct *c) { + return GetSource(c); + }, + [](const parser::CriticalConstruct *c) { + return GetSource(c); + }, + [](const parser::DoConstruct *c) { + return GetSource(c); + }, + [](const parser::IfConstruct *c) { + return GetSource(c); + }, + [](const parser::SelectRankConstruct *c) { + return GetSource(c); + }, + [](const parser::SelectTypeConstruct *c) { + return GetSource(c); + }, + [](const parser::WhereConstruct *c) { + return GetSource(c); + }, + [](const parser::ForallConstruct *c) { + return GetSource( + &std::get< + parser::Statement>( + c->t)); + }, + [](const parser::CompilerDirective *c) { + return GetSource(c); + }, + [](const parser::OpenMPConstruct *c) { return "openmp"s; }, + [](const parser::OpenMPEndLoopDirective *c) { + return "openmp end loop"s; + }, + }, + u) + << "] :{\n"; +} + +void EndOp::dump() const { + DebugChannel() + << "\t}: [" + << std::visit( + common::visitors{ + [](const parser::AssociateConstruct *c) { + return GetSource(c); + }, + [](const parser::BlockConstruct *c) { + return GetSource(c); + }, + [](const parser::CaseConstruct *c) { + return GetSource(c); + }, + [](const parser::ChangeTeamConstruct *c) { + return GetSource(c); + }, + [](const parser::CriticalConstruct *c) { + return GetSource(c); + }, + [](const parser::DoConstruct *c) { + return GetSource(c); + }, + [](const parser::IfConstruct *c) { + return GetSource(c); + }, + [](const parser::SelectRankConstruct *c) { + return GetSource(c); + }, + [](const parser::SelectTypeConstruct *c) { + return GetSource(c); + }, + [](const parser::WhereConstruct *c) { + return GetSource(c); + }, + [](const parser::ForallConstruct *c) { + return GetSource(c); + }, + [](const parser::CompilerDirective *c) { + return GetSource(c); + }, + [](const parser::OpenMPConstruct *c) { return "openmp"s; }, + [](const parser::OpenMPEndLoopDirective *c) { + return "openmp end loop"s; + }, + }, + u) + << "]\n"; +} + +void IndirectGotoOp::dump() const { + DebugChannel() << "\tindirect-goto " << symbol->name().ToString() << ':'; + for (auto lab : labelRefs) { + DebugChannel() << ' ' << lab; + } + DebugChannel() << '\n'; +} + +void DoIncrementOp::dump() const { + DebugChannel() << "\tincrement ["; + DebugChannel() << "]\n"; +} + +void DoCompareOp::dump() const { + DebugChannel() << "\tcompare ["; + DebugChannel() << "]\n"; +} + +void Op::Build(std::list &ops, + const parser::Statement &ec, AnalysisData &ad) { + std::visit( + common::visitors{ + [&](const auto &s) { ops.emplace_back(ActionOp{ec}); }, + [&](const common::Indirection &s) { + if (hasAltReturns(s.value())) { + auto next{BuildNewLabel(ad)}; + auto labels{toLabelRef(ad, getAltReturnLabels(s.value().v))}; + labels.push_back(next); + ops.emplace_back(SwitchOp{s.value(), std::move(labels)}); + ops.emplace_back(next); + } else { + ops.emplace_back(ActionOp{ec}); + } + }, + [&](const common::Indirection &s) { + AddAssign(ad, std::get(s.value().t).symbol, + std::get(s.value().t)); + ops.emplace_back(ActionOp{ec}); + }, + [&](const common::Indirection &s) { + ops.emplace_back(GotoOp{s.value(), + s.value().v + ? std::get<2>(FindStack(ad.nameStack, &s.value().v.value())) + : NearestEnclosingDoConstruct(ad)}); + }, + [&](const common::Indirection &s) { + ops.emplace_back(GotoOp{s.value(), + s.value().v + ? std::get<1>(FindStack(ad.nameStack, &s.value().v.value())) + : NearestEnclosingDoConstruct(ad)}); + }, + [&](const common::Indirection &s) { + ops.emplace_back( + GotoOp{s.value(), FetchLabel(ad, s.value().v).get()}); + }, + [&](const parser::FailImageStmt &s) { + ops.emplace_back(ReturnOp{s}); + }, + [&](const common::Indirection &s) { + ops.emplace_back(ReturnOp{s.value()}); + }, + [&](const common::Indirection &s) { + ops.emplace_back(ActionOp{ec}); + ops.emplace_back(ReturnOp{s.value()}); + }, + [&](const common::Indirection &s) { + threeLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + threeLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + threeLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + errLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + errLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + errLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + errLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + errLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + errLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + errLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + auto next{BuildNewLabel(ad)}; + auto labels{toLabelRef( + ad, std::get>(s.value().t))}; + labels.push_back(next); + ops.emplace_back(SwitchOp{s.value(), std::move(labels)}); + ops.emplace_back(next); + }, + [&](const common::Indirection &s) { + ops.emplace_back(SwitchOp{s.value(), + toLabelRef(ad, + std::list{std::get<1>(s.value().t), + std::get<2>(s.value().t), std::get<3>(s.value().t)})}); + }, + [&](const common::Indirection &s) { + ops.emplace_back( + IndirectGotoOp{std::get(s.value().t).symbol, + toLabelRef( + ad, std::get>(s.value().t))}); + }, + [&](const common::Indirection &s) { + auto then{BuildNewLabel(ad)}; + auto endif{BuildNewLabel(ad)}; + ops.emplace_back(ConditionalGotoOp{s.value(), then, endif}); + ops.emplace_back(then); + ops.emplace_back(ActionOp{ec}); + ops.emplace_back(endif); + }, + }, + ec.statement.u); +} + +template struct ElementMap; +template<> struct ElementMap { + using type = parser::CaseConstruct::Case; +}; +template<> struct ElementMap { + using type = parser::SelectRankConstruct::RankCase; +}; +template<> struct ElementMap { + using type = parser::SelectTypeConstruct::TypeCase; +}; + +struct ControlFlowAnalyzer { + explicit ControlFlowAnalyzer(std::list &ops, AnalysisData &ad) + : linearOps{ops}, ad{ad} {} + + LabelOp buildNewLabel() { return BuildNewLabel(ad); } + + Op findLabel(const parser::Label &lab) { + auto iter{ad.labelMap.find(lab)}; + if (iter == ad.labelMap.end()) { + LabelOp ll{ad.labelBuilder}; + ad.labelMap.insert({lab, ll}); + return {ll}; + } + return {iter->second}; + } + + template constexpr bool Pre(const A &) { return true; } + template constexpr void Post(const A &) {} + + template bool Pre(const parser::Statement &stmt) { + if (stmt.label) { + linearOps.emplace_back(findLabel(*stmt.label)); + } + if constexpr (std::is_same_v) { + Op::Build(linearOps, stmt, ad); + } + return true; + } + + // named constructs + template bool linearConstruct(const A &construct) { + std::list ops; + LabelOp label{buildNewLabel()}; + const parser::Name *name{getName(construct)}; + ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel); + ops.emplace_back(BeginOp{construct}); + ControlFlowAnalyzer cfa{ops, ad}; + Walk(std::get(construct.t), cfa); + ops.emplace_back(label); + ops.emplace_back(EndOp{construct}); + linearOps.splice(linearOps.end(), ops); + ad.nameStack.pop_back(); + return false; + } + + bool Pre(const parser::AssociateConstruct &c) { return linearConstruct(c); } + bool Pre(const parser::ChangeTeamConstruct &c) { return linearConstruct(c); } + bool Pre(const parser::CriticalConstruct &c) { return linearConstruct(c); } + + bool Pre(const parser::BlockConstruct &construct) { + std::list ops; + LabelOp label{buildNewLabel()}; + const auto &optName{ + std::get>(construct.t) + .statement.v}; + const parser::Name *name{optName ? &*optName : nullptr}; + ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel); + ops.emplace_back(BeginOp{construct}); + ControlFlowAnalyzer cfa{ops, ad}; + Walk(std::get(construct.t), cfa); + ops.emplace_back(EndOp{construct}); + ops.emplace_back(label); + linearOps.splice(linearOps.end(), ops); + ad.nameStack.pop_back(); + return false; + } + + bool Pre(const parser::DoConstruct &construct) { + std::list ops; + LabelOp backedgeLab{buildNewLabel()}; + LabelOp incrementLab{buildNewLabel()}; + LabelOp entryLab{buildNewLabel()}; + LabelOp exitLab{buildNewLabel()}; + const parser::Name *name{getName(construct)}; + LabelRef exitOpRef{GetLabelRef(exitLab)}; + ad.nameStack.emplace_back(name, exitOpRef, GetLabelRef(incrementLab)); + ops.emplace_back(BeginOp{construct}); + ops.emplace_back(GotoOp{GetLabelRef(backedgeLab)}); + ops.emplace_back(incrementLab); + ops.emplace_back(DoIncrementOp{construct}); + ops.emplace_back(backedgeLab); + ops.emplace_back(DoCompareOp{construct}); + ops.emplace_back(ConditionalGotoOp{ + std::get>(construct.t), + GetLabelRef(entryLab), exitOpRef}); + ops.push_back(entryLab); + ControlFlowAnalyzer cfa{ops, ad}; + Walk(std::get(construct.t), cfa); + ops.emplace_back(GotoOp{GetLabelRef(incrementLab)}); + ops.emplace_back(EndOp{construct}); + ops.emplace_back(exitLab); + linearOps.splice(linearOps.end(), ops); + ad.nameStack.pop_back(); + return false; + } + + bool Pre(const parser::IfConstruct &construct) { + std::list ops; + LabelOp thenLab{buildNewLabel()}; + LabelOp elseLab{buildNewLabel()}; + LabelOp exitLab{buildNewLabel()}; + const parser::Name *name{getName(construct)}; + ad.nameStack.emplace_back(name, GetLabelRef(exitLab), unspecifiedLabel); + ops.emplace_back(BeginOp{construct}); + ops.emplace_back(ConditionalGotoOp{ + std::get>(construct.t), + GetLabelRef(thenLab), GetLabelRef(elseLab)}); + ops.emplace_back(thenLab); + ControlFlowAnalyzer cfa{ops, ad}; + Walk(std::get(construct.t), cfa); + LabelRef exitOpRef{GetLabelRef(exitLab)}; + ops.emplace_back(GotoOp{exitOpRef}); + for (const auto &elseIfBlock : + std::get>(construct.t)) { + ops.emplace_back(elseLab); + LabelOp newThenLab{buildNewLabel()}; + LabelOp newElseLab{buildNewLabel()}; + ops.emplace_back(ConditionalGotoOp{ + std::get>(elseIfBlock.t), + GetLabelRef(newThenLab), GetLabelRef(newElseLab)}); + ops.emplace_back(newThenLab); + Walk(std::get(elseIfBlock.t), cfa); + ops.emplace_back(GotoOp{exitOpRef}); + elseLab = newElseLab; + } + ops.emplace_back(elseLab); + if (const auto &optElseBlock{ + std::get>( + construct.t)}) { + Walk(std::get(optElseBlock->t), cfa); + } + ops.emplace_back(GotoOp{exitOpRef}); + ops.emplace_back(exitLab); + ops.emplace_back(EndOp{construct}); + linearOps.splice(linearOps.end(), ops); + ad.nameStack.pop_back(); + return false; + } + + template bool Multiway(const A &construct) { + using B = typename ElementMap::type; + std::list ops; + LabelOp exitLab{buildNewLabel()}; + const parser::Name *name{getName(construct)}; + ad.nameStack.emplace_back(name, GetLabelRef(exitLab), unspecifiedLabel); + ops.emplace_back(BeginOp{construct}); + const auto N{std::get>(construct.t).size()}; + LabelRef exitOpRef{GetLabelRef(exitLab)}; + if (N > 0) { + typename std::list::size_type i; + std::vector toLabels; + for (i = 0; i != N; ++i) { + toLabels.emplace_back(buildNewLabel()); + } + std::vector targets; + for (i = 0; i != N; ++i) { + targets.emplace_back(GetLabelRef(toLabels[i])); + } + ops.emplace_back(SwitchOp{construct, targets}); + ControlFlowAnalyzer cfa{ops, ad}; + i = 0; + for (const auto &caseBlock : std::get>(construct.t)) { + ops.emplace_back(toLabels[i++]); + Walk(std::get(caseBlock.t), cfa); + ops.emplace_back(GotoOp{exitOpRef}); + } + } + ops.emplace_back(exitLab); + ops.emplace_back(EndOp{construct}); + linearOps.splice(linearOps.end(), ops); + ad.nameStack.pop_back(); + return false; + } + + bool Pre(const parser::CaseConstruct &c) { return Multiway(c); } + bool Pre(const parser::SelectRankConstruct &c) { return Multiway(c); } + bool Pre(const parser::SelectTypeConstruct &c) { return Multiway(c); } + + bool Pre(const parser::WhereConstruct &c) { + std::list ops; + LabelOp label{buildNewLabel()}; + const parser::Name *name{getName(c)}; + ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel); + ops.emplace_back(BeginOp{c}); + ControlFlowAnalyzer cfa{ops, ad}; + Walk(std::get>(c.t), cfa); + Walk( + std::get>(c.t), cfa); + Walk(std::get>(c.t), cfa); + ops.emplace_back(label); + ops.emplace_back(EndOp{c}); + linearOps.splice(linearOps.end(), ops); + ad.nameStack.pop_back(); + return false; + } + + bool Pre(const parser::ForallConstruct &construct) { + std::list ops; + LabelOp label{buildNewLabel()}; + const parser::Name *name{getName(construct)}; + ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel); + ops.emplace_back(BeginOp{construct}); + ControlFlowAnalyzer cfa{ops, ad}; + Walk(std::get>(construct.t), cfa); + ops.emplace_back(label); + ops.emplace_back(EndOp{construct}); + linearOps.splice(linearOps.end(), ops); + ad.nameStack.pop_back(); + return false; + } + + template const parser::Name *getName(const A &a) { + const auto &optName{std::get<0>(std::get<0>(a.t).statement.t)}; + return optName ? &*optName : nullptr; + } + + LabelRef GetLabelRef(const LabelOp &label) { + label.setReferenced(); + return label; + } + + LabelRef GetLabelRef(const parser::Label &label) { + return FetchLabel(ad, label); + } + + std::list &linearOps; + AnalysisData &ad; +}; +} + +void dump(const std::list &ops) { + for (auto &op : ops) { + op.dump(); + } +} + +template +void CreateFlatIR(const A &ptree, std::list &ops, AnalysisData &ad) { + flat::ControlFlowAnalyzer linearize{ops, ad}; + Walk(ptree, linearize); +} + +#define INSTANTIATE_EXPLICITLY(T) \ + template void CreateFlatIR( \ + const parser::T &, std::list &, AnalysisData &) +INSTANTIATE_EXPLICITLY(MainProgram); +INSTANTIATE_EXPLICITLY(FunctionSubprogram); +INSTANTIATE_EXPLICITLY(SubroutineSubprogram); +} diff --git a/flang/lib/FIR/flattened.h b/flang/lib/FIR/flattened.h new file mode 100644 index 0000000..01ea15e --- /dev/null +++ b/flang/lib/FIR/flattened.h @@ -0,0 +1,256 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_FIR_FLATTENED_H_ +#define FORTRAN_FIR_FLATTENED_H_ + +#include "common.h" +#include "mixin.h" +#include "../parser/parse-tree.h" +#include +#include +#include +#include + +namespace Fortran::FIR { + +struct AnalysisData; + +namespace flat { + +// This is a flattened, linearized representation of the parse tree. It captures +// the executable specification of the input program. The flattened IR can be +// used to construct the Fortran IR. + +struct Op; +struct LabelOp; +struct GotoOp; +struct ReturnOp; +struct ConditionalGotoOp; +struct SwitchIOOp; +struct SwitchOp; +struct ActionOp; +struct BeginOp; +struct EndOp; +struct IndirectGotoOp; +struct DoIncrementOp; +struct DoCompareOp; + +using LabelRef = unsigned; +constexpr LabelRef unspecifiedLabel{~0u}; + +struct LabelBuilder; + +// target for a control-flow edge +struct LabelOp { + explicit LabelOp(LabelBuilder &builder); + LabelOp(const LabelOp &that); + LabelOp &operator=(const LabelOp &that); + void setReferenced() const; + bool isReferenced() const; + LabelRef get() const { return label_; } + operator LabelRef() const { return get(); } + void dump() const; + +private: + LabelBuilder &builder_; + LabelRef label_; +}; + +struct ArtificialJump {}; +constexpr ArtificialJump ARTIFICIAL{}; + +// a source of an absolute control flow edge +struct GotoOp + : public SumTypeCopyMixin { + template + GotoOp(const A &stmt, LabelRef dest) + : SumTypeCopyMixin{&stmt}, target{dest} {} + GotoOp(LabelRef dest) : SumTypeCopyMixin{ARTIFICIAL}, target{dest} {} + void dump() const; + + LabelRef target; +}; + +// control exits the procedure +struct ReturnOp : public SumTypeCopyMixin { + SUM_TYPE_COPY_MIXIN(ReturnOp) + void dump() const; + + template ReturnOp(const A &stmt) : SumTypeCopyMixin{&stmt} {} +}; + +// two-way branch based on a condition +struct ConditionalGotoOp + : public SumTypeCopyMixin *, + const parser::Statement *, const parser::IfStmt *, + const parser::Statement *> { + template + ConditionalGotoOp(const A &cond, LabelRef tb, LabelRef fb) + : SumTypeCopyMixin{&cond}, trueLabel{tb}, falseLabel{fb} {} + void dump() const; + + LabelRef trueLabel; + LabelRef falseLabel; +}; + +// multi-way branch based on a target-value of a variable +struct IndirectGotoOp { + IndirectGotoOp( + const semantics::Symbol *symbol, std::vector &&labelRefs) + : symbol{symbol}, labelRefs{labelRefs} {} + void dump() const; + + const semantics::Symbol *symbol; + std::vector labelRefs; +}; + +// intrinsic IO operations can return with an implied multi-way branch +struct SwitchIOOp + : public SumTypeCopyMixin { + template + SwitchIOOp(const A &io, LabelRef next, std::optional errLab, + std::optional eorLab = std::nullopt, + std::optional endLab = std::nullopt) + : SumTypeCopyMixin{&io}, next{next}, errLabel{errLab}, eorLabel{eorLab}, + endLabel{endLab} {} + void dump() const; + + LabelRef next; + std::optional errLabel; + std::optional eorLabel; + std::optional endLabel; +}; + +// multi-way branch based on conditions +struct SwitchOp + : public SumTypeCopyMixin { + template + SwitchOp(const A &sw, const std::vector &refs) + : SumTypeCopyMixin{&sw}, refs{refs} {} + void dump() const; + + const std::vector refs; +}; + +// a compute step +struct ActionOp { + ActionOp(const parser::Statement &stmt) : v{&stmt} {} + void dump() const; + + const parser::Statement *v; +}; + +#define CONSTRUCT_TYPES \ + const parser::AssociateConstruct *, const parser::BlockConstruct *, \ + const parser::CaseConstruct *, const parser::ChangeTeamConstruct *, \ + const parser::CriticalConstruct *, const parser::DoConstruct *, \ + const parser::IfConstruct *, const parser::SelectRankConstruct *, \ + const parser::SelectTypeConstruct *, const parser::WhereConstruct *, \ + const parser::ForallConstruct *, const parser::CompilerDirective *, \ + const parser::OpenMPConstruct *, const parser::OpenMPEndLoopDirective * + +// entry into a Fortran construct +struct BeginOp : public SumTypeCopyMixin { + SUM_TYPE_COPY_MIXIN(BeginOp) + void dump() const; + + template BeginOp(const A &c) : SumTypeCopyMixin{&c} {} +}; + +// exit from a Fortran construct +struct EndOp : public SumTypeCopyMixin { + SUM_TYPE_COPY_MIXIN(EndOp) + void dump() const; + + template EndOp(const A &c) : SumTypeCopyMixin{&c} {} +}; + +struct DoIncrementOp { + DoIncrementOp(const parser::DoConstruct &stmt) : v{&stmt} {} + void dump() const; + + const parser::DoConstruct *v; +}; + +struct DoCompareOp { + DoCompareOp(const parser::DoConstruct &stmt) : v{&stmt} {} + void dump() const; + + const parser::DoConstruct *v; +}; + +// the flat FIR is a list of Ops, where an Op is any of ... +struct Op : public SumTypeMixin { + template Op(const A &thing) : SumTypeMixin{thing} {} + + void dump() const { + std::visit([](const auto &op) { op.dump(); }, u); + } + + static void Build(std::list &ops, + const parser::Statement &ec, AnalysisData &ad); +}; + +// helper to build unique labels +struct LabelBuilder { + LabelBuilder(); + LabelRef getNext(); + void setReferenced(LabelRef label); + bool isReferenced(LabelRef label) const; + std::vector referenced; + unsigned counter; +}; + +LabelOp FetchLabel(AnalysisData &ad, const parser::Label &label); + +std::vector GetAssign( + AnalysisData &ad, const semantics::Symbol *symbol); +} + +struct AnalysisData { + std::map labelMap; + std::vector> + nameStack; + flat::LabelBuilder labelBuilder; + std::map> assignMap; +}; + +// entry-point into building the flat IR +template +void CreateFlatIR(const A &ptree, std::list &ops, AnalysisData &ad); + +#define EXPLICIT_INSTANTIATION(T) \ + extern template void CreateFlatIR( \ + const parser::T &, std::list &, AnalysisData &) +EXPLICIT_INSTANTIATION(MainProgram); +EXPLICIT_INSTANTIATION(FunctionSubprogram); +EXPLICIT_INSTANTIATION(SubroutineSubprogram); + +// dump flat IR +void dump(const std::list &ops); +} + +#endif // FORTRAN_FIR_FLATTENED_H_ -- 2.7.4