1 //===-- PFTBuilder.cc -----------------------------------------------------===//
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 #include "flang/Lower/PFTBuilder.h"
10 #include "flang/Lower/Utils.h"
11 #include "flang/Parser/dump-parse-tree.h"
12 #include "flang/Parser/parse-tree-visitor.h"
13 #include "flang/Semantics/semantics.h"
14 #include "flang/Semantics/tools.h"
15 #include "llvm/Support/CommandLine.h"
16 
17 static llvm::cl::opt<bool> clDisableStructuredFir(
18     "no-structured-fir", llvm::cl::desc("disable generation of structured FIR"),
19     llvm::cl::init(false), llvm::cl::Hidden);
20 
21 using namespace Fortran;
22 
23 namespace {
24 /// Helpers to unveil parser node inside Fortran::parser::Statement<>,
25 /// Fortran::parser::UnlabeledStatement, and Fortran::common::Indirection<>
26 template <typename A>
27 struct RemoveIndirectionHelper {
28   using Type = A;
29 };
30 template <typename A>
31 struct RemoveIndirectionHelper<common::Indirection<A>> {
32   using Type = A;
33 };
34 
35 template <typename A>
36 struct UnwrapStmt {
37   static constexpr bool isStmt{false};
38 };
39 template <typename A>
40 struct UnwrapStmt<parser::Statement<A>> {
41   static constexpr bool isStmt{true};
42   using Type = typename RemoveIndirectionHelper<A>::Type;
43   constexpr UnwrapStmt(const parser::Statement<A> &a)
44       : unwrapped{removeIndirection(a.statement)}, position{a.source},
45         label{a.label} {}
46   const Type &unwrapped;
47   parser::CharBlock position;
48   std::optional<parser::Label> label;
49 };
50 template <typename A>
51 struct UnwrapStmt<parser::UnlabeledStatement<A>> {
52   static constexpr bool isStmt{true};
53   using Type = typename RemoveIndirectionHelper<A>::Type;
54   constexpr UnwrapStmt(const parser::UnlabeledStatement<A> &a)
55       : unwrapped{removeIndirection(a.statement)}, position{a.source} {}
56   const Type &unwrapped;
57   parser::CharBlock position;
58   std::optional<parser::Label> label;
59 };
60 
61 /// The instantiation of a parse tree visitor (Pre and Post) is extremely
62 /// expensive in terms of compile and link time.  So one goal here is to
63 /// limit the bridge to one such instantiation.
64 class PFTBuilder {
65 public:
66   PFTBuilder(const semantics::SemanticsContext &semanticsContext)
67       : pgm{std::make_unique<lower::pft::Program>()},
68         parentVariantStack{*pgm.get()}, semanticsContext{semanticsContext} {}
69 
70   /// Get the result
71   std::unique_ptr<lower::pft::Program> result() { return std::move(pgm); }
72 
73   template <typename A>
74   constexpr bool Pre(const A &a) {
75     if constexpr (lower::pft::isFunctionLike<A>) {
76       return enterFunction(a, semanticsContext);
77     } else if constexpr (lower::pft::isConstruct<A> ||
78                          lower::pft::isDirective<A>) {
79       return enterConstructOrDirective(a);
80     } else if constexpr (UnwrapStmt<A>::isStmt) {
81       using T = typename UnwrapStmt<A>::Type;
82       // Node "a" being visited has one of the following types:
83       // Statement<T>, Statement<Indirection<T>, UnlabeledStatement<T>,
84       // or UnlabeledStatement<Indirection<T>>
85       auto stmt{UnwrapStmt<A>(a)};
86       if constexpr (lower::pft::isConstructStmt<T> ||
87                     lower::pft::isOtherStmt<T>) {
88         addEvaluation(lower::pft::Evaluation{stmt.unwrapped,
89                                              parentVariantStack.back(),
90                                              stmt.position, stmt.label});
91         return false;
92       } else if constexpr (std::is_same_v<T, parser::ActionStmt>) {
93         addEvaluation(
94             makeEvaluationAction(stmt.unwrapped, stmt.position, stmt.label));
95         return true;
96       }
97     }
98     return true;
99   }
100 
101   template <typename A>
102   constexpr void Post(const A &) {
103     if constexpr (lower::pft::isFunctionLike<A>) {
104       exitFunction();
105     } else if constexpr (lower::pft::isConstruct<A> ||
106                          lower::pft::isDirective<A>) {
107       exitConstructOrDirective();
108     }
109   }
110 
111   // Module like
112   bool Pre(const parser::Module &node) { return enterModule(node); }
113   bool Pre(const parser::Submodule &node) { return enterModule(node); }
114 
115   void Post(const parser::Module &) { exitModule(); }
116   void Post(const parser::Submodule &) { exitModule(); }
117 
118   // Block data
119   bool Pre(const parser::BlockData &node) {
120     addUnit(lower::pft::BlockDataUnit{node, parentVariantStack.back()});
121     return false;
122   }
123 
124   // Get rid of production wrapper
125   bool Pre(const parser::UnlabeledStatement<parser::ForallAssignmentStmt>
126                &statement) {
127     addEvaluation(std::visit(
128         [&](const auto &x) {
129           return lower::pft::Evaluation{
130               x, parentVariantStack.back(), statement.source, {}};
131         },
132         statement.statement.u));
133     return false;
134   }
135   bool Pre(const parser::Statement<parser::ForallAssignmentStmt> &statement) {
136     addEvaluation(std::visit(
137         [&](const auto &x) {
138           return lower::pft::Evaluation{x, parentVariantStack.back(),
139                                         statement.source, statement.label};
140         },
141         statement.statement.u));
142     return false;
143   }
144   bool Pre(const parser::WhereBodyConstruct &whereBody) {
145     return std::visit(
146         common::visitors{
147             [&](const parser::Statement<parser::AssignmentStmt> &stmt) {
148               // Not caught as other AssignmentStmt because it is not
149               // wrapped in a parser::ActionStmt.
150               addEvaluation(lower::pft::Evaluation{stmt.statement,
151                                                    parentVariantStack.back(),
152                                                    stmt.source, stmt.label});
153               return false;
154             },
155             [&](const auto &) { return true; },
156         },
157         whereBody.u);
158   }
159 
160 private:
161   /// Initialize a new module-like unit and make it the builder's focus.
162   template <typename A>
163   bool enterModule(const A &func) {
164     auto &unit =
165         addUnit(lower::pft::ModuleLikeUnit{func, parentVariantStack.back()});
166     functionList = &unit.nestedFunctions;
167     parentVariantStack.emplace_back(unit);
168     return true;
169   }
170 
171   void exitModule() {
172     parentVariantStack.pop_back();
173     resetFunctionList();
174   }
175 
176   /// Initialize a new function-like unit and make it the builder's focus.
177   template <typename A>
178   bool enterFunction(const A &func,
179                      const semantics::SemanticsContext &semanticsContext) {
180     auto &unit = addFunction(lower::pft::FunctionLikeUnit{
181         func, parentVariantStack.back(), semanticsContext});
182     labelEvaluationMap = &unit.labelEvaluationMap;
183     assignSymbolLabelMap = &unit.assignSymbolLabelMap;
184     functionList = &unit.nestedFunctions;
185     pushEvaluationList(&unit.evaluationList);
186     parentVariantStack.emplace_back(unit);
187     return true;
188   }
189 
190   void exitFunction() {
191     // Guarantee that there is a branch target after the last user statement.
192     static const parser::ContinueStmt endTarget{};
193     addEvaluation(
194         lower::pft::Evaluation{endTarget, parentVariantStack.back(), {}, {}});
195     lastLexicalEvaluation = nullptr;
196     analyzeBranches(nullptr, *evaluationListStack.back()); // add branch links
197     popEvaluationList();
198     labelEvaluationMap = nullptr;
199     assignSymbolLabelMap = nullptr;
200     parentVariantStack.pop_back();
201     resetFunctionList();
202   }
203 
204   /// Initialize a new construct and make it the builder's focus.
205   template <typename A>
206   bool enterConstructOrDirective(const A &construct) {
207     auto &eval = addEvaluation(
208         lower::pft::Evaluation{construct, parentVariantStack.back()});
209     eval.evaluationList.reset(new lower::pft::EvaluationList);
210     pushEvaluationList(eval.evaluationList.get());
211     parentVariantStack.emplace_back(eval);
212     constructAndDirectiveStack.emplace_back(&eval);
213     return true;
214   }
215 
216   void exitConstructOrDirective() {
217     popEvaluationList();
218     parentVariantStack.pop_back();
219     constructAndDirectiveStack.pop_back();
220   }
221 
222   /// Reset functionList to an enclosing function's functionList.
223   void resetFunctionList() {
224     if (!parentVariantStack.empty()) {
225       parentVariantStack.back().visit(common::visitors{
226           [&](lower::pft::FunctionLikeUnit &p) {
227             functionList = &p.nestedFunctions;
228           },
229           [&](lower::pft::ModuleLikeUnit &p) {
230             functionList = &p.nestedFunctions;
231           },
232           [&](auto &) { functionList = nullptr; },
233       });
234     }
235   }
236 
237   template <typename A>
238   A &addUnit(A &&unit) {
239     pgm->getUnits().emplace_back(std::move(unit));
240     return std::get<A>(pgm->getUnits().back());
241   }
242 
243   template <typename A>
244   A &addFunction(A &&func) {
245     if (functionList) {
246       functionList->emplace_back(std::move(func));
247       return functionList->back();
248     }
249     return addUnit(std::move(func));
250   }
251 
252   // ActionStmt has a couple of non-conforming cases, explicitly handled here.
253   // The other cases use an Indirection, which are discarded in the PFT.
254   lower::pft::Evaluation
255   makeEvaluationAction(const parser::ActionStmt &statement,
256                        parser::CharBlock position,
257                        std::optional<parser::Label> label) {
258     return std::visit(
259         common::visitors{
260             [&](const auto &x) {
261               return lower::pft::Evaluation{removeIndirection(x),
262                                             parentVariantStack.back(), position,
263                                             label};
264             },
265         },
266         statement.u);
267   }
268 
269   /// Append an Evaluation to the end of the current list.
270   lower::pft::Evaluation &addEvaluation(lower::pft::Evaluation &&eval) {
271     assert(functionList && "not in a function");
272     assert(evaluationListStack.size() > 0);
273     if (constructAndDirectiveStack.size() > 0) {
274       eval.parentConstruct = constructAndDirectiveStack.back();
275     }
276     evaluationListStack.back()->emplace_back(std::move(eval));
277     lower::pft::Evaluation *p = &evaluationListStack.back()->back();
278     if (p->isActionStmt() || p->isConstructStmt()) {
279       if (lastLexicalEvaluation) {
280         lastLexicalEvaluation->lexicalSuccessor = p;
281         p->printIndex = lastLexicalEvaluation->printIndex + 1;
282       } else {
283         p->printIndex = 1;
284       }
285       lastLexicalEvaluation = p;
286     }
287     if (p->label.has_value()) {
288       labelEvaluationMap->try_emplace(*p->label, p);
289     }
290     return evaluationListStack.back()->back();
291   }
292 
293   /// push a new list on the stack of Evaluation lists
294   void pushEvaluationList(lower::pft::EvaluationList *eval) {
295     assert(functionList && "not in a function");
296     assert(eval && eval->empty() && "evaluation list isn't correct");
297     evaluationListStack.emplace_back(eval);
298   }
299 
300   /// pop the current list and return to the last Evaluation list
301   void popEvaluationList() {
302     assert(functionList && "not in a function");
303     evaluationListStack.pop_back();
304   }
305 
306   /// Mark I/O statement ERR, EOR, and END specifier branch targets.
307   template <typename A>
308   void analyzeIoBranches(lower::pft::Evaluation &eval, const A &stmt) {
309     auto processIfLabel{[&](const auto &specs) {
310       using LabelNodes =
311           std::tuple<parser::ErrLabel, parser::EorLabel, parser::EndLabel>;
312       for (const auto &spec : specs) {
313         const auto *label = std::visit(
314             [](const auto &label) -> const parser::Label * {
315               using B = std::decay_t<decltype(label)>;
316               if constexpr (common::HasMember<B, LabelNodes>) {
317                 return &label.v;
318               }
319               return nullptr;
320             },
321             spec.u);
322 
323         if (label)
324           markBranchTarget(eval, *label);
325       }
326     }};
327 
328     using OtherIOStmts =
329         std::tuple<parser::BackspaceStmt, parser::CloseStmt,
330                    parser::EndfileStmt, parser::FlushStmt, parser::OpenStmt,
331                    parser::RewindStmt, parser::WaitStmt>;
332 
333     if constexpr (std::is_same_v<A, parser::ReadStmt> ||
334                   std::is_same_v<A, parser::WriteStmt>) {
335       processIfLabel(stmt.controls);
336     } else if constexpr (std::is_same_v<A, parser::InquireStmt>) {
337       processIfLabel(std::get<std::list<parser::InquireSpec>>(stmt.u));
338     } else if constexpr (common::HasMember<A, OtherIOStmts>) {
339       processIfLabel(stmt.v);
340     } else {
341       // Always crash if this is instantiated
342       static_assert(!std::is_same_v<A, parser::ReadStmt>,
343                     "Unexpected IO statement");
344     }
345   }
346 
347   /// Set the exit of a construct, possibly from multiple enclosing constructs.
348   void setConstructExit(lower::pft::Evaluation &eval) {
349     eval.constructExit = eval.evaluationList->back().lexicalSuccessor;
350     if (eval.constructExit && eval.constructExit->isNopConstructStmt()) {
351       eval.constructExit = eval.constructExit->parentConstruct->constructExit;
352     }
353     assert(eval.constructExit && "missing construct exit");
354   }
355 
356   void markBranchTarget(lower::pft::Evaluation &sourceEvaluation,
357                         lower::pft::Evaluation &targetEvaluation) {
358     sourceEvaluation.isUnstructured = true;
359     if (!sourceEvaluation.controlSuccessor) {
360       sourceEvaluation.controlSuccessor = &targetEvaluation;
361     }
362     targetEvaluation.isNewBlock = true;
363   }
364   void markBranchTarget(lower::pft::Evaluation &sourceEvaluation,
365                         parser::Label label) {
366     assert(label && "missing branch target label");
367     lower::pft::Evaluation *targetEvaluation{
368         labelEvaluationMap->find(label)->second};
369     assert(targetEvaluation && "missing branch target evaluation");
370     markBranchTarget(sourceEvaluation, *targetEvaluation);
371   }
372 
373   /// Return the first non-nop successor of an evaluation, possibly exiting
374   /// from one or more enclosing constructs.
375   lower::pft::Evaluation *exitSuccessor(lower::pft::Evaluation &eval) {
376     lower::pft::Evaluation *successor{eval.lexicalSuccessor};
377     if (successor && successor->isNopConstructStmt()) {
378       successor = successor->parentConstruct->constructExit;
379     }
380     assert(successor && "missing exit successor");
381     return successor;
382   }
383 
384   /// Mark the exit successor of an Evaluation as a new block.
385   void markSuccessorAsNewBlock(lower::pft::Evaluation &eval) {
386     exitSuccessor(eval)->isNewBlock = true;
387   }
388 
389   template <typename A>
390   inline std::string getConstructName(const A &stmt) {
391     using MaybeConstructNameWrapper =
392         std::tuple<parser::BlockStmt, parser::CycleStmt, parser::ElseStmt,
393                    parser::ElsewhereStmt, parser::EndAssociateStmt,
394                    parser::EndBlockStmt, parser::EndCriticalStmt,
395                    parser::EndDoStmt, parser::EndForallStmt, parser::EndIfStmt,
396                    parser::EndSelectStmt, parser::EndWhereStmt,
397                    parser::ExitStmt>;
398     if constexpr (common::HasMember<A, MaybeConstructNameWrapper>) {
399       if (stmt.v)
400         return stmt.v->ToString();
401     }
402 
403     using MaybeConstructNameInTuple = std::tuple<
404         parser::AssociateStmt, parser::CaseStmt, parser::ChangeTeamStmt,
405         parser::CriticalStmt, parser::ElseIfStmt, parser::EndChangeTeamStmt,
406         parser::ForallConstructStmt, parser::IfThenStmt, parser::LabelDoStmt,
407         parser::MaskedElsewhereStmt, parser::NonLabelDoStmt,
408         parser::SelectCaseStmt, parser::SelectRankCaseStmt,
409         parser::TypeGuardStmt, parser::WhereConstructStmt>;
410 
411     if constexpr (common::HasMember<A, MaybeConstructNameInTuple>) {
412       if (auto name{std::get<std::optional<parser::Name>>(stmt.t)})
413         return name->ToString();
414     }
415 
416     // These statements have several std::optional<parser::Name>
417     if constexpr (std::is_same_v<A, parser::SelectRankStmt> ||
418                   std::is_same_v<A, parser::SelectTypeStmt>) {
419       if (auto name{std::get<0>(stmt.t)}) {
420         return name->ToString();
421       }
422     }
423     return {};
424   }
425 
426   /// \p parentConstruct can be null if this statement is at the highest
427   /// level of a program.
428   template <typename A>
429   void insertConstructName(const A &stmt,
430                            lower::pft::Evaluation *parentConstruct) {
431     std::string name{getConstructName(stmt)};
432     if (!name.empty()) {
433       constructNameMap[name] = parentConstruct;
434     }
435   }
436 
437   /// Insert branch links for a list of Evaluations.
438   /// \p parentConstruct can be null if the evaluationList contains the
439   /// top-level statements of a program.
440   void analyzeBranches(lower::pft::Evaluation *parentConstruct,
441                        std::list<lower::pft::Evaluation> &evaluationList) {
442     lower::pft::Evaluation *lastConstructStmtEvaluation{nullptr};
443     lower::pft::Evaluation *lastIfStmtEvaluation{nullptr};
444     for (auto &eval : evaluationList) {
445       eval.visit(common::visitors{
446           // Action statements
447           [&](const parser::CallStmt &s) {
448             // Look for alternate return specifiers.
449             const auto &args{std::get<std::list<parser::ActualArgSpec>>(s.v.t)};
450             for (const auto &arg : args) {
451               const auto &actual{std::get<parser::ActualArg>(arg.t)};
452               if (const auto *altReturn{
453                       std::get_if<parser::AltReturnSpec>(&actual.u)}) {
454                 markBranchTarget(eval, altReturn->v);
455               }
456             }
457           },
458           [&](const parser::CycleStmt &s) {
459             std::string name{getConstructName(s)};
460             lower::pft::Evaluation *construct{name.empty()
461                                                   ? doConstructStack.back()
462                                                   : constructNameMap[name]};
463             assert(construct && "missing CYCLE construct");
464             markBranchTarget(eval, construct->evaluationList->back());
465           },
466           [&](const parser::ExitStmt &s) {
467             std::string name{getConstructName(s)};
468             lower::pft::Evaluation *construct{name.empty()
469                                                   ? doConstructStack.back()
470                                                   : constructNameMap[name]};
471             assert(construct && "missing EXIT construct");
472             markBranchTarget(eval, *construct->constructExit);
473           },
474           [&](const parser::GotoStmt &s) { markBranchTarget(eval, s.v); },
475           [&](const parser::IfStmt &) { lastIfStmtEvaluation = &eval; },
476           [&](const parser::ReturnStmt &) {
477             eval.isUnstructured = true;
478             if (eval.lexicalSuccessor->lexicalSuccessor)
479               markSuccessorAsNewBlock(eval);
480           },
481           [&](const parser::StopStmt &) {
482             eval.isUnstructured = true;
483             if (eval.lexicalSuccessor->lexicalSuccessor)
484               markSuccessorAsNewBlock(eval);
485           },
486           [&](const parser::ComputedGotoStmt &s) {
487             for (auto &label : std::get<std::list<parser::Label>>(s.t)) {
488               markBranchTarget(eval, label);
489             }
490           },
491           [&](const parser::ArithmeticIfStmt &s) {
492             markBranchTarget(eval, std::get<1>(s.t));
493             markBranchTarget(eval, std::get<2>(s.t));
494             markBranchTarget(eval, std::get<3>(s.t));
495             if (semantics::ExprHasTypeCategory(
496                     *semantics::GetExpr(std::get<parser::Expr>(s.t)),
497                     common::TypeCategory::Real)) {
498               // Real expression evaluation uses an additional local block.
499               eval.localBlocks.emplace_back(nullptr);
500             }
501           },
502           [&](const parser::AssignStmt &s) { // legacy label assignment
503             auto &label = std::get<parser::Label>(s.t);
504             const auto *sym = std::get<parser::Name>(s.t).symbol;
505             assert(sym && "missing AssignStmt symbol");
506             lower::pft::Evaluation *target{
507                 labelEvaluationMap->find(label)->second};
508             assert(target && "missing branch target evaluation");
509             if (!target->isA<parser::FormatStmt>()) {
510               target->isNewBlock = true;
511             }
512             auto iter = assignSymbolLabelMap->find(*sym);
513             if (iter == assignSymbolLabelMap->end()) {
514               lower::pft::LabelSet labelSet{};
515               labelSet.insert(label);
516               assignSymbolLabelMap->try_emplace(*sym, labelSet);
517             } else {
518               iter->second.insert(label);
519             }
520           },
521           [&](const parser::AssignedGotoStmt &) {
522             // Although this statement is a branch, it doesn't have any
523             // explicit control successors.  So the code at the end of the
524             // loop won't mark the exit successor.  Do that here.
525             markSuccessorAsNewBlock(eval);
526           },
527 
528           // Construct statements
529           [&](const parser::AssociateStmt &s) {
530             insertConstructName(s, parentConstruct);
531           },
532           [&](const parser::BlockStmt &s) {
533             insertConstructName(s, parentConstruct);
534           },
535           [&](const parser::SelectCaseStmt &s) {
536             insertConstructName(s, parentConstruct);
537             lastConstructStmtEvaluation = &eval;
538           },
539           [&](const parser::CaseStmt &) {
540             eval.isNewBlock = true;
541             lastConstructStmtEvaluation->controlSuccessor = &eval;
542             lastConstructStmtEvaluation = &eval;
543           },
544           [&](const parser::EndSelectStmt &) {
545             eval.lexicalSuccessor->isNewBlock = true;
546             lastConstructStmtEvaluation = nullptr;
547           },
548           [&](const parser::ChangeTeamStmt &s) {
549             insertConstructName(s, parentConstruct);
550           },
551           [&](const parser::CriticalStmt &s) {
552             insertConstructName(s, parentConstruct);
553           },
554           [&](const parser::NonLabelDoStmt &s) {
555             insertConstructName(s, parentConstruct);
556             doConstructStack.push_back(parentConstruct);
557             auto &control{std::get<std::optional<parser::LoopControl>>(s.t)};
558             // eval.block is the loop preheader block, which will be set
559             // elsewhere if the NonLabelDoStmt is itself a target.
560             // eval.localBlocks[0] is the loop header block.
561             eval.localBlocks.emplace_back(nullptr);
562             if (!control.has_value()) {
563               eval.isUnstructured = true; // infinite loop
564               return;
565             }
566             eval.lexicalSuccessor->isNewBlock = true;
567             eval.controlSuccessor = &evaluationList.back();
568             if (std::holds_alternative<parser::ScalarLogicalExpr>(control->u)) {
569               eval.isUnstructured = true; // while loop
570             }
571             // Defer additional processing for an unstructured concurrent loop
572             // to the EndDoStmt, when the loop is known to be unstructured.
573           },
574           [&](const parser::EndDoStmt &) {
575             lower::pft::Evaluation &doEval{evaluationList.front()};
576             eval.controlSuccessor = &doEval;
577             doConstructStack.pop_back();
578             if (parentConstruct->lowerAsStructured()) {
579               return;
580             }
581             // Now that the loop is known to be unstructured, finish concurrent
582             // loop processing, using NonLabelDoStmt information.
583             parentConstruct->constructExit->isNewBlock = true;
584             const auto &doStmt{doEval.getIf<parser::NonLabelDoStmt>()};
585             assert(doStmt && "missing NonLabelDoStmt");
586             auto &control{
587                 std::get<std::optional<parser::LoopControl>>(doStmt->t)};
588             if (!control.has_value()) {
589               return; // infinite loop
590             }
591             const auto *concurrent{
592                 std::get_if<parser::LoopControl::Concurrent>(&control->u)};
593             if (!concurrent) {
594               return;
595             }
596             // Unstructured concurrent loop.  NonLabelDoStmt code accounts
597             // for one concurrent loop dimension.  Reserve preheader,
598             // header, and latch blocks for the remaining dimensions, and
599             // one block for a mask expression.
600             const auto &header{
601                 std::get<parser::ConcurrentHeader>(concurrent->t)};
602             auto dims{std::get<std::list<parser::ConcurrentControl>>(header.t)
603                           .size()};
604             for (; dims > 1; --dims) {
605               doEval.localBlocks.emplace_back(nullptr); // preheader
606               doEval.localBlocks.emplace_back(nullptr); // header
607               eval.localBlocks.emplace_back(nullptr);   // latch
608             }
609             if (std::get<std::optional<parser::ScalarLogicalExpr>>(header.t)) {
610               doEval.localBlocks.emplace_back(nullptr); // mask
611             }
612           },
613           [&](const parser::IfThenStmt &s) {
614             insertConstructName(s, parentConstruct);
615             eval.lexicalSuccessor->isNewBlock = true;
616             lastConstructStmtEvaluation = &eval;
617           },
618           [&](const parser::ElseIfStmt &) {
619             eval.isNewBlock = true;
620             eval.lexicalSuccessor->isNewBlock = true;
621             lastConstructStmtEvaluation->controlSuccessor = &eval;
622             lastConstructStmtEvaluation = &eval;
623           },
624           [&](const parser::ElseStmt &) {
625             eval.isNewBlock = true;
626             lastConstructStmtEvaluation->controlSuccessor = &eval;
627             lastConstructStmtEvaluation = nullptr;
628           },
629           [&](const parser::EndIfStmt &) {
630             if (parentConstruct->lowerAsUnstructured()) {
631               parentConstruct->constructExit->isNewBlock = true;
632             }
633             if (lastConstructStmtEvaluation) {
634               lastConstructStmtEvaluation->controlSuccessor =
635                   parentConstruct->constructExit;
636               lastConstructStmtEvaluation = nullptr;
637             }
638           },
639           [&](const parser::SelectRankStmt &s) {
640             insertConstructName(s, parentConstruct);
641           },
642           [&](const parser::SelectRankCaseStmt &) { eval.isNewBlock = true; },
643           [&](const parser::SelectTypeStmt &s) {
644             insertConstructName(s, parentConstruct);
645           },
646           [&](const parser::TypeGuardStmt &) { eval.isNewBlock = true; },
647 
648           // Constructs - set (unstructured) construct exit targets
649           [&](const parser::AssociateConstruct &) { setConstructExit(eval); },
650           [&](const parser::BlockConstruct &) {
651             // EndBlockStmt may have code.
652             eval.constructExit = &eval.evaluationList->back();
653           },
654           [&](const parser::CaseConstruct &) {
655             setConstructExit(eval);
656             eval.isUnstructured = true;
657           },
658           [&](const parser::ChangeTeamConstruct &) {
659             // EndChangeTeamStmt may have code.
660             eval.constructExit = &eval.evaluationList->back();
661           },
662           [&](const parser::CriticalConstruct &) {
663             // EndCriticalStmt may have code.
664             eval.constructExit = &eval.evaluationList->back();
665           },
666           [&](const parser::DoConstruct &) { setConstructExit(eval); },
667           [&](const parser::IfConstruct &) { setConstructExit(eval); },
668           [&](const parser::SelectRankConstruct &) {
669             setConstructExit(eval);
670             eval.isUnstructured = true;
671           },
672           [&](const parser::SelectTypeConstruct &) {
673             setConstructExit(eval);
674             eval.isUnstructured = true;
675           },
676 
677           [&](const auto &stmt) {
678             using A = std::decay_t<decltype(stmt)>;
679             using IoStmts = std::tuple<parser::BackspaceStmt, parser::CloseStmt,
680                                        parser::EndfileStmt, parser::FlushStmt,
681                                        parser::InquireStmt, parser::OpenStmt,
682                                        parser::ReadStmt, parser::RewindStmt,
683                                        parser::WaitStmt, parser::WriteStmt>;
684             if constexpr (common::HasMember<A, IoStmts>) {
685               analyzeIoBranches(eval, stmt);
686             }
687 
688             /* do nothing */
689           },
690       });
691 
692       // Analyze construct evaluations.
693       if (eval.evaluationList) {
694         analyzeBranches(&eval, *eval.evaluationList);
695       }
696 
697       // Insert branch links for an unstructured IF statement.
698       if (lastIfStmtEvaluation && lastIfStmtEvaluation != &eval) {
699         // eval is the action substatement of an IfStmt.
700         if (eval.lowerAsUnstructured()) {
701           eval.isNewBlock = true;
702           markSuccessorAsNewBlock(eval);
703           lastIfStmtEvaluation->isUnstructured = true;
704         }
705         lastIfStmtEvaluation->controlSuccessor = exitSuccessor(eval);
706         lastIfStmtEvaluation = nullptr;
707       }
708 
709       // Set the successor of the last statement in an IF or SELECT block.
710       if (!eval.controlSuccessor && eval.lexicalSuccessor &&
711           eval.lexicalSuccessor->isIntermediateConstructStmt()) {
712         eval.controlSuccessor = parentConstruct->constructExit;
713         eval.lexicalSuccessor->isNewBlock = true;
714       }
715 
716       // Propagate isUnstructured flag to enclosing construct.
717       if (parentConstruct && eval.isUnstructured) {
718         parentConstruct->isUnstructured = true;
719       }
720 
721       // The lexical successor of a branch starts a new block.
722       if (eval.controlSuccessor && eval.isActionStmt() &&
723           eval.lowerAsUnstructured()) {
724         markSuccessorAsNewBlock(eval);
725       }
726     }
727   }
728 
729   std::unique_ptr<lower::pft::Program> pgm;
730   std::vector<lower::pft::ParentVariant> parentVariantStack;
731   const semantics::SemanticsContext &semanticsContext;
732 
733   /// functionList points to the internal or module procedure function list
734   /// of a FunctionLikeUnit or a ModuleLikeUnit.  It may be null.
735   std::list<lower::pft::FunctionLikeUnit> *functionList{nullptr};
736   std::vector<lower::pft::Evaluation *> constructAndDirectiveStack{};
737   std::vector<lower::pft::Evaluation *> doConstructStack{};
738   /// evaluationListStack is the current nested construct evaluationList state.
739   std::vector<lower::pft::EvaluationList *> evaluationListStack{};
740   llvm::DenseMap<parser::Label, lower::pft::Evaluation *> *labelEvaluationMap{
741       nullptr};
742   lower::pft::SymbolLabelMap *assignSymbolLabelMap{nullptr};
743   std::map<std::string, lower::pft::Evaluation *> constructNameMap{};
744   lower::pft::Evaluation *lastLexicalEvaluation{nullptr};
745 };
746 
747 class PFTDumper {
748 public:
749   void dumpPFT(llvm::raw_ostream &outputStream, lower::pft::Program &pft) {
750     for (auto &unit : pft.getUnits()) {
751       std::visit(common::visitors{
752                      [&](lower::pft::BlockDataUnit &unit) {
753                        outputStream << getNodeIndex(unit) << " ";
754                        outputStream << "BlockData: ";
755                        outputStream << "\nEndBlockData\n\n";
756                      },
757                      [&](lower::pft::FunctionLikeUnit &func) {
758                        dumpFunctionLikeUnit(outputStream, func);
759                      },
760                      [&](lower::pft::ModuleLikeUnit &unit) {
761                        dumpModuleLikeUnit(outputStream, unit);
762                      },
763                  },
764                  unit);
765     }
766   }
767 
768   llvm::StringRef evaluationName(lower::pft::Evaluation &eval) {
769     return eval.visit(common::visitors{
770         [](const auto &parseTreeNode) {
771           return parser::ParseTreeDumper::GetNodeName(parseTreeNode);
772         },
773     });
774   }
775 
776   void dumpEvaluationList(llvm::raw_ostream &outputStream,
777                           lower::pft::EvaluationList &evaluationList,
778                           int indent = 1) {
779     static const std::string white{"                                      ++"};
780     std::string indentString{white.substr(0, indent * 2)};
781     for (lower::pft::Evaluation &eval : evaluationList) {
782       llvm::StringRef name{evaluationName(eval)};
783       std::string bang{eval.isUnstructured ? "!" : ""};
784       if (eval.isConstruct() || eval.isDirective()) {
785         outputStream << indentString << "<<" << name << bang << ">>";
786         if (eval.constructExit) {
787           outputStream << " -> " << eval.constructExit->printIndex;
788         }
789         outputStream << '\n';
790         dumpEvaluationList(outputStream, *eval.evaluationList, indent + 1);
791         outputStream << indentString << "<<End " << name << bang << ">>\n";
792         continue;
793       }
794       outputStream << indentString;
795       if (eval.printIndex) {
796         outputStream << eval.printIndex << ' ';
797       }
798       if (eval.isNewBlock) {
799         outputStream << '^';
800       }
801       if (eval.localBlocks.size()) {
802         outputStream << '*';
803       }
804       outputStream << name << bang;
805       if (eval.isActionStmt() || eval.isConstructStmt()) {
806         if (eval.controlSuccessor) {
807           outputStream << " -> " << eval.controlSuccessor->printIndex;
808         }
809       }
810       if (eval.position.size()) {
811         outputStream << ": " << eval.position.ToString();
812       }
813       outputStream << '\n';
814     }
815   }
816 
817   void dumpFunctionLikeUnit(llvm::raw_ostream &outputStream,
818                             lower::pft::FunctionLikeUnit &functionLikeUnit) {
819     outputStream << getNodeIndex(functionLikeUnit) << " ";
820     llvm::StringRef unitKind{};
821     std::string name{};
822     std::string header{};
823     if (functionLikeUnit.beginStmt) {
824       functionLikeUnit.beginStmt->visit(common::visitors{
825           [&](const parser::Statement<parser::ProgramStmt> &statement) {
826             unitKind = "Program";
827             name = statement.statement.v.ToString();
828           },
829           [&](const parser::Statement<parser::FunctionStmt> &statement) {
830             unitKind = "Function";
831             name = std::get<parser::Name>(statement.statement.t).ToString();
832             header = statement.source.ToString();
833           },
834           [&](const parser::Statement<parser::SubroutineStmt> &statement) {
835             unitKind = "Subroutine";
836             name = std::get<parser::Name>(statement.statement.t).ToString();
837             header = statement.source.ToString();
838           },
839           [&](const parser::Statement<parser::MpSubprogramStmt> &statement) {
840             unitKind = "MpSubprogram";
841             name = statement.statement.v.ToString();
842             header = statement.source.ToString();
843           },
844           [&](const auto &) {},
845       });
846     } else {
847       unitKind = "Program";
848       name = "<anonymous>";
849     }
850     outputStream << unitKind << ' ' << name;
851     if (header.size())
852       outputStream << ": " << header;
853     outputStream << '\n';
854     dumpEvaluationList(outputStream, functionLikeUnit.evaluationList);
855     if (!functionLikeUnit.nestedFunctions.empty()) {
856       outputStream << "\nContains\n";
857       for (auto &func : functionLikeUnit.nestedFunctions)
858         dumpFunctionLikeUnit(outputStream, func);
859       outputStream << "EndContains\n";
860     }
861     outputStream << "End" << unitKind << ' ' << name << "\n\n";
862   }
863 
864   void dumpModuleLikeUnit(llvm::raw_ostream &outputStream,
865                           lower::pft::ModuleLikeUnit &moduleLikeUnit) {
866     outputStream << getNodeIndex(moduleLikeUnit) << " ";
867     outputStream << "ModuleLike: ";
868     outputStream << "\nContains\n";
869     for (auto &func : moduleLikeUnit.nestedFunctions)
870       dumpFunctionLikeUnit(outputStream, func);
871     outputStream << "EndContains\nEndModuleLike\n\n";
872   }
873 
874   template <typename T>
875   std::size_t getNodeIndex(const T &node) {
876     auto addr{static_cast<const void *>(&node)};
877     auto it{nodeIndexes.find(addr)};
878     if (it != nodeIndexes.end()) {
879       return it->second;
880     }
881     nodeIndexes.try_emplace(addr, nextIndex);
882     return nextIndex++;
883   }
884   std::size_t getNodeIndex(const lower::pft::Program &) { return 0; }
885 
886 private:
887   llvm::DenseMap<const void *, std::size_t> nodeIndexes;
888   std::size_t nextIndex{1}; // 0 is the root
889 };
890 
891 } // namespace
892 
893 template <typename A, typename T>
894 static lower::pft::FunctionLikeUnit::FunctionStatement
895 getFunctionStmt(const T &func) {
896   return std::get<parser::Statement<A>>(func.t);
897 }
898 template <typename A, typename T>
899 static lower::pft::ModuleLikeUnit::ModuleStatement getModuleStmt(const T &mod) {
900   return std::get<parser::Statement<A>>(mod.t);
901 }
902 
903 static const semantics::Symbol *getSymbol(
904     std::optional<lower::pft::FunctionLikeUnit::FunctionStatement> &beginStmt) {
905   if (!beginStmt)
906     return nullptr;
907 
908   const auto *symbol = beginStmt->visit(common::visitors{
909       [](const parser::Statement<parser::ProgramStmt> &stmt)
910           -> const semantics::Symbol * { return stmt.statement.v.symbol; },
911       [](const parser::Statement<parser::FunctionStmt> &stmt)
912           -> const semantics::Symbol * {
913         return std::get<parser::Name>(stmt.statement.t).symbol;
914       },
915       [](const parser::Statement<parser::SubroutineStmt> &stmt)
916           -> const semantics::Symbol * {
917         return std::get<parser::Name>(stmt.statement.t).symbol;
918       },
919       [](const parser::Statement<parser::MpSubprogramStmt> &stmt)
920           -> const semantics::Symbol * { return stmt.statement.v.symbol; },
921       [](const auto &) -> const semantics::Symbol * {
922         llvm_unreachable("unknown FunctionLike beginStmt");
923         return nullptr;
924       }});
925   assert(symbol && "parser::Name must have resolved symbol");
926   return symbol;
927 }
928 
929 bool Fortran::lower::pft::Evaluation::lowerAsStructured() const {
930   return !lowerAsUnstructured();
931 }
932 
933 bool Fortran::lower::pft::Evaluation::lowerAsUnstructured() const {
934   return isUnstructured || clDisableStructuredFir;
935 }
936 
937 lower::pft::FunctionLikeUnit *
938 Fortran::lower::pft::Evaluation::getOwningProcedure() const {
939   return parentVariant.visit(common::visitors{
940       [](lower::pft::FunctionLikeUnit &c) { return &c; },
941       [&](lower::pft::Evaluation &c) { return c.getOwningProcedure(); },
942       [](auto &) -> lower::pft::FunctionLikeUnit * { return nullptr; },
943   });
944 }
945 
946 namespace {
947 /// This helper class is for sorting the symbols in the symbol table. We want
948 /// the symbols in an order such that a symbol will be visited after those it
949 /// depends upon. Otherwise this sort is stable and preserves the order of the
950 /// symbol table, which is sorted by name.
951 struct SymbolDependenceDepth {
952   explicit SymbolDependenceDepth(
953       std::vector<std::vector<lower::pft::Variable>> &vars)
954       : vars{vars} {}
955 
956   // Recursively visit each symbol to determine the height of its dependence on
957   // other symbols.
958   int analyze(const semantics::Symbol &sym) {
959     auto done = seen.insert(&sym);
960     if (!done.second)
961       return 0;
962     if (semantics::IsProcedure(sym)) {
963       // TODO: add declaration?
964       return 0;
965     }
966     if (sym.has<semantics::UseDetails>() ||
967         sym.has<semantics::HostAssocDetails>() ||
968         sym.has<semantics::NamelistDetails>() ||
969         sym.has<semantics::MiscDetails>()) {
970       // FIXME: do we want to do anything with any of these?
971       return 0;
972     }
973 
974     // Symbol must be something lowering will have to allocate.
975     bool global = semantics::IsSaved(sym);
976     int depth = 0;
977     const auto *symTy = sym.GetType();
978     assert(symTy && "symbol must have a type");
979 
980     // check CHARACTER's length
981     if (symTy->category() == semantics::DeclTypeSpec::Character)
982       if (auto e = symTy->characterTypeSpec().length().GetExplicit())
983         for (const auto &s : evaluate::CollectSymbols(*e))
984           depth = std::max(analyze(s) + 1, depth);
985 
986     if (const auto *details = sym.detailsIf<semantics::ObjectEntityDetails>()) {
987       auto doExplicit = [&](const auto &bound) {
988         if (bound.isExplicit()) {
989           semantics::SomeExpr e{*bound.GetExplicit()};
990           for (const auto &s : evaluate::CollectSymbols(e))
991             depth = std::max(analyze(s) + 1, depth);
992         }
993       };
994       // handle any symbols in array bound declarations
995       for (const auto &subs : details->shape()) {
996         doExplicit(subs.lbound());
997         doExplicit(subs.ubound());
998       }
999       // handle any symbols in coarray bound declarations
1000       for (const auto &subs : details->coshape()) {
1001         doExplicit(subs.lbound());
1002         doExplicit(subs.ubound());
1003       }
1004       // handle any symbols in initialization expressions
1005       if (auto e = details->init()) {
1006         // A PARAMETER may not be marked as implicitly SAVE, so set the flag.
1007         global = true;
1008         for (const auto &s : evaluate::CollectSymbols(*e))
1009           depth = std::max(analyze(s) + 1, depth);
1010       }
1011     }
1012     adjustSize(depth + 1);
1013     vars[depth].emplace_back(sym, global, depth);
1014     if (Fortran::semantics::IsAllocatable(sym))
1015       vars[depth].back().setHeapAlloc();
1016     if (Fortran::semantics::IsPointer(sym))
1017       vars[depth].back().setPointer();
1018     if (sym.attrs().test(Fortran::semantics::Attr::TARGET))
1019       vars[depth].back().setTarget();
1020     return depth;
1021   }
1022 
1023   // Save the final list of symbols as a single vector and free the rest.
1024   void finalize() {
1025     for (int i = 1, end = vars.size(); i < end; ++i)
1026       vars[0].insert(vars[0].end(), vars[i].begin(), vars[i].end());
1027     vars.resize(1);
1028   }
1029 
1030 private:
1031   // Make sure the table is of appropriate size.
1032   void adjustSize(std::size_t size) {
1033     if (vars.size() < size)
1034       vars.resize(size);
1035   }
1036 
1037   llvm::SmallSet<const semantics::Symbol *, 32> seen;
1038   std::vector<std::vector<lower::pft::Variable>> &vars;
1039 };
1040 } // namespace
1041 
1042 void Fortran::lower::pft::FunctionLikeUnit::processSymbolTable(
1043     const semantics::Scope &scope) {
1044   SymbolDependenceDepth sdd{varList};
1045   for (const auto &iter : scope)
1046     sdd.analyze(iter.second.get());
1047   sdd.finalize();
1048 }
1049 
1050 Fortran::lower::pft::FunctionLikeUnit::FunctionLikeUnit(
1051     const parser::MainProgram &func, const lower::pft::ParentVariant &parent,
1052     const semantics::SemanticsContext &semanticsContext)
1053     : ProgramUnit{func, parent}, endStmt{
1054                                      getFunctionStmt<parser::EndProgramStmt>(
1055                                          func)} {
1056   const auto &ps{
1057       std::get<std::optional<parser::Statement<parser::ProgramStmt>>>(func.t)};
1058   if (ps.has_value()) {
1059     beginStmt = ps.value();
1060     symbol = getSymbol(beginStmt);
1061     processSymbolTable(*symbol->scope());
1062   } else {
1063     processSymbolTable(semanticsContext.FindScope(
1064         std::get<parser::Statement<parser::EndProgramStmt>>(func.t).source));
1065   }
1066 }
1067 
1068 Fortran::lower::pft::FunctionLikeUnit::FunctionLikeUnit(
1069     const parser::FunctionSubprogram &func,
1070     const lower::pft::ParentVariant &parent,
1071     const semantics::SemanticsContext &)
1072     : ProgramUnit{func, parent},
1073       beginStmt{getFunctionStmt<parser::FunctionStmt>(func)},
1074       endStmt{getFunctionStmt<parser::EndFunctionStmt>(func)}, symbol{getSymbol(
1075                                                                    beginStmt)} {
1076   processSymbolTable(*symbol->scope());
1077 }
1078 
1079 Fortran::lower::pft::FunctionLikeUnit::FunctionLikeUnit(
1080     const parser::SubroutineSubprogram &func,
1081     const lower::pft::ParentVariant &parent,
1082     const semantics::SemanticsContext &)
1083     : ProgramUnit{func, parent},
1084       beginStmt{getFunctionStmt<parser::SubroutineStmt>(func)},
1085       endStmt{getFunctionStmt<parser::EndSubroutineStmt>(func)},
1086       symbol{getSymbol(beginStmt)} {
1087   processSymbolTable(*symbol->scope());
1088 }
1089 
1090 Fortran::lower::pft::FunctionLikeUnit::FunctionLikeUnit(
1091     const parser::SeparateModuleSubprogram &func,
1092     const lower::pft::ParentVariant &parent,
1093     const semantics::SemanticsContext &)
1094     : ProgramUnit{func, parent},
1095       beginStmt{getFunctionStmt<parser::MpSubprogramStmt>(func)},
1096       endStmt{getFunctionStmt<parser::EndMpSubprogramStmt>(func)},
1097       symbol{getSymbol(beginStmt)} {
1098   processSymbolTable(*symbol->scope());
1099 }
1100 
1101 Fortran::lower::pft::ModuleLikeUnit::ModuleLikeUnit(
1102     const parser::Module &m, const lower::pft::ParentVariant &parent)
1103     : ProgramUnit{m, parent}, beginStmt{getModuleStmt<parser::ModuleStmt>(m)},
1104       endStmt{getModuleStmt<parser::EndModuleStmt>(m)} {}
1105 
1106 Fortran::lower::pft::ModuleLikeUnit::ModuleLikeUnit(
1107     const parser::Submodule &m, const lower::pft::ParentVariant &parent)
1108     : ProgramUnit{m, parent}, beginStmt{getModuleStmt<parser::SubmoduleStmt>(
1109                                   m)},
1110       endStmt{getModuleStmt<parser::EndSubmoduleStmt>(m)} {}
1111 
1112 Fortran::lower::pft::BlockDataUnit::BlockDataUnit(
1113     const parser::BlockData &bd, const lower::pft::ParentVariant &parent)
1114     : ProgramUnit{bd, parent} {}
1115 
1116 std::unique_ptr<lower::pft::Program>
1117 Fortran::lower::createPFT(const parser::Program &root,
1118                           const semantics::SemanticsContext &semanticsContext) {
1119   PFTBuilder walker(semanticsContext);
1120   Walk(root, walker);
1121   return walker.result();
1122 }
1123 
1124 void Fortran::lower::dumpPFT(llvm::raw_ostream &outputStream,
1125                              lower::pft::Program &pft) {
1126   PFTDumper{}.dumpPFT(outputStream, pft);
1127 }
1128 
1129 void Fortran::lower::pft::Program::dump() { dumpPFT(llvm::errs(), *this); }
1130