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 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ 10 // 11 //===----------------------------------------------------------------------===// 12 // 13 // PFT (Pre-FIR Tree) interface. 14 // 15 //===----------------------------------------------------------------------===// 16 17 #ifndef FORTRAN_LOWER_PFTBUILDER_H 18 #define FORTRAN_LOWER_PFTBUILDER_H 19 20 #include "flang/Common/reference.h" 21 #include "flang/Common/template.h" 22 #include "flang/Lower/HostAssociations.h" 23 #include "flang/Lower/PFTDefs.h" 24 #include "flang/Parser/parse-tree.h" 25 #include "flang/Semantics/attr.h" 26 #include "flang/Semantics/scope.h" 27 #include "flang/Semantics/semantics.h" 28 #include "flang/Semantics/symbol.h" 29 #include "llvm/Support/ErrorHandling.h" 30 #include "llvm/Support/raw_ostream.h" 31 32 namespace Fortran::lower::pft { 33 34 struct Evaluation; 35 struct Program; 36 struct ModuleLikeUnit; 37 struct FunctionLikeUnit; 38 39 using EvaluationList = std::list<Evaluation>; 40 using LabelEvalMap = llvm::DenseMap<Fortran::parser::Label, Evaluation *>; 41 42 /// Provide a variant like container that can hold references. It can hold 43 /// constant or mutable references. It is used in the other classes to provide 44 /// union of const references to parse-tree nodes. 45 template <bool isConst, typename... A> 46 class ReferenceVariantBase { 47 public: 48 template <typename B> 49 using BaseType = std::conditional_t<isConst, const B, B>; 50 template <typename B> 51 using Ref = common::Reference<BaseType<B>>; 52 53 ReferenceVariantBase() = delete; ReferenceVariantBase(std::variant<Ref<A>...> b)54 ReferenceVariantBase(std::variant<Ref<A>...> b) : u(b) {} 55 template <typename T> ReferenceVariantBase(Ref<T> b)56 ReferenceVariantBase(Ref<T> b) : u(b) {} 57 58 template <typename B> get()59 constexpr BaseType<B> &get() const { 60 return std::get<Ref<B>>(u).get(); 61 } 62 template <typename B> getStatement()63 constexpr BaseType<B> &getStatement() const { 64 return std::get<Ref<parser::Statement<B>>>(u).get().statement; 65 } 66 template <typename B> getIf()67 constexpr BaseType<B> *getIf() const { 68 const Ref<B> *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 /// PftNode 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 PftNode = 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::EntryStmt, parser::FormatStmt>; 113 114 using ConstructStmts = std::tuple< 115 parser::AssociateStmt, parser::EndAssociateStmt, parser::BlockStmt, 116 parser::EndBlockStmt, parser::SelectCaseStmt, parser::CaseStmt, 117 parser::EndSelectStmt, parser::ChangeTeamStmt, parser::EndChangeTeamStmt, 118 parser::CriticalStmt, parser::EndCriticalStmt, parser::NonLabelDoStmt, 119 parser::EndDoStmt, parser::IfThenStmt, parser::ElseIfStmt, parser::ElseStmt, 120 parser::EndIfStmt, parser::SelectRankStmt, parser::SelectRankCaseStmt, 121 parser::SelectTypeStmt, parser::TypeGuardStmt, parser::WhereConstructStmt, 122 parser::MaskedElsewhereStmt, parser::ElsewhereStmt, parser::EndWhereStmt, 123 parser::ForallConstructStmt, parser::EndForallStmt>; 124 125 using EndStmts = 126 std::tuple<parser::EndProgramStmt, parser::EndFunctionStmt, 127 parser::EndSubroutineStmt, parser::EndMpSubprogramStmt>; 128 129 using Constructs = 130 std::tuple<parser::AssociateConstruct, parser::BlockConstruct, 131 parser::CaseConstruct, parser::ChangeTeamConstruct, 132 parser::CriticalConstruct, parser::DoConstruct, 133 parser::IfConstruct, parser::SelectRankConstruct, 134 parser::SelectTypeConstruct, parser::WhereConstruct, 135 parser::ForallConstruct>; 136 137 using Directives = 138 std::tuple<parser::CompilerDirective, parser::OpenACCConstruct, 139 parser::OpenACCDeclarativeConstruct, parser::OpenMPConstruct, 140 parser::OpenMPDeclarativeConstruct, parser::OmpEndLoopDirective>; 141 142 using DeclConstructs = std::tuple<parser::OpenMPDeclarativeConstruct, 143 parser::OpenACCDeclarativeConstruct>; 144 145 template <typename A> 146 static constexpr bool isActionStmt{common::HasMember<A, ActionStmts>}; 147 148 template <typename A> 149 static constexpr bool isOtherStmt{common::HasMember<A, OtherStmts>}; 150 151 template <typename A> 152 static constexpr bool isConstructStmt{common::HasMember<A, ConstructStmts>}; 153 154 template <typename A> 155 static constexpr bool isEndStmt{common::HasMember<A, EndStmts>}; 156 157 template <typename A> 158 static constexpr bool isConstruct{common::HasMember<A, Constructs>}; 159 160 template <typename A> 161 static constexpr bool isDirective{common::HasMember<A, Directives>}; 162 163 template <typename A> 164 static constexpr bool isDeclConstruct{common::HasMember<A, DeclConstructs>}; 165 166 template <typename A> 167 static constexpr bool isIntermediateConstructStmt{common::HasMember< 168 A, std::tuple<parser::CaseStmt, parser::ElseIfStmt, parser::ElseStmt, 169 parser::SelectRankCaseStmt, parser::TypeGuardStmt>>}; 170 171 template <typename A> 172 static constexpr bool isNopConstructStmt{common::HasMember< 173 A, std::tuple<parser::CaseStmt, parser::EndSelectStmt, parser::ElseIfStmt, 174 parser::ElseStmt, parser::EndIfStmt, 175 parser::SelectRankCaseStmt, parser::TypeGuardStmt>>}; 176 177 template <typename A> 178 static constexpr bool isExecutableDirective{common::HasMember< 179 A, std::tuple<parser::CompilerDirective, parser::OpenACCConstruct, 180 parser::OpenMPConstruct>>}; 181 182 template <typename A> 183 static constexpr bool isFunctionLike{common::HasMember< 184 A, std::tuple<parser::MainProgram, parser::FunctionSubprogram, 185 parser::SubroutineSubprogram, 186 parser::SeparateModuleSubprogram>>}; 187 188 template <typename A> 189 struct MakeReferenceVariantHelper {}; 190 template <typename... A> 191 struct MakeReferenceVariantHelper<std::variant<A...>> { 192 using type = ReferenceVariant<A...>; 193 }; 194 template <typename... A> 195 struct MakeReferenceVariantHelper<std::tuple<A...>> { 196 using type = ReferenceVariant<A...>; 197 }; 198 template <typename A> 199 using MakeReferenceVariant = typename MakeReferenceVariantHelper<A>::type; 200 201 using EvaluationTuple = 202 common::CombineTuples<ActionStmts, OtherStmts, ConstructStmts, EndStmts, 203 Constructs, Directives>; 204 /// Hide non-nullable pointers to the parse-tree node. 205 /// Build type std::variant<const A* const, const B* const, ...> 206 /// from EvaluationTuple type (std::tuple<A, B, ...>). 207 using EvaluationVariant = MakeReferenceVariant<EvaluationTuple>; 208 209 /// Function-like units contain lists of evaluations. These can be simple 210 /// statements or constructs, where a construct contains its own evaluations. 211 struct Evaluation : EvaluationVariant { 212 213 /// General ctor 214 template <typename A> 215 Evaluation(const A &a, const PftNode &parent, 216 const parser::CharBlock &position, 217 const std::optional<parser::Label> &label) 218 : EvaluationVariant{a}, parent{parent}, position{position}, label{label} { 219 } 220 221 /// Construct and Directive ctor 222 template <typename A> 223 Evaluation(const A &a, const PftNode &parent) 224 : EvaluationVariant{a}, parent{parent} { 225 static_assert(pft::isConstruct<A> || pft::isDirective<A>, 226 "must be a construct or directive"); 227 } 228 229 /// Evaluation classification predicates. 230 constexpr bool isActionStmt() const { 231 return visit(common::visitors{ 232 [](auto &r) { return pft::isActionStmt<std::decay_t<decltype(r)>>; }}); 233 } 234 constexpr bool isOtherStmt() const { 235 return visit(common::visitors{ 236 [](auto &r) { return pft::isOtherStmt<std::decay_t<decltype(r)>>; }}); 237 } 238 constexpr bool isConstructStmt() const { 239 return visit(common::visitors{[](auto &r) { 240 return pft::isConstructStmt<std::decay_t<decltype(r)>>; 241 }}); 242 } 243 constexpr bool isEndStmt() const { 244 return visit(common::visitors{ 245 [](auto &r) { return pft::isEndStmt<std::decay_t<decltype(r)>>; }}); 246 } 247 constexpr bool isConstruct() const { 248 return visit(common::visitors{ 249 [](auto &r) { return pft::isConstruct<std::decay_t<decltype(r)>>; }}); 250 } 251 constexpr bool isDirective() const { 252 return visit(common::visitors{ 253 [](auto &r) { return pft::isDirective<std::decay_t<decltype(r)>>; }}); 254 } 255 constexpr bool isNopConstructStmt() const { 256 return visit(common::visitors{[](auto &r) { 257 return pft::isNopConstructStmt<std::decay_t<decltype(r)>>; 258 }}); 259 } 260 constexpr bool isExecutableDirective() const { 261 return visit(common::visitors{[](auto &r) { 262 return pft::isExecutableDirective<std::decay_t<decltype(r)>>; 263 }}); 264 } 265 266 /// Return the predicate: "This is a non-initial, non-terminal construct 267 /// statement." For an IfConstruct, this is ElseIfStmt and ElseStmt. 268 constexpr bool isIntermediateConstructStmt() const { 269 return visit(common::visitors{[](auto &r) { 270 return pft::isIntermediateConstructStmt<std::decay_t<decltype(r)>>; 271 }}); 272 } 273 274 LLVM_DUMP_METHOD void dump() const; 275 276 /// Return the first non-nop successor of an evaluation, possibly exiting 277 /// from one or more enclosing constructs. 278 Evaluation &nonNopSuccessor() const { 279 Evaluation *successor = lexicalSuccessor; 280 if (successor && successor->isNopConstructStmt()) { 281 successor = successor->parentConstruct->constructExit; 282 } 283 assert(successor && "missing successor"); 284 return *successor; 285 } 286 287 /// Return true if this Evaluation has at least one nested evaluation. 288 bool hasNestedEvaluations() const { 289 return evaluationList && !evaluationList->empty(); 290 } 291 292 /// Return nested evaluation list. 293 EvaluationList &getNestedEvaluations() { 294 assert(evaluationList && "no nested evaluations"); 295 return *evaluationList; 296 } 297 298 Evaluation &getFirstNestedEvaluation() { 299 assert(hasNestedEvaluations() && "no nested evaluations"); 300 return evaluationList->front(); 301 } 302 303 Evaluation &getLastNestedEvaluation() { 304 assert(hasNestedEvaluations() && "no nested evaluations"); 305 return evaluationList->back(); 306 } 307 308 /// Return the FunctionLikeUnit containing this evaluation (or nullptr). 309 FunctionLikeUnit *getOwningProcedure() const; 310 311 bool lowerAsStructured() const; 312 bool lowerAsUnstructured() const; 313 314 // FIR generation looks primarily at PFT ActionStmt and ConstructStmt leaf 315 // nodes. Members such as lexicalSuccessor and block are applicable only 316 // to these nodes, plus some directives. The controlSuccessor member is 317 // used for nonlexical successors, such as linking to a GOTO target. For 318 // multiway branches, it is set to the first target. Successor and exit 319 // links always target statements or directives. An internal Construct 320 // node has a constructExit link that applies to exits from anywhere within 321 // the construct. 322 // 323 // An unstructured construct is one that contains some form of goto. This 324 // is indicated by the isUnstructured member flag, which may be set on a 325 // statement and propagated to enclosing constructs. This distinction allows 326 // a structured IF or DO statement to be materialized with custom structured 327 // FIR operations. An unstructured statement is materialized as mlir 328 // operation sequences that include explicit branches. 329 // 330 // The block member is set for statements that begin a new block. This 331 // block is the target of any branch to the statement. Statements may have 332 // additional (unstructured) "local" blocks, but such blocks cannot be the 333 // target of any explicit branch. The primary example of an (unstructured) 334 // statement that may have multiple associated blocks is NonLabelDoStmt, 335 // which may have a loop preheader block for loop initialization code (the 336 // block member), and always has a "local" header block that is the target 337 // of the loop back edge. If the NonLabelDoStmt is a concurrent loop, it 338 // may be associated with an arbitrary number of nested preheader, header, 339 // and mask blocks. 340 // 341 // The printIndex member is only set for statements. It is used for dumps 342 // (and debugging) and does not affect FIR generation. 343 344 PftNode parent; 345 parser::CharBlock position{}; 346 std::optional<parser::Label> label{}; 347 std::unique_ptr<EvaluationList> evaluationList; // nested evaluations 348 Evaluation *parentConstruct{nullptr}; // set for nodes below the top level 349 Evaluation *lexicalSuccessor{nullptr}; // set for leaf nodes, some directives 350 Evaluation *controlSuccessor{nullptr}; // set for some leaf nodes 351 Evaluation *constructExit{nullptr}; // set for constructs 352 bool isNewBlock{false}; // evaluation begins a new basic block 353 bool isUnstructured{false}; // evaluation has unstructured control flow 354 bool negateCondition{false}; // If[Then]Stmt condition must be negated 355 mlir::Block *block{nullptr}; // isNewBlock block (ActionStmt, ConstructStmt) 356 int printIndex{0}; // (ActionStmt, ConstructStmt) evaluation index for dumps 357 }; 358 359 using ProgramVariant = 360 ReferenceVariant<parser::MainProgram, parser::FunctionSubprogram, 361 parser::SubroutineSubprogram, parser::Module, 362 parser::Submodule, parser::SeparateModuleSubprogram, 363 parser::BlockData, parser::CompilerDirective>; 364 /// A program is a list of program units. 365 /// These units can be function like, module like, or block data. 366 struct ProgramUnit : ProgramVariant { 367 template <typename A> 368 ProgramUnit(const A &p, const PftNode &parent) 369 : ProgramVariant{p}, parent{parent} {} 370 ProgramUnit(ProgramUnit &&) = default; 371 ProgramUnit(const ProgramUnit &) = delete; 372 373 PftNode parent; 374 }; 375 376 /// A variable captures an object to be created per the declaration part of a 377 /// function like unit. 378 /// 379 /// Fortran EQUIVALENCE statements are a mechanism that introduces aliasing 380 /// between named variables. The set of overlapping aliases will materialize a 381 /// generic store object with a designated offset and size. Participant 382 /// symbols will simply be pointers into the aggregate store. 383 /// 384 /// EQUIVALENCE can also interact with COMMON and other global variables to 385 /// imply aliasing between (subparts of) a global and other local variable 386 /// names. 387 /// 388 /// Properties can be applied by lowering. For example, a local array that is 389 /// known to be very large may be transformed into a heap allocated entity by 390 /// lowering. That decision would be tracked in its Variable instance. 391 struct Variable { 392 /// Most variables are nominal and require the allocation of local/global 393 /// storage space. A nominal variable may also be an alias for some other 394 /// (subpart) of storage. 395 struct Nominal { 396 Nominal(const semantics::Symbol *symbol, int depth, bool global) 397 : symbol{symbol}, depth{depth}, global{global} {} 398 const semantics::Symbol *symbol{}; 399 400 bool isGlobal() const { return global; } 401 402 int depth{}; 403 bool global{}; 404 bool heapAlloc{}; // variable needs deallocation on exit 405 bool pointer{}; 406 bool target{}; 407 bool aliaser{}; // participates in EQUIVALENCE union 408 std::size_t aliasOffset{}; 409 }; 410 411 /// <offset, size> pair 412 using Interval = std::tuple<std::size_t, std::size_t>; 413 414 /// An interval of storage is a contiguous block of memory to be allocated or 415 /// mapped onto another variable. Aliasing variables will be pointers into 416 /// interval stores and may overlap each other. 417 struct AggregateStore { 418 AggregateStore(Interval &&interval, 419 const Fortran::semantics::Symbol &namingSym, 420 bool isGlobal = false) 421 : interval{std::move(interval)}, namingSymbol{&namingSym}, 422 isGlobalAggregate{isGlobal} {} 423 AggregateStore(const semantics::Symbol &initialValueSym, 424 const semantics::Symbol &namingSym, bool isGlobal = false) 425 : interval{initialValueSym.offset(), initialValueSym.size()}, 426 namingSymbol{&namingSym}, initialValueSymbol{&initialValueSym}, 427 isGlobalAggregate{isGlobal} {}; 428 429 bool isGlobal() const { return isGlobalAggregate; } 430 /// Get offset of the aggregate inside its scope. 431 std::size_t getOffset() const { return std::get<0>(interval); } 432 /// Returns symbols holding the aggregate initial value if any. 433 const semantics::Symbol *getInitialValueSymbol() const { 434 return initialValueSymbol; 435 } 436 /// Returns the symbol that gives its name to the aggregate. 437 const semantics::Symbol &getNamingSymbol() const { return *namingSymbol; } 438 /// Scope to which the aggregates belongs to. 439 const semantics::Scope &getOwningScope() const { 440 return getNamingSymbol().owner(); 441 } 442 /// <offset, size> of the aggregate in its scope. 443 Interval interval{}; 444 /// Symbol that gives its name to the aggregate. Always set by constructor. 445 const semantics::Symbol *namingSymbol; 446 /// Compiler generated symbol with the aggregate initial value if any. 447 const semantics::Symbol *initialValueSymbol = nullptr; 448 /// Is this a global aggregate ? 449 bool isGlobalAggregate; 450 }; 451 452 explicit Variable(const Fortran::semantics::Symbol &sym, bool global = false, 453 int depth = 0) 454 : var{Nominal(&sym, depth, global)} {} 455 explicit Variable(AggregateStore &&istore) : var{std::move(istore)} {} 456 457 /// Return the front-end symbol for a nominal variable. 458 const Fortran::semantics::Symbol &getSymbol() const { 459 assert(hasSymbol() && "variable is not nominal"); 460 return *std::get<Nominal>(var).symbol; 461 } 462 463 /// Return the aggregate store. 464 const AggregateStore &getAggregateStore() const { 465 assert(isAggregateStore()); 466 return std::get<AggregateStore>(var); 467 } 468 469 /// Return the interval range of an aggregate store. 470 const Interval &getInterval() const { 471 assert(isAggregateStore()); 472 return std::get<AggregateStore>(var).interval; 473 } 474 475 /// Only nominal variable have front-end symbols. 476 bool hasSymbol() const { return std::holds_alternative<Nominal>(var); } 477 478 /// Is this an aggregate store? 479 bool isAggregateStore() const { 480 return std::holds_alternative<AggregateStore>(var); 481 } 482 483 /// Is this variable a global? 484 bool isGlobal() const { 485 return std::visit([](const auto &x) { return x.isGlobal(); }, var); 486 } 487 488 /// Is this a module variable ? 489 bool isModuleVariable() const { 490 const semantics::Scope *scope = getOwningScope(); 491 return scope && scope->IsModule(); 492 } 493 494 const Fortran::semantics::Scope *getOwningScope() const { 495 return std::visit( 496 common::visitors{ 497 [](const Nominal &x) { return &x.symbol->GetUltimate().owner(); }, 498 [](const AggregateStore &agg) { return &agg.getOwningScope(); }}, 499 var); 500 } 501 502 bool isHeapAlloc() const { 503 if (auto *s = std::get_if<Nominal>(&var)) 504 return s->heapAlloc; 505 return false; 506 } 507 bool isPointer() const { 508 if (auto *s = std::get_if<Nominal>(&var)) 509 return s->pointer; 510 return false; 511 } 512 bool isTarget() const { 513 if (auto *s = std::get_if<Nominal>(&var)) 514 return s->target; 515 return false; 516 } 517 518 /// An alias(er) is a variable that is part of a EQUIVALENCE that is allocated 519 /// locally on the stack. 520 bool isAlias() const { 521 if (auto *s = std::get_if<Nominal>(&var)) 522 return s->aliaser; 523 return false; 524 } 525 std::size_t getAlias() const { 526 if (auto *s = std::get_if<Nominal>(&var)) 527 return s->aliasOffset; 528 return 0; 529 } 530 void setAlias(std::size_t offset) { 531 if (auto *s = std::get_if<Nominal>(&var)) { 532 s->aliaser = true; 533 s->aliasOffset = offset; 534 } else { 535 llvm_unreachable("not a nominal var"); 536 } 537 } 538 539 void setHeapAlloc(bool to = true) { 540 if (auto *s = std::get_if<Nominal>(&var)) 541 s->heapAlloc = to; 542 else 543 llvm_unreachable("not a nominal var"); 544 } 545 void setPointer(bool to = true) { 546 if (auto *s = std::get_if<Nominal>(&var)) 547 s->pointer = to; 548 else 549 llvm_unreachable("not a nominal var"); 550 } 551 void setTarget(bool to = true) { 552 if (auto *s = std::get_if<Nominal>(&var)) 553 s->target = to; 554 else 555 llvm_unreachable("not a nominal var"); 556 } 557 558 /// The depth is recorded for nominal variables as a debugging aid. 559 int getDepth() const { 560 if (auto *s = std::get_if<Nominal>(&var)) 561 return s->depth; 562 return 0; 563 } 564 565 LLVM_DUMP_METHOD void dump() const; 566 567 private: 568 std::variant<Nominal, AggregateStore> var; 569 }; 570 571 /// Function-like units may contain evaluations (executable statements) and 572 /// nested function-like units (internal procedures and function statements). 573 struct FunctionLikeUnit : public ProgramUnit { 574 // wrapper statements for function-like syntactic structures 575 using FunctionStatement = 576 ReferenceVariant<parser::Statement<parser::ProgramStmt>, 577 parser::Statement<parser::EndProgramStmt>, 578 parser::Statement<parser::FunctionStmt>, 579 parser::Statement<parser::EndFunctionStmt>, 580 parser::Statement<parser::SubroutineStmt>, 581 parser::Statement<parser::EndSubroutineStmt>, 582 parser::Statement<parser::MpSubprogramStmt>, 583 parser::Statement<parser::EndMpSubprogramStmt>>; 584 585 FunctionLikeUnit( 586 const parser::MainProgram &f, const PftNode &parent, 587 const Fortran::semantics::SemanticsContext &semanticsContext); 588 FunctionLikeUnit( 589 const parser::FunctionSubprogram &f, const PftNode &parent, 590 const Fortran::semantics::SemanticsContext &semanticsContext); 591 FunctionLikeUnit( 592 const parser::SubroutineSubprogram &f, const PftNode &parent, 593 const Fortran::semantics::SemanticsContext &semanticsContext); 594 FunctionLikeUnit( 595 const parser::SeparateModuleSubprogram &f, const PftNode &parent, 596 const Fortran::semantics::SemanticsContext &semanticsContext); 597 FunctionLikeUnit(FunctionLikeUnit &&) = default; 598 FunctionLikeUnit(const FunctionLikeUnit &) = delete; 599 600 std::vector<Variable> getOrderedSymbolTable() { return varList[0]; } 601 602 bool isMainProgram() const { 603 return endStmt.isA<parser::Statement<parser::EndProgramStmt>>(); 604 } 605 606 /// Get the starting source location for this function like unit 607 parser::CharBlock getStartingSourceLoc() const; 608 609 void setActiveEntry(int entryIndex) { 610 assert(entryIndex >= 0 && entryIndex < (int)entryPointList.size() && 611 "invalid entry point index"); 612 activeEntry = entryIndex; 613 } 614 615 /// Return a reference to the subprogram symbol of this FunctionLikeUnit. 616 /// This should not be called if the FunctionLikeUnit is the main program 617 /// since anonymous main programs do not have a symbol. 618 const semantics::Symbol &getSubprogramSymbol() const { 619 const semantics::Symbol *symbol = entryPointList[activeEntry].first; 620 if (!symbol) 621 llvm::report_fatal_error( 622 "not inside a procedure; do not call on main program."); 623 return *symbol; 624 } 625 626 /// Return a pointer to the current entry point Evaluation. 627 /// This is null for a primary entry point. 628 Evaluation *getEntryEval() const { 629 return entryPointList[activeEntry].second; 630 } 631 632 //===--------------------------------------------------------------------===// 633 // Host associations 634 //===--------------------------------------------------------------------===// 635 636 void setHostAssociatedSymbols( 637 const llvm::SetVector<const semantics::Symbol *> &symbols) { 638 hostAssociations.addSymbolsToBind(symbols); 639 } 640 641 /// Return the host associations, if any, from the parent (host) procedure. 642 /// Crashes if the parent is not a procedure. 643 HostAssociations &parentHostAssoc(); 644 645 /// Return true iff the parent is a procedure and the parent has a non-empty 646 /// set of host associations. 647 bool parentHasHostAssoc(); 648 649 /// Return the host associations for this function like unit. The list of host 650 /// associations are kept in the host procedure. 651 HostAssociations &getHostAssoc() { return hostAssociations; } 652 653 LLVM_DUMP_METHOD void dump() const; 654 655 /// Anonymous programs do not have a begin statement 656 std::optional<FunctionStatement> beginStmt; 657 FunctionStatement endStmt; 658 EvaluationList evaluationList; 659 LabelEvalMap labelEvaluationMap; 660 SymbolLabelMap assignSymbolLabelMap; 661 std::list<FunctionLikeUnit> nestedFunctions; 662 /// <Symbol, Evaluation> pairs for each entry point. The pair at index 0 663 /// is the primary entry point; remaining pairs are alternate entry points. 664 /// The primary entry point symbol is Null for an anonymous program. 665 /// A named program symbol has MainProgramDetails. Other symbols have 666 /// SubprogramDetails. Evaluations are filled in for alternate entries. 667 llvm::SmallVector<std::pair<const semantics::Symbol *, Evaluation *>, 1> 668 entryPointList{std::pair{nullptr, nullptr}}; 669 /// Current index into entryPointList. Index 0 is the primary entry point. 670 int activeEntry = 0; 671 /// Primary result for function subprograms with alternate entries. This 672 /// is one of the largest result values, not necessarily the first one. 673 const semantics::Symbol *primaryResult{nullptr}; 674 /// Terminal basic block (if any) 675 mlir::Block *finalBlock{}; 676 std::vector<std::vector<Variable>> varList; 677 HostAssociations hostAssociations; 678 }; 679 680 /// Module-like units contain a list of function-like units. 681 struct ModuleLikeUnit : public ProgramUnit { 682 // wrapper statements for module-like syntactic structures 683 using ModuleStatement = 684 ReferenceVariant<parser::Statement<parser::ModuleStmt>, 685 parser::Statement<parser::EndModuleStmt>, 686 parser::Statement<parser::SubmoduleStmt>, 687 parser::Statement<parser::EndSubmoduleStmt>>; 688 689 ModuleLikeUnit(const parser::Module &m, const PftNode &parent); 690 ModuleLikeUnit(const parser::Submodule &m, const PftNode &parent); 691 ~ModuleLikeUnit() = default; 692 ModuleLikeUnit(ModuleLikeUnit &&) = default; 693 ModuleLikeUnit(const ModuleLikeUnit &) = delete; 694 695 LLVM_DUMP_METHOD void dump() const; 696 697 std::vector<Variable> getOrderedSymbolTable() { return varList[0]; } 698 699 /// Get the starting source location for this module like unit. 700 parser::CharBlock getStartingSourceLoc() const; 701 702 /// Get the module scope. 703 const Fortran::semantics::Scope &getScope() const; 704 705 ModuleStatement beginStmt; 706 ModuleStatement endStmt; 707 std::list<FunctionLikeUnit> nestedFunctions; 708 EvaluationList evaluationList; 709 std::vector<std::vector<Variable>> varList; 710 }; 711 712 /// Block data units contain the variables and data initializers for common 713 /// blocks, etc. 714 struct BlockDataUnit : public ProgramUnit { 715 BlockDataUnit(const parser::BlockData &bd, const PftNode &parent, 716 const Fortran::semantics::SemanticsContext &semanticsContext); 717 BlockDataUnit(BlockDataUnit &&) = default; 718 BlockDataUnit(const BlockDataUnit &) = delete; 719 720 LLVM_DUMP_METHOD void dump() const; 721 722 const Fortran::semantics::Scope &symTab; // symbol table 723 }; 724 725 // Top level compiler directives 726 struct CompilerDirectiveUnit : public ProgramUnit { 727 CompilerDirectiveUnit(const parser::CompilerDirective &directive, 728 const PftNode &parent) 729 : ProgramUnit{directive, parent} {}; 730 CompilerDirectiveUnit(CompilerDirectiveUnit &&) = default; 731 CompilerDirectiveUnit(const CompilerDirectiveUnit &) = delete; 732 }; 733 734 /// A Program is the top-level root of the PFT. 735 struct Program { 736 using Units = std::variant<FunctionLikeUnit, ModuleLikeUnit, BlockDataUnit, 737 CompilerDirectiveUnit>; 738 739 Program(semantics::CommonBlockList &&commonBlocks) 740 : commonBlocks{std::move(commonBlocks)} {} 741 Program(Program &&) = default; 742 Program(const Program &) = delete; 743 744 const std::list<Units> &getUnits() const { return units; } 745 std::list<Units> &getUnits() { return units; } 746 const semantics::CommonBlockList &getCommonBlocks() const { 747 return commonBlocks; 748 } 749 750 /// LLVM dump method on a Program. 751 LLVM_DUMP_METHOD void dump() const; 752 753 private: 754 std::list<Units> units; 755 semantics::CommonBlockList commonBlocks; 756 }; 757 758 /// Return the list of variables that appears in the specification expressions 759 /// of a function result. 760 std::vector<pft::Variable> 761 buildFuncResultDependencyList(const Fortran::semantics::Symbol &); 762 763 /// Helper to get location from FunctionLikeUnit/ModuleLikeUnit begin/end 764 /// statements. 765 template <typename T> 766 static parser::CharBlock stmtSourceLoc(const T &stmt) { 767 return stmt.visit(common::visitors{[](const auto &x) { return x.source; }}); 768 } 769 770 /// Get the first PFT ancestor node that has type ParentType. 771 template <typename ParentType, typename A> 772 ParentType *getAncestor(A &node) { 773 if (auto *seekedParent = node.parent.template getIf<ParentType>()) 774 return seekedParent; 775 return node.parent.visit(common::visitors{ 776 [](Program &p) -> ParentType * { return nullptr; }, 777 [](auto &p) -> ParentType * { return getAncestor<ParentType>(p); }}); 778 } 779 780 /// Call the provided \p callBack on all symbols that are referenced inside \p 781 /// funit. 782 void visitAllSymbols(const FunctionLikeUnit &funit, 783 std::function<void(const semantics::Symbol &)> callBack); 784 785 /// Call the provided \p callBack on all symbols that are referenced inside \p 786 /// eval region. 787 void visitAllSymbols(const Evaluation &eval, 788 std::function<void(const semantics::Symbol &)> callBack); 789 790 } // namespace Fortran::lower::pft 791 792 namespace Fortran::lower { 793 /// Create a PFT (Pre-FIR Tree) from the parse tree. 794 /// 795 /// A PFT is a light weight tree over the parse tree that is used to create FIR. 796 /// The PFT captures pointers back into the parse tree, so the parse tree must 797 /// not be changed between the construction of the PFT and its last use. The 798 /// PFT captures a structured view of a program. A program is a list of units. 799 /// A function like unit contains a list of evaluations. An evaluation is 800 /// either a statement, or a construct with a nested list of evaluations. 801 std::unique_ptr<pft::Program> 802 createPFT(const parser::Program &root, 803 const Fortran::semantics::SemanticsContext &semanticsContext); 804 805 /// Dumper for displaying a PFT. 806 void dumpPFT(llvm::raw_ostream &outputStream, const pft::Program &pft); 807 } // namespace Fortran::lower 808 809 #endif // FORTRAN_LOWER_PFTBUILDER_H 810