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