Warning: This file is not a C or C++ file. It does not have highlighting.

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

Warning: This file is not a C or C++ file. It does not have highlighting.

source code of flang/include/flang/Lower/PFTBuilder.h