xref: /llvm-project/flang/include/flang/Lower/PFTBuilder.h (revision 6003be7ef14bd95647e1ea6ec9685c1310f8ce58)
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