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