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 &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
160   const auto &combinedDir{
161       std::get<parser::AccCombinedDirective>(beginBlockDir.t)};
162   PushContextAndClauseSets(combinedDir.source, combinedDir.v);
163 }
164 
165 void AccStructureChecker::Leave(const parser::OpenACCCombinedConstruct &x) {
166   const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
167   const auto &combinedDir{
168       std::get<parser::AccCombinedDirective>(beginBlockDir.t)};
169   switch (combinedDir.v) {
170   case llvm::acc::Directive::ACCD_kernels_loop:
171   case llvm::acc::Directive::ACCD_parallel_loop:
172     // Restriction - 1962 -> (880-881) (KERNELS LOOP)
173     // Restriction - 1962 -> (843-844) (PARALLEL LOOP)
174     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
175         {llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait,
176             llvm::acc::Clause::ACCC_num_gangs,
177             llvm::acc::Clause::ACCC_num_workers,
178             llvm::acc::Clause::ACCC_vector_length});
179     break;
180   case llvm::acc::Directive::ACCD_serial_loop:
181     // Restriction - 1962 -> (919) (SERIAL LOOP)
182     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
183         {llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait});
184     break;
185   default:
186     break;
187   }
188   dirContext_.pop_back();
189 }
190 
191 void AccStructureChecker::Enter(const parser::OpenACCLoopConstruct &x) {
192   const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
193   const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
194   PushContextAndClauseSets(loopDir.source, loopDir.v);
195 }
196 
197 void AccStructureChecker::Leave(const parser::OpenACCLoopConstruct &x) {
198   const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
199   const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
200   if (loopDir.v == llvm::acc::Directive::ACCD_loop) {
201     // Restriction - 1615-1616
202     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
203         loopOnlyAllowedAfterDeviceTypeClauses);
204     // Restriction - 1622
205     CheckNotAllowedIfClause(llvm::acc::Clause::ACCC_seq,
206         {llvm::acc::Clause::ACCC_gang, llvm::acc::Clause::ACCC_vector,
207             llvm::acc::Clause::ACCC_worker});
208   }
209   dirContext_.pop_back();
210 }
211 
212 void AccStructureChecker::Enter(const parser::OpenACCStandaloneConstruct &x) {
213   const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
214   PushContextAndClauseSets(standaloneDir.source, standaloneDir.v);
215 }
216 
217 void AccStructureChecker::Leave(const parser::OpenACCStandaloneConstruct &x) {
218   const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
219   switch (standaloneDir.v) {
220   case llvm::acc::Directive::ACCD_enter_data:
221   case llvm::acc::Directive::ACCD_exit_data:
222   case llvm::acc::Directive::ACCD_set:
223     // Restriction - 1117-1118 (ENTER DATA)
224     // Restriction - 1161-1162 (EXIT DATA)
225     // Restriction - 2254 (SET)
226     CheckRequireAtLeastOneOf();
227     break;
228   case llvm::acc::Directive::ACCD_update:
229     // Restriction - 2301
230     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
231         updateOnlyAllowedAfterDeviceTypeClauses);
232     break;
233   default:
234     break;
235   }
236   dirContext_.pop_back();
237 }
238 
239 void AccStructureChecker::Enter(const parser::OpenACCRoutineConstruct &x) {
240   PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_routine);
241 }
242 void AccStructureChecker::Leave(const parser::OpenACCRoutineConstruct &) {
243   // Restriction - 2409
244   CheckRequireAtLeastOneOf();
245   // Restriction - 2407-2408
246   CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
247       routineOnlyAllowedAfterDeviceTypeClauses);
248   dirContext_.pop_back();
249 }
250 
251 // Clause checkers
252 CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(Collapse, ACCC_collapse)
253 
254 CHECK_SIMPLE_CLAUSE(Auto, ACCC_auto)
255 CHECK_SIMPLE_CLAUSE(Async, ACCC_async)
256 CHECK_SIMPLE_CLAUSE(Attach, ACCC_attach)
257 CHECK_SIMPLE_CLAUSE(Bind, ACCC_bind)
258 CHECK_SIMPLE_CLAUSE(Capture, ACCC_capture)
259 CHECK_SIMPLE_CLAUSE(Copy, ACCC_copy)
260 CHECK_SIMPLE_CLAUSE(Default, ACCC_default)
261 CHECK_SIMPLE_CLAUSE(DefaultAsync, ACCC_default_async)
262 CHECK_SIMPLE_CLAUSE(Delete, ACCC_delete)
263 CHECK_SIMPLE_CLAUSE(Detach, ACCC_detach)
264 CHECK_SIMPLE_CLAUSE(Device, ACCC_device)
265 CHECK_SIMPLE_CLAUSE(DeviceNum, ACCC_device_num)
266 CHECK_SIMPLE_CLAUSE(DevicePtr, ACCC_deviceptr)
267 CHECK_SIMPLE_CLAUSE(DeviceResident, ACCC_device_resident)
268 CHECK_SIMPLE_CLAUSE(DeviceType, ACCC_device_type)
269 CHECK_SIMPLE_CLAUSE(Finalize, ACCC_finalize)
270 CHECK_SIMPLE_CLAUSE(FirstPrivate, ACCC_firstprivate)
271 CHECK_SIMPLE_CLAUSE(Gang, ACCC_gang)
272 CHECK_SIMPLE_CLAUSE(Host, ACCC_host)
273 CHECK_SIMPLE_CLAUSE(If, ACCC_if)
274 CHECK_SIMPLE_CLAUSE(IfPresent, ACCC_if_present)
275 CHECK_SIMPLE_CLAUSE(Independent, ACCC_independent)
276 CHECK_SIMPLE_CLAUSE(Link, ACCC_link)
277 CHECK_SIMPLE_CLAUSE(NoCreate, ACCC_no_create)
278 CHECK_SIMPLE_CLAUSE(NoHost, ACCC_nohost)
279 CHECK_SIMPLE_CLAUSE(NumGangs, ACCC_num_gangs)
280 CHECK_SIMPLE_CLAUSE(NumWorkers, ACCC_num_workers)
281 CHECK_SIMPLE_CLAUSE(Present, ACCC_present)
282 CHECK_SIMPLE_CLAUSE(Private, ACCC_private)
283 CHECK_SIMPLE_CLAUSE(Read, ACCC_read)
284 CHECK_SIMPLE_CLAUSE(Reduction, ACCC_reduction)
285 CHECK_SIMPLE_CLAUSE(Self, ACCC_self)
286 CHECK_SIMPLE_CLAUSE(Seq, ACCC_seq)
287 CHECK_SIMPLE_CLAUSE(Tile, ACCC_tile)
288 CHECK_SIMPLE_CLAUSE(UseDevice, ACCC_use_device)
289 CHECK_SIMPLE_CLAUSE(Vector, ACCC_vector)
290 CHECK_SIMPLE_CLAUSE(VectorLength, ACCC_vector_length)
291 CHECK_SIMPLE_CLAUSE(Wait, ACCC_wait)
292 CHECK_SIMPLE_CLAUSE(Worker, ACCC_worker)
293 CHECK_SIMPLE_CLAUSE(Write, ACCC_write)
294 
295 void AccStructureChecker::Enter(const parser::AccClause::Create &c) {
296   CheckAllowed(llvm::acc::Clause::ACCC_create);
297   const auto &modifierClause{c.v};
298   if (const auto &modifier{
299           std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
300     if (modifier->v != parser::AccDataModifier::Modifier::Zero) {
301       context_.Say(GetContext().clauseSource,
302           "Only the ZERO modifier is allowed for the %s clause "
303           "on the %s directive"_err_en_US,
304           parser::ToUpperCaseLetters(
305               llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_create)
306                   .str()),
307           ContextDirectiveAsFortran());
308     }
309   }
310 }
311 
312 void AccStructureChecker::Enter(const parser::AccClause::Copyin &c) {
313   CheckAllowed(llvm::acc::Clause::ACCC_copyin);
314   const auto &modifierClause{c.v};
315   if (const auto &modifier{
316           std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
317     if (modifier->v != parser::AccDataModifier::Modifier::ReadOnly) {
318       context_.Say(GetContext().clauseSource,
319           "Only the READONLY modifier is allowed for the %s clause "
320           "on the %s directive"_err_en_US,
321           parser::ToUpperCaseLetters(
322               llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyin)
323                   .str()),
324           ContextDirectiveAsFortran());
325     }
326   }
327 }
328 
329 void AccStructureChecker::Enter(const parser::AccClause::Copyout &c) {
330   CheckAllowed(llvm::acc::Clause::ACCC_copyout);
331   const auto &modifierClause{c.v};
332   if (const auto &modifier{
333           std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
334     if (modifier->v != parser::AccDataModifier::Modifier::Zero) {
335       context_.Say(GetContext().clauseSource,
336           "Only the ZERO modifier is allowed for the %s clause "
337           "on the %s directive"_err_en_US,
338           parser::ToUpperCaseLetters(
339               llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyout)
340                   .str()),
341           ContextDirectiveAsFortran());
342     }
343   }
344 }
345 
346 llvm::StringRef AccStructureChecker::getClauseName(llvm::acc::Clause clause) {
347   return llvm::acc::getOpenACCClauseName(clause);
348 }
349 
350 llvm::StringRef AccStructureChecker::getDirectiveName(
351     llvm::acc::Directive directive) {
352   return llvm::acc::getOpenACCDirectiveName(directive);
353 }
354 
355 } // namespace Fortran::semantics
356