1 //===-- Lower/PFTBuilder.h -- PFT builder -----------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // PFT (Pre-FIR Tree) interface. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef FORTRAN_LOWER_PFTBUILDER_H 14 #define FORTRAN_LOWER_PFTBUILDER_H 15 16 #include "flang/Common/reference.h" 17 #include "flang/Common/template.h" 18 #include "flang/Parser/parse-tree.h" 19 #include "llvm/ADT/DenseMap.h" 20 #include "llvm/ADT/SmallSet.h" 21 #include "llvm/Support/raw_ostream.h" 22 23 namespace mlir { 24 class Block; 25 } 26 27 namespace Fortran { 28 namespace semantics { 29 class SemanticsContext; 30 class Scope; 31 } // namespace semantics 32 namespace lower { 33 namespace pft { 34 35 struct Evaluation; 36 struct Program; 37 struct ModuleLikeUnit; 38 struct FunctionLikeUnit; 39 40 // TODO: A collection of Evaluations can obviously be any of the container 41 // types; leaving this as a std::list _for now_ because we reserve the right to 42 // insert PFT nodes in any order in O(1) time. 43 using EvaluationList = std::list<Evaluation>; 44 using LabelEvalMap = llvm::DenseMap<Fortran::parser::Label, Evaluation *>; 45 46 /// Provide a variant like container that can hold references. It can hold 47 /// constant or mutable references. It is used in the other classes to provide 48 /// union of const references to parse-tree nodes. 49 template <bool isConst, typename... A> 50 class ReferenceVariantBase { 51 public: 52 template <typename B> 53 using BaseType = std::conditional_t<isConst, const B, B>; 54 template <typename B> 55 using Ref = common::Reference<BaseType<B>>; 56 57 ReferenceVariantBase() = delete; ReferenceVariantBase(std::variant<Ref<A>...> b)58 ReferenceVariantBase(std::variant<Ref<A>...> b) : u(b) {} 59 template <typename T> ReferenceVariantBase(Ref<T> b)60 ReferenceVariantBase(Ref<T> b) : u(b) {} 61 62 template <typename B> get()63 constexpr BaseType<B> &get() const { 64 return std::get<Ref<B>> > (u).get(); 65 } 66 template <typename B> getIf()67 constexpr BaseType<B> *getIf() const { 68 auto *ptr = std::get_if<Ref<B>>(&u); 69 return ptr ? &ptr->get() : nullptr; 70 } 71 template <typename B> isA()72 constexpr bool isA() const { 73 return std::holds_alternative<Ref<B>>(u); 74 } 75 template <typename VISITOR> visit(VISITOR && visitor)76 constexpr auto visit(VISITOR &&visitor) const { 77 return std::visit( 78 common::visitors{[&visitor](auto ref) { return visitor(ref.get()); }}, 79 u); 80 } 81 82 private: 83 std::variant<Ref<A>...> u; 84 }; 85 template <typename... A> 86 using ReferenceVariant = ReferenceVariantBase<true, A...>; 87 template <typename... A> 88 using MutableReferenceVariant = ReferenceVariantBase<false, A...>; 89 90 /// ParentVariant is used to provide a reference to the unit a parse-tree node 91 /// belongs to. It is a variant of non-nullable pointers. 92 using ParentVariant = MutableReferenceVariant<Program, ModuleLikeUnit, 93 FunctionLikeUnit, Evaluation>; 94 95 /// Classify the parse-tree nodes from ExecutablePartConstruct 96 97 using ActionStmts = std::tuple< 98 parser::AllocateStmt, parser::AssignmentStmt, parser::BackspaceStmt, 99 parser::CallStmt, parser::CloseStmt, parser::ContinueStmt, 100 parser::CycleStmt, parser::DeallocateStmt, parser::EndfileStmt, 101 parser::EventPostStmt, parser::EventWaitStmt, parser::ExitStmt, 102 parser::FailImageStmt, parser::FlushStmt, parser::FormTeamStmt, 103 parser::GotoStmt, parser::IfStmt, parser::InquireStmt, parser::LockStmt, 104 parser::NullifyStmt, parser::OpenStmt, parser::PointerAssignmentStmt, 105 parser::PrintStmt, parser::ReadStmt, parser::ReturnStmt, parser::RewindStmt, 106 parser::StopStmt, parser::SyncAllStmt, parser::SyncImagesStmt, 107 parser::SyncMemoryStmt, parser::SyncTeamStmt, parser::UnlockStmt, 108 parser::WaitStmt, parser::WhereStmt, parser::WriteStmt, 109 parser::ComputedGotoStmt, parser::ForallStmt, parser::ArithmeticIfStmt, 110 parser::AssignStmt, parser::AssignedGotoStmt, parser::PauseStmt>; 111 112 using OtherStmts = std::tuple<parser::FormatStmt, parser::EntryStmt, 113 parser::DataStmt, parser::NamelistStmt>; 114 115 using ConstructStmts = std::tuple< 116 parser::AssociateStmt, parser::EndAssociateStmt, parser::BlockStmt, 117 parser::EndBlockStmt, parser::SelectCaseStmt, parser::CaseStmt, 118 parser::EndSelectStmt, parser::ChangeTeamStmt, parser::EndChangeTeamStmt, 119 parser::CriticalStmt, parser::EndCriticalStmt, parser::NonLabelDoStmt, 120 parser::EndDoStmt, parser::IfThenStmt, parser::ElseIfStmt, parser::ElseStmt, 121 parser::EndIfStmt, parser::SelectRankStmt, parser::SelectRankCaseStmt, 122 parser::SelectTypeStmt, parser::TypeGuardStmt, parser::WhereConstructStmt, 123 parser::MaskedElsewhereStmt, parser::ElsewhereStmt, parser::EndWhereStmt, 124 parser::ForallConstructStmt, parser::EndForallStmt>; 125 126 using Constructs = 127 std::tuple<parser::AssociateConstruct, parser::BlockConstruct, 128 parser::CaseConstruct, parser::ChangeTeamConstruct, 129 parser::CriticalConstruct, parser::DoConstruct, 130 parser::IfConstruct, parser::SelectRankConstruct, 131 parser::SelectTypeConstruct, parser::WhereConstruct, 132 parser::ForallConstruct>; 133 134 using Directives = 135 std::tuple<parser::CompilerDirective, parser::OpenACCConstruct, 136 parser::OpenMPConstruct, parser::OmpEndLoopDirective>; 137 138 template <typename A> 139 static constexpr bool isActionStmt{common::HasMember<A, ActionStmts>}; 140 141 template <typename A> 142 static constexpr bool isOtherStmt{common::HasMember<A, OtherStmts>}; 143 144 template <typename A> 145 static constexpr bool isConstructStmt{common::HasMember<A, ConstructStmts>}; 146 147 template <typename A> 148 static constexpr bool isConstruct{common::HasMember<A, Constructs>}; 149 150 template <typename A> 151 static constexpr bool isDirective{common::HasMember<A, Directives>}; 152 153 template <typename A> 154 static constexpr bool isIntermediateConstructStmt{common::HasMember< 155 A, std::tuple<parser::CaseStmt, parser::ElseIfStmt, parser::ElseStmt, 156 parser::SelectRankCaseStmt, parser::TypeGuardStmt>>}; 157 158 template <typename A> 159 static constexpr bool isNopConstructStmt{common::HasMember< 160 A, std::tuple<parser::EndAssociateStmt, parser::CaseStmt, 161 parser::EndSelectStmt, parser::ElseIfStmt, parser::ElseStmt, 162 parser::EndIfStmt, parser::SelectRankCaseStmt, 163 parser::TypeGuardStmt>>}; 164 165 template <typename A> 166 static constexpr bool isFunctionLike{common::HasMember< 167 A, std::tuple<parser::MainProgram, parser::FunctionSubprogram, 168 parser::SubroutineSubprogram, 169 parser::SeparateModuleSubprogram>>}; 170 171 using LabelSet = llvm::SmallSet<parser::Label, 5>; 172 using SymbolRef = common::Reference<const semantics::Symbol>; 173 using SymbolLabelMap = llvm::DenseMap<SymbolRef, LabelSet>; 174 175 template <typename A> 176 struct MakeReferenceVariantHelper {}; 177 template <typename... A> 178 struct MakeReferenceVariantHelper<std::variant<A...>> { 179 using type = ReferenceVariant<A...>; 180 }; 181 template <typename... A> 182 struct MakeReferenceVariantHelper<std::tuple<A...>> { 183 using type = ReferenceVariant<A...>; 184 }; 185 template <typename A> 186 using MakeReferenceVariant = typename MakeReferenceVariantHelper<A>::type; 187 188 using EvaluationTuple = 189 common::CombineTuples<ActionStmts, OtherStmts, ConstructStmts, Constructs, 190 Directives>; 191 /// Hide non-nullable pointers to the parse-tree node. 192 /// Build type std::variant<const A* const, const B* const, ...> 193 /// from EvaluationTuple type (std::tuple<A, B, ...>). 194 using EvaluationVariant = MakeReferenceVariant<EvaluationTuple>; 195 196 /// Function-like units contain lists of evaluations. These can be simple 197 /// statements or constructs, where a construct contains its own evaluations. 198 struct Evaluation : EvaluationVariant { 199 200 /// General ctor 201 template <typename A> 202 Evaluation(const A &a, const ParentVariant &parentVariant, 203 const parser::CharBlock &position, 204 const std::optional<parser::Label> &label) 205 : EvaluationVariant{a}, 206 parentVariant{parentVariant}, position{position}, label{label} {} 207 208 /// Construct ctor 209 template <typename A> 210 Evaluation(const A &a, const ParentVariant &parentVariant) 211 : EvaluationVariant{a}, parentVariant{parentVariant} { 212 static_assert(pft::isConstruct<A> || pft::isDirective<A>, 213 "must be a construct or directive"); 214 } 215 216 /// Evaluation classification predicates. 217 constexpr bool isActionStmt() const { 218 return visit(common::visitors{ 219 [](auto &r) { return pft::isActionStmt<std::decay_t<decltype(r)>>; }}); 220 } 221 constexpr bool isOtherStmt() const { 222 return visit(common::visitors{ 223 [](auto &r) { return pft::isOtherStmt<std::decay_t<decltype(r)>>; }}); 224 } 225 constexpr bool isConstructStmt() const { 226 return visit(common::visitors{[](auto &r) { 227 return pft::isConstructStmt<std::decay_t<decltype(r)>>; 228 }}); 229 } 230 constexpr bool isConstruct() const { 231 return visit(common::visitors{ 232 [](auto &r) { return pft::isConstruct<std::decay_t<decltype(r)>>; }}); 233 } 234 constexpr bool isDirective() const { 235 return visit(common::visitors{ 236 [](auto &r) { return pft::isDirective<std::decay_t<decltype(r)>>; }}); 237 } 238 constexpr bool isNopConstructStmt() const { 239 return visit(common::visitors{[](auto &r) { 240 return pft::isNopConstructStmt<std::decay_t<decltype(r)>>; 241 }}); 242 } 243 244 /// Return the predicate: "This is a non-initial, non-terminal construct 245 /// statement." For an IfConstruct, this is ElseIfStmt and ElseStmt. 246 constexpr bool isIntermediateConstructStmt() const { 247 return visit(common::visitors{[](auto &r) { 248 return pft::isIntermediateConstructStmt<std::decay_t<decltype(r)>>; 249 }}); 250 } 251 252 /// Return the first non-nop successor of an evaluation, possibly exiting 253 /// from one or more enclosing constructs. 254 Evaluation &nonNopSuccessor() const { 255 Evaluation *successor = lexicalSuccessor; 256 if (successor && successor->isNopConstructStmt()) { 257 successor = successor->parentConstruct->constructExit; 258 } 259 assert(successor && "missing successor"); 260 return *successor; 261 } 262 263 /// Return true if this Evaluation has at least one nested evaluation. 264 bool hasNestedEvaluations() const { 265 return evaluationList && !evaluationList->empty(); 266 } 267 268 /// Return nested evaluation list. 269 EvaluationList &getNestedEvaluations() { 270 assert(evaluationList && "no nested evaluations"); 271 return *evaluationList; 272 } 273 274 Evaluation &getFirstNestedEvaluation() { 275 assert(hasNestedEvaluations() && "no nested evaluations"); 276 return evaluationList->front(); 277 } 278 279 Evaluation &getLastNestedEvaluation() { 280 assert(hasNestedEvaluations() && "no nested evaluations"); 281 return evaluationList->back(); 282 } 283 284 /// Return the FunctionLikeUnit containing this evaluation (or nullptr). 285 FunctionLikeUnit *getOwningProcedure() const; 286 287 bool lowerAsStructured() const; 288 bool lowerAsUnstructured() const; 289 290 // FIR generation looks primarily at PFT statement (leaf) nodes. So members 291 // such as lexicalSuccessor and the various block fields are only applicable 292 // to statement nodes. One exception is that an internal construct node is 293 // a convenient place for a constructExit link that applies to exits from any 294 // statement within the construct. The controlSuccessor member is used for 295 // nonlexical successors, such as linking to a GOTO target. For multiway 296 // branches, controlSuccessor is set to one of the targets (might as well be 297 // the first target). Successor and exit links always target statements. 298 // 299 // An unstructured construct is one that contains some form of goto. This 300 // is indicated by the isUnstructured member flag, which may be set on a 301 // statement and propagated to enclosing constructs. This distinction allows 302 // a structured IF or DO statement to be materialized with custom structured 303 // FIR operations. An unstructured statement is materialized as mlir 304 // operation sequences that include explicit branches. 305 // 306 // There are two mlir::Block members. The block member is set for statements 307 // that begin a new block. If a statement may have more than one associated 308 // block, this member must be the block that would be the target of a branch 309 // to the statement. The prime example of a statement that may have multiple 310 // associated blocks is NonLabelDoStmt, which may have a loop preheader block 311 // for loop initialization code, and always has a header block that is the 312 // target of the loop back edge. If the NonLabelDoStmt is a concurrent loop, 313 // there may be an arbitrary number of nested preheader, header, and mask 314 // blocks. Any such additional blocks in the localBlocks member are local 315 // to a construct and cannot be the target of an unstructured branch. For 316 // NonLabelDoStmt, the block member designates the preheader block, which may 317 // be absent if loop initialization code may be appended to a predecessor 318 // block. The primary loop header block is localBlocks[0], with additional 319 // DO CONCURRENT blocks at localBlocks[1], etc. 320 // 321 // The printIndex member is only set for statements. It is used for dumps 322 // and does not affect FIR generation. It may also be helpful for debugging. 323 324 ParentVariant parentVariant; 325 parser::CharBlock position{}; 326 std::optional<parser::Label> label{}; 327 std::unique_ptr<EvaluationList> evaluationList; // nested evaluations 328 Evaluation *parentConstruct{nullptr}; // set for nodes below the top level 329 Evaluation *lexicalSuccessor{nullptr}; // set for ActionStmt, ConstructStmt 330 Evaluation *controlSuccessor{nullptr}; // set for some statements 331 Evaluation *constructExit{nullptr}; // set for constructs 332 bool isNewBlock{false}; // evaluation begins a new basic block 333 bool isUnstructured{false}; // evaluation has unstructured control flow 334 bool skip{false}; // evaluation has been processed in advance 335 mlir::Block *block{nullptr}; // isNewBlock block 336 llvm::SmallVector<mlir::Block *, 1> localBlocks{}; // construct local blocks 337 int printIndex{0}; // (ActionStmt, ConstructStmt) evaluation index for dumps 338 }; 339 340 using ProgramVariant = 341 ReferenceVariant<parser::MainProgram, parser::FunctionSubprogram, 342 parser::SubroutineSubprogram, parser::Module, 343 parser::Submodule, parser::SeparateModuleSubprogram, 344 parser::BlockData>; 345 /// A program is a list of program units. 346 /// These units can be function like, module like, or block data. 347 struct ProgramUnit : ProgramVariant { 348 template <typename A> 349 ProgramUnit(const A &p, const ParentVariant &parentVariant) 350 : ProgramVariant{p}, parentVariant{parentVariant} {} 351 ProgramUnit(ProgramUnit &&) = default; 352 ProgramUnit(const ProgramUnit &) = delete; 353 354 ParentVariant parentVariant; 355 }; 356 357 /// A variable captures an object to be created per the declaration part of a 358 /// function like unit. 359 /// 360 /// Properties can be applied by lowering. For example, a local array that is 361 /// known to be very large may be transformed into a heap allocated entity by 362 /// lowering. That decision would be tracked in its Variable instance. 363 struct Variable { 364 explicit Variable(const Fortran::semantics::Symbol &sym, bool global = false, 365 int depth = 0) 366 : sym{&sym}, depth{depth}, global{global} {} 367 368 const Fortran::semantics::Symbol &getSymbol() const { return *sym; } 369 370 bool isGlobal() const { return global; } 371 bool isHeapAlloc() const { return heapAlloc; } 372 bool isPointer() const { return pointer; } 373 bool isTarget() const { return target; } 374 int getDepth() const { return depth; } 375 376 void setHeapAlloc(bool to = true) { heapAlloc = to; } 377 void setPointer(bool to = true) { pointer = to; } 378 void setTarget(bool to = true) { target = to; } 379 380 private: 381 const Fortran::semantics::Symbol *sym; 382 int depth; 383 bool global; 384 bool heapAlloc{false}; // variable needs deallocation on exit 385 bool pointer{false}; 386 bool target{false}; 387 }; 388 389 /// Function-like units may contain evaluations (executable statements) and 390 /// nested function-like units (internal procedures and function statements). 391 struct FunctionLikeUnit : public ProgramUnit { 392 // wrapper statements for function-like syntactic structures 393 using FunctionStatement = 394 ReferenceVariant<parser::Statement<parser::ProgramStmt>, 395 parser::Statement<parser::EndProgramStmt>, 396 parser::Statement<parser::FunctionStmt>, 397 parser::Statement<parser::EndFunctionStmt>, 398 parser::Statement<parser::SubroutineStmt>, 399 parser::Statement<parser::EndSubroutineStmt>, 400 parser::Statement<parser::MpSubprogramStmt>, 401 parser::Statement<parser::EndMpSubprogramStmt>>; 402 403 FunctionLikeUnit( 404 const parser::MainProgram &f, const ParentVariant &parentVariant, 405 const Fortran::semantics::SemanticsContext &semanticsContext); 406 FunctionLikeUnit( 407 const parser::FunctionSubprogram &f, const ParentVariant &parentVariant, 408 const Fortran::semantics::SemanticsContext &semanticsContext); 409 FunctionLikeUnit( 410 const parser::SubroutineSubprogram &f, const ParentVariant &parentVariant, 411 const Fortran::semantics::SemanticsContext &semanticsContext); 412 FunctionLikeUnit( 413 const parser::SeparateModuleSubprogram &f, 414 const ParentVariant &parentVariant, 415 const Fortran::semantics::SemanticsContext &semanticsContext); 416 FunctionLikeUnit(FunctionLikeUnit &&) = default; 417 FunctionLikeUnit(const FunctionLikeUnit &) = delete; 418 419 void processSymbolTable(const Fortran::semantics::Scope &); 420 421 std::vector<Variable> getOrderedSymbolTable() { return varList[0]; } 422 423 bool isMainProgram() const { 424 return endStmt.isA<parser::Statement<parser::EndProgramStmt>>(); 425 } 426 427 /// Get the starting source location for this function like unit 428 parser::CharBlock getStartingSourceLoc() { 429 if (beginStmt) 430 return stmtSourceLoc(*beginStmt); 431 if (!evaluationList.empty()) 432 return evaluationList.front().position; 433 return stmtSourceLoc(endStmt); 434 } 435 436 /// Returns reference to the subprogram symbol of this FunctionLikeUnit. 437 /// Dies if the FunctionLikeUnit is not a subprogram. 438 const semantics::Symbol &getSubprogramSymbol() const { 439 assert(symbol && "not inside a procedure"); 440 return *symbol; 441 } 442 443 /// Helper to get location from FunctionLikeUnit begin/end statements. 444 static parser::CharBlock stmtSourceLoc(const FunctionStatement &stmt) { 445 return stmt.visit(common::visitors{[](const auto &x) { return x.source; }}); 446 } 447 448 /// Anonymous programs do not have a begin statement 449 std::optional<FunctionStatement> beginStmt; 450 FunctionStatement endStmt; 451 EvaluationList evaluationList; 452 LabelEvalMap labelEvaluationMap; 453 SymbolLabelMap assignSymbolLabelMap; 454 std::list<FunctionLikeUnit> nestedFunctions; 455 /// Symbol associated to this FunctionLikeUnit. 456 /// Null if the FunctionLikeUnit is an anonymous program. 457 /// The symbol has MainProgramDetails for named programs, otherwise it has 458 /// SubprogramDetails. 459 const semantics::Symbol *symbol{nullptr}; 460 /// Terminal basic block (if any) 461 mlir::Block *finalBlock{}; 462 std::vector<std::vector<Variable>> varList; 463 }; 464 465 /// Module-like units contain a list of function-like units. 466 struct ModuleLikeUnit : public ProgramUnit { 467 // wrapper statements for module-like syntactic structures 468 using ModuleStatement = 469 ReferenceVariant<parser::Statement<parser::ModuleStmt>, 470 parser::Statement<parser::EndModuleStmt>, 471 parser::Statement<parser::SubmoduleStmt>, 472 parser::Statement<parser::EndSubmoduleStmt>>; 473 474 ModuleLikeUnit(const parser::Module &m, const ParentVariant &parentVariant); 475 ModuleLikeUnit(const parser::Submodule &m, 476 const ParentVariant &parentVariant); 477 ~ModuleLikeUnit() = default; 478 ModuleLikeUnit(ModuleLikeUnit &&) = default; 479 ModuleLikeUnit(const ModuleLikeUnit &) = delete; 480 481 ModuleStatement beginStmt; 482 ModuleStatement endStmt; 483 std::list<FunctionLikeUnit> nestedFunctions; 484 }; 485 486 struct BlockDataUnit : public ProgramUnit { 487 BlockDataUnit(const parser::BlockData &bd, 488 const ParentVariant &parentVariant); 489 BlockDataUnit(BlockDataUnit &&) = default; 490 BlockDataUnit(const BlockDataUnit &) = delete; 491 }; 492 493 /// A Program is the top-level root of the PFT. 494 struct Program { 495 using Units = std::variant<FunctionLikeUnit, ModuleLikeUnit, BlockDataUnit>; 496 497 Program() = default; 498 Program(Program &&) = default; 499 Program(const Program &) = delete; 500 501 std::list<Units> &getUnits() { return units; } 502 503 /// LLVM dump method on a Program. 504 void dump(); 505 506 private: 507 std::list<Units> units; 508 }; 509 510 } // namespace pft 511 512 /// Create a PFT (Pre-FIR Tree) from the parse tree. 513 /// 514 /// A PFT is a light weight tree over the parse tree that is used to create FIR. 515 /// The PFT captures pointers back into the parse tree, so the parse tree must 516 /// not be changed between the construction of the PFT and its last use. The 517 /// PFT captures a structured view of a program. A program is a list of units. 518 /// A function like unit contains a list of evaluations. An evaluation is 519 /// either a statement, or a construct with a nested list of evaluations. 520 std::unique_ptr<pft::Program> 521 createPFT(const parser::Program &root, 522 const Fortran::semantics::SemanticsContext &semanticsContext); 523 524 /// Dumper for displaying a PFT. 525 void dumpPFT(llvm::raw_ostream &outputStream, pft::Program &pft); 526 527 } // namespace lower 528 } // namespace Fortran 529 530 #endif // FORTRAN_LOWER_PFTBUILDER_H 531