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 class NoBranchingEnforce {
48 public:
49   NoBranchingEnforce(SemanticsContext &context,
50       parser::CharBlock sourcePosition, llvm::acc::Directive directive)
51       : context_{context}, sourcePosition_{sourcePosition}, currentDirective_{
52                                                                 directive} {}
53   template <typename T> bool Pre(const T &) { return true; }
54   template <typename T> void Post(const T &) {}
55 
56   template <typename T> bool Pre(const parser::Statement<T> &statement) {
57     currentStatementSourcePosition_ = statement.source;
58     return true;
59   }
60 
61   void Post(const parser::ReturnStmt &) { EmitBranchOutError("RETURN"); }
62   void Post(const parser::ExitStmt &exitStmt) {
63     if (const auto &exitName{exitStmt.v}) {
64       CheckConstructNameBranching("EXIT", exitName.value());
65     }
66   }
67   void Post(const parser::StopStmt &) { EmitBranchOutError("STOP"); }
68 
69 private:
70   parser::MessageFormattedText GetEnclosingMsg() const {
71     return {"Enclosing %s construct"_en_US,
72         parser::ToUpperCaseLetters(
73             llvm::acc::getOpenACCDirectiveName(currentDirective_).str())};
74   }
75 
76   void EmitBranchOutError(const char *stmt) const {
77     context_
78         .Say(currentStatementSourcePosition_,
79             "%s statement is not allowed in a %s construct"_err_en_US, stmt,
80             parser::ToUpperCaseLetters(
81                 llvm::acc::getOpenACCDirectiveName(currentDirective_).str()))
82         .Attach(sourcePosition_, GetEnclosingMsg());
83   }
84 
85   void EmitBranchOutErrorWithName(
86       const char *stmt, const parser::Name &toName) const {
87     const std::string branchingToName{toName.ToString()};
88     const auto upperCaseConstructName{parser::ToUpperCaseLetters(
89         llvm::acc::getOpenACCDirectiveName(currentDirective_).str())};
90     context_
91         .Say(currentStatementSourcePosition_,
92             "%s to construct '%s' outside of %s construct is not allowed"_err_en_US,
93             stmt, branchingToName, upperCaseConstructName)
94         .Attach(sourcePosition_, GetEnclosingMsg());
95   }
96 
97   // Current semantic checker is not following OpenACC constructs as they are
98   // not Fortran constructs. Hence the ConstructStack doesn't capture OpenACC
99   // constructs. Apply an inverse way to figure out if a construct-name is
100   // branching out of an OpenACC construct. The control flow goes out of an
101   // OpenACC construct, if a construct-name from statement is found in
102   // ConstructStack.
103   void CheckConstructNameBranching(
104       const char *stmt, const parser::Name &stmtName) {
105     const ConstructStack &stack{context_.constructStack()};
106     for (auto iter{stack.cend()}; iter-- != stack.cbegin();) {
107       const ConstructNode &construct{*iter};
108       const auto &constructName{MaybeGetNodeName(construct)};
109       if (constructName) {
110         if (stmtName.source == constructName->source) {
111           EmitBranchOutErrorWithName(stmt, stmtName);
112           return;
113         }
114       }
115     }
116   }
117 
118   SemanticsContext &context_;
119   parser::CharBlock currentStatementSourcePosition_;
120   parser::CharBlock sourcePosition_;
121   llvm::acc::Directive currentDirective_;
122 };
123 
124 void AccStructureChecker::Enter(const parser::AccClause &x) {
125   SetContextClause(x);
126 }
127 
128 void AccStructureChecker::Leave(const parser::AccClauseList &) {}
129 
130 void AccStructureChecker::Enter(const parser::OpenACCBlockConstruct &x) {
131   const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
132   const auto &endBlockDir{std::get<parser::AccEndBlockDirective>(x.t)};
133   const auto &beginAccBlockDir{
134       std::get<parser::AccBlockDirective>(beginBlockDir.t)};
135 
136   CheckMatching(beginAccBlockDir, endBlockDir.v);
137   PushContextAndClauseSets(beginAccBlockDir.source, beginAccBlockDir.v);
138 }
139 
140 void AccStructureChecker::Leave(const parser::OpenACCBlockConstruct &x) {
141   const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
142   const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)};
143   const parser::Block &block{std::get<parser::Block>(x.t)};
144   switch (blockDir.v) {
145   case llvm::acc::Directive::ACCD_kernels:
146   case llvm::acc::Directive::ACCD_parallel:
147     // Restriction - 880-881 (KERNELS)
148     // Restriction - 843-844 (PARALLEL)
149     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
150         parallelAndKernelsOnlyAllowedAfterDeviceTypeClauses);
151     // Restriction - 877 (KERNELS)
152     // Restriction - 840 (PARALLEL)
153     CheckNoBranching(block, GetContext().directive, blockDir.source);
154     break;
155   case llvm::acc::Directive::ACCD_serial:
156     // Restriction - 919
157     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
158         serialOnlyAllowedAfterDeviceTypeClauses);
159     // Restriction - 916
160     CheckNoBranching(block, llvm::acc::Directive::ACCD_serial, blockDir.source);
161     break;
162   case llvm::acc::Directive::ACCD_data:
163     // Restriction - 1117-1118
164     CheckRequireAtLeastOneOf();
165     break;
166   case llvm::acc::Directive::ACCD_host_data:
167     // Restriction - 1578
168     CheckRequireAtLeastOneOf();
169     break;
170   default:
171     break;
172   }
173   dirContext_.pop_back();
174 }
175 
176 void AccStructureChecker::CheckNoBranching(const parser::Block &block,
177     const llvm::acc::Directive directive,
178     const parser::CharBlock &directiveSource) const {
179   NoBranchingEnforce noBranchingEnforce{context_, directiveSource, directive};
180   parser::Walk(block, noBranchingEnforce);
181 }
182 
183 void AccStructureChecker::Enter(
184     const parser::OpenACCStandaloneDeclarativeConstruct &x) {
185   const auto &declarativeDir{std::get<parser::AccDeclarativeDirective>(x.t)};
186   PushContextAndClauseSets(declarativeDir.source, declarativeDir.v);
187 }
188 
189 void AccStructureChecker::Leave(
190     const parser::OpenACCStandaloneDeclarativeConstruct &) {
191   // Restriction - 2075
192   CheckAtLeastOneClause();
193   dirContext_.pop_back();
194 }
195 
196 void AccStructureChecker::Enter(const parser::OpenACCCombinedConstruct &x) {
197   const auto &beginCombinedDir{
198       std::get<parser::AccBeginCombinedDirective>(x.t)};
199   const auto &combinedDir{
200       std::get<parser::AccCombinedDirective>(beginCombinedDir.t)};
201 
202   // check matching, End directive is optional
203   if (const auto &endCombinedDir{
204           std::get<std::optional<parser::AccEndCombinedDirective>>(x.t)}) {
205     CheckMatching<parser::AccCombinedDirective>(combinedDir, endCombinedDir->v);
206   }
207 
208   PushContextAndClauseSets(combinedDir.source, combinedDir.v);
209 }
210 
211 void AccStructureChecker::Leave(const parser::OpenACCCombinedConstruct &x) {
212   const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
213   const auto &combinedDir{
214       std::get<parser::AccCombinedDirective>(beginBlockDir.t)};
215   switch (combinedDir.v) {
216   case llvm::acc::Directive::ACCD_kernels_loop:
217   case llvm::acc::Directive::ACCD_parallel_loop:
218     // Restriction - 1962 -> (880-881) (KERNELS LOOP)
219     // Restriction - 1962 -> (843-844) (PARALLEL LOOP)
220     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
221         {llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait,
222             llvm::acc::Clause::ACCC_num_gangs,
223             llvm::acc::Clause::ACCC_num_workers,
224             llvm::acc::Clause::ACCC_vector_length});
225     break;
226   case llvm::acc::Directive::ACCD_serial_loop:
227     // Restriction - 1962 -> (919) (SERIAL LOOP)
228     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
229         {llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait});
230     break;
231   default:
232     break;
233   }
234   dirContext_.pop_back();
235 }
236 
237 void AccStructureChecker::Enter(const parser::OpenACCLoopConstruct &x) {
238   const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
239   const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
240   PushContextAndClauseSets(loopDir.source, loopDir.v);
241 }
242 
243 void AccStructureChecker::Leave(const parser::OpenACCLoopConstruct &x) {
244   const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
245   const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
246   if (loopDir.v == llvm::acc::Directive::ACCD_loop) {
247     // Restriction - 1615-1616
248     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
249         loopOnlyAllowedAfterDeviceTypeClauses);
250     // Restriction - 1622
251     CheckNotAllowedIfClause(llvm::acc::Clause::ACCC_seq,
252         {llvm::acc::Clause::ACCC_gang, llvm::acc::Clause::ACCC_vector,
253             llvm::acc::Clause::ACCC_worker});
254   }
255   dirContext_.pop_back();
256 }
257 
258 void AccStructureChecker::Enter(const parser::OpenACCStandaloneConstruct &x) {
259   const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
260   PushContextAndClauseSets(standaloneDir.source, standaloneDir.v);
261 }
262 
263 void AccStructureChecker::Leave(const parser::OpenACCStandaloneConstruct &x) {
264   const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
265   switch (standaloneDir.v) {
266   case llvm::acc::Directive::ACCD_enter_data:
267   case llvm::acc::Directive::ACCD_exit_data:
268   case llvm::acc::Directive::ACCD_set:
269     // Restriction - 1117-1118 (ENTER DATA)
270     // Restriction - 1161-1162 (EXIT DATA)
271     // Restriction - 2254 (SET)
272     CheckRequireAtLeastOneOf();
273     break;
274   case llvm::acc::Directive::ACCD_update:
275     // Restriction - 2301
276     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
277         updateOnlyAllowedAfterDeviceTypeClauses);
278     break;
279   default:
280     break;
281   }
282   dirContext_.pop_back();
283 }
284 
285 void AccStructureChecker::Enter(const parser::OpenACCRoutineConstruct &x) {
286   PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_routine);
287 }
288 void AccStructureChecker::Leave(const parser::OpenACCRoutineConstruct &) {
289   // Restriction - 2409
290   CheckRequireAtLeastOneOf();
291   // Restriction - 2407-2408
292   CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
293       routineOnlyAllowedAfterDeviceTypeClauses);
294   dirContext_.pop_back();
295 }
296 
297 void AccStructureChecker::Enter(const parser::OpenACCWaitConstruct &x) {
298   const auto &verbatim{std::get<parser::Verbatim>(x.t)};
299   PushContextAndClauseSets(verbatim.source, llvm::acc::Directive::ACCD_wait);
300 }
301 void AccStructureChecker::Leave(const parser::OpenACCWaitConstruct &x) {
302   dirContext_.pop_back();
303 }
304 
305 void AccStructureChecker::Enter(const parser::OpenACCAtomicConstruct &x) {
306   PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_atomic);
307 }
308 void AccStructureChecker::Leave(const parser::OpenACCAtomicConstruct &x) {
309   dirContext_.pop_back();
310 }
311 
312 // Clause checkers
313 CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(Collapse, ACCC_collapse)
314 
315 CHECK_SIMPLE_CLAUSE(Auto, ACCC_auto)
316 CHECK_SIMPLE_CLAUSE(Async, ACCC_async)
317 CHECK_SIMPLE_CLAUSE(Attach, ACCC_attach)
318 CHECK_SIMPLE_CLAUSE(Bind, ACCC_bind)
319 CHECK_SIMPLE_CLAUSE(Capture, ACCC_capture)
320 CHECK_SIMPLE_CLAUSE(Copy, ACCC_copy)
321 CHECK_SIMPLE_CLAUSE(Default, ACCC_default)
322 CHECK_SIMPLE_CLAUSE(DefaultAsync, ACCC_default_async)
323 CHECK_SIMPLE_CLAUSE(Delete, ACCC_delete)
324 CHECK_SIMPLE_CLAUSE(Detach, ACCC_detach)
325 CHECK_SIMPLE_CLAUSE(Device, ACCC_device)
326 CHECK_SIMPLE_CLAUSE(DeviceNum, ACCC_device_num)
327 CHECK_SIMPLE_CLAUSE(Deviceptr, ACCC_deviceptr)
328 CHECK_SIMPLE_CLAUSE(DeviceResident, ACCC_device_resident)
329 CHECK_SIMPLE_CLAUSE(DeviceType, ACCC_device_type)
330 CHECK_SIMPLE_CLAUSE(Finalize, ACCC_finalize)
331 CHECK_SIMPLE_CLAUSE(Firstprivate, ACCC_firstprivate)
332 CHECK_SIMPLE_CLAUSE(Gang, ACCC_gang)
333 CHECK_SIMPLE_CLAUSE(Host, ACCC_host)
334 CHECK_SIMPLE_CLAUSE(If, ACCC_if)
335 CHECK_SIMPLE_CLAUSE(IfPresent, ACCC_if_present)
336 CHECK_SIMPLE_CLAUSE(Independent, ACCC_independent)
337 CHECK_SIMPLE_CLAUSE(Link, ACCC_link)
338 CHECK_SIMPLE_CLAUSE(NoCreate, ACCC_no_create)
339 CHECK_SIMPLE_CLAUSE(Nohost, ACCC_nohost)
340 CHECK_SIMPLE_CLAUSE(NumGangs, ACCC_num_gangs)
341 CHECK_SIMPLE_CLAUSE(NumWorkers, ACCC_num_workers)
342 CHECK_SIMPLE_CLAUSE(Present, ACCC_present)
343 CHECK_SIMPLE_CLAUSE(Private, ACCC_private)
344 CHECK_SIMPLE_CLAUSE(Read, ACCC_read)
345 CHECK_SIMPLE_CLAUSE(Reduction, ACCC_reduction)
346 CHECK_SIMPLE_CLAUSE(Self, ACCC_self)
347 CHECK_SIMPLE_CLAUSE(Seq, ACCC_seq)
348 CHECK_SIMPLE_CLAUSE(Tile, ACCC_tile)
349 CHECK_SIMPLE_CLAUSE(UseDevice, ACCC_use_device)
350 CHECK_SIMPLE_CLAUSE(Vector, ACCC_vector)
351 CHECK_SIMPLE_CLAUSE(VectorLength, ACCC_vector_length)
352 CHECK_SIMPLE_CLAUSE(Wait, ACCC_wait)
353 CHECK_SIMPLE_CLAUSE(Worker, ACCC_worker)
354 CHECK_SIMPLE_CLAUSE(Write, ACCC_write)
355 
356 void AccStructureChecker::Enter(const parser::AccClause::Create &c) {
357   CheckAllowed(llvm::acc::Clause::ACCC_create);
358   const auto &modifierClause{c.v};
359   if (const auto &modifier{
360           std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
361     if (modifier->v != parser::AccDataModifier::Modifier::Zero) {
362       context_.Say(GetContext().clauseSource,
363           "Only the ZERO modifier is allowed for the %s clause "
364           "on the %s directive"_err_en_US,
365           parser::ToUpperCaseLetters(
366               llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_create)
367                   .str()),
368           ContextDirectiveAsFortran());
369     }
370   }
371 }
372 
373 void AccStructureChecker::Enter(const parser::AccClause::Copyin &c) {
374   CheckAllowed(llvm::acc::Clause::ACCC_copyin);
375   const auto &modifierClause{c.v};
376   if (const auto &modifier{
377           std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
378     if (modifier->v != parser::AccDataModifier::Modifier::ReadOnly) {
379       context_.Say(GetContext().clauseSource,
380           "Only the READONLY modifier is allowed for the %s clause "
381           "on the %s directive"_err_en_US,
382           parser::ToUpperCaseLetters(
383               llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyin)
384                   .str()),
385           ContextDirectiveAsFortran());
386     }
387   }
388 }
389 
390 void AccStructureChecker::Enter(const parser::AccClause::Copyout &c) {
391   CheckAllowed(llvm::acc::Clause::ACCC_copyout);
392   const auto &modifierClause{c.v};
393   if (const auto &modifier{
394           std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
395     if (modifier->v != parser::AccDataModifier::Modifier::Zero) {
396       context_.Say(GetContext().clauseSource,
397           "Only the ZERO modifier is allowed for the %s clause "
398           "on the %s directive"_err_en_US,
399           parser::ToUpperCaseLetters(
400               llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyout)
401                   .str()),
402           ContextDirectiveAsFortran());
403     }
404   }
405 }
406 
407 llvm::StringRef AccStructureChecker::getClauseName(llvm::acc::Clause clause) {
408   return llvm::acc::getOpenACCClauseName(clause);
409 }
410 
411 llvm::StringRef AccStructureChecker::getDirectiveName(
412     llvm::acc::Directive directive) {
413   return llvm::acc::getOpenACCDirectiveName(directive);
414 }
415 
416 } // namespace Fortran::semantics
417