1 //===-- lib/Semantics/canonicalize-acc.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 //===----------------------------------------------------------------------===//
8
9 #include "canonicalize-acc.h"
10 #include "flang/Parser/parse-tree-visitor.h"
11 #include "flang/Semantics/tools.h"
12
13 // After Loop Canonicalization, rewrite OpenACC parse tree to make OpenACC
14 // Constructs more structured which provide explicit scopes for later
15 // structural checks and semantic analysis.
16 // 1. move structured DoConstruct into
17 // OpenACCLoopConstruct. Compilation will not proceed in case of errors
18 // after this pass.
19 // 2. move structured DoConstruct into OpenACCCombinedConstruct. Move
20 // AccEndCombinedConstruct into OpenACCCombinedConstruct if present.
21 // Compilation will not proceed in case of errors after this pass.
22 namespace Fortran::semantics {
23
24 using namespace parser::literals;
25
26 class CanonicalizationOfAcc {
27 public:
Pre(T &)28 template <typename T> bool Pre(T &) { return true; }
Post(T &)29 template <typename T> void Post(T &) {}
CanonicalizationOfAcc(parser::Messages & messages)30 CanonicalizationOfAcc(parser::Messages &messages) : messages_{messages} {}
31
Post(parser::Block & block)32 void Post(parser::Block &block) {
33 for (auto it{block.begin()}; it != block.end(); ++it) {
34 if (auto *accLoop{parser::Unwrap<parser::OpenACCLoopConstruct>(*it)}) {
35 RewriteOpenACCLoopConstruct(*accLoop, block, it);
36 } else if (auto *accCombined{
37 parser::Unwrap<parser::OpenACCCombinedConstruct>(*it)}) {
38 RewriteOpenACCCombinedConstruct(*accCombined, block, it);
39 } else if (auto *endDir{
40 parser::Unwrap<parser::AccEndCombinedDirective>(*it)}) {
41 // Unmatched AccEndCombinedDirective
42 messages_.Say(endDir->v.source,
43 "The %s directive must follow the DO loop associated with the "
44 "loop construct"_err_en_US,
45 parser::ToUpperCaseLetters(endDir->v.source.ToString()));
46 }
47 } // Block list
48 }
49
50 private:
51 // Check constraint in 2.9.7
52 // If there are n tile sizes in the list, the loop construct must be
53 // immediately followed by n tightly-nested loops.
54 template <typename C, typename D>
CheckTileClauseRestriction(const C & x)55 void CheckTileClauseRestriction(const C &x) {
56 const auto &beginLoopDirective = std::get<D>(x.t);
57 const auto &accClauseList =
58 std::get<parser::AccClauseList>(beginLoopDirective.t);
59 for (const auto &clause : accClauseList.v) {
60 if (const auto *tileClause =
61 std::get_if<parser::AccClause::Tile>(&clause.u)) {
62 const parser::AccTileExprList &tileExprList = tileClause->v;
63 const std::list<parser::AccTileExpr> &listTileExpr = tileExprList.v;
64 std::size_t tileArgNb = listTileExpr.size();
65
66 const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
67 if (outer->IsDoConcurrent())
68 return; // Tile is not allowed on DO CONURRENT
69 for (const parser::DoConstruct *loop{&*outer}; loop && tileArgNb > 0;
70 --tileArgNb) {
71 const auto &block{std::get<parser::Block>(loop->t)};
72 const auto it{block.begin()};
73 loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it)
74 : nullptr;
75 }
76
77 if (tileArgNb > 0) {
78 messages_.Say(beginLoopDirective.source,
79 "The loop construct with the TILE clause must be followed by %d "
80 "tightly-nested loops"_err_en_US,
81 listTileExpr.size());
82 }
83 }
84 }
85 }
86
87 // Check constraint on line 1835 in Section 2.9
88 // A tile and collapse clause may not appear on loop that is associated with
89 // do concurrent.
90 template <typename C, typename D>
CheckDoConcurrentClauseRestriction(const C & x)91 void CheckDoConcurrentClauseRestriction(const C &x) {
92 const auto &doCons{std::get<std::optional<parser::DoConstruct>>(x.t)};
93 if (!doCons->IsDoConcurrent())
94 return;
95 const auto &beginLoopDirective = std::get<D>(x.t);
96 const auto &accClauseList =
97 std::get<parser::AccClauseList>(beginLoopDirective.t);
98 for (const auto &clause : accClauseList.v) {
99 if (std::holds_alternative<parser::AccClause::Collapse>(clause.u) ||
100 std::holds_alternative<parser::AccClause::Tile>(clause.u)) {
101 messages_.Say(beginLoopDirective.source,
102 "TILE and COLLAPSE clause may not appear on loop construct "
103 "associated with DO CONCURRENT"_err_en_US);
104 }
105 }
106 }
107
RewriteOpenACCLoopConstruct(parser::OpenACCLoopConstruct & x,parser::Block & block,parser::Block::iterator it)108 void RewriteOpenACCLoopConstruct(parser::OpenACCLoopConstruct &x,
109 parser::Block &block, parser::Block::iterator it) {
110 // Check the sequence of DoConstruct in the same iteration
111 //
112 // Original:
113 // ExecutableConstruct -> OpenACCConstruct -> OpenACCLoopConstruct
114 // ACCBeginLoopDirective
115 // ExecutableConstruct -> DoConstruct
116 //
117 // After rewriting:
118 // ExecutableConstruct -> OpenACCConstruct -> OpenACCLoopConstruct
119 // AccBeginLoopDirective
120 // DoConstruct
121 parser::Block::iterator nextIt;
122 auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
123 auto &dir{std::get<parser::AccLoopDirective>(beginDir.t)};
124
125 nextIt = it;
126 if (++nextIt != block.end()) {
127 if (auto *doCons{parser::Unwrap<parser::DoConstruct>(*nextIt)}) {
128 if (doCons->GetLoopControl()) {
129 // move DoConstruct
130 std::get<std::optional<parser::DoConstruct>>(x.t) =
131 std::move(*doCons);
132 nextIt = block.erase(nextIt);
133 } else {
134 messages_.Say(dir.source,
135 "DO loop after the %s directive must have loop control"_err_en_US,
136 parser::ToUpperCaseLetters(dir.source.ToString()));
137 }
138
139 CheckDoConcurrentClauseRestriction<parser::OpenACCLoopConstruct,
140 parser::AccBeginLoopDirective>(x);
141 CheckTileClauseRestriction<parser::OpenACCLoopConstruct,
142 parser::AccBeginLoopDirective>(x);
143
144 return; // found do-loop
145 }
146 }
147 messages_.Say(dir.source,
148 "A DO loop must follow the %s directive"_err_en_US,
149 parser::ToUpperCaseLetters(dir.source.ToString()));
150 }
151
RewriteOpenACCCombinedConstruct(parser::OpenACCCombinedConstruct & x,parser::Block & block,parser::Block::iterator it)152 void RewriteOpenACCCombinedConstruct(parser::OpenACCCombinedConstruct &x,
153 parser::Block &block, parser::Block::iterator it) {
154 // Check the sequence of DoConstruct in the same iteration
155 //
156 // Original:
157 // ExecutableConstruct -> OpenACCConstruct -> OpenACCCombinedConstruct
158 // ACCBeginCombinedDirective
159 // ExecutableConstruct -> DoConstruct
160 // ExecutableConstruct -> AccEndCombinedDirective (if available)
161 //
162 // After rewriting:
163 // ExecutableConstruct -> OpenACCConstruct -> OpenACCCombinedConstruct
164 // ACCBeginCombinedDirective
165 // DoConstruct
166 // AccEndCombinedDirective (if available)
167 parser::Block::iterator nextIt;
168 auto &beginDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
169 auto &dir{std::get<parser::AccCombinedDirective>(beginDir.t)};
170
171 nextIt = it;
172 if (++nextIt != block.end()) {
173 if (auto *doCons{parser::Unwrap<parser::DoConstruct>(*nextIt)}) {
174 if (doCons->GetLoopControl()) {
175 // move DoConstruct
176 std::get<std::optional<parser::DoConstruct>>(x.t) =
177 std::move(*doCons);
178 nextIt = block.erase(nextIt);
179 // try to match AccEndCombinedDirective
180 if (nextIt != block.end()) {
181 if (auto *endDir{
182 parser::Unwrap<parser::AccEndCombinedDirective>(*nextIt)}) {
183 std::get<std::optional<parser::AccEndCombinedDirective>>(x.t) =
184 std::move(*endDir);
185 block.erase(nextIt);
186 }
187 }
188 } else {
189 messages_.Say(dir.source,
190 "DO loop after the %s directive must have loop control"_err_en_US,
191 parser::ToUpperCaseLetters(dir.source.ToString()));
192 }
193
194 CheckDoConcurrentClauseRestriction<parser::OpenACCCombinedConstruct,
195 parser::AccBeginCombinedDirective>(x);
196 CheckTileClauseRestriction<parser::OpenACCCombinedConstruct,
197 parser::AccBeginCombinedDirective>(x);
198
199 return; // found do-loop
200 }
201 }
202 messages_.Say(dir.source,
203 "A DO loop must follow the %s directive"_err_en_US,
204 parser::ToUpperCaseLetters(dir.source.ToString()));
205 }
206
207 parser::Messages &messages_;
208 };
209
CanonicalizeAcc(parser::Messages & messages,parser::Program & program)210 bool CanonicalizeAcc(parser::Messages &messages, parser::Program &program) {
211 CanonicalizationOfAcc acc{messages};
212 Walk(program, acc);
213 return !messages.AnyFatalError();
214 }
215 } // namespace Fortran::semantics
216