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