[flang][OpenMP]Add parsing support for MAP(MAPPER(name) ...) (#116274)
[llvm-project.git] / flang / lib / Semantics / canonicalize-omp.cpp
blob0481b3d41f501d9c62974ac23c1cbdbcd6f23690
1 //===-- lib/Semantics/canonicalize-omp.cpp --------------------------------===//
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 //===----------------------------------------------------------------------===//
9 #include "canonicalize-omp.h"
10 #include "flang/Parser/parse-tree-visitor.h"
12 // After Loop Canonicalization, rewrite OpenMP parse tree to make OpenMP
13 // Constructs more structured which provide explicit scopes for later
14 // structural checks and semantic analysis.
15 // 1. move structured DoConstruct and OmpEndLoopDirective into
16 // OpenMPLoopConstruct. Compilation will not proceed in case of errors
17 // after this pass.
18 // 2. Associate declarative OMP allocation directives with their
19 // respective executable allocation directive
20 // 3. TBD
21 namespace Fortran::semantics {
23 using namespace parser::literals;
25 class CanonicalizationOfOmp {
26 public:
27 template <typename T> bool Pre(T &) { return true; }
28 template <typename T> void Post(T &) {}
29 CanonicalizationOfOmp(parser::Messages &messages) : messages_{messages} {}
31 void Post(parser::Block &block) {
32 for (auto it{block.begin()}; it != block.end(); ++it) {
33 if (auto *ompCons{GetConstructIf<parser::OpenMPConstruct>(*it)}) {
34 // OpenMPLoopConstruct
35 if (auto *ompLoop{
36 std::get_if<parser::OpenMPLoopConstruct>(&ompCons->u)}) {
37 RewriteOpenMPLoopConstruct(*ompLoop, block, it);
39 } else if (auto *endDir{
40 GetConstructIf<parser::OmpEndLoopDirective>(*it)}) {
41 // Unmatched OmpEndLoopDirective
42 auto &dir{std::get<parser::OmpLoopDirective>(endDir->t)};
43 messages_.Say(dir.source,
44 "The %s directive must follow the DO loop associated with the "
45 "loop construct"_err_en_US,
46 parser::ToUpperCaseLetters(dir.source.ToString()));
48 } // Block list
51 void Post(parser::ExecutionPart &body) { RewriteOmpAllocations(body); }
53 private:
54 template <typename T> T *GetConstructIf(parser::ExecutionPartConstruct &x) {
55 if (auto *y{std::get_if<parser::ExecutableConstruct>(&x.u)}) {
56 if (auto *z{std::get_if<common::Indirection<T>>(&y->u)}) {
57 return &z->value();
60 return nullptr;
63 template <typename T> T *GetOmpIf(parser::ExecutionPartConstruct &x) {
64 if (auto *construct{GetConstructIf<parser::OpenMPConstruct>(x)}) {
65 if (auto *omp{std::get_if<T>(&construct->u)}) {
66 return omp;
69 return nullptr;
72 void RewriteOpenMPLoopConstruct(parser::OpenMPLoopConstruct &x,
73 parser::Block &block, parser::Block::iterator it) {
74 // Check the sequence of DoConstruct and OmpEndLoopDirective
75 // in the same iteration
77 // Original:
78 // ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct
79 // OmpBeginLoopDirective
80 // ExecutableConstruct -> DoConstruct
81 // ExecutableConstruct -> OmpEndLoopDirective (if available)
83 // After rewriting:
84 // ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct
85 // OmpBeginLoopDirective
86 // DoConstruct
87 // OmpEndLoopDirective (if available)
88 parser::Block::iterator nextIt;
89 auto &beginDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
90 auto &dir{std::get<parser::OmpLoopDirective>(beginDir.t)};
92 nextIt = it;
93 while (++nextIt != block.end()) {
94 // Ignore compiler directives.
95 if (GetConstructIf<parser::CompilerDirective>(*nextIt))
96 continue;
98 if (auto *doCons{GetConstructIf<parser::DoConstruct>(*nextIt)}) {
99 if (doCons->GetLoopControl()) {
100 // move DoConstruct
101 std::get<std::optional<parser::DoConstruct>>(x.t) =
102 std::move(*doCons);
103 nextIt = block.erase(nextIt);
104 // try to match OmpEndLoopDirective
105 if (nextIt != block.end()) {
106 if (auto *endDir{
107 GetConstructIf<parser::OmpEndLoopDirective>(*nextIt)}) {
108 std::get<std::optional<parser::OmpEndLoopDirective>>(x.t) =
109 std::move(*endDir);
110 block.erase(nextIt);
113 } else {
114 messages_.Say(dir.source,
115 "DO loop after the %s directive must have loop control"_err_en_US,
116 parser::ToUpperCaseLetters(dir.source.ToString()));
118 } else {
119 messages_.Say(dir.source,
120 "A DO loop must follow the %s directive"_err_en_US,
121 parser::ToUpperCaseLetters(dir.source.ToString()));
123 // If we get here, we either found a loop, or issued an error message.
124 return;
128 void RewriteOmpAllocations(parser::ExecutionPart &body) {
129 // Rewrite leading declarative allocations so they are nested
130 // within their respective executable allocate directive
132 // Original:
133 // ExecutionPartConstruct -> OpenMPDeclarativeAllocate
134 // ExecutionPartConstruct -> OpenMPDeclarativeAllocate
135 // ExecutionPartConstruct -> OpenMPExecutableAllocate
137 // After rewriting:
138 // ExecutionPartConstruct -> OpenMPExecutableAllocate
139 // ExecutionPartConstruct -> OpenMPDeclarativeAllocate
140 // ExecutionPartConstruct -> OpenMPDeclarativeAllocate
141 for (auto it = body.v.rbegin(); it != body.v.rend();) {
142 if (auto *exec = GetOmpIf<parser::OpenMPExecutableAllocate>(*(it++))) {
143 parser::OpenMPDeclarativeAllocate *decl;
144 std::list<parser::OpenMPDeclarativeAllocate> subAllocates;
145 while (it != body.v.rend() &&
146 (decl = GetOmpIf<parser::OpenMPDeclarativeAllocate>(*it))) {
147 subAllocates.push_front(std::move(*decl));
148 it = decltype(it)(body.v.erase(std::next(it).base()));
150 if (!subAllocates.empty()) {
151 std::get<std::optional<std::list<parser::OpenMPDeclarativeAllocate>>>(
152 exec->t) = {std::move(subAllocates)};
158 parser::Messages &messages_;
161 bool CanonicalizeOmp(parser::Messages &messages, parser::Program &program) {
162 CanonicalizationOfOmp omp{messages};
163 Walk(program, omp);
164 return !messages.AnyFatalError();
166 } // namespace Fortran::semantics