1 | //===----------------------------------------------------------------------===// |
---|---|
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 "resolve-directives.h" |
10 | |
11 | #include "check-acc-structure.h" |
12 | #include "check-omp-structure.h" |
13 | #include "resolve-names-utils.h" |
14 | #include "flang/Common/idioms.h" |
15 | #include "flang/Evaluate/fold.h" |
16 | #include "flang/Evaluate/tools.h" |
17 | #include "flang/Evaluate/type.h" |
18 | #include "flang/Parser/parse-tree-visitor.h" |
19 | #include "flang/Parser/parse-tree.h" |
20 | #include "flang/Parser/tools.h" |
21 | #include "flang/Semantics/expression.h" |
22 | #include "flang/Semantics/openmp-dsa.h" |
23 | #include "flang/Semantics/openmp-modifiers.h" |
24 | #include "flang/Semantics/symbol.h" |
25 | #include "flang/Semantics/tools.h" |
26 | #include "llvm/Support/Debug.h" |
27 | #include <list> |
28 | #include <map> |
29 | #include <sstream> |
30 | |
31 | template <typename T> |
32 | static Fortran::semantics::Scope *GetScope( |
33 | Fortran::semantics::SemanticsContext &context, const T &x) { |
34 | std::optional<Fortran::parser::CharBlock> source{GetLastSource(x)}; |
35 | return source ? &context.FindScope(*source) : nullptr; |
36 | } |
37 | |
38 | namespace Fortran::semantics { |
39 | |
40 | template <typename T> class DirectiveAttributeVisitor { |
41 | public: |
42 | explicit DirectiveAttributeVisitor(SemanticsContext &context) |
43 | : context_{context} {} |
44 | |
45 | template <typename A> bool Pre(const A &) { return true; } |
46 | template <typename A> void Post(const A &) {} |
47 | |
48 | protected: |
49 | struct DirContext { |
50 | DirContext(const parser::CharBlock &source, T d, Scope &s) |
51 | : directiveSource{source}, directive{d}, scope{s} {} |
52 | parser::CharBlock directiveSource; |
53 | T directive; |
54 | Scope &scope; |
55 | Symbol::Flag defaultDSA{Symbol::Flag::AccShared}; // TODOACC |
56 | std::map<const Symbol *, Symbol::Flag> objectWithDSA; |
57 | bool withinConstruct{false}; |
58 | std::int64_t associatedLoopLevel{0}; |
59 | }; |
60 | |
61 | DirContext &GetContext() { |
62 | CHECK(!dirContext_.empty()); |
63 | return dirContext_.back(); |
64 | } |
65 | std::optional<DirContext> GetContextIf() { |
66 | return dirContext_.empty() |
67 | ? std::nullopt |
68 | : std::make_optional<DirContext>(dirContext_.back()); |
69 | } |
70 | void PushContext(const parser::CharBlock &source, T dir, Scope &scope) { |
71 | dirContext_.emplace_back(source, dir, scope); |
72 | } |
73 | void PushContext(const parser::CharBlock &source, T dir) { |
74 | dirContext_.emplace_back(source, dir, context_.FindScope(source)); |
75 | } |
76 | void PopContext() { dirContext_.pop_back(); } |
77 | void SetContextDirectiveSource(parser::CharBlock &dir) { |
78 | GetContext().directiveSource = dir; |
79 | } |
80 | Scope &currScope() { return GetContext().scope; } |
81 | void SetContextDefaultDSA(Symbol::Flag flag) { |
82 | GetContext().defaultDSA = flag; |
83 | } |
84 | void AddToContextObjectWithDSA( |
85 | const Symbol &symbol, Symbol::Flag flag, DirContext &context) { |
86 | context.objectWithDSA.emplace(&symbol, flag); |
87 | } |
88 | void AddToContextObjectWithDSA(const Symbol &symbol, Symbol::Flag flag) { |
89 | AddToContextObjectWithDSA(symbol, flag, GetContext()); |
90 | } |
91 | bool IsObjectWithDSA(const Symbol &symbol) { |
92 | auto it{GetContext().objectWithDSA.find(&symbol)}; |
93 | return it != GetContext().objectWithDSA.end(); |
94 | } |
95 | void SetContextAssociatedLoopLevel(std::int64_t level) { |
96 | GetContext().associatedLoopLevel = level; |
97 | } |
98 | Symbol &MakeAssocSymbol( |
99 | const SourceName &name, const Symbol &prev, Scope &scope) { |
100 | const auto pair{scope.try_emplace(name, Attrs{}, HostAssocDetails{prev})}; |
101 | return *pair.first->second; |
102 | } |
103 | Symbol &MakeAssocSymbol(const SourceName &name, const Symbol &prev) { |
104 | return MakeAssocSymbol(name, prev, currScope()); |
105 | } |
106 | void AddDataSharingAttributeObject(SymbolRef object) { |
107 | dataSharingAttributeObjects_.insert(object); |
108 | } |
109 | void ClearDataSharingAttributeObjects() { |
110 | dataSharingAttributeObjects_.clear(); |
111 | } |
112 | bool HasDataSharingAttributeObject(const Symbol &); |
113 | const parser::Name *GetLoopIndex(const parser::DoConstruct &); |
114 | const parser::DoConstruct *GetDoConstructIf( |
115 | const parser::ExecutionPartConstruct &); |
116 | Symbol *DeclareNewAccessEntity(const Symbol &, Symbol::Flag, Scope &); |
117 | Symbol *DeclareAccessEntity(const parser::Name &, Symbol::Flag, Scope &); |
118 | Symbol *DeclareAccessEntity(Symbol &, Symbol::Flag, Scope &); |
119 | Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); |
120 | |
121 | UnorderedSymbolSet dataSharingAttributeObjects_; // on one directive |
122 | SemanticsContext &context_; |
123 | std::vector<DirContext> dirContext_; // used as a stack |
124 | }; |
125 | |
126 | class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> { |
127 | public: |
128 | explicit AccAttributeVisitor(SemanticsContext &context, Scope *topScope) |
129 | : DirectiveAttributeVisitor(context), topScope_(topScope) {} |
130 | |
131 | template <typename A> void Walk(const A &x) { parser::Walk(x, *this); } |
132 | template <typename A> bool Pre(const A &) { return true; } |
133 | template <typename A> void Post(const A &) {} |
134 | |
135 | bool Pre(const parser::OpenACCBlockConstruct &); |
136 | void Post(const parser::OpenACCBlockConstruct &) { PopContext(); } |
137 | bool Pre(const parser::OpenACCCombinedConstruct &); |
138 | void Post(const parser::OpenACCCombinedConstruct &) { PopContext(); } |
139 | |
140 | bool Pre(const parser::OpenACCDeclarativeConstruct &); |
141 | void Post(const parser::OpenACCDeclarativeConstruct &) { PopContext(); } |
142 | |
143 | void Post(const parser::AccDeclarativeDirective &) { |
144 | GetContext().withinConstruct = true; |
145 | } |
146 | |
147 | bool Pre(const parser::OpenACCRoutineConstruct &); |
148 | bool Pre(const parser::AccBindClause &); |
149 | void Post(const parser::OpenACCStandaloneDeclarativeConstruct &); |
150 | |
151 | void Post(const parser::AccBeginBlockDirective &) { |
152 | GetContext().withinConstruct = true; |
153 | } |
154 | |
155 | bool Pre(const parser::OpenACCLoopConstruct &); |
156 | void Post(const parser::OpenACCLoopConstruct &) { PopContext(); } |
157 | void Post(const parser::AccLoopDirective &) { |
158 | GetContext().withinConstruct = true; |
159 | } |
160 | |
161 | bool Pre(const parser::OpenACCStandaloneConstruct &); |
162 | void Post(const parser::OpenACCStandaloneConstruct &) { PopContext(); } |
163 | void Post(const parser::AccStandaloneDirective &) { |
164 | GetContext().withinConstruct = true; |
165 | } |
166 | |
167 | bool Pre(const parser::OpenACCCacheConstruct &); |
168 | void Post(const parser::OpenACCCacheConstruct &) { PopContext(); } |
169 | |
170 | void Post(const parser::AccDefaultClause &); |
171 | |
172 | bool Pre(const parser::AccClause::Attach &); |
173 | bool Pre(const parser::AccClause::Detach &); |
174 | |
175 | bool Pre(const parser::AccClause::Copy &x) { |
176 | ResolveAccObjectList(x.v, Symbol::Flag::AccCopy); |
177 | return false; |
178 | } |
179 | |
180 | bool Pre(const parser::AccClause::Create &x) { |
181 | const auto &objectList{std::get<parser::AccObjectList>(x.v.t)}; |
182 | ResolveAccObjectList(objectList, Symbol::Flag::AccCreate); |
183 | return false; |
184 | } |
185 | |
186 | bool Pre(const parser::AccClause::Copyin &x) { |
187 | const auto &objectList{std::get<parser::AccObjectList>(x.v.t)}; |
188 | const auto &modifier{ |
189 | std::get<std::optional<parser::AccDataModifier>>(x.v.t)}; |
190 | if (modifier && |
191 | (*modifier).v == parser::AccDataModifier::Modifier::ReadOnly) { |
192 | ResolveAccObjectList(objectList, Symbol::Flag::AccCopyInReadOnly); |
193 | } else { |
194 | ResolveAccObjectList(objectList, Symbol::Flag::AccCopyIn); |
195 | } |
196 | return false; |
197 | } |
198 | |
199 | bool Pre(const parser::AccClause::Copyout &x) { |
200 | const auto &objectList{std::get<parser::AccObjectList>(x.v.t)}; |
201 | ResolveAccObjectList(objectList, Symbol::Flag::AccCopyOut); |
202 | return false; |
203 | } |
204 | |
205 | bool Pre(const parser::AccClause::Present &x) { |
206 | ResolveAccObjectList(x.v, Symbol::Flag::AccPresent); |
207 | return false; |
208 | } |
209 | bool Pre(const parser::AccClause::Private &x) { |
210 | ResolveAccObjectList(x.v, Symbol::Flag::AccPrivate); |
211 | return false; |
212 | } |
213 | bool Pre(const parser::AccClause::Firstprivate &x) { |
214 | ResolveAccObjectList(x.v, Symbol::Flag::AccFirstPrivate); |
215 | return false; |
216 | } |
217 | |
218 | bool Pre(const parser::AccClause::Device &x) { |
219 | ResolveAccObjectList(x.v, Symbol::Flag::AccDevice); |
220 | return false; |
221 | } |
222 | |
223 | bool Pre(const parser::AccClause::DeviceResident &x) { |
224 | ResolveAccObjectList(x.v, Symbol::Flag::AccDeviceResident); |
225 | return false; |
226 | } |
227 | |
228 | bool Pre(const parser::AccClause::Deviceptr &x) { |
229 | ResolveAccObjectList(x.v, Symbol::Flag::AccDevicePtr); |
230 | return false; |
231 | } |
232 | |
233 | bool Pre(const parser::AccClause::Link &x) { |
234 | ResolveAccObjectList(x.v, Symbol::Flag::AccLink); |
235 | return false; |
236 | } |
237 | |
238 | bool Pre(const parser::AccClause::Host &x) { |
239 | ResolveAccObjectList(x.v, Symbol::Flag::AccHost); |
240 | return false; |
241 | } |
242 | |
243 | bool Pre(const parser::AccClause::Self &x) { |
244 | const std::optional<parser::AccSelfClause> &accSelfClause = x.v; |
245 | if (accSelfClause && |
246 | std::holds_alternative<parser::AccObjectList>((*accSelfClause).u)) { |
247 | const auto &accObjectList = |
248 | std::get<parser::AccObjectList>((*accSelfClause).u); |
249 | ResolveAccObjectList(accObjectList, Symbol::Flag::AccSelf); |
250 | } |
251 | return false; |
252 | } |
253 | |
254 | void Post(const parser::Name &); |
255 | |
256 | private: |
257 | std::int64_t GetAssociatedLoopLevelFromClauses(const parser::AccClauseList &); |
258 | |
259 | Symbol::Flags dataSharingAttributeFlags{Symbol::Flag::AccShared, |
260 | Symbol::Flag::AccPrivate, Symbol::Flag::AccFirstPrivate, |
261 | Symbol::Flag::AccReduction}; |
262 | |
263 | Symbol::Flags dataMappingAttributeFlags{Symbol::Flag::AccCreate, |
264 | Symbol::Flag::AccCopyIn, Symbol::Flag::AccCopyOut, |
265 | Symbol::Flag::AccDelete, Symbol::Flag::AccPresent}; |
266 | |
267 | Symbol::Flags accDataMvtFlags{ |
268 | Symbol::Flag::AccDevice, Symbol::Flag::AccHost, Symbol::Flag::AccSelf}; |
269 | |
270 | Symbol::Flags accFlagsRequireMark{Symbol::Flag::AccCreate, |
271 | Symbol::Flag::AccCopyIn, Symbol::Flag::AccCopyInReadOnly, |
272 | Symbol::Flag::AccCopy, Symbol::Flag::AccCopyOut, |
273 | Symbol::Flag::AccDevicePtr, Symbol::Flag::AccDeviceResident, |
274 | Symbol::Flag::AccLink, Symbol::Flag::AccPresent}; |
275 | |
276 | void CheckAssociatedLoop(const parser::DoConstruct &); |
277 | void ResolveAccObjectList(const parser::AccObjectList &, Symbol::Flag); |
278 | void ResolveAccObject(const parser::AccObject &, Symbol::Flag); |
279 | Symbol *ResolveAcc(const parser::Name &, Symbol::Flag, Scope &); |
280 | Symbol *ResolveAcc(Symbol &, Symbol::Flag, Scope &); |
281 | Symbol *ResolveName(const parser::Name &, bool parentScope = false); |
282 | Symbol *ResolveFctName(const parser::Name &); |
283 | Symbol *ResolveAccCommonBlockName(const parser::Name *); |
284 | Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); |
285 | Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag); |
286 | void CheckMultipleAppearances( |
287 | const parser::Name &, const Symbol &, Symbol::Flag); |
288 | void AllowOnlyArrayAndSubArray(const parser::AccObjectList &objectList); |
289 | void DoNotAllowAssumedSizedArray(const parser::AccObjectList &objectList); |
290 | void AllowOnlyVariable(const parser::AccObject &object); |
291 | void EnsureAllocatableOrPointer( |
292 | const llvm::acc::Clause clause, const parser::AccObjectList &objectList); |
293 | void AddRoutineInfoToSymbol( |
294 | Symbol &, const parser::OpenACCRoutineConstruct &); |
295 | Scope *topScope_; |
296 | }; |
297 | |
298 | // Data-sharing and Data-mapping attributes for data-refs in OpenMP construct |
299 | class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> { |
300 | public: |
301 | explicit OmpAttributeVisitor(SemanticsContext &context) |
302 | : DirectiveAttributeVisitor(context) {} |
303 | |
304 | template <typename A> void Walk(const A &x) { parser::Walk(x, *this); } |
305 | template <typename A> bool Pre(const A &) { return true; } |
306 | template <typename A> void Post(const A &) {} |
307 | |
308 | template <typename A> bool Pre(const parser::Statement<A> &statement) { |
309 | currentStatementSource_ = statement.source; |
310 | // Keep track of the labels in all the labelled statements |
311 | if (statement.label) { |
312 | auto label{statement.label.value()}; |
313 | // Get the context to check if the labelled statement is in an |
314 | // enclosing OpenMP construct |
315 | std::optional<DirContext> thisContext{GetContextIf()}; |
316 | targetLabels_.emplace( |
317 | label, std::make_pair(currentStatementSource_, thisContext)); |
318 | // Check if a statement that causes a jump to the 'label' |
319 | // has already been encountered |
320 | auto range{sourceLabels_.equal_range(label)}; |
321 | for (auto it{range.first}; it != range.second; ++it) { |
322 | // Check if both the statement with 'label' and the statement that |
323 | // causes a jump to the 'label' are in the same scope |
324 | CheckLabelContext(it->second.first, currentStatementSource_, |
325 | it->second.second, thisContext); |
326 | } |
327 | } |
328 | return true; |
329 | } |
330 | |
331 | bool Pre(const parser::InternalSubprogram &) { |
332 | // Clear the labels being tracked in the previous scope |
333 | ClearLabels(); |
334 | return true; |
335 | } |
336 | |
337 | bool Pre(const parser::ModuleSubprogram &) { |
338 | // Clear the labels being tracked in the previous scope |
339 | ClearLabels(); |
340 | return true; |
341 | } |
342 | |
343 | bool Pre(const parser::StmtFunctionStmt &x) { |
344 | const auto &parsedExpr{std::get<parser::Scalar<parser::Expr>>(x.t)}; |
345 | if (const auto *expr{GetExpr(context_, parsedExpr)}) { |
346 | for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) { |
347 | if (!IsStmtFunctionDummy(symbol)) { |
348 | stmtFunctionExprSymbols_.insert(symbol.GetUltimate()); |
349 | } |
350 | } |
351 | } |
352 | return true; |
353 | } |
354 | |
355 | bool Pre(const parser::OmpDirectiveSpecification &x) { |
356 | PushContext(x.source, x.DirId()); |
357 | return true; |
358 | } |
359 | void Post(const parser::OmpDirectiveSpecification &) { PopContext(); } |
360 | |
361 | bool Pre(const parser::OmpMetadirectiveDirective &x) { |
362 | PushContext(x.source, llvm::omp::Directive::OMPD_metadirective); |
363 | return true; |
364 | } |
365 | void Post(const parser::OmpMetadirectiveDirective &) { PopContext(); } |
366 | |
367 | bool Pre(const parser::OpenMPBlockConstruct &); |
368 | void Post(const parser::OpenMPBlockConstruct &); |
369 | |
370 | void Post(const parser::OmpBeginBlockDirective &) { |
371 | GetContext().withinConstruct = true; |
372 | } |
373 | |
374 | bool Pre(const parser::OpenMPSimpleStandaloneConstruct &); |
375 | void Post(const parser::OpenMPSimpleStandaloneConstruct &) { PopContext(); } |
376 | |
377 | bool Pre(const parser::OpenMPLoopConstruct &); |
378 | void Post(const parser::OpenMPLoopConstruct &) { PopContext(); } |
379 | void Post(const parser::OmpBeginLoopDirective &) { |
380 | GetContext().withinConstruct = true; |
381 | } |
382 | bool Pre(const parser::DoConstruct &); |
383 | |
384 | bool Pre(const parser::OpenMPSectionsConstruct &); |
385 | void Post(const parser::OpenMPSectionsConstruct &) { PopContext(); } |
386 | |
387 | bool Pre(const parser::OpenMPCriticalConstruct &critical); |
388 | void Post(const parser::OpenMPCriticalConstruct &) { PopContext(); } |
389 | |
390 | bool Pre(const parser::OpenMPDeclareSimdConstruct &x) { |
391 | PushContext(x.source, llvm::omp::Directive::OMPD_declare_simd); |
392 | const auto &name{std::get<std::optional<parser::Name>>(x.t)}; |
393 | if (name) { |
394 | ResolveOmpName(*name, Symbol::Flag::OmpDeclareSimd); |
395 | } |
396 | return true; |
397 | } |
398 | void Post(const parser::OpenMPDeclareSimdConstruct &) { PopContext(); } |
399 | |
400 | bool Pre(const parser::OpenMPDepobjConstruct &x) { |
401 | PushContext(x.source, llvm::omp::Directive::OMPD_depobj); |
402 | for (auto &arg : x.v.Arguments().v) { |
403 | if (auto *locator{std::get_if<parser::OmpLocator>(&arg.u)}) { |
404 | if (auto *object{std::get_if<parser::OmpObject>(&locator->u)}) { |
405 | ResolveOmpObject(*object, Symbol::Flag::OmpDependObject); |
406 | } |
407 | } |
408 | } |
409 | return true; |
410 | } |
411 | void Post(const parser::OpenMPDepobjConstruct &) { PopContext(); } |
412 | |
413 | bool Pre(const parser::OpenMPFlushConstruct &x) { |
414 | PushContext(x.source, llvm::omp::Directive::OMPD_flush); |
415 | for (auto &arg : x.v.Arguments().v) { |
416 | if (auto *locator{std::get_if<parser::OmpLocator>(&arg.u)}) { |
417 | if (auto *object{std::get_if<parser::OmpObject>(&locator->u)}) { |
418 | if (auto *name{std::get_if<parser::Name>(&object->u)}) { |
419 | // ResolveOmpCommonBlockName resolves the symbol as a side effect |
420 | if (!ResolveOmpCommonBlockName(name)) { |
421 | context_.Say(name->source, // 2.15.3 |
422 | "COMMON block must be declared in the same scoping unit " |
423 | "in which the OpenMP directive or clause appears"_err_en_US); |
424 | } |
425 | } |
426 | } |
427 | } |
428 | } |
429 | return true; |
430 | } |
431 | void Post(const parser::OpenMPFlushConstruct &) { PopContext(); } |
432 | |
433 | bool Pre(const parser::OpenMPRequiresConstruct &x) { |
434 | using Flags = WithOmpDeclarative::RequiresFlags; |
435 | using Requires = WithOmpDeclarative::RequiresFlag; |
436 | PushContext(x.source, llvm::omp::Directive::OMPD_requires); |
437 | |
438 | // Gather information from the clauses. |
439 | Flags flags; |
440 | std::optional<common::OmpMemoryOrderType> memOrder; |
441 | for (const auto &clause : std::get<parser::OmpClauseList>(x.t).v) { |
442 | flags |= common::visit( |
443 | common::visitors{ |
444 | [&memOrder]( |
445 | const parser::OmpClause::AtomicDefaultMemOrder &atomic) { |
446 | memOrder = atomic.v.v; |
447 | return Flags{}; |
448 | }, |
449 | [](const parser::OmpClause::ReverseOffload &) { |
450 | return Flags{Requires::ReverseOffload}; |
451 | }, |
452 | [](const parser::OmpClause::UnifiedAddress &) { |
453 | return Flags{Requires::UnifiedAddress}; |
454 | }, |
455 | [](const parser::OmpClause::UnifiedSharedMemory &) { |
456 | return Flags{Requires::UnifiedSharedMemory}; |
457 | }, |
458 | [](const parser::OmpClause::DynamicAllocators &) { |
459 | return Flags{Requires::DynamicAllocators}; |
460 | }, |
461 | [](const auto &) { return Flags{}; }}, |
462 | clause.u); |
463 | } |
464 | // Merge clauses into parents' symbols details. |
465 | AddOmpRequiresToScope(currScope(), flags, memOrder); |
466 | return true; |
467 | } |
468 | void Post(const parser::OpenMPRequiresConstruct &) { PopContext(); } |
469 | |
470 | bool Pre(const parser::OpenMPDeclareTargetConstruct &); |
471 | void Post(const parser::OpenMPDeclareTargetConstruct &) { PopContext(); } |
472 | |
473 | bool Pre(const parser::OpenMPDeclareMapperConstruct &); |
474 | void Post(const parser::OpenMPDeclareMapperConstruct &) { PopContext(); } |
475 | |
476 | bool Pre(const parser::OpenMPDeclareReductionConstruct &); |
477 | void Post(const parser::OpenMPDeclareReductionConstruct &) { PopContext(); } |
478 | |
479 | bool Pre(const parser::OpenMPThreadprivate &); |
480 | void Post(const parser::OpenMPThreadprivate &) { PopContext(); } |
481 | |
482 | bool Pre(const parser::OpenMPDeclarativeAllocate &); |
483 | void Post(const parser::OpenMPDeclarativeAllocate &) { PopContext(); } |
484 | |
485 | bool Pre(const parser::OpenMPDispatchConstruct &); |
486 | void Post(const parser::OpenMPDispatchConstruct &) { PopContext(); } |
487 | |
488 | bool Pre(const parser::OpenMPExecutableAllocate &); |
489 | void Post(const parser::OpenMPExecutableAllocate &); |
490 | |
491 | bool Pre(const parser::OpenMPAllocatorsConstruct &); |
492 | void Post(const parser::OpenMPAllocatorsConstruct &); |
493 | |
494 | void Post(const parser::OmpObjectList &x) { |
495 | // The objects from OMP clauses should have already been resolved, |
496 | // except common blocks (the ResolveNamesVisitor does not visit |
497 | // parser::Name, those are dealt with as members of other structures). |
498 | // Iterate over elements of x, and resolve any common blocks that |
499 | // are still unresolved. |
500 | for (const parser::OmpObject &obj : x.v) { |
501 | auto *name{std::get_if<parser::Name>(&obj.u)}; |
502 | if (name && !name->symbol) { |
503 | Resolve(*name, currScope().MakeCommonBlock(name->source)); |
504 | } |
505 | } |
506 | } |
507 | |
508 | // 2.15.3 Data-Sharing Attribute Clauses |
509 | bool Pre(const parser::OmpClause::Inclusive &x) { |
510 | ResolveOmpObjectList(x.v, Symbol::Flag::OmpInclusiveScan); |
511 | return false; |
512 | } |
513 | bool Pre(const parser::OmpClause::Exclusive &x) { |
514 | ResolveOmpObjectList(x.v, Symbol::Flag::OmpExclusiveScan); |
515 | return false; |
516 | } |
517 | void Post(const parser::OmpDefaultClause &); |
518 | bool Pre(const parser::OmpClause::Shared &x) { |
519 | ResolveOmpObjectList(x.v, Symbol::Flag::OmpShared); |
520 | return false; |
521 | } |
522 | bool Pre(const parser::OmpClause::Private &x) { |
523 | ResolveOmpObjectList(x.v, Symbol::Flag::OmpPrivate); |
524 | return false; |
525 | } |
526 | bool Pre(const parser::OmpAllocateClause &x) { |
527 | const auto &objectList{std::get<parser::OmpObjectList>(x.t)}; |
528 | ResolveOmpObjectList(objectList, Symbol::Flag::OmpAllocate); |
529 | return false; |
530 | } |
531 | bool Pre(const parser::OmpClause::Firstprivate &x) { |
532 | ResolveOmpObjectList(x.v, Symbol::Flag::OmpFirstPrivate); |
533 | return false; |
534 | } |
535 | bool Pre(const parser::OmpClause::Lastprivate &x) { |
536 | const auto &objList{std::get<parser::OmpObjectList>(x.v.t)}; |
537 | ResolveOmpObjectList(objList, Symbol::Flag::OmpLastPrivate); |
538 | return false; |
539 | } |
540 | bool Pre(const parser::OmpClause::Copyin &x) { |
541 | ResolveOmpObjectList(x.v, Symbol::Flag::OmpCopyIn); |
542 | return false; |
543 | } |
544 | bool Pre(const parser::OmpClause::Copyprivate &x) { |
545 | ResolveOmpObjectList(x.v, Symbol::Flag::OmpCopyPrivate); |
546 | return false; |
547 | } |
548 | bool Pre(const parser::OmpLinearClause &x) { |
549 | auto &objects{std::get<parser::OmpObjectList>(x.t)}; |
550 | ResolveOmpObjectList(objects, Symbol::Flag::OmpLinear); |
551 | return false; |
552 | } |
553 | |
554 | bool Pre(const parser::OmpClause::Uniform &x) { |
555 | ResolveOmpNameList(x.v, Symbol::Flag::OmpUniform); |
556 | return false; |
557 | } |
558 | |
559 | bool Pre(const parser::OmpInReductionClause &x) { |
560 | auto &objects{std::get<parser::OmpObjectList>(x.t)}; |
561 | ResolveOmpObjectList(objects, Symbol::Flag::OmpInReduction); |
562 | return false; |
563 | } |
564 | |
565 | bool Pre(const parser::OmpClause::Reduction &x) { |
566 | const auto &objList{std::get<parser::OmpObjectList>(x.v.t)}; |
567 | ResolveOmpObjectList(objList, Symbol::Flag::OmpReduction); |
568 | |
569 | if (auto &modifiers{OmpGetModifiers(x.v)}) { |
570 | auto createDummyProcSymbol = [&](const parser::Name *name) { |
571 | // If name resolution failed, create a dummy symbol |
572 | const auto namePair{currScope().try_emplace( |
573 | name->source, Attrs{}, ProcEntityDetails{})}; |
574 | auto &newSymbol{*namePair.first->second}; |
575 | if (context_.intrinsics().IsIntrinsic(name->ToString())) { |
576 | newSymbol.attrs().set(Attr::INTRINSIC); |
577 | } |
578 | name->symbol = &newSymbol; |
579 | }; |
580 | |
581 | for (auto &mod : *modifiers) { |
582 | if (!std::holds_alternative<parser::OmpReductionIdentifier>(mod.u)) { |
583 | continue; |
584 | } |
585 | auto &opr{std::get<parser::OmpReductionIdentifier>(mod.u)}; |
586 | if (auto *procD{parser::Unwrap<parser::ProcedureDesignator>(opr.u)}) { |
587 | if (auto *name{parser::Unwrap<parser::Name>(procD->u)}) { |
588 | if (!name->symbol) { |
589 | if (!ResolveName(name)) { |
590 | createDummyProcSymbol(name); |
591 | } |
592 | } |
593 | } |
594 | if (auto *procRef{ |
595 | parser::Unwrap<parser::ProcComponentRef>(procD->u)}) { |
596 | if (!procRef->v.thing.component.symbol) { |
597 | if (!ResolveName(&procRef->v.thing.component)) { |
598 | createDummyProcSymbol(&procRef->v.thing.component); |
599 | } |
600 | } |
601 | } |
602 | } |
603 | } |
604 | using ReductionModifier = parser::OmpReductionModifier; |
605 | if (auto *maybeModifier{ |
606 | OmpGetUniqueModifier<ReductionModifier>(modifiers)}) { |
607 | if (maybeModifier->v == ReductionModifier::Value::Inscan) { |
608 | ResolveOmpObjectList(objList, Symbol::Flag::OmpInScanReduction); |
609 | } |
610 | } |
611 | } |
612 | return false; |
613 | } |
614 | |
615 | bool Pre(const parser::OmpAlignedClause &x) { |
616 | const auto &alignedNameList{std::get<parser::OmpObjectList>(x.t)}; |
617 | ResolveOmpObjectList(alignedNameList, Symbol::Flag::OmpAligned); |
618 | return false; |
619 | } |
620 | |
621 | bool Pre(const parser::OmpClause::Nontemporal &x) { |
622 | const auto &nontemporalNameList{x.v}; |
623 | ResolveOmpNameList(nontemporalNameList, Symbol::Flag::OmpNontemporal); |
624 | return false; |
625 | } |
626 | |
627 | void Post(const parser::OmpIteration &x) { |
628 | if (const auto &name{std::get<parser::Name>(x.t)}; !name.symbol) { |
629 | auto *symbol{currScope().FindSymbol(name.source)}; |
630 | if (!symbol) { |
631 | // OmpIteration must use an existing object. If there isn't one, |
632 | // create a fake one and flag an error later. |
633 | symbol = &currScope().MakeSymbol( |
634 | name.source, Attrs{}, EntityDetails(/*isDummy=*/true)); |
635 | } |
636 | Resolve(name, symbol); |
637 | } |
638 | } |
639 | |
640 | bool Pre(const parser::OmpClause::UseDevicePtr &x) { |
641 | ResolveOmpObjectList(x.v, Symbol::Flag::OmpUseDevicePtr); |
642 | return false; |
643 | } |
644 | |
645 | bool Pre(const parser::OmpClause::UseDeviceAddr &x) { |
646 | ResolveOmpObjectList(x.v, Symbol::Flag::OmpUseDeviceAddr); |
647 | return false; |
648 | } |
649 | |
650 | bool Pre(const parser::OmpClause::IsDevicePtr &x) { |
651 | ResolveOmpObjectList(x.v, Symbol::Flag::OmpIsDevicePtr); |
652 | return false; |
653 | } |
654 | |
655 | bool Pre(const parser::OmpClause::HasDeviceAddr &x) { |
656 | ResolveOmpObjectList(x.v, Symbol::Flag::OmpHasDeviceAddr); |
657 | return false; |
658 | } |
659 | |
660 | void Post(const parser::Name &); |
661 | |
662 | // Keep track of labels in the statements that causes jumps to target labels |
663 | void Post(const parser::GotoStmt &gotoStmt) { CheckSourceLabel(gotoStmt.v); } |
664 | void Post(const parser::ComputedGotoStmt &computedGotoStmt) { |
665 | for (auto &label : std::get<std::list<parser::Label>>(computedGotoStmt.t)) { |
666 | CheckSourceLabel(label); |
667 | } |
668 | } |
669 | void Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) { |
670 | CheckSourceLabel(std::get<1>(arithmeticIfStmt.t)); |
671 | CheckSourceLabel(std::get<2>(arithmeticIfStmt.t)); |
672 | CheckSourceLabel(std::get<3>(arithmeticIfStmt.t)); |
673 | } |
674 | void Post(const parser::AssignedGotoStmt &assignedGotoStmt) { |
675 | for (auto &label : std::get<std::list<parser::Label>>(assignedGotoStmt.t)) { |
676 | CheckSourceLabel(label); |
677 | } |
678 | } |
679 | void Post(const parser::AltReturnSpec &altReturnSpec) { |
680 | CheckSourceLabel(altReturnSpec.v); |
681 | } |
682 | void Post(const parser::ErrLabel &errLabel) { CheckSourceLabel(errLabel.v); } |
683 | void Post(const parser::EndLabel &endLabel) { CheckSourceLabel(endLabel.v); } |
684 | void Post(const parser::EorLabel &eorLabel) { CheckSourceLabel(eorLabel.v); } |
685 | |
686 | void Post(const parser::OmpMapClause &x) { |
687 | Symbol::Flag ompFlag = Symbol::Flag::OmpMapToFrom; |
688 | auto &mods{OmpGetModifiers(x)}; |
689 | if (auto *mapType{OmpGetUniqueModifier<parser::OmpMapType>(mods)}) { |
690 | switch (mapType->v) { |
691 | case parser::OmpMapType::Value::To: |
692 | ompFlag = Symbol::Flag::OmpMapTo; |
693 | break; |
694 | case parser::OmpMapType::Value::From: |
695 | ompFlag = Symbol::Flag::OmpMapFrom; |
696 | break; |
697 | case parser::OmpMapType::Value::Tofrom: |
698 | ompFlag = Symbol::Flag::OmpMapToFrom; |
699 | break; |
700 | case parser::OmpMapType::Value::Alloc: |
701 | ompFlag = Symbol::Flag::OmpMapAlloc; |
702 | break; |
703 | case parser::OmpMapType::Value::Release: |
704 | ompFlag = Symbol::Flag::OmpMapRelease; |
705 | break; |
706 | case parser::OmpMapType::Value::Delete: |
707 | ompFlag = Symbol::Flag::OmpMapDelete; |
708 | break; |
709 | } |
710 | } |
711 | const auto &ompObjList{std::get<parser::OmpObjectList>(x.t)}; |
712 | for (const auto &ompObj : ompObjList.v) { |
713 | common::visit( |
714 | common::visitors{ |
715 | [&](const parser::Designator &designator) { |
716 | if (const auto *name{ |
717 | semantics::getDesignatorNameIfDataRef(designator)}) { |
718 | if (name->symbol) { |
719 | name->symbol->set(ompFlag); |
720 | AddToContextObjectWithDSA(*name->symbol, ompFlag); |
721 | } |
722 | if (name->symbol && |
723 | semantics::IsAssumedSizeArray(*name->symbol)) { |
724 | context_.Say(designator.source, |
725 | "Assumed-size whole arrays may not appear on the %s " |
726 | "clause"_err_en_US, |
727 | "MAP"); |
728 | } |
729 | } |
730 | }, |
731 | [&](const auto &name) {}, |
732 | }, |
733 | ompObj.u); |
734 | |
735 | ResolveOmpObject(ompObj, ompFlag); |
736 | } |
737 | } |
738 | |
739 | const parser::OmpClause *associatedClause{nullptr}; |
740 | void SetAssociatedClause(const parser::OmpClause &c) { |
741 | associatedClause = &c; |
742 | } |
743 | const parser::OmpClause *GetAssociatedClause() { return associatedClause; } |
744 | |
745 | private: |
746 | std::int64_t GetAssociatedLoopLevelFromClauses(const parser::OmpClauseList &); |
747 | |
748 | Symbol::Flags dataSharingAttributeFlags{Symbol::Flag::OmpShared, |
749 | Symbol::Flag::OmpPrivate, Symbol::Flag::OmpFirstPrivate, |
750 | Symbol::Flag::OmpLastPrivate, Symbol::Flag::OmpReduction, |
751 | Symbol::Flag::OmpLinear}; |
752 | |
753 | Symbol::Flags privateDataSharingAttributeFlags{Symbol::Flag::OmpPrivate, |
754 | Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate}; |
755 | |
756 | Symbol::Flags ompFlagsRequireNewSymbol{Symbol::Flag::OmpPrivate, |
757 | Symbol::Flag::OmpLinear, Symbol::Flag::OmpFirstPrivate, |
758 | Symbol::Flag::OmpLastPrivate, Symbol::Flag::OmpShared, |
759 | Symbol::Flag::OmpReduction, Symbol::Flag::OmpCriticalLock, |
760 | Symbol::Flag::OmpCopyIn, Symbol::Flag::OmpUseDevicePtr, |
761 | Symbol::Flag::OmpUseDeviceAddr, Symbol::Flag::OmpIsDevicePtr, |
762 | Symbol::Flag::OmpHasDeviceAddr, Symbol::Flag::OmpUniform}; |
763 | |
764 | Symbol::Flags ompFlagsRequireMark{Symbol::Flag::OmpThreadprivate, |
765 | Symbol::Flag::OmpDeclareTarget, Symbol::Flag::OmpExclusiveScan, |
766 | Symbol::Flag::OmpInclusiveScan, Symbol::Flag::OmpInScanReduction}; |
767 | |
768 | Symbol::Flags dataCopyingAttributeFlags{ |
769 | Symbol::Flag::OmpCopyIn, Symbol::Flag::OmpCopyPrivate}; |
770 | |
771 | std::vector<const parser::Name *> allocateNames_; // on one directive |
772 | UnorderedSymbolSet privateDataSharingAttributeObjects_; // on one directive |
773 | UnorderedSymbolSet stmtFunctionExprSymbols_; |
774 | std::multimap<const parser::Label, |
775 | std::pair<parser::CharBlock, std::optional<DirContext>>> |
776 | sourceLabels_; |
777 | std::map<const parser::Label, |
778 | std::pair<parser::CharBlock, std::optional<DirContext>>> |
779 | targetLabels_; |
780 | parser::CharBlock currentStatementSource_; |
781 | |
782 | void AddAllocateName(const parser::Name *&object) { |
783 | allocateNames_.push_back(x: object); |
784 | } |
785 | void ClearAllocateNames() { allocateNames_.clear(); } |
786 | |
787 | void AddPrivateDataSharingAttributeObjects(SymbolRef object) { |
788 | privateDataSharingAttributeObjects_.insert(object); |
789 | } |
790 | void ClearPrivateDataSharingAttributeObjects() { |
791 | privateDataSharingAttributeObjects_.clear(); |
792 | } |
793 | |
794 | // Predetermined DSA rules |
795 | void PrivatizeAssociatedLoopIndexAndCheckLoopLevel( |
796 | const parser::OpenMPLoopConstruct &); |
797 | void ResolveSeqLoopIndexInParallelOrTaskConstruct(const parser::Name &); |
798 | |
799 | bool IsNestedInDirective(llvm::omp::Directive directive); |
800 | void ResolveOmpObjectList(const parser::OmpObjectList &, Symbol::Flag); |
801 | void ResolveOmpObject(const parser::OmpObject &, Symbol::Flag); |
802 | Symbol *ResolveOmp(const parser::Name &, Symbol::Flag, Scope &); |
803 | Symbol *ResolveOmp(Symbol &, Symbol::Flag, Scope &); |
804 | Symbol *ResolveOmpCommonBlockName(const parser::Name *); |
805 | void ResolveOmpNameList(const std::list<parser::Name> &, Symbol::Flag); |
806 | void ResolveOmpName(const parser::Name &, Symbol::Flag); |
807 | Symbol *ResolveName(const parser::Name *); |
808 | Symbol *ResolveOmpObjectScope(const parser::Name *); |
809 | Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); |
810 | Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag); |
811 | void CheckMultipleAppearances( |
812 | const parser::Name &, const Symbol &, Symbol::Flag); |
813 | |
814 | void CheckDataCopyingClause( |
815 | const parser::Name &, const Symbol &, Symbol::Flag); |
816 | void CheckAssocLoopLevel(std::int64_t level, const parser::OmpClause *clause); |
817 | void CheckObjectIsPrivatizable( |
818 | const parser::Name &, const Symbol &, Symbol::Flag); |
819 | void CheckSourceLabel(const parser::Label &); |
820 | void CheckLabelContext(const parser::CharBlock, const parser::CharBlock, |
821 | std::optional<DirContext>, std::optional<DirContext>); |
822 | void ClearLabels() { |
823 | sourceLabels_.clear(); |
824 | targetLabels_.clear(); |
825 | }; |
826 | void CheckAllNamesInAllocateStmt(const parser::CharBlock &source, |
827 | const parser::OmpObjectList &ompObjectList, |
828 | const parser::AllocateStmt &allocate); |
829 | void CheckNameInAllocateStmt(const parser::CharBlock &source, |
830 | const parser::Name &ompObject, const parser::AllocateStmt &allocate); |
831 | |
832 | std::int64_t ordCollapseLevel{0}; |
833 | |
834 | void AddOmpRequiresToScope(Scope &, WithOmpDeclarative::RequiresFlags, |
835 | std::optional<common::OmpMemoryOrderType>); |
836 | void IssueNonConformanceWarning( |
837 | llvm::omp::Directive D, parser::CharBlock source); |
838 | |
839 | void CreateImplicitSymbols(const Symbol *symbol); |
840 | |
841 | void AddToContextObjectWithExplicitDSA(Symbol &symbol, Symbol::Flag flag) { |
842 | AddToContextObjectWithDSA(symbol, flag); |
843 | if (dataSharingAttributeFlags.test(flag)) { |
844 | symbol.set(Symbol::Flag::OmpExplicit); |
845 | } |
846 | } |
847 | |
848 | // Clear any previous data-sharing attribute flags and set the new ones. |
849 | // Needed when setting PreDetermined DSAs, that take precedence over |
850 | // Implicit ones. |
851 | void SetSymbolDSA(Symbol &symbol, Symbol::Flags flags) { |
852 | symbol.flags() &= ~(dataSharingAttributeFlags | |
853 | Symbol::Flags{Symbol::Flag::OmpExplicit, Symbol::Flag::OmpImplicit, |
854 | Symbol::Flag::OmpPreDetermined}); |
855 | symbol.flags() |= flags; |
856 | } |
857 | }; |
858 | |
859 | template <typename T> |
860 | bool DirectiveAttributeVisitor<T>::HasDataSharingAttributeObject( |
861 | const Symbol &object) { |
862 | auto it{dataSharingAttributeObjects_.find(object)}; |
863 | return it != dataSharingAttributeObjects_.end(); |
864 | } |
865 | |
866 | template <typename T> |
867 | const parser::Name *DirectiveAttributeVisitor<T>::GetLoopIndex( |
868 | const parser::DoConstruct &x) { |
869 | using Bounds = parser::LoopControl::Bounds; |
870 | if (x.GetLoopControl()) { |
871 | if (const Bounds * b{std::get_if<Bounds>(&x.GetLoopControl()->u)}) { |
872 | return &b->name.thing; |
873 | } else { |
874 | return nullptr; |
875 | } |
876 | } else { |
877 | context_ |
878 | .Say(std::get<parser::Statement<parser::NonLabelDoStmt>>(x.t).source, |
879 | "Loop control is not present in the DO LOOP"_err_en_US) |
880 | .Attach(GetContext().directiveSource, |
881 | "associated with the enclosing LOOP construct"_en_US); |
882 | return nullptr; |
883 | } |
884 | } |
885 | |
886 | template <typename T> |
887 | const parser::DoConstruct *DirectiveAttributeVisitor<T>::GetDoConstructIf( |
888 | const parser::ExecutionPartConstruct &x) { |
889 | return parser::Unwrap<parser::DoConstruct>(x); |
890 | } |
891 | |
892 | template <typename T> |
893 | Symbol *DirectiveAttributeVisitor<T>::DeclareNewAccessEntity( |
894 | const Symbol &object, Symbol::Flag flag, Scope &scope) { |
895 | assert(object.owner() != currScope()); |
896 | auto &symbol{MakeAssocSymbol(object.name(), object, scope)}; |
897 | symbol.set(flag); |
898 | if (flag == Symbol::Flag::OmpCopyIn) { |
899 | // The symbol in copyin clause must be threadprivate entity. |
900 | symbol.set(Symbol::Flag::OmpThreadprivate); |
901 | } |
902 | return &symbol; |
903 | } |
904 | |
905 | template <typename T> |
906 | Symbol *DirectiveAttributeVisitor<T>::DeclareAccessEntity( |
907 | const parser::Name &name, Symbol::Flag flag, Scope &scope) { |
908 | if (!name.symbol) { |
909 | return nullptr; // not resolved by Name Resolution step, do nothing |
910 | } |
911 | name.symbol = DeclareAccessEntity(*name.symbol, flag, scope); |
912 | return name.symbol; |
913 | } |
914 | |
915 | template <typename T> |
916 | Symbol *DirectiveAttributeVisitor<T>::DeclareAccessEntity( |
917 | Symbol &object, Symbol::Flag flag, Scope &scope) { |
918 | if (object.owner() != currScope()) { |
919 | return DeclareNewAccessEntity(object, flag, scope); |
920 | } else { |
921 | object.set(flag); |
922 | return &object; |
923 | } |
924 | } |
925 | |
926 | bool AccAttributeVisitor::Pre(const parser::OpenACCBlockConstruct &x) { |
927 | const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)}; |
928 | const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)}; |
929 | switch (blockDir.v) { |
930 | case llvm::acc::Directive::ACCD_data: |
931 | case llvm::acc::Directive::ACCD_host_data: |
932 | case llvm::acc::Directive::ACCD_kernels: |
933 | case llvm::acc::Directive::ACCD_parallel: |
934 | case llvm::acc::Directive::ACCD_serial: |
935 | PushContext(blockDir.source, blockDir.v); |
936 | break; |
937 | default: |
938 | break; |
939 | } |
940 | ClearDataSharingAttributeObjects(); |
941 | return true; |
942 | } |
943 | |
944 | bool AccAttributeVisitor::Pre(const parser::OpenACCDeclarativeConstruct &x) { |
945 | if (const auto *declConstruct{ |
946 | std::get_if<parser::OpenACCStandaloneDeclarativeConstruct>(&x.u)}) { |
947 | const auto &declDir{ |
948 | std::get<parser::AccDeclarativeDirective>(declConstruct->t)}; |
949 | PushContext(declDir.source, llvm::acc::Directive::ACCD_declare); |
950 | } |
951 | ClearDataSharingAttributeObjects(); |
952 | return true; |
953 | } |
954 | |
955 | static const parser::AccObjectList &GetAccObjectList( |
956 | const parser::AccClause &clause) { |
957 | if (const auto *copyClause = |
958 | std::get_if<Fortran::parser::AccClause::Copy>(&clause.u)) { |
959 | return copyClause->v; |
960 | } else if (const auto *createClause = |
961 | std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) { |
962 | const Fortran::parser::AccObjectListWithModifier &listWithModifier = |
963 | createClause->v; |
964 | const Fortran::parser::AccObjectList &accObjectList = |
965 | std::get<Fortran::parser::AccObjectList>(listWithModifier.t); |
966 | return accObjectList; |
967 | } else if (const auto *copyinClause = |
968 | std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) { |
969 | const Fortran::parser::AccObjectListWithModifier &listWithModifier = |
970 | copyinClause->v; |
971 | const Fortran::parser::AccObjectList &accObjectList = |
972 | std::get<Fortran::parser::AccObjectList>(listWithModifier.t); |
973 | return accObjectList; |
974 | } else if (const auto *copyoutClause = |
975 | std::get_if<Fortran::parser::AccClause::Copyout>(&clause.u)) { |
976 | const Fortran::parser::AccObjectListWithModifier &listWithModifier = |
977 | copyoutClause->v; |
978 | const Fortran::parser::AccObjectList &accObjectList = |
979 | std::get<Fortran::parser::AccObjectList>(listWithModifier.t); |
980 | return accObjectList; |
981 | } else if (const auto *presentClause = |
982 | std::get_if<Fortran::parser::AccClause::Present>(&clause.u)) { |
983 | return presentClause->v; |
984 | } else if (const auto *deviceptrClause = |
985 | std::get_if<Fortran::parser::AccClause::Deviceptr>( |
986 | &clause.u)) { |
987 | return deviceptrClause->v; |
988 | } else if (const auto *deviceResidentClause = |
989 | std::get_if<Fortran::parser::AccClause::DeviceResident>( |
990 | &clause.u)) { |
991 | return deviceResidentClause->v; |
992 | } else if (const auto *linkClause = |
993 | std::get_if<Fortran::parser::AccClause::Link>(&clause.u)) { |
994 | return linkClause->v; |
995 | } else { |
996 | llvm_unreachable("Clause without object list!"); |
997 | } |
998 | } |
999 | |
1000 | void AccAttributeVisitor::Post( |
1001 | const parser::OpenACCStandaloneDeclarativeConstruct &x) { |
1002 | const auto &clauseList = std::get<parser::AccClauseList>(x.t); |
1003 | for (const auto &clause : clauseList.v) { |
1004 | // Restriction - line 2414 |
1005 | // We assume the restriction is present because clauses that require |
1006 | // moving data would require the size of the data to be present, but |
1007 | // the deviceptr and present clauses do not require moving data and |
1008 | // thus we permit them. |
1009 | if (!std::holds_alternative<parser::AccClause::Deviceptr>(clause.u) && |
1010 | !std::holds_alternative<parser::AccClause::Present>(clause.u)) { |
1011 | DoNotAllowAssumedSizedArray(GetAccObjectList(clause)); |
1012 | } |
1013 | } |
1014 | } |
1015 | |
1016 | bool AccAttributeVisitor::Pre(const parser::OpenACCLoopConstruct &x) { |
1017 | const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)}; |
1018 | const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)}; |
1019 | const auto &clauseList{std::get<parser::AccClauseList>(beginDir.t)}; |
1020 | if (loopDir.v == llvm::acc::Directive::ACCD_loop) { |
1021 | PushContext(loopDir.source, loopDir.v); |
1022 | } |
1023 | ClearDataSharingAttributeObjects(); |
1024 | SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList)); |
1025 | const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)}; |
1026 | CheckAssociatedLoop(*outer); |
1027 | return true; |
1028 | } |
1029 | |
1030 | bool AccAttributeVisitor::Pre(const parser::OpenACCStandaloneConstruct &x) { |
1031 | const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)}; |
1032 | switch (standaloneDir.v) { |
1033 | case llvm::acc::Directive::ACCD_enter_data: |
1034 | case llvm::acc::Directive::ACCD_exit_data: |
1035 | case llvm::acc::Directive::ACCD_init: |
1036 | case llvm::acc::Directive::ACCD_set: |
1037 | case llvm::acc::Directive::ACCD_shutdown: |
1038 | case llvm::acc::Directive::ACCD_update: |
1039 | PushContext(standaloneDir.source, standaloneDir.v); |
1040 | break; |
1041 | default: |
1042 | break; |
1043 | } |
1044 | ClearDataSharingAttributeObjects(); |
1045 | return true; |
1046 | } |
1047 | |
1048 | Symbol *AccAttributeVisitor::ResolveName( |
1049 | const parser::Name &name, bool parentScope) { |
1050 | Symbol *prev{currScope().FindSymbol(name.source)}; |
1051 | // Check in parent scope if asked for. |
1052 | if (!prev && parentScope) { |
1053 | prev = currScope().parent().FindSymbol(name.source); |
1054 | } |
1055 | if (prev != name.symbol) { |
1056 | name.symbol = prev; |
1057 | } |
1058 | return prev; |
1059 | } |
1060 | |
1061 | Symbol *AccAttributeVisitor::ResolveFctName(const parser::Name &name) { |
1062 | Symbol *prev{currScope().FindSymbol(name.source)}; |
1063 | if (!prev || (prev && prev->IsFuncResult())) { |
1064 | prev = currScope().parent().FindSymbol(name.source); |
1065 | if (!prev) { |
1066 | prev = &context_.globalScope().MakeSymbol( |
1067 | name.source, Attrs{}, ProcEntityDetails{}); |
1068 | } |
1069 | } |
1070 | if (prev != name.symbol) { |
1071 | name.symbol = prev; |
1072 | } |
1073 | return prev; |
1074 | } |
1075 | |
1076 | template <typename T> |
1077 | common::IfNoLvalue<T, T> FoldExpr( |
1078 | evaluate::FoldingContext &foldingContext, T &&expr) { |
1079 | return evaluate::Fold(foldingContext, std::move(expr)); |
1080 | } |
1081 | |
1082 | template <typename T> |
1083 | MaybeExpr EvaluateExpr( |
1084 | Fortran::semantics::SemanticsContext &semanticsContext, const T &expr) { |
1085 | return FoldExpr( |
1086 | semanticsContext.foldingContext(), AnalyzeExpr(semanticsContext, expr)); |
1087 | } |
1088 | |
1089 | void AccAttributeVisitor::AddRoutineInfoToSymbol( |
1090 | Symbol &symbol, const parser::OpenACCRoutineConstruct &x) { |
1091 | if (symbol.has<SubprogramDetails>()) { |
1092 | Fortran::semantics::OpenACCRoutineInfo info; |
1093 | std::vector<OpenACCRoutineDeviceTypeInfo *> currentDevices; |
1094 | currentDevices.push_back(&info); |
1095 | const auto &clauses{std::get<Fortran::parser::AccClauseList>(x.t)}; |
1096 | for (const Fortran::parser::AccClause &clause : clauses.v) { |
1097 | if (const auto *dTypeClause{ |
1098 | std::get_if<Fortran::parser::AccClause::DeviceType>(&clause.u)}) { |
1099 | currentDevices.clear(); |
1100 | for (const auto &deviceTypeExpr : dTypeClause->v.v) { |
1101 | currentDevices.push_back(&info.add_deviceTypeInfo(deviceTypeExpr.v)); |
1102 | } |
1103 | } else if (std::get_if<Fortran::parser::AccClause::Nohost>(&clause.u)) { |
1104 | info.set_isNohost(); |
1105 | } else if (std::get_if<Fortran::parser::AccClause::Seq>(&clause.u)) { |
1106 | for (auto &device : currentDevices) { |
1107 | device->set_isSeq(); |
1108 | } |
1109 | } else if (std::get_if<Fortran::parser::AccClause::Vector>(&clause.u)) { |
1110 | for (auto &device : currentDevices) { |
1111 | device->set_isVector(); |
1112 | } |
1113 | } else if (std::get_if<Fortran::parser::AccClause::Worker>(&clause.u)) { |
1114 | for (auto &device : currentDevices) { |
1115 | device->set_isWorker(); |
1116 | } |
1117 | } else if (const auto *gangClause{ |
1118 | std::get_if<Fortran::parser::AccClause::Gang>( |
1119 | &clause.u)}) { |
1120 | for (auto &device : currentDevices) { |
1121 | device->set_isGang(); |
1122 | } |
1123 | if (gangClause->v) { |
1124 | const Fortran::parser::AccGangArgList &x = *gangClause->v; |
1125 | int numArgs{0}; |
1126 | for (const Fortran::parser::AccGangArg &gangArg : x.v) { |
1127 | CHECK(numArgs <= 1 && "expecting 0 or 1 gang dim args"); |
1128 | if (const auto *dim{std::get_if<Fortran::parser::AccGangArg::Dim>( |
1129 | &gangArg.u)}) { |
1130 | if (const auto v{EvaluateInt64(context_, dim->v)}) { |
1131 | for (auto &device : currentDevices) { |
1132 | device->set_gangDim(*v); |
1133 | } |
1134 | } |
1135 | } |
1136 | numArgs++; |
1137 | } |
1138 | } |
1139 | } else if (const auto *bindClause{ |
1140 | std::get_if<Fortran::parser::AccClause::Bind>( |
1141 | &clause.u)}) { |
1142 | if (const auto *name{ |
1143 | std::get_if<Fortran::parser::Name>(&bindClause->v.u)}) { |
1144 | if (Symbol * sym{ResolveFctName(*name)}) { |
1145 | Symbol &ultimate{sym->GetUltimate()}; |
1146 | for (auto &device : currentDevices) { |
1147 | device->set_bindName(SymbolRef{ultimate}); |
1148 | } |
1149 | } else { |
1150 | context_.Say((*name).source, |
1151 | "No function or subroutine declared for '%s'"_err_en_US, |
1152 | (*name).source); |
1153 | } |
1154 | } else if (const auto charExpr{ |
1155 | std::get_if<Fortran::parser::ScalarDefaultCharExpr>( |
1156 | &bindClause->v.u)}) { |
1157 | auto *charConst{ |
1158 | Fortran::parser::Unwrap<Fortran::parser::CharLiteralConstant>( |
1159 | *charExpr)}; |
1160 | std::string str{std::get<std::string>(charConst->t)}; |
1161 | for (auto &device : currentDevices) { |
1162 | device->set_bindName(std::string(str)); |
1163 | } |
1164 | } |
1165 | } |
1166 | } |
1167 | symbol.get<SubprogramDetails>().add_openACCRoutineInfo(info); |
1168 | } |
1169 | } |
1170 | |
1171 | bool AccAttributeVisitor::Pre(const parser::OpenACCRoutineConstruct &x) { |
1172 | const auto &verbatim{std::get<parser::Verbatim>(x.t)}; |
1173 | if (topScope_) { |
1174 | PushContext( |
1175 | verbatim.source, llvm::acc::Directive::ACCD_routine, *topScope_); |
1176 | } else { |
1177 | PushContext(verbatim.source, llvm::acc::Directive::ACCD_routine); |
1178 | } |
1179 | const auto &optName{std::get<std::optional<parser::Name>>(x.t)}; |
1180 | if (optName) { |
1181 | if (Symbol *sym = ResolveFctName(*optName)) { |
1182 | Symbol &ultimate{sym->GetUltimate()}; |
1183 | AddRoutineInfoToSymbol(ultimate, x); |
1184 | } else { |
1185 | context_.Say((*optName).source, |
1186 | "No function or subroutine declared for '%s'"_err_en_US, |
1187 | (*optName).source); |
1188 | } |
1189 | } else { |
1190 | if (currScope().symbol()) { |
1191 | AddRoutineInfoToSymbol(*currScope().symbol(), x); |
1192 | } |
1193 | } |
1194 | return true; |
1195 | } |
1196 | |
1197 | bool AccAttributeVisitor::Pre(const parser::AccBindClause &x) { |
1198 | if (const auto *name{std::get_if<parser::Name>(&x.u)}) { |
1199 | if (!ResolveFctName(*name)) { |
1200 | context_.Say(name->source, |
1201 | "No function or subroutine declared for '%s'"_err_en_US, |
1202 | name->source); |
1203 | } |
1204 | } |
1205 | return true; |
1206 | } |
1207 | |
1208 | bool AccAttributeVisitor::Pre(const parser::OpenACCCombinedConstruct &x) { |
1209 | const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)}; |
1210 | const auto &combinedDir{ |
1211 | std::get<parser::AccCombinedDirective>(beginBlockDir.t)}; |
1212 | switch (combinedDir.v) { |
1213 | case llvm::acc::Directive::ACCD_kernels_loop: |
1214 | case llvm::acc::Directive::ACCD_parallel_loop: |
1215 | case llvm::acc::Directive::ACCD_serial_loop: |
1216 | PushContext(combinedDir.source, combinedDir.v); |
1217 | break; |
1218 | default: |
1219 | break; |
1220 | } |
1221 | const auto &clauseList{std::get<parser::AccClauseList>(beginBlockDir.t)}; |
1222 | SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList)); |
1223 | const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)}; |
1224 | CheckAssociatedLoop(*outer); |
1225 | ClearDataSharingAttributeObjects(); |
1226 | return true; |
1227 | } |
1228 | |
1229 | static bool IsLastNameArray(const parser::Designator &designator) { |
1230 | const auto &name{GetLastName(designator)}; |
1231 | const evaluate::DataRef dataRef{*(name.symbol)}; |
1232 | return common::visit( |
1233 | common::visitors{ |
1234 | [](const evaluate::SymbolRef &ref) { |
1235 | return ref->Rank() > 0 || |
1236 | ref->GetType()->category() == DeclTypeSpec::Numeric; |
1237 | }, |
1238 | [](const evaluate::ArrayRef &aref) { |
1239 | return aref.base().IsSymbol() || |
1240 | aref.base().GetComponent().base().Rank() == 0; |
1241 | }, |
1242 | [](const auto &) { return false; }, |
1243 | }, |
1244 | dataRef.u); |
1245 | } |
1246 | |
1247 | void AccAttributeVisitor::AllowOnlyArrayAndSubArray( |
1248 | const parser::AccObjectList &objectList) { |
1249 | for (const auto &accObject : objectList.v) { |
1250 | common::visit( |
1251 | common::visitors{ |
1252 | [&](const parser::Designator &designator) { |
1253 | if (!IsLastNameArray(designator)) { |
1254 | context_.Say(designator.source, |
1255 | "Only array element or subarray are allowed in %s directive"_err_en_US, |
1256 | parser::ToUpperCaseLetters( |
1257 | llvm::acc::getOpenACCDirectiveName( |
1258 | GetContext().directive) |
1259 | .str())); |
1260 | } |
1261 | }, |
1262 | [&](const auto &name) { |
1263 | context_.Say(name.source, |
1264 | "Only array element or subarray are allowed in %s directive"_err_en_US, |
1265 | parser::ToUpperCaseLetters( |
1266 | llvm::acc::getOpenACCDirectiveName(GetContext().directive) |
1267 | .str())); |
1268 | }, |
1269 | }, |
1270 | accObject.u); |
1271 | } |
1272 | } |
1273 | |
1274 | void AccAttributeVisitor::DoNotAllowAssumedSizedArray( |
1275 | const parser::AccObjectList &objectList) { |
1276 | for (const auto &accObject : objectList.v) { |
1277 | common::visit( |
1278 | common::visitors{ |
1279 | [&](const parser::Designator &designator) { |
1280 | const auto &name{GetLastName(designator)}; |
1281 | if (name.symbol && semantics::IsAssumedSizeArray(*name.symbol)) { |
1282 | context_.Say(designator.source, |
1283 | "Assumed-size dummy arrays may not appear on the %s " |
1284 | "directive"_err_en_US, |
1285 | parser::ToUpperCaseLetters( |
1286 | llvm::acc::getOpenACCDirectiveName( |
1287 | GetContext().directive) |
1288 | .str())); |
1289 | } |
1290 | }, |
1291 | [&](const auto &name) { |
1292 | |
1293 | }, |
1294 | }, |
1295 | accObject.u); |
1296 | } |
1297 | } |
1298 | |
1299 | void AccAttributeVisitor::AllowOnlyVariable(const parser::AccObject &object) { |
1300 | common::visit( |
1301 | common::visitors{ |
1302 | [&](const parser::Designator &designator) { |
1303 | const auto &name{GetLastName(designator)}; |
1304 | if (name.symbol && !semantics::IsVariableName(*name.symbol) && |
1305 | !semantics::IsNamedConstant(*name.symbol)) { |
1306 | context_.Say(designator.source, |
1307 | "Only variables are allowed in data clauses on the %s " |
1308 | "directive"_err_en_US, |
1309 | parser::ToUpperCaseLetters( |
1310 | llvm::acc::getOpenACCDirectiveName(GetContext().directive) |
1311 | .str())); |
1312 | } |
1313 | }, |
1314 | [&](const auto &name) {}, |
1315 | }, |
1316 | object.u); |
1317 | } |
1318 | |
1319 | bool AccAttributeVisitor::Pre(const parser::OpenACCCacheConstruct &x) { |
1320 | const auto &verbatim{std::get<parser::Verbatim>(x.t)}; |
1321 | PushContext(verbatim.source, llvm::acc::Directive::ACCD_cache); |
1322 | ClearDataSharingAttributeObjects(); |
1323 | |
1324 | const auto &objectListWithModifier = |
1325 | std::get<parser::AccObjectListWithModifier>(x.t); |
1326 | const auto &objectList = |
1327 | std::get<Fortran::parser::AccObjectList>(objectListWithModifier.t); |
1328 | |
1329 | // 2.10 Cache directive restriction: A var in a cache directive must be a |
1330 | // single array element or a simple subarray. |
1331 | AllowOnlyArrayAndSubArray(objectList); |
1332 | |
1333 | return true; |
1334 | } |
1335 | |
1336 | std::int64_t AccAttributeVisitor::GetAssociatedLoopLevelFromClauses( |
1337 | const parser::AccClauseList &x) { |
1338 | std::int64_t collapseLevel{0}; |
1339 | for (const auto &clause : x.v) { |
1340 | if (const auto *collapseClause{ |
1341 | std::get_if<parser::AccClause::Collapse>(&clause.u)}) { |
1342 | const parser::AccCollapseArg &arg = collapseClause->v; |
1343 | const auto &collapseValue{std::get<parser::ScalarIntConstantExpr>(arg.t)}; |
1344 | if (const auto v{EvaluateInt64(context_, collapseValue)}) { |
1345 | collapseLevel = *v; |
1346 | } |
1347 | } |
1348 | } |
1349 | |
1350 | if (collapseLevel) { |
1351 | return collapseLevel; |
1352 | } |
1353 | return 1; // default is outermost loop |
1354 | } |
1355 | |
1356 | void AccAttributeVisitor::CheckAssociatedLoop( |
1357 | const parser::DoConstruct &outerDoConstruct) { |
1358 | std::int64_t level{GetContext().associatedLoopLevel}; |
1359 | if (level <= 0) { // collapse value was negative or 0 |
1360 | return; |
1361 | } |
1362 | |
1363 | const auto getNextDoConstruct = |
1364 | [this](const parser::Block &block, |
1365 | std::int64_t &level) -> const parser::DoConstruct * { |
1366 | for (const auto &entry : block) { |
1367 | if (const auto *doConstruct = GetDoConstructIf(entry)) { |
1368 | return doConstruct; |
1369 | } else if (parser::Unwrap<parser::CompilerDirective>(entry)) { |
1370 | // It is allowed to have a compiler directive associated with the loop. |
1371 | continue; |
1372 | } else if (const auto &accLoop{ |
1373 | parser::Unwrap<parser::OpenACCLoopConstruct>(entry)}) { |
1374 | if (level == 0) |
1375 | break; |
1376 | const auto &beginDir{ |
1377 | std::get<parser::AccBeginLoopDirective>(accLoop->t)}; |
1378 | context_.Say(beginDir.source, |
1379 | "LOOP directive not expected in COLLAPSE loop nest"_err_en_US); |
1380 | level = 0; |
1381 | } else { |
1382 | break; |
1383 | } |
1384 | } |
1385 | return nullptr; |
1386 | }; |
1387 | |
1388 | auto checkExprHasSymbols = [&](llvm::SmallVector<Symbol *> &ivs, |
1389 | semantics::UnorderedSymbolSet &symbols) { |
1390 | for (auto iv : ivs) { |
1391 | if (symbols.count(*iv) != 0) { |
1392 | context_.Say(GetContext().directiveSource, |
1393 | "Trip count must be computable and invariant"_err_en_US); |
1394 | } |
1395 | } |
1396 | }; |
1397 | |
1398 | Symbol::Flag flag = Symbol::Flag::AccPrivate; |
1399 | llvm::SmallVector<Symbol *> ivs; |
1400 | using Bounds = parser::LoopControl::Bounds; |
1401 | for (const parser::DoConstruct *loop{&outerDoConstruct}; loop && level > 0;) { |
1402 | // Go through all nested loops to ensure index variable exists. |
1403 | if (const parser::Name * ivName{GetLoopIndex(*loop)}) { |
1404 | if (auto *symbol{ResolveAcc(*ivName, flag, currScope())}) { |
1405 | if (auto &control{loop->GetLoopControl()}) { |
1406 | if (const Bounds * b{std::get_if<Bounds>(&control->u)}) { |
1407 | if (auto lowerExpr{semantics::AnalyzeExpr(context_, b->lower)}) { |
1408 | semantics::UnorderedSymbolSet lowerSyms = |
1409 | evaluate::CollectSymbols(*lowerExpr); |
1410 | checkExprHasSymbols(ivs, lowerSyms); |
1411 | } |
1412 | if (auto upperExpr{semantics::AnalyzeExpr(context_, b->upper)}) { |
1413 | semantics::UnorderedSymbolSet upperSyms = |
1414 | evaluate::CollectSymbols(*upperExpr); |
1415 | checkExprHasSymbols(ivs, upperSyms); |
1416 | } |
1417 | } |
1418 | } |
1419 | ivs.push_back(symbol); |
1420 | } |
1421 | } |
1422 | |
1423 | const auto &block{std::get<parser::Block>(loop->t)}; |
1424 | --level; |
1425 | loop = getNextDoConstruct(block, level); |
1426 | } |
1427 | CHECK(level == 0); |
1428 | } |
1429 | |
1430 | void AccAttributeVisitor::EnsureAllocatableOrPointer( |
1431 | const llvm::acc::Clause clause, const parser::AccObjectList &objectList) { |
1432 | for (const auto &accObject : objectList.v) { |
1433 | common::visit( |
1434 | common::visitors{ |
1435 | [&](const parser::Designator &designator) { |
1436 | const auto &lastName{GetLastName(designator)}; |
1437 | if (!IsAllocatableOrObjectPointer(lastName.symbol)) { |
1438 | context_.Say(designator.source, |
1439 | "Argument `%s` on the %s clause must be a variable or " |
1440 | "array with the POINTER or ALLOCATABLE attribute"_err_en_US, |
1441 | lastName.symbol->name(), |
1442 | parser::ToUpperCaseLetters( |
1443 | llvm::acc::getOpenACCClauseName(clause).str())); |
1444 | } |
1445 | }, |
1446 | [&](const auto &name) { |
1447 | context_.Say(name.source, |
1448 | "Argument on the %s clause must be a variable or " |
1449 | "array with the POINTER or ALLOCATABLE attribute"_err_en_US, |
1450 | parser::ToUpperCaseLetters( |
1451 | llvm::acc::getOpenACCClauseName(clause).str())); |
1452 | }, |
1453 | }, |
1454 | accObject.u); |
1455 | } |
1456 | } |
1457 | |
1458 | bool AccAttributeVisitor::Pre(const parser::AccClause::Attach &x) { |
1459 | // Restriction - line 1708-1709 |
1460 | EnsureAllocatableOrPointer(llvm::acc::Clause::ACCC_attach, x.v); |
1461 | return true; |
1462 | } |
1463 | |
1464 | bool AccAttributeVisitor::Pre(const parser::AccClause::Detach &x) { |
1465 | // Restriction - line 1715-1717 |
1466 | EnsureAllocatableOrPointer(llvm::acc::Clause::ACCC_detach, x.v); |
1467 | return true; |
1468 | } |
1469 | |
1470 | void AccAttributeVisitor::Post(const parser::AccDefaultClause &x) { |
1471 | if (!dirContext_.empty()) { |
1472 | switch (x.v) { |
1473 | case llvm::acc::DefaultValue::ACC_Default_present: |
1474 | SetContextDefaultDSA(Symbol::Flag::AccPresent); |
1475 | break; |
1476 | case llvm::acc::DefaultValue::ACC_Default_none: |
1477 | SetContextDefaultDSA(Symbol::Flag::AccNone); |
1478 | break; |
1479 | } |
1480 | } |
1481 | } |
1482 | |
1483 | // For OpenACC constructs, check all the data-refs within the constructs |
1484 | // and adjust the symbol for each Name if necessary |
1485 | void AccAttributeVisitor::Post(const parser::Name &name) { |
1486 | auto *symbol{name.symbol}; |
1487 | if (symbol && !dirContext_.empty() && GetContext().withinConstruct) { |
1488 | if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() && |
1489 | !symbol->has<SubprogramDetails>() && !IsObjectWithDSA(*symbol)) { |
1490 | if (Symbol * found{currScope().FindSymbol(name.source)}) { |
1491 | if (symbol != found) { |
1492 | name.symbol = found; // adjust the symbol within region |
1493 | } else if (GetContext().defaultDSA == Symbol::Flag::AccNone) { |
1494 | // 2.5.14. |
1495 | context_.Say(name.source, |
1496 | "The DEFAULT(NONE) clause requires that '%s' must be listed in " |
1497 | "a data-mapping clause"_err_en_US, |
1498 | symbol->name()); |
1499 | } |
1500 | } |
1501 | } |
1502 | } // within OpenACC construct |
1503 | } |
1504 | |
1505 | Symbol *AccAttributeVisitor::ResolveAccCommonBlockName( |
1506 | const parser::Name *name) { |
1507 | if (auto *prev{name |
1508 | ? GetContext().scope.parent().FindCommonBlock(name->source) |
1509 | : nullptr}) { |
1510 | name->symbol = prev; |
1511 | return prev; |
1512 | } |
1513 | // Check if the Common Block is declared in the current scope |
1514 | if (auto *commonBlockSymbol{ |
1515 | name ? GetContext().scope.FindCommonBlock(name->source) : nullptr}) { |
1516 | name->symbol = commonBlockSymbol; |
1517 | return commonBlockSymbol; |
1518 | } |
1519 | return nullptr; |
1520 | } |
1521 | |
1522 | void AccAttributeVisitor::ResolveAccObjectList( |
1523 | const parser::AccObjectList &accObjectList, Symbol::Flag accFlag) { |
1524 | for (const auto &accObject : accObjectList.v) { |
1525 | AllowOnlyVariable(accObject); |
1526 | ResolveAccObject(accObject, accFlag); |
1527 | } |
1528 | } |
1529 | |
1530 | void AccAttributeVisitor::ResolveAccObject( |
1531 | const parser::AccObject &accObject, Symbol::Flag accFlag) { |
1532 | common::visit( |
1533 | common::visitors{ |
1534 | [&](const parser::Designator &designator) { |
1535 | if (const auto *name{ |
1536 | semantics::getDesignatorNameIfDataRef(designator)}) { |
1537 | if (auto *symbol{ResolveAcc(*name, accFlag, currScope())}) { |
1538 | AddToContextObjectWithDSA(*symbol, accFlag); |
1539 | if (dataSharingAttributeFlags.test(accFlag)) { |
1540 | CheckMultipleAppearances(*name, *symbol, accFlag); |
1541 | } |
1542 | } |
1543 | } else { |
1544 | // Array sections to be changed to substrings as needed |
1545 | if (AnalyzeExpr(context_, designator)) { |
1546 | if (std::holds_alternative<parser::Substring>(designator.u)) { |
1547 | context_.Say(designator.source, |
1548 | "Substrings are not allowed on OpenACC " |
1549 | "directives or clauses"_err_en_US); |
1550 | } |
1551 | } |
1552 | // other checks, more TBD |
1553 | } |
1554 | }, |
1555 | [&](const parser::Name &name) { // common block |
1556 | if (auto *symbol{ResolveAccCommonBlockName(&name)}) { |
1557 | CheckMultipleAppearances( |
1558 | name, *symbol, Symbol::Flag::AccCommonBlock); |
1559 | for (auto &object : symbol->get<CommonBlockDetails>().objects()) { |
1560 | if (auto *resolvedObject{ |
1561 | ResolveAcc(*object, accFlag, currScope())}) { |
1562 | AddToContextObjectWithDSA(*resolvedObject, accFlag); |
1563 | } |
1564 | } |
1565 | } else { |
1566 | context_.Say(name.source, |
1567 | "COMMON block must be declared in the same scoping unit " |
1568 | "in which the OpenACC directive or clause appears"_err_en_US); |
1569 | } |
1570 | }, |
1571 | }, |
1572 | accObject.u); |
1573 | } |
1574 | |
1575 | Symbol *AccAttributeVisitor::ResolveAcc( |
1576 | const parser::Name &name, Symbol::Flag accFlag, Scope &scope) { |
1577 | return DeclareOrMarkOtherAccessEntity(name, accFlag); |
1578 | } |
1579 | |
1580 | Symbol *AccAttributeVisitor::ResolveAcc( |
1581 | Symbol &symbol, Symbol::Flag accFlag, Scope &scope) { |
1582 | return DeclareOrMarkOtherAccessEntity(symbol, accFlag); |
1583 | } |
1584 | |
1585 | Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity( |
1586 | const parser::Name &name, Symbol::Flag accFlag) { |
1587 | Symbol *prev{currScope().FindSymbol(name.source)}; |
1588 | if (!name.symbol || !prev) { |
1589 | return nullptr; |
1590 | } else if (prev != name.symbol) { |
1591 | name.symbol = prev; |
1592 | } |
1593 | return DeclareOrMarkOtherAccessEntity(*prev, accFlag); |
1594 | } |
1595 | |
1596 | Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity( |
1597 | Symbol &object, Symbol::Flag accFlag) { |
1598 | if (accFlagsRequireMark.test(accFlag)) { |
1599 | if (GetContext().directive == llvm::acc::ACCD_declare) { |
1600 | object.set(Symbol::Flag::AccDeclare); |
1601 | object.set(accFlag); |
1602 | } |
1603 | } |
1604 | return &object; |
1605 | } |
1606 | |
1607 | static bool WithMultipleAppearancesAccException( |
1608 | const Symbol &symbol, Symbol::Flag flag) { |
1609 | return false; // Place holder |
1610 | } |
1611 | |
1612 | void AccAttributeVisitor::CheckMultipleAppearances( |
1613 | const parser::Name &name, const Symbol &symbol, Symbol::Flag accFlag) { |
1614 | const auto *target{&symbol}; |
1615 | if (HasDataSharingAttributeObject(*target) && |
1616 | !WithMultipleAppearancesAccException(symbol, accFlag)) { |
1617 | context_.Say(name.source, |
1618 | "'%s' appears in more than one data-sharing clause " |
1619 | "on the same OpenACC directive"_err_en_US, |
1620 | name.ToString()); |
1621 | } else { |
1622 | AddDataSharingAttributeObject(*target); |
1623 | } |
1624 | } |
1625 | |
1626 | #ifndef NDEBUG |
1627 | |
1628 | #define DEBUG_TYPE "omp" |
1629 | |
1630 | static llvm::raw_ostream &operator<<( |
1631 | llvm::raw_ostream &os, const Symbol::Flags &flags); |
1632 | |
1633 | namespace dbg { |
1634 | static void DumpAssocSymbols(llvm::raw_ostream &os, const Symbol &sym); |
1635 | static std::string ScopeSourcePos(const Fortran::semantics::Scope &scope); |
1636 | } // namespace dbg |
1637 | |
1638 | #endif |
1639 | |
1640 | bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) { |
1641 | const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)}; |
1642 | const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; |
1643 | switch (beginDir.v) { |
1644 | case llvm::omp::Directive::OMPD_masked: |
1645 | case llvm::omp::Directive::OMPD_parallel_masked: |
1646 | case llvm::omp::Directive::OMPD_master: |
1647 | case llvm::omp::Directive::OMPD_parallel_master: |
1648 | case llvm::omp::Directive::OMPD_ordered: |
1649 | case llvm::omp::Directive::OMPD_parallel: |
1650 | case llvm::omp::Directive::OMPD_scope: |
1651 | case llvm::omp::Directive::OMPD_single: |
1652 | case llvm::omp::Directive::OMPD_target: |
1653 | case llvm::omp::Directive::OMPD_target_data: |
1654 | case llvm::omp::Directive::OMPD_task: |
1655 | case llvm::omp::Directive::OMPD_taskgroup: |
1656 | case llvm::omp::Directive::OMPD_teams: |
1657 | case llvm::omp::Directive::OMPD_workshare: |
1658 | case llvm::omp::Directive::OMPD_parallel_workshare: |
1659 | case llvm::omp::Directive::OMPD_target_teams: |
1660 | case llvm::omp::Directive::OMPD_target_parallel: |
1661 | PushContext(beginDir.source, beginDir.v); |
1662 | break; |
1663 | default: |
1664 | // TODO others |
1665 | break; |
1666 | } |
1667 | if (beginDir.v == llvm::omp::Directive::OMPD_master || |
1668 | beginDir.v == llvm::omp::Directive::OMPD_parallel_master) |
1669 | IssueNonConformanceWarning(beginDir.v, beginDir.source); |
1670 | ClearDataSharingAttributeObjects(); |
1671 | ClearPrivateDataSharingAttributeObjects(); |
1672 | ClearAllocateNames(); |
1673 | return true; |
1674 | } |
1675 | |
1676 | void OmpAttributeVisitor::Post(const parser::OpenMPBlockConstruct &x) { |
1677 | const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)}; |
1678 | const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; |
1679 | switch (beginDir.v) { |
1680 | case llvm::omp::Directive::OMPD_masked: |
1681 | case llvm::omp::Directive::OMPD_master: |
1682 | case llvm::omp::Directive::OMPD_parallel_masked: |
1683 | case llvm::omp::Directive::OMPD_parallel_master: |
1684 | case llvm::omp::Directive::OMPD_parallel: |
1685 | case llvm::omp::Directive::OMPD_scope: |
1686 | case llvm::omp::Directive::OMPD_single: |
1687 | case llvm::omp::Directive::OMPD_target: |
1688 | case llvm::omp::Directive::OMPD_task: |
1689 | case llvm::omp::Directive::OMPD_teams: |
1690 | case llvm::omp::Directive::OMPD_parallel_workshare: |
1691 | case llvm::omp::Directive::OMPD_target_teams: |
1692 | case llvm::omp::Directive::OMPD_target_parallel: { |
1693 | bool hasPrivate; |
1694 | for (const auto *allocName : allocateNames_) { |
1695 | hasPrivate = false; |
1696 | for (auto privateObj : privateDataSharingAttributeObjects_) { |
1697 | const Symbol &symbolPrivate{*privateObj}; |
1698 | if (allocName->source == symbolPrivate.name()) { |
1699 | hasPrivate = true; |
1700 | break; |
1701 | } |
1702 | } |
1703 | if (!hasPrivate) { |
1704 | context_.Say(allocName->source, |
1705 | "The ALLOCATE clause requires that '%s' must be listed in a " |
1706 | "private " |
1707 | "data-sharing attribute clause on the same directive"_err_en_US, |
1708 | allocName->ToString()); |
1709 | } |
1710 | } |
1711 | break; |
1712 | } |
1713 | default: |
1714 | break; |
1715 | } |
1716 | PopContext(); |
1717 | } |
1718 | |
1719 | bool OmpAttributeVisitor::Pre( |
1720 | const parser::OpenMPSimpleStandaloneConstruct &x) { |
1721 | const auto &standaloneDir{std::get<parser::OmpDirectiveName>(x.v.t)}; |
1722 | switch (standaloneDir.v) { |
1723 | case llvm::omp::Directive::OMPD_barrier: |
1724 | case llvm::omp::Directive::OMPD_ordered: |
1725 | case llvm::omp::Directive::OMPD_scan: |
1726 | case llvm::omp::Directive::OMPD_target_enter_data: |
1727 | case llvm::omp::Directive::OMPD_target_exit_data: |
1728 | case llvm::omp::Directive::OMPD_target_update: |
1729 | case llvm::omp::Directive::OMPD_taskwait: |
1730 | case llvm::omp::Directive::OMPD_taskyield: |
1731 | PushContext(standaloneDir.source, standaloneDir.v); |
1732 | break; |
1733 | default: |
1734 | break; |
1735 | } |
1736 | ClearDataSharingAttributeObjects(); |
1737 | return true; |
1738 | } |
1739 | |
1740 | bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) { |
1741 | const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; |
1742 | const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; |
1743 | const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)}; |
1744 | switch (beginDir.v) { |
1745 | case llvm::omp::Directive::OMPD_distribute: |
1746 | case llvm::omp::Directive::OMPD_distribute_parallel_do: |
1747 | case llvm::omp::Directive::OMPD_distribute_parallel_do_simd: |
1748 | case llvm::omp::Directive::OMPD_distribute_simd: |
1749 | case llvm::omp::Directive::OMPD_do: |
1750 | case llvm::omp::Directive::OMPD_do_simd: |
1751 | case llvm::omp::Directive::OMPD_loop: |
1752 | case llvm::omp::Directive::OMPD_masked_taskloop_simd: |
1753 | case llvm::omp::Directive::OMPD_masked_taskloop: |
1754 | case llvm::omp::Directive::OMPD_master_taskloop_simd: |
1755 | case llvm::omp::Directive::OMPD_master_taskloop: |
1756 | case llvm::omp::Directive::OMPD_parallel_do: |
1757 | case llvm::omp::Directive::OMPD_parallel_do_simd: |
1758 | case llvm::omp::Directive::OMPD_parallel_masked_taskloop_simd: |
1759 | case llvm::omp::Directive::OMPD_parallel_masked_taskloop: |
1760 | case llvm::omp::Directive::OMPD_parallel_master_taskloop_simd: |
1761 | case llvm::omp::Directive::OMPD_parallel_master_taskloop: |
1762 | case llvm::omp::Directive::OMPD_simd: |
1763 | case llvm::omp::Directive::OMPD_target_loop: |
1764 | case llvm::omp::Directive::OMPD_target_parallel_do: |
1765 | case llvm::omp::Directive::OMPD_target_parallel_do_simd: |
1766 | case llvm::omp::Directive::OMPD_target_parallel_loop: |
1767 | case llvm::omp::Directive::OMPD_target_teams_distribute: |
1768 | case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do: |
1769 | case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd: |
1770 | case llvm::omp::Directive::OMPD_target_teams_distribute_simd: |
1771 | case llvm::omp::Directive::OMPD_target_teams_loop: |
1772 | case llvm::omp::Directive::OMPD_target_simd: |
1773 | case llvm::omp::Directive::OMPD_taskloop: |
1774 | case llvm::omp::Directive::OMPD_taskloop_simd: |
1775 | case llvm::omp::Directive::OMPD_teams_distribute: |
1776 | case llvm::omp::Directive::OMPD_teams_distribute_parallel_do: |
1777 | case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd: |
1778 | case llvm::omp::Directive::OMPD_teams_distribute_simd: |
1779 | case llvm::omp::Directive::OMPD_teams_loop: |
1780 | case llvm::omp::Directive::OMPD_tile: |
1781 | case llvm::omp::Directive::OMPD_unroll: |
1782 | PushContext(beginDir.source, beginDir.v); |
1783 | break; |
1784 | default: |
1785 | break; |
1786 | } |
1787 | if (beginDir.v == llvm::omp::OMPD_master_taskloop || |
1788 | beginDir.v == llvm::omp::OMPD_master_taskloop_simd || |
1789 | beginDir.v == llvm::omp::OMPD_parallel_master_taskloop || |
1790 | beginDir.v == llvm::omp::OMPD_parallel_master_taskloop_simd || |
1791 | beginDir.v == llvm::omp::Directive::OMPD_target_loop) |
1792 | IssueNonConformanceWarning(beginDir.v, beginDir.source); |
1793 | ClearDataSharingAttributeObjects(); |
1794 | SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList)); |
1795 | |
1796 | if (beginDir.v == llvm::omp::Directive::OMPD_do) { |
1797 | if (const auto &doConstruct{ |
1798 | std::get<std::optional<parser::DoConstruct>>(x.t)}) { |
1799 | if (doConstruct.value().IsDoWhile()) { |
1800 | return true; |
1801 | } |
1802 | } |
1803 | } |
1804 | PrivatizeAssociatedLoopIndexAndCheckLoopLevel(x); |
1805 | ordCollapseLevel = GetAssociatedLoopLevelFromClauses(clauseList) + 1; |
1806 | return true; |
1807 | } |
1808 | |
1809 | void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct( |
1810 | const parser::Name &iv) { |
1811 | // Find the parallel or task generating construct enclosing the |
1812 | // sequential loop. |
1813 | auto targetIt{dirContext_.rbegin()}; |
1814 | for (;; ++targetIt) { |
1815 | if (targetIt == dirContext_.rend()) { |
1816 | return; |
1817 | } |
1818 | if (llvm::omp::allParallelSet.test(targetIt->directive) || |
1819 | llvm::omp::taskGeneratingSet.test(targetIt->directive)) { |
1820 | break; |
1821 | } |
1822 | } |
1823 | // If this symbol already has a data-sharing attribute then there is nothing |
1824 | // to do here. |
1825 | if (const Symbol * symbol{iv.symbol}) { |
1826 | for (auto symMap : targetIt->objectWithDSA) { |
1827 | if (symMap.first->name() == symbol->name()) { |
1828 | return; |
1829 | } |
1830 | } |
1831 | } |
1832 | // If this symbol already has an explicit data-sharing attribute in the |
1833 | // enclosing OpenMP parallel or task then there is nothing to do here. |
1834 | if (auto *symbol{targetIt->scope.FindSymbol(iv.source)}) { |
1835 | if (symbol->owner() == targetIt->scope) { |
1836 | if (symbol->test(Symbol::Flag::OmpExplicit) && |
1837 | (symbol->flags() & dataSharingAttributeFlags).any()) { |
1838 | return; |
1839 | } |
1840 | } |
1841 | } |
1842 | // Otherwise find the symbol and make it Private for the entire enclosing |
1843 | // parallel or task |
1844 | if (auto *symbol{ResolveOmp(iv, Symbol::Flag::OmpPrivate, targetIt->scope)}) { |
1845 | targetIt++; |
1846 | SetSymbolDSA( |
1847 | *symbol, {Symbol::Flag::OmpPreDetermined, Symbol::Flag::OmpPrivate}); |
1848 | iv.symbol = symbol; // adjust the symbol within region |
1849 | for (auto it{dirContext_.rbegin()}; it != targetIt; ++it) { |
1850 | AddToContextObjectWithDSA(*symbol, Symbol::Flag::OmpPrivate, *it); |
1851 | } |
1852 | } |
1853 | } |
1854 | |
1855 | // [OMP-4.5]2.15.1.1 Data-sharing Attribute Rules - Predetermined |
1856 | // - A loop iteration variable for a sequential loop in a parallel |
1857 | // or task generating construct is private in the innermost such |
1858 | // construct that encloses the loop |
1859 | // Loop iteration variables are not well defined for DO WHILE loop. |
1860 | // Use of DO CONCURRENT inside OpenMP construct is unspecified behavior |
1861 | // till OpenMP-5.0 standard. |
1862 | // In above both cases we skip the privatization of iteration variables. |
1863 | bool OmpAttributeVisitor::Pre(const parser::DoConstruct &x) { |
1864 | if (!dirContext_.empty() && GetContext().withinConstruct) { |
1865 | llvm::SmallVector<const parser::Name *> ivs; |
1866 | if (x.IsDoNormal()) { |
1867 | const parser::Name *iv{GetLoopIndex(x)}; |
1868 | if (iv && iv->symbol) |
1869 | ivs.push_back(Elt: iv); |
1870 | } |
1871 | ordCollapseLevel--; |
1872 | for (auto iv : ivs) { |
1873 | if (!iv->symbol->test(Symbol::Flag::OmpPreDetermined)) { |
1874 | ResolveSeqLoopIndexInParallelOrTaskConstruct(iv: *iv); |
1875 | } else { |
1876 | // TODO: conflict checks with explicitly determined DSA |
1877 | } |
1878 | if (ordCollapseLevel) { |
1879 | if (const auto *details{iv->symbol->detailsIf<HostAssocDetails>()}) { |
1880 | const Symbol *tpSymbol = &details->symbol(); |
1881 | if (tpSymbol->test(Symbol::Flag::OmpThreadprivate)) { |
1882 | context_.Say(iv->source, |
1883 | "Loop iteration variable %s is not allowed in THREADPRIVATE."_err_en_US, |
1884 | iv->ToString()); |
1885 | } |
1886 | } |
1887 | } |
1888 | } |
1889 | } |
1890 | return true; |
1891 | } |
1892 | |
1893 | std::int64_t OmpAttributeVisitor::GetAssociatedLoopLevelFromClauses( |
1894 | const parser::OmpClauseList &x) { |
1895 | std::int64_t orderedLevel{0}; |
1896 | std::int64_t collapseLevel{0}; |
1897 | |
1898 | const parser::OmpClause *ordClause{nullptr}; |
1899 | const parser::OmpClause *collClause{nullptr}; |
1900 | |
1901 | for (const auto &clause : x.v) { |
1902 | if (const auto *orderedClause{ |
1903 | std::get_if<parser::OmpClause::Ordered>(&clause.u)}) { |
1904 | if (const auto v{EvaluateInt64(context_, orderedClause->v)}) { |
1905 | orderedLevel = *v; |
1906 | } |
1907 | ordClause = &clause; |
1908 | } |
1909 | if (const auto *collapseClause{ |
1910 | std::get_if<parser::OmpClause::Collapse>(&clause.u)}) { |
1911 | if (const auto v{EvaluateInt64(context_, collapseClause->v)}) { |
1912 | collapseLevel = *v; |
1913 | } |
1914 | collClause = &clause; |
1915 | } |
1916 | } |
1917 | |
1918 | if (orderedLevel && (!collapseLevel || orderedLevel >= collapseLevel)) { |
1919 | SetAssociatedClause(*ordClause); |
1920 | return orderedLevel; |
1921 | } else if (!orderedLevel && collapseLevel) { |
1922 | SetAssociatedClause(*collClause); |
1923 | return collapseLevel; |
1924 | } // orderedLevel < collapseLevel is an error handled in structural checks |
1925 | return 1; // default is outermost loop |
1926 | } |
1927 | |
1928 | // 2.15.1.1 Data-sharing Attribute Rules - Predetermined |
1929 | // - The loop iteration variable(s) in the associated do-loop(s) of a do, |
1930 | // parallel do, taskloop, or distribute construct is (are) private. |
1931 | // - The loop iteration variable in the associated do-loop of a simd construct |
1932 | // with just one associated do-loop is linear with a linear-step that is the |
1933 | // increment of the associated do-loop. |
1934 | // - The loop iteration variables in the associated do-loops of a simd |
1935 | // construct with multiple associated do-loops are lastprivate. |
1936 | void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel( |
1937 | const parser::OpenMPLoopConstruct &x) { |
1938 | unsigned version{context_.langOptions().OpenMPVersion}; |
1939 | std::int64_t level{GetContext().associatedLoopLevel}; |
1940 | if (level <= 0) { |
1941 | return; |
1942 | } |
1943 | Symbol::Flag ivDSA; |
1944 | if (!llvm::omp::allSimdSet.test(GetContext().directive)) { |
1945 | ivDSA = Symbol::Flag::OmpPrivate; |
1946 | } else if (level == 1) { |
1947 | ivDSA = Symbol::Flag::OmpLinear; |
1948 | } else { |
1949 | ivDSA = Symbol::Flag::OmpLastPrivate; |
1950 | } |
1951 | |
1952 | const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)}; |
1953 | if (outer.has_value()) { |
1954 | for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) { |
1955 | // go through all the nested do-loops and resolve index variables |
1956 | const parser::Name *iv{GetLoopIndex(*loop)}; |
1957 | if (iv) { |
1958 | if (auto *symbol{ResolveOmp(*iv, ivDSA, currScope())}) { |
1959 | SetSymbolDSA(*symbol, {Symbol::Flag::OmpPreDetermined, ivDSA}); |
1960 | iv->symbol = symbol; // adjust the symbol within region |
1961 | AddToContextObjectWithDSA(*symbol, ivDSA); |
1962 | } |
1963 | |
1964 | const auto &block{std::get<parser::Block>(loop->t)}; |
1965 | const auto it{block.begin()}; |
1966 | loop = it != block.end() ? GetDoConstructIf(*it) : nullptr; |
1967 | } |
1968 | } |
1969 | CheckAssocLoopLevel(level, GetAssociatedClause()); |
1970 | } else { |
1971 | context_.Say(GetContext().directiveSource, |
1972 | "A DO loop must follow the %s directive"_err_en_US, |
1973 | parser::ToUpperCaseLetters( |
1974 | llvm::omp::getOpenMPDirectiveName(GetContext().directive, version) |
1975 | .str())); |
1976 | } |
1977 | } |
1978 | void OmpAttributeVisitor::CheckAssocLoopLevel( |
1979 | std::int64_t level, const parser::OmpClause *clause) { |
1980 | if (clause && level != 0) { |
1981 | context_.Say(clause->source, |
1982 | "The value of the parameter in the COLLAPSE or ORDERED clause must" |
1983 | " not be larger than the number of nested loops" |
1984 | " following the construct."_err_en_US); |
1985 | } |
1986 | } |
1987 | |
1988 | bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct &x) { |
1989 | const auto &beginSectionsDir{ |
1990 | std::get<parser::OmpBeginSectionsDirective>(x.t)}; |
1991 | const auto &beginDir{ |
1992 | std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)}; |
1993 | switch (beginDir.v) { |
1994 | case llvm::omp::Directive::OMPD_parallel_sections: |
1995 | case llvm::omp::Directive::OMPD_sections: |
1996 | PushContext(beginDir.source, beginDir.v); |
1997 | GetContext().withinConstruct = true; |
1998 | break; |
1999 | default: |
2000 | break; |
2001 | } |
2002 | ClearDataSharingAttributeObjects(); |
2003 | return true; |
2004 | } |
2005 | |
2006 | bool OmpAttributeVisitor::Pre(const parser::OpenMPCriticalConstruct &x) { |
2007 | const auto &beginCriticalDir{std::get<parser::OmpCriticalDirective>(x.t)}; |
2008 | const auto &endCriticalDir{std::get<parser::OmpEndCriticalDirective>(x.t)}; |
2009 | PushContext(beginCriticalDir.source, llvm::omp::Directive::OMPD_critical); |
2010 | GetContext().withinConstruct = true; |
2011 | if (const auto &criticalName{ |
2012 | std::get<std::optional<parser::Name>>(beginCriticalDir.t)}) { |
2013 | ResolveOmpName(*criticalName, Symbol::Flag::OmpCriticalLock); |
2014 | } |
2015 | if (const auto &endCriticalName{ |
2016 | std::get<std::optional<parser::Name>>(endCriticalDir.t)}) { |
2017 | ResolveOmpName(*endCriticalName, Symbol::Flag::OmpCriticalLock); |
2018 | } |
2019 | return true; |
2020 | } |
2021 | |
2022 | bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclareTargetConstruct &x) { |
2023 | PushContext(x.source, llvm::omp::Directive::OMPD_declare_target); |
2024 | const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)}; |
2025 | if (const auto *objectList{parser::Unwrap<parser::OmpObjectList>(spec.u)}) { |
2026 | ResolveOmpObjectList(*objectList, Symbol::Flag::OmpDeclareTarget); |
2027 | } else if (const auto *clauseList{ |
2028 | parser::Unwrap<parser::OmpClauseList>(spec.u)}) { |
2029 | for (const auto &clause : clauseList->v) { |
2030 | if (const auto *toClause{std::get_if<parser::OmpClause::To>(&clause.u)}) { |
2031 | auto &objList{std::get<parser::OmpObjectList>(toClause->v.t)}; |
2032 | ResolveOmpObjectList(objList, Symbol::Flag::OmpDeclareTarget); |
2033 | } else if (const auto *linkClause{ |
2034 | std::get_if<parser::OmpClause::Link>(&clause.u)}) { |
2035 | ResolveOmpObjectList(linkClause->v, Symbol::Flag::OmpDeclareTarget); |
2036 | } else if (const auto *enterClause{ |
2037 | std::get_if<parser::OmpClause::Enter>(&clause.u)}) { |
2038 | ResolveOmpObjectList(enterClause->v, Symbol::Flag::OmpDeclareTarget); |
2039 | } |
2040 | } |
2041 | } |
2042 | return true; |
2043 | } |
2044 | |
2045 | bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclareMapperConstruct &x) { |
2046 | PushContext(x.source, llvm::omp::Directive::OMPD_declare_mapper); |
2047 | return true; |
2048 | } |
2049 | |
2050 | bool OmpAttributeVisitor::Pre( |
2051 | const parser::OpenMPDeclareReductionConstruct &x) { |
2052 | PushContext(x.source, llvm::omp::Directive::OMPD_declare_reduction); |
2053 | return true; |
2054 | } |
2055 | |
2056 | bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate &x) { |
2057 | PushContext(x.source, llvm::omp::Directive::OMPD_threadprivate); |
2058 | const auto &list{std::get<parser::OmpObjectList>(x.t)}; |
2059 | ResolveOmpObjectList(list, Symbol::Flag::OmpThreadprivate); |
2060 | return true; |
2061 | } |
2062 | |
2063 | bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclarativeAllocate &x) { |
2064 | PushContext(x.source, llvm::omp::Directive::OMPD_allocate); |
2065 | const auto &list{std::get<parser::OmpObjectList>(x.t)}; |
2066 | ResolveOmpObjectList(list, Symbol::Flag::OmpDeclarativeAllocateDirective); |
2067 | return false; |
2068 | } |
2069 | |
2070 | bool OmpAttributeVisitor::Pre(const parser::OpenMPDispatchConstruct &x) { |
2071 | PushContext(x.source, llvm::omp::Directive::OMPD_dispatch); |
2072 | return true; |
2073 | } |
2074 | |
2075 | bool OmpAttributeVisitor::Pre(const parser::OpenMPExecutableAllocate &x) { |
2076 | IssueNonConformanceWarning(llvm::omp::Directive::OMPD_allocate, x.source); |
2077 | PushContext(x.source, llvm::omp::Directive::OMPD_allocate); |
2078 | const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)}; |
2079 | if (list) { |
2080 | ResolveOmpObjectList(*list, Symbol::Flag::OmpExecutableAllocateDirective); |
2081 | } |
2082 | return true; |
2083 | } |
2084 | |
2085 | bool OmpAttributeVisitor::Pre(const parser::OpenMPAllocatorsConstruct &x) { |
2086 | PushContext(x.source, llvm::omp::Directive::OMPD_allocators); |
2087 | const auto &clauseList{std::get<parser::OmpClauseList>(x.t)}; |
2088 | for (const auto &clause : clauseList.v) { |
2089 | if (const auto *allocClause{ |
2090 | std::get_if<parser::OmpClause::Allocate>(&clause.u)}) { |
2091 | ResolveOmpObjectList(std::get<parser::OmpObjectList>(allocClause->v.t), |
2092 | Symbol::Flag::OmpExecutableAllocateDirective); |
2093 | } |
2094 | } |
2095 | return true; |
2096 | } |
2097 | |
2098 | void OmpAttributeVisitor::Post(const parser::OmpDefaultClause &x) { |
2099 | // The DEFAULT clause may also be used on METADIRECTIVE. In that case |
2100 | // there is nothing to do. |
2101 | using DataSharingAttribute = parser::OmpDefaultClause::DataSharingAttribute; |
2102 | if (auto *dsa{std::get_if<DataSharingAttribute>(&x.u)}) { |
2103 | if (!dirContext_.empty()) { |
2104 | switch (*dsa) { |
2105 | case DataSharingAttribute::Private: |
2106 | SetContextDefaultDSA(Symbol::Flag::OmpPrivate); |
2107 | break; |
2108 | case DataSharingAttribute::Firstprivate: |
2109 | SetContextDefaultDSA(Symbol::Flag::OmpFirstPrivate); |
2110 | break; |
2111 | case DataSharingAttribute::Shared: |
2112 | SetContextDefaultDSA(Symbol::Flag::OmpShared); |
2113 | break; |
2114 | case DataSharingAttribute::None: |
2115 | SetContextDefaultDSA(Symbol::Flag::OmpNone); |
2116 | break; |
2117 | } |
2118 | } |
2119 | } |
2120 | } |
2121 | |
2122 | bool OmpAttributeVisitor::IsNestedInDirective(llvm::omp::Directive directive) { |
2123 | if (dirContext_.size() >= 1) { |
2124 | for (std::size_t i = dirContext_.size() - 1; i > 0; --i) { |
2125 | if (dirContext_[i - 1].directive == directive) { |
2126 | return true; |
2127 | } |
2128 | } |
2129 | } |
2130 | return false; |
2131 | } |
2132 | |
2133 | void OmpAttributeVisitor::Post(const parser::OpenMPExecutableAllocate &x) { |
2134 | bool hasAllocator = false; |
2135 | // TODO: Investigate whether searching the clause list can be done with |
2136 | // parser::Unwrap instead of the following loop |
2137 | const auto &clauseList{std::get<parser::OmpClauseList>(x.t)}; |
2138 | for (const auto &clause : clauseList.v) { |
2139 | if (std::get_if<parser::OmpClause::Allocator>(&clause.u)) { |
2140 | hasAllocator = true; |
2141 | } |
2142 | } |
2143 | |
2144 | if (IsNestedInDirective(llvm::omp::Directive::OMPD_target) && !hasAllocator) { |
2145 | // TODO: expand this check to exclude the case when a requires |
2146 | // directive with the dynamic_allocators clause is present |
2147 | // in the same compilation unit (OMP5.0 2.11.3). |
2148 | context_.Say(x.source, |
2149 | "ALLOCATE directives that appear in a TARGET region " |
2150 | "must specify an allocator clause"_err_en_US); |
2151 | } |
2152 | |
2153 | const auto &allocateStmt = |
2154 | std::get<parser::Statement<parser::AllocateStmt>>(x.t).statement; |
2155 | if (const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)}) { |
2156 | CheckAllNamesInAllocateStmt( |
2157 | std::get<parser::Verbatim>(x.t).source, *list, allocateStmt); |
2158 | } |
2159 | if (const auto &subDirs{ |
2160 | std::get<std::optional<std::list<parser::OpenMPDeclarativeAllocate>>>( |
2161 | x.t)}) { |
2162 | for (const auto &dalloc : *subDirs) { |
2163 | CheckAllNamesInAllocateStmt(std::get<parser::Verbatim>(dalloc.t).source, |
2164 | std::get<parser::OmpObjectList>(dalloc.t), allocateStmt); |
2165 | } |
2166 | } |
2167 | PopContext(); |
2168 | } |
2169 | |
2170 | void OmpAttributeVisitor::Post(const parser::OpenMPAllocatorsConstruct &x) { |
2171 | const auto &dir{std::get<parser::Verbatim>(x.t)}; |
2172 | const auto &clauseList{std::get<parser::OmpClauseList>(x.t)}; |
2173 | for (const auto &clause : clauseList.v) { |
2174 | if (const auto *alloc{ |
2175 | std::get_if<parser::OmpClause::Allocate>(&clause.u)}) { |
2176 | CheckAllNamesInAllocateStmt(dir.source, |
2177 | std::get<parser::OmpObjectList>(alloc->v.t), |
2178 | std::get<parser::Statement<parser::AllocateStmt>>(x.t).statement); |
2179 | |
2180 | auto &modifiers{OmpGetModifiers(alloc->v)}; |
2181 | bool hasAllocator{ |
2182 | OmpGetUniqueModifier<parser::OmpAllocatorSimpleModifier>(modifiers) || |
2183 | OmpGetUniqueModifier<parser::OmpAllocatorComplexModifier>(modifiers)}; |
2184 | |
2185 | // TODO: As with allocate directive, exclude the case when a requires |
2186 | // directive with the dynamic_allocators clause is present in |
2187 | // the same compilation unit (OMP5.0 2.11.3). |
2188 | if (IsNestedInDirective(llvm::omp::Directive::OMPD_target) && |
2189 | !hasAllocator) { |
2190 | context_.Say(x.source, |
2191 | "ALLOCATORS directives that appear in a TARGET region " |
2192 | "must specify an allocator"_err_en_US); |
2193 | } |
2194 | } |
2195 | } |
2196 | PopContext(); |
2197 | } |
2198 | |
2199 | static bool IsPrivatizable(const Symbol *sym) { |
2200 | auto *misc{sym->detailsIf<MiscDetails>()}; |
2201 | return IsVariableName(*sym) && !IsProcedure(*sym) && !IsNamedConstant(*sym) && |
2202 | ( // OpenMP 5.2, 5.1.1: Assumed-size arrays are shared |
2203 | !semantics::IsAssumedSizeArray(*sym) || |
2204 | // If CrayPointer is among the DSA list then the |
2205 | // CrayPointee is Privatizable |
2206 | sym->test(Symbol::Flag::CrayPointee)) && |
2207 | !sym->owner().IsDerivedType() && |
2208 | sym->owner().kind() != Scope::Kind::ImpliedDos && |
2209 | sym->owner().kind() != Scope::Kind::Forall && |
2210 | !sym->detailsIf<semantics::AssocEntityDetails>() && |
2211 | !sym->detailsIf<semantics::NamelistDetails>() && |
2212 | (!misc || |
2213 | (misc->kind() != MiscDetails::Kind::ComplexPartRe && |
2214 | misc->kind() != MiscDetails::Kind::ComplexPartIm && |
2215 | misc->kind() != MiscDetails::Kind::KindParamInquiry && |
2216 | misc->kind() != MiscDetails::Kind::LenParamInquiry && |
2217 | misc->kind() != MiscDetails::Kind::ConstructName)); |
2218 | } |
2219 | |
2220 | static bool IsSymbolStaticStorageDuration(const Symbol &symbol) { |
2221 | LLVM_DEBUG(llvm::dbgs() << "IsSymbolStaticStorageDuration("<< symbol.name() |
2222 | << "):\n"); |
2223 | auto ultSym = symbol.GetUltimate(); |
2224 | // Module-scope variable |
2225 | return (ultSym.owner().kind() == Scope::Kind::Module) || |
2226 | // Data statement variable |
2227 | (ultSym.flags().test(Symbol::Flag::InDataStmt)) || |
2228 | // Save attribute variable |
2229 | (ultSym.attrs().test(Attr::SAVE)) || |
2230 | // Referenced in a common block |
2231 | (ultSym.flags().test(Symbol::Flag::InCommonBlock)); |
2232 | } |
2233 | |
2234 | void OmpAttributeVisitor::CreateImplicitSymbols(const Symbol *symbol) { |
2235 | if (!IsPrivatizable(symbol)) { |
2236 | return; |
2237 | } |
2238 | |
2239 | LLVM_DEBUG(llvm::dbgs() << "CreateImplicitSymbols: "<< *symbol << '\n'); |
2240 | |
2241 | // Implicitly determined DSAs |
2242 | // OMP 5.2 5.1.1 - Variables Referenced in a Construct |
2243 | Symbol *lastDeclSymbol = nullptr; |
2244 | Symbol::Flags prevDSA; |
2245 | for (int dirDepth{0}; dirDepth < (int)dirContext_.size(); ++dirDepth) { |
2246 | DirContext &dirContext = dirContext_[dirDepth]; |
2247 | Symbol::Flags dsa; |
2248 | |
2249 | Scope &scope{context_.FindScope(dirContext.directiveSource)}; |
2250 | auto it{scope.find(symbol->name())}; |
2251 | if (it != scope.end()) { |
2252 | // There is already a symbol in the current scope, use its DSA. |
2253 | dsa = GetSymbolDSA(*it->second); |
2254 | } else { |
2255 | for (auto symMap : dirContext.objectWithDSA) { |
2256 | if (symMap.first->name() == symbol->name()) { |
2257 | // `symbol` already has a data-sharing attribute in the current |
2258 | // context, use it. |
2259 | dsa.set(symMap.second); |
2260 | break; |
2261 | } |
2262 | } |
2263 | } |
2264 | |
2265 | // When handling each implicit rule for a given symbol, one of the |
2266 | // following actions may be taken: |
2267 | // 1. Declare a new private or shared symbol. |
2268 | // 2. Use the last declared symbol, by inserting a new symbol in the |
2269 | // scope being processed, associated with it. |
2270 | // If no symbol was declared previously, then no association is needed |
2271 | // and the symbol from the enclosing scope will be inherited by the |
2272 | // current one. |
2273 | // |
2274 | // Because of how symbols are collected in lowering, not inserting a new |
2275 | // symbol in the second case could lead to the conclusion that a symbol |
2276 | // from an enclosing construct was declared in the current construct, |
2277 | // which would result in wrong privatization code being generated. |
2278 | // Consider the following example: |
2279 | // |
2280 | // !$omp parallel default(private) ! p1 |
2281 | // !$omp parallel default(private) shared(x) ! p2 |
2282 | // x = 10 |
2283 | // !$omp end parallel |
2284 | // !$omp end parallel |
2285 | // |
2286 | // If a new x symbol was not inserted in the inner parallel construct |
2287 | // (p2), it would use the x symbol definition from the enclosing scope. |
2288 | // Then, when p2's default symbols were collected in lowering, the x |
2289 | // symbol from the outer parallel construct (p1) would be collected, as |
2290 | // it would have the private flag set. |
2291 | // This would make x appear to be defined in p2, causing it to be |
2292 | // privatized in p2 and its privatization in p1 to be skipped. |
2293 | auto makeSymbol = [&](Symbol::Flags flags) { |
2294 | const Symbol *hostSymbol = |
2295 | lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate(); |
2296 | assert(flags.LeastElement()); |
2297 | Symbol::Flag flag = *flags.LeastElement(); |
2298 | lastDeclSymbol = DeclareNewAccessEntity( |
2299 | *hostSymbol, flag, context_.FindScope(dirContext.directiveSource)); |
2300 | lastDeclSymbol->flags() |= flags; |
2301 | return lastDeclSymbol; |
2302 | }; |
2303 | auto useLastDeclSymbol = [&]() { |
2304 | if (lastDeclSymbol) { |
2305 | const Symbol *hostSymbol = |
2306 | lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate(); |
2307 | MakeAssocSymbol(symbol->name(), *hostSymbol, |
2308 | context_.FindScope(dirContext.directiveSource)); |
2309 | } |
2310 | }; |
2311 | |
2312 | #ifndef NDEBUG |
2313 | auto printImplicitRule = [&](const char *id) { |
2314 | LLVM_DEBUG(llvm::dbgs() << "\t"<< id << ": dsa: "<< dsa << '\n'); |
2315 | LLVM_DEBUG( |
2316 | llvm::dbgs() << "\t\tScope: "<< dbg::ScopeSourcePos(scope) << '\n'); |
2317 | }; |
2318 | #define PRINT_IMPLICIT_RULE(id) printImplicitRule(id) |
2319 | #else |
2320 | #define PRINT_IMPLICIT_RULE(id) |
2321 | #endif |
2322 | |
2323 | bool taskGenDir = llvm::omp::taskGeneratingSet.test(dirContext.directive); |
2324 | bool targetDir = llvm::omp::allTargetSet.test(dirContext.directive); |
2325 | bool parallelDir = llvm::omp::allParallelSet.test(dirContext.directive); |
2326 | bool teamsDir = llvm::omp::allTeamsSet.test(dirContext.directive); |
2327 | bool isStaticStorageDuration = IsSymbolStaticStorageDuration(*symbol); |
2328 | |
2329 | if (dsa.any()) { |
2330 | if (parallelDir || taskGenDir || teamsDir) { |
2331 | Symbol *prevDeclSymbol{lastDeclSymbol}; |
2332 | // NOTE As `dsa` will match that of the symbol in the current scope |
2333 | // (if any), we won't override the DSA of any existing symbol. |
2334 | if ((dsa & dataSharingAttributeFlags).any()) { |
2335 | makeSymbol(dsa); |
2336 | } |
2337 | // Fix host association of explicit symbols, as they can be created |
2338 | // before implicit ones in enclosing scope. |
2339 | if (prevDeclSymbol && prevDeclSymbol != lastDeclSymbol && |
2340 | lastDeclSymbol->test(Symbol::Flag::OmpExplicit)) { |
2341 | const auto *hostAssoc{lastDeclSymbol->detailsIf<HostAssocDetails>()}; |
2342 | if (hostAssoc && hostAssoc->symbol() != *prevDeclSymbol) { |
2343 | lastDeclSymbol->set_details(HostAssocDetails{*prevDeclSymbol}); |
2344 | } |
2345 | } |
2346 | } |
2347 | prevDSA = dsa; |
2348 | PRINT_IMPLICIT_RULE("0) already has DSA"); |
2349 | continue; |
2350 | } |
2351 | |
2352 | // NOTE Because of how lowering uses OmpImplicit flag, we can only set it |
2353 | // for symbols with private DSA. |
2354 | // Also, as the default clause is handled separately in lowering, |
2355 | // don't mark its symbols with OmpImplicit either. |
2356 | // Ideally, lowering should be changed and all implicit symbols |
2357 | // should be marked with OmpImplicit. |
2358 | |
2359 | if (dirContext.defaultDSA == Symbol::Flag::OmpPrivate || |
2360 | dirContext.defaultDSA == Symbol::Flag::OmpFirstPrivate || |
2361 | dirContext.defaultDSA == Symbol::Flag::OmpShared) { |
2362 | // 1) default |
2363 | // Allowed only with parallel, teams and task generating constructs. |
2364 | if (!parallelDir && !taskGenDir && !teamsDir) { |
2365 | return; |
2366 | } |
2367 | dsa = {dirContext.defaultDSA}; |
2368 | makeSymbol(dsa); |
2369 | PRINT_IMPLICIT_RULE("1) default"); |
2370 | } else if (parallelDir) { |
2371 | // 2) parallel -> shared |
2372 | dsa = {Symbol::Flag::OmpShared}; |
2373 | makeSymbol(dsa); |
2374 | PRINT_IMPLICIT_RULE("2) parallel"); |
2375 | } else if (!taskGenDir && !targetDir) { |
2376 | // 3) enclosing context |
2377 | dsa = prevDSA; |
2378 | useLastDeclSymbol(); |
2379 | PRINT_IMPLICIT_RULE("3) enclosing context"); |
2380 | } else if (targetDir) { |
2381 | // TODO 4) not mapped target variable -> firstprivate |
2382 | dsa = prevDSA; |
2383 | } else if (taskGenDir) { |
2384 | // TODO 5) dummy arg in orphaned taskgen construct -> firstprivate |
2385 | if (prevDSA.test(Symbol::Flag::OmpShared) || |
2386 | (isStaticStorageDuration && |
2387 | (prevDSA & dataSharingAttributeFlags).none())) { |
2388 | // 6) shared in enclosing context -> shared |
2389 | dsa = {Symbol::Flag::OmpShared}; |
2390 | makeSymbol(dsa); |
2391 | PRINT_IMPLICIT_RULE("6) taskgen: shared"); |
2392 | } else { |
2393 | // 7) firstprivate |
2394 | dsa = {Symbol::Flag::OmpFirstPrivate}; |
2395 | makeSymbol(dsa)->set(Symbol::Flag::OmpImplicit); |
2396 | PRINT_IMPLICIT_RULE("7) taskgen: firstprivate"); |
2397 | } |
2398 | } |
2399 | prevDSA = dsa; |
2400 | } |
2401 | } |
2402 | |
2403 | // For OpenMP constructs, check all the data-refs within the constructs |
2404 | // and adjust the symbol for each Name if necessary |
2405 | void OmpAttributeVisitor::Post(const parser::Name &name) { |
2406 | auto *symbol{name.symbol}; |
2407 | |
2408 | if (symbol && !dirContext_.empty() && GetContext().withinConstruct) { |
2409 | if (IsPrivatizable(symbol) && !IsObjectWithDSA(*symbol)) { |
2410 | // TODO: create a separate function to go through the rules for |
2411 | // predetermined, explicitly determined, and implicitly |
2412 | // determined data-sharing attributes (2.15.1.1). |
2413 | if (Symbol * found{currScope().FindSymbol(name.source)}) { |
2414 | if (symbol != found) { |
2415 | name.symbol = found; // adjust the symbol within region |
2416 | } else if (GetContext().defaultDSA == Symbol::Flag::OmpNone && |
2417 | !symbol->GetUltimate().test(Symbol::Flag::OmpThreadprivate) && |
2418 | // Exclude indices of sequential loops that are privatised in |
2419 | // the scope of the parallel region, and not in this scope. |
2420 | // TODO: check whether this should be caught in IsObjectWithDSA |
2421 | !symbol->test(Symbol::Flag::OmpPrivate)) { |
2422 | if (symbol->GetUltimate().test(Symbol::Flag::CrayPointee)) { |
2423 | std::string crayPtrName{ |
2424 | semantics::GetCrayPointer(*symbol).name().ToString()}; |
2425 | if (!IsObjectWithDSA(*currScope().FindSymbol(crayPtrName))) |
2426 | context_.Say(name.source, |
2427 | "The DEFAULT(NONE) clause requires that the Cray Pointer '%s' must be listed in a data-sharing attribute clause"_err_en_US, |
2428 | crayPtrName); |
2429 | } else { |
2430 | context_.Say(name.source, |
2431 | "The DEFAULT(NONE) clause requires that '%s' must be listed in a data-sharing attribute clause"_err_en_US, |
2432 | symbol->name()); |
2433 | } |
2434 | } |
2435 | } |
2436 | } |
2437 | |
2438 | if (Symbol * found{currScope().FindSymbol(name.source)}) { |
2439 | if (found->GetUltimate().test(semantics::Symbol::Flag::OmpThreadprivate)) |
2440 | return; |
2441 | } |
2442 | |
2443 | CreateImplicitSymbols(symbol); |
2444 | } // within OpenMP construct |
2445 | } |
2446 | |
2447 | Symbol *OmpAttributeVisitor::ResolveName(const parser::Name *name) { |
2448 | if (auto *resolvedSymbol{ |
2449 | name ? GetContext().scope.FindSymbol(name->source) : nullptr}) { |
2450 | name->symbol = resolvedSymbol; |
2451 | return resolvedSymbol; |
2452 | } else { |
2453 | return nullptr; |
2454 | } |
2455 | } |
2456 | |
2457 | void OmpAttributeVisitor::ResolveOmpName( |
2458 | const parser::Name &name, Symbol::Flag ompFlag) { |
2459 | if (ResolveName(&name)) { |
2460 | if (auto *resolvedSymbol{ResolveOmp(name, ompFlag, currScope())}) { |
2461 | if (dataSharingAttributeFlags.test(ompFlag)) { |
2462 | AddToContextObjectWithExplicitDSA(*resolvedSymbol, ompFlag); |
2463 | } |
2464 | } |
2465 | } else if (ompFlag == Symbol::Flag::OmpCriticalLock) { |
2466 | const auto pair{ |
2467 | GetContext().scope.try_emplace(name.source, Attrs{}, UnknownDetails{})}; |
2468 | CHECK(pair.second); |
2469 | name.symbol = &pair.first->second.get(); |
2470 | } |
2471 | } |
2472 | |
2473 | void OmpAttributeVisitor::ResolveOmpNameList( |
2474 | const std::list<parser::Name> &nameList, Symbol::Flag ompFlag) { |
2475 | for (const auto &name : nameList) { |
2476 | ResolveOmpName(name, ompFlag); |
2477 | } |
2478 | } |
2479 | |
2480 | Symbol *OmpAttributeVisitor::ResolveOmpCommonBlockName( |
2481 | const parser::Name *name) { |
2482 | if (!name) { |
2483 | return nullptr; |
2484 | } |
2485 | if (auto *cb{GetProgramUnitOrBlockConstructContaining(GetContext().scope) |
2486 | .FindCommonBlock(name->source)}) { |
2487 | name->symbol = cb; |
2488 | return cb; |
2489 | } |
2490 | return nullptr; |
2491 | } |
2492 | |
2493 | // Use this function over ResolveOmpName when an omp object's scope needs |
2494 | // resolving, it's symbol flag isn't important and a simple check for resolution |
2495 | // failure is desired. Using ResolveOmpName means needing to work with the |
2496 | // context to check for failure, whereas here a pointer comparison is all that's |
2497 | // needed. |
2498 | Symbol *OmpAttributeVisitor::ResolveOmpObjectScope(const parser::Name *name) { |
2499 | |
2500 | // TODO: Investigate whether the following block can be replaced by, or |
2501 | // included in, the ResolveOmpName function |
2502 | if (auto *prev{name ? GetContext().scope.parent().FindSymbol(name->source) |
2503 | : nullptr}) { |
2504 | name->symbol = prev; |
2505 | return nullptr; |
2506 | } |
2507 | |
2508 | // TODO: Investigate whether the following block can be replaced by, or |
2509 | // included in, the ResolveOmpName function |
2510 | if (auto *ompSymbol{ |
2511 | name ? GetContext().scope.FindSymbol(name->source) : nullptr}) { |
2512 | name->symbol = ompSymbol; |
2513 | return ompSymbol; |
2514 | } |
2515 | return nullptr; |
2516 | } |
2517 | |
2518 | void OmpAttributeVisitor::ResolveOmpObjectList( |
2519 | const parser::OmpObjectList &ompObjectList, Symbol::Flag ompFlag) { |
2520 | for (const auto &ompObject : ompObjectList.v) { |
2521 | ResolveOmpObject(ompObject, ompFlag); |
2522 | } |
2523 | } |
2524 | |
2525 | /// True if either symbol is in a namelist or some other symbol in the same |
2526 | /// equivalence set as symbol is in a namelist. |
2527 | static bool SymbolOrEquivalentIsInNamelist(const Symbol &symbol) { |
2528 | auto isInNamelist{[](const Symbol &sym) { |
2529 | const Symbol &ultimate{sym.GetUltimate()}; |
2530 | return ultimate.test(Symbol::Flag::InNamelist); |
2531 | }}; |
2532 | |
2533 | const EquivalenceSet *eqv{FindEquivalenceSet(symbol)}; |
2534 | if (!eqv) { |
2535 | return isInNamelist(symbol); |
2536 | } |
2537 | |
2538 | return llvm::any_of(*eqv, [isInNamelist](const EquivalenceObject &obj) { |
2539 | return isInNamelist(obj.symbol); |
2540 | }); |
2541 | } |
2542 | |
2543 | void OmpAttributeVisitor::ResolveOmpObject( |
2544 | const parser::OmpObject &ompObject, Symbol::Flag ompFlag) { |
2545 | unsigned version{context_.langOptions().OpenMPVersion}; |
2546 | common::visit( |
2547 | common::visitors{ |
2548 | [&](const parser::Designator &designator) { |
2549 | if (const auto *name{ |
2550 | semantics::getDesignatorNameIfDataRef(designator)}) { |
2551 | if (auto *symbol{ResolveOmp(*name, ompFlag, currScope())}) { |
2552 | auto checkExclusivelists = |
2553 | [&](const Symbol *symbol1, Symbol::Flag firstOmpFlag, |
2554 | const Symbol *symbol2, Symbol::Flag secondOmpFlag) { |
2555 | if ((symbol1->test(firstOmpFlag) && |
2556 | symbol2->test(secondOmpFlag)) || |
2557 | (symbol1->test(secondOmpFlag) && |
2558 | symbol2->test(firstOmpFlag))) { |
2559 | context_.Say(designator.source, |
2560 | "Variable '%s' may not " |
2561 | "appear on both %s and %s " |
2562 | "clauses on a %s construct"_err_en_US, |
2563 | symbol2->name(), |
2564 | Symbol::OmpFlagToClauseName(firstOmpFlag), |
2565 | Symbol::OmpFlagToClauseName(secondOmpFlag), |
2566 | parser::ToUpperCaseLetters( |
2567 | llvm::omp::getOpenMPDirectiveName( |
2568 | GetContext().directive, version) |
2569 | .str())); |
2570 | } |
2571 | }; |
2572 | if (dataCopyingAttributeFlags.test(ompFlag)) { |
2573 | CheckDataCopyingClause(*name, *symbol, ompFlag); |
2574 | } else { |
2575 | AddToContextObjectWithExplicitDSA(*symbol, ompFlag); |
2576 | if (dataSharingAttributeFlags.test(ompFlag)) { |
2577 | CheckMultipleAppearances(*name, *symbol, ompFlag); |
2578 | } |
2579 | if (privateDataSharingAttributeFlags.test(ompFlag)) { |
2580 | CheckObjectIsPrivatizable(*name, *symbol, ompFlag); |
2581 | } |
2582 | |
2583 | if (ompFlag == Symbol::Flag::OmpAllocate) { |
2584 | AddAllocateName(name); |
2585 | } |
2586 | } |
2587 | if (ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective && |
2588 | IsAllocatable(*symbol) && |
2589 | !IsNestedInDirective(llvm::omp::Directive::OMPD_allocate)) { |
2590 | context_.Say(designator.source, |
2591 | "List items specified in the ALLOCATE directive must not " |
2592 | "have the ALLOCATABLE attribute unless the directive is " |
2593 | "associated with an ALLOCATE statement"_err_en_US); |
2594 | } |
2595 | if ((ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective || |
2596 | ompFlag == |
2597 | Symbol::Flag::OmpExecutableAllocateDirective) && |
2598 | ResolveOmpObjectScope(name) == nullptr) { |
2599 | context_.Say(designator.source, // 2.15.3 |
2600 | "List items must be declared in the same scoping unit " |
2601 | "in which the %s directive appears"_err_en_US, |
2602 | parser::ToUpperCaseLetters( |
2603 | llvm::omp::getOpenMPDirectiveName( |
2604 | GetContext().directive, version) |
2605 | .str())); |
2606 | } |
2607 | if (ompFlag == Symbol::Flag::OmpReduction) { |
2608 | // Using variables inside of a namelist in OpenMP reductions |
2609 | // is allowed by the standard, but is not allowed for |
2610 | // privatisation. This looks like an oversight. If the |
2611 | // namelist is hoisted to a global, we cannot apply the |
2612 | // mapping for the reduction variable: resulting in incorrect |
2613 | // results. Disabling this hoisting could make some real |
2614 | // production code go slower. See discussion in #109303 |
2615 | if (SymbolOrEquivalentIsInNamelist(*symbol)) { |
2616 | context_.Say(name->source, |
2617 | "Variable '%s' in NAMELIST cannot be in a REDUCTION clause"_err_en_US, |
2618 | name->ToString()); |
2619 | } |
2620 | } |
2621 | if (ompFlag == Symbol::Flag::OmpInclusiveScan || |
2622 | ompFlag == Symbol::Flag::OmpExclusiveScan) { |
2623 | if (!symbol->test(Symbol::Flag::OmpInScanReduction)) { |
2624 | context_.Say(name->source, |
2625 | "List item %s must appear in REDUCTION clause " |
2626 | "with the INSCAN modifier of the parent " |
2627 | "directive"_err_en_US, |
2628 | name->ToString()); |
2629 | } |
2630 | } |
2631 | if (ompFlag == Symbol::Flag::OmpDeclareTarget) { |
2632 | if (symbol->IsFuncResult()) { |
2633 | if (Symbol * func{currScope().symbol()}) { |
2634 | CHECK(func->IsSubprogram()); |
2635 | func->set(ompFlag); |
2636 | name->symbol = func; |
2637 | } |
2638 | } |
2639 | } |
2640 | if (GetContext().directive == |
2641 | llvm::omp::Directive::OMPD_target_data) { |
2642 | checkExclusivelists(symbol, Symbol::Flag::OmpUseDevicePtr, |
2643 | symbol, Symbol::Flag::OmpUseDeviceAddr); |
2644 | } |
2645 | if (llvm::omp::allDistributeSet.test(GetContext().directive)) { |
2646 | checkExclusivelists(symbol, Symbol::Flag::OmpFirstPrivate, |
2647 | symbol, Symbol::Flag::OmpLastPrivate); |
2648 | } |
2649 | if (llvm::omp::allTargetSet.test(GetContext().directive)) { |
2650 | checkExclusivelists(symbol, Symbol::Flag::OmpIsDevicePtr, |
2651 | symbol, Symbol::Flag::OmpHasDeviceAddr); |
2652 | const auto *hostAssocSym{symbol}; |
2653 | if (!(symbol->test(Symbol::Flag::OmpIsDevicePtr) || |
2654 | symbol->test(Symbol::Flag::OmpHasDeviceAddr))) { |
2655 | if (const auto *details{ |
2656 | symbol->detailsIf<HostAssocDetails>()}) { |
2657 | hostAssocSym = &details->symbol(); |
2658 | } |
2659 | } |
2660 | Symbol::Flag dataMappingAttributeFlags[] = { |
2661 | Symbol::Flag::OmpMapTo, Symbol::Flag::OmpMapFrom, |
2662 | Symbol::Flag::OmpMapToFrom, Symbol::Flag::OmpMapAlloc, |
2663 | Symbol::Flag::OmpMapRelease, Symbol::Flag::OmpMapDelete, |
2664 | Symbol::Flag::OmpIsDevicePtr, |
2665 | Symbol::Flag::OmpHasDeviceAddr}; |
2666 | |
2667 | Symbol::Flag dataSharingAttributeFlags[] = { |
2668 | Symbol::Flag::OmpPrivate, Symbol::Flag::OmpFirstPrivate, |
2669 | Symbol::Flag::OmpLastPrivate, Symbol::Flag::OmpShared, |
2670 | Symbol::Flag::OmpLinear}; |
2671 | |
2672 | // For OMP TARGET TEAMS directive some sharing attribute |
2673 | // flags and mapping attribute flags can co-exist. |
2674 | if (!(llvm::omp::allTeamsSet.test(GetContext().directive) || |
2675 | llvm::omp::allParallelSet.test( |
2676 | GetContext().directive))) { |
2677 | for (Symbol::Flag ompFlag1 : dataMappingAttributeFlags) { |
2678 | for (Symbol::Flag ompFlag2 : dataSharingAttributeFlags) { |
2679 | if ((hostAssocSym->test(ompFlag2) && |
2680 | hostAssocSym->test( |
2681 | Symbol::Flag::OmpExplicit)) || |
2682 | (symbol->test(ompFlag2) && |
2683 | symbol->test(Symbol::Flag::OmpExplicit))) { |
2684 | checkExclusivelists( |
2685 | hostAssocSym, ompFlag1, symbol, ompFlag2); |
2686 | } |
2687 | } |
2688 | } |
2689 | } |
2690 | } |
2691 | } |
2692 | } else { |
2693 | // Array sections to be changed to substrings as needed |
2694 | if (AnalyzeExpr(context_, designator)) { |
2695 | if (std::holds_alternative<parser::Substring>(designator.u)) { |
2696 | context_.Say(designator.source, |
2697 | "Substrings are not allowed on OpenMP " |
2698 | "directives or clauses"_err_en_US); |
2699 | } |
2700 | } |
2701 | // other checks, more TBD |
2702 | } |
2703 | }, |
2704 | [&](const parser::Name &name) { // common block |
2705 | if (auto *symbol{ResolveOmpCommonBlockName(&name)}) { |
2706 | if (!dataCopyingAttributeFlags.test(ompFlag)) { |
2707 | CheckMultipleAppearances( |
2708 | name, *symbol, Symbol::Flag::OmpCommonBlock); |
2709 | } |
2710 | // 2.15.3 When a named common block appears in a list, it has the |
2711 | // same meaning as if every explicit member of the common block |
2712 | // appeared in the list |
2713 | auto &details{symbol->get<CommonBlockDetails>()}; |
2714 | unsigned index{0}; |
2715 | for (auto &object : details.objects()) { |
2716 | if (auto *resolvedObject{ |
2717 | ResolveOmp(*object, ompFlag, currScope())}) { |
2718 | if (dataCopyingAttributeFlags.test(ompFlag)) { |
2719 | CheckDataCopyingClause(name, *resolvedObject, ompFlag); |
2720 | } else { |
2721 | AddToContextObjectWithExplicitDSA(*resolvedObject, ompFlag); |
2722 | } |
2723 | details.replace_object(*resolvedObject, index); |
2724 | } |
2725 | index++; |
2726 | } |
2727 | } else { |
2728 | context_.Say(name.source, // 2.15.3 |
2729 | "COMMON block must be declared in the same scoping unit " |
2730 | "in which the OpenMP directive or clause appears"_err_en_US); |
2731 | } |
2732 | }, |
2733 | }, |
2734 | ompObject.u); |
2735 | } |
2736 | |
2737 | Symbol *OmpAttributeVisitor::ResolveOmp( |
2738 | const parser::Name &name, Symbol::Flag ompFlag, Scope &scope) { |
2739 | if (ompFlagsRequireNewSymbol.test(ompFlag)) { |
2740 | return DeclareAccessEntity(name, ompFlag, scope); |
2741 | } else { |
2742 | return DeclareOrMarkOtherAccessEntity(name, ompFlag); |
2743 | } |
2744 | } |
2745 | |
2746 | Symbol *OmpAttributeVisitor::ResolveOmp( |
2747 | Symbol &symbol, Symbol::Flag ompFlag, Scope &scope) { |
2748 | if (ompFlagsRequireNewSymbol.test(ompFlag)) { |
2749 | return DeclareAccessEntity(symbol, ompFlag, scope); |
2750 | } else { |
2751 | return DeclareOrMarkOtherAccessEntity(symbol, ompFlag); |
2752 | } |
2753 | } |
2754 | |
2755 | Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity( |
2756 | const parser::Name &name, Symbol::Flag ompFlag) { |
2757 | Symbol *prev{currScope().FindSymbol(name.source)}; |
2758 | if (!name.symbol || !prev) { |
2759 | return nullptr; |
2760 | } else if (prev != name.symbol) { |
2761 | name.symbol = prev; |
2762 | } |
2763 | return DeclareOrMarkOtherAccessEntity(*prev, ompFlag); |
2764 | } |
2765 | |
2766 | Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity( |
2767 | Symbol &object, Symbol::Flag ompFlag) { |
2768 | if (ompFlagsRequireMark.test(ompFlag)) { |
2769 | object.set(ompFlag); |
2770 | } |
2771 | return &object; |
2772 | } |
2773 | |
2774 | static bool WithMultipleAppearancesOmpException( |
2775 | const Symbol &symbol, Symbol::Flag flag) { |
2776 | return (flag == Symbol::Flag::OmpFirstPrivate && |
2777 | symbol.test(Symbol::Flag::OmpLastPrivate)) || |
2778 | (flag == Symbol::Flag::OmpLastPrivate && |
2779 | symbol.test(Symbol::Flag::OmpFirstPrivate)); |
2780 | } |
2781 | |
2782 | void OmpAttributeVisitor::CheckMultipleAppearances( |
2783 | const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) { |
2784 | const auto *target{&symbol}; |
2785 | if (ompFlagsRequireNewSymbol.test(ompFlag)) { |
2786 | if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) { |
2787 | target = &details->symbol(); |
2788 | } |
2789 | } |
2790 | if (HasDataSharingAttributeObject(target->GetUltimate()) && |
2791 | !WithMultipleAppearancesOmpException(symbol, ompFlag)) { |
2792 | context_.Say(name.source, |
2793 | "'%s' appears in more than one data-sharing clause " |
2794 | "on the same OpenMP directive"_err_en_US, |
2795 | name.ToString()); |
2796 | } else { |
2797 | AddDataSharingAttributeObject(target->GetUltimate()); |
2798 | if (privateDataSharingAttributeFlags.test(ompFlag)) { |
2799 | AddPrivateDataSharingAttributeObjects(*target); |
2800 | } |
2801 | } |
2802 | } |
2803 | |
2804 | void ResolveAccParts(SemanticsContext &context, const parser::ProgramUnit &node, |
2805 | Scope *topScope) { |
2806 | if (context.IsEnabled(common::LanguageFeature::OpenACC)) { |
2807 | AccAttributeVisitor{context, topScope}.Walk(x: node); |
2808 | } |
2809 | } |
2810 | |
2811 | void ResolveOmpParts( |
2812 | SemanticsContext &context, const parser::ProgramUnit &node) { |
2813 | if (context.IsEnabled(common::LanguageFeature::OpenMP)) { |
2814 | OmpAttributeVisitor{context}.Walk(x: node); |
2815 | if (!context.AnyFatalError()) { |
2816 | // The data-sharing attribute of the loop iteration variable for a |
2817 | // sequential loop (2.15.1.1) can only be determined when visiting |
2818 | // the corresponding DoConstruct, a second walk is to adjust the |
2819 | // symbols for all the data-refs of that loop iteration variable |
2820 | // prior to the DoConstruct. |
2821 | OmpAttributeVisitor{context}.Walk(x: node); |
2822 | } |
2823 | } |
2824 | } |
2825 | |
2826 | void ResolveOmpTopLevelParts( |
2827 | SemanticsContext &context, const parser::Program &program) { |
2828 | if (!context.IsEnabled(common::LanguageFeature::OpenMP)) { |
2829 | return; |
2830 | } |
2831 | |
2832 | // Gather REQUIRES clauses from all non-module top-level program unit symbols, |
2833 | // combine them together ensuring compatibility and apply them to all these |
2834 | // program units. Modules are skipped because their REQUIRES clauses should be |
2835 | // propagated via USE statements instead. |
2836 | WithOmpDeclarative::RequiresFlags combinedFlags; |
2837 | std::optional<common::OmpMemoryOrderType> combinedMemOrder; |
2838 | |
2839 | // Function to go through non-module top level program units and extract |
2840 | // REQUIRES information to be processed by a function-like argument. |
2841 | auto processProgramUnits{[&](auto processFn) { |
2842 | for (const parser::ProgramUnit &unit : program.v) { |
2843 | if (!std::holds_alternative<common::Indirection<parser::Module>>( |
2844 | unit.u) && |
2845 | !std::holds_alternative<common::Indirection<parser::Submodule>>( |
2846 | unit.u) && |
2847 | !std::holds_alternative< |
2848 | common::Indirection<parser::CompilerDirective>>(unit.u)) { |
2849 | Symbol *symbol{common::visit( |
2850 | [&context](auto &x) { |
2851 | Scope *scope = GetScope(context, x.value()); |
2852 | return scope ? scope->symbol() : nullptr; |
2853 | }, |
2854 | unit.u)}; |
2855 | // FIXME There is no symbol defined for MainProgram units in certain |
2856 | // circumstances, so REQUIRES information has no place to be stored in |
2857 | // these cases. |
2858 | if (!symbol) { |
2859 | continue; |
2860 | } |
2861 | common::visit( |
2862 | [&](auto &details) { |
2863 | if constexpr (std::is_convertible_v<decltype(&details), |
2864 | WithOmpDeclarative *>) { |
2865 | processFn(*symbol, details); |
2866 | } |
2867 | }, |
2868 | symbol->details()); |
2869 | } |
2870 | } |
2871 | }}; |
2872 | |
2873 | // Combine global REQUIRES information from all program units except modules |
2874 | // and submodules. |
2875 | processProgramUnits([&](Symbol &symbol, WithOmpDeclarative &details) { |
2876 | if (const WithOmpDeclarative::RequiresFlags * |
2877 | flags{details.ompRequires()}) { |
2878 | combinedFlags |= *flags; |
2879 | } |
2880 | if (const common::OmpMemoryOrderType * |
2881 | memOrder{details.ompAtomicDefaultMemOrder()}) { |
2882 | if (combinedMemOrder && *combinedMemOrder != *memOrder) { |
2883 | context.Say(symbol.scope()->sourceRange(), |
2884 | "Conflicting '%s' REQUIRES clauses found in compilation " |
2885 | "unit"_err_en_US, |
2886 | parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName( |
2887 | llvm::omp::Clause::OMPC_atomic_default_mem_order) |
2888 | .str())); |
2889 | } |
2890 | combinedMemOrder = *memOrder; |
2891 | } |
2892 | }); |
2893 | |
2894 | // Update all program units except modules and submodules with the combined |
2895 | // global REQUIRES information. |
2896 | processProgramUnits([&](Symbol &, WithOmpDeclarative &details) { |
2897 | if (combinedFlags.any()) { |
2898 | details.set_ompRequires(combinedFlags); |
2899 | } |
2900 | if (combinedMemOrder) { |
2901 | details.set_ompAtomicDefaultMemOrder(*combinedMemOrder); |
2902 | } |
2903 | }); |
2904 | } |
2905 | |
2906 | static bool IsSymbolThreadprivate(const Symbol &symbol) { |
2907 | if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) { |
2908 | return details->symbol().test(Symbol::Flag::OmpThreadprivate); |
2909 | } |
2910 | return symbol.test(Symbol::Flag::OmpThreadprivate); |
2911 | } |
2912 | |
2913 | static bool IsSymbolPrivate(const Symbol &symbol) { |
2914 | LLVM_DEBUG(llvm::dbgs() << "IsSymbolPrivate("<< symbol.name() << "):\n"); |
2915 | LLVM_DEBUG(dbg::DumpAssocSymbols(llvm::dbgs(), symbol)); |
2916 | |
2917 | if (Symbol::Flags dsa{GetSymbolDSA(symbol)}; dsa.any()) { |
2918 | if (dsa.test(Symbol::Flag::OmpShared)) { |
2919 | return false; |
2920 | } |
2921 | return true; |
2922 | } |
2923 | |
2924 | // A symbol that has not gone through constructs that may privatize the |
2925 | // original symbol may be predetermined as private. |
2926 | // (OMP 5.2 5.1.1 - Variables Referenced in a Construct) |
2927 | if (symbol == symbol.GetUltimate()) { |
2928 | switch (symbol.owner().kind()) { |
2929 | case Scope::Kind::MainProgram: |
2930 | case Scope::Kind::Subprogram: |
2931 | case Scope::Kind::BlockConstruct: |
2932 | return !symbol.attrs().test(Attr::SAVE) && |
2933 | !symbol.attrs().test(Attr::PARAMETER) && !IsAssumedShape(symbol) && |
2934 | !symbol.flags().test(Symbol::Flag::InCommonBlock); |
2935 | default: |
2936 | return false; |
2937 | } |
2938 | } |
2939 | return false; |
2940 | } |
2941 | |
2942 | void OmpAttributeVisitor::CheckDataCopyingClause( |
2943 | const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) { |
2944 | if (ompFlag == Symbol::Flag::OmpCopyIn) { |
2945 | // List of items/objects that can appear in a 'copyin' clause must be |
2946 | // 'threadprivate' |
2947 | if (!IsSymbolThreadprivate(symbol)) { |
2948 | context_.Say(name.source, |
2949 | "Non-THREADPRIVATE object '%s' in COPYIN clause"_err_en_US, |
2950 | symbol.name()); |
2951 | } |
2952 | } else if (ompFlag == Symbol::Flag::OmpCopyPrivate && |
2953 | GetContext().directive == llvm::omp::Directive::OMPD_single) { |
2954 | // A list item that appears in a 'copyprivate' clause may not appear on a |
2955 | // 'private' or 'firstprivate' clause on a single construct |
2956 | if (IsObjectWithDSA(symbol) && |
2957 | (symbol.test(Symbol::Flag::OmpPrivate) || |
2958 | symbol.test(Symbol::Flag::OmpFirstPrivate))) { |
2959 | context_.Say(name.source, |
2960 | "COPYPRIVATE variable '%s' may not appear on a PRIVATE or " |
2961 | "FIRSTPRIVATE clause on a SINGLE construct"_err_en_US, |
2962 | symbol.name()); |
2963 | } else if (!IsSymbolThreadprivate(symbol) && !IsSymbolPrivate(symbol)) { |
2964 | // List of items/objects that can appear in a 'copyprivate' clause must be |
2965 | // either 'private' or 'threadprivate' in enclosing context. |
2966 | context_.Say(name.source, |
2967 | "COPYPRIVATE variable '%s' is not PRIVATE or THREADPRIVATE in " |
2968 | "outer context"_err_en_US, |
2969 | symbol.name()); |
2970 | } |
2971 | } |
2972 | } |
2973 | |
2974 | void OmpAttributeVisitor::CheckObjectIsPrivatizable( |
2975 | const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) { |
2976 | const auto &ultimateSymbol{symbol.GetUltimate()}; |
2977 | llvm::StringRef clauseName{"PRIVATE"}; |
2978 | if (ompFlag == Symbol::Flag::OmpFirstPrivate) { |
2979 | clauseName = "FIRSTPRIVATE"; |
2980 | } else if (ompFlag == Symbol::Flag::OmpLastPrivate) { |
2981 | clauseName = "LASTPRIVATE"; |
2982 | } |
2983 | |
2984 | if (SymbolOrEquivalentIsInNamelist(symbol)) { |
2985 | context_.Say(name.source, |
2986 | "Variable '%s' in NAMELIST cannot be in a %s clause"_err_en_US, |
2987 | name.ToString(), clauseName.str()); |
2988 | } |
2989 | |
2990 | if (ultimateSymbol.has<AssocEntityDetails>()) { |
2991 | context_.Say(name.source, |
2992 | "Variable '%s' in ASSOCIATE cannot be in a %s clause"_err_en_US, |
2993 | name.ToString(), clauseName.str()); |
2994 | } |
2995 | |
2996 | if (stmtFunctionExprSymbols_.find(ultimateSymbol) != |
2997 | stmtFunctionExprSymbols_.end()) { |
2998 | context_.Say(name.source, |
2999 | "Variable '%s' in statement function expression cannot be in a " |
3000 | "%s clause"_err_en_US, |
3001 | name.ToString(), clauseName.str()); |
3002 | } |
3003 | } |
3004 | |
3005 | void OmpAttributeVisitor::CheckSourceLabel(const parser::Label &label) { |
3006 | // Get the context to check if the statement causing a jump to the 'label' is |
3007 | // in an enclosing OpenMP construct |
3008 | std::optional<DirContext> thisContext{GetContextIf()}; |
3009 | sourceLabels_.emplace( |
3010 | label, std::make_pair(currentStatementSource_, thisContext)); |
3011 | // Check if the statement with 'label' to which a jump is being introduced |
3012 | // has already been encountered |
3013 | auto it{targetLabels_.find(label)}; |
3014 | if (it != targetLabels_.end()) { |
3015 | // Check if both the statement with 'label' and the statement that causes a |
3016 | // jump to the 'label' are in the same scope |
3017 | CheckLabelContext(currentStatementSource_, it->second.first, thisContext, |
3018 | it->second.second); |
3019 | } |
3020 | } |
3021 | |
3022 | // Check for invalid branch into or out of OpenMP structured blocks |
3023 | void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source, |
3024 | const parser::CharBlock target, std::optional<DirContext> sourceContext, |
3025 | std::optional<DirContext> targetContext) { |
3026 | unsigned version{context_.langOptions().OpenMPVersion}; |
3027 | if (targetContext && |
3028 | (!sourceContext || |
3029 | (sourceContext->scope != targetContext->scope && |
3030 | !DoesScopeContain( |
3031 | &targetContext->scope, sourceContext->scope)))) { |
3032 | context_ |
3033 | .Say(source, "invalid branch into an OpenMP structured block"_err_en_US) |
3034 | .Attach(target, "In the enclosing %s directive branched into"_en_US, |
3035 | parser::ToUpperCaseLetters(llvm::omp::getOpenMPDirectiveName( |
3036 | targetContext->directive, version) |
3037 | .str())); |
3038 | } |
3039 | if (sourceContext && |
3040 | (!targetContext || |
3041 | (sourceContext->scope != targetContext->scope && |
3042 | !DoesScopeContain( |
3043 | &sourceContext->scope, targetContext->scope)))) { |
3044 | context_ |
3045 | .Say(source, |
3046 | "invalid branch leaving an OpenMP structured block"_err_en_US) |
3047 | .Attach(target, "Outside the enclosing %s directive"_en_US, |
3048 | parser::ToUpperCaseLetters(llvm::omp::getOpenMPDirectiveName( |
3049 | sourceContext->directive, version) |
3050 | .str())); |
3051 | } |
3052 | } |
3053 | |
3054 | // Goes through the names in an OmpObjectList and checks if each name appears |
3055 | // in the given allocate statement |
3056 | void OmpAttributeVisitor::CheckAllNamesInAllocateStmt( |
3057 | const parser::CharBlock &source, const parser::OmpObjectList &ompObjectList, |
3058 | const parser::AllocateStmt &allocate) { |
3059 | for (const auto &obj : ompObjectList.v) { |
3060 | if (const auto *d{std::get_if<parser::Designator>(&obj.u)}) { |
3061 | if (const auto *ref{std::get_if<parser::DataRef>(&d->u)}) { |
3062 | if (const auto *n{std::get_if<parser::Name>(&ref->u)}) { |
3063 | CheckNameInAllocateStmt(source, *n, allocate); |
3064 | } |
3065 | } |
3066 | } |
3067 | } |
3068 | } |
3069 | |
3070 | void OmpAttributeVisitor::CheckNameInAllocateStmt( |
3071 | const parser::CharBlock &source, const parser::Name &name, |
3072 | const parser::AllocateStmt &allocate) { |
3073 | for (const auto &allocation : |
3074 | std::get<std::list<parser::Allocation>>(allocate.t)) { |
3075 | const auto &allocObj = std::get<parser::AllocateObject>(allocation.t); |
3076 | if (const auto *n{std::get_if<parser::Name>(&allocObj.u)}) { |
3077 | if (n->source == name.source) { |
3078 | return; |
3079 | } |
3080 | } |
3081 | } |
3082 | unsigned version{context_.langOptions().OpenMPVersion}; |
3083 | context_.Say(source, |
3084 | "Object '%s' in %s directive not " |
3085 | "found in corresponding ALLOCATE statement"_err_en_US, |
3086 | name.ToString(), |
3087 | parser::ToUpperCaseLetters( |
3088 | llvm::omp::getOpenMPDirectiveName(GetContext().directive, version) |
3089 | .str())); |
3090 | } |
3091 | |
3092 | void OmpAttributeVisitor::AddOmpRequiresToScope(Scope &scope, |
3093 | WithOmpDeclarative::RequiresFlags flags, |
3094 | std::optional<common::OmpMemoryOrderType> memOrder) { |
3095 | Scope *scopeIter = &scope; |
3096 | do { |
3097 | if (Symbol * symbol{scopeIter->symbol()}) { |
3098 | common::visit( |
3099 | [&](auto &details) { |
3100 | // Store clauses information into the symbol for the parent and |
3101 | // enclosing modules, programs, functions and subroutines. |
3102 | if constexpr (std::is_convertible_v<decltype(&details), |
3103 | WithOmpDeclarative *>) { |
3104 | if (flags.any()) { |
3105 | if (const WithOmpDeclarative::RequiresFlags * |
3106 | otherFlags{details.ompRequires()}) { |
3107 | flags |= *otherFlags; |
3108 | } |
3109 | details.set_ompRequires(flags); |
3110 | } |
3111 | if (memOrder) { |
3112 | if (details.has_ompAtomicDefaultMemOrder() && |
3113 | *details.ompAtomicDefaultMemOrder() != *memOrder) { |
3114 | context_.Say(scopeIter->sourceRange(), |
3115 | "Conflicting '%s' REQUIRES clauses found in compilation " |
3116 | "unit"_err_en_US, |
3117 | parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName( |
3118 | llvm::omp::Clause::OMPC_atomic_default_mem_order) |
3119 | .str())); |
3120 | } |
3121 | details.set_ompAtomicDefaultMemOrder(*memOrder); |
3122 | } |
3123 | } |
3124 | }, |
3125 | symbol->details()); |
3126 | } |
3127 | scopeIter = &scopeIter->parent(); |
3128 | } while (!scopeIter->IsGlobal()); |
3129 | } |
3130 | |
3131 | void OmpAttributeVisitor::IssueNonConformanceWarning( |
3132 | llvm::omp::Directive D, parser::CharBlock source) { |
3133 | std::string warnStr; |
3134 | llvm::raw_string_ostream warnStrOS(warnStr); |
3135 | unsigned version{context_.langOptions().OpenMPVersion}; |
3136 | warnStrOS << "OpenMP directive " |
3137 | << parser::ToUpperCaseLetters( |
3138 | llvm::omp::getOpenMPDirectiveName(D, version).str()) |
3139 | << " has been deprecated"; |
3140 | |
3141 | auto setAlternativeStr = [&warnStrOS](llvm::StringRef alt) { |
3142 | warnStrOS << ", please use "<< alt << " instead."; |
3143 | }; |
3144 | switch (D) { |
3145 | case llvm::omp::OMPD_master: |
3146 | setAlternativeStr("MASKED"); |
3147 | break; |
3148 | case llvm::omp::OMPD_master_taskloop: |
3149 | setAlternativeStr("MASKED TASKLOOP"); |
3150 | break; |
3151 | case llvm::omp::OMPD_master_taskloop_simd: |
3152 | setAlternativeStr("MASKED TASKLOOP SIMD"); |
3153 | break; |
3154 | case llvm::omp::OMPD_parallel_master: |
3155 | setAlternativeStr("PARALLEL MASKED"); |
3156 | break; |
3157 | case llvm::omp::OMPD_parallel_master_taskloop: |
3158 | setAlternativeStr("PARALLEL MASKED TASKLOOP"); |
3159 | break; |
3160 | case llvm::omp::OMPD_parallel_master_taskloop_simd: |
3161 | setAlternativeStr("PARALLEL_MASKED TASKLOOP SIMD"); |
3162 | break; |
3163 | case llvm::omp::OMPD_allocate: |
3164 | setAlternativeStr("ALLOCATORS"); |
3165 | break; |
3166 | case llvm::omp::OMPD_target_loop: |
3167 | default:; |
3168 | } |
3169 | context_.Warn(common::UsageWarning::OpenMPUsage, source, "%s"_warn_en_US, |
3170 | warnStrOS.str()); |
3171 | } |
3172 | |
3173 | #ifndef NDEBUG |
3174 | |
3175 | static llvm::raw_ostream &operator<<( |
3176 | llvm::raw_ostream &os, const Symbol::Flags &flags) { |
3177 | flags.Dump(os, Symbol::EnumToString); |
3178 | return os; |
3179 | } |
3180 | |
3181 | namespace dbg { |
3182 | |
3183 | static llvm::raw_ostream &operator<<( |
3184 | llvm::raw_ostream &os, std::optional<parser::SourcePosition> srcPos) { |
3185 | if (srcPos) { |
3186 | os << *srcPos.value().path << ":"<< srcPos.value().line << ": "; |
3187 | } |
3188 | return os; |
3189 | } |
3190 | |
3191 | static std::optional<parser::SourcePosition> GetSourcePosition( |
3192 | const Fortran::semantics::Scope &scope, |
3193 | const Fortran::parser::CharBlock &src) { |
3194 | parser::AllCookedSources &allCookedSources{ |
3195 | scope.context().allCookedSources()}; |
3196 | if (std::optional<parser::ProvenanceRange> prange{ |
3197 | allCookedSources.GetProvenanceRange(src)}) { |
3198 | return allCookedSources.allSources().GetSourcePosition(prange->start()); |
3199 | } |
3200 | return std::nullopt; |
3201 | } |
3202 | |
3203 | // Returns a string containing the source location of `scope` followed by |
3204 | // its first source line. |
3205 | static std::string ScopeSourcePos(const Fortran::semantics::Scope &scope) { |
3206 | const parser::CharBlock &sourceRange{scope.sourceRange()}; |
3207 | std::string src{sourceRange.ToString()}; |
3208 | size_t nl{src.find(c: '\n')}; |
3209 | std::string str; |
3210 | llvm::raw_string_ostream ss{str}; |
3211 | |
3212 | ss << GetSourcePosition(scope, sourceRange) << src.substr(0, nl); |
3213 | return str; |
3214 | } |
3215 | |
3216 | static void DumpAssocSymbols(llvm::raw_ostream &os, const Symbol &sym) { |
3217 | os << '\t' << sym << '\n'; |
3218 | os << "\t\tOwner: "<< ScopeSourcePos(sym.owner()) << '\n'; |
3219 | if (const auto *details{sym.detailsIf<HostAssocDetails>()}) { |
3220 | DumpAssocSymbols(os, details->symbol()); |
3221 | } |
3222 | } |
3223 | |
3224 | } // namespace dbg |
3225 | |
3226 | #endif |
3227 | |
3228 | } // namespace Fortran::semantics |
3229 |
Definitions
- GetScope
- DirectiveAttributeVisitor
- DirectiveAttributeVisitor
- Pre
- Post
- DirContext
- DirContext
- GetContext
- GetContextIf
- PushContext
- PushContext
- PopContext
- SetContextDirectiveSource
- currScope
- SetContextDefaultDSA
- AddToContextObjectWithDSA
- AddToContextObjectWithDSA
- IsObjectWithDSA
- SetContextAssociatedLoopLevel
- MakeAssocSymbol
- MakeAssocSymbol
- AddDataSharingAttributeObject
- ClearDataSharingAttributeObjects
- AccAttributeVisitor
- AccAttributeVisitor
- Walk
- Pre
- Post
- Post
- Post
- Post
- Post
- Post
- Post
- Post
- Post
- Post
- Post
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- OmpAttributeVisitor
- OmpAttributeVisitor
- Walk
- Pre
- Post
- Pre
- Pre
- Pre
- Pre
- Pre
- Post
- Pre
- Post
- Post
- Post
- Post
- Post
- Post
- Post
- Pre
- Post
- Pre
- Post
- Pre
- Post
- Pre
- Post
- Post
- Post
- Post
- Post
- Post
- Post
- Post
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Post
- Pre
- Pre
- Pre
- Pre
- Post
- Post
- Post
- Post
- Post
- Post
- Post
- Post
- Post
- SetAssociatedClause
- GetAssociatedClause
- AddAllocateName
- ClearAllocateNames
- AddPrivateDataSharingAttributeObjects
- ClearPrivateDataSharingAttributeObjects
- ClearLabels
- AddToContextObjectWithExplicitDSA
- SetSymbolDSA
- HasDataSharingAttributeObject
- GetLoopIndex
- GetDoConstructIf
- DeclareNewAccessEntity
- DeclareAccessEntity
- DeclareAccessEntity
- Pre
- Pre
- GetAccObjectList
- Post
- Pre
- Pre
- ResolveName
- ResolveFctName
- FoldExpr
- EvaluateExpr
- AddRoutineInfoToSymbol
- Pre
- Pre
- Pre
- IsLastNameArray
- AllowOnlyArrayAndSubArray
- DoNotAllowAssumedSizedArray
- AllowOnlyVariable
- Pre
- GetAssociatedLoopLevelFromClauses
- CheckAssociatedLoop
- EnsureAllocatableOrPointer
- Pre
- Pre
- Post
- Post
- ResolveAccCommonBlockName
- ResolveAccObjectList
- ResolveAccObject
- ResolveAcc
- ResolveAcc
- DeclareOrMarkOtherAccessEntity
- DeclareOrMarkOtherAccessEntity
- WithMultipleAppearancesAccException
- CheckMultipleAppearances
- Pre
- Post
- Pre
- Pre
- ResolveSeqLoopIndexInParallelOrTaskConstruct
- Pre
- GetAssociatedLoopLevelFromClauses
- PrivatizeAssociatedLoopIndexAndCheckLoopLevel
- CheckAssocLoopLevel
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Pre
- Post
- IsNestedInDirective
- Post
- Post
- IsPrivatizable
- IsSymbolStaticStorageDuration
- CreateImplicitSymbols
- Post
- ResolveName
- ResolveOmpName
- ResolveOmpNameList
- ResolveOmpCommonBlockName
- ResolveOmpObjectScope
- ResolveOmpObjectList
- SymbolOrEquivalentIsInNamelist
- ResolveOmpObject
- ResolveOmp
- ResolveOmp
- DeclareOrMarkOtherAccessEntity
- DeclareOrMarkOtherAccessEntity
- WithMultipleAppearancesOmpException
- CheckMultipleAppearances
- ResolveAccParts
- ResolveOmpParts
- ResolveOmpTopLevelParts
- IsSymbolThreadprivate
- IsSymbolPrivate
- CheckDataCopyingClause
- CheckObjectIsPrivatizable
- CheckSourceLabel
- CheckLabelContext
- CheckAllNamesInAllocateStmt
- CheckNameInAllocateStmt
- AddOmpRequiresToScope
- IssueNonConformanceWarning
- operator<<
- operator<<
- GetSourcePosition
- ScopeSourcePos
Learn to use CMake with our Intro Training
Find out more