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