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 | |
32 | namespace Fortran::lower::pft { |
33 | |
34 | struct Evaluation; |
35 | struct Program; |
36 | struct ModuleLikeUnit; |
37 | struct FunctionLikeUnit; |
38 | |
39 | using EvaluationList = std::list<Evaluation>; |
40 | |
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. |
44 | template <bool isConst, typename... A> |
45 | class ReferenceVariantBase { |
46 | public: |
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 | |
81 | private: |
82 | std::variant<Ref<A>...> u; |
83 | }; |
84 | template <typename... A> |
85 | using ReferenceVariant = ReferenceVariantBase<true, A...>; |
86 | template <typename... A> |
87 | using 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. |
91 | using PftNode = MutableReferenceVariant<Program, ModuleLikeUnit, |
92 | FunctionLikeUnit, Evaluation>; |
93 | |
94 | /// Classify the parse-tree nodes from ExecutablePartConstruct |
95 | |
96 | using 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 | |
112 | using OtherStmts = std::tuple<parser::EntryStmt, parser::FormatStmt>; |
113 | |
114 | using ConstructStmts = std::tuple< |
115 | parser::AssociateStmt, parser::EndAssociateStmt, parser::BlockStmt, |
116 | parser::EndBlockStmt, parser::SelectCaseStmt, parser::CaseStmt, |
117 | parser::EndSelectStmt, parser::ChangeTeamStmt, parser::EndChangeTeamStmt, |
118 | parser::CriticalStmt, parser::EndCriticalStmt, parser::NonLabelDoStmt, |
119 | parser::EndDoStmt, parser::IfThenStmt, parser::ElseIfStmt, parser::ElseStmt, |
120 | parser::EndIfStmt, parser::SelectRankStmt, parser::SelectRankCaseStmt, |
121 | parser::SelectTypeStmt, parser::TypeGuardStmt, parser::WhereConstructStmt, |
122 | parser::MaskedElsewhereStmt, parser::ElsewhereStmt, parser::EndWhereStmt, |
123 | parser::ForallConstructStmt, parser::EndForallStmt>; |
124 | |
125 | using EndStmts = |
126 | std::tuple<parser::EndProgramStmt, parser::EndFunctionStmt, |
127 | parser::EndSubroutineStmt, parser::EndMpSubprogramStmt>; |
128 | |
129 | using Constructs = |
130 | std::tuple<parser::AssociateConstruct, parser::BlockConstruct, |
131 | parser::CaseConstruct, parser::ChangeTeamConstruct, |
132 | parser::CriticalConstruct, parser::DoConstruct, |
133 | parser::IfConstruct, parser::SelectRankConstruct, |
134 | parser::SelectTypeConstruct, parser::WhereConstruct, |
135 | parser::ForallConstruct>; |
136 | |
137 | using Directives = |
138 | std::tuple<parser::CompilerDirective, parser::OpenACCConstruct, |
139 | parser::OpenACCRoutineConstruct, |
140 | parser::OpenACCDeclarativeConstruct, parser::OpenMPConstruct, |
141 | parser::OpenMPDeclarativeConstruct, parser::OmpEndLoopDirective, |
142 | parser::CUFKernelDoConstruct>; |
143 | |
144 | using DeclConstructs = std::tuple<parser::OpenMPDeclarativeConstruct, |
145 | parser::OpenACCDeclarativeConstruct>; |
146 | |
147 | template <typename A> |
148 | static constexpr bool isActionStmt{common::HasMember<A, ActionStmts>}; |
149 | |
150 | template <typename A> |
151 | static constexpr bool isOtherStmt{common::HasMember<A, OtherStmts>}; |
152 | |
153 | template <typename A> |
154 | static constexpr bool isConstructStmt{common::HasMember<A, ConstructStmts>}; |
155 | |
156 | template <typename A> |
157 | static constexpr bool isEndStmt{common::HasMember<A, EndStmts>}; |
158 | |
159 | template <typename A> |
160 | static constexpr bool isConstruct{common::HasMember<A, Constructs>}; |
161 | |
162 | template <typename A> |
163 | static constexpr bool isDirective{common::HasMember<A, Directives>}; |
164 | |
165 | template <typename A> |
166 | static constexpr bool isDeclConstruct{common::HasMember<A, DeclConstructs>}; |
167 | |
168 | template <typename A> |
169 | static constexpr bool isIntermediateConstructStmt{common::HasMember< |
170 | A, std::tuple<parser::CaseStmt, parser::ElseIfStmt, parser::ElseStmt, |
171 | parser::SelectRankCaseStmt, parser::TypeGuardStmt>>}; |
172 | |
173 | template <typename A> |
174 | static constexpr bool isNopConstructStmt{common::HasMember< |
175 | A, std::tuple<parser::CaseStmt, parser::ElseIfStmt, parser::ElseStmt, |
176 | parser::EndIfStmt, parser::SelectRankCaseStmt, |
177 | parser::TypeGuardStmt>>}; |
178 | |
179 | template <typename A> |
180 | static constexpr bool isExecutableDirective{common::HasMember< |
181 | A, std::tuple<parser::CompilerDirective, parser::OpenACCConstruct, |
182 | parser::OpenMPConstruct, parser::CUFKernelDoConstruct>>}; |
183 | |
184 | template <typename A> |
185 | static constexpr bool isFunctionLike{common::HasMember< |
186 | A, std::tuple<parser::MainProgram, parser::FunctionSubprogram, |
187 | parser::SubroutineSubprogram, |
188 | parser::SeparateModuleSubprogram>>}; |
189 | |
190 | template <typename A> |
191 | struct MakeReferenceVariantHelper {}; |
192 | template <typename... A> |
193 | struct MakeReferenceVariantHelper<std::variant<A...>> { |
194 | using type = ReferenceVariant<A...>; |
195 | }; |
196 | template <typename... A> |
197 | struct MakeReferenceVariantHelper<std::tuple<A...>> { |
198 | using type = ReferenceVariant<A...>; |
199 | }; |
200 | template <typename A> |
201 | using MakeReferenceVariant = typename MakeReferenceVariantHelper<A>::type; |
202 | |
203 | using 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, ...>). |
209 | using 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. |
213 | struct 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 | |
362 | using 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. |
370 | struct 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. |
395 | struct 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 | |
574 | private: |
575 | std::variant<Nominal, AggregateStore> var; |
576 | }; |
577 | |
578 | using VariableList = std::vector<Variable>; |
579 | using 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. |
584 | const 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. |
589 | VariableList 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. |
593 | VariableList getDependentVariableList(const Fortran::semantics::Symbol &); |
594 | |
595 | void 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). |
599 | struct 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. |
725 | struct 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. |
755 | struct 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 |
767 | struct 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 |
776 | struct 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. |
786 | struct 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 | |
807 | private: |
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. |
815 | template <typename T> |
816 | static 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. |
821 | template <typename ParentType, typename A> |
822 | ParentType *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. |
831 | template <typename A> |
832 | ScopeVariableListMap &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. |
840 | void 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. |
845 | void visitAllSymbols(const Evaluation &eval, |
846 | std::function<void(const semantics::Symbol &)> callBack); |
847 | |
848 | } // namespace Fortran::lower::pft |
849 | |
850 | namespace 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. |
859 | std::unique_ptr<pft::Program> |
860 | createPFT(const parser::Program &root, |
861 | const Fortran::semantics::SemanticsContext &semanticsContext); |
862 | |
863 | /// Dumper for displaying a PFT. |
864 | void 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.