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

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