1 //===-- lib/Semantics/check-acc-structure.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 #include "check-acc-structure.h"
9 #include "flang/Parser/parse-tree.h"
10 #include "flang/Semantics/tools.h"
11
12 #define CHECK_SIMPLE_CLAUSE(X, Y) \
13 void AccStructureChecker::Enter(const parser::AccClause::X &) { \
14 CheckAllowed(llvm::acc::Clause::Y); \
15 }
16
17 #define CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(X, Y) \
18 void AccStructureChecker::Enter(const parser::AccClause::X &c) { \
19 CheckAllowed(llvm::acc::Clause::Y); \
20 RequiresConstantPositiveParameter(llvm::acc::Clause::Y, c.v); \
21 }
22
23 namespace Fortran::semantics {
24
25 static constexpr inline AccClauseSet
26 computeConstructOnlyAllowedAfterDeviceTypeClauses{
27 llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait,
28 llvm::acc::Clause::ACCC_num_gangs, llvm::acc::Clause::ACCC_num_workers,
29 llvm::acc::Clause::ACCC_vector_length};
30
31 static constexpr inline AccClauseSet loopOnlyAllowedAfterDeviceTypeClauses{
32 llvm::acc::Clause::ACCC_auto, llvm::acc::Clause::ACCC_collapse,
33 llvm::acc::Clause::ACCC_independent, llvm::acc::Clause::ACCC_gang,
34 llvm::acc::Clause::ACCC_seq, llvm::acc::Clause::ACCC_tile,
35 llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker};
36
37 static constexpr inline AccClauseSet updateOnlyAllowedAfterDeviceTypeClauses{
38 llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait};
39
40 static constexpr inline AccClauseSet routineOnlyAllowedAfterDeviceTypeClauses{
41 llvm::acc::Clause::ACCC_bind, llvm::acc::Clause::ACCC_gang,
42 llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker};
43
CheckAllowedModifier(llvm::acc::Clause clause)44 bool AccStructureChecker::CheckAllowedModifier(llvm::acc::Clause clause) {
45 if (GetContext().directive == llvm::acc::ACCD_enter_data ||
46 GetContext().directive == llvm::acc::ACCD_exit_data) {
47 context_.Say(GetContext().clauseSource,
48 "Modifier is not allowed for the %s clause "
49 "on the %s directive"_err_en_US,
50 parser::ToUpperCaseLetters(getClauseName(clause).str()),
51 ContextDirectiveAsFortran());
52 return true;
53 }
54 return false;
55 }
56
IsComputeConstruct(llvm::acc::Directive directive) const57 bool AccStructureChecker::IsComputeConstruct(
58 llvm::acc::Directive directive) const {
59 return directive == llvm::acc::ACCD_parallel ||
60 directive == llvm::acc::ACCD_parallel_loop ||
61 directive == llvm::acc::ACCD_serial ||
62 directive == llvm::acc::ACCD_serial_loop ||
63 directive == llvm::acc::ACCD_kernels ||
64 directive == llvm::acc::ACCD_kernels_loop;
65 }
66
IsInsideComputeConstruct() const67 bool AccStructureChecker::IsInsideComputeConstruct() const {
68 if (dirContext_.size() <= 1)
69 return false;
70
71 // Check all nested context skipping the first one.
72 for (std::size_t i = dirContext_.size() - 1; i > 0; --i) {
73 if (IsComputeConstruct(dirContext_[i - 1].directive))
74 return true;
75 }
76 return false;
77 }
78
CheckNotInComputeConstruct()79 void AccStructureChecker::CheckNotInComputeConstruct() {
80 if (IsInsideComputeConstruct())
81 context_.Say(GetContext().directiveSource,
82 "Directive %s may not be called within a compute region"_err_en_US,
83 ContextDirectiveAsFortran());
84 }
85
Enter(const parser::AccClause & x)86 void AccStructureChecker::Enter(const parser::AccClause &x) {
87 SetContextClause(x);
88 }
89
Leave(const parser::AccClauseList &)90 void AccStructureChecker::Leave(const parser::AccClauseList &) {}
91
Enter(const parser::OpenACCBlockConstruct & x)92 void AccStructureChecker::Enter(const parser::OpenACCBlockConstruct &x) {
93 const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
94 const auto &endBlockDir{std::get<parser::AccEndBlockDirective>(x.t)};
95 const auto &beginAccBlockDir{
96 std::get<parser::AccBlockDirective>(beginBlockDir.t)};
97
98 CheckMatching(beginAccBlockDir, endBlockDir.v);
99 PushContextAndClauseSets(beginAccBlockDir.source, beginAccBlockDir.v);
100 }
101
Leave(const parser::OpenACCBlockConstruct & x)102 void AccStructureChecker::Leave(const parser::OpenACCBlockConstruct &x) {
103 const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
104 const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)};
105 const parser::Block &block{std::get<parser::Block>(x.t)};
106 switch (blockDir.v) {
107 case llvm::acc::Directive::ACCD_kernels:
108 case llvm::acc::Directive::ACCD_parallel:
109 case llvm::acc::Directive::ACCD_serial:
110 // Restriction - line 1004-1005
111 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
112 computeConstructOnlyAllowedAfterDeviceTypeClauses);
113 // Restriction - line 1001
114 CheckNoBranching(block, GetContext().directive, blockDir.source);
115 break;
116 case llvm::acc::Directive::ACCD_data:
117 // Restriction - line 1249-1250
118 CheckRequireAtLeastOneOf();
119 break;
120 case llvm::acc::Directive::ACCD_host_data:
121 // Restriction - line 1746
122 CheckRequireAtLeastOneOf();
123 break;
124 default:
125 break;
126 }
127 dirContext_.pop_back();
128 }
129
Enter(const parser::OpenACCStandaloneDeclarativeConstruct & x)130 void AccStructureChecker::Enter(
131 const parser::OpenACCStandaloneDeclarativeConstruct &x) {
132 const auto &declarativeDir{std::get<parser::AccDeclarativeDirective>(x.t)};
133 PushContextAndClauseSets(declarativeDir.source, declarativeDir.v);
134 }
135
Leave(const parser::OpenACCStandaloneDeclarativeConstruct &)136 void AccStructureChecker::Leave(
137 const parser::OpenACCStandaloneDeclarativeConstruct &) {
138 // Restriction - line 2409
139 CheckAtLeastOneClause();
140 dirContext_.pop_back();
141 }
142
Enter(const parser::OpenACCCombinedConstruct & x)143 void AccStructureChecker::Enter(const parser::OpenACCCombinedConstruct &x) {
144 const auto &beginCombinedDir{
145 std::get<parser::AccBeginCombinedDirective>(x.t)};
146 const auto &combinedDir{
147 std::get<parser::AccCombinedDirective>(beginCombinedDir.t)};
148
149 // check matching, End directive is optional
150 if (const auto &endCombinedDir{
151 std::get<std::optional<parser::AccEndCombinedDirective>>(x.t)}) {
152 CheckMatching<parser::AccCombinedDirective>(combinedDir, endCombinedDir->v);
153 }
154
155 PushContextAndClauseSets(combinedDir.source, combinedDir.v);
156 }
157
Leave(const parser::OpenACCCombinedConstruct & x)158 void AccStructureChecker::Leave(const parser::OpenACCCombinedConstruct &x) {
159 const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
160 const auto &combinedDir{
161 std::get<parser::AccCombinedDirective>(beginBlockDir.t)};
162 switch (combinedDir.v) {
163 case llvm::acc::Directive::ACCD_kernels_loop:
164 case llvm::acc::Directive::ACCD_parallel_loop:
165 case llvm::acc::Directive::ACCD_serial_loop:
166 // Restriction - line 1004-1005
167 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
168 computeConstructOnlyAllowedAfterDeviceTypeClauses);
169 break;
170 default:
171 break;
172 }
173 dirContext_.pop_back();
174 }
175
Enter(const parser::OpenACCLoopConstruct & x)176 void AccStructureChecker::Enter(const parser::OpenACCLoopConstruct &x) {
177 const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
178 const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
179 PushContextAndClauseSets(loopDir.source, loopDir.v);
180 }
181
Leave(const parser::OpenACCLoopConstruct & x)182 void AccStructureChecker::Leave(const parser::OpenACCLoopConstruct &x) {
183 const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
184 const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
185 if (loopDir.v == llvm::acc::Directive::ACCD_loop) {
186 // Restriction - line 1818-1819
187 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
188 loopOnlyAllowedAfterDeviceTypeClauses);
189 // Restriction - line 1834
190 CheckNotAllowedIfClause(llvm::acc::Clause::ACCC_seq,
191 {llvm::acc::Clause::ACCC_gang, llvm::acc::Clause::ACCC_vector,
192 llvm::acc::Clause::ACCC_worker});
193 }
194 dirContext_.pop_back();
195 }
196
Enter(const parser::OpenACCStandaloneConstruct & x)197 void AccStructureChecker::Enter(const parser::OpenACCStandaloneConstruct &x) {
198 const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
199 PushContextAndClauseSets(standaloneDir.source, standaloneDir.v);
200 }
201
Leave(const parser::OpenACCStandaloneConstruct & x)202 void AccStructureChecker::Leave(const parser::OpenACCStandaloneConstruct &x) {
203 const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
204 switch (standaloneDir.v) {
205 case llvm::acc::Directive::ACCD_enter_data:
206 case llvm::acc::Directive::ACCD_exit_data:
207 // Restriction - line 1310-1311 (ENTER DATA)
208 // Restriction - line 1312-1313 (EXIT DATA)
209 CheckRequireAtLeastOneOf();
210 break;
211 case llvm::acc::Directive::ACCD_set:
212 // Restriction - line 2610
213 CheckRequireAtLeastOneOf();
214 // Restriction - line 2602
215 CheckNotInComputeConstruct();
216 break;
217 case llvm::acc::Directive::ACCD_update:
218 // Restriction - line 2636
219 CheckRequireAtLeastOneOf();
220 // Restriction - line 2669
221 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
222 updateOnlyAllowedAfterDeviceTypeClauses);
223 break;
224 case llvm::acc::Directive::ACCD_init:
225 case llvm::acc::Directive::ACCD_shutdown:
226 // Restriction - line 2525 (INIT)
227 // Restriction - line 2561 (SHUTDOWN)
228 CheckNotInComputeConstruct();
229 break;
230 default:
231 break;
232 }
233 dirContext_.pop_back();
234 }
235
Enter(const parser::OpenACCRoutineConstruct & x)236 void AccStructureChecker::Enter(const parser::OpenACCRoutineConstruct &x) {
237 PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_routine);
238 }
Leave(const parser::OpenACCRoutineConstruct &)239 void AccStructureChecker::Leave(const parser::OpenACCRoutineConstruct &) {
240 // Restriction - line 2790
241 CheckRequireAtLeastOneOf();
242 // Restriction - line 2788-2789
243 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
244 routineOnlyAllowedAfterDeviceTypeClauses);
245 dirContext_.pop_back();
246 }
247
Enter(const parser::OpenACCWaitConstruct & x)248 void AccStructureChecker::Enter(const parser::OpenACCWaitConstruct &x) {
249 const auto &verbatim{std::get<parser::Verbatim>(x.t)};
250 PushContextAndClauseSets(verbatim.source, llvm::acc::Directive::ACCD_wait);
251 }
Leave(const parser::OpenACCWaitConstruct & x)252 void AccStructureChecker::Leave(const parser::OpenACCWaitConstruct &x) {
253 dirContext_.pop_back();
254 }
255
Enter(const parser::OpenACCAtomicConstruct & x)256 void AccStructureChecker::Enter(const parser::OpenACCAtomicConstruct &x) {
257 PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_atomic);
258 }
Leave(const parser::OpenACCAtomicConstruct & x)259 void AccStructureChecker::Leave(const parser::OpenACCAtomicConstruct &x) {
260 dirContext_.pop_back();
261 }
262
Enter(const parser::OpenACCCacheConstruct & x)263 void AccStructureChecker::Enter(const parser::OpenACCCacheConstruct &x) {
264 const auto &verbatim = std::get<parser::Verbatim>(x.t);
265 PushContextAndClauseSets(verbatim.source, llvm::acc::Directive::ACCD_cache);
266 SetContextDirectiveSource(verbatim.source);
267 }
Leave(const parser::OpenACCCacheConstruct & x)268 void AccStructureChecker::Leave(const parser::OpenACCCacheConstruct &x) {
269 dirContext_.pop_back();
270 }
271
272 // Clause checkers
CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(Collapse,ACCC_collapse)273 CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(Collapse, ACCC_collapse)
274
275 CHECK_SIMPLE_CLAUSE(Auto, ACCC_auto)
276 CHECK_SIMPLE_CLAUSE(Async, ACCC_async)
277 CHECK_SIMPLE_CLAUSE(Attach, ACCC_attach)
278 CHECK_SIMPLE_CLAUSE(Bind, ACCC_bind)
279 CHECK_SIMPLE_CLAUSE(Capture, ACCC_capture)
280 CHECK_SIMPLE_CLAUSE(Copy, ACCC_copy)
281 CHECK_SIMPLE_CLAUSE(Default, ACCC_default)
282 CHECK_SIMPLE_CLAUSE(DefaultAsync, ACCC_default_async)
283 CHECK_SIMPLE_CLAUSE(Delete, ACCC_delete)
284 CHECK_SIMPLE_CLAUSE(Detach, ACCC_detach)
285 CHECK_SIMPLE_CLAUSE(Device, ACCC_device)
286 CHECK_SIMPLE_CLAUSE(DeviceNum, ACCC_device_num)
287 CHECK_SIMPLE_CLAUSE(Deviceptr, ACCC_deviceptr)
288 CHECK_SIMPLE_CLAUSE(DeviceResident, ACCC_device_resident)
289 CHECK_SIMPLE_CLAUSE(DeviceType, ACCC_device_type)
290 CHECK_SIMPLE_CLAUSE(Finalize, ACCC_finalize)
291 CHECK_SIMPLE_CLAUSE(Firstprivate, ACCC_firstprivate)
292 CHECK_SIMPLE_CLAUSE(Gang, ACCC_gang)
293 CHECK_SIMPLE_CLAUSE(Host, ACCC_host)
294 CHECK_SIMPLE_CLAUSE(If, ACCC_if)
295 CHECK_SIMPLE_CLAUSE(IfPresent, ACCC_if_present)
296 CHECK_SIMPLE_CLAUSE(Independent, ACCC_independent)
297 CHECK_SIMPLE_CLAUSE(Link, ACCC_link)
298 CHECK_SIMPLE_CLAUSE(NoCreate, ACCC_no_create)
299 CHECK_SIMPLE_CLAUSE(Nohost, ACCC_nohost)
300 CHECK_SIMPLE_CLAUSE(NumGangs, ACCC_num_gangs)
301 CHECK_SIMPLE_CLAUSE(NumWorkers, ACCC_num_workers)
302 CHECK_SIMPLE_CLAUSE(Present, ACCC_present)
303 CHECK_SIMPLE_CLAUSE(Private, ACCC_private)
304 CHECK_SIMPLE_CLAUSE(Read, ACCC_read)
305 CHECK_SIMPLE_CLAUSE(Reduction, ACCC_reduction)
306 CHECK_SIMPLE_CLAUSE(Seq, ACCC_seq)
307 CHECK_SIMPLE_CLAUSE(Tile, ACCC_tile)
308 CHECK_SIMPLE_CLAUSE(UseDevice, ACCC_use_device)
309 CHECK_SIMPLE_CLAUSE(Vector, ACCC_vector)
310 CHECK_SIMPLE_CLAUSE(VectorLength, ACCC_vector_length)
311 CHECK_SIMPLE_CLAUSE(Wait, ACCC_wait)
312 CHECK_SIMPLE_CLAUSE(Worker, ACCC_worker)
313 CHECK_SIMPLE_CLAUSE(Write, ACCC_write)
314
315 void AccStructureChecker::Enter(const parser::AccClause::Create &c) {
316 CheckAllowed(llvm::acc::Clause::ACCC_create);
317 const auto &modifierClause{c.v};
318 if (const auto &modifier{
319 std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
320 if (modifier->v != parser::AccDataModifier::Modifier::Zero) {
321 context_.Say(GetContext().clauseSource,
322 "Only the ZERO modifier is allowed for the %s clause "
323 "on the %s directive"_err_en_US,
324 parser::ToUpperCaseLetters(
325 llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_create)
326 .str()),
327 ContextDirectiveAsFortran());
328 }
329 }
330 }
331
Enter(const parser::AccClause::Copyin & c)332 void AccStructureChecker::Enter(const parser::AccClause::Copyin &c) {
333 CheckAllowed(llvm::acc::Clause::ACCC_copyin);
334 const auto &modifierClause{c.v};
335 if (const auto &modifier{
336 std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
337 if (CheckAllowedModifier(llvm::acc::Clause::ACCC_copyin))
338 return;
339 if (modifier->v != parser::AccDataModifier::Modifier::ReadOnly) {
340 context_.Say(GetContext().clauseSource,
341 "Only the READONLY modifier is allowed for the %s clause "
342 "on the %s directive"_err_en_US,
343 parser::ToUpperCaseLetters(
344 llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyin)
345 .str()),
346 ContextDirectiveAsFortran());
347 }
348 }
349 }
350
Enter(const parser::AccClause::Copyout & c)351 void AccStructureChecker::Enter(const parser::AccClause::Copyout &c) {
352 CheckAllowed(llvm::acc::Clause::ACCC_copyout);
353 const auto &modifierClause{c.v};
354 if (const auto &modifier{
355 std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
356 if (CheckAllowedModifier(llvm::acc::Clause::ACCC_copyout))
357 return;
358 if (modifier->v != parser::AccDataModifier::Modifier::Zero) {
359 context_.Say(GetContext().clauseSource,
360 "Only the ZERO modifier is allowed for the %s clause "
361 "on the %s directive"_err_en_US,
362 parser::ToUpperCaseLetters(
363 llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyout)
364 .str()),
365 ContextDirectiveAsFortran());
366 }
367 }
368 }
369
Enter(const parser::AccClause::Self & x)370 void AccStructureChecker::Enter(const parser::AccClause::Self &x) {
371 CheckAllowed(llvm::acc::Clause::ACCC_self);
372 const parser::AccSelfClause &accSelfClause = x.v;
373 if (GetContext().directive == llvm::acc::Directive::ACCD_update &&
374 std::holds_alternative<std::optional<parser::ScalarLogicalExpr>>(
375 accSelfClause.u)) {
376 context_.Say(GetContext().clauseSource,
377 "SELF clause on the %s directive must have a var-list"_err_en_US,
378 ContextDirectiveAsFortran());
379 } else if (GetContext().directive != llvm::acc::Directive::ACCD_update &&
380 std::holds_alternative<parser::AccObjectList>(accSelfClause.u)) {
381 const auto &accObjectList =
382 std::get<parser::AccObjectList>(accSelfClause.u);
383 if (accObjectList.v.size() != 1) {
384 context_.Say(GetContext().clauseSource,
385 "SELF clause on the %s directive only accepts optional scalar logical"
386 " expression"_err_en_US,
387 ContextDirectiveAsFortran());
388 }
389 }
390 }
391
getClauseName(llvm::acc::Clause clause)392 llvm::StringRef AccStructureChecker::getClauseName(llvm::acc::Clause clause) {
393 return llvm::acc::getOpenACCClauseName(clause);
394 }
395
getDirectiveName(llvm::acc::Directive directive)396 llvm::StringRef AccStructureChecker::getDirectiveName(
397 llvm::acc::Directive directive) {
398 return llvm::acc::getOpenACCDirectiveName(directive);
399 }
400
401 } // namespace Fortran::semantics
402