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     parallelAndKernelsOnlyAllowedAfterDeviceTypeClauses{
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 serialOnlyAllowedAfterDeviceTypeClauses{
32     llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait};
33 
34 static constexpr inline AccClauseSet loopOnlyAllowedAfterDeviceTypeClauses{
35     llvm::acc::Clause::ACCC_auto, llvm::acc::Clause::ACCC_collapse,
36     llvm::acc::Clause::ACCC_independent, llvm::acc::Clause::ACCC_gang,
37     llvm::acc::Clause::ACCC_seq, llvm::acc::Clause::ACCC_tile,
38     llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker};
39 
40 static constexpr inline AccClauseSet updateOnlyAllowedAfterDeviceTypeClauses{
41     llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait};
42 
43 static constexpr inline AccClauseSet routineOnlyAllowedAfterDeviceTypeClauses{
44     llvm::acc::Clause::ACCC_bind, llvm::acc::Clause::ACCC_gang,
45     llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker};
46 
47 bool AccStructureChecker::CheckAllowedModifier(llvm::acc::Clause clause) {
48   if (GetContext().directive == llvm::acc::ACCD_enter_data ||
49       GetContext().directive == llvm::acc::ACCD_exit_data) {
50     context_.Say(GetContext().clauseSource,
51         "Modifier is not allowed for the %s clause "
52         "on the %s directive"_err_en_US,
53         parser::ToUpperCaseLetters(getClauseName(clause).str()),
54         ContextDirectiveAsFortran());
55     return true;
56   }
57   return false;
58 }
59 
60 void AccStructureChecker::Enter(const parser::AccClause &x) {
61   SetContextClause(x);
62 }
63 
64 void AccStructureChecker::Leave(const parser::AccClauseList &) {}
65 
66 void AccStructureChecker::Enter(const parser::OpenACCBlockConstruct &x) {
67   const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
68   const auto &endBlockDir{std::get<parser::AccEndBlockDirective>(x.t)};
69   const auto &beginAccBlockDir{
70       std::get<parser::AccBlockDirective>(beginBlockDir.t)};
71 
72   CheckMatching(beginAccBlockDir, endBlockDir.v);
73   PushContextAndClauseSets(beginAccBlockDir.source, beginAccBlockDir.v);
74 }
75 
76 void AccStructureChecker::Leave(const parser::OpenACCBlockConstruct &x) {
77   const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
78   const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)};
79   const parser::Block &block{std::get<parser::Block>(x.t)};
80   switch (blockDir.v) {
81   case llvm::acc::Directive::ACCD_kernels:
82   case llvm::acc::Directive::ACCD_parallel:
83     // Restriction - 880-881 (KERNELS)
84     // Restriction - 843-844 (PARALLEL)
85     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
86         parallelAndKernelsOnlyAllowedAfterDeviceTypeClauses);
87     // Restriction - 877 (KERNELS)
88     // Restriction - 840 (PARALLEL)
89     CheckNoBranching(block, GetContext().directive, blockDir.source);
90     break;
91   case llvm::acc::Directive::ACCD_serial:
92     // Restriction - 919
93     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
94         serialOnlyAllowedAfterDeviceTypeClauses);
95     // Restriction - 916
96     CheckNoBranching(block, llvm::acc::Directive::ACCD_serial, blockDir.source);
97     break;
98   case llvm::acc::Directive::ACCD_data:
99     // Restriction - 1117-1118
100     CheckRequireAtLeastOneOf();
101     break;
102   case llvm::acc::Directive::ACCD_host_data:
103     // Restriction - 1578
104     CheckRequireAtLeastOneOf();
105     break;
106   default:
107     break;
108   }
109   dirContext_.pop_back();
110 }
111 
112 void AccStructureChecker::Enter(
113     const parser::OpenACCStandaloneDeclarativeConstruct &x) {
114   const auto &declarativeDir{std::get<parser::AccDeclarativeDirective>(x.t)};
115   PushContextAndClauseSets(declarativeDir.source, declarativeDir.v);
116 }
117 
118 void AccStructureChecker::Leave(
119     const parser::OpenACCStandaloneDeclarativeConstruct &) {
120   // Restriction - 2075
121   CheckAtLeastOneClause();
122   dirContext_.pop_back();
123 }
124 
125 void AccStructureChecker::Enter(const parser::OpenACCCombinedConstruct &x) {
126   const auto &beginCombinedDir{
127       std::get<parser::AccBeginCombinedDirective>(x.t)};
128   const auto &combinedDir{
129       std::get<parser::AccCombinedDirective>(beginCombinedDir.t)};
130 
131   // check matching, End directive is optional
132   if (const auto &endCombinedDir{
133           std::get<std::optional<parser::AccEndCombinedDirective>>(x.t)}) {
134     CheckMatching<parser::AccCombinedDirective>(combinedDir, endCombinedDir->v);
135   }
136 
137   PushContextAndClauseSets(combinedDir.source, combinedDir.v);
138 }
139 
140 void AccStructureChecker::Leave(const parser::OpenACCCombinedConstruct &x) {
141   const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
142   const auto &combinedDir{
143       std::get<parser::AccCombinedDirective>(beginBlockDir.t)};
144   switch (combinedDir.v) {
145   case llvm::acc::Directive::ACCD_kernels_loop:
146   case llvm::acc::Directive::ACCD_parallel_loop:
147     // Restriction - 1962 -> (880-881) (KERNELS LOOP)
148     // Restriction - 1962 -> (843-844) (PARALLEL LOOP)
149     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
150         {llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait,
151             llvm::acc::Clause::ACCC_num_gangs,
152             llvm::acc::Clause::ACCC_num_workers,
153             llvm::acc::Clause::ACCC_vector_length});
154     break;
155   case llvm::acc::Directive::ACCD_serial_loop:
156     // Restriction - 1962 -> (919) (SERIAL LOOP)
157     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
158         {llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait});
159     break;
160   default:
161     break;
162   }
163   dirContext_.pop_back();
164 }
165 
166 void AccStructureChecker::Enter(const parser::OpenACCLoopConstruct &x) {
167   const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
168   const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
169   PushContextAndClauseSets(loopDir.source, loopDir.v);
170 }
171 
172 void AccStructureChecker::Leave(const parser::OpenACCLoopConstruct &x) {
173   const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
174   const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
175   if (loopDir.v == llvm::acc::Directive::ACCD_loop) {
176     // Restriction - 1615-1616
177     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
178         loopOnlyAllowedAfterDeviceTypeClauses);
179     // Restriction - 1622
180     CheckNotAllowedIfClause(llvm::acc::Clause::ACCC_seq,
181         {llvm::acc::Clause::ACCC_gang, llvm::acc::Clause::ACCC_vector,
182             llvm::acc::Clause::ACCC_worker});
183   }
184   dirContext_.pop_back();
185 }
186 
187 void AccStructureChecker::Enter(const parser::OpenACCStandaloneConstruct &x) {
188   const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
189   PushContextAndClauseSets(standaloneDir.source, standaloneDir.v);
190 }
191 
192 void AccStructureChecker::Leave(const parser::OpenACCStandaloneConstruct &x) {
193   const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
194   switch (standaloneDir.v) {
195   case llvm::acc::Directive::ACCD_enter_data:
196   case llvm::acc::Directive::ACCD_exit_data:
197   case llvm::acc::Directive::ACCD_set:
198     // Restriction - 1117-1118 (ENTER DATA)
199     // Restriction - 1161-1162 (EXIT DATA)
200     // Restriction - 2254 (SET)
201     CheckRequireAtLeastOneOf();
202     break;
203   case llvm::acc::Directive::ACCD_update:
204     // Restriction - 2301
205     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
206         updateOnlyAllowedAfterDeviceTypeClauses);
207     break;
208   default:
209     break;
210   }
211   dirContext_.pop_back();
212 }
213 
214 void AccStructureChecker::Enter(const parser::OpenACCRoutineConstruct &x) {
215   PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_routine);
216 }
217 void AccStructureChecker::Leave(const parser::OpenACCRoutineConstruct &) {
218   // Restriction - 2409
219   CheckRequireAtLeastOneOf();
220   // Restriction - 2407-2408
221   CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
222       routineOnlyAllowedAfterDeviceTypeClauses);
223   dirContext_.pop_back();
224 }
225 
226 void AccStructureChecker::Enter(const parser::OpenACCWaitConstruct &x) {
227   const auto &verbatim{std::get<parser::Verbatim>(x.t)};
228   PushContextAndClauseSets(verbatim.source, llvm::acc::Directive::ACCD_wait);
229 }
230 void AccStructureChecker::Leave(const parser::OpenACCWaitConstruct &x) {
231   dirContext_.pop_back();
232 }
233 
234 void AccStructureChecker::Enter(const parser::OpenACCAtomicConstruct &x) {
235   PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_atomic);
236 }
237 void AccStructureChecker::Leave(const parser::OpenACCAtomicConstruct &x) {
238   dirContext_.pop_back();
239 }
240 
241 void AccStructureChecker::Enter(const parser::OpenACCCacheConstruct &x) {
242   const auto &verbatim = std::get<parser::Verbatim>(x.t);
243   PushContextAndClauseSets(verbatim.source, llvm::acc::Directive::ACCD_cache);
244   SetContextDirectiveSource(verbatim.source);
245 }
246 void AccStructureChecker::Leave(const parser::OpenACCCacheConstruct &x) {
247   dirContext_.pop_back();
248 }
249 
250 // Clause checkers
251 CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(Collapse, ACCC_collapse)
252 
253 CHECK_SIMPLE_CLAUSE(Auto, ACCC_auto)
254 CHECK_SIMPLE_CLAUSE(Async, ACCC_async)
255 CHECK_SIMPLE_CLAUSE(Attach, ACCC_attach)
256 CHECK_SIMPLE_CLAUSE(Bind, ACCC_bind)
257 CHECK_SIMPLE_CLAUSE(Capture, ACCC_capture)
258 CHECK_SIMPLE_CLAUSE(Copy, ACCC_copy)
259 CHECK_SIMPLE_CLAUSE(Default, ACCC_default)
260 CHECK_SIMPLE_CLAUSE(DefaultAsync, ACCC_default_async)
261 CHECK_SIMPLE_CLAUSE(Delete, ACCC_delete)
262 CHECK_SIMPLE_CLAUSE(Detach, ACCC_detach)
263 CHECK_SIMPLE_CLAUSE(Device, ACCC_device)
264 CHECK_SIMPLE_CLAUSE(DeviceNum, ACCC_device_num)
265 CHECK_SIMPLE_CLAUSE(Deviceptr, ACCC_deviceptr)
266 CHECK_SIMPLE_CLAUSE(DeviceResident, ACCC_device_resident)
267 CHECK_SIMPLE_CLAUSE(DeviceType, ACCC_device_type)
268 CHECK_SIMPLE_CLAUSE(Finalize, ACCC_finalize)
269 CHECK_SIMPLE_CLAUSE(Firstprivate, ACCC_firstprivate)
270 CHECK_SIMPLE_CLAUSE(Gang, ACCC_gang)
271 CHECK_SIMPLE_CLAUSE(Host, ACCC_host)
272 CHECK_SIMPLE_CLAUSE(If, ACCC_if)
273 CHECK_SIMPLE_CLAUSE(IfPresent, ACCC_if_present)
274 CHECK_SIMPLE_CLAUSE(Independent, ACCC_independent)
275 CHECK_SIMPLE_CLAUSE(Link, ACCC_link)
276 CHECK_SIMPLE_CLAUSE(NoCreate, ACCC_no_create)
277 CHECK_SIMPLE_CLAUSE(Nohost, ACCC_nohost)
278 CHECK_SIMPLE_CLAUSE(NumGangs, ACCC_num_gangs)
279 CHECK_SIMPLE_CLAUSE(NumWorkers, ACCC_num_workers)
280 CHECK_SIMPLE_CLAUSE(Present, ACCC_present)
281 CHECK_SIMPLE_CLAUSE(Private, ACCC_private)
282 CHECK_SIMPLE_CLAUSE(Read, ACCC_read)
283 CHECK_SIMPLE_CLAUSE(Reduction, ACCC_reduction)
284 CHECK_SIMPLE_CLAUSE(Self, ACCC_self)
285 CHECK_SIMPLE_CLAUSE(Seq, ACCC_seq)
286 CHECK_SIMPLE_CLAUSE(Tile, ACCC_tile)
287 CHECK_SIMPLE_CLAUSE(UseDevice, ACCC_use_device)
288 CHECK_SIMPLE_CLAUSE(Vector, ACCC_vector)
289 CHECK_SIMPLE_CLAUSE(VectorLength, ACCC_vector_length)
290 CHECK_SIMPLE_CLAUSE(Wait, ACCC_wait)
291 CHECK_SIMPLE_CLAUSE(Worker, ACCC_worker)
292 CHECK_SIMPLE_CLAUSE(Write, ACCC_write)
293 
294 void AccStructureChecker::Enter(const parser::AccClause::Create &c) {
295   CheckAllowed(llvm::acc::Clause::ACCC_create);
296   const auto &modifierClause{c.v};
297   if (const auto &modifier{
298           std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
299     if (modifier->v != parser::AccDataModifier::Modifier::Zero) {
300       context_.Say(GetContext().clauseSource,
301           "Only the ZERO modifier is allowed for the %s clause "
302           "on the %s directive"_err_en_US,
303           parser::ToUpperCaseLetters(
304               llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_create)
305                   .str()),
306           ContextDirectiveAsFortran());
307     }
308   }
309 }
310 
311 void AccStructureChecker::Enter(const parser::AccClause::Copyin &c) {
312   CheckAllowed(llvm::acc::Clause::ACCC_copyin);
313   const auto &modifierClause{c.v};
314   if (const auto &modifier{
315           std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
316     if (CheckAllowedModifier(llvm::acc::Clause::ACCC_copyin))
317       return;
318     if (modifier->v != parser::AccDataModifier::Modifier::ReadOnly) {
319       context_.Say(GetContext().clauseSource,
320           "Only the READONLY modifier is allowed for the %s clause "
321           "on the %s directive"_err_en_US,
322           parser::ToUpperCaseLetters(
323               llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyin)
324                   .str()),
325           ContextDirectiveAsFortran());
326     }
327   }
328 }
329 
330 void AccStructureChecker::Enter(const parser::AccClause::Copyout &c) {
331   CheckAllowed(llvm::acc::Clause::ACCC_copyout);
332   const auto &modifierClause{c.v};
333   if (const auto &modifier{
334           std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
335     if (CheckAllowedModifier(llvm::acc::Clause::ACCC_copyout))
336       return;
337     if (modifier->v != parser::AccDataModifier::Modifier::Zero) {
338       context_.Say(GetContext().clauseSource,
339           "Only the ZERO modifier is allowed for the %s clause "
340           "on the %s directive"_err_en_US,
341           parser::ToUpperCaseLetters(
342               llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyout)
343                   .str()),
344           ContextDirectiveAsFortran());
345     }
346   }
347 }
348 
349 llvm::StringRef AccStructureChecker::getClauseName(llvm::acc::Clause clause) {
350   return llvm::acc::getOpenACCClauseName(clause);
351 }
352 
353 llvm::StringRef AccStructureChecker::getDirectiveName(
354     llvm::acc::Directive directive) {
355   return llvm::acc::getOpenACCDirectiveName(directive);
356 }
357 
358 } // namespace Fortran::semantics
359