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
31template <typename T>
32static 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
38namespace Fortran::semantics {
39
40template <typename T> class DirectiveAttributeVisitor {
41public:
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
48protected:
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
126class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
127public:
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
256private:
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
299class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
300public:
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
745private:
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
859template <typename T>
860bool DirectiveAttributeVisitor<T>::HasDataSharingAttributeObject(
861 const Symbol &object) {
862 auto it{dataSharingAttributeObjects_.find(object)};
863 return it != dataSharingAttributeObjects_.end();
864}
865
866template <typename T>
867const 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
886template <typename T>
887const parser::DoConstruct *DirectiveAttributeVisitor<T>::GetDoConstructIf(
888 const parser::ExecutionPartConstruct &x) {
889 return parser::Unwrap<parser::DoConstruct>(x);
890}
891
892template <typename T>
893Symbol *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
905template <typename T>
906Symbol *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
915template <typename T>
916Symbol *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
926bool 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
944bool 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
955static 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
1000void 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
1016bool 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
1030bool 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
1048Symbol *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
1061Symbol *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
1076template <typename T>
1077common::IfNoLvalue<T, T> FoldExpr(
1078 evaluate::FoldingContext &foldingContext, T &&expr) {
1079 return evaluate::Fold(foldingContext, std::move(expr));
1080}
1081
1082template <typename T>
1083MaybeExpr EvaluateExpr(
1084 Fortran::semantics::SemanticsContext &semanticsContext, const T &expr) {
1085 return FoldExpr(
1086 semanticsContext.foldingContext(), AnalyzeExpr(semanticsContext, expr));
1087}
1088
1089void 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
1171bool 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
1197bool 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
1208bool 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
1229static 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
1247void 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
1274void 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
1299void 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
1319bool 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
1336std::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
1356void 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
1430void 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
1458bool 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
1464bool 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
1470void 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
1485void 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
1505Symbol *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
1522void 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
1530void 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
1575Symbol *AccAttributeVisitor::ResolveAcc(
1576 const parser::Name &name, Symbol::Flag accFlag, Scope &scope) {
1577 return DeclareOrMarkOtherAccessEntity(name, accFlag);
1578}
1579
1580Symbol *AccAttributeVisitor::ResolveAcc(
1581 Symbol &symbol, Symbol::Flag accFlag, Scope &scope) {
1582 return DeclareOrMarkOtherAccessEntity(symbol, accFlag);
1583}
1584
1585Symbol *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
1596Symbol *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
1607static bool WithMultipleAppearancesAccException(
1608 const Symbol &symbol, Symbol::Flag flag) {
1609 return false; // Place holder
1610}
1611
1612void 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
1630static llvm::raw_ostream &operator<<(
1631 llvm::raw_ostream &os, const Symbol::Flags &flags);
1632
1633namespace dbg {
1634static void DumpAssocSymbols(llvm::raw_ostream &os, const Symbol &sym);
1635static std::string ScopeSourcePos(const Fortran::semantics::Scope &scope);
1636} // namespace dbg
1637
1638#endif
1639
1640bool 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
1676void 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
1719bool 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
1740bool 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
1809void 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.
1863bool 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
1893std::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.
1936void 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}
1978void 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
1988bool 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
2006bool 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
2022bool 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
2045bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclareMapperConstruct &x) {
2046 PushContext(x.source, llvm::omp::Directive::OMPD_declare_mapper);
2047 return true;
2048}
2049
2050bool OmpAttributeVisitor::Pre(
2051 const parser::OpenMPDeclareReductionConstruct &x) {
2052 PushContext(x.source, llvm::omp::Directive::OMPD_declare_reduction);
2053 return true;
2054}
2055
2056bool 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
2063bool 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
2070bool OmpAttributeVisitor::Pre(const parser::OpenMPDispatchConstruct &x) {
2071 PushContext(x.source, llvm::omp::Directive::OMPD_dispatch);
2072 return true;
2073}
2074
2075bool 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
2085bool 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
2098void 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
2122bool 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
2133void 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
2170void 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
2199static 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
2220static 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
2234void 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
2405void 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
2447Symbol *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
2457void 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
2473void 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
2480Symbol *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.
2498Symbol *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
2518void 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.
2527static 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
2543void 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
2737Symbol *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
2746Symbol *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
2755Symbol *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
2766Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
2767 Symbol &object, Symbol::Flag ompFlag) {
2768 if (ompFlagsRequireMark.test(ompFlag)) {
2769 object.set(ompFlag);
2770 }
2771 return &object;
2772}
2773
2774static 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
2782void 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
2804void 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
2811void 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
2826void 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
2906static 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
2913static 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
2942void 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
2974void 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
3005void 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
3023void 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
3056void 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
3070void 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
3092void 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
3131void 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
3175static llvm::raw_ostream &operator<<(
3176 llvm::raw_ostream &os, const Symbol::Flags &flags) {
3177 flags.Dump(os, Symbol::EnumToString);
3178 return os;
3179}
3180
3181namespace dbg {
3182
3183static 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
3191static 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.
3205static 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
3216static 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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of flang/lib/Semantics/resolve-directives.cpp