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