diff --git a/include/fir/.clang-format b/include/fir/.clang-format new file mode 100644 index 000000000000..a74fda4b6734 --- /dev/null +++ b/include/fir/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes diff --git a/include/flang/common/template.h b/include/flang/common/template.h index c8a18e704fb4..460f1a8bdaed 100644 --- a/include/flang/common/template.h +++ b/include/flang/common/template.h @@ -153,15 +153,12 @@ template struct VariantToTupleHelper> { template using VariantToTuple = typename VariantToTupleHelper::type; -template -struct AreTypesDistinctHelper { +template struct AreTypesDistinctHelper { static constexpr bool value() { - if constexpr (std::is_same_v) { - return false; - } if constexpr (sizeof...(REST) > 0) { - return AreTypesDistinctHelper::value() && - AreTypesDistinctHelper::value(); + // extra () for clang-format + return ((... && !std::is_same_v)) && + AreTypesDistinctHelper::value(); } return true; } diff --git a/include/flang/lower/.clang-format b/include/flang/lower/.clang-format new file mode 100644 index 000000000000..a74fda4b6734 --- /dev/null +++ b/include/flang/lower/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes diff --git a/include/flang/lower/PFTBuilder.h b/include/flang/lower/PFTBuilder.h new file mode 100644 index 000000000000..fc1c3e8b22bc --- /dev/null +++ b/include/flang/lower/PFTBuilder.h @@ -0,0 +1,388 @@ +//===-- include/flang/lower/PFTBuilder.h ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_LOWER_PFT_BUILDER_H_ +#define FORTRAN_LOWER_PFT_BUILDER_H_ + +#include "flang/common/template.h" +#include "flang/parser/parse-tree.h" +#include "llvm/Support/raw_ostream.h" +#include + +/// Build a light-weight tree over the parse-tree to help with lowering to FIR. +/// It is named Pre-FIR Tree (PFT) to underline it has no other usage than +/// helping lowering to FIR. +/// The PFT will capture pointers back into the parse tree, so the parse tree +/// data structure may not be changed between the construction of the +/// PFT and all of its uses. +/// +/// The PFT captures a structured view of the program. The program is a list of +/// units. Function like units will contain lists of evaluations. Evaluations +/// are either statements or constructs, where a construct contains a list of +/// evaluations. The resulting PFT structure can then be used to create FIR. + +namespace Fortran::lower { +namespace PFT { + +struct Evaluation; +struct Program; +struct ModuleLikeUnit; +struct FunctionLikeUnit; + +// TODO: A collection of Evaluations can obviously be any of the container +// types; leaving this as a std::list _for now_ because we reserve the right to +// insert PFT nodes in any order in O(1) time. +using EvaluationCollection = std::list; + +struct ParentType { + template + ParentType(A &parent) : p{&parent} {} + const std::variant + p; +}; + +/// Flags to describe the impact of parse-trees nodes on the program +/// control flow. These annotations to parse-tree nodes are later used to +/// build the control flow graph when lowering to FIR. +enum class CFGAnnotation { + None, // Node does not impact control flow. + Goto, // Node acts like a goto on the control flow. + CondGoto, // Node acts like a conditional goto on the control flow. + IndGoto, // Node acts like an indirect goto on the control flow. + IoSwitch, // Node is an IO statement with ERR, END, or EOR specifier. + Switch, // Node acts like a switch on the control flow. + Iterative, // Node creates iterations in the control flow. + FirStructuredOp, // Node is a structured loop. + Return, // Node triggers a return from the current procedure. + Terminate // Node terminates the program. +}; + +/// Compiler-generated jump +/// +/// This is used to convert implicit control-flow edges to explicit form in the +/// decorated PFT +struct CGJump { + CGJump(Evaluation &to) : target{to} {} + Evaluation ⌖ +}; + +/// Classify the parse-tree nodes from ExecutablePartConstruct + +using ActionStmts = std::tuple< + parser::AllocateStmt, parser::AssignmentStmt, parser::BackspaceStmt, + parser::CallStmt, parser::CloseStmt, parser::ContinueStmt, + parser::CycleStmt, parser::DeallocateStmt, parser::EndfileStmt, + parser::EventPostStmt, parser::EventWaitStmt, parser::ExitStmt, + parser::FailImageStmt, parser::FlushStmt, parser::FormTeamStmt, + parser::GotoStmt, parser::IfStmt, parser::InquireStmt, parser::LockStmt, + parser::NullifyStmt, parser::OpenStmt, parser::PointerAssignmentStmt, + parser::PrintStmt, parser::ReadStmt, parser::ReturnStmt, parser::RewindStmt, + parser::StopStmt, parser::SyncAllStmt, parser::SyncImagesStmt, + parser::SyncMemoryStmt, parser::SyncTeamStmt, parser::UnlockStmt, + parser::WaitStmt, parser::WhereStmt, parser::WriteStmt, + parser::ComputedGotoStmt, parser::ForallStmt, parser::ArithmeticIfStmt, + parser::AssignStmt, parser::AssignedGotoStmt, parser::PauseStmt>; + +using OtherStmts = std::tuple; + +using Constructs = + std::tuple; + +using ConstructStmts = std::tuple< + parser::AssociateStmt, parser::EndAssociateStmt, parser::BlockStmt, + parser::EndBlockStmt, parser::SelectCaseStmt, parser::CaseStmt, + parser::EndSelectStmt, parser::ChangeTeamStmt, parser::EndChangeTeamStmt, + parser::CriticalStmt, parser::EndCriticalStmt, parser::NonLabelDoStmt, + parser::EndDoStmt, parser::IfThenStmt, parser::ElseIfStmt, parser::ElseStmt, + parser::EndIfStmt, parser::SelectRankStmt, parser::SelectRankCaseStmt, + parser::SelectTypeStmt, parser::TypeGuardStmt, parser::WhereConstructStmt, + parser::MaskedElsewhereStmt, parser::ElsewhereStmt, parser::EndWhereStmt, + parser::ForallConstructStmt, parser::EndForallStmt>; + +template +constexpr static bool isActionStmt{common::HasMember}; + +template +constexpr static bool isConstruct{common::HasMember}; + +template +constexpr static bool isConstructStmt{common::HasMember}; + +template +constexpr static bool isOtherStmt{common::HasMember}; + +template +constexpr static bool isGenerated{std::is_same_v}; + +template +constexpr static bool isFunctionLike{common::HasMember< + A, std::tuple>}; + +/// Function-like units can contains lists of evaluations. These can be +/// (simple) statements or constructs, where a construct contains its own +/// evaluations. +struct Evaluation { + using EvalTuple = common::CombineTuples; + + /// Hide non-nullable pointers to the parse-tree node. + template + using MakeRefType = const A *const; + using EvalVariant = + common::CombineVariants, + std::variant>; + template + constexpr auto visit(A visitor) const { + return std::visit(common::visitors{ + [&](const auto *p) { return visitor(*p); }, + [&](auto &r) { return visitor(r); }, + }, + u); + } + template + constexpr const A *getIf() const { + if constexpr (!std::is_same_v) { + if (auto *ptr{std::get_if>(&u)}) { + return *ptr; + } + } else { + return std::get_if(&u); + } + return nullptr; + } + template + constexpr bool isA() const { + if constexpr (!std::is_same_v) { + return std::holds_alternative>(u); + } + return std::holds_alternative(u); + } + + Evaluation() = delete; + Evaluation(const Evaluation &) = delete; + Evaluation(Evaluation &&) = default; + + /// General ctor + template + Evaluation(const A &a, const ParentType &p, const parser::CharBlock &pos, + const std::optional &lab) + : u{&a}, parent{p}, pos{pos}, lab{lab} {} + + /// Compiler-generated jump + Evaluation(const CGJump &jump, const ParentType &p) + : u{jump}, parent{p}, cfg{CFGAnnotation::Goto} {} + + /// Construct ctor + template + Evaluation(const A &a, const ParentType &parent) : u{&a}, parent{parent} { + static_assert(PFT::isConstruct, "must be a construct"); + } + + constexpr bool isActionOrGenerated() const { + return visit(common::visitors{ + [](auto &r) { + using T = std::decay_t; + return isActionStmt || isGenerated; + }, + }); + } + + constexpr bool isStmt() const { + return visit(common::visitors{ + [](auto &r) { + using T = std::decay_t; + static constexpr bool isStmt{isActionStmt || isOtherStmt || + isConstructStmt}; + static_assert(!(isStmt && PFT::isConstruct), + "statement classification is inconsistent"); + return isStmt; + }, + }); + } + constexpr bool isConstruct() const { return !isStmt(); } + + /// Set the type of originating control flow type for this evaluation. + void setCFG(CFGAnnotation a, Evaluation *cstr) { + cfg = a; + setBranches(cstr); + } + + /// Is this evaluation a control-flow origin? (The PFT must be annotated) + bool isControlOrigin() const { return cfg != CFGAnnotation::None; } + + /// Is this evaluation a control-flow target? (The PFT must be annotated) + bool isControlTarget() const { return isTarget; } + + /// Set the containsBranches flag iff this evaluation (a construct) contains + /// control flow + void setBranches() { containsBranches = true; } + + EvaluationCollection *getConstructEvals() { + auto *evals{subs.get()}; + if (isStmt() && !evals) { + return nullptr; + } + if (isConstruct() && evals) { + return evals; + } + llvm_unreachable("evaluation subs is inconsistent"); + return nullptr; + } + + /// Set that the construct `cstr` (if not a nullptr) has branches. + static void setBranches(Evaluation *cstr) { + if (cstr) + cstr->setBranches(); + } + + EvalVariant u; + ParentType parent; + parser::CharBlock pos; + std::optional lab; + std::unique_ptr subs; // construct sub-statements + CFGAnnotation cfg{CFGAnnotation::None}; + bool isTarget{false}; // this evaluation is a control target + bool containsBranches{false}; // construct contains branches +}; + +/// A program is a list of program units. +/// These units can be function like, module like, or block data +struct ProgramUnit { + template + ProgramUnit(const A &ptr, const ParentType &parent) + : p{&ptr}, parent{parent} {} + ProgramUnit(ProgramUnit &&) = default; + ProgramUnit(const ProgramUnit &) = delete; + + const std::variant< + const parser::MainProgram *, const parser::FunctionSubprogram *, + const parser::SubroutineSubprogram *, const parser::Module *, + const parser::Submodule *, const parser::SeparateModuleSubprogram *, + const parser::BlockData *> + p; + ParentType parent; +}; + +/// Function-like units have similar structure. They all can contain executable +/// statements as well as other function-like units (internal procedures and +/// function statements). +struct FunctionLikeUnit : public ProgramUnit { + // wrapper statements for function-like syntactic structures + using FunctionStatement = + std::variant *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *>; + + FunctionLikeUnit(const parser::MainProgram &f, const ParentType &parent); + FunctionLikeUnit(const parser::FunctionSubprogram &f, + const ParentType &parent); + FunctionLikeUnit(const parser::SubroutineSubprogram &f, + const ParentType &parent); + FunctionLikeUnit(const parser::SeparateModuleSubprogram &f, + const ParentType &parent); + FunctionLikeUnit(FunctionLikeUnit &&) = default; + FunctionLikeUnit(const FunctionLikeUnit &) = delete; + + bool isMainProgram() { + return std::holds_alternative< + const parser::Statement *>(funStmts.back()); + } + const parser::FunctionStmt *getFunction() { + return getA(); + } + const parser::SubroutineStmt *getSubroutine() { + return getA(); + } + const parser::MpSubprogramStmt *getMPSubp() { + return getA(); + } + + std::list funStmts; // begin/end pair + EvaluationCollection evals; // statements + std::list funcs; // internal procedures + +private: + template + const A *getA() { + if (auto p = std::get_if *>(&funStmts.front())) + return &(*p)->statement; + return nullptr; + } +}; + +/// Module-like units have similar structure. They all can contain a list of +/// function-like units. +struct ModuleLikeUnit : public ProgramUnit { + // wrapper statements for module-like syntactic structures + using ModuleStatement = + std::variant *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *>; + + ModuleLikeUnit(const parser::Module &m, const ParentType &parent); + ModuleLikeUnit(const parser::Submodule &m, const ParentType &parent); + ~ModuleLikeUnit() = default; + ModuleLikeUnit(ModuleLikeUnit &&) = default; + ModuleLikeUnit(const ModuleLikeUnit &) = delete; + + std::list modStmts; + std::list funcs; +}; + +struct BlockDataUnit : public ProgramUnit { + BlockDataUnit(const parser::BlockData &bd, const ParentType &parent); + BlockDataUnit(BlockDataUnit &&) = default; + BlockDataUnit(const BlockDataUnit &) = delete; +}; + +/// A Program is the top-level PFT +struct Program { + using Units = std::variant; + + Program() = default; + Program(Program &&) = default; + Program(const Program &) = delete; + + std::list &getUnits() { return units; } + +private: + std::list units; +}; + +} // namespace PFT + +/// Create an PFT from the parse tree +std::unique_ptr createPFT(const parser::Program &root); + +/// Decorate the PFT with control flow annotations +/// +/// The PFT must be decorated with control-flow annotations to prepare it for +/// use in generating a CFG-like structure. +void annotateControl(PFT::Program &); + +void dumpPFT(llvm::raw_ostream &o, PFT::Program &); + +} // namespace Fortran::lower + +#endif // FORTRAN_LOWER_PFT_BUILDER_H_ diff --git a/include/flang/optimizer/.clang-format b/include/flang/optimizer/.clang-format new file mode 100644 index 000000000000..a74fda4b6734 --- /dev/null +++ b/include/flang/optimizer/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes diff --git a/include/flang/parser/dump-parse-tree.h b/include/flang/parser/dump-parse-tree.h index aca18137f955..a6181834c94f 100644 --- a/include/flang/parser/dump-parse-tree.h +++ b/include/flang/parser/dump-parse-tree.h @@ -40,11 +40,11 @@ class ParseTreeDumper { std::ostream &out, const AnalyzedObjectsAsFortran *asFortran = nullptr) : out_(out), asFortran_{asFortran} {} - constexpr const char *GetNodeName(const char *) { return "char *"; } + static constexpr const char *GetNodeName(const char *) { return "char *"; } #define NODE_NAME(T, N) \ - constexpr const char *GetNodeName(const T &) { return N; } + static constexpr const char *GetNodeName(const T &) { return N; } #define NODE_ENUM(T, E) \ - std::string GetNodeName(const T::E &x) { \ + static std::string GetNodeName(const T::E &x) { \ return #E " = "s + T::EnumToString(x); \ } #define NODE(T1, T2) NODE_NAME(T1::T2, #T2) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 343195bae2b0..35c3e139b1bf 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -9,5 +9,6 @@ add_subdirectory(common) add_subdirectory(evaluate) add_subdirectory(decimal) +add_subdirectory(lower) add_subdirectory(parser) add_subdirectory(semantics) diff --git a/lib/fir/.clang-format b/lib/fir/.clang-format new file mode 100644 index 000000000000..a74fda4b6734 --- /dev/null +++ b/lib/fir/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes diff --git a/lib/lower/.clang-format b/lib/lower/.clang-format new file mode 100644 index 000000000000..a74fda4b6734 --- /dev/null +++ b/lib/lower/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes diff --git a/lib/lower/CMakeLists.txt b/lib/lower/CMakeLists.txt new file mode 100644 index 000000000000..25802de2a0d1 --- /dev/null +++ b/lib/lower/CMakeLists.txt @@ -0,0 +1,15 @@ +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error") + +add_library(FortranLower + PFTBuilder.cpp +) + +target_link_libraries(FortranLower + LLVMSupport +) + +install (TARGETS FortranLower + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) diff --git a/lib/lower/PFTBuilder.cpp b/lib/lower/PFTBuilder.cpp new file mode 100644 index 000000000000..95a7860b7321 --- /dev/null +++ b/lib/lower/PFTBuilder.cpp @@ -0,0 +1,716 @@ +//===-- lib/lower/PFTBuilder.cc -------------------------------------------===// +// +// 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" +#include "llvm/ADT/DenseMap.h" +#include +#include +#include + +namespace Fortran::lower { +namespace { + +/// Helpers to unveil parser node inside parser::Statement<>, +/// parser::UnlabeledStatement, and common::Indirection<> +template +struct RemoveIndirectionHelper { + using Type = A; + static constexpr const Type &unwrap(const A &a) { return a; } +}; +template +struct RemoveIndirectionHelper> { + using Type = A; + static constexpr const Type &unwrap(const common::Indirection &a) { + return a.value(); + } +}; + +template +const auto &removeIndirection(const A &a) { + return RemoveIndirectionHelper::unwrap(a); +} + +template +struct UnwrapStmt { + static constexpr bool isStmt{false}; +}; +template +struct UnwrapStmt> { + static constexpr bool isStmt{true}; + using Type = typename RemoveIndirectionHelper::Type; + constexpr UnwrapStmt(const parser::Statement &a) + : unwrapped{removeIndirection(a.statement)}, pos{a.source}, lab{a.label} { + } + const Type &unwrapped; + parser::CharBlock pos; + std::optional lab; +}; +template +struct UnwrapStmt> { + static constexpr bool isStmt{true}; + using Type = typename RemoveIndirectionHelper::Type; + constexpr UnwrapStmt(const parser::UnlabeledStatement &a) + : unwrapped{removeIndirection(a.statement)}, pos{a.source} {} + const Type &unwrapped; + parser::CharBlock pos; + std::optional 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 result() { return std::move(pgm); } + + template + constexpr bool Pre(const A &a) { + bool visit{true}; + if constexpr (PFT::isFunctionLike) { + return enterFunc(a); + } else if constexpr (PFT::isConstruct) { + return enterConstruct(a); + } else if constexpr (UnwrapStmt::isStmt) { + using T = typename UnwrapStmt::Type; + // Node "a" being visited has one of the following types: + // Statement, Statement, UnlabeledStatement, + // or UnlabeledStatement> + auto stmt{UnwrapStmt(a)}; + if constexpr (PFT::isConstructStmt || PFT::isOtherStmt) { + addEval(PFT::Evaluation{stmt.unwrapped, parents.back(), stmt.pos, + stmt.lab}); + visit = false; + } else if constexpr (std::is_same_v) { + addEval(makeEvalAction(stmt.unwrapped, stmt.pos, stmt.lab)); + visit = false; + } + } + return visit; + } + + template + constexpr void Post(const A &) { + if constexpr (PFT::isFunctionLike) { + exitFunc(); + } else if constexpr (PFT::isConstruct) { + 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 + &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 &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 &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 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 + 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 + 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 + 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 + A &addUnit(A &&unit) { + pgm->getUnits().emplace_back(std::move(unit)); + return std::get(pgm->getUnits().back()); + } + + template + 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 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 *funclist{nullptr}; + /// evallist is a stack of pointer to FunctionLikeUnit::evals (or + /// Evaluation::subs) that are being build. + std::vector evallist; + std::vector parents; +}; + +template +constexpr bool hasLabel(const A &stmt) { + auto isLabel{ + [](const auto &v) { return std::holds_alternative, + "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) { + outputStream << "PFT root node:" << getNodeIndex(pft) << "\n"; + for (auto &unit : pft.getUnits()) { + std::visit(common::visitors{ + [&](PFT::BlockDataUnit &unit) { + outputStream << getNodeIndex(unit) << " "; + outputStream << "BlockData: "; + dumpParentInfo(outputStream, unit); + 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); + }, + }); + } + + template + void dumpParentInfo(llvm::raw_ostream &stream, const A &evalOrUnit) { + stream << " parent:"; + std::visit( + common::visitors{ + [&](const auto *parent) { stream << getNodeIndex(*parent); }, + }, + evalOrUnit.parent.p); + stream << " "; + } + + 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 << ">>"; + dumpParentInfo(outputStream, eval); + outputStream << "\n"; + dumpEvalList(outputStream, *subs, indent + 1); + outputStream << indentString << "<>\n"; + } else { + outputStream << name; + dumpParentInfo(outputStream, eval); + 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{}; + std::visit( + common::visitors{ + [&](const parser::Statement *statement) { + unitKind = "Program"; + name = statement->statement.v.ToString(); + }, + [&](const parser::Statement *statement) { + unitKind = "Function"; + name = std::get(statement->statement.t).ToString(); + header = statement->source.ToString(); + }, + [&](const parser::Statement *statement) { + unitKind = "Subroutine"; + name = std::get(statement->statement.t).ToString(); + header = statement->source.ToString(); + }, + [&](const parser::Statement *statement) { + unitKind = "MpSubprogram"; + name = statement->statement.v.ToString(); + header = statement->source.ToString(); + }, + [&](auto *) { + if (std::get_if + *>(&functionLikeUnit.funStmts.back())) { + unitKind = "Program"; + name = ""; + } else { + unitKind = ">>>>> Error - no program unit <<<<<"; + } + }, + }, + functionLikeUnit.funStmts.front()); + outputStream << unitKind << ' ' << name; + dumpParentInfo(outputStream, functionLikeUnit); + 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: "; + dumpParentInfo(outputStream, moduleLikeUnit); + outputStream << "\nContains\n"; + for (auto &func : moduleLikeUnit.funcs) + dumpFunctionLikeUnit(outputStream, func); + outputStream << "EndContains\nEndModuleLike\n\n"; + } + + template + std::size_t getNodeIndex(const T &node) { + auto addr{static_cast(&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 nodeIndexes; + std::size_t nextIndex{1}; // 0 is the root +}; + +} // namespace + +PFT::FunctionLikeUnit::FunctionLikeUnit(const parser::MainProgram &func, + const PFT::ParentType &parent) + : ProgramUnit{func, parent} { + auto &ps{ + std::get>>(func.t)}; + if (ps.has_value()) { + const parser::Statement &statement{ps.value()}; + funStmts.push_back(&statement); + } + funStmts.push_back( + &std::get>(func.t)); +} + +PFT::FunctionLikeUnit::FunctionLikeUnit(const parser::FunctionSubprogram &func, + const PFT::ParentType &parent) + : ProgramUnit{func, parent} { + funStmts.push_back( + &std::get>(func.t)); + funStmts.push_back( + &std::get>(func.t)); +} + +PFT::FunctionLikeUnit::FunctionLikeUnit( + const parser::SubroutineSubprogram &func, const PFT::ParentType &parent) + : ProgramUnit{func, parent} { + funStmts.push_back( + &std::get>(func.t)); + funStmts.push_back( + &std::get>(func.t)); +} + +PFT::FunctionLikeUnit::FunctionLikeUnit( + const parser::SeparateModuleSubprogram &func, const PFT::ParentType &parent) + : ProgramUnit{func, parent} { + funStmts.push_back( + &std::get>(func.t)); + funStmts.push_back( + &std::get>(func.t)); +} + +PFT::ModuleLikeUnit::ModuleLikeUnit(const parser::Module &m, + const PFT::ParentType &parent) + : ProgramUnit{m, parent} { + modStmts.push_back(&std::get>(m.t)); + modStmts.push_back(&std::get>(m.t)); +} + +PFT::ModuleLikeUnit::ModuleLikeUnit(const parser::Submodule &m, + const PFT::ParentType &parent) + : ProgramUnit{m, parent} { + modStmts.push_back(&std::get>(m.t)); + modStmts.push_back( + &std::get>(m.t)); +} + +PFT::BlockDataUnit::BlockDataUnit(const parser::BlockData &bd, + const PFT::ParentType &parent) + : ProgramUnit{bd, parent} {} + +std::unique_ptr 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 an PFT. +void dumpPFT(llvm::raw_ostream &outputStream, PFT::Program &pft) { + PFTDumper{}.dumpPFT(outputStream, pft); +} + +} // namespace Fortran::lower diff --git a/lib/optimizer/.clang-format b/lib/optimizer/.clang-format new file mode 100644 index 000000000000..a74fda4b6734 --- /dev/null +++ b/lib/optimizer/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes diff --git a/test-lit/CMakeLists.txt b/test-lit/CMakeLists.txt index 0819e57b81f3..111814303b0b 100644 --- a/test-lit/CMakeLists.txt +++ b/test-lit/CMakeLists.txt @@ -1,6 +1,8 @@ # Test runner infrastructure for Flang. This configures the Flang test trees # for use by Lit, and delegates to LLVM's lit test handlers. +set(FLANG_INTRINSIC_MODULES_DIR ${FLANG_BINARY_DIR}/tools/f18/include) + configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py diff --git a/test-lit/lit.cfg.py b/test-lit/lit.cfg.py index c27e6a616b85..3ca3c8ad23f7 100644 --- a/test-lit/lit.cfg.py +++ b/test-lit/lit.cfg.py @@ -61,9 +61,12 @@ # to search to ensure that we get the tools just built and not some random # tools that might happen to be in the user's PATH. tool_dirs = [config.llvm_tools_dir, config.flang_tools_dir] +flang_includes = "-I" + config.flang_intrinsic_modules_dir tools = [ToolSubst('%flang', command=FindTool('flang'), unresolved='fatal'), - ToolSubst('%f18', command=FindTool('f18'), unresolved='fatal')] + ToolSubst('%f18', command=FindTool('f18'), unresolved='fatal'), + ToolSubst('%f18_with_includes', command=FindTool('f18'), + extra_args=[flang_includes], unresolved='fatal')] llvm_config.add_tool_substitutions(tools, tool_dirs) diff --git a/test-lit/lit.site.cfg.py.in b/test-lit/lit.site.cfg.py.in index ad31bf1594f9..d00f3856fb41 100644 --- a/test-lit/lit.site.cfg.py.in +++ b/test-lit/lit.site.cfg.py.in @@ -6,6 +6,7 @@ config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" config.flang_obj_root = "@FLANG_BINARY_DIR@" config.flang_src_dir = "@FLANG_SOURCE_DIR@" config.flang_tools_dir = "@FLANG_TOOLS_DIR@" +config.flang_intrinsic_modules_dir = "@FLANG_INTRINSIC_MODULES_DIR@" config.python_executable = "@PYTHON_EXECUTABLE@" # Support substitution of the tools_dir with user parameters. This is diff --git a/test-lit/lower/pre-fir-tree01.f90 b/test-lit/lower/pre-fir-tree01.f90 new file mode 100644 index 000000000000..4a6f3b39bfd3 --- /dev/null +++ b/test-lit/lower/pre-fir-tree01.f90 @@ -0,0 +1,113 @@ +! RUN: %f18 -fdebug-pre-fir-tree -fparse-only %s | FileCheck %s + +! Test structure of the Pre-FIR tree + +! CHECK: PFT root node:[[#%u, ROOT:]] +! CHECK: [[#%u, FOO:]]{{.*}}Subroutine foo{{.*}}parent:[[#ROOT]] +subroutine foo() + ! CHECK: [[#%u, DO1:]]{{.*}}<>{{.*}}parent:[[#FOO]] + ! CHECK: NonLabelDoStmt{{.*}}parent:[[#DO1]] + do i=1,5 + ! CHECK: PrintStmt{{.*}}parent:[[#DO1]] + print *, "hey" + ! CHECK: [[#%u, DO2:]]{{.*}}<>{{.*}}parent:[[#DO1]] + do j=1,5 + ! CHECK: PrintStmt{{.*}}parent:[[#DO2]] + print *, "hello", i, j + ! CHECK: EndDoStmt{{.*}}parent:[[#DO2]] + end do + ! CHECK: EndDoStmt{{.*}}parent:[[#DO1]] + end do +! CHECK: EndSubroutine +end subroutine + +! CHECK: BlockData{{.*}}parent:[[#ROOT]] +block data + integer, parameter :: n = 100 + integer, dimension(n) :: a, b, c + common /arrays/ a, b, c +end + +! CHECK: [[#%u, TEST_MOD:]]{{.*}}ModuleLike{{.*}}parent:[[#ROOT]] +module test_mod +interface + ! check specification parts are not part of the PFT. + ! CHECK-NOT: node + module subroutine dump() + end subroutine +end interface + integer :: xdim + real, allocatable :: pressure(:) +contains + ! CHECK: [[#%u, M_FOO:]]{{.*}}Subroutine foo{{.*}}parent:[[#TEST_MOD]] + subroutine foo() + contains + ! CHECK: [[#%u, SUBFOO:]]{{.*}}Subroutine subfoo{{.*}}parent:[[#M_FOO]] + subroutine subfoo() + end subroutine + ! CHECK: [[#%u, SUBFOO2:]]{{.*}}Function subfoo2{{.*}}parent:[[#M_FOO]] + function subfoo2() + end function + end subroutine + + ! CHECK: [[#%u, M_FOO2:]]{{.*}}Function foo2{{.*}}parent:[[#TEST_MOD]] + function foo2(i, j) + integer i, j, foo2 + ! CHECK: AssignmentStmt{{.*}}parent:[[#M_FOO2]] + foo2 = i + j + contains + ! CHECK: [[#%u, SUBFOO:]]{{.*}}Subroutine subfoo{{.*}}parent:[[#M_FOO2]] + subroutine subfoo() + end subroutine + end function +end module + +! CHECK: [[#%u, SUB_MOD:]]{{.*}}ModuleLike{{.*}}parent:[[#ROOT]] +submodule (test_mod) test_mod_impl +contains + ! CHECK: [[#%u, SUBM_FOO:]]{{.*}}Subroutine foo{{.*}}parent:[[#SUB_MOD]] + subroutine foo() + contains + ! CHECK: [[#%u, SUBFOO:]]{{.*}}Subroutine subfoo{{.*}}parent:[[#SUBM_FOO]] + subroutine subfoo() + end subroutine + ! CHECK: [[#%u, SUBFOO2:]]{{.*}}Function subfoo2{{.*}}parent:[[#SUBM_FOO]] + function subfoo2() + end function + end subroutine + ! CHECK: [[#%u, MP_DUMP:]]{{.*}}MpSubprogram dump{{.*}}parent:[[#SUB_MOD]] + module procedure dump + ! CHECK: FormatStmt{{.*}}parent:[[#MP_DUMP]] +11 format (2E16.4, I6) + ! CHECK: [[#%u, IF1:]]{{.*}}<>{{.*}}parent:[[#MP_DUMP]] + ! CHECK: IfThenStmt{{.*}}parent:[[#IF1]] + if (xdim > 100) then + ! CHECK: PrintStmt{{.*}}parent:[[#IF1]] + print *, "test: ", xdim + ! CHECK: ElseStmt{{.*}}parent:[[#IF1]] + else + ! CHECK: WriteStmt{{.*}}parent:[[#IF1]] + write (*, 11) "test: ", xdim, pressure + ! CHECK: EndIfStmt{{.*}}parent:[[#IF1]] + end if + end procedure +end submodule + +! CHECK: BlockData{{.*}}parent:[[#ROOT]] +block data + integer i, j, k + common /indexes/ i, j, k +end + +! CHECK: [[#%u, BAR:]]{{.*}}Function bar{{.*}}parent:[[#ROOT]] +function bar() +end function + +! CHECK: [[#%u, PROG:]]{{.*}}Program {{.*}}parent:[[#ROOT]] + ! check specification parts are not part of the PFT. + ! CHECK-NOT: node + use test_mod + real, allocatable :: x(:) + ! CHECK: AllocateStmt{{.*}}parent:[[#PROG]] + allocate(x(foo2(10, 30))) +end diff --git a/test-lit/lower/pre-fir-tree02.f90 b/test-lit/lower/pre-fir-tree02.f90 new file mode 100644 index 000000000000..518326f14f0b --- /dev/null +++ b/test-lit/lower/pre-fir-tree02.f90 @@ -0,0 +1,319 @@ +! RUN: %f18 -fdebug-pre-fir-tree -fparse-only %s | FileCheck %s + +! Test Pre-FIR Tree captures all the intended nodes from the parse-tree +! Coarray and OpenMP related nodes are tested in other files. + +! CHECK: PFT root node:[[#%u, ROOT:]] +! CHECK: [[#%u, PROG:]]{{.*}}Program test_prog{{.*}}parent:[[#ROOT]] +program test_prog + ! Check specification part is not part of the tree. + interface + subroutine incr(i) + integer, intent(inout) :: i + end subroutine + end interface + integer :: i, j, k + real, allocatable, target :: x(:) + real :: y(100) + ! CHECK-NOT: node + ! CHECK: [[#%u, DO1:]]{{.*}}<>{{.*}}parent:[[#PROG]] + ! CHECK: NonLabelDoStmt{{.*}}parent:[[#DO1]] + do i=1,5 + ! CHECK: PrintStmt{{.*}}parent:[[#DO1]] + print *, "hey" + ! CHECK: [[#%u, DO2:]]{{.*}}<>{{.*}}parent:[[#DO1]] + ! CHECK: NonLabelDoStmt{{.*}}parent:[[#DO2]] + do j=1,5 + ! CHECK: PrintStmt{{.*}}parent:[[#DO2]] + print *, "hello", i, j + ! CHECK: EndDoStmt{{.*}}parent:[[#DO2]] + end do + ! CHECK: EndDoStmt{{.*}}parent:[[#DO1]] + end do + + ! CHECK: [[#%u, ASSOC:]]{{.*}}<>{{.*}}parent:[[#PROG]] + ! CHECK: AssociateStmt{{.*}}parent:[[#ASSOC]] + associate (k => i + j) + ! CHECK: AllocateStmt{{.*}}parent:[[#ASSOC]] + allocate(x(k)) + ! CHECK: EndAssociateStmt{{.*}}parent:[[#ASSOC]] + end associate + + ! CHECK: [[#%u, BLOCK:]]{{.*}}<>{{.*}}parent:[[#PROG]] + ! CHECK: BlockStmt{{.*}}parent:[[#BLOCK]] + block + integer :: k, l + real, pointer :: p(:) + ! CHECK: PointerAssignmentStmt{{.*}}parent:[[#BLOCK]] + p => x + ! CHECK: AssignmentStmt{{.*}}parent:[[#BLOCK]] + k = size(p) + ! CHECK: AssignmentStmt{{.*}}parent:[[#BLOCK]] + l = 1 + ! CHECK: [[#%u, SELECTCASE:]]{{.*}}<>{{.*}}parent:[[#BLOCK]] + ! CHECK: SelectCaseStmt{{.*}}parent:[[#SELECTCASE]] + select case (k) + ! CHECK: CaseStmt{{.*}}parent:[[#SELECTCASE]] + case (:0) + ! CHECK: NullifyStmt{{.*}}parent:[[#SELECTCASE]] + nullify(p) + ! CHECK: CaseStmt{{.*}}parent:[[#SELECTCASE]] + case (1) + ! CHECK: [[#%u, IFTHEN:]]{{.*}}<>{{.*}}parent:[[#SELECTCASE]] + ! CHECK: IfThenStmt{{.*}}parent:[[#IFTHEN]] + if (p(1)>0.) then + ! CHECK: PrintStmt{{.*}}parent:[[#IFTHEN]] + print *, "+" + ! CHECK: ElseIfStmt{{.*}}parent:[[#IFTHEN]] + else if (p(1)==0.) then + ! CHECK: PrintStmt{{.*}}parent:[[#IFTHEN]] + print *, "0." + ! CHECK: ElseStmt{{.*}}parent:[[#IFTHEN]] + else + ! CHECK: PrintStmt{{.*}}parent:[[#IFTHEN]] + print *, "-" + ! CHECK: EndIfStmt{{.*}}parent:[[#IFTHEN]] + end if + ! CHECK: CaseStmt{{.*}}parent:[[#SELECTCASE]] + case (2:10) + ! CHECK: CaseStmt{{.*}}parent:[[#SELECTCASE]] + case default + ! Note: label-do-loop are canonicalized into do constructs + ! CHECK: [[#%u, DO3:]]{{.*}}<>{{.*}}parent:[[#SELECTCASE]] + ! CHECK: NonLabelDoStmt{{.*}}parent:[[#DO3]] + do 22 while(l<=k) + ! CHECK: IfStmt{{.*}}parent:[[#DO3]] + if (p(l)<0.) p(l)=cos(p(l)) + ! CHECK: CallStmt{{.*}}parent:[[#DO3]] +22 call incr(l) + ! CHECK: EndDoStmt{{.*}}parent:[[#DO3]] + ! CHECK: CaseStmt{{.*}}parent:[[#SELECTCASE]] + case (100:) + ! CHECK: EndSelectStmt{{.*}}parent:[[#SELECTCASE]] + end select + ! CHECK: EndBlockStmt{{.*}}parent:[[#BLOCK]] + end block + + ! CHECK-NOT: WhereConstruct + ! CHECK: WhereStmt{{.*}}parent:[[#PROG]] + where (x > 1.) x = x/2. + + ! CHECK: [[#%u, WHERE:]]{{.*}}<>{{.*}}parent:[[#PROG]] + ! CHECK: WhereConstructStmt{{.*}}parent:[[#WHERE]] + where (x == 0.) + ! CHECK: AssignmentStmt{{.*}}parent:[[#WHERE]] + x = 0.01 + ! CHECK: MaskedElsewhereStmt{{.*}}parent:[[#WHERE]] + elsewhere (x < 0.5) + ! CHECK: AssignmentStmt{{.*}}parent:[[#WHERE]] + x = x*2. + ! CHECK: [[#%u, WHERE2:]]{{.*}}<>{{.*}}parent:[[#WHERE]] + where (y > 0.4) + ! CHECK: AssignmentStmt{{.*}}parent:[[#WHERE2]] + y = y/2. + end where + ! CHECK: ElsewhereStmt{{.*}}parent:[[#WHERE]] + elsewhere + ! CHECK: AssignmentStmt{{.*}}parent:[[#WHERE]] + x = x + 1. + ! CHECK: EndWhereStmt{{.*}}parent:[[#WHERE]] + end where + + ! CHECK-NOT: ForAllConstruct + ! CHECK: ForallStmt{{.*}}parent:[[#PROG]] + forall (i = 1:5) x = y(i) + + ! CHECK: [[#%u, FORALL:]]{{.*}}<>{{.*}}parent:[[#PROG]] + ! CHECK: ForallConstructStmt{{.*}}parent:[[#FORALL]] + forall (i = 1:5) + ! CHECK: AssignmentStmt{{.*}}parent:[[#FORALL]] + x(i) = x(i) + y(10*i) + ! CHECK: EndForallStmt{{.*}}parent:[[#FORALL]] + end forall + + ! CHECK: DeallocateStmt{{.*}}parent:[[#PROG]] + deallocate(x) +end + +! CHECK: [[#%u, MOD:]]{{.*}}ModuleLike{{.*}}parent:[[#ROOT]] +module test + type :: a_type + integer :: x + end type + type, extends(a_type) :: b_type + integer :: y + end type +contains + ! CHECK: [[#%u, FOO:]]{{.*}}Function foo{{.*}}parent:[[#MOD]] + function foo(x) + real x(..) + integer :: foo + ! CHECK: [[#%u, SELECTRANK:]]{{.*}}<>{{.*}}parent:[[#FOO]] + ! CHECK: SelectRankStmt{{.*}}parent:[[#SELECTRANK]] + select rank(x) + ! CHECK: SelectRankCaseStmt{{.*}}parent:[[#SELECTRANK]] + rank (0) + ! CHECK: AssignmentStmt{{.*}}parent:[[#SELECTRANK]] + foo = 0 + ! CHECK: SelectRankCaseStmt{{.*}}parent:[[#SELECTRANK]] + rank (*) + ! CHECK: AssignmentStmt{{.*}}parent:[[#SELECTRANK]] + foo = -1 + ! CHECK: SelectRankCaseStmt{{.*}}parent:[[#SELECTRANK]] + rank (1) + ! CHECK: AssignmentStmt{{.*}}parent:[[#SELECTRANK]] + foo = 1 + ! CHECK: SelectRankCaseStmt{{.*}}parent:[[#SELECTRANK]] + rank default + ! CHECK: AssignmentStmt{{.*}}parent:[[#SELECTRANK]] + foo = 2 + ! CHECK: EndSelectStmt{{.*}}parent:[[#SELECTRANK]] + end select + end function + + ! CHECK: [[#%u, BAR:]]{{.*}}Function bar{{.*}}parent:[[#MOD]] + function bar(x) + class(*) :: x + ! CHECK: [[#%u, SELECTTYPE:]]{{.*}}<>{{.*}}parent:[[#BAR]] + ! CHECK: SelectTypeStmt{{.*}}parent:[[#SELECTTYPE]] + select type(x) + ! CHECK: TypeGuardStmt{{.*}}parent:[[#SELECTTYPE]] + type is (integer) + ! CHECK: AssignmentStmt{{.*}}parent:[[#SELECTTYPE]] + bar = 0 + ! CHECK: TypeGuardStmt{{.*}}parent:[[#SELECTTYPE]] + class is (a_type) + ! CHECK: AssignmentStmt{{.*}}parent:[[#SELECTTYPE]] + bar = 1 + ! CHECK: ReturnStmt{{.*}}parent:[[#SELECTTYPE]] + return + ! CHECK: TypeGuardStmt{{.*}}parent:[[#SELECTTYPE]] + class default + ! CHECK: AssignmentStmt{{.*}}parent:[[#SELECTTYPE]] + bar = -1 + ! CHECK: EndSelectStmt{{.*}}parent:[[#SELECTTYPE]] + end select + end function + + ! CHECK: [[#%u, SUB:]]{{.*}}Subroutine sub{{.*}}parent:[[#MOD]] + subroutine sub(a) + real(4):: a + ! CompilerDirective + ! CHECK: <>{{.*}}parent:[[#SUB]] + !DIR$ IGNORE_TKR a + end subroutine + + +end module + +! CHECK: [[#%u, ALTSUB:]]{{.*}}Subroutine altreturn{{.*}}parent:[[#ROOT]] +subroutine altreturn(i, j, *, *) + ! CHECK: [[#%u, IFTHEN:]]{{.*}}<>{{.*}}parent:[[#ALTSUB]] + if (i>j) then + ! CHECK: ReturnStmt{{.*}}parent:[[#IFTHEN]] + return 1 + else + ! CHECK: ReturnStmt{{.*}}parent:[[#IFTHEN]] + return 2 + end if +end subroutine + + +! Remaining TODO + +! CHECK: [[#%u, IO:]]{{.*}}Subroutine iostmts{{.*}}parent:[[#ROOT]] +subroutine iostmts(filename, a, b, c) + character(*) :: filename + integer :: length + logical :: file_is_opened + real, a, b ,c + ! CHECK: InquireStmt{{.*}}parent:[[#]] + inquire(file=filename, opened=file_is_opened) + ! CHECK: [[#%u, IFTHEN:]]{{.*}}<>{{.*}}parent:[[#IO]] + if (file_is_opened) then + ! CHECK: OpenStmt{{.*}}parent:[[#IFTHEN]] + open(10, FILE=filename) + end if + ! CHECK: ReadStmt{{.*}}parent:[[#IO]] + read(10, *) length + ! CHECK: RewindStmt{{.*}}parent:[[#IO]] + rewind 10 + ! CHECK: NamelistStmt{{.*}}parent:[[#IO]] + namelist /nlist/ a, b, c + ! CHECK: WriteStmt{{.*}}parent:[[#IO]] + write(10, NML=nlist) + ! CHECK: BackspaceStmt{{.*}}parent:[[#IO]] + backspace(10) + ! CHECK: FormatStmt{{.*}}parent:[[#IO]] +1 format (1PE12.4) + ! CHECK: WriteStmt{{.*}}parent:[[#IO]] + write (10, 1) a + ! CHECK: EndfileStmt{{.*}}parent:[[#IO]] + endfile 10 + ! CHECK: FlushStmt{{.*}}parent:[[#IO]] + flush 10 + ! CHECK: WaitStmt{{.*}}parent:[[#IO]] + wait(10) + ! CHECK: CloseStmt{{.*}}parent:[[#IO]] + close(10) +end subroutine + + +! CHECK: [[#%u, SUB2:]]{{.*}}Subroutine sub2{{.*}}parent:[[#ROOT]] +subroutine sub2() + integer :: i, j, k, l + i = 0 +1 j = i + ! CHECK: ContinueStmt{{.*}}parent:[[#SUB2]] +2 continue + i = i+1 +3 j = j+1 +! CHECK: ArithmeticIfStmt{{.*}}parent:[[#SUB2]] + if (j-i) 3, 4, 5 + ! CHECK: GotoStmt{{.*}}parent:[[#SUB2]] +4 goto 6 + +! FIXME: is name resolution on assigned goto broken/todo ? +! WILLCHECK: AssignStmt{{.*}}parent:[[#SUB2]] +!55 assign 6 to label +! WILLCHECK: AssignedGotoStmt{{.*}}parent:[[#SUB2]] +!66 go to label (5, 6) + +! CHECK: ComputedGotoStmt{{.*}}parent:[[#SUB2]] + go to (5, 6), 1 + mod(i, 2) +5 j = j + 1 +6 i = i + j/2 + + ! CHECK: [[#%u, DO1:]]{{.*}}<>{{.*}}parent:[[#SUB2]] + do1: do k=1,10 + ! CHECK: [[#%u, DO2:]]{{.*}}<>{{.*}}parent:[[#DO1]] + do2: do l=5,20 + ! CHECK: CycleStmt{{.*}}parent:[[#DO2]] + cycle do1 + ! CHECK: ExitStmt{{.*}}parent:[[#DO2]] + exit do2 + end do do2 + end do do1 + + ! CHECK: PauseStmt{{.*}}parent:[[#SUB2]] + pause 7 + ! CHECK: StopStmt{{.*}}parent:[[#SUB2]] + stop +end subroutine + + +! CHECK: [[#%u, SUB3:]]{{.*}}Subroutine sub3{{.*}}parent:[[#ROOT]] +subroutine sub3() + print *, "normal" + ! CHECK: EntryStmt{{.*}}parent:[[#SUB3]] + entry sub4entry() + print *, "test" +end subroutine + +! CHECK: [[#%u, SUB4:]]{{.*}}Subroutine sub4{{.*}}parent:[[#ROOT]] +subroutine sub4(i, j) + integer :: i + print*, "test" + ! CHECK: DataStmt{{.*}}parent:[[#SUB4]] + data i /1/ +end subroutine diff --git a/test-lit/lower/pre-fir-tree03.f90 b/test-lit/lower/pre-fir-tree03.f90 new file mode 100644 index 000000000000..9d0cf1d67e1d --- /dev/null +++ b/test-lit/lower/pre-fir-tree03.f90 @@ -0,0 +1,56 @@ +! RUN: %f18 -fdebug-pre-fir-tree -fparse-only -fopenmp %s | FileCheck %s + +! Test Pre-FIR Tree captures OpenMP related constructs + +! CHECK: PFT root node:[[#%u, ROOT:]] +! CHECK: [[#%u, PROG:]]{{.*}}Program test_omp{{.*}}parent:[[#ROOT]] +program test_omp + ! CHECK: PrintStmt{{.*}}parent:[[#PROG]] + print *, "sequential" + + ! CHECK: [[#%u, OMP_PAR:]]{{.*}}OpenMPConstruct{{.*}}parent:[[#PROG]] + !$omp parallel + + ! CHECK: PrintStmt{{.*}}parent:[[#OMP_PAR]] + print *, "in omp //" + ! CHECK: [[#%u, OMP_LOOP:]]{{.*}}OpenMPConstruct{{.*}}parent:[[#OMP_PAR]] + !$omp do + ! CHECK: [[#%u, DO1:]]{{.*}}DoConstruct{{.*}}parent:[[#OMP_LOOP]] + ! CHECK: LabelDoStmt{{.*}}parent:[[#DO1]] + do i=1,100 + ! CHECK: PrintStmt{{.*}}parent:[[#DO1]] + print *, "in omp do" + ! CHECK: EndDoStmt{{.*}}parent:[[#DO1]] + end do + ! CHECK: OmpEndLoopDirective{{.*}}parent:[[#OMP_LOOP]] + !$omp end do + + ! CHECK: PrintStmt{{.*}}parent:[[#OMP_PAR]] + print *, "not in omp do" + + ! CHECK: [[#%u, OMP_LOOP2:]]{{.*}}OpenMPConstruct{{.*}}parent:[[#OMP_PAR]] + !$omp do + ! CHECK: [[#%u, DO2:]]{{.*}}DoConstruct{{.*}}parent:[[#OMP_LOOP2]] + ! CHECK: LabelDoStmt{{.*}}parent:[[#DO2]] + do i=1,100 + ! CHECK: PrintStmt{{.*}}parent:[[#DO2]] + print *, "in omp do" + ! CHECK: EndDoStmt{{.*}}parent:[[#DO2]] + end do + ! CHECK-NOT: OmpEndLoopDirective + ! CHECK: PrintStmt{{.*}}parent:[[#OMP_PAR]] + print *, "no in omp do" + !$omp end parallel + + ! CHECK: PrintStmt{{.*}}parent:[[#PROG]] + print *, "sequential again" + + ! CHECK: [[#%u, OMP_TASK:]]{{.*}}OpenMPConstruct{{.*}}parent:[[#PROG]] + !$omp task + ! CHECK: PrintStmt{{.*}}parent:[[#OMP_TASK]] + print *, "in task" + !$omp end task + + ! CHECK: PrintStmt{{.*}}parent:[[#PROG]] + print *, "sequential again" +end program diff --git a/test-lit/lower/pre-fir-tree04.f90 b/test-lit/lower/pre-fir-tree04.f90 new file mode 100644 index 000000000000..5fc702c0afb6 --- /dev/null +++ b/test-lit/lower/pre-fir-tree04.f90 @@ -0,0 +1,66 @@ +! RUN: %f18_with_includes -fdebug-pre-fir-tree -fparse-only %s | FileCheck %s + +! Test Pre-FIR Tree captures all the coarray related statements + +! CHECK: PFT root node:[[#%u, ROOT:]] +! CHECK: [[#%u, PROG:]]{{.*}}Subroutine test_coarray{{.*}}parent:[[#ROOT]] +Subroutine test_coarray + use iso_fortran_env, only: team_type, event_type, lock_type + type(team_type) :: t + type(event_type) :: done + type(lock_type) :: alock + real :: y[10,*] + integer :: counter[*] + logical :: is_master + ! CHECK: [[#%u, CHANGE_TEAM:]]{{.*}}ChangeTeamConstruct{{.*}}parent:[[#PROG]] + change team(t, x[5,*] => y) + ! CHECK: AssignmentStmt{{.*}}parent:[[#CHANGE_TEAM]] + x = x[4, 1] + end team + ! CHECK: FormTeamStmt{{.*}}parent:[[#PROG]] + form team(1, t) + + ! CHECK: [[#%u, IF:]]{{.*}}IfConstruct{{.*}}parent:[[#PROG]] + if (this_image() == 1) then + ! CHECK: EventPostStmt{{.*}}parent:[[#IF]] + event post (done) + else + ! CHECK: EventWaitStmt{{.*}}parent:[[#IF]] + event wait (done) + end if + + ! CHECK: [[#%u, CRITICAL:]]{{.*}}CriticalConstruct{{.*}}parent:[[#PROG]] + critical + ! CHECK: AssignmentStmt{{.*}}parent:[[#CRITICAL]] + counter[1] = counter[1] + 1 + end critical + + ! CHECK: LockStmt{{.*}}parent:[[#PROG]] + lock(alock) + ! CHECK: PrintStmt{{.*}}parent:[[#PROG]] + print *, "I have the lock" + ! CHECK: UnlockStmt{{.*}}parent:[[#PROG]] + unlock(alock) + + ! CHECK: SyncAllStmt{{.*}}parent:[[#PROG]] + sync all + ! CHECK: SyncMemoryStmt{{.*}}parent:[[#PROG]] + sync memory + ! CHECK: SyncTeamStmt{{.*}}parent:[[#PROG]] + sync team(t) + + ! CHECK: [[#%u, IF2:]]{{.*}}IfConstruct{{.*}}parent:[[#PROG]] + if (this_image() == 1) then + ! CHECK: SyncImagesStmt{{.*}}parent:[[#IF2]] + sync images(*) + else + ! CHECK: SyncImagesStmt{{.*}}parent:[[#IF2]] + sync images(1) + end if + + ! CHECK: [[#%u, IF3:]]{{.*}}IfConstruct{{.*}}parent:[[#PROG]] + if (y<0.) then + ! CHECK: FailImageStmt{{.*}}parent:[[#IF3]] + fail image + end if +end diff --git a/tools/bbc/.clang-format b/tools/bbc/.clang-format new file mode 100644 index 000000000000..a74fda4b6734 --- /dev/null +++ b/tools/bbc/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes diff --git a/tools/f18/CMakeLists.txt b/tools/f18/CMakeLists.txt index 676549c95cf4..79f5c52d6a6c 100644 --- a/tools/f18/CMakeLists.txt +++ b/tools/f18/CMakeLists.txt @@ -18,6 +18,8 @@ target_link_libraries(f18 FortranParser FortranEvaluate FortranSemantics + LLVMSupport + FortranLower ) add_executable(f18-parse-demo diff --git a/tools/f18/f18.cpp b/tools/f18/f18.cpp index 56f008ba6fe1..f2a084204ee8 100644 --- a/tools/f18/f18.cpp +++ b/tools/f18/f18.cpp @@ -11,6 +11,7 @@ #include "flang/common/Fortran-features.h" #include "flang/common/default-kinds.h" #include "flang/evaluate/expression.h" +#include "flang/lower/PFTBuilder.h" #include "flang/parser/characters.h" #include "flang/parser/dump-parse-tree.h" #include "flang/parser/message.h" @@ -22,6 +23,7 @@ #include "flang/semantics/expression.h" #include "flang/semantics/semantics.h" #include "flang/semantics/unparse-with-symbols.h" +#include "llvm/Support/raw_ostream.h" #include #include #include @@ -92,6 +94,7 @@ struct DriverOptions { bool dumpUnparse{false}; bool dumpUnparseWithSymbols{false}; bool dumpParseTree{false}; + bool dumpPreFirTree{false}; bool dumpSymbols{false}; bool debugResolveNames{false}; bool debugNoSemantics{false}; @@ -308,6 +311,15 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, nullptr /* action before each statement */, &asFortran); return {}; } + if (driver.dumpPreFirTree) { + if (auto ast{Fortran::lower::createPFT(parseTree)}) { + Fortran::lower::annotateControl(*ast); + Fortran::lower::dumpPFT(llvm::outs(), *ast); + } else { + std::cerr << "Pre FIR Tree is NULL.\n"; + exitStatus = EXIT_FAILURE; + } + } if (driver.parseOnly) { return {}; } @@ -475,6 +487,9 @@ int main(int argc, char *const argv[]) { options.needProvenanceRangeToCharBlockMappings = true; } else if (arg == "-fdebug-dump-parse-tree") { driver.dumpParseTree = true; + } else if (arg == "-fdebug-pre-fir-tree") { + driver.dumpPreFirTree = true; + } else if (arg == "-fdebug-resolve-names") { } else if (arg == "-fdebug-dump-symbols") { driver.dumpSymbols = true; } else if (arg == "-fdebug-resolve-names") { diff --git a/tools/tco/.clang-format b/tools/tco/.clang-format new file mode 100644 index 000000000000..a74fda4b6734 --- /dev/null +++ b/tools/tco/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes