1 //===----------------------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "resolve-directives.h"
11 #include "check-acc-structure.h"
12 #include "check-omp-structure.h"
13 #include "resolve-names-utils.h"
14 #include "flang/Common/idioms.h"
15 #include "flang/Evaluate/fold.h"
16 #include "flang/Evaluate/type.h"
17 #include "flang/Parser/parse-tree-visitor.h"
18 #include "flang/Parser/parse-tree.h"
19 #include "flang/Parser/tools.h"
20 #include "flang/Semantics/expression.h"
24 namespace Fortran::semantics
{
26 template <typename T
> class DirectiveAttributeVisitor
{
28 explicit DirectiveAttributeVisitor(SemanticsContext
&context
)
29 : context_
{context
} {}
31 template <typename A
> bool Pre(const A
&) { return true; }
32 template <typename A
> void Post(const A
&) {}
36 DirContext(const parser::CharBlock
&source
, T d
, Scope
&s
)
37 : directiveSource
{source
}, directive
{d
}, scope
{s
} {}
38 parser::CharBlock directiveSource
;
41 Symbol::Flag defaultDSA
{Symbol::Flag::AccShared
}; // TODOACC
42 std::map
<const Symbol
*, Symbol::Flag
> objectWithDSA
;
43 bool withinConstruct
{false};
44 std::int64_t associatedLoopLevel
{0};
47 DirContext
&GetContext() {
48 CHECK(!dirContext_
.empty());
49 return dirContext_
.back();
51 std::optional
<DirContext
> GetContextIf() {
52 return dirContext_
.empty()
54 : std::make_optional
<DirContext
>(dirContext_
.back());
56 void PushContext(const parser::CharBlock
&source
, T dir
) {
57 dirContext_
.emplace_back(source
, dir
, context_
.FindScope(source
));
59 void PopContext() { dirContext_
.pop_back(); }
60 void SetContextDirectiveSource(parser::CharBlock
&dir
) {
61 GetContext().directiveSource
= dir
;
63 Scope
&currScope() { return GetContext().scope
; }
64 void SetContextDefaultDSA(Symbol::Flag flag
) {
65 GetContext().defaultDSA
= flag
;
67 void AddToContextObjectWithDSA(
68 const Symbol
&symbol
, Symbol::Flag flag
, DirContext
&context
) {
69 context
.objectWithDSA
.emplace(&symbol
, flag
);
71 void AddToContextObjectWithDSA(const Symbol
&symbol
, Symbol::Flag flag
) {
72 AddToContextObjectWithDSA(symbol
, flag
, GetContext());
74 bool IsObjectWithDSA(const Symbol
&symbol
) {
75 auto it
{GetContext().objectWithDSA
.find(&symbol
)};
76 return it
!= GetContext().objectWithDSA
.end();
78 void SetContextAssociatedLoopLevel(std::int64_t level
) {
79 GetContext().associatedLoopLevel
= level
;
81 Symbol
&MakeAssocSymbol(const SourceName
&name
, Symbol
&prev
, Scope
&scope
) {
82 const auto pair
{scope
.try_emplace(name
, Attrs
{}, HostAssocDetails
{prev
})};
83 return *pair
.first
->second
;
85 Symbol
&MakeAssocSymbol(const SourceName
&name
, Symbol
&prev
) {
86 return MakeAssocSymbol(name
, prev
, currScope());
88 static const parser::Name
*GetDesignatorNameIfDataRef(
89 const parser::Designator
&designator
) {
90 const auto *dataRef
{std::get_if
<parser::DataRef
>(&designator
.u
)};
91 return dataRef
? std::get_if
<parser::Name
>(&dataRef
->u
) : nullptr;
93 void AddDataSharingAttributeObject(SymbolRef object
) {
94 dataSharingAttributeObjects_
.insert(object
);
96 void ClearDataSharingAttributeObjects() {
97 dataSharingAttributeObjects_
.clear();
99 bool HasDataSharingAttributeObject(const Symbol
&);
100 const parser::Name
*GetLoopIndex(const parser::DoConstruct
&);
101 const parser::DoConstruct
*GetDoConstructIf(
102 const parser::ExecutionPartConstruct
&);
103 Symbol
*DeclarePrivateAccessEntity(
104 const parser::Name
&, Symbol::Flag
, Scope
&);
105 Symbol
*DeclarePrivateAccessEntity(Symbol
&, Symbol::Flag
, Scope
&);
106 Symbol
*DeclareOrMarkOtherAccessEntity(const parser::Name
&, Symbol::Flag
);
108 UnorderedSymbolSet dataSharingAttributeObjects_
; // on one directive
109 SemanticsContext
&context_
;
110 std::vector
<DirContext
> dirContext_
; // used as a stack
113 class AccAttributeVisitor
: DirectiveAttributeVisitor
<llvm::acc::Directive
> {
115 explicit AccAttributeVisitor(SemanticsContext
&context
)
116 : DirectiveAttributeVisitor(context
) {}
118 template <typename A
> void Walk(const A
&x
) { parser::Walk(x
, *this); }
119 template <typename A
> bool Pre(const A
&) { return true; }
120 template <typename A
> void Post(const A
&) {}
122 bool Pre(const parser::OpenACCBlockConstruct
&);
123 void Post(const parser::OpenACCBlockConstruct
&) { PopContext(); }
124 bool Pre(const parser::OpenACCCombinedConstruct
&);
125 void Post(const parser::OpenACCCombinedConstruct
&) { PopContext(); }
127 bool Pre(const parser::OpenACCDeclarativeConstruct
&);
128 void Post(const parser::OpenACCDeclarativeConstruct
&) { PopContext(); }
130 bool Pre(const parser::OpenACCRoutineConstruct
&);
131 bool Pre(const parser::AccBindClause
&);
132 void Post(const parser::OpenACCStandaloneDeclarativeConstruct
&);
134 void Post(const parser::AccBeginBlockDirective
&) {
135 GetContext().withinConstruct
= true;
138 bool Pre(const parser::OpenACCLoopConstruct
&);
139 void Post(const parser::OpenACCLoopConstruct
&) { PopContext(); }
140 void Post(const parser::AccLoopDirective
&) {
141 GetContext().withinConstruct
= true;
144 bool Pre(const parser::OpenACCStandaloneConstruct
&);
145 void Post(const parser::OpenACCStandaloneConstruct
&) { PopContext(); }
146 void Post(const parser::AccStandaloneDirective
&) {
147 GetContext().withinConstruct
= true;
150 bool Pre(const parser::OpenACCCacheConstruct
&);
151 void Post(const parser::OpenACCCacheConstruct
&) { PopContext(); }
153 void Post(const parser::AccDefaultClause
&);
155 bool Pre(const parser::AccClause::Attach
&);
156 bool Pre(const parser::AccClause::Detach
&);
158 bool Pre(const parser::AccClause::Copy
&x
) {
159 ResolveAccObjectList(x
.v
, Symbol::Flag::AccCopyIn
);
160 ResolveAccObjectList(x
.v
, Symbol::Flag::AccCopyOut
);
164 bool Pre(const parser::AccClause::Create
&x
) {
165 const auto &objectList
{std::get
<parser::AccObjectList
>(x
.v
.t
)};
166 ResolveAccObjectList(objectList
, Symbol::Flag::AccCreate
);
170 bool Pre(const parser::AccClause::Copyin
&x
) {
171 const auto &objectList
{std::get
<parser::AccObjectList
>(x
.v
.t
)};
172 ResolveAccObjectList(objectList
, Symbol::Flag::AccCopyIn
);
176 bool Pre(const parser::AccClause::Copyout
&x
) {
177 const auto &objectList
{std::get
<parser::AccObjectList
>(x
.v
.t
)};
178 ResolveAccObjectList(objectList
, Symbol::Flag::AccCopyOut
);
182 bool Pre(const parser::AccClause::Present
&x
) {
183 ResolveAccObjectList(x
.v
, Symbol::Flag::AccPresent
);
186 bool Pre(const parser::AccClause::Private
&x
) {
187 ResolveAccObjectList(x
.v
, Symbol::Flag::AccPrivate
);
190 bool Pre(const parser::AccClause::Firstprivate
&x
) {
191 ResolveAccObjectList(x
.v
, Symbol::Flag::AccFirstPrivate
);
195 void Post(const parser::Name
&);
198 std::int64_t GetAssociatedLoopLevelFromClauses(const parser::AccClauseList
&);
200 static constexpr Symbol::Flags dataSharingAttributeFlags
{
201 Symbol::Flag::AccShared
, Symbol::Flag::AccPrivate
,
202 Symbol::Flag::AccPresent
, Symbol::Flag::AccFirstPrivate
,
203 Symbol::Flag::AccReduction
};
205 static constexpr Symbol::Flags dataMappingAttributeFlags
{
206 Symbol::Flag::AccCreate
, Symbol::Flag::AccCopyIn
,
207 Symbol::Flag::AccCopyOut
, Symbol::Flag::AccDelete
};
209 static constexpr Symbol::Flags accFlagsRequireNewSymbol
{
210 Symbol::Flag::AccPrivate
, Symbol::Flag::AccFirstPrivate
,
211 Symbol::Flag::AccReduction
};
213 static constexpr Symbol::Flags accFlagsRequireMark
{};
215 void PrivatizeAssociatedLoopIndex(const parser::OpenACCLoopConstruct
&);
216 void ResolveAccObjectList(const parser::AccObjectList
&, Symbol::Flag
);
217 void ResolveAccObject(const parser::AccObject
&, Symbol::Flag
);
218 Symbol
*ResolveAcc(const parser::Name
&, Symbol::Flag
, Scope
&);
219 Symbol
*ResolveAcc(Symbol
&, Symbol::Flag
, Scope
&);
220 Symbol
*ResolveName(const parser::Name
&);
221 Symbol
*ResolveAccCommonBlockName(const parser::Name
*);
222 Symbol
*DeclareOrMarkOtherAccessEntity(const parser::Name
&, Symbol::Flag
);
223 Symbol
*DeclareOrMarkOtherAccessEntity(Symbol
&, Symbol::Flag
);
224 void CheckMultipleAppearances(
225 const parser::Name
&, const Symbol
&, Symbol::Flag
);
226 void AllowOnlyArrayAndSubArray(const parser::AccObjectList
&objectList
);
227 void DoNotAllowAssumedSizedArray(const parser::AccObjectList
&objectList
);
228 void EnsureAllocatableOrPointer(
229 const llvm::acc::Clause clause
, const parser::AccObjectList
&objectList
);
232 // Data-sharing and Data-mapping attributes for data-refs in OpenMP construct
233 class OmpAttributeVisitor
: DirectiveAttributeVisitor
<llvm::omp::Directive
> {
235 explicit OmpAttributeVisitor(SemanticsContext
&context
)
236 : DirectiveAttributeVisitor(context
) {}
238 template <typename A
> void Walk(const A
&x
) { parser::Walk(x
, *this); }
239 template <typename A
> bool Pre(const A
&) { return true; }
240 template <typename A
> void Post(const A
&) {}
242 template <typename A
> bool Pre(const parser::Statement
<A
> &statement
) {
243 currentStatementSource_
= statement
.source
;
244 // Keep track of the labels in all the labelled statements
245 if (statement
.label
) {
246 auto label
{statement
.label
.value()};
247 // Get the context to check if the labelled statement is in an
248 // enclosing OpenMP construct
249 std::optional
<DirContext
> thisContext
{GetContextIf()};
250 targetLabels_
.emplace(
251 label
, std::make_pair(currentStatementSource_
, thisContext
));
252 // Check if a statement that causes a jump to the 'label'
253 // has already been encountered
254 auto range
{sourceLabels_
.equal_range(label
)};
255 for (auto it
{range
.first
}; it
!= range
.second
; ++it
) {
256 // Check if both the statement with 'label' and the statement that
257 // causes a jump to the 'label' are in the same scope
258 CheckLabelContext(it
->second
.first
, currentStatementSource_
,
259 it
->second
.second
, thisContext
);
265 bool Pre(const parser::InternalSubprogram
&) {
266 // Clear the labels being tracked in the previous scope
271 bool Pre(const parser::ModuleSubprogram
&) {
272 // Clear the labels being tracked in the previous scope
277 bool Pre(const parser::SpecificationPart
&x
) {
278 Walk(std::get
<std::list
<parser::OpenMPDeclarativeConstruct
>>(x
.t
));
282 bool Pre(const parser::StmtFunctionStmt
&x
) {
283 const auto &parsedExpr
{std::get
<parser::Scalar
<parser::Expr
>>(x
.t
)};
284 if (const auto *expr
{GetExpr(context_
, parsedExpr
)}) {
285 for (const Symbol
&symbol
: evaluate::CollectSymbols(*expr
)) {
286 if (!IsStmtFunctionDummy(symbol
)) {
287 stmtFunctionExprSymbols_
.insert(symbol
.GetUltimate());
294 bool Pre(const parser::OpenMPBlockConstruct
&);
295 void Post(const parser::OpenMPBlockConstruct
&);
297 void Post(const parser::OmpBeginBlockDirective
&) {
298 GetContext().withinConstruct
= true;
301 bool Pre(const parser::OpenMPSimpleStandaloneConstruct
&);
302 void Post(const parser::OpenMPSimpleStandaloneConstruct
&) { PopContext(); }
304 bool Pre(const parser::OpenMPLoopConstruct
&);
305 void Post(const parser::OpenMPLoopConstruct
&) { PopContext(); }
306 void Post(const parser::OmpBeginLoopDirective
&) {
307 GetContext().withinConstruct
= true;
309 bool Pre(const parser::DoConstruct
&);
311 bool Pre(const parser::OpenMPSectionsConstruct
&);
312 void Post(const parser::OpenMPSectionsConstruct
&) { PopContext(); }
314 bool Pre(const parser::OpenMPCriticalConstruct
&critical
);
315 void Post(const parser::OpenMPCriticalConstruct
&) { PopContext(); }
317 bool Pre(const parser::OpenMPDeclareSimdConstruct
&x
) {
318 PushContext(x
.source
, llvm::omp::Directive::OMPD_declare_simd
);
319 const auto &name
{std::get
<std::optional
<parser::Name
>>(x
.t
)};
321 ResolveOmpName(*name
, Symbol::Flag::OmpDeclareSimd
);
325 void Post(const parser::OpenMPDeclareSimdConstruct
&) { PopContext(); }
327 bool Pre(const parser::OpenMPRequiresConstruct
&x
) {
328 PushContext(x
.source
, llvm::omp::Directive::OMPD_requires
);
331 void Post(const parser::OpenMPRequiresConstruct
&) { PopContext(); }
333 bool Pre(const parser::OpenMPThreadprivate
&);
334 void Post(const parser::OpenMPThreadprivate
&) { PopContext(); }
336 bool Pre(const parser::OpenMPDeclarativeAllocate
&);
337 void Post(const parser::OpenMPDeclarativeAllocate
&) { PopContext(); }
339 bool Pre(const parser::OpenMPExecutableAllocate
&);
340 void Post(const parser::OpenMPExecutableAllocate
&);
342 // 2.15.3 Data-Sharing Attribute Clauses
343 void Post(const parser::OmpDefaultClause
&);
344 bool Pre(const parser::OmpClause::Shared
&x
) {
345 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpShared
);
348 bool Pre(const parser::OmpClause::Private
&x
) {
349 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpPrivate
);
352 bool Pre(const parser::OmpAllocateClause
&x
) {
353 const auto &objectList
{std::get
<parser::OmpObjectList
>(x
.t
)};
354 ResolveOmpObjectList(objectList
, Symbol::Flag::OmpAllocate
);
357 bool Pre(const parser::OmpClause::Firstprivate
&x
) {
358 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpFirstPrivate
);
361 bool Pre(const parser::OmpClause::Lastprivate
&x
) {
362 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpLastPrivate
);
365 bool Pre(const parser::OmpClause::Copyin
&x
) {
366 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpCopyIn
);
369 bool Pre(const parser::OmpClause::Copyprivate
&x
) {
370 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpCopyPrivate
);
373 bool Pre(const parser::OmpLinearClause
&x
) {
374 common::visit(common::visitors
{
375 [&](const parser::OmpLinearClause::WithoutModifier
376 &linearWithoutModifier
) {
377 ResolveOmpNameList(linearWithoutModifier
.names
,
378 Symbol::Flag::OmpLinear
);
380 [&](const parser::OmpLinearClause::WithModifier
381 &linearWithModifier
) {
383 linearWithModifier
.names
, Symbol::Flag::OmpLinear
);
390 bool Pre(const parser::OmpClause::Reduction
&x
) {
391 const parser::OmpReductionOperator
&opr
{
392 std::get
<parser::OmpReductionOperator
>(x
.v
.t
)};
393 if (const auto *procD
{parser::Unwrap
<parser::ProcedureDesignator
>(opr
.u
)}) {
394 if (const auto *name
{parser::Unwrap
<parser::Name
>(procD
->u
)}) {
396 const auto namePair
{currScope().try_emplace(
397 name
->source
, Attrs
{}, ProcEntityDetails
{})};
398 auto &symbol
{*namePair
.first
->second
};
399 name
->symbol
= &symbol
;
400 name
->symbol
->set(Symbol::Flag::OmpReduction
);
401 AddToContextObjectWithDSA(*name
->symbol
, Symbol::Flag::OmpReduction
);
404 if (const auto *procRef
{
405 parser::Unwrap
<parser::ProcComponentRef
>(procD
->u
)}) {
406 ResolveOmp(*procRef
->v
.thing
.component
.symbol
,
407 Symbol::Flag::OmpReduction
, currScope());
410 const auto &objList
{std::get
<parser::OmpObjectList
>(x
.v
.t
)};
411 ResolveOmpObjectList(objList
, Symbol::Flag::OmpReduction
);
415 bool Pre(const parser::OmpAlignedClause
&x
) {
416 const auto &alignedNameList
{std::get
<std::list
<parser::Name
>>(x
.t
)};
417 ResolveOmpNameList(alignedNameList
, Symbol::Flag::OmpAligned
);
421 bool Pre(const parser::OmpClause::Nontemporal
&x
) {
422 const auto &nontemporalNameList
{x
.v
};
423 ResolveOmpNameList(nontemporalNameList
, Symbol::Flag::OmpNontemporal
);
427 bool Pre(const parser::OmpDependClause
&x
) {
428 if (const auto *dependSink
{
429 std::get_if
<parser::OmpDependClause::Sink
>(&x
.u
)}) {
430 const auto &dependSinkVec
{dependSink
->v
};
431 for (const auto &dependSinkElement
: dependSinkVec
) {
432 const auto &name
{std::get
<parser::Name
>(dependSinkElement
.t
)};
439 void Post(const parser::Name
&);
441 // Keep track of labels in the statements that causes jumps to target labels
442 void Post(const parser::GotoStmt
&gotoStmt
) { CheckSourceLabel(gotoStmt
.v
); }
443 void Post(const parser::ComputedGotoStmt
&computedGotoStmt
) {
444 for (auto &label
: std::get
<std::list
<parser::Label
>>(computedGotoStmt
.t
)) {
445 CheckSourceLabel(label
);
448 void Post(const parser::ArithmeticIfStmt
&arithmeticIfStmt
) {
449 CheckSourceLabel(std::get
<1>(arithmeticIfStmt
.t
));
450 CheckSourceLabel(std::get
<2>(arithmeticIfStmt
.t
));
451 CheckSourceLabel(std::get
<3>(arithmeticIfStmt
.t
));
453 void Post(const parser::AssignedGotoStmt
&assignedGotoStmt
) {
454 for (auto &label
: std::get
<std::list
<parser::Label
>>(assignedGotoStmt
.t
)) {
455 CheckSourceLabel(label
);
458 void Post(const parser::AltReturnSpec
&altReturnSpec
) {
459 CheckSourceLabel(altReturnSpec
.v
);
461 void Post(const parser::ErrLabel
&errLabel
) { CheckSourceLabel(errLabel
.v
); }
462 void Post(const parser::EndLabel
&endLabel
) { CheckSourceLabel(endLabel
.v
); }
463 void Post(const parser::EorLabel
&eorLabel
) { CheckSourceLabel(eorLabel
.v
); }
465 const parser::OmpClause
*associatedClause
{nullptr};
466 void SetAssociatedClause(const parser::OmpClause
&c
) {
467 associatedClause
= &c
;
469 const parser::OmpClause
*GetAssociatedClause() { return associatedClause
; }
472 std::int64_t GetAssociatedLoopLevelFromClauses(const parser::OmpClauseList
&);
474 static constexpr Symbol::Flags dataSharingAttributeFlags
{
475 Symbol::Flag::OmpShared
, Symbol::Flag::OmpPrivate
,
476 Symbol::Flag::OmpFirstPrivate
, Symbol::Flag::OmpLastPrivate
,
477 Symbol::Flag::OmpReduction
, Symbol::Flag::OmpLinear
};
479 static constexpr Symbol::Flags privateDataSharingAttributeFlags
{
480 Symbol::Flag::OmpPrivate
, Symbol::Flag::OmpFirstPrivate
,
481 Symbol::Flag::OmpLastPrivate
};
483 static constexpr Symbol::Flags ompFlagsRequireNewSymbol
{
484 Symbol::Flag::OmpPrivate
, Symbol::Flag::OmpLinear
,
485 Symbol::Flag::OmpFirstPrivate
, Symbol::Flag::OmpLastPrivate
,
486 Symbol::Flag::OmpReduction
, Symbol::Flag::OmpCriticalLock
,
487 Symbol::Flag::OmpCopyIn
};
489 static constexpr Symbol::Flags ompFlagsRequireMark
{
490 Symbol::Flag::OmpThreadprivate
};
492 static constexpr Symbol::Flags dataCopyingAttributeFlags
{
493 Symbol::Flag::OmpCopyIn
, Symbol::Flag::OmpCopyPrivate
};
495 std::vector
<const parser::Name
*> allocateNames_
; // on one directive
496 UnorderedSymbolSet privateDataSharingAttributeObjects_
; // on one directive
497 UnorderedSymbolSet stmtFunctionExprSymbols_
;
498 std::multimap
<const parser::Label
,
499 std::pair
<parser::CharBlock
, std::optional
<DirContext
>>>
501 std::map
<const parser::Label
,
502 std::pair
<parser::CharBlock
, std::optional
<DirContext
>>>
504 parser::CharBlock currentStatementSource_
;
506 void AddAllocateName(const parser::Name
*&object
) {
507 allocateNames_
.push_back(object
);
509 void ClearAllocateNames() { allocateNames_
.clear(); }
511 void AddPrivateDataSharingAttributeObjects(SymbolRef object
) {
512 privateDataSharingAttributeObjects_
.insert(object
);
514 void ClearPrivateDataSharingAttributeObjects() {
515 privateDataSharingAttributeObjects_
.clear();
518 // Predetermined DSA rules
519 void PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
520 const parser::OpenMPLoopConstruct
&);
521 void ResolveSeqLoopIndexInParallelOrTaskConstruct(const parser::Name
&);
523 bool IsNestedInDirective(llvm::omp::Directive directive
);
524 void ResolveOmpObjectList(const parser::OmpObjectList
&, Symbol::Flag
);
525 void ResolveOmpObject(const parser::OmpObject
&, Symbol::Flag
);
526 Symbol
*ResolveOmp(const parser::Name
&, Symbol::Flag
, Scope
&);
527 Symbol
*ResolveOmp(Symbol
&, Symbol::Flag
, Scope
&);
528 Symbol
*ResolveOmpCommonBlockName(const parser::Name
*);
529 void ResolveOmpNameList(const std::list
<parser::Name
> &, Symbol::Flag
);
530 void ResolveOmpName(const parser::Name
&, Symbol::Flag
);
531 Symbol
*ResolveName(const parser::Name
*);
532 Symbol
*ResolveOmpObjectScope(const parser::Name
*);
533 Symbol
*DeclareOrMarkOtherAccessEntity(const parser::Name
&, Symbol::Flag
);
534 Symbol
*DeclareOrMarkOtherAccessEntity(Symbol
&, Symbol::Flag
);
535 void CheckMultipleAppearances(
536 const parser::Name
&, const Symbol
&, Symbol::Flag
);
538 void CheckDataCopyingClause(
539 const parser::Name
&, const Symbol
&, Symbol::Flag
);
540 void CheckAssocLoopLevel(std::int64_t level
, const parser::OmpClause
*clause
);
541 void CheckObjectInNamelist(
542 const parser::Name
&, const Symbol
&, Symbol::Flag
);
543 void CheckSourceLabel(const parser::Label
&);
544 void CheckLabelContext(const parser::CharBlock
, const parser::CharBlock
,
545 std::optional
<DirContext
>, std::optional
<DirContext
>);
547 sourceLabels_
.clear();
548 targetLabels_
.clear();
551 bool HasSymbolInEnclosingScope(const Symbol
&, Scope
&);
552 std::int64_t ordCollapseLevel
{0};
555 template <typename T
>
556 bool DirectiveAttributeVisitor
<T
>::HasDataSharingAttributeObject(
557 const Symbol
&object
) {
558 auto it
{dataSharingAttributeObjects_
.find(object
)};
559 return it
!= dataSharingAttributeObjects_
.end();
562 template <typename T
>
563 const parser::Name
*DirectiveAttributeVisitor
<T
>::GetLoopIndex(
564 const parser::DoConstruct
&x
) {
565 using Bounds
= parser::LoopControl::Bounds
;
566 if (x
.GetLoopControl()) {
567 return &std::get
<Bounds
>(x
.GetLoopControl()->u
).name
.thing
;
570 .Say(std::get
<parser::Statement
<parser::NonLabelDoStmt
>>(x
.t
).source
,
571 "Loop control is not present in the DO LOOP"_err_en_US
)
572 .Attach(GetContext().directiveSource
,
573 "associated with the enclosing LOOP construct"_en_US
);
578 template <typename T
>
579 const parser::DoConstruct
*DirectiveAttributeVisitor
<T
>::GetDoConstructIf(
580 const parser::ExecutionPartConstruct
&x
) {
581 return parser::Unwrap
<parser::DoConstruct
>(x
);
584 template <typename T
>
585 Symbol
*DirectiveAttributeVisitor
<T
>::DeclarePrivateAccessEntity(
586 const parser::Name
&name
, Symbol::Flag flag
, Scope
&scope
) {
588 return nullptr; // not resolved by Name Resolution step, do nothing
590 name
.symbol
= DeclarePrivateAccessEntity(*name
.symbol
, flag
, scope
);
594 template <typename T
>
595 Symbol
*DirectiveAttributeVisitor
<T
>::DeclarePrivateAccessEntity(
596 Symbol
&object
, Symbol::Flag flag
, Scope
&scope
) {
597 if (object
.owner() != currScope()) {
598 auto &symbol
{MakeAssocSymbol(object
.name(), object
, scope
)};
600 if (flag
== Symbol::Flag::OmpCopyIn
) {
601 // The symbol in copyin clause must be threadprivate entity.
602 symbol
.set(Symbol::Flag::OmpThreadprivate
);
611 bool AccAttributeVisitor::Pre(const parser::OpenACCBlockConstruct
&x
) {
612 const auto &beginBlockDir
{std::get
<parser::AccBeginBlockDirective
>(x
.t
)};
613 const auto &blockDir
{std::get
<parser::AccBlockDirective
>(beginBlockDir
.t
)};
614 switch (blockDir
.v
) {
615 case llvm::acc::Directive::ACCD_data
:
616 case llvm::acc::Directive::ACCD_host_data
:
617 case llvm::acc::Directive::ACCD_kernels
:
618 case llvm::acc::Directive::ACCD_parallel
:
619 case llvm::acc::Directive::ACCD_serial
:
620 PushContext(blockDir
.source
, blockDir
.v
);
625 ClearDataSharingAttributeObjects();
629 bool AccAttributeVisitor::Pre(const parser::OpenACCDeclarativeConstruct
&x
) {
630 if (const auto *declConstruct
{
631 std::get_if
<parser::OpenACCStandaloneDeclarativeConstruct
>(&x
.u
)}) {
633 std::get
<parser::AccDeclarativeDirective
>(declConstruct
->t
)};
634 PushContext(declDir
.source
, llvm::acc::Directive::ACCD_declare
);
635 } else if (const auto *routineConstruct
{
636 std::get_if
<parser::OpenACCRoutineConstruct
>(&x
.u
)}) {
637 const auto &verbatim
{std::get
<parser::Verbatim
>(routineConstruct
->t
)};
638 PushContext(verbatim
.source
, llvm::acc::Directive::ACCD_routine
);
640 ClearDataSharingAttributeObjects();
644 static const parser::AccObjectList
&GetAccObjectList(
645 const parser::AccClause
&clause
) {
646 if (const auto *copyClause
=
647 std::get_if
<Fortran::parser::AccClause::Copy
>(&clause
.u
)) {
648 return copyClause
->v
;
649 } else if (const auto *createClause
=
650 std::get_if
<Fortran::parser::AccClause::Create
>(&clause
.u
)) {
651 const Fortran::parser::AccObjectListWithModifier
&listWithModifier
=
653 const Fortran::parser::AccObjectList
&accObjectList
=
654 std::get
<Fortran::parser::AccObjectList
>(listWithModifier
.t
);
655 return accObjectList
;
656 } else if (const auto *copyinClause
=
657 std::get_if
<Fortran::parser::AccClause::Copyin
>(&clause
.u
)) {
658 const Fortran::parser::AccObjectListWithModifier
&listWithModifier
=
660 const Fortran::parser::AccObjectList
&accObjectList
=
661 std::get
<Fortran::parser::AccObjectList
>(listWithModifier
.t
);
662 return accObjectList
;
663 } else if (const auto *copyoutClause
=
664 std::get_if
<Fortran::parser::AccClause::Copyout
>(&clause
.u
)) {
665 const Fortran::parser::AccObjectListWithModifier
&listWithModifier
=
667 const Fortran::parser::AccObjectList
&accObjectList
=
668 std::get
<Fortran::parser::AccObjectList
>(listWithModifier
.t
);
669 return accObjectList
;
670 } else if (const auto *presentClause
=
671 std::get_if
<Fortran::parser::AccClause::Present
>(&clause
.u
)) {
672 return presentClause
->v
;
673 } else if (const auto *deviceptrClause
=
674 std::get_if
<Fortran::parser::AccClause::Deviceptr
>(
676 return deviceptrClause
->v
;
677 } else if (const auto *deviceResidentClause
=
678 std::get_if
<Fortran::parser::AccClause::DeviceResident
>(
680 return deviceResidentClause
->v
;
681 } else if (const auto *linkClause
=
682 std::get_if
<Fortran::parser::AccClause::Link
>(&clause
.u
)) {
683 return linkClause
->v
;
685 llvm_unreachable("Clause without object list!");
689 void AccAttributeVisitor::Post(
690 const parser::OpenACCStandaloneDeclarativeConstruct
&x
) {
691 const auto &clauseList
= std::get
<parser::AccClauseList
>(x
.t
);
692 for (const auto &clause
: clauseList
.v
) {
693 // Restriction - line 2414
694 DoNotAllowAssumedSizedArray(GetAccObjectList(clause
));
698 bool AccAttributeVisitor::Pre(const parser::OpenACCLoopConstruct
&x
) {
699 const auto &beginDir
{std::get
<parser::AccBeginLoopDirective
>(x
.t
)};
700 const auto &loopDir
{std::get
<parser::AccLoopDirective
>(beginDir
.t
)};
701 const auto &clauseList
{std::get
<parser::AccClauseList
>(beginDir
.t
)};
702 if (loopDir
.v
== llvm::acc::Directive::ACCD_loop
) {
703 PushContext(loopDir
.source
, loopDir
.v
);
705 ClearDataSharingAttributeObjects();
706 SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList
));
707 PrivatizeAssociatedLoopIndex(x
);
711 bool AccAttributeVisitor::Pre(const parser::OpenACCStandaloneConstruct
&x
) {
712 const auto &standaloneDir
{std::get
<parser::AccStandaloneDirective
>(x
.t
)};
713 switch (standaloneDir
.v
) {
714 case llvm::acc::Directive::ACCD_enter_data
:
715 case llvm::acc::Directive::ACCD_exit_data
:
716 case llvm::acc::Directive::ACCD_init
:
717 case llvm::acc::Directive::ACCD_set
:
718 case llvm::acc::Directive::ACCD_shutdown
:
719 case llvm::acc::Directive::ACCD_update
:
720 PushContext(standaloneDir
.source
, standaloneDir
.v
);
725 ClearDataSharingAttributeObjects();
729 Symbol
*AccAttributeVisitor::ResolveName(const parser::Name
&name
) {
730 Symbol
*prev
{currScope().FindSymbol(name
.source
)};
731 if (prev
!= name
.symbol
) {
737 bool AccAttributeVisitor::Pre(const parser::OpenACCRoutineConstruct
&x
) {
738 const auto &optName
{std::get
<std::optional
<parser::Name
>>(x
.t
)};
740 if (!ResolveName(*optName
)) {
741 context_
.Say((*optName
).source
,
742 "No function or subroutine declared for '%s'"_err_en_US
,
749 bool AccAttributeVisitor::Pre(const parser::AccBindClause
&x
) {
750 if (const auto *name
{std::get_if
<parser::Name
>(&x
.u
)}) {
751 if (!ResolveName(*name
)) {
752 context_
.Say(name
->source
,
753 "No function or subroutine declared for '%s'"_err_en_US
,
760 bool AccAttributeVisitor::Pre(const parser::OpenACCCombinedConstruct
&x
) {
761 const auto &beginBlockDir
{std::get
<parser::AccBeginCombinedDirective
>(x
.t
)};
762 const auto &combinedDir
{
763 std::get
<parser::AccCombinedDirective
>(beginBlockDir
.t
)};
764 switch (combinedDir
.v
) {
765 case llvm::acc::Directive::ACCD_kernels_loop
:
766 case llvm::acc::Directive::ACCD_parallel_loop
:
767 case llvm::acc::Directive::ACCD_serial_loop
:
768 PushContext(combinedDir
.source
, combinedDir
.v
);
773 ClearDataSharingAttributeObjects();
777 static bool IsLastNameArray(const parser::Designator
&designator
) {
778 const auto &name
{GetLastName(designator
)};
779 const evaluate::DataRef dataRef
{*(name
.symbol
)};
780 return common::visit(
782 [](const evaluate::SymbolRef
&ref
) { return ref
->Rank() > 0; },
783 [](const evaluate::ArrayRef
&aref
) {
784 return aref
.base().IsSymbol() ||
785 aref
.base().GetComponent().base().Rank() == 0;
787 [](const auto &) { return false; },
792 void AccAttributeVisitor::AllowOnlyArrayAndSubArray(
793 const parser::AccObjectList
&objectList
) {
794 for (const auto &accObject
: objectList
.v
) {
797 [&](const parser::Designator
&designator
) {
798 if (!IsLastNameArray(designator
)) {
799 context_
.Say(designator
.source
,
800 "Only array element or subarray are allowed in %s directive"_err_en_US
,
801 parser::ToUpperCaseLetters(
802 llvm::acc::getOpenACCDirectiveName(
803 GetContext().directive
)
807 [&](const auto &name
) {
808 context_
.Say(name
.source
,
809 "Only array element or subarray are allowed in %s directive"_err_en_US
,
810 parser::ToUpperCaseLetters(
811 llvm::acc::getOpenACCDirectiveName(GetContext().directive
)
819 void AccAttributeVisitor::DoNotAllowAssumedSizedArray(
820 const parser::AccObjectList
&objectList
) {
821 for (const auto &accObject
: objectList
.v
) {
824 [&](const parser::Designator
&designator
) {
825 const auto &name
{GetLastName(designator
)};
826 if (name
.symbol
&& semantics::IsAssumedSizeArray(*name
.symbol
)) {
827 context_
.Say(designator
.source
,
828 "Assumed-size dummy arrays may not appear on the %s "
829 "directive"_err_en_US
,
830 parser::ToUpperCaseLetters(
831 llvm::acc::getOpenACCDirectiveName(
832 GetContext().directive
)
836 [&](const auto &name
) {
844 bool AccAttributeVisitor::Pre(const parser::OpenACCCacheConstruct
&x
) {
845 const auto &verbatim
{std::get
<parser::Verbatim
>(x
.t
)};
846 PushContext(verbatim
.source
, llvm::acc::Directive::ACCD_cache
);
847 ClearDataSharingAttributeObjects();
849 const auto &objectListWithModifier
=
850 std::get
<parser::AccObjectListWithModifier
>(x
.t
);
851 const auto &objectList
=
852 std::get
<Fortran::parser::AccObjectList
>(objectListWithModifier
.t
);
854 // 2.10 Cache directive restriction: A var in a cache directive must be a
855 // single array element or a simple subarray.
856 AllowOnlyArrayAndSubArray(objectList
);
861 std::int64_t AccAttributeVisitor::GetAssociatedLoopLevelFromClauses(
862 const parser::AccClauseList
&x
) {
863 std::int64_t collapseLevel
{0};
864 for (const auto &clause
: x
.v
) {
865 if (const auto *collapseClause
{
866 std::get_if
<parser::AccClause::Collapse
>(&clause
.u
)}) {
867 if (const auto v
{EvaluateInt64(context_
, collapseClause
->v
)}) {
874 return collapseLevel
;
876 return 1; // default is outermost loop
879 void AccAttributeVisitor::PrivatizeAssociatedLoopIndex(
880 const parser::OpenACCLoopConstruct
&x
) {
881 std::int64_t level
{GetContext().associatedLoopLevel
};
882 if (level
<= 0) { // collpase value was negative or 0
885 Symbol::Flag ivDSA
{Symbol::Flag::AccPrivate
};
887 const auto getNextDoConstruct
=
888 [this](const parser::Block
&block
) -> const parser::DoConstruct
* {
889 for (const auto &entry
: block
) {
890 if (const auto *doConstruct
= GetDoConstructIf(entry
)) {
892 } else if (parser::Unwrap
<parser::CompilerDirective
>(entry
)) {
893 // It is allowed to have a compiler directive associated with the loop.
902 const auto &outer
{std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)};
903 for (const parser::DoConstruct
*loop
{&*outer
}; loop
&& level
> 0; --level
) {
904 // go through all the nested do-loops and resolve index variables
905 const parser::Name
*iv
{GetLoopIndex(*loop
)};
907 if (auto *symbol
{ResolveAcc(*iv
, ivDSA
, currScope())}) {
908 symbol
->set(Symbol::Flag::AccPreDetermined
);
909 iv
->symbol
= symbol
; // adjust the symbol within region
910 AddToContextObjectWithDSA(*symbol
, ivDSA
);
914 const auto &block
{std::get
<parser::Block
>(loop
->t
)};
915 loop
= getNextDoConstruct(block
);
920 void AccAttributeVisitor::EnsureAllocatableOrPointer(
921 const llvm::acc::Clause clause
, const parser::AccObjectList
&objectList
) {
922 for (const auto &accObject
: objectList
.v
) {
925 [&](const parser::Designator
&designator
) {
926 const auto &lastName
{GetLastName(designator
)};
927 if (!IsAllocatableOrPointer(*lastName
.symbol
)) {
928 context_
.Say(designator
.source
,
929 "Argument `%s` on the %s clause must be a variable or "
930 "array with the POINTER or ALLOCATABLE attribute"_err_en_US
,
931 lastName
.symbol
->name(),
932 parser::ToUpperCaseLetters(
933 llvm::acc::getOpenACCClauseName(clause
).str()));
936 [&](const auto &name
) {
937 context_
.Say(name
.source
,
938 "Argument on the %s clause must be a variable or "
939 "array with the POINTER or ALLOCATABLE attribute"_err_en_US
,
940 parser::ToUpperCaseLetters(
941 llvm::acc::getOpenACCClauseName(clause
).str()));
948 bool AccAttributeVisitor::Pre(const parser::AccClause::Attach
&x
) {
949 // Restriction - line 1708-1709
950 EnsureAllocatableOrPointer(llvm::acc::Clause::ACCC_attach
, x
.v
);
954 bool AccAttributeVisitor::Pre(const parser::AccClause::Detach
&x
) {
955 // Restriction - line 1715-1717
956 EnsureAllocatableOrPointer(llvm::acc::Clause::ACCC_detach
, x
.v
);
960 void AccAttributeVisitor::Post(const parser::AccDefaultClause
&x
) {
961 if (!dirContext_
.empty()) {
963 case llvm::acc::DefaultValue::ACC_Default_present
:
964 SetContextDefaultDSA(Symbol::Flag::AccPresent
);
966 case llvm::acc::DefaultValue::ACC_Default_none
:
967 SetContextDefaultDSA(Symbol::Flag::AccNone
);
973 // For OpenACC constructs, check all the data-refs within the constructs
974 // and adjust the symbol for each Name if necessary
975 void AccAttributeVisitor::Post(const parser::Name
&name
) {
976 auto *symbol
{name
.symbol
};
977 if (symbol
&& !dirContext_
.empty() && GetContext().withinConstruct
) {
978 if (!symbol
->owner().IsDerivedType() && !symbol
->has
<ProcEntityDetails
>() &&
979 !IsObjectWithDSA(*symbol
)) {
980 if (Symbol
* found
{currScope().FindSymbol(name
.source
)}) {
981 if (symbol
!= found
) {
982 name
.symbol
= found
; // adjust the symbol within region
983 } else if (GetContext().defaultDSA
== Symbol::Flag::AccNone
) {
985 context_
.Say(name
.source
,
986 "The DEFAULT(NONE) clause requires that '%s' must be listed in "
987 "a data-mapping clause"_err_en_US
,
992 } // within OpenACC construct
995 Symbol
*AccAttributeVisitor::ResolveAccCommonBlockName(
996 const parser::Name
*name
) {
999 } else if (auto *prev
{
1000 GetContext().scope
.parent().FindCommonBlock(name
->source
)}) {
1001 name
->symbol
= prev
;
1008 void AccAttributeVisitor::ResolveAccObjectList(
1009 const parser::AccObjectList
&accObjectList
, Symbol::Flag accFlag
) {
1010 for (const auto &accObject
: accObjectList
.v
) {
1011 ResolveAccObject(accObject
, accFlag
);
1015 void AccAttributeVisitor::ResolveAccObject(
1016 const parser::AccObject
&accObject
, Symbol::Flag accFlag
) {
1019 [&](const parser::Designator
&designator
) {
1020 if (const auto *name
{GetDesignatorNameIfDataRef(designator
)}) {
1021 if (auto *symbol
{ResolveAcc(*name
, accFlag
, currScope())}) {
1022 AddToContextObjectWithDSA(*symbol
, accFlag
);
1023 if (dataSharingAttributeFlags
.test(accFlag
)) {
1024 CheckMultipleAppearances(*name
, *symbol
, accFlag
);
1028 // Array sections to be changed to substrings as needed
1029 if (AnalyzeExpr(context_
, designator
)) {
1030 if (std::holds_alternative
<parser::Substring
>(designator
.u
)) {
1031 context_
.Say(designator
.source
,
1032 "Substrings are not allowed on OpenACC "
1033 "directives or clauses"_err_en_US
);
1036 // other checks, more TBD
1039 [&](const parser::Name
&name
) { // common block
1040 if (auto *symbol
{ResolveAccCommonBlockName(&name
)}) {
1041 CheckMultipleAppearances(
1042 name
, *symbol
, Symbol::Flag::AccCommonBlock
);
1043 for (auto &object
: symbol
->get
<CommonBlockDetails
>().objects()) {
1044 if (auto *resolvedObject
{
1045 ResolveAcc(*object
, accFlag
, currScope())}) {
1046 AddToContextObjectWithDSA(*resolvedObject
, accFlag
);
1050 context_
.Say(name
.source
,
1051 "COMMON block must be declared in the same scoping unit "
1052 "in which the OpenACC directive or clause appears"_err_en_US
);
1059 Symbol
*AccAttributeVisitor::ResolveAcc(
1060 const parser::Name
&name
, Symbol::Flag accFlag
, Scope
&scope
) {
1061 if (accFlagsRequireNewSymbol
.test(accFlag
)) {
1062 return DeclarePrivateAccessEntity(name
, accFlag
, scope
);
1064 return DeclareOrMarkOtherAccessEntity(name
, accFlag
);
1068 Symbol
*AccAttributeVisitor::ResolveAcc(
1069 Symbol
&symbol
, Symbol::Flag accFlag
, Scope
&scope
) {
1070 if (accFlagsRequireNewSymbol
.test(accFlag
)) {
1071 return DeclarePrivateAccessEntity(symbol
, accFlag
, scope
);
1073 return DeclareOrMarkOtherAccessEntity(symbol
, accFlag
);
1077 Symbol
*AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1078 const parser::Name
&name
, Symbol::Flag accFlag
) {
1079 Symbol
*prev
{currScope().FindSymbol(name
.source
)};
1080 if (!name
.symbol
|| !prev
) {
1082 } else if (prev
!= name
.symbol
) {
1085 return DeclareOrMarkOtherAccessEntity(*prev
, accFlag
);
1088 Symbol
*AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1089 Symbol
&object
, Symbol::Flag accFlag
) {
1090 if (accFlagsRequireMark
.test(accFlag
)) {
1091 object
.set(accFlag
);
1096 static bool WithMultipleAppearancesAccException(
1097 const Symbol
&symbol
, Symbol::Flag flag
) {
1098 return false; // Place holder
1101 void AccAttributeVisitor::CheckMultipleAppearances(
1102 const parser::Name
&name
, const Symbol
&symbol
, Symbol::Flag accFlag
) {
1103 const auto *target
{&symbol
};
1104 if (accFlagsRequireNewSymbol
.test(accFlag
)) {
1105 if (const auto *details
{symbol
.detailsIf
<HostAssocDetails
>()}) {
1106 target
= &details
->symbol();
1109 if (HasDataSharingAttributeObject(*target
) &&
1110 !WithMultipleAppearancesAccException(symbol
, accFlag
)) {
1111 context_
.Say(name
.source
,
1112 "'%s' appears in more than one data-sharing clause "
1113 "on the same OpenACC directive"_err_en_US
,
1116 AddDataSharingAttributeObject(*target
);
1120 bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct
&x
) {
1121 const auto &beginBlockDir
{std::get
<parser::OmpBeginBlockDirective
>(x
.t
)};
1122 const auto &beginDir
{std::get
<parser::OmpBlockDirective
>(beginBlockDir
.t
)};
1123 switch (beginDir
.v
) {
1124 case llvm::omp::Directive::OMPD_master
:
1125 case llvm::omp::Directive::OMPD_ordered
:
1126 case llvm::omp::Directive::OMPD_parallel
:
1127 case llvm::omp::Directive::OMPD_single
:
1128 case llvm::omp::Directive::OMPD_target
:
1129 case llvm::omp::Directive::OMPD_target_data
:
1130 case llvm::omp::Directive::OMPD_task
:
1131 case llvm::omp::Directive::OMPD_taskgroup
:
1132 case llvm::omp::Directive::OMPD_teams
:
1133 case llvm::omp::Directive::OMPD_workshare
:
1134 case llvm::omp::Directive::OMPD_parallel_workshare
:
1135 case llvm::omp::Directive::OMPD_target_teams
:
1136 case llvm::omp::Directive::OMPD_target_parallel
:
1137 PushContext(beginDir
.source
, beginDir
.v
);
1143 ClearDataSharingAttributeObjects();
1144 ClearPrivateDataSharingAttributeObjects();
1145 ClearAllocateNames();
1149 void OmpAttributeVisitor::Post(const parser::OpenMPBlockConstruct
&x
) {
1150 const auto &beginBlockDir
{std::get
<parser::OmpBeginBlockDirective
>(x
.t
)};
1151 const auto &beginDir
{std::get
<parser::OmpBlockDirective
>(beginBlockDir
.t
)};
1152 switch (beginDir
.v
) {
1153 case llvm::omp::Directive::OMPD_parallel
:
1154 case llvm::omp::Directive::OMPD_single
:
1155 case llvm::omp::Directive::OMPD_target
:
1156 case llvm::omp::Directive::OMPD_task
:
1157 case llvm::omp::Directive::OMPD_teams
:
1158 case llvm::omp::Directive::OMPD_parallel_workshare
:
1159 case llvm::omp::Directive::OMPD_target_teams
:
1160 case llvm::omp::Directive::OMPD_target_parallel
: {
1162 for (const auto *allocName
: allocateNames_
) {
1164 for (auto privateObj
: privateDataSharingAttributeObjects_
) {
1165 const Symbol
&symbolPrivate
{*privateObj
};
1166 if (allocName
->source
== symbolPrivate
.name()) {
1172 context_
.Say(allocName
->source
,
1173 "The ALLOCATE clause requires that '%s' must be listed in a "
1175 "data-sharing attribute clause on the same directive"_err_en_US
,
1176 allocName
->ToString());
1187 bool OmpAttributeVisitor::Pre(
1188 const parser::OpenMPSimpleStandaloneConstruct
&x
) {
1189 const auto &standaloneDir
{
1190 std::get
<parser::OmpSimpleStandaloneDirective
>(x
.t
)};
1191 switch (standaloneDir
.v
) {
1192 case llvm::omp::Directive::OMPD_barrier
:
1193 case llvm::omp::Directive::OMPD_ordered
:
1194 case llvm::omp::Directive::OMPD_target_enter_data
:
1195 case llvm::omp::Directive::OMPD_target_exit_data
:
1196 case llvm::omp::Directive::OMPD_target_update
:
1197 case llvm::omp::Directive::OMPD_taskwait
:
1198 case llvm::omp::Directive::OMPD_taskyield
:
1199 PushContext(standaloneDir
.source
, standaloneDir
.v
);
1204 ClearDataSharingAttributeObjects();
1208 bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct
&x
) {
1209 const auto &beginLoopDir
{std::get
<parser::OmpBeginLoopDirective
>(x
.t
)};
1210 const auto &beginDir
{std::get
<parser::OmpLoopDirective
>(beginLoopDir
.t
)};
1211 const auto &clauseList
{std::get
<parser::OmpClauseList
>(beginLoopDir
.t
)};
1212 switch (beginDir
.v
) {
1213 case llvm::omp::Directive::OMPD_distribute
:
1214 case llvm::omp::Directive::OMPD_distribute_parallel_do
:
1215 case llvm::omp::Directive::OMPD_distribute_parallel_do_simd
:
1216 case llvm::omp::Directive::OMPD_distribute_simd
:
1217 case llvm::omp::Directive::OMPD_do
:
1218 case llvm::omp::Directive::OMPD_do_simd
:
1219 case llvm::omp::Directive::OMPD_parallel_do
:
1220 case llvm::omp::Directive::OMPD_parallel_do_simd
:
1221 case llvm::omp::Directive::OMPD_simd
:
1222 case llvm::omp::Directive::OMPD_target_parallel_do
:
1223 case llvm::omp::Directive::OMPD_target_parallel_do_simd
:
1224 case llvm::omp::Directive::OMPD_target_teams_distribute
:
1225 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do
:
1226 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd
:
1227 case llvm::omp::Directive::OMPD_target_teams_distribute_simd
:
1228 case llvm::omp::Directive::OMPD_target_simd
:
1229 case llvm::omp::Directive::OMPD_taskloop
:
1230 case llvm::omp::Directive::OMPD_taskloop_simd
:
1231 case llvm::omp::Directive::OMPD_teams_distribute
:
1232 case llvm::omp::Directive::OMPD_teams_distribute_parallel_do
:
1233 case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd
:
1234 case llvm::omp::Directive::OMPD_teams_distribute_simd
:
1235 case llvm::omp::Directive::OMPD_tile
:
1236 case llvm::omp::Directive::OMPD_unroll
:
1237 PushContext(beginDir
.source
, beginDir
.v
);
1242 ClearDataSharingAttributeObjects();
1243 SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList
));
1245 if (beginDir
.v
== llvm::omp::Directive::OMPD_do
) {
1246 if (const auto &doConstruct
{
1247 std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)}) {
1248 if (doConstruct
.value().IsDoWhile()) {
1253 PrivatizeAssociatedLoopIndexAndCheckLoopLevel(x
);
1254 ordCollapseLevel
= GetAssociatedLoopLevelFromClauses(clauseList
) + 1;
1258 void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct(
1259 const parser::Name
&iv
) {
1260 auto targetIt
{dirContext_
.rbegin()};
1261 for (;; ++targetIt
) {
1262 if (targetIt
== dirContext_
.rend()) {
1265 if (llvm::omp::parallelSet
.test(targetIt
->directive
) ||
1266 llvm::omp::taskGeneratingSet
.test(targetIt
->directive
)) {
1270 if (auto *symbol
{ResolveOmp(iv
, Symbol::Flag::OmpPrivate
, targetIt
->scope
)}) {
1272 symbol
->set(Symbol::Flag::OmpPreDetermined
);
1273 iv
.symbol
= symbol
; // adjust the symbol within region
1274 for (auto it
{dirContext_
.rbegin()}; it
!= targetIt
; ++it
) {
1275 AddToContextObjectWithDSA(*symbol
, Symbol::Flag::OmpPrivate
, *it
);
1280 // [OMP-4.5]2.15.1.1 Data-sharing Attribute Rules - Predetermined
1281 // - A loop iteration variable for a sequential loop in a parallel
1282 // or task generating construct is private in the innermost such
1283 // construct that encloses the loop
1284 // Loop iteration variables are not well defined for DO WHILE loop.
1285 // Use of DO CONCURRENT inside OpenMP construct is unspecified behavior
1286 // till OpenMP-5.0 standard.
1287 // In above both cases we skip the privatization of iteration variables.
1288 bool OmpAttributeVisitor::Pre(const parser::DoConstruct
&x
) {
1289 // TODO:[OpenMP 5.1] DO CONCURRENT indices are private
1290 if (x
.IsDoNormal()) {
1291 if (!dirContext_
.empty() && GetContext().withinConstruct
) {
1292 const parser::Name
*iv
{GetLoopIndex(x
)};
1293 if (iv
&& iv
->symbol
) {
1294 if (!iv
->symbol
->test(Symbol::Flag::OmpPreDetermined
)) {
1295 ResolveSeqLoopIndexInParallelOrTaskConstruct(*iv
);
1297 // TODO: conflict checks with explicitly determined DSA
1300 if (ordCollapseLevel
) {
1301 if (const auto *details
{iv
->symbol
->detailsIf
<HostAssocDetails
>()}) {
1302 const Symbol
*tpSymbol
= &details
->symbol();
1303 if (tpSymbol
->test(Symbol::Flag::OmpThreadprivate
)) {
1304 context_
.Say(iv
->source
,
1305 "Loop iteration variable %s is not allowed in THREADPRIVATE."_err_en_US
,
1316 std::int64_t OmpAttributeVisitor::GetAssociatedLoopLevelFromClauses(
1317 const parser::OmpClauseList
&x
) {
1318 std::int64_t orderedLevel
{0};
1319 std::int64_t collapseLevel
{0};
1321 const parser::OmpClause
*ordClause
{nullptr};
1322 const parser::OmpClause
*collClause
{nullptr};
1324 for (const auto &clause
: x
.v
) {
1325 if (const auto *orderedClause
{
1326 std::get_if
<parser::OmpClause::Ordered
>(&clause
.u
)}) {
1327 if (const auto v
{EvaluateInt64(context_
, orderedClause
->v
)}) {
1330 ordClause
= &clause
;
1332 if (const auto *collapseClause
{
1333 std::get_if
<parser::OmpClause::Collapse
>(&clause
.u
)}) {
1334 if (const auto v
{EvaluateInt64(context_
, collapseClause
->v
)}) {
1337 collClause
= &clause
;
1341 if (orderedLevel
&& (!collapseLevel
|| orderedLevel
>= collapseLevel
)) {
1342 SetAssociatedClause(*ordClause
);
1343 return orderedLevel
;
1344 } else if (!orderedLevel
&& collapseLevel
) {
1345 SetAssociatedClause(*collClause
);
1346 return collapseLevel
;
1347 } // orderedLevel < collapseLevel is an error handled in structural checks
1348 return 1; // default is outermost loop
1351 // 2.15.1.1 Data-sharing Attribute Rules - Predetermined
1352 // - The loop iteration variable(s) in the associated do-loop(s) of a do,
1353 // parallel do, taskloop, or distribute construct is (are) private.
1354 // - The loop iteration variable in the associated do-loop of a simd construct
1355 // with just one associated do-loop is linear with a linear-step that is the
1356 // increment of the associated do-loop.
1357 // - The loop iteration variables in the associated do-loops of a simd
1358 // construct with multiple associated do-loops are lastprivate.
1359 void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
1360 const parser::OpenMPLoopConstruct
&x
) {
1361 std::int64_t level
{GetContext().associatedLoopLevel
};
1366 if (!llvm::omp::simdSet
.test(GetContext().directive
)) {
1367 ivDSA
= Symbol::Flag::OmpPrivate
;
1368 } else if (level
== 1) {
1369 ivDSA
= Symbol::Flag::OmpLinear
;
1371 ivDSA
= Symbol::Flag::OmpLastPrivate
;
1374 const auto &outer
{std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)};
1375 for (const parser::DoConstruct
*loop
{&*outer
}; loop
&& level
> 0; --level
) {
1376 // go through all the nested do-loops and resolve index variables
1377 const parser::Name
*iv
{GetLoopIndex(*loop
)};
1379 if (auto *symbol
{ResolveOmp(*iv
, ivDSA
, currScope())}) {
1380 symbol
->set(Symbol::Flag::OmpPreDetermined
);
1381 iv
->symbol
= symbol
; // adjust the symbol within region
1382 AddToContextObjectWithDSA(*symbol
, ivDSA
);
1385 const auto &block
{std::get
<parser::Block
>(loop
->t
)};
1386 const auto it
{block
.begin()};
1387 loop
= it
!= block
.end() ? GetDoConstructIf(*it
) : nullptr;
1390 CheckAssocLoopLevel(level
, GetAssociatedClause());
1392 void OmpAttributeVisitor::CheckAssocLoopLevel(
1393 std::int64_t level
, const parser::OmpClause
*clause
) {
1394 if (clause
&& level
!= 0) {
1395 context_
.Say(clause
->source
,
1396 "The value of the parameter in the COLLAPSE or ORDERED clause must"
1397 " not be larger than the number of nested loops"
1398 " following the construct."_err_en_US
);
1402 bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct
&x
) {
1403 const auto &beginSectionsDir
{
1404 std::get
<parser::OmpBeginSectionsDirective
>(x
.t
)};
1405 const auto &beginDir
{
1406 std::get
<parser::OmpSectionsDirective
>(beginSectionsDir
.t
)};
1407 switch (beginDir
.v
) {
1408 case llvm::omp::Directive::OMPD_parallel_sections
:
1409 case llvm::omp::Directive::OMPD_sections
:
1410 PushContext(beginDir
.source
, beginDir
.v
);
1415 ClearDataSharingAttributeObjects();
1419 bool OmpAttributeVisitor::Pre(const parser::OpenMPCriticalConstruct
&x
) {
1420 const auto &beginCriticalDir
{std::get
<parser::OmpCriticalDirective
>(x
.t
)};
1421 const auto &endCriticalDir
{std::get
<parser::OmpEndCriticalDirective
>(x
.t
)};
1422 PushContext(beginCriticalDir
.source
, llvm::omp::Directive::OMPD_critical
);
1423 if (const auto &criticalName
{
1424 std::get
<std::optional
<parser::Name
>>(beginCriticalDir
.t
)}) {
1425 ResolveOmpName(*criticalName
, Symbol::Flag::OmpCriticalLock
);
1427 if (const auto &endCriticalName
{
1428 std::get
<std::optional
<parser::Name
>>(endCriticalDir
.t
)}) {
1429 ResolveOmpName(*endCriticalName
, Symbol::Flag::OmpCriticalLock
);
1434 bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate
&x
) {
1435 PushContext(x
.source
, llvm::omp::Directive::OMPD_threadprivate
);
1436 const auto &list
{std::get
<parser::OmpObjectList
>(x
.t
)};
1437 ResolveOmpObjectList(list
, Symbol::Flag::OmpThreadprivate
);
1441 bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclarativeAllocate
&x
) {
1442 PushContext(x
.source
, llvm::omp::Directive::OMPD_allocate
);
1443 const auto &list
{std::get
<parser::OmpObjectList
>(x
.t
)};
1444 ResolveOmpObjectList(list
, Symbol::Flag::OmpDeclarativeAllocateDirective
);
1448 bool OmpAttributeVisitor::Pre(const parser::OpenMPExecutableAllocate
&x
) {
1449 PushContext(x
.source
, llvm::omp::Directive::OMPD_allocate
);
1450 const auto &list
{std::get
<std::optional
<parser::OmpObjectList
>>(x
.t
)};
1452 ResolveOmpObjectList(*list
, Symbol::Flag::OmpExecutableAllocateDirective
);
1457 void OmpAttributeVisitor::Post(const parser::OmpDefaultClause
&x
) {
1458 if (!dirContext_
.empty()) {
1460 case parser::OmpDefaultClause::Type::Private
:
1461 SetContextDefaultDSA(Symbol::Flag::OmpPrivate
);
1463 case parser::OmpDefaultClause::Type::Firstprivate
:
1464 SetContextDefaultDSA(Symbol::Flag::OmpFirstPrivate
);
1466 case parser::OmpDefaultClause::Type::Shared
:
1467 SetContextDefaultDSA(Symbol::Flag::OmpShared
);
1469 case parser::OmpDefaultClause::Type::None
:
1470 SetContextDefaultDSA(Symbol::Flag::OmpNone
);
1476 bool OmpAttributeVisitor::IsNestedInDirective(llvm::omp::Directive directive
) {
1477 if (dirContext_
.size() >= 1) {
1478 for (std::size_t i
= dirContext_
.size() - 1; i
> 0; --i
) {
1479 if (dirContext_
[i
- 1].directive
== directive
) {
1487 void OmpAttributeVisitor::Post(const parser::OpenMPExecutableAllocate
&x
) {
1488 bool hasAllocator
= false;
1489 // TODO: Investigate whether searching the clause list can be done with
1490 // parser::Unwrap instead of the following loop
1491 const auto &clauseList
{std::get
<parser::OmpClauseList
>(x
.t
)};
1492 for (const auto &clause
: clauseList
.v
) {
1493 if (std::get_if
<parser::OmpClause::Allocator
>(&clause
.u
)) {
1494 hasAllocator
= true;
1498 if (IsNestedInDirective(llvm::omp::Directive::OMPD_target
) && !hasAllocator
) {
1499 // TODO: expand this check to exclude the case when a requires
1500 // directive with the dynamic_allocators clause is present
1501 // in the same compilation unit (OMP5.0 2.11.3).
1502 context_
.Say(x
.source
,
1503 "ALLOCATE directives that appear in a TARGET region "
1504 "must specify an allocator clause"_err_en_US
);
1509 // For OpenMP constructs, check all the data-refs within the constructs
1510 // and adjust the symbol for each Name if necessary
1511 void OmpAttributeVisitor::Post(const parser::Name
&name
) {
1512 auto *symbol
{name
.symbol
};
1513 if (symbol
&& !dirContext_
.empty() && GetContext().withinConstruct
) {
1514 if (!symbol
->owner().IsDerivedType() && !IsProcedure(*symbol
) &&
1515 !IsObjectWithDSA(*symbol
) && !IsNamedConstant(*symbol
)) {
1516 // TODO: create a separate function to go through the rules for
1517 // predetermined, explicitly determined, and implicitly
1518 // determined data-sharing attributes (2.15.1.1).
1519 if (Symbol
* found
{currScope().FindSymbol(name
.source
)}) {
1520 if (symbol
!= found
) {
1521 name
.symbol
= found
; // adjust the symbol within region
1522 } else if (GetContext().defaultDSA
== Symbol::Flag::OmpNone
) {
1523 context_
.Say(name
.source
,
1524 "The DEFAULT(NONE) clause requires that '%s' must be listed in "
1525 "a data-sharing attribute clause"_err_en_US
,
1530 std::vector
<Symbol
*> defaultDSASymbols
;
1531 for (int dirDepth
{0}; dirDepth
< (int)dirContext_
.size(); ++dirDepth
) {
1532 DirContext
&dirContext
= dirContext_
[dirDepth
];
1533 bool hasDataSharingAttr
{false};
1534 for (auto symMap
: dirContext
.objectWithDSA
) {
1535 // if the `symbol` already has a data-sharing attribute
1536 if (symMap
.first
->name() == name
.symbol
->name()) {
1537 hasDataSharingAttr
= true;
1541 if (hasDataSharingAttr
) {
1542 if (defaultDSASymbols
.size())
1543 symbol
= &MakeAssocSymbol(symbol
->name(), *defaultDSASymbols
.back(),
1544 context_
.FindScope(dirContext
.directiveSource
));
1548 if (dirContext
.defaultDSA
== semantics::Symbol::Flag::OmpPrivate
||
1549 dirContext
.defaultDSA
== semantics::Symbol::Flag::OmpFirstPrivate
) {
1550 Symbol
*hostSymbol
= defaultDSASymbols
.size() ? defaultDSASymbols
.back()
1551 : &symbol
->GetUltimate();
1552 defaultDSASymbols
.push_back(
1553 DeclarePrivateAccessEntity(*hostSymbol
, dirContext
.defaultDSA
,
1554 context_
.FindScope(dirContext
.directiveSource
)));
1555 } else if (defaultDSASymbols
.size())
1556 symbol
= &MakeAssocSymbol(symbol
->name(), *defaultDSASymbols
.back(),
1557 context_
.FindScope(dirContext
.directiveSource
));
1559 } // within OpenMP construct
1562 Symbol
*OmpAttributeVisitor::ResolveName(const parser::Name
*name
) {
1563 if (auto *resolvedSymbol
{
1564 name
? GetContext().scope
.FindSymbol(name
->source
) : nullptr}) {
1565 name
->symbol
= resolvedSymbol
;
1566 return resolvedSymbol
;
1572 void OmpAttributeVisitor::ResolveOmpName(
1573 const parser::Name
&name
, Symbol::Flag ompFlag
) {
1574 if (ResolveName(&name
)) {
1575 if (auto *resolvedSymbol
{ResolveOmp(name
, ompFlag
, currScope())}) {
1576 if (dataSharingAttributeFlags
.test(ompFlag
)) {
1577 AddToContextObjectWithDSA(*resolvedSymbol
, ompFlag
);
1580 } else if (ompFlag
== Symbol::Flag::OmpCriticalLock
) {
1582 GetContext().scope
.try_emplace(name
.source
, Attrs
{}, UnknownDetails
{})};
1584 name
.symbol
= &pair
.first
->second
.get();
1588 void OmpAttributeVisitor::ResolveOmpNameList(
1589 const std::list
<parser::Name
> &nameList
, Symbol::Flag ompFlag
) {
1590 for (const auto &name
: nameList
) {
1591 ResolveOmpName(name
, ompFlag
);
1595 Symbol
*OmpAttributeVisitor::ResolveOmpCommonBlockName(
1596 const parser::Name
*name
) {
1598 ? GetContext().scope
.parent().FindCommonBlock(name
->source
)
1600 name
->symbol
= prev
;
1603 // Check if the Common Block is declared in the current scope
1604 if (auto *commonBlockSymbol
{
1605 name
? GetContext().scope
.FindCommonBlock(name
->source
) : nullptr}) {
1606 name
->symbol
= commonBlockSymbol
;
1607 return commonBlockSymbol
;
1612 // Use this function over ResolveOmpName when an omp object's scope needs
1613 // resolving, it's symbol flag isn't important and a simple check for resolution
1614 // failure is desired. Using ResolveOmpName means needing to work with the
1615 // context to check for failure, whereas here a pointer comparison is all that's
1617 Symbol
*OmpAttributeVisitor::ResolveOmpObjectScope(const parser::Name
*name
) {
1619 // TODO: Investigate whether the following block can be replaced by, or
1620 // included in, the ResolveOmpName function
1621 if (auto *prev
{name
? GetContext().scope
.parent().FindSymbol(name
->source
)
1623 name
->symbol
= prev
;
1627 // TODO: Investigate whether the following block can be replaced by, or
1628 // included in, the ResolveOmpName function
1629 if (auto *ompSymbol
{
1630 name
? GetContext().scope
.FindSymbol(name
->source
) : nullptr}) {
1631 name
->symbol
= ompSymbol
;
1637 void OmpAttributeVisitor::ResolveOmpObjectList(
1638 const parser::OmpObjectList
&ompObjectList
, Symbol::Flag ompFlag
) {
1639 for (const auto &ompObject
: ompObjectList
.v
) {
1640 ResolveOmpObject(ompObject
, ompFlag
);
1644 void OmpAttributeVisitor::ResolveOmpObject(
1645 const parser::OmpObject
&ompObject
, Symbol::Flag ompFlag
) {
1648 [&](const parser::Designator
&designator
) {
1649 if (const auto *name
{GetDesignatorNameIfDataRef(designator
)}) {
1650 if (auto *symbol
{ResolveOmp(*name
, ompFlag
, currScope())}) {
1651 if (dataCopyingAttributeFlags
.test(ompFlag
)) {
1652 CheckDataCopyingClause(*name
, *symbol
, ompFlag
);
1654 AddToContextObjectWithDSA(*symbol
, ompFlag
);
1655 if (dataSharingAttributeFlags
.test(ompFlag
)) {
1656 CheckMultipleAppearances(*name
, *symbol
, ompFlag
);
1658 if (privateDataSharingAttributeFlags
.test(ompFlag
)) {
1659 CheckObjectInNamelist(*name
, *symbol
, ompFlag
);
1662 if (ompFlag
== Symbol::Flag::OmpAllocate
) {
1663 AddAllocateName(name
);
1666 if (ompFlag
== Symbol::Flag::OmpDeclarativeAllocateDirective
&&
1667 IsAllocatable(*symbol
)) {
1668 context_
.Say(designator
.source
,
1669 "List items specified in the ALLOCATE directive must not "
1670 "have the ALLOCATABLE attribute unless the directive is "
1671 "associated with an ALLOCATE statement"_err_en_US
);
1673 if ((ompFlag
== Symbol::Flag::OmpDeclarativeAllocateDirective
||
1675 Symbol::Flag::OmpExecutableAllocateDirective
) &&
1676 ResolveOmpObjectScope(name
) == nullptr) {
1677 context_
.Say(designator
.source
, // 2.15.3
1678 "List items must be declared in the same scoping unit "
1679 "in which the ALLOCATE directive appears"_err_en_US
);
1683 // Array sections to be changed to substrings as needed
1684 if (AnalyzeExpr(context_
, designator
)) {
1685 if (std::holds_alternative
<parser::Substring
>(designator
.u
)) {
1686 context_
.Say(designator
.source
,
1687 "Substrings are not allowed on OpenMP "
1688 "directives or clauses"_err_en_US
);
1691 // other checks, more TBD
1694 [&](const parser::Name
&name
) { // common block
1695 if (auto *symbol
{ResolveOmpCommonBlockName(&name
)}) {
1696 if (!dataCopyingAttributeFlags
.test(ompFlag
)) {
1697 CheckMultipleAppearances(
1698 name
, *symbol
, Symbol::Flag::OmpCommonBlock
);
1700 // 2.15.3 When a named common block appears in a list, it has the
1701 // same meaning as if every explicit member of the common block
1702 // appeared in the list
1703 auto &details
{symbol
->get
<CommonBlockDetails
>()};
1705 for (auto &object
: details
.objects()) {
1706 if (auto *resolvedObject
{
1707 ResolveOmp(*object
, ompFlag
, currScope())}) {
1708 if (dataCopyingAttributeFlags
.test(ompFlag
)) {
1709 CheckDataCopyingClause(name
, *resolvedObject
, ompFlag
);
1711 AddToContextObjectWithDSA(*resolvedObject
, ompFlag
);
1713 details
.replace_object(*resolvedObject
, index
);
1718 context_
.Say(name
.source
, // 2.15.3
1719 "COMMON block must be declared in the same scoping unit "
1720 "in which the OpenMP directive or clause appears"_err_en_US
);
1727 Symbol
*OmpAttributeVisitor::ResolveOmp(
1728 const parser::Name
&name
, Symbol::Flag ompFlag
, Scope
&scope
) {
1729 if (ompFlagsRequireNewSymbol
.test(ompFlag
)) {
1730 return DeclarePrivateAccessEntity(name
, ompFlag
, scope
);
1732 return DeclareOrMarkOtherAccessEntity(name
, ompFlag
);
1736 Symbol
*OmpAttributeVisitor::ResolveOmp(
1737 Symbol
&symbol
, Symbol::Flag ompFlag
, Scope
&scope
) {
1738 if (ompFlagsRequireNewSymbol
.test(ompFlag
)) {
1739 return DeclarePrivateAccessEntity(symbol
, ompFlag
, scope
);
1741 return DeclareOrMarkOtherAccessEntity(symbol
, ompFlag
);
1745 Symbol
*OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1746 const parser::Name
&name
, Symbol::Flag ompFlag
) {
1747 Symbol
*prev
{currScope().FindSymbol(name
.source
)};
1748 if (!name
.symbol
|| !prev
) {
1750 } else if (prev
!= name
.symbol
) {
1753 return DeclareOrMarkOtherAccessEntity(*prev
, ompFlag
);
1756 Symbol
*OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1757 Symbol
&object
, Symbol::Flag ompFlag
) {
1758 if (ompFlagsRequireMark
.test(ompFlag
)) {
1759 object
.set(ompFlag
);
1764 static bool WithMultipleAppearancesOmpException(
1765 const Symbol
&symbol
, Symbol::Flag flag
) {
1766 return (flag
== Symbol::Flag::OmpFirstPrivate
&&
1767 symbol
.test(Symbol::Flag::OmpLastPrivate
)) ||
1768 (flag
== Symbol::Flag::OmpLastPrivate
&&
1769 symbol
.test(Symbol::Flag::OmpFirstPrivate
));
1772 void OmpAttributeVisitor::CheckMultipleAppearances(
1773 const parser::Name
&name
, const Symbol
&symbol
, Symbol::Flag ompFlag
) {
1774 const auto *target
{&symbol
};
1775 if (ompFlagsRequireNewSymbol
.test(ompFlag
)) {
1776 if (const auto *details
{symbol
.detailsIf
<HostAssocDetails
>()}) {
1777 target
= &details
->symbol();
1780 if (HasDataSharingAttributeObject(*target
) &&
1781 !WithMultipleAppearancesOmpException(symbol
, ompFlag
)) {
1782 context_
.Say(name
.source
,
1783 "'%s' appears in more than one data-sharing clause "
1784 "on the same OpenMP directive"_err_en_US
,
1787 AddDataSharingAttributeObject(*target
);
1788 if (privateDataSharingAttributeFlags
.test(ompFlag
)) {
1789 AddPrivateDataSharingAttributeObjects(*target
);
1794 void ResolveAccParts(
1795 SemanticsContext
&context
, const parser::ProgramUnit
&node
) {
1796 if (context
.IsEnabled(common::LanguageFeature::OpenACC
)) {
1797 AccAttributeVisitor
{context
}.Walk(node
);
1801 void ResolveOmpParts(
1802 SemanticsContext
&context
, const parser::ProgramUnit
&node
) {
1803 if (context
.IsEnabled(common::LanguageFeature::OpenMP
)) {
1804 OmpAttributeVisitor
{context
}.Walk(node
);
1805 if (!context
.AnyFatalError()) {
1806 // The data-sharing attribute of the loop iteration variable for a
1807 // sequential loop (2.15.1.1) can only be determined when visiting
1808 // the corresponding DoConstruct, a second walk is to adjust the
1809 // symbols for all the data-refs of that loop iteration variable
1810 // prior to the DoConstruct.
1811 OmpAttributeVisitor
{context
}.Walk(node
);
1816 void OmpAttributeVisitor::CheckDataCopyingClause(
1817 const parser::Name
&name
, const Symbol
&symbol
, Symbol::Flag ompFlag
) {
1818 const auto *checkSymbol
{&symbol
};
1819 if (const auto *details
{symbol
.detailsIf
<HostAssocDetails
>()}) {
1820 checkSymbol
= &details
->symbol();
1823 if (ompFlag
== Symbol::Flag::OmpCopyIn
) {
1824 // List of items/objects that can appear in a 'copyin' clause must be
1826 if (!checkSymbol
->test(Symbol::Flag::OmpThreadprivate
)) {
1827 context_
.Say(name
.source
,
1828 "Non-THREADPRIVATE object '%s' in COPYIN clause"_err_en_US
,
1829 checkSymbol
->name());
1831 } else if (ompFlag
== Symbol::Flag::OmpCopyPrivate
&&
1832 GetContext().directive
== llvm::omp::Directive::OMPD_single
) {
1833 // A list item that appears in a 'copyprivate' clause may not appear on a
1834 // 'private' or 'firstprivate' clause on a single construct
1835 if (IsObjectWithDSA(symbol
) &&
1836 (symbol
.test(Symbol::Flag::OmpPrivate
) ||
1837 symbol
.test(Symbol::Flag::OmpFirstPrivate
))) {
1838 context_
.Say(name
.source
,
1839 "COPYPRIVATE variable '%s' may not appear on a PRIVATE or "
1840 "FIRSTPRIVATE clause on a SINGLE construct"_err_en_US
,
1843 // List of items/objects that can appear in a 'copyprivate' clause must be
1844 // either 'private' or 'threadprivate' in enclosing context.
1845 if (!checkSymbol
->test(Symbol::Flag::OmpThreadprivate
) &&
1846 !(HasSymbolInEnclosingScope(symbol
, currScope()) &&
1847 symbol
.test(Symbol::Flag::OmpPrivate
))) {
1848 context_
.Say(name
.source
,
1849 "COPYPRIVATE variable '%s' is not PRIVATE or THREADPRIVATE in "
1850 "outer context"_err_en_US
,
1857 void OmpAttributeVisitor::CheckObjectInNamelist(
1858 const parser::Name
&name
, const Symbol
&symbol
, Symbol::Flag ompFlag
) {
1859 const auto &ultimateSymbol
{symbol
.GetUltimate()};
1860 llvm::StringRef clauseName
{"PRIVATE"};
1861 if (ompFlag
== Symbol::Flag::OmpFirstPrivate
) {
1862 clauseName
= "FIRSTPRIVATE";
1863 } else if (ompFlag
== Symbol::Flag::OmpLastPrivate
) {
1864 clauseName
= "LASTPRIVATE";
1867 if (ultimateSymbol
.test(Symbol::Flag::InNamelist
)) {
1868 context_
.Say(name
.source
,
1869 "Variable '%s' in NAMELIST cannot be in a %s clause"_err_en_US
,
1870 name
.ToString(), clauseName
.str());
1874 void OmpAttributeVisitor::CheckSourceLabel(const parser::Label
&label
) {
1875 // Get the context to check if the statement causing a jump to the 'label' is
1876 // in an enclosing OpenMP construct
1877 std::optional
<DirContext
> thisContext
{GetContextIf()};
1878 sourceLabels_
.emplace(
1879 label
, std::make_pair(currentStatementSource_
, thisContext
));
1880 // Check if the statement with 'label' to which a jump is being introduced
1881 // has already been encountered
1882 auto it
{targetLabels_
.find(label
)};
1883 if (it
!= targetLabels_
.end()) {
1884 // Check if both the statement with 'label' and the statement that causes a
1885 // jump to the 'label' are in the same scope
1886 CheckLabelContext(currentStatementSource_
, it
->second
.first
, thisContext
,
1891 // Check for invalid branch into or out of OpenMP structured blocks
1892 void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source
,
1893 const parser::CharBlock target
, std::optional
<DirContext
> sourceContext
,
1894 std::optional
<DirContext
> targetContext
) {
1895 if (targetContext
&&
1897 (sourceContext
->scope
!= targetContext
->scope
&&
1899 &targetContext
->scope
, sourceContext
->scope
)))) {
1901 .Say(source
, "invalid branch into an OpenMP structured block"_err_en_US
)
1902 .Attach(target
, "In the enclosing %s directive branched into"_en_US
,
1903 parser::ToUpperCaseLetters(
1904 llvm::omp::getOpenMPDirectiveName(targetContext
->directive
)
1907 if (sourceContext
&&
1909 (sourceContext
->scope
!= targetContext
->scope
&&
1911 &sourceContext
->scope
, targetContext
->scope
)))) {
1914 "invalid branch leaving an OpenMP structured block"_err_en_US
)
1915 .Attach(target
, "Outside the enclosing %s directive"_en_US
,
1916 parser::ToUpperCaseLetters(
1917 llvm::omp::getOpenMPDirectiveName(sourceContext
->directive
)
1922 bool OmpAttributeVisitor::HasSymbolInEnclosingScope(
1923 const Symbol
&symbol
, Scope
&scope
) {
1924 const auto symbols
{scope
.parent().GetSymbols()};
1925 return llvm::is_contained(symbols
, symbol
);
1928 } // namespace Fortran::semantics