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/Common/enum-set.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 | using ReductionOpsSet = |
25 | Fortran::common::EnumSet<Fortran::parser::ReductionOperator::Operator, |
26 | Fortran::parser::ReductionOperator::Operator_enumSize>; |
27 | |
28 | static ReductionOpsSet reductionIntegerSet{ |
29 | Fortran::parser::ReductionOperator::Operator::Plus, |
30 | Fortran::parser::ReductionOperator::Operator::Multiply, |
31 | Fortran::parser::ReductionOperator::Operator::Max, |
32 | Fortran::parser::ReductionOperator::Operator::Min, |
33 | Fortran::parser::ReductionOperator::Operator::Iand, |
34 | Fortran::parser::ReductionOperator::Operator::Ior, |
35 | Fortran::parser::ReductionOperator::Operator::Ieor}; |
36 | |
37 | static ReductionOpsSet reductionRealSet{ |
38 | Fortran::parser::ReductionOperator::Operator::Plus, |
39 | Fortran::parser::ReductionOperator::Operator::Multiply, |
40 | Fortran::parser::ReductionOperator::Operator::Max, |
41 | Fortran::parser::ReductionOperator::Operator::Min}; |
42 | |
43 | static ReductionOpsSet reductionComplexSet{ |
44 | Fortran::parser::ReductionOperator::Operator::Plus, |
45 | Fortran::parser::ReductionOperator::Operator::Multiply}; |
46 | |
47 | static ReductionOpsSet reductionLogicalSet{ |
48 | Fortran::parser::ReductionOperator::Operator::And, |
49 | Fortran::parser::ReductionOperator::Operator::Or, |
50 | Fortran::parser::ReductionOperator::Operator::Eqv, |
51 | Fortran::parser::ReductionOperator::Operator::Neqv}; |
52 | |
53 | namespace Fortran::semantics { |
54 | |
55 | static constexpr inline AccClauseSet |
56 | computeConstructOnlyAllowedAfterDeviceTypeClauses{ |
57 | llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait, |
58 | llvm::acc::Clause::ACCC_num_gangs, llvm::acc::Clause::ACCC_num_workers, |
59 | llvm::acc::Clause::ACCC_vector_length}; |
60 | |
61 | static constexpr inline AccClauseSet loopOnlyAllowedAfterDeviceTypeClauses{ |
62 | llvm::acc::Clause::ACCC_auto, llvm::acc::Clause::ACCC_collapse, |
63 | llvm::acc::Clause::ACCC_independent, llvm::acc::Clause::ACCC_gang, |
64 | llvm::acc::Clause::ACCC_seq, llvm::acc::Clause::ACCC_tile, |
65 | llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker}; |
66 | |
67 | static constexpr inline AccClauseSet updateOnlyAllowedAfterDeviceTypeClauses{ |
68 | llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait}; |
69 | |
70 | static constexpr inline AccClauseSet routineOnlyAllowedAfterDeviceTypeClauses{ |
71 | llvm::acc::Clause::ACCC_bind, llvm::acc::Clause::ACCC_gang, |
72 | llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker, |
73 | llvm::acc::Clause::ACCC_seq}; |
74 | |
75 | static constexpr inline AccClauseSet routineMutuallyExclusiveClauses{ |
76 | llvm::acc::Clause::ACCC_gang, llvm::acc::Clause::ACCC_worker, |
77 | llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_seq}; |
78 | |
79 | bool AccStructureChecker::CheckAllowedModifier(llvm::acc::Clause clause) { |
80 | if (GetContext().directive == llvm::acc::ACCD_enter_data || |
81 | GetContext().directive == llvm::acc::ACCD_exit_data) { |
82 | context_.Say(GetContext().clauseSource, |
83 | "Modifier is not allowed for the %s clause " |
84 | "on the %s directive"_err_en_US, |
85 | parser::ToUpperCaseLetters(getClauseName(clause).str()), |
86 | ContextDirectiveAsFortran()); |
87 | return true; |
88 | } |
89 | return false; |
90 | } |
91 | |
92 | bool AccStructureChecker::IsComputeConstruct( |
93 | llvm::acc::Directive directive) const { |
94 | return directive == llvm::acc::ACCD_parallel || |
95 | directive == llvm::acc::ACCD_parallel_loop || |
96 | directive == llvm::acc::ACCD_serial || |
97 | directive == llvm::acc::ACCD_serial_loop || |
98 | directive == llvm::acc::ACCD_kernels || |
99 | directive == llvm::acc::ACCD_kernels_loop; |
100 | } |
101 | |
102 | bool AccStructureChecker::IsInsideComputeConstruct() const { |
103 | if (dirContext_.size() <= 1) { |
104 | return false; |
105 | } |
106 | |
107 | // Check all nested context skipping the first one. |
108 | for (std::size_t i = dirContext_.size() - 1; i > 0; --i) { |
109 | if (IsComputeConstruct(dirContext_[i - 1].directive)) { |
110 | return true; |
111 | } |
112 | } |
113 | return false; |
114 | } |
115 | |
116 | void AccStructureChecker::CheckNotInComputeConstruct() { |
117 | if (IsInsideComputeConstruct()) { |
118 | context_.Say(GetContext().directiveSource, |
119 | "Directive %s may not be called within a compute region"_err_en_US, |
120 | ContextDirectiveAsFortran()); |
121 | } |
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 | case llvm::acc::Directive::ACCD_serial: |
148 | // Restriction - line 1004-1005 |
149 | CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, |
150 | computeConstructOnlyAllowedAfterDeviceTypeClauses); |
151 | // Restriction - line 1001 |
152 | CheckNoBranching(block, GetContext().directive, blockDir.source); |
153 | break; |
154 | case llvm::acc::Directive::ACCD_data: |
155 | // Restriction - 2.6.5 pt 1 |
156 | // Only a warning is emitted here for portability reason. |
157 | CheckRequireAtLeastOneOf(/*warnInsteadOfError=*/true); |
158 | // Restriction is not formally in the specification but all compilers emit |
159 | // an error and it is likely to be omitted from the spec. |
160 | CheckNoBranching(block, GetContext().directive, blockDir.source); |
161 | break; |
162 | case llvm::acc::Directive::ACCD_host_data: |
163 | // Restriction - line 1746 |
164 | CheckRequireAtLeastOneOf(); |
165 | break; |
166 | default: |
167 | break; |
168 | } |
169 | dirContext_.pop_back(); |
170 | } |
171 | |
172 | void AccStructureChecker::Enter( |
173 | const parser::OpenACCStandaloneDeclarativeConstruct &x) { |
174 | const auto &declarativeDir{std::get<parser::AccDeclarativeDirective>(x.t)}; |
175 | PushContextAndClauseSets(declarativeDir.source, declarativeDir.v); |
176 | } |
177 | |
178 | void AccStructureChecker::Leave( |
179 | const parser::OpenACCStandaloneDeclarativeConstruct &x) { |
180 | // Restriction - line 2409 |
181 | CheckAtLeastOneClause(); |
182 | |
183 | // Restriction - line 2417-2418 - In a Fortran module declaration section, |
184 | // only create, copyin, device_resident, and link clauses are allowed. |
185 | const auto &declarativeDir{std::get<parser::AccDeclarativeDirective>(x.t)}; |
186 | const auto &scope{context_.FindScope(declarativeDir.source)}; |
187 | const Scope &containingScope{GetProgramUnitContaining(scope)}; |
188 | if (containingScope.kind() == Scope::Kind::Module) { |
189 | for (auto cl : GetContext().actualClauses) { |
190 | if (cl != llvm::acc::Clause::ACCC_create && |
191 | cl != llvm::acc::Clause::ACCC_copyin && |
192 | cl != llvm::acc::Clause::ACCC_device_resident && |
193 | cl != llvm::acc::Clause::ACCC_link) { |
194 | context_.Say(GetContext().directiveSource, |
195 | "%s clause is not allowed on the %s directive in module " |
196 | "declaration " |
197 | "section"_err_en_US, |
198 | parser::ToUpperCaseLetters( |
199 | llvm::acc::getOpenACCClauseName(cl).str()), |
200 | ContextDirectiveAsFortran()); |
201 | } |
202 | } |
203 | } |
204 | dirContext_.pop_back(); |
205 | } |
206 | |
207 | void AccStructureChecker::Enter(const parser::OpenACCCombinedConstruct &x) { |
208 | const auto &beginCombinedDir{ |
209 | std::get<parser::AccBeginCombinedDirective>(x.t)}; |
210 | const auto &combinedDir{ |
211 | std::get<parser::AccCombinedDirective>(beginCombinedDir.t)}; |
212 | |
213 | // check matching, End directive is optional |
214 | if (const auto &endCombinedDir{ |
215 | std::get<std::optional<parser::AccEndCombinedDirective>>(x.t)}) { |
216 | CheckMatching<parser::AccCombinedDirective>(combinedDir, endCombinedDir->v); |
217 | } |
218 | |
219 | PushContextAndClauseSets(combinedDir.source, combinedDir.v); |
220 | } |
221 | |
222 | void AccStructureChecker::Leave(const parser::OpenACCCombinedConstruct &x) { |
223 | const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)}; |
224 | const auto &combinedDir{ |
225 | std::get<parser::AccCombinedDirective>(beginBlockDir.t)}; |
226 | auto &doCons{std::get<std::optional<parser::DoConstruct>>(x.t)}; |
227 | switch (combinedDir.v) { |
228 | case llvm::acc::Directive::ACCD_kernels_loop: |
229 | case llvm::acc::Directive::ACCD_parallel_loop: |
230 | case llvm::acc::Directive::ACCD_serial_loop: |
231 | // Restriction - line 1004-1005 |
232 | CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, |
233 | computeConstructOnlyAllowedAfterDeviceTypeClauses | |
234 | loopOnlyAllowedAfterDeviceTypeClauses); |
235 | if (doCons) { |
236 | const parser::Block &block{std::get<parser::Block>(doCons->t)}; |
237 | CheckNoBranching(block, GetContext().directive, beginBlockDir.source); |
238 | } |
239 | break; |
240 | default: |
241 | break; |
242 | } |
243 | dirContext_.pop_back(); |
244 | } |
245 | |
246 | void AccStructureChecker::Enter(const parser::OpenACCLoopConstruct &x) { |
247 | const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)}; |
248 | const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)}; |
249 | PushContextAndClauseSets(loopDir.source, loopDir.v); |
250 | } |
251 | |
252 | void AccStructureChecker::Leave(const parser::OpenACCLoopConstruct &x) { |
253 | const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)}; |
254 | const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)}; |
255 | if (loopDir.v == llvm::acc::Directive::ACCD_loop) { |
256 | // Restriction - line 1818-1819 |
257 | CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, |
258 | loopOnlyAllowedAfterDeviceTypeClauses); |
259 | // Restriction - line 1834 |
260 | CheckNotAllowedIfClause(llvm::acc::Clause::ACCC_seq, |
261 | {llvm::acc::Clause::ACCC_gang, llvm::acc::Clause::ACCC_vector, |
262 | llvm::acc::Clause::ACCC_worker}); |
263 | } |
264 | dirContext_.pop_back(); |
265 | } |
266 | |
267 | void AccStructureChecker::Enter(const parser::OpenACCStandaloneConstruct &x) { |
268 | const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)}; |
269 | PushContextAndClauseSets(standaloneDir.source, standaloneDir.v); |
270 | } |
271 | |
272 | void AccStructureChecker::Leave(const parser::OpenACCStandaloneConstruct &x) { |
273 | const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)}; |
274 | switch (standaloneDir.v) { |
275 | case llvm::acc::Directive::ACCD_enter_data: |
276 | case llvm::acc::Directive::ACCD_exit_data: |
277 | // Restriction - line 1310-1311 (ENTER DATA) |
278 | // Restriction - line 1312-1313 (EXIT DATA) |
279 | CheckRequireAtLeastOneOf(); |
280 | break; |
281 | case llvm::acc::Directive::ACCD_set: |
282 | // Restriction - line 2610 |
283 | CheckRequireAtLeastOneOf(); |
284 | // Restriction - line 2602 |
285 | CheckNotInComputeConstruct(); |
286 | break; |
287 | case llvm::acc::Directive::ACCD_update: |
288 | // Restriction - line 2636 |
289 | CheckRequireAtLeastOneOf(); |
290 | // Restriction - line 2669 |
291 | CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, |
292 | updateOnlyAllowedAfterDeviceTypeClauses); |
293 | break; |
294 | case llvm::acc::Directive::ACCD_init: |
295 | case llvm::acc::Directive::ACCD_shutdown: |
296 | // Restriction - line 2525 (INIT) |
297 | // Restriction - line 2561 (SHUTDOWN) |
298 | CheckNotInComputeConstruct(); |
299 | break; |
300 | default: |
301 | break; |
302 | } |
303 | dirContext_.pop_back(); |
304 | } |
305 | |
306 | void AccStructureChecker::Enter(const parser::OpenACCRoutineConstruct &x) { |
307 | PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_routine); |
308 | const auto &optName{std::get<std::optional<parser::Name>>(x.t)}; |
309 | if (!optName) { |
310 | const auto &verbatim{std::get<parser::Verbatim>(x.t)}; |
311 | const auto &scope{context_.FindScope(verbatim.source)}; |
312 | const Scope &containingScope{GetProgramUnitContaining(scope)}; |
313 | if (containingScope.kind() == Scope::Kind::Module) { |
314 | context_.Say(GetContext().directiveSource, |
315 | "ROUTINE directive without name must appear within the specification " |
316 | "part of a subroutine or function definition, or within an interface " |
317 | "body for a subroutine or function in an interface block"_err_en_US); |
318 | } |
319 | } |
320 | } |
321 | void AccStructureChecker::Leave(const parser::OpenACCRoutineConstruct &) { |
322 | // Restriction - line 2790 |
323 | CheckRequireAtLeastOneOf(); |
324 | // Restriction - line 2788-2789 |
325 | CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, |
326 | routineOnlyAllowedAfterDeviceTypeClauses); |
327 | dirContext_.pop_back(); |
328 | } |
329 | |
330 | void AccStructureChecker::Enter(const parser::OpenACCWaitConstruct &x) { |
331 | const auto &verbatim{std::get<parser::Verbatim>(x.t)}; |
332 | PushContextAndClauseSets(verbatim.source, llvm::acc::Directive::ACCD_wait); |
333 | } |
334 | void AccStructureChecker::Leave(const parser::OpenACCWaitConstruct &x) { |
335 | dirContext_.pop_back(); |
336 | } |
337 | |
338 | void AccStructureChecker::Enter(const parser::OpenACCAtomicConstruct &x) { |
339 | PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_atomic); |
340 | } |
341 | void AccStructureChecker::Leave(const parser::OpenACCAtomicConstruct &x) { |
342 | dirContext_.pop_back(); |
343 | } |
344 | |
345 | void AccStructureChecker::Enter(const parser::AccAtomicUpdate &x) { |
346 | const parser::AssignmentStmt &assignment{ |
347 | std::get<parser::Statement<parser::AssignmentStmt>>(x.t).statement}; |
348 | const auto &var{std::get<parser::Variable>(assignment.t)}; |
349 | const auto &expr{std::get<parser::Expr>(assignment.t)}; |
350 | const auto *rhs{GetExpr(context_, expr)}; |
351 | const auto *lhs{GetExpr(context_, var)}; |
352 | if (lhs && rhs) { |
353 | if (lhs->Rank() != 0) |
354 | context_.Say(expr.source, |
355 | "LHS of atomic update statement must be scalar"_err_en_US); |
356 | if (rhs->Rank() != 0) |
357 | context_.Say(var.GetSource(), |
358 | "RHS of atomic update statement must be scalar"_err_en_US); |
359 | } |
360 | } |
361 | |
362 | void AccStructureChecker::Enter(const parser::OpenACCCacheConstruct &x) { |
363 | const auto &verbatim = std::get<parser::Verbatim>(x.t); |
364 | PushContextAndClauseSets(verbatim.source, llvm::acc::Directive::ACCD_cache); |
365 | SetContextDirectiveSource(verbatim.source); |
366 | if (loopNestLevel == 0) { |
367 | context_.Say(verbatim.source, |
368 | "The CACHE directive must be inside a loop"_err_en_US); |
369 | } |
370 | } |
371 | void AccStructureChecker::Leave(const parser::OpenACCCacheConstruct &x) { |
372 | dirContext_.pop_back(); |
373 | } |
374 | |
375 | // Clause checkers |
376 | CHECK_SIMPLE_CLAUSE(Auto, ACCC_auto) |
377 | CHECK_SIMPLE_CLAUSE(Attach, ACCC_attach) |
378 | CHECK_SIMPLE_CLAUSE(Bind, ACCC_bind) |
379 | CHECK_SIMPLE_CLAUSE(Capture, ACCC_capture) |
380 | CHECK_SIMPLE_CLAUSE(Default, ACCC_default) |
381 | CHECK_SIMPLE_CLAUSE(DefaultAsync, ACCC_default_async) |
382 | CHECK_SIMPLE_CLAUSE(Delete, ACCC_delete) |
383 | CHECK_SIMPLE_CLAUSE(Detach, ACCC_detach) |
384 | CHECK_SIMPLE_CLAUSE(Device, ACCC_device) |
385 | CHECK_SIMPLE_CLAUSE(DeviceNum, ACCC_device_num) |
386 | CHECK_SIMPLE_CLAUSE(Finalize, ACCC_finalize) |
387 | CHECK_SIMPLE_CLAUSE(Firstprivate, ACCC_firstprivate) |
388 | CHECK_SIMPLE_CLAUSE(Host, ACCC_host) |
389 | CHECK_SIMPLE_CLAUSE(IfPresent, ACCC_if_present) |
390 | CHECK_SIMPLE_CLAUSE(Independent, ACCC_independent) |
391 | CHECK_SIMPLE_CLAUSE(NoCreate, ACCC_no_create) |
392 | CHECK_SIMPLE_CLAUSE(Nohost, ACCC_nohost) |
393 | CHECK_SIMPLE_CLAUSE(Private, ACCC_private) |
394 | CHECK_SIMPLE_CLAUSE(Read, ACCC_read) |
395 | CHECK_SIMPLE_CLAUSE(UseDevice, ACCC_use_device) |
396 | CHECK_SIMPLE_CLAUSE(Wait, ACCC_wait) |
397 | CHECK_SIMPLE_CLAUSE(Write, ACCC_write) |
398 | CHECK_SIMPLE_CLAUSE(Unknown, ACCC_unknown) |
399 | |
400 | void AccStructureChecker::CheckMultipleOccurrenceInDeclare( |
401 | const parser::AccObjectList &list, llvm::acc::Clause clause) { |
402 | if (GetContext().directive != llvm::acc::Directive::ACCD_declare) |
403 | return; |
404 | for (const auto &object : list.v) { |
405 | common::visit( |
406 | common::visitors{ |
407 | [&](const parser::Designator &designator) { |
408 | if (const auto *name = getDesignatorNameIfDataRef(designator)) { |
409 | if (declareSymbols.contains(&name->symbol->GetUltimate())) { |
410 | if (declareSymbols[&name->symbol->GetUltimate()] == clause) { |
411 | context_.Warn(common::UsageWarning::OpenAccUsage, |
412 | GetContext().clauseSource, |
413 | "'%s' in the %s clause is already present in the same clause in this module"_warn_en_US, |
414 | name->symbol->name(), |
415 | parser::ToUpperCaseLetters( |
416 | llvm::acc::getOpenACCClauseName(clause).str())); |
417 | } else { |
418 | context_.Say(GetContext().clauseSource, |
419 | "'%s' in the %s clause is already present in another " |
420 | "%s clause in this module"_err_en_US, |
421 | name->symbol->name(), |
422 | parser::ToUpperCaseLetters( |
423 | llvm::acc::getOpenACCClauseName(clause).str()), |
424 | parser::ToUpperCaseLetters( |
425 | llvm::acc::getOpenACCClauseName( |
426 | declareSymbols[&name->symbol->GetUltimate()]) |
427 | .str())); |
428 | } |
429 | } |
430 | declareSymbols.insert({&name->symbol->GetUltimate(), clause}); |
431 | } |
432 | }, |
433 | [&](const parser::Name &name) { |
434 | // TODO: check common block |
435 | }}, |
436 | object.u); |
437 | } |
438 | } |
439 | |
440 | void AccStructureChecker::CheckMultipleOccurrenceInDeclare( |
441 | const parser::AccObjectListWithModifier &list, llvm::acc::Clause clause) { |
442 | const auto &objectList = std::get<Fortran::parser::AccObjectList>(list.t); |
443 | CheckMultipleOccurrenceInDeclare(objectList, clause); |
444 | } |
445 | |
446 | void AccStructureChecker::Enter(const parser::AccClause::Async &c) { |
447 | llvm::acc::Clause crtClause = llvm::acc::Clause::ACCC_async; |
448 | CheckAllowed(crtClause); |
449 | CheckAllowedOncePerGroup(crtClause, llvm::acc::Clause::ACCC_device_type); |
450 | } |
451 | |
452 | void AccStructureChecker::Enter(const parser::AccClause::Create &c) { |
453 | CheckAllowed(llvm::acc::Clause::ACCC_create); |
454 | const auto &modifierClause{c.v}; |
455 | if (const auto &modifier{ |
456 | std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) { |
457 | if (modifier->v != parser::AccDataModifier::Modifier::Zero) { |
458 | context_.Say(GetContext().clauseSource, |
459 | "Only the ZERO modifier is allowed for the %s clause " |
460 | "on the %s directive"_err_en_US, |
461 | parser::ToUpperCaseLetters( |
462 | llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_create) |
463 | .str()), |
464 | ContextDirectiveAsFortran()); |
465 | } |
466 | if (GetContext().directive == llvm::acc::Directive::ACCD_declare) { |
467 | context_.Say(GetContext().clauseSource, |
468 | "The ZERO modifier is not allowed for the %s clause " |
469 | "on the %s directive"_err_en_US, |
470 | parser::ToUpperCaseLetters( |
471 | llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_create) |
472 | .str()), |
473 | ContextDirectiveAsFortran()); |
474 | } |
475 | } |
476 | CheckMultipleOccurrenceInDeclare( |
477 | modifierClause, llvm::acc::Clause::ACCC_create); |
478 | } |
479 | |
480 | void AccStructureChecker::Enter(const parser::AccClause::Copyin &c) { |
481 | CheckAllowed(llvm::acc::Clause::ACCC_copyin); |
482 | const auto &modifierClause{c.v}; |
483 | if (const auto &modifier{ |
484 | std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) { |
485 | if (CheckAllowedModifier(llvm::acc::Clause::ACCC_copyin)) { |
486 | return; |
487 | } |
488 | if (modifier->v != parser::AccDataModifier::Modifier::ReadOnly) { |
489 | context_.Say(GetContext().clauseSource, |
490 | "Only the READONLY modifier is allowed for the %s clause " |
491 | "on the %s directive"_err_en_US, |
492 | parser::ToUpperCaseLetters( |
493 | llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyin) |
494 | .str()), |
495 | ContextDirectiveAsFortran()); |
496 | } |
497 | } |
498 | CheckMultipleOccurrenceInDeclare( |
499 | modifierClause, llvm::acc::Clause::ACCC_copyin); |
500 | } |
501 | |
502 | void AccStructureChecker::Enter(const parser::AccClause::Copyout &c) { |
503 | CheckAllowed(llvm::acc::Clause::ACCC_copyout); |
504 | const auto &modifierClause{c.v}; |
505 | if (const auto &modifier{ |
506 | std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) { |
507 | if (CheckAllowedModifier(llvm::acc::Clause::ACCC_copyout)) { |
508 | return; |
509 | } |
510 | if (modifier->v != parser::AccDataModifier::Modifier::Zero) { |
511 | context_.Say(GetContext().clauseSource, |
512 | "Only the ZERO modifier is allowed for the %s clause " |
513 | "on the %s directive"_err_en_US, |
514 | parser::ToUpperCaseLetters( |
515 | llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyout) |
516 | .str()), |
517 | ContextDirectiveAsFortran()); |
518 | } |
519 | if (GetContext().directive == llvm::acc::Directive::ACCD_declare) { |
520 | context_.Say(GetContext().clauseSource, |
521 | "The ZERO modifier is not allowed for the %s clause " |
522 | "on the %s directive"_err_en_US, |
523 | parser::ToUpperCaseLetters( |
524 | llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyout) |
525 | .str()), |
526 | ContextDirectiveAsFortran()); |
527 | } |
528 | } |
529 | CheckMultipleOccurrenceInDeclare( |
530 | modifierClause, llvm::acc::Clause::ACCC_copyout); |
531 | } |
532 | |
533 | void AccStructureChecker::Enter(const parser::AccClause::DeviceType &d) { |
534 | CheckAllowed(llvm::acc::Clause::ACCC_device_type); |
535 | if (GetContext().directive == llvm::acc::Directive::ACCD_set && |
536 | d.v.v.size() > 1) { |
537 | context_.Say(GetContext().clauseSource, |
538 | "The %s clause on the %s directive accepts only one value"_err_en_US, |
539 | parser::ToUpperCaseLetters( |
540 | llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_device_type) |
541 | .str()), |
542 | ContextDirectiveAsFortran()); |
543 | } |
544 | ResetCrtGroup(); |
545 | } |
546 | |
547 | void AccStructureChecker::Enter(const parser::AccClause::Seq &g) { |
548 | llvm::acc::Clause crtClause = llvm::acc::Clause::ACCC_seq; |
549 | if (GetContext().directive == llvm::acc::Directive::ACCD_routine) { |
550 | CheckMutuallyExclusivePerGroup(crtClause, |
551 | llvm::acc::Clause::ACCC_device_type, routineMutuallyExclusiveClauses); |
552 | } |
553 | CheckAllowed(crtClause); |
554 | } |
555 | |
556 | void AccStructureChecker::Enter(const parser::AccClause::Vector &g) { |
557 | llvm::acc::Clause crtClause = llvm::acc::Clause::ACCC_vector; |
558 | if (GetContext().directive == llvm::acc::Directive::ACCD_routine) { |
559 | CheckMutuallyExclusivePerGroup(crtClause, |
560 | llvm::acc::Clause::ACCC_device_type, routineMutuallyExclusiveClauses); |
561 | } |
562 | CheckAllowed(crtClause); |
563 | if (GetContext().directive != llvm::acc::Directive::ACCD_routine) { |
564 | CheckAllowedOncePerGroup(crtClause, llvm::acc::Clause::ACCC_device_type); |
565 | } |
566 | } |
567 | |
568 | void AccStructureChecker::Enter(const parser::AccClause::Worker &g) { |
569 | llvm::acc::Clause crtClause = llvm::acc::Clause::ACCC_worker; |
570 | if (GetContext().directive == llvm::acc::Directive::ACCD_routine) { |
571 | CheckMutuallyExclusivePerGroup(crtClause, |
572 | llvm::acc::Clause::ACCC_device_type, routineMutuallyExclusiveClauses); |
573 | } |
574 | CheckAllowed(crtClause); |
575 | if (GetContext().directive != llvm::acc::Directive::ACCD_routine) { |
576 | CheckAllowedOncePerGroup(crtClause, llvm::acc::Clause::ACCC_device_type); |
577 | } |
578 | } |
579 | |
580 | void AccStructureChecker::Enter(const parser::AccClause::Tile &g) { |
581 | CheckAllowed(llvm::acc::Clause::ACCC_tile); |
582 | CheckAllowedOncePerGroup( |
583 | llvm::acc::Clause::ACCC_tile, llvm::acc::Clause::ACCC_device_type); |
584 | } |
585 | |
586 | void AccStructureChecker::Enter(const parser::AccClause::Gang &g) { |
587 | llvm::acc::Clause crtClause = llvm::acc::Clause::ACCC_gang; |
588 | if (GetContext().directive == llvm::acc::Directive::ACCD_routine) { |
589 | CheckMutuallyExclusivePerGroup(crtClause, |
590 | llvm::acc::Clause::ACCC_device_type, routineMutuallyExclusiveClauses); |
591 | } |
592 | CheckAllowed(crtClause); |
593 | if (GetContext().directive != llvm::acc::Directive::ACCD_routine) { |
594 | CheckAllowedOncePerGroup(crtClause, llvm::acc::Clause::ACCC_device_type); |
595 | } |
596 | |
597 | if (g.v) { |
598 | bool hasNum = false; |
599 | bool hasDim = false; |
600 | bool hasStatic = false; |
601 | const Fortran::parser::AccGangArgList &x = *g.v; |
602 | for (const Fortran::parser::AccGangArg &gangArg : x.v) { |
603 | if (std::get_if<Fortran::parser::AccGangArg::Num>(&gangArg.u)) { |
604 | hasNum = true; |
605 | } else if (std::get_if<Fortran::parser::AccGangArg::Dim>(&gangArg.u)) { |
606 | hasDim = true; |
607 | } else if (std::get_if<Fortran::parser::AccGangArg::Static>(&gangArg.u)) { |
608 | hasStatic = true; |
609 | } |
610 | } |
611 | |
612 | if (GetContext().directive == llvm::acc::Directive::ACCD_routine && |
613 | (hasStatic || hasNum)) { |
614 | context_.Say(GetContext().clauseSource, |
615 | "Only the dim argument is allowed on the %s clause on the %s directive"_err_en_US, |
616 | parser::ToUpperCaseLetters( |
617 | llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_gang) |
618 | .str()), |
619 | ContextDirectiveAsFortran()); |
620 | } |
621 | |
622 | if (hasDim && hasNum) { |
623 | context_.Say(GetContext().clauseSource, |
624 | "The num argument is not allowed when dim is specified"_err_en_US); |
625 | } |
626 | } |
627 | } |
628 | |
629 | void AccStructureChecker::Enter(const parser::AccClause::NumGangs &n) { |
630 | CheckAllowed(llvm::acc::Clause::ACCC_num_gangs, |
631 | /*warnInsteadOfError=*/GetContext().directive == |
632 | llvm::acc::Directive::ACCD_serial || |
633 | GetContext().directive == llvm::acc::Directive::ACCD_serial_loop); |
634 | CheckAllowedOncePerGroup( |
635 | llvm::acc::Clause::ACCC_num_gangs, llvm::acc::Clause::ACCC_device_type); |
636 | |
637 | if (n.v.size() > 3) |
638 | context_.Say(GetContext().clauseSource, |
639 | "NUM_GANGS clause accepts a maximum of 3 arguments"_err_en_US); |
640 | } |
641 | |
642 | void AccStructureChecker::Enter(const parser::AccClause::NumWorkers &n) { |
643 | CheckAllowed(llvm::acc::Clause::ACCC_num_workers, |
644 | /*warnInsteadOfError=*/GetContext().directive == |
645 | llvm::acc::Directive::ACCD_serial || |
646 | GetContext().directive == llvm::acc::Directive::ACCD_serial_loop); |
647 | CheckAllowedOncePerGroup( |
648 | llvm::acc::Clause::ACCC_num_workers, llvm::acc::Clause::ACCC_device_type); |
649 | } |
650 | |
651 | void AccStructureChecker::Enter(const parser::AccClause::VectorLength &n) { |
652 | CheckAllowed(llvm::acc::Clause::ACCC_vector_length, |
653 | /*warnInsteadOfError=*/GetContext().directive == |
654 | llvm::acc::Directive::ACCD_serial || |
655 | GetContext().directive == llvm::acc::Directive::ACCD_serial_loop); |
656 | CheckAllowedOncePerGroup(llvm::acc::Clause::ACCC_vector_length, |
657 | llvm::acc::Clause::ACCC_device_type); |
658 | } |
659 | |
660 | void AccStructureChecker::Enter(const parser::AccClause::Reduction &reduction) { |
661 | CheckAllowed(llvm::acc::Clause::ACCC_reduction); |
662 | |
663 | // From OpenACC 3.3 |
664 | // At a minimum, the supported data types include Fortran logical as well as |
665 | // the numerical data types (e.g. integer, real, double precision, complex). |
666 | // However, for each reduction operator, the supported data types include only |
667 | // the types permitted as operands to the corresponding operator in the base |
668 | // language where (1) for max and min, the corresponding operator is less-than |
669 | // and (2) for other operators, the operands and the result are the same type. |
670 | // |
671 | // The following check that the reduction operator is supported with the given |
672 | // type. |
673 | const parser::AccObjectListWithReduction &list{reduction.v}; |
674 | const auto &op{std::get<parser::ReductionOperator>(list.t)}; |
675 | const auto &objects{std::get<parser::AccObjectList>(list.t)}; |
676 | |
677 | for (const auto &object : objects.v) { |
678 | common::visit( |
679 | common::visitors{ |
680 | [&](const parser::Designator &designator) { |
681 | if (const auto *name = getDesignatorNameIfDataRef(designator)) { |
682 | if (name->symbol) { |
683 | const auto *type{name->symbol->GetType()}; |
684 | if (type->IsNumeric(TypeCategory::Integer) && |
685 | !reductionIntegerSet.test(op.v)) { |
686 | context_.Say(GetContext().clauseSource, |
687 | "reduction operator not supported for integer type"_err_en_US); |
688 | } else if (type->IsNumeric(TypeCategory::Real) && |
689 | !reductionRealSet.test(op.v)) { |
690 | context_.Say(GetContext().clauseSource, |
691 | "reduction operator not supported for real type"_err_en_US); |
692 | } else if (type->IsNumeric(TypeCategory::Complex) && |
693 | !reductionComplexSet.test(op.v)) { |
694 | context_.Say(GetContext().clauseSource, |
695 | "reduction operator not supported for complex type"_err_en_US); |
696 | } else if (type->category() == |
697 | Fortran::semantics::DeclTypeSpec::Category::Logical && |
698 | !reductionLogicalSet.test(op.v)) { |
699 | context_.Say(GetContext().clauseSource, |
700 | "reduction operator not supported for logical type"_err_en_US); |
701 | } |
702 | // TODO: check composite type. |
703 | } |
704 | } |
705 | }, |
706 | [&](const Fortran::parser::Name &name) { |
707 | // TODO: check common block |
708 | }}, |
709 | object.u); |
710 | } |
711 | } |
712 | |
713 | void AccStructureChecker::Enter(const parser::AccClause::Self &x) { |
714 | CheckAllowed(llvm::acc::Clause::ACCC_self); |
715 | const std::optional<parser::AccSelfClause> &accSelfClause = x.v; |
716 | if (GetContext().directive == llvm::acc::Directive::ACCD_update && |
717 | ((accSelfClause && |
718 | std::holds_alternative<std::optional<parser::ScalarLogicalExpr>>( |
719 | (*accSelfClause).u)) || |
720 | !accSelfClause)) { |
721 | context_.Say(GetContext().clauseSource, |
722 | "SELF clause on the %s directive must have a var-list"_err_en_US, |
723 | ContextDirectiveAsFortran()); |
724 | } else if (GetContext().directive != llvm::acc::Directive::ACCD_update && |
725 | accSelfClause && |
726 | std::holds_alternative<parser::AccObjectList>((*accSelfClause).u)) { |
727 | const auto &accObjectList = |
728 | std::get<parser::AccObjectList>((*accSelfClause).u); |
729 | if (accObjectList.v.size() != 1) { |
730 | context_.Say(GetContext().clauseSource, |
731 | "SELF clause on the %s directive only accepts optional scalar logical" |
732 | " expression"_err_en_US, |
733 | ContextDirectiveAsFortran()); |
734 | } |
735 | } |
736 | } |
737 | |
738 | void AccStructureChecker::Enter(const parser::AccClause::Collapse &x) { |
739 | CheckAllowed(llvm::acc::Clause::ACCC_collapse); |
740 | CheckAllowedOncePerGroup( |
741 | llvm::acc::Clause::ACCC_collapse, llvm::acc::Clause::ACCC_device_type); |
742 | const parser::AccCollapseArg &accCollapseArg = x.v; |
743 | const auto &collapseValue{ |
744 | std::get<parser::ScalarIntConstantExpr>(accCollapseArg.t)}; |
745 | RequiresConstantPositiveParameter( |
746 | llvm::acc::Clause::ACCC_collapse, collapseValue); |
747 | } |
748 | |
749 | void AccStructureChecker::Enter(const parser::AccClause::Present &x) { |
750 | CheckAllowed(llvm::acc::Clause::ACCC_present); |
751 | CheckMultipleOccurrenceInDeclare(x.v, llvm::acc::Clause::ACCC_present); |
752 | } |
753 | |
754 | void AccStructureChecker::Enter(const parser::AccClause::Copy &x) { |
755 | CheckAllowed(llvm::acc::Clause::ACCC_copy); |
756 | CheckMultipleOccurrenceInDeclare(x.v, llvm::acc::Clause::ACCC_copy); |
757 | } |
758 | |
759 | void AccStructureChecker::Enter(const parser::AccClause::Deviceptr &x) { |
760 | CheckAllowed(llvm::acc::Clause::ACCC_deviceptr); |
761 | CheckMultipleOccurrenceInDeclare(x.v, llvm::acc::Clause::ACCC_deviceptr); |
762 | } |
763 | |
764 | void AccStructureChecker::Enter(const parser::AccClause::DeviceResident &x) { |
765 | CheckAllowed(llvm::acc::Clause::ACCC_device_resident); |
766 | CheckMultipleOccurrenceInDeclare( |
767 | x.v, llvm::acc::Clause::ACCC_device_resident); |
768 | } |
769 | |
770 | void AccStructureChecker::Enter(const parser::AccClause::Link &x) { |
771 | CheckAllowed(llvm::acc::Clause::ACCC_link); |
772 | CheckMultipleOccurrenceInDeclare(x.v, llvm::acc::Clause::ACCC_link); |
773 | } |
774 | |
775 | void AccStructureChecker::Enter(const parser::AccClause::Shortloop &x) { |
776 | if (CheckAllowed(llvm::acc::Clause::ACCC_shortloop)) { |
777 | context_.Warn(common::UsageWarning::OpenAccUsage, GetContext().clauseSource, |
778 | "Non-standard shortloop clause ignored"_warn_en_US); |
779 | } |
780 | } |
781 | |
782 | void AccStructureChecker::Enter(const parser::AccClause::If &x) { |
783 | CheckAllowed(llvm::acc::Clause::ACCC_if); |
784 | if (const auto *expr{GetExpr(x.v)}) { |
785 | if (auto type{expr->GetType()}) { |
786 | if (type->category() == TypeCategory::Integer || |
787 | type->category() == TypeCategory::Logical) { |
788 | return; // LOGICAL and INTEGER type supported for the if clause. |
789 | } |
790 | } |
791 | } |
792 | context_.Say( |
793 | GetContext().clauseSource, "Must have LOGICAL or INTEGER type"_err_en_US); |
794 | } |
795 | |
796 | void AccStructureChecker::Enter(const parser::OpenACCEndConstruct &x) { |
797 | context_.Warn(common::UsageWarning::OpenAccUsage, x.source, |
798 | "Misplaced OpenACC end directive"_warn_en_US); |
799 | } |
800 | |
801 | void AccStructureChecker::Enter(const parser::Module &) { |
802 | declareSymbols.clear(); |
803 | } |
804 | |
805 | void AccStructureChecker::Enter(const parser::FunctionSubprogram &x) { |
806 | declareSymbols.clear(); |
807 | } |
808 | |
809 | void AccStructureChecker::Enter(const parser::SubroutineSubprogram &) { |
810 | declareSymbols.clear(); |
811 | } |
812 | |
813 | void AccStructureChecker::Enter(const parser::SeparateModuleSubprogram &) { |
814 | declareSymbols.clear(); |
815 | } |
816 | |
817 | void AccStructureChecker::Enter(const parser::DoConstruct &) { |
818 | ++loopNestLevel; |
819 | } |
820 | |
821 | void AccStructureChecker::Leave(const parser::DoConstruct &) { |
822 | --loopNestLevel; |
823 | } |
824 | |
825 | llvm::StringRef AccStructureChecker::getDirectiveName( |
826 | llvm::acc::Directive directive) { |
827 | return llvm::acc::getOpenACCDirectiveName(directive); |
828 | } |
829 | |
830 | llvm::StringRef AccStructureChecker::getClauseName(llvm::acc::Clause clause) { |
831 | return llvm::acc::getOpenACCClauseName(clause); |
832 | } |
833 | |
834 | } // namespace Fortran::semantics |
835 |
Definitions
- Enter
- reductionIntegerSet
- reductionRealSet
- reductionComplexSet
- reductionLogicalSet
- computeConstructOnlyAllowedAfterDeviceTypeClauses
- loopOnlyAllowedAfterDeviceTypeClauses
- updateOnlyAllowedAfterDeviceTypeClauses
- routineOnlyAllowedAfterDeviceTypeClauses
- routineMutuallyExclusiveClauses
- CheckAllowedModifier
- IsComputeConstruct
- IsInsideComputeConstruct
- CheckNotInComputeConstruct
- Enter
- Leave
- Enter
- Leave
- Enter
- Leave
- Enter
- Leave
- Enter
- Leave
- Enter
- Leave
- Enter
- Leave
- Enter
- Leave
- Enter
- Leave
- Enter
- Enter
- Leave
- CheckMultipleOccurrenceInDeclare
- CheckMultipleOccurrenceInDeclare
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Enter
- Leave
- getDirectiveName
Improve your Profiling and Debugging skills
Find out more