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/tools.h"
17 #include "flang/Evaluate/type.h"
18 #include "flang/Parser/parse-tree-visitor.h"
19 #include "flang/Parser/parse-tree.h"
20 #include "flang/Parser/tools.h"
21 #include "flang/Semantics/expression.h"
22 #include "flang/Semantics/symbol.h"
23 #include "flang/Semantics/tools.h"
29 static Fortran::semantics::Scope
*GetScope(
30 Fortran::semantics::SemanticsContext
&context
, const T
&x
) {
31 std::optional
<Fortran::parser::CharBlock
> source
{GetLastSource(x
)};
32 return source
? &context
.FindScope(*source
) : nullptr;
35 namespace Fortran::semantics
{
37 template <typename T
> class DirectiveAttributeVisitor
{
39 explicit DirectiveAttributeVisitor(SemanticsContext
&context
)
40 : context_
{context
} {}
42 template <typename A
> bool Pre(const A
&) { return true; }
43 template <typename A
> void Post(const A
&) {}
47 DirContext(const parser::CharBlock
&source
, T d
, Scope
&s
)
48 : directiveSource
{source
}, directive
{d
}, scope
{s
} {}
49 parser::CharBlock directiveSource
;
52 Symbol::Flag defaultDSA
{Symbol::Flag::AccShared
}; // TODOACC
53 std::map
<const Symbol
*, Symbol::Flag
> objectWithDSA
;
54 bool withinConstruct
{false};
55 std::int64_t associatedLoopLevel
{0};
58 DirContext
&GetContext() {
59 CHECK(!dirContext_
.empty());
60 return dirContext_
.back();
62 std::optional
<DirContext
> GetContextIf() {
63 return dirContext_
.empty()
65 : std::make_optional
<DirContext
>(dirContext_
.back());
67 void PushContext(const parser::CharBlock
&source
, T dir
, Scope
&scope
) {
68 dirContext_
.emplace_back(source
, dir
, scope
);
70 void PushContext(const parser::CharBlock
&source
, T dir
) {
71 dirContext_
.emplace_back(source
, dir
, context_
.FindScope(source
));
73 void PopContext() { dirContext_
.pop_back(); }
74 void SetContextDirectiveSource(parser::CharBlock
&dir
) {
75 GetContext().directiveSource
= dir
;
77 Scope
&currScope() { return GetContext().scope
; }
78 void SetContextDefaultDSA(Symbol::Flag flag
) {
79 GetContext().defaultDSA
= flag
;
81 void AddToContextObjectWithDSA(
82 const Symbol
&symbol
, Symbol::Flag flag
, DirContext
&context
) {
83 context
.objectWithDSA
.emplace(&symbol
, flag
);
85 void AddToContextObjectWithDSA(const Symbol
&symbol
, Symbol::Flag flag
) {
86 AddToContextObjectWithDSA(symbol
, flag
, GetContext());
88 bool IsObjectWithDSA(const Symbol
&symbol
) {
89 auto it
{GetContext().objectWithDSA
.find(&symbol
)};
90 return it
!= GetContext().objectWithDSA
.end();
92 void SetContextAssociatedLoopLevel(std::int64_t level
) {
93 GetContext().associatedLoopLevel
= level
;
95 Symbol
&MakeAssocSymbol(
96 const SourceName
&name
, const Symbol
&prev
, Scope
&scope
) {
97 const auto pair
{scope
.try_emplace(name
, Attrs
{}, HostAssocDetails
{prev
})};
98 return *pair
.first
->second
;
100 Symbol
&MakeAssocSymbol(const SourceName
&name
, const Symbol
&prev
) {
101 return MakeAssocSymbol(name
, prev
, currScope());
103 void AddDataSharingAttributeObject(SymbolRef object
) {
104 dataSharingAttributeObjects_
.insert(object
);
106 void ClearDataSharingAttributeObjects() {
107 dataSharingAttributeObjects_
.clear();
109 bool HasDataSharingAttributeObject(const Symbol
&);
110 const parser::Name
*GetLoopIndex(const parser::DoConstruct
&);
111 const parser::DoConstruct
*GetDoConstructIf(
112 const parser::ExecutionPartConstruct
&);
113 Symbol
*DeclareNewPrivateAccessEntity(const Symbol
&, Symbol::Flag
, Scope
&);
114 Symbol
*DeclarePrivateAccessEntity(
115 const parser::Name
&, Symbol::Flag
, Scope
&);
116 Symbol
*DeclarePrivateAccessEntity(Symbol
&, Symbol::Flag
, Scope
&);
117 Symbol
*DeclareOrMarkOtherAccessEntity(const parser::Name
&, Symbol::Flag
);
119 UnorderedSymbolSet dataSharingAttributeObjects_
; // on one directive
120 SemanticsContext
&context_
;
121 std::vector
<DirContext
> dirContext_
; // used as a stack
124 class AccAttributeVisitor
: DirectiveAttributeVisitor
<llvm::acc::Directive
> {
126 explicit AccAttributeVisitor(SemanticsContext
&context
, Scope
*topScope
)
127 : DirectiveAttributeVisitor(context
), topScope_(topScope
) {}
129 template <typename A
> void Walk(const A
&x
) { parser::Walk(x
, *this); }
130 template <typename A
> bool Pre(const A
&) { return true; }
131 template <typename A
> void Post(const A
&) {}
133 bool Pre(const parser::OpenACCBlockConstruct
&);
134 void Post(const parser::OpenACCBlockConstruct
&) { PopContext(); }
135 bool Pre(const parser::OpenACCCombinedConstruct
&);
136 void Post(const parser::OpenACCCombinedConstruct
&) { PopContext(); }
138 bool Pre(const parser::OpenACCDeclarativeConstruct
&);
139 void Post(const parser::OpenACCDeclarativeConstruct
&) { PopContext(); }
141 void Post(const parser::AccDeclarativeDirective
&) {
142 GetContext().withinConstruct
= true;
145 bool Pre(const parser::OpenACCRoutineConstruct
&);
146 bool Pre(const parser::AccBindClause
&);
147 void Post(const parser::OpenACCStandaloneDeclarativeConstruct
&);
149 void Post(const parser::AccBeginBlockDirective
&) {
150 GetContext().withinConstruct
= true;
153 bool Pre(const parser::OpenACCLoopConstruct
&);
154 void Post(const parser::OpenACCLoopConstruct
&) { PopContext(); }
155 void Post(const parser::AccLoopDirective
&) {
156 GetContext().withinConstruct
= true;
159 bool Pre(const parser::OpenACCStandaloneConstruct
&);
160 void Post(const parser::OpenACCStandaloneConstruct
&) { PopContext(); }
161 void Post(const parser::AccStandaloneDirective
&) {
162 GetContext().withinConstruct
= true;
165 bool Pre(const parser::OpenACCCacheConstruct
&);
166 void Post(const parser::OpenACCCacheConstruct
&) { PopContext(); }
168 void Post(const parser::AccDefaultClause
&);
170 bool Pre(const parser::AccClause::Attach
&);
171 bool Pre(const parser::AccClause::Detach
&);
173 bool Pre(const parser::AccClause::Copy
&x
) {
174 ResolveAccObjectList(x
.v
, Symbol::Flag::AccCopy
);
178 bool Pre(const parser::AccClause::Create
&x
) {
179 const auto &objectList
{std::get
<parser::AccObjectList
>(x
.v
.t
)};
180 ResolveAccObjectList(objectList
, Symbol::Flag::AccCreate
);
184 bool Pre(const parser::AccClause::Copyin
&x
) {
185 const auto &objectList
{std::get
<parser::AccObjectList
>(x
.v
.t
)};
186 const auto &modifier
{
187 std::get
<std::optional
<parser::AccDataModifier
>>(x
.v
.t
)};
189 (*modifier
).v
== parser::AccDataModifier::Modifier::ReadOnly
) {
190 ResolveAccObjectList(objectList
, Symbol::Flag::AccCopyInReadOnly
);
192 ResolveAccObjectList(objectList
, Symbol::Flag::AccCopyIn
);
197 bool Pre(const parser::AccClause::Copyout
&x
) {
198 const auto &objectList
{std::get
<parser::AccObjectList
>(x
.v
.t
)};
199 ResolveAccObjectList(objectList
, Symbol::Flag::AccCopyOut
);
203 bool Pre(const parser::AccClause::Present
&x
) {
204 ResolveAccObjectList(x
.v
, Symbol::Flag::AccPresent
);
207 bool Pre(const parser::AccClause::Private
&x
) {
208 ResolveAccObjectList(x
.v
, Symbol::Flag::AccPrivate
);
211 bool Pre(const parser::AccClause::Firstprivate
&x
) {
212 ResolveAccObjectList(x
.v
, Symbol::Flag::AccFirstPrivate
);
216 bool Pre(const parser::AccClause::Device
&x
) {
217 ResolveAccObjectList(x
.v
, Symbol::Flag::AccDevice
);
221 bool Pre(const parser::AccClause::DeviceResident
&x
) {
222 ResolveAccObjectList(x
.v
, Symbol::Flag::AccDeviceResident
);
226 bool Pre(const parser::AccClause::Deviceptr
&x
) {
227 ResolveAccObjectList(x
.v
, Symbol::Flag::AccDevicePtr
);
231 bool Pre(const parser::AccClause::Link
&x
) {
232 ResolveAccObjectList(x
.v
, Symbol::Flag::AccLink
);
236 bool Pre(const parser::AccClause::Host
&x
) {
237 ResolveAccObjectList(x
.v
, Symbol::Flag::AccHost
);
241 bool Pre(const parser::AccClause::Self
&x
) {
242 const std::optional
<parser::AccSelfClause
> &accSelfClause
= x
.v
;
244 std::holds_alternative
<parser::AccObjectList
>((*accSelfClause
).u
)) {
245 const auto &accObjectList
=
246 std::get
<parser::AccObjectList
>((*accSelfClause
).u
);
247 ResolveAccObjectList(accObjectList
, Symbol::Flag::AccSelf
);
252 void Post(const parser::Name
&);
255 std::int64_t GetAssociatedLoopLevelFromClauses(const parser::AccClauseList
&);
257 Symbol::Flags dataSharingAttributeFlags
{Symbol::Flag::AccShared
,
258 Symbol::Flag::AccPrivate
, Symbol::Flag::AccFirstPrivate
,
259 Symbol::Flag::AccReduction
};
261 Symbol::Flags dataMappingAttributeFlags
{Symbol::Flag::AccCreate
,
262 Symbol::Flag::AccCopyIn
, Symbol::Flag::AccCopyOut
,
263 Symbol::Flag::AccDelete
, Symbol::Flag::AccPresent
};
265 Symbol::Flags accDataMvtFlags
{
266 Symbol::Flag::AccDevice
, Symbol::Flag::AccHost
, Symbol::Flag::AccSelf
};
268 Symbol::Flags accFlagsRequireMark
{Symbol::Flag::AccCreate
,
269 Symbol::Flag::AccCopyIn
, Symbol::Flag::AccCopyInReadOnly
,
270 Symbol::Flag::AccCopy
, Symbol::Flag::AccCopyOut
,
271 Symbol::Flag::AccDevicePtr
, Symbol::Flag::AccDeviceResident
,
272 Symbol::Flag::AccLink
, Symbol::Flag::AccPresent
};
274 void CheckAssociatedLoop(const parser::DoConstruct
&);
275 void ResolveAccObjectList(const parser::AccObjectList
&, Symbol::Flag
);
276 void ResolveAccObject(const parser::AccObject
&, Symbol::Flag
);
277 Symbol
*ResolveAcc(const parser::Name
&, Symbol::Flag
, Scope
&);
278 Symbol
*ResolveAcc(Symbol
&, Symbol::Flag
, Scope
&);
279 Symbol
*ResolveName(const parser::Name
&, bool parentScope
= false);
280 Symbol
*ResolveFctName(const parser::Name
&);
281 Symbol
*ResolveAccCommonBlockName(const parser::Name
*);
282 Symbol
*DeclareOrMarkOtherAccessEntity(const parser::Name
&, Symbol::Flag
);
283 Symbol
*DeclareOrMarkOtherAccessEntity(Symbol
&, Symbol::Flag
);
284 void CheckMultipleAppearances(
285 const parser::Name
&, const Symbol
&, Symbol::Flag
);
286 void AllowOnlyArrayAndSubArray(const parser::AccObjectList
&objectList
);
287 void DoNotAllowAssumedSizedArray(const parser::AccObjectList
&objectList
);
288 void AllowOnlyVariable(const parser::AccObject
&object
);
289 void EnsureAllocatableOrPointer(
290 const llvm::acc::Clause clause
, const parser::AccObjectList
&objectList
);
291 void AddRoutineInfoToSymbol(
292 Symbol
&, const parser::OpenACCRoutineConstruct
&);
296 // Data-sharing and Data-mapping attributes for data-refs in OpenMP construct
297 class OmpAttributeVisitor
: DirectiveAttributeVisitor
<llvm::omp::Directive
> {
299 explicit OmpAttributeVisitor(SemanticsContext
&context
)
300 : DirectiveAttributeVisitor(context
) {}
302 template <typename A
> void Walk(const A
&x
) { parser::Walk(x
, *this); }
303 template <typename A
> bool Pre(const A
&) { return true; }
304 template <typename A
> void Post(const A
&) {}
306 template <typename A
> bool Pre(const parser::Statement
<A
> &statement
) {
307 currentStatementSource_
= statement
.source
;
308 // Keep track of the labels in all the labelled statements
309 if (statement
.label
) {
310 auto label
{statement
.label
.value()};
311 // Get the context to check if the labelled statement is in an
312 // enclosing OpenMP construct
313 std::optional
<DirContext
> thisContext
{GetContextIf()};
314 targetLabels_
.emplace(
315 label
, std::make_pair(currentStatementSource_
, thisContext
));
316 // Check if a statement that causes a jump to the 'label'
317 // has already been encountered
318 auto range
{sourceLabels_
.equal_range(label
)};
319 for (auto it
{range
.first
}; it
!= range
.second
; ++it
) {
320 // Check if both the statement with 'label' and the statement that
321 // causes a jump to the 'label' are in the same scope
322 CheckLabelContext(it
->second
.first
, currentStatementSource_
,
323 it
->second
.second
, thisContext
);
329 bool Pre(const parser::InternalSubprogram
&) {
330 // Clear the labels being tracked in the previous scope
335 bool Pre(const parser::ModuleSubprogram
&) {
336 // Clear the labels being tracked in the previous scope
341 bool Pre(const parser::StmtFunctionStmt
&x
) {
342 const auto &parsedExpr
{std::get
<parser::Scalar
<parser::Expr
>>(x
.t
)};
343 if (const auto *expr
{GetExpr(context_
, parsedExpr
)}) {
344 for (const Symbol
&symbol
: evaluate::CollectSymbols(*expr
)) {
345 if (!IsStmtFunctionDummy(symbol
)) {
346 stmtFunctionExprSymbols_
.insert(symbol
.GetUltimate());
353 bool Pre(const parser::OpenMPBlockConstruct
&);
354 void Post(const parser::OpenMPBlockConstruct
&);
356 void Post(const parser::OmpBeginBlockDirective
&) {
357 GetContext().withinConstruct
= true;
360 bool Pre(const parser::OpenMPSimpleStandaloneConstruct
&);
361 void Post(const parser::OpenMPSimpleStandaloneConstruct
&) { PopContext(); }
363 bool Pre(const parser::OpenMPLoopConstruct
&);
364 void Post(const parser::OpenMPLoopConstruct
&) { PopContext(); }
365 void Post(const parser::OmpBeginLoopDirective
&) {
366 GetContext().withinConstruct
= true;
368 bool Pre(const parser::DoConstruct
&);
370 bool Pre(const parser::OpenMPSectionsConstruct
&);
371 void Post(const parser::OpenMPSectionsConstruct
&) { PopContext(); }
373 bool Pre(const parser::OpenMPCriticalConstruct
&critical
);
374 void Post(const parser::OpenMPCriticalConstruct
&) { PopContext(); }
376 bool Pre(const parser::OpenMPDeclareSimdConstruct
&x
) {
377 PushContext(x
.source
, llvm::omp::Directive::OMPD_declare_simd
);
378 const auto &name
{std::get
<std::optional
<parser::Name
>>(x
.t
)};
380 ResolveOmpName(*name
, Symbol::Flag::OmpDeclareSimd
);
384 void Post(const parser::OpenMPDeclareSimdConstruct
&) { PopContext(); }
386 bool Pre(const parser::OpenMPDepobjConstruct
&x
) {
387 PushContext(x
.source
, llvm::omp::Directive::OMPD_depobj
);
388 auto &object
{std::get
<parser::OmpObject
>(x
.t
)};
389 ResolveOmpObject(object
, Symbol::Flag::OmpDependObject
);
392 void Post(const parser::OpenMPDepobjConstruct
&) { PopContext(); }
394 bool Pre(const parser::OpenMPRequiresConstruct
&x
) {
395 using Flags
= WithOmpDeclarative::RequiresFlags
;
396 using Requires
= WithOmpDeclarative::RequiresFlag
;
397 PushContext(x
.source
, llvm::omp::Directive::OMPD_requires
);
399 // Gather information from the clauses.
401 std::optional
<common::OmpAtomicDefaultMemOrderType
> memOrder
;
402 for (const auto &clause
: std::get
<parser::OmpClauseList
>(x
.t
).v
) {
403 flags
|= common::visit(
406 const parser::OmpClause::AtomicDefaultMemOrder
&atomic
) {
407 memOrder
= atomic
.v
.v
;
410 [](const parser::OmpClause::ReverseOffload
&) {
411 return Flags
{Requires::ReverseOffload
};
413 [](const parser::OmpClause::UnifiedAddress
&) {
414 return Flags
{Requires::UnifiedAddress
};
416 [](const parser::OmpClause::UnifiedSharedMemory
&) {
417 return Flags
{Requires::UnifiedSharedMemory
};
419 [](const parser::OmpClause::DynamicAllocators
&) {
420 return Flags
{Requires::DynamicAllocators
};
422 [](const auto &) { return Flags
{}; }},
425 // Merge clauses into parents' symbols details.
426 AddOmpRequiresToScope(currScope(), flags
, memOrder
);
429 void Post(const parser::OpenMPRequiresConstruct
&) { PopContext(); }
431 bool Pre(const parser::OpenMPDeclareTargetConstruct
&);
432 void Post(const parser::OpenMPDeclareTargetConstruct
&) { PopContext(); }
434 bool Pre(const parser::OpenMPDeclareMapperConstruct
&);
435 void Post(const parser::OpenMPDeclareMapperConstruct
&) { PopContext(); }
437 bool Pre(const parser::OpenMPThreadprivate
&);
438 void Post(const parser::OpenMPThreadprivate
&) { PopContext(); }
440 bool Pre(const parser::OpenMPDeclarativeAllocate
&);
441 void Post(const parser::OpenMPDeclarativeAllocate
&) { PopContext(); }
443 bool Pre(const parser::OpenMPExecutableAllocate
&);
444 void Post(const parser::OpenMPExecutableAllocate
&);
446 bool Pre(const parser::OpenMPAllocatorsConstruct
&);
447 void Post(const parser::OpenMPAllocatorsConstruct
&);
449 void Post(const parser::OmpObjectList
&x
) {
450 // The objects from OMP clauses should have already been resolved,
451 // except common blocks (the ResolveNamesVisitor does not visit
452 // parser::Name, those are dealt with as members of other structures).
453 // Iterate over elements of x, and resolve any common blocks that
454 // are still unresolved.
455 for (const parser::OmpObject
&obj
: x
.v
) {
456 auto *name
{std::get_if
<parser::Name
>(&obj
.u
)};
457 if (name
&& !name
->symbol
) {
458 Resolve(*name
, currScope().MakeCommonBlock(name
->source
));
463 // 2.15.3 Data-Sharing Attribute Clauses
464 bool Pre(const parser::OmpClause::Inclusive
&x
) {
465 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpInclusiveScan
);
468 bool Pre(const parser::OmpClause::Exclusive
&x
) {
469 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpExclusiveScan
);
472 void Post(const parser::OmpDefaultClause
&);
473 bool Pre(const parser::OmpClause::Shared
&x
) {
474 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpShared
);
477 bool Pre(const parser::OmpClause::Private
&x
) {
478 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpPrivate
);
481 bool Pre(const parser::OmpAllocateClause
&x
) {
482 const auto &objectList
{std::get
<parser::OmpObjectList
>(x
.t
)};
483 ResolveOmpObjectList(objectList
, Symbol::Flag::OmpAllocate
);
486 bool Pre(const parser::OmpClause::Firstprivate
&x
) {
487 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpFirstPrivate
);
490 bool Pre(const parser::OmpClause::Lastprivate
&x
) {
491 const auto &objList
{std::get
<parser::OmpObjectList
>(x
.v
.t
)};
492 ResolveOmpObjectList(objList
, Symbol::Flag::OmpLastPrivate
);
495 bool Pre(const parser::OmpClause::Copyin
&x
) {
496 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpCopyIn
);
499 bool Pre(const parser::OmpClause::Copyprivate
&x
) {
500 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpCopyPrivate
);
503 bool Pre(const parser::OmpLinearClause
&x
) {
504 common::visit(common::visitors
{
505 [&](const parser::OmpLinearClause::WithoutModifier
506 &linearWithoutModifier
) {
507 ResolveOmpNameList(linearWithoutModifier
.names
,
508 Symbol::Flag::OmpLinear
);
510 [&](const parser::OmpLinearClause::WithModifier
511 &linearWithModifier
) {
513 linearWithModifier
.names
, Symbol::Flag::OmpLinear
);
520 bool Pre(const parser::OmpClause::Reduction
&x
) {
521 const parser::OmpReductionOperator
&opr
{
522 std::get
<parser::OmpReductionOperator
>(x
.v
.t
)};
523 auto createDummyProcSymbol
= [&](const parser::Name
*name
) {
524 // If name resolution failed, create a dummy symbol
526 currScope().try_emplace(name
->source
, Attrs
{}, ProcEntityDetails
{})};
527 auto &newSymbol
{*namePair
.first
->second
};
528 if (context_
.intrinsics().IsIntrinsic(name
->ToString())) {
529 newSymbol
.attrs().set(Attr::INTRINSIC
);
531 name
->symbol
= &newSymbol
;
533 if (const auto *procD
{parser::Unwrap
<parser::ProcedureDesignator
>(opr
.u
)}) {
534 if (const auto *name
{parser::Unwrap
<parser::Name
>(procD
->u
)}) {
536 if (!ResolveName(name
)) {
537 createDummyProcSymbol(name
);
541 if (const auto *procRef
{
542 parser::Unwrap
<parser::ProcComponentRef
>(procD
->u
)}) {
543 if (!procRef
->v
.thing
.component
.symbol
) {
544 if (!ResolveName(&procRef
->v
.thing
.component
)) {
545 createDummyProcSymbol(&procRef
->v
.thing
.component
);
550 const auto &objList
{std::get
<parser::OmpObjectList
>(x
.v
.t
)};
551 ResolveOmpObjectList(objList
, Symbol::Flag::OmpReduction
);
552 using ReductionModifier
= parser::OmpReductionClause::ReductionModifier
;
553 const auto &maybeModifier
{
554 std::get
<std::optional
<ReductionModifier
>>(x
.v
.t
)};
555 if (maybeModifier
&& *maybeModifier
== ReductionModifier::Inscan
) {
556 ResolveOmpObjectList(objList
, Symbol::Flag::OmpInScanReduction
);
561 bool Pre(const parser::OmpAlignedClause
&x
) {
562 const auto &alignedNameList
{std::get
<parser::OmpObjectList
>(x
.t
)};
563 ResolveOmpObjectList(alignedNameList
, Symbol::Flag::OmpAligned
);
567 bool Pre(const parser::OmpClause::Nontemporal
&x
) {
568 const auto &nontemporalNameList
{x
.v
};
569 ResolveOmpNameList(nontemporalNameList
, Symbol::Flag::OmpNontemporal
);
573 void Post(const parser::OmpIteration
&x
) {
574 if (const auto &name
{std::get
<parser::Name
>(x
.t
)}; !name
.symbol
) {
575 auto *symbol
{currScope().FindSymbol(name
.source
)};
577 // OmpIteration must use an existing object. If there isn't one,
578 // create a fake one and flag an error later.
579 symbol
= &currScope().MakeSymbol(
580 name
.source
, Attrs
{}, EntityDetails(/*isDummy=*/true));
582 Resolve(name
, symbol
);
586 bool Pre(const parser::OmpClause::UseDevicePtr
&x
) {
587 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpUseDevicePtr
);
591 bool Pre(const parser::OmpClause::UseDeviceAddr
&x
) {
592 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpUseDeviceAddr
);
596 bool Pre(const parser::OmpClause::IsDevicePtr
&x
) {
597 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpIsDevicePtr
);
601 bool Pre(const parser::OmpClause::HasDeviceAddr
&x
) {
602 ResolveOmpObjectList(x
.v
, Symbol::Flag::OmpHasDeviceAddr
);
606 void Post(const parser::Name
&);
608 // Keep track of labels in the statements that causes jumps to target labels
609 void Post(const parser::GotoStmt
&gotoStmt
) { CheckSourceLabel(gotoStmt
.v
); }
610 void Post(const parser::ComputedGotoStmt
&computedGotoStmt
) {
611 for (auto &label
: std::get
<std::list
<parser::Label
>>(computedGotoStmt
.t
)) {
612 CheckSourceLabel(label
);
615 void Post(const parser::ArithmeticIfStmt
&arithmeticIfStmt
) {
616 CheckSourceLabel(std::get
<1>(arithmeticIfStmt
.t
));
617 CheckSourceLabel(std::get
<2>(arithmeticIfStmt
.t
));
618 CheckSourceLabel(std::get
<3>(arithmeticIfStmt
.t
));
620 void Post(const parser::AssignedGotoStmt
&assignedGotoStmt
) {
621 for (auto &label
: std::get
<std::list
<parser::Label
>>(assignedGotoStmt
.t
)) {
622 CheckSourceLabel(label
);
625 void Post(const parser::AltReturnSpec
&altReturnSpec
) {
626 CheckSourceLabel(altReturnSpec
.v
);
628 void Post(const parser::ErrLabel
&errLabel
) { CheckSourceLabel(errLabel
.v
); }
629 void Post(const parser::EndLabel
&endLabel
) { CheckSourceLabel(endLabel
.v
); }
630 void Post(const parser::EorLabel
&eorLabel
) { CheckSourceLabel(eorLabel
.v
); }
632 void Post(const parser::OmpMapClause
&x
) {
633 Symbol::Flag ompFlag
= Symbol::Flag::OmpMapToFrom
;
634 // There is only one `type' allowed, but it's parsed as a list. Multiple
635 // types are diagnosed in the semantic checks for OpenMP.
636 if (const auto &mapType
{
637 std::get
<std::optional
<std::list
<parser::OmpMapClause::Type
>>>(
639 switch (mapType
->front()) {
640 case parser::OmpMapClause::Type::To
:
641 ompFlag
= Symbol::Flag::OmpMapTo
;
643 case parser::OmpMapClause::Type::From
:
644 ompFlag
= Symbol::Flag::OmpMapFrom
;
646 case parser::OmpMapClause::Type::Tofrom
:
647 ompFlag
= Symbol::Flag::OmpMapToFrom
;
649 case parser::OmpMapClause::Type::Alloc
:
650 ompFlag
= Symbol::Flag::OmpMapAlloc
;
652 case parser::OmpMapClause::Type::Release
:
653 ompFlag
= Symbol::Flag::OmpMapRelease
;
655 case parser::OmpMapClause::Type::Delete
:
656 ompFlag
= Symbol::Flag::OmpMapDelete
;
660 const auto &ompObjList
{std::get
<parser::OmpObjectList
>(x
.t
)};
661 for (const auto &ompObj
: ompObjList
.v
) {
664 [&](const parser::Designator
&designator
) {
665 if (const auto *name
{
666 semantics::getDesignatorNameIfDataRef(designator
)}) {
668 name
->symbol
->set(ompFlag
);
669 AddToContextObjectWithDSA(*name
->symbol
, ompFlag
);
672 semantics::IsAssumedSizeArray(*name
->symbol
)) {
673 context_
.Say(designator
.source
,
674 "Assumed-size whole arrays may not appear on the %s "
680 [&](const auto &name
) {},
684 ResolveOmpObject(ompObj
, ompFlag
);
688 const parser::OmpClause
*associatedClause
{nullptr};
689 void SetAssociatedClause(const parser::OmpClause
&c
) {
690 associatedClause
= &c
;
692 const parser::OmpClause
*GetAssociatedClause() { return associatedClause
; }
695 std::int64_t GetAssociatedLoopLevelFromClauses(const parser::OmpClauseList
&);
697 Symbol::Flags dataSharingAttributeFlags
{Symbol::Flag::OmpShared
,
698 Symbol::Flag::OmpPrivate
, Symbol::Flag::OmpFirstPrivate
,
699 Symbol::Flag::OmpLastPrivate
, Symbol::Flag::OmpReduction
,
700 Symbol::Flag::OmpLinear
};
702 Symbol::Flags privateDataSharingAttributeFlags
{Symbol::Flag::OmpPrivate
,
703 Symbol::Flag::OmpFirstPrivate
, Symbol::Flag::OmpLastPrivate
};
705 Symbol::Flags ompFlagsRequireNewSymbol
{Symbol::Flag::OmpPrivate
,
706 Symbol::Flag::OmpLinear
, Symbol::Flag::OmpFirstPrivate
,
707 Symbol::Flag::OmpLastPrivate
, Symbol::Flag::OmpReduction
,
708 Symbol::Flag::OmpCriticalLock
, Symbol::Flag::OmpCopyIn
,
709 Symbol::Flag::OmpUseDevicePtr
, Symbol::Flag::OmpUseDeviceAddr
,
710 Symbol::Flag::OmpIsDevicePtr
, Symbol::Flag::OmpHasDeviceAddr
};
712 Symbol::Flags ompFlagsRequireMark
{Symbol::Flag::OmpThreadprivate
,
713 Symbol::Flag::OmpDeclareTarget
, Symbol::Flag::OmpExclusiveScan
,
714 Symbol::Flag::OmpInclusiveScan
, Symbol::Flag::OmpInScanReduction
};
716 Symbol::Flags dataCopyingAttributeFlags
{
717 Symbol::Flag::OmpCopyIn
, Symbol::Flag::OmpCopyPrivate
};
719 std::vector
<const parser::Name
*> allocateNames_
; // on one directive
720 UnorderedSymbolSet privateDataSharingAttributeObjects_
; // on one directive
721 UnorderedSymbolSet stmtFunctionExprSymbols_
;
722 std::multimap
<const parser::Label
,
723 std::pair
<parser::CharBlock
, std::optional
<DirContext
>>>
725 std::map
<const parser::Label
,
726 std::pair
<parser::CharBlock
, std::optional
<DirContext
>>>
728 parser::CharBlock currentStatementSource_
;
730 void AddAllocateName(const parser::Name
*&object
) {
731 allocateNames_
.push_back(object
);
733 void ClearAllocateNames() { allocateNames_
.clear(); }
735 void AddPrivateDataSharingAttributeObjects(SymbolRef object
) {
736 privateDataSharingAttributeObjects_
.insert(object
);
738 void ClearPrivateDataSharingAttributeObjects() {
739 privateDataSharingAttributeObjects_
.clear();
742 // Predetermined DSA rules
743 void PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
744 const parser::OpenMPLoopConstruct
&);
745 void ResolveSeqLoopIndexInParallelOrTaskConstruct(const parser::Name
&);
747 bool IsNestedInDirective(llvm::omp::Directive directive
);
748 void ResolveOmpObjectList(const parser::OmpObjectList
&, Symbol::Flag
);
749 void ResolveOmpObject(const parser::OmpObject
&, Symbol::Flag
);
750 Symbol
*ResolveOmp(const parser::Name
&, Symbol::Flag
, Scope
&);
751 Symbol
*ResolveOmp(Symbol
&, Symbol::Flag
, Scope
&);
752 Symbol
*ResolveOmpCommonBlockName(const parser::Name
*);
753 void ResolveOmpNameList(const std::list
<parser::Name
> &, Symbol::Flag
);
754 void ResolveOmpName(const parser::Name
&, Symbol::Flag
);
755 Symbol
*ResolveName(const parser::Name
*);
756 Symbol
*ResolveOmpObjectScope(const parser::Name
*);
757 Symbol
*DeclareOrMarkOtherAccessEntity(const parser::Name
&, Symbol::Flag
);
758 Symbol
*DeclareOrMarkOtherAccessEntity(Symbol
&, Symbol::Flag
);
759 void CheckMultipleAppearances(
760 const parser::Name
&, const Symbol
&, Symbol::Flag
);
762 void CheckDataCopyingClause(
763 const parser::Name
&, const Symbol
&, Symbol::Flag
);
764 void CheckAssocLoopLevel(std::int64_t level
, const parser::OmpClause
*clause
);
765 void CheckObjectIsPrivatizable(
766 const parser::Name
&, const Symbol
&, Symbol::Flag
);
767 void CheckSourceLabel(const parser::Label
&);
768 void CheckLabelContext(const parser::CharBlock
, const parser::CharBlock
,
769 std::optional
<DirContext
>, std::optional
<DirContext
>);
771 sourceLabels_
.clear();
772 targetLabels_
.clear();
774 void CheckAllNamesInAllocateStmt(const parser::CharBlock
&source
,
775 const parser::OmpObjectList
&ompObjectList
,
776 const parser::AllocateStmt
&allocate
);
777 void CheckNameInAllocateStmt(const parser::CharBlock
&source
,
778 const parser::Name
&ompObject
, const parser::AllocateStmt
&allocate
);
780 std::int64_t ordCollapseLevel
{0};
782 void AddOmpRequiresToScope(Scope
&, WithOmpDeclarative::RequiresFlags
,
783 std::optional
<common::OmpAtomicDefaultMemOrderType
>);
784 void IssueNonConformanceWarning(
785 llvm::omp::Directive D
, parser::CharBlock source
);
787 void CreateImplicitSymbols(
788 const Symbol
*symbol
, std::optional
<Symbol::Flag
> setFlag
= std::nullopt
);
791 template <typename T
>
792 bool DirectiveAttributeVisitor
<T
>::HasDataSharingAttributeObject(
793 const Symbol
&object
) {
794 auto it
{dataSharingAttributeObjects_
.find(object
)};
795 return it
!= dataSharingAttributeObjects_
.end();
798 template <typename T
>
799 const parser::Name
*DirectiveAttributeVisitor
<T
>::GetLoopIndex(
800 const parser::DoConstruct
&x
) {
801 using Bounds
= parser::LoopControl::Bounds
;
802 if (x
.GetLoopControl()) {
803 if (const Bounds
* b
{std::get_if
<Bounds
>(&x
.GetLoopControl()->u
)}) {
804 return &b
->name
.thing
;
810 .Say(std::get
<parser::Statement
<parser::NonLabelDoStmt
>>(x
.t
).source
,
811 "Loop control is not present in the DO LOOP"_err_en_US
)
812 .Attach(GetContext().directiveSource
,
813 "associated with the enclosing LOOP construct"_en_US
);
818 template <typename T
>
819 const parser::DoConstruct
*DirectiveAttributeVisitor
<T
>::GetDoConstructIf(
820 const parser::ExecutionPartConstruct
&x
) {
821 return parser::Unwrap
<parser::DoConstruct
>(x
);
824 template <typename T
>
825 Symbol
*DirectiveAttributeVisitor
<T
>::DeclareNewPrivateAccessEntity(
826 const Symbol
&object
, Symbol::Flag flag
, Scope
&scope
) {
827 assert(object
.owner() != currScope());
828 auto &symbol
{MakeAssocSymbol(object
.name(), object
, scope
)};
830 if (flag
== Symbol::Flag::OmpCopyIn
) {
831 // The symbol in copyin clause must be threadprivate entity.
832 symbol
.set(Symbol::Flag::OmpThreadprivate
);
837 template <typename T
>
838 Symbol
*DirectiveAttributeVisitor
<T
>::DeclarePrivateAccessEntity(
839 const parser::Name
&name
, Symbol::Flag flag
, Scope
&scope
) {
841 return nullptr; // not resolved by Name Resolution step, do nothing
843 name
.symbol
= DeclarePrivateAccessEntity(*name
.symbol
, flag
, scope
);
847 template <typename T
>
848 Symbol
*DirectiveAttributeVisitor
<T
>::DeclarePrivateAccessEntity(
849 Symbol
&object
, Symbol::Flag flag
, Scope
&scope
) {
850 if (object
.owner() != currScope()) {
851 return DeclareNewPrivateAccessEntity(object
, flag
, scope
);
858 bool AccAttributeVisitor::Pre(const parser::OpenACCBlockConstruct
&x
) {
859 const auto &beginBlockDir
{std::get
<parser::AccBeginBlockDirective
>(x
.t
)};
860 const auto &blockDir
{std::get
<parser::AccBlockDirective
>(beginBlockDir
.t
)};
861 switch (blockDir
.v
) {
862 case llvm::acc::Directive::ACCD_data
:
863 case llvm::acc::Directive::ACCD_host_data
:
864 case llvm::acc::Directive::ACCD_kernels
:
865 case llvm::acc::Directive::ACCD_parallel
:
866 case llvm::acc::Directive::ACCD_serial
:
867 PushContext(blockDir
.source
, blockDir
.v
);
872 ClearDataSharingAttributeObjects();
876 bool AccAttributeVisitor::Pre(const parser::OpenACCDeclarativeConstruct
&x
) {
877 if (const auto *declConstruct
{
878 std::get_if
<parser::OpenACCStandaloneDeclarativeConstruct
>(&x
.u
)}) {
880 std::get
<parser::AccDeclarativeDirective
>(declConstruct
->t
)};
881 PushContext(declDir
.source
, llvm::acc::Directive::ACCD_declare
);
883 ClearDataSharingAttributeObjects();
887 static const parser::AccObjectList
&GetAccObjectList(
888 const parser::AccClause
&clause
) {
889 if (const auto *copyClause
=
890 std::get_if
<Fortran::parser::AccClause::Copy
>(&clause
.u
)) {
891 return copyClause
->v
;
892 } else if (const auto *createClause
=
893 std::get_if
<Fortran::parser::AccClause::Create
>(&clause
.u
)) {
894 const Fortran::parser::AccObjectListWithModifier
&listWithModifier
=
896 const Fortran::parser::AccObjectList
&accObjectList
=
897 std::get
<Fortran::parser::AccObjectList
>(listWithModifier
.t
);
898 return accObjectList
;
899 } else if (const auto *copyinClause
=
900 std::get_if
<Fortran::parser::AccClause::Copyin
>(&clause
.u
)) {
901 const Fortran::parser::AccObjectListWithModifier
&listWithModifier
=
903 const Fortran::parser::AccObjectList
&accObjectList
=
904 std::get
<Fortran::parser::AccObjectList
>(listWithModifier
.t
);
905 return accObjectList
;
906 } else if (const auto *copyoutClause
=
907 std::get_if
<Fortran::parser::AccClause::Copyout
>(&clause
.u
)) {
908 const Fortran::parser::AccObjectListWithModifier
&listWithModifier
=
910 const Fortran::parser::AccObjectList
&accObjectList
=
911 std::get
<Fortran::parser::AccObjectList
>(listWithModifier
.t
);
912 return accObjectList
;
913 } else if (const auto *presentClause
=
914 std::get_if
<Fortran::parser::AccClause::Present
>(&clause
.u
)) {
915 return presentClause
->v
;
916 } else if (const auto *deviceptrClause
=
917 std::get_if
<Fortran::parser::AccClause::Deviceptr
>(
919 return deviceptrClause
->v
;
920 } else if (const auto *deviceResidentClause
=
921 std::get_if
<Fortran::parser::AccClause::DeviceResident
>(
923 return deviceResidentClause
->v
;
924 } else if (const auto *linkClause
=
925 std::get_if
<Fortran::parser::AccClause::Link
>(&clause
.u
)) {
926 return linkClause
->v
;
928 llvm_unreachable("Clause without object list!");
932 void AccAttributeVisitor::Post(
933 const parser::OpenACCStandaloneDeclarativeConstruct
&x
) {
934 const auto &clauseList
= std::get
<parser::AccClauseList
>(x
.t
);
935 for (const auto &clause
: clauseList
.v
) {
936 // Restriction - line 2414
937 DoNotAllowAssumedSizedArray(GetAccObjectList(clause
));
941 bool AccAttributeVisitor::Pre(const parser::OpenACCLoopConstruct
&x
) {
942 const auto &beginDir
{std::get
<parser::AccBeginLoopDirective
>(x
.t
)};
943 const auto &loopDir
{std::get
<parser::AccLoopDirective
>(beginDir
.t
)};
944 const auto &clauseList
{std::get
<parser::AccClauseList
>(beginDir
.t
)};
945 if (loopDir
.v
== llvm::acc::Directive::ACCD_loop
) {
946 PushContext(loopDir
.source
, loopDir
.v
);
948 ClearDataSharingAttributeObjects();
949 SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList
));
950 const auto &outer
{std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)};
951 CheckAssociatedLoop(*outer
);
955 bool AccAttributeVisitor::Pre(const parser::OpenACCStandaloneConstruct
&x
) {
956 const auto &standaloneDir
{std::get
<parser::AccStandaloneDirective
>(x
.t
)};
957 switch (standaloneDir
.v
) {
958 case llvm::acc::Directive::ACCD_enter_data
:
959 case llvm::acc::Directive::ACCD_exit_data
:
960 case llvm::acc::Directive::ACCD_init
:
961 case llvm::acc::Directive::ACCD_set
:
962 case llvm::acc::Directive::ACCD_shutdown
:
963 case llvm::acc::Directive::ACCD_update
:
964 PushContext(standaloneDir
.source
, standaloneDir
.v
);
969 ClearDataSharingAttributeObjects();
973 Symbol
*AccAttributeVisitor::ResolveName(
974 const parser::Name
&name
, bool parentScope
) {
975 Symbol
*prev
{currScope().FindSymbol(name
.source
)};
976 // Check in parent scope if asked for.
977 if (!prev
&& parentScope
) {
978 prev
= currScope().parent().FindSymbol(name
.source
);
980 if (prev
!= name
.symbol
) {
986 Symbol
*AccAttributeVisitor::ResolveFctName(const parser::Name
&name
) {
987 Symbol
*prev
{currScope().FindSymbol(name
.source
)};
988 if (!prev
|| (prev
&& prev
->IsFuncResult())) {
989 prev
= currScope().parent().FindSymbol(name
.source
);
991 prev
= &context_
.globalScope().MakeSymbol(
992 name
.source
, Attrs
{}, ProcEntityDetails
{});
995 if (prev
!= name
.symbol
) {
1001 template <typename T
>
1002 common::IfNoLvalue
<T
, T
> FoldExpr(
1003 evaluate::FoldingContext
&foldingContext
, T
&&expr
) {
1004 return evaluate::Fold(foldingContext
, std::move(expr
));
1007 template <typename T
>
1008 MaybeExpr
EvaluateExpr(
1009 Fortran::semantics::SemanticsContext
&semanticsContext
, const T
&expr
) {
1011 semanticsContext
.foldingContext(), AnalyzeExpr(semanticsContext
, expr
));
1014 void AccAttributeVisitor::AddRoutineInfoToSymbol(
1015 Symbol
&symbol
, const parser::OpenACCRoutineConstruct
&x
) {
1016 if (symbol
.has
<SubprogramDetails
>()) {
1017 Fortran::semantics::OpenACCRoutineInfo info
;
1018 const auto &clauses
= std::get
<Fortran::parser::AccClauseList
>(x
.t
);
1019 for (const Fortran::parser::AccClause
&clause
: clauses
.v
) {
1020 if (std::get_if
<Fortran::parser::AccClause::Seq
>(&clause
.u
)) {
1021 if (info
.deviceTypeInfos().empty()) {
1024 info
.deviceTypeInfos().back().set_isSeq();
1026 } else if (const auto *gangClause
=
1027 std::get_if
<Fortran::parser::AccClause::Gang
>(&clause
.u
)) {
1028 if (info
.deviceTypeInfos().empty()) {
1031 info
.deviceTypeInfos().back().set_isGang();
1033 if (gangClause
->v
) {
1034 const Fortran::parser::AccGangArgList
&x
= *gangClause
->v
;
1035 for (const Fortran::parser::AccGangArg
&gangArg
: x
.v
) {
1036 if (const auto *dim
=
1037 std::get_if
<Fortran::parser::AccGangArg::Dim
>(&gangArg
.u
)) {
1038 if (const auto v
{EvaluateInt64(context_
, dim
->v
)}) {
1039 if (info
.deviceTypeInfos().empty()) {
1040 info
.set_gangDim(*v
);
1042 info
.deviceTypeInfos().back().set_gangDim(*v
);
1048 } else if (std::get_if
<Fortran::parser::AccClause::Vector
>(&clause
.u
)) {
1049 if (info
.deviceTypeInfos().empty()) {
1050 info
.set_isVector();
1052 info
.deviceTypeInfos().back().set_isVector();
1054 } else if (std::get_if
<Fortran::parser::AccClause::Worker
>(&clause
.u
)) {
1055 if (info
.deviceTypeInfos().empty()) {
1056 info
.set_isWorker();
1058 info
.deviceTypeInfos().back().set_isWorker();
1060 } else if (std::get_if
<Fortran::parser::AccClause::Nohost
>(&clause
.u
)) {
1061 info
.set_isNohost();
1062 } else if (const auto *bindClause
=
1063 std::get_if
<Fortran::parser::AccClause::Bind
>(&clause
.u
)) {
1064 if (const auto *name
=
1065 std::get_if
<Fortran::parser::Name
>(&bindClause
->v
.u
)) {
1066 if (Symbol
*sym
= ResolveFctName(*name
)) {
1067 if (info
.deviceTypeInfos().empty()) {
1068 info
.set_bindName(sym
->name().ToString());
1070 info
.deviceTypeInfos().back().set_bindName(
1071 sym
->name().ToString());
1074 context_
.Say((*name
).source
,
1075 "No function or subroutine declared for '%s'"_err_en_US
,
1078 } else if (const auto charExpr
=
1079 std::get_if
<Fortran::parser::ScalarDefaultCharExpr
>(
1080 &bindClause
->v
.u
)) {
1082 Fortran::parser::Unwrap
<Fortran::parser::CharLiteralConstant
>(
1084 std::string str
{std::get
<std::string
>(charConst
->t
)};
1085 std::stringstream bindName
;
1086 bindName
<< "\"" << str
<< "\"";
1087 if (info
.deviceTypeInfos().empty()) {
1088 info
.set_bindName(bindName
.str());
1090 info
.deviceTypeInfos().back().set_bindName(bindName
.str());
1093 } else if (const auto *dType
=
1094 std::get_if
<Fortran::parser::AccClause::DeviceType
>(
1096 const parser::AccDeviceTypeExprList
&deviceTypeExprList
= dType
->v
;
1097 OpenACCRoutineDeviceTypeInfo dtypeInfo
;
1098 dtypeInfo
.set_dType(deviceTypeExprList
.v
.front().v
);
1099 info
.add_deviceTypeInfo(dtypeInfo
);
1102 symbol
.get
<SubprogramDetails
>().add_openACCRoutineInfo(info
);
1106 bool AccAttributeVisitor::Pre(const parser::OpenACCRoutineConstruct
&x
) {
1107 const auto &verbatim
{std::get
<parser::Verbatim
>(x
.t
)};
1110 verbatim
.source
, llvm::acc::Directive::ACCD_routine
, *topScope_
);
1112 PushContext(verbatim
.source
, llvm::acc::Directive::ACCD_routine
);
1114 const auto &optName
{std::get
<std::optional
<parser::Name
>>(x
.t
)};
1116 if (Symbol
*sym
= ResolveFctName(*optName
)) {
1117 Symbol
&ultimate
{sym
->GetUltimate()};
1118 AddRoutineInfoToSymbol(ultimate
, x
);
1120 context_
.Say((*optName
).source
,
1121 "No function or subroutine declared for '%s'"_err_en_US
,
1125 if (currScope().symbol()) {
1126 AddRoutineInfoToSymbol(*currScope().symbol(), x
);
1132 bool AccAttributeVisitor::Pre(const parser::AccBindClause
&x
) {
1133 if (const auto *name
{std::get_if
<parser::Name
>(&x
.u
)}) {
1134 if (!ResolveFctName(*name
)) {
1135 context_
.Say(name
->source
,
1136 "No function or subroutine declared for '%s'"_err_en_US
,
1143 bool AccAttributeVisitor::Pre(const parser::OpenACCCombinedConstruct
&x
) {
1144 const auto &beginBlockDir
{std::get
<parser::AccBeginCombinedDirective
>(x
.t
)};
1145 const auto &combinedDir
{
1146 std::get
<parser::AccCombinedDirective
>(beginBlockDir
.t
)};
1147 switch (combinedDir
.v
) {
1148 case llvm::acc::Directive::ACCD_kernels_loop
:
1149 case llvm::acc::Directive::ACCD_parallel_loop
:
1150 case llvm::acc::Directive::ACCD_serial_loop
:
1151 PushContext(combinedDir
.source
, combinedDir
.v
);
1156 const auto &clauseList
{std::get
<parser::AccClauseList
>(beginBlockDir
.t
)};
1157 SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList
));
1158 const auto &outer
{std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)};
1159 CheckAssociatedLoop(*outer
);
1160 ClearDataSharingAttributeObjects();
1164 static bool IsLastNameArray(const parser::Designator
&designator
) {
1165 const auto &name
{GetLastName(designator
)};
1166 const evaluate::DataRef dataRef
{*(name
.symbol
)};
1167 return common::visit(
1169 [](const evaluate::SymbolRef
&ref
) {
1170 return ref
->Rank() > 0 ||
1171 ref
->GetType()->category() == DeclTypeSpec::Numeric
;
1173 [](const evaluate::ArrayRef
&aref
) {
1174 return aref
.base().IsSymbol() ||
1175 aref
.base().GetComponent().base().Rank() == 0;
1177 [](const auto &) { return false; },
1182 void AccAttributeVisitor::AllowOnlyArrayAndSubArray(
1183 const parser::AccObjectList
&objectList
) {
1184 for (const auto &accObject
: objectList
.v
) {
1187 [&](const parser::Designator
&designator
) {
1188 if (!IsLastNameArray(designator
)) {
1189 context_
.Say(designator
.source
,
1190 "Only array element or subarray are allowed in %s directive"_err_en_US
,
1191 parser::ToUpperCaseLetters(
1192 llvm::acc::getOpenACCDirectiveName(
1193 GetContext().directive
)
1197 [&](const auto &name
) {
1198 context_
.Say(name
.source
,
1199 "Only array element or subarray are allowed in %s directive"_err_en_US
,
1200 parser::ToUpperCaseLetters(
1201 llvm::acc::getOpenACCDirectiveName(GetContext().directive
)
1209 void AccAttributeVisitor::DoNotAllowAssumedSizedArray(
1210 const parser::AccObjectList
&objectList
) {
1211 for (const auto &accObject
: objectList
.v
) {
1214 [&](const parser::Designator
&designator
) {
1215 const auto &name
{GetLastName(designator
)};
1216 if (name
.symbol
&& semantics::IsAssumedSizeArray(*name
.symbol
)) {
1217 context_
.Say(designator
.source
,
1218 "Assumed-size dummy arrays may not appear on the %s "
1219 "directive"_err_en_US
,
1220 parser::ToUpperCaseLetters(
1221 llvm::acc::getOpenACCDirectiveName(
1222 GetContext().directive
)
1226 [&](const auto &name
) {
1234 void AccAttributeVisitor::AllowOnlyVariable(const parser::AccObject
&object
) {
1237 [&](const parser::Designator
&designator
) {
1238 const auto &name
{GetLastName(designator
)};
1239 if (name
.symbol
&& !semantics::IsVariableName(*name
.symbol
) &&
1240 !semantics::IsNamedConstant(*name
.symbol
)) {
1241 context_
.Say(designator
.source
,
1242 "Only variables are allowed in data clauses on the %s "
1243 "directive"_err_en_US
,
1244 parser::ToUpperCaseLetters(
1245 llvm::acc::getOpenACCDirectiveName(GetContext().directive
)
1249 [&](const auto &name
) {},
1254 bool AccAttributeVisitor::Pre(const parser::OpenACCCacheConstruct
&x
) {
1255 const auto &verbatim
{std::get
<parser::Verbatim
>(x
.t
)};
1256 PushContext(verbatim
.source
, llvm::acc::Directive::ACCD_cache
);
1257 ClearDataSharingAttributeObjects();
1259 const auto &objectListWithModifier
=
1260 std::get
<parser::AccObjectListWithModifier
>(x
.t
);
1261 const auto &objectList
=
1262 std::get
<Fortran::parser::AccObjectList
>(objectListWithModifier
.t
);
1264 // 2.10 Cache directive restriction: A var in a cache directive must be a
1265 // single array element or a simple subarray.
1266 AllowOnlyArrayAndSubArray(objectList
);
1271 std::int64_t AccAttributeVisitor::GetAssociatedLoopLevelFromClauses(
1272 const parser::AccClauseList
&x
) {
1273 std::int64_t collapseLevel
{0};
1274 for (const auto &clause
: x
.v
) {
1275 if (const auto *collapseClause
{
1276 std::get_if
<parser::AccClause::Collapse
>(&clause
.u
)}) {
1277 const parser::AccCollapseArg
&arg
= collapseClause
->v
;
1278 const auto &collapseValue
{std::get
<parser::ScalarIntConstantExpr
>(arg
.t
)};
1279 if (const auto v
{EvaluateInt64(context_
, collapseValue
)}) {
1285 if (collapseLevel
) {
1286 return collapseLevel
;
1288 return 1; // default is outermost loop
1291 void AccAttributeVisitor::CheckAssociatedLoop(
1292 const parser::DoConstruct
&outerDoConstruct
) {
1293 std::int64_t level
{GetContext().associatedLoopLevel
};
1294 if (level
<= 0) { // collapse value was negative or 0
1298 const auto getNextDoConstruct
=
1299 [this](const parser::Block
&block
,
1300 std::int64_t &level
) -> const parser::DoConstruct
* {
1301 for (const auto &entry
: block
) {
1302 if (const auto *doConstruct
= GetDoConstructIf(entry
)) {
1304 } else if (parser::Unwrap
<parser::CompilerDirective
>(entry
)) {
1305 // It is allowed to have a compiler directive associated with the loop.
1307 } else if (const auto &accLoop
{
1308 parser::Unwrap
<parser::OpenACCLoopConstruct
>(entry
)}) {
1311 const auto &beginDir
{
1312 std::get
<parser::AccBeginLoopDirective
>(accLoop
->t
)};
1313 context_
.Say(beginDir
.source
,
1314 "LOOP directive not expected in COLLAPSE loop nest"_err_en_US
);
1323 auto checkExprHasSymbols
= [&](llvm::SmallVector
<Symbol
*> &ivs
,
1324 semantics::UnorderedSymbolSet
&symbols
) {
1325 for (auto iv
: ivs
) {
1326 if (symbols
.count(*iv
) != 0) {
1327 context_
.Say(GetContext().directiveSource
,
1328 "Trip count must be computable and invariant"_err_en_US
);
1333 Symbol::Flag flag
= Symbol::Flag::AccPrivate
;
1334 llvm::SmallVector
<Symbol
*> ivs
;
1335 using Bounds
= parser::LoopControl::Bounds
;
1336 for (const parser::DoConstruct
*loop
{&outerDoConstruct
}; loop
&& level
> 0;) {
1337 // Go through all nested loops to ensure index variable exists.
1338 if (const parser::Name
* ivName
{GetLoopIndex(*loop
)}) {
1339 if (auto *symbol
{ResolveAcc(*ivName
, flag
, currScope())}) {
1340 if (auto &control
{loop
->GetLoopControl()}) {
1341 if (const Bounds
* b
{std::get_if
<Bounds
>(&control
->u
)}) {
1342 if (auto lowerExpr
{semantics::AnalyzeExpr(context_
, b
->lower
)}) {
1343 semantics::UnorderedSymbolSet lowerSyms
=
1344 evaluate::CollectSymbols(*lowerExpr
);
1345 checkExprHasSymbols(ivs
, lowerSyms
);
1347 if (auto upperExpr
{semantics::AnalyzeExpr(context_
, b
->upper
)}) {
1348 semantics::UnorderedSymbolSet upperSyms
=
1349 evaluate::CollectSymbols(*upperExpr
);
1350 checkExprHasSymbols(ivs
, upperSyms
);
1354 ivs
.push_back(symbol
);
1358 const auto &block
{std::get
<parser::Block
>(loop
->t
)};
1360 loop
= getNextDoConstruct(block
, level
);
1365 void AccAttributeVisitor::EnsureAllocatableOrPointer(
1366 const llvm::acc::Clause clause
, const parser::AccObjectList
&objectList
) {
1367 for (const auto &accObject
: objectList
.v
) {
1370 [&](const parser::Designator
&designator
) {
1371 const auto &lastName
{GetLastName(designator
)};
1372 if (!IsAllocatableOrObjectPointer(lastName
.symbol
)) {
1373 context_
.Say(designator
.source
,
1374 "Argument `%s` on the %s clause must be a variable or "
1375 "array with the POINTER or ALLOCATABLE attribute"_err_en_US
,
1376 lastName
.symbol
->name(),
1377 parser::ToUpperCaseLetters(
1378 llvm::acc::getOpenACCClauseName(clause
).str()));
1381 [&](const auto &name
) {
1382 context_
.Say(name
.source
,
1383 "Argument on the %s clause must be a variable or "
1384 "array with the POINTER or ALLOCATABLE attribute"_err_en_US
,
1385 parser::ToUpperCaseLetters(
1386 llvm::acc::getOpenACCClauseName(clause
).str()));
1393 bool AccAttributeVisitor::Pre(const parser::AccClause::Attach
&x
) {
1394 // Restriction - line 1708-1709
1395 EnsureAllocatableOrPointer(llvm::acc::Clause::ACCC_attach
, x
.v
);
1399 bool AccAttributeVisitor::Pre(const parser::AccClause::Detach
&x
) {
1400 // Restriction - line 1715-1717
1401 EnsureAllocatableOrPointer(llvm::acc::Clause::ACCC_detach
, x
.v
);
1405 void AccAttributeVisitor::Post(const parser::AccDefaultClause
&x
) {
1406 if (!dirContext_
.empty()) {
1408 case llvm::acc::DefaultValue::ACC_Default_present
:
1409 SetContextDefaultDSA(Symbol::Flag::AccPresent
);
1411 case llvm::acc::DefaultValue::ACC_Default_none
:
1412 SetContextDefaultDSA(Symbol::Flag::AccNone
);
1418 // For OpenACC constructs, check all the data-refs within the constructs
1419 // and adjust the symbol for each Name if necessary
1420 void AccAttributeVisitor::Post(const parser::Name
&name
) {
1421 auto *symbol
{name
.symbol
};
1422 if (symbol
&& !dirContext_
.empty() && GetContext().withinConstruct
) {
1423 if (!symbol
->owner().IsDerivedType() && !symbol
->has
<ProcEntityDetails
>() &&
1424 !symbol
->has
<SubprogramDetails
>() && !IsObjectWithDSA(*symbol
)) {
1425 if (Symbol
* found
{currScope().FindSymbol(name
.source
)}) {
1426 if (symbol
!= found
) {
1427 name
.symbol
= found
; // adjust the symbol within region
1428 } else if (GetContext().defaultDSA
== Symbol::Flag::AccNone
) {
1430 context_
.Say(name
.source
,
1431 "The DEFAULT(NONE) clause requires that '%s' must be listed in "
1432 "a data-mapping clause"_err_en_US
,
1437 } // within OpenACC construct
1440 Symbol
*AccAttributeVisitor::ResolveAccCommonBlockName(
1441 const parser::Name
*name
) {
1443 ? GetContext().scope
.parent().FindCommonBlock(name
->source
)
1445 name
->symbol
= prev
;
1448 // Check if the Common Block is declared in the current scope
1449 if (auto *commonBlockSymbol
{
1450 name
? GetContext().scope
.FindCommonBlock(name
->source
) : nullptr}) {
1451 name
->symbol
= commonBlockSymbol
;
1452 return commonBlockSymbol
;
1457 void AccAttributeVisitor::ResolveAccObjectList(
1458 const parser::AccObjectList
&accObjectList
, Symbol::Flag accFlag
) {
1459 for (const auto &accObject
: accObjectList
.v
) {
1460 AllowOnlyVariable(accObject
);
1461 ResolveAccObject(accObject
, accFlag
);
1465 void AccAttributeVisitor::ResolveAccObject(
1466 const parser::AccObject
&accObject
, Symbol::Flag accFlag
) {
1469 [&](const parser::Designator
&designator
) {
1470 if (const auto *name
{
1471 semantics::getDesignatorNameIfDataRef(designator
)}) {
1472 if (auto *symbol
{ResolveAcc(*name
, accFlag
, currScope())}) {
1473 AddToContextObjectWithDSA(*symbol
, accFlag
);
1474 if (dataSharingAttributeFlags
.test(accFlag
)) {
1475 CheckMultipleAppearances(*name
, *symbol
, accFlag
);
1479 // Array sections to be changed to substrings as needed
1480 if (AnalyzeExpr(context_
, designator
)) {
1481 if (std::holds_alternative
<parser::Substring
>(designator
.u
)) {
1482 context_
.Say(designator
.source
,
1483 "Substrings are not allowed on OpenACC "
1484 "directives or clauses"_err_en_US
);
1487 // other checks, more TBD
1490 [&](const parser::Name
&name
) { // common block
1491 if (auto *symbol
{ResolveAccCommonBlockName(&name
)}) {
1492 CheckMultipleAppearances(
1493 name
, *symbol
, Symbol::Flag::AccCommonBlock
);
1494 for (auto &object
: symbol
->get
<CommonBlockDetails
>().objects()) {
1495 if (auto *resolvedObject
{
1496 ResolveAcc(*object
, accFlag
, currScope())}) {
1497 AddToContextObjectWithDSA(*resolvedObject
, accFlag
);
1501 context_
.Say(name
.source
,
1502 "COMMON block must be declared in the same scoping unit "
1503 "in which the OpenACC directive or clause appears"_err_en_US
);
1510 Symbol
*AccAttributeVisitor::ResolveAcc(
1511 const parser::Name
&name
, Symbol::Flag accFlag
, Scope
&scope
) {
1512 return DeclareOrMarkOtherAccessEntity(name
, accFlag
);
1515 Symbol
*AccAttributeVisitor::ResolveAcc(
1516 Symbol
&symbol
, Symbol::Flag accFlag
, Scope
&scope
) {
1517 return DeclareOrMarkOtherAccessEntity(symbol
, accFlag
);
1520 Symbol
*AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1521 const parser::Name
&name
, Symbol::Flag accFlag
) {
1522 Symbol
*prev
{currScope().FindSymbol(name
.source
)};
1523 if (!name
.symbol
|| !prev
) {
1525 } else if (prev
!= name
.symbol
) {
1528 return DeclareOrMarkOtherAccessEntity(*prev
, accFlag
);
1531 Symbol
*AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
1532 Symbol
&object
, Symbol::Flag accFlag
) {
1533 if (accFlagsRequireMark
.test(accFlag
)) {
1534 if (GetContext().directive
== llvm::acc::ACCD_declare
) {
1535 object
.set(Symbol::Flag::AccDeclare
);
1536 object
.set(accFlag
);
1542 static bool WithMultipleAppearancesAccException(
1543 const Symbol
&symbol
, Symbol::Flag flag
) {
1544 return false; // Place holder
1547 void AccAttributeVisitor::CheckMultipleAppearances(
1548 const parser::Name
&name
, const Symbol
&symbol
, Symbol::Flag accFlag
) {
1549 const auto *target
{&symbol
};
1550 if (HasDataSharingAttributeObject(*target
) &&
1551 !WithMultipleAppearancesAccException(symbol
, accFlag
)) {
1552 context_
.Say(name
.source
,
1553 "'%s' appears in more than one data-sharing clause "
1554 "on the same OpenACC directive"_err_en_US
,
1557 AddDataSharingAttributeObject(*target
);
1561 bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct
&x
) {
1562 const auto &beginBlockDir
{std::get
<parser::OmpBeginBlockDirective
>(x
.t
)};
1563 const auto &beginDir
{std::get
<parser::OmpBlockDirective
>(beginBlockDir
.t
)};
1564 switch (beginDir
.v
) {
1565 case llvm::omp::Directive::OMPD_masked
:
1566 case llvm::omp::Directive::OMPD_parallel_masked
:
1567 case llvm::omp::Directive::OMPD_master
:
1568 case llvm::omp::Directive::OMPD_parallel_master
:
1569 case llvm::omp::Directive::OMPD_ordered
:
1570 case llvm::omp::Directive::OMPD_parallel
:
1571 case llvm::omp::Directive::OMPD_scope
:
1572 case llvm::omp::Directive::OMPD_single
:
1573 case llvm::omp::Directive::OMPD_target
:
1574 case llvm::omp::Directive::OMPD_target_data
:
1575 case llvm::omp::Directive::OMPD_task
:
1576 case llvm::omp::Directive::OMPD_taskgroup
:
1577 case llvm::omp::Directive::OMPD_teams
:
1578 case llvm::omp::Directive::OMPD_workshare
:
1579 case llvm::omp::Directive::OMPD_parallel_workshare
:
1580 case llvm::omp::Directive::OMPD_target_teams
:
1581 case llvm::omp::Directive::OMPD_target_parallel
:
1582 PushContext(beginDir
.source
, beginDir
.v
);
1588 if (beginDir
.v
== llvm::omp::Directive::OMPD_master
||
1589 beginDir
.v
== llvm::omp::Directive::OMPD_parallel_master
)
1590 IssueNonConformanceWarning(beginDir
.v
, beginDir
.source
);
1591 ClearDataSharingAttributeObjects();
1592 ClearPrivateDataSharingAttributeObjects();
1593 ClearAllocateNames();
1597 void OmpAttributeVisitor::Post(const parser::OpenMPBlockConstruct
&x
) {
1598 const auto &beginBlockDir
{std::get
<parser::OmpBeginBlockDirective
>(x
.t
)};
1599 const auto &beginDir
{std::get
<parser::OmpBlockDirective
>(beginBlockDir
.t
)};
1600 switch (beginDir
.v
) {
1601 case llvm::omp::Directive::OMPD_masked
:
1602 case llvm::omp::Directive::OMPD_master
:
1603 case llvm::omp::Directive::OMPD_parallel_masked
:
1604 case llvm::omp::Directive::OMPD_parallel_master
:
1605 case llvm::omp::Directive::OMPD_parallel
:
1606 case llvm::omp::Directive::OMPD_scope
:
1607 case llvm::omp::Directive::OMPD_single
:
1608 case llvm::omp::Directive::OMPD_target
:
1609 case llvm::omp::Directive::OMPD_task
:
1610 case llvm::omp::Directive::OMPD_teams
:
1611 case llvm::omp::Directive::OMPD_parallel_workshare
:
1612 case llvm::omp::Directive::OMPD_target_teams
:
1613 case llvm::omp::Directive::OMPD_target_parallel
: {
1615 for (const auto *allocName
: allocateNames_
) {
1617 for (auto privateObj
: privateDataSharingAttributeObjects_
) {
1618 const Symbol
&symbolPrivate
{*privateObj
};
1619 if (allocName
->source
== symbolPrivate
.name()) {
1625 context_
.Say(allocName
->source
,
1626 "The ALLOCATE clause requires that '%s' must be listed in a "
1628 "data-sharing attribute clause on the same directive"_err_en_US
,
1629 allocName
->ToString());
1640 bool OmpAttributeVisitor::Pre(
1641 const parser::OpenMPSimpleStandaloneConstruct
&x
) {
1642 const auto &standaloneDir
{
1643 std::get
<parser::OmpSimpleStandaloneDirective
>(x
.t
)};
1644 switch (standaloneDir
.v
) {
1645 case llvm::omp::Directive::OMPD_barrier
:
1646 case llvm::omp::Directive::OMPD_ordered
:
1647 case llvm::omp::Directive::OMPD_scan
:
1648 case llvm::omp::Directive::OMPD_target_enter_data
:
1649 case llvm::omp::Directive::OMPD_target_exit_data
:
1650 case llvm::omp::Directive::OMPD_target_update
:
1651 case llvm::omp::Directive::OMPD_taskwait
:
1652 case llvm::omp::Directive::OMPD_taskyield
:
1653 PushContext(standaloneDir
.source
, standaloneDir
.v
);
1658 ClearDataSharingAttributeObjects();
1662 bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct
&x
) {
1663 const auto &beginLoopDir
{std::get
<parser::OmpBeginLoopDirective
>(x
.t
)};
1664 const auto &beginDir
{std::get
<parser::OmpLoopDirective
>(beginLoopDir
.t
)};
1665 const auto &clauseList
{std::get
<parser::OmpClauseList
>(beginLoopDir
.t
)};
1666 switch (beginDir
.v
) {
1667 case llvm::omp::Directive::OMPD_distribute
:
1668 case llvm::omp::Directive::OMPD_distribute_parallel_do
:
1669 case llvm::omp::Directive::OMPD_distribute_parallel_do_simd
:
1670 case llvm::omp::Directive::OMPD_distribute_simd
:
1671 case llvm::omp::Directive::OMPD_do
:
1672 case llvm::omp::Directive::OMPD_do_simd
:
1673 case llvm::omp::Directive::OMPD_loop
:
1674 case llvm::omp::Directive::OMPD_masked_taskloop_simd
:
1675 case llvm::omp::Directive::OMPD_masked_taskloop
:
1676 case llvm::omp::Directive::OMPD_master_taskloop_simd
:
1677 case llvm::omp::Directive::OMPD_master_taskloop
:
1678 case llvm::omp::Directive::OMPD_parallel_do
:
1679 case llvm::omp::Directive::OMPD_parallel_do_simd
:
1680 case llvm::omp::Directive::OMPD_parallel_masked_taskloop_simd
:
1681 case llvm::omp::Directive::OMPD_parallel_masked_taskloop
:
1682 case llvm::omp::Directive::OMPD_parallel_master_taskloop_simd
:
1683 case llvm::omp::Directive::OMPD_parallel_master_taskloop
:
1684 case llvm::omp::Directive::OMPD_simd
:
1685 case llvm::omp::Directive::OMPD_target_loop
:
1686 case llvm::omp::Directive::OMPD_target_parallel_do
:
1687 case llvm::omp::Directive::OMPD_target_parallel_do_simd
:
1688 case llvm::omp::Directive::OMPD_target_parallel_loop
:
1689 case llvm::omp::Directive::OMPD_target_teams_distribute
:
1690 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do
:
1691 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd
:
1692 case llvm::omp::Directive::OMPD_target_teams_distribute_simd
:
1693 case llvm::omp::Directive::OMPD_target_teams_loop
:
1694 case llvm::omp::Directive::OMPD_target_simd
:
1695 case llvm::omp::Directive::OMPD_taskloop
:
1696 case llvm::omp::Directive::OMPD_taskloop_simd
:
1697 case llvm::omp::Directive::OMPD_teams_distribute
:
1698 case llvm::omp::Directive::OMPD_teams_distribute_parallel_do
:
1699 case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd
:
1700 case llvm::omp::Directive::OMPD_teams_distribute_simd
:
1701 case llvm::omp::Directive::OMPD_teams_loop
:
1702 case llvm::omp::Directive::OMPD_tile
:
1703 case llvm::omp::Directive::OMPD_unroll
:
1704 PushContext(beginDir
.source
, beginDir
.v
);
1709 if (beginDir
.v
== llvm::omp::OMPD_master_taskloop
||
1710 beginDir
.v
== llvm::omp::OMPD_master_taskloop_simd
||
1711 beginDir
.v
== llvm::omp::OMPD_parallel_master_taskloop
||
1712 beginDir
.v
== llvm::omp::OMPD_parallel_master_taskloop_simd
||
1713 beginDir
.v
== llvm::omp::Directive::OMPD_target_loop
)
1714 IssueNonConformanceWarning(beginDir
.v
, beginDir
.source
);
1715 ClearDataSharingAttributeObjects();
1716 SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList
));
1718 if (beginDir
.v
== llvm::omp::Directive::OMPD_do
) {
1719 if (const auto &doConstruct
{
1720 std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)}) {
1721 if (doConstruct
.value().IsDoWhile()) {
1726 PrivatizeAssociatedLoopIndexAndCheckLoopLevel(x
);
1727 ordCollapseLevel
= GetAssociatedLoopLevelFromClauses(clauseList
) + 1;
1731 void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct(
1732 const parser::Name
&iv
) {
1733 // Find the parallel or task generating construct enclosing the
1735 auto targetIt
{dirContext_
.rbegin()};
1736 for (;; ++targetIt
) {
1737 if (targetIt
== dirContext_
.rend()) {
1740 if (llvm::omp::allParallelSet
.test(targetIt
->directive
) ||
1741 llvm::omp::taskGeneratingSet
.test(targetIt
->directive
)) {
1745 // If this symbol already has a data-sharing attribute then there is nothing
1747 if (const Symbol
* symbol
{iv
.symbol
}) {
1748 for (auto symMap
: targetIt
->objectWithDSA
) {
1749 if (symMap
.first
->name() == symbol
->name()) {
1754 // If this symbol is already Private or Firstprivate in the enclosing
1755 // OpenMP parallel or task then there is nothing to do here.
1756 if (auto *symbol
{targetIt
->scope
.FindSymbol(iv
.source
)}) {
1757 if (symbol
->owner() == targetIt
->scope
) {
1758 if (symbol
->test(Symbol::Flag::OmpPrivate
) ||
1759 symbol
->test(Symbol::Flag::OmpFirstPrivate
)) {
1764 // Otherwise find the symbol and make it Private for the entire enclosing
1766 if (auto *symbol
{ResolveOmp(iv
, Symbol::Flag::OmpPrivate
, targetIt
->scope
)}) {
1768 symbol
->set(Symbol::Flag::OmpPreDetermined
);
1769 iv
.symbol
= symbol
; // adjust the symbol within region
1770 for (auto it
{dirContext_
.rbegin()}; it
!= targetIt
; ++it
) {
1771 AddToContextObjectWithDSA(*symbol
, Symbol::Flag::OmpPrivate
, *it
);
1776 // [OMP-4.5]2.15.1.1 Data-sharing Attribute Rules - Predetermined
1777 // - A loop iteration variable for a sequential loop in a parallel
1778 // or task generating construct is private in the innermost such
1779 // construct that encloses the loop
1780 // Loop iteration variables are not well defined for DO WHILE loop.
1781 // Use of DO CONCURRENT inside OpenMP construct is unspecified behavior
1782 // till OpenMP-5.0 standard.
1783 // In above both cases we skip the privatization of iteration variables.
1784 // [OpenMP 5.1] DO CONCURRENT indices are private
1785 bool OmpAttributeVisitor::Pre(const parser::DoConstruct
&x
) {
1786 if (!dirContext_
.empty() && GetContext().withinConstruct
) {
1787 llvm::SmallVector
<const parser::Name
*> ivs
;
1788 if (x
.IsDoNormal()) {
1789 const parser::Name
*iv
{GetLoopIndex(x
)};
1790 if (iv
&& iv
->symbol
)
1792 } else if (x
.IsDoConcurrent()) {
1793 const Fortran::parser::LoopControl
*loopControl
= &*x
.GetLoopControl();
1794 const Fortran::parser::LoopControl::Concurrent
&concurrent
=
1795 std::get
<Fortran::parser::LoopControl::Concurrent
>(loopControl
->u
);
1796 const Fortran::parser::ConcurrentHeader
&concurrentHeader
=
1797 std::get
<Fortran::parser::ConcurrentHeader
>(concurrent
.t
);
1798 const std::list
<Fortran::parser::ConcurrentControl
> &controls
=
1799 std::get
<std::list
<Fortran::parser::ConcurrentControl
>>(
1800 concurrentHeader
.t
);
1801 for (const auto &control
: controls
) {
1802 const parser::Name
*iv
{&std::get
<0>(control
.t
)};
1803 if (iv
&& iv
->symbol
)
1808 for (auto iv
: ivs
) {
1809 if (!iv
->symbol
->test(Symbol::Flag::OmpPreDetermined
)) {
1810 ResolveSeqLoopIndexInParallelOrTaskConstruct(*iv
);
1812 // TODO: conflict checks with explicitly determined DSA
1814 if (ordCollapseLevel
) {
1815 if (const auto *details
{iv
->symbol
->detailsIf
<HostAssocDetails
>()}) {
1816 const Symbol
*tpSymbol
= &details
->symbol();
1817 // TODO: DoConcurrent won't capture the following check because a new
1818 // symbol is declared in ResolveIndexName(), which will not have the
1819 // OmpThreadprivate flag.
1820 if (tpSymbol
->test(Symbol::Flag::OmpThreadprivate
)) {
1821 context_
.Say(iv
->source
,
1822 "Loop iteration variable %s is not allowed in THREADPRIVATE."_err_en_US
,
1832 std::int64_t OmpAttributeVisitor::GetAssociatedLoopLevelFromClauses(
1833 const parser::OmpClauseList
&x
) {
1834 std::int64_t orderedLevel
{0};
1835 std::int64_t collapseLevel
{0};
1837 const parser::OmpClause
*ordClause
{nullptr};
1838 const parser::OmpClause
*collClause
{nullptr};
1840 for (const auto &clause
: x
.v
) {
1841 if (const auto *orderedClause
{
1842 std::get_if
<parser::OmpClause::Ordered
>(&clause
.u
)}) {
1843 if (const auto v
{EvaluateInt64(context_
, orderedClause
->v
)}) {
1846 ordClause
= &clause
;
1848 if (const auto *collapseClause
{
1849 std::get_if
<parser::OmpClause::Collapse
>(&clause
.u
)}) {
1850 if (const auto v
{EvaluateInt64(context_
, collapseClause
->v
)}) {
1853 collClause
= &clause
;
1857 if (orderedLevel
&& (!collapseLevel
|| orderedLevel
>= collapseLevel
)) {
1858 SetAssociatedClause(*ordClause
);
1859 return orderedLevel
;
1860 } else if (!orderedLevel
&& collapseLevel
) {
1861 SetAssociatedClause(*collClause
);
1862 return collapseLevel
;
1863 } // orderedLevel < collapseLevel is an error handled in structural checks
1864 return 1; // default is outermost loop
1867 // 2.15.1.1 Data-sharing Attribute Rules - Predetermined
1868 // - The loop iteration variable(s) in the associated do-loop(s) of a do,
1869 // parallel do, taskloop, or distribute construct is (are) private.
1870 // - The loop iteration variable in the associated do-loop of a simd construct
1871 // with just one associated do-loop is linear with a linear-step that is the
1872 // increment of the associated do-loop.
1873 // - The loop iteration variables in the associated do-loops of a simd
1874 // construct with multiple associated do-loops are lastprivate.
1875 void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
1876 const parser::OpenMPLoopConstruct
&x
) {
1877 std::int64_t level
{GetContext().associatedLoopLevel
};
1882 if (!llvm::omp::allSimdSet
.test(GetContext().directive
)) {
1883 ivDSA
= Symbol::Flag::OmpPrivate
;
1884 } else if (level
== 1) {
1885 ivDSA
= Symbol::Flag::OmpLinear
;
1887 ivDSA
= Symbol::Flag::OmpLastPrivate
;
1890 const auto &outer
{std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)};
1891 for (const parser::DoConstruct
*loop
{&*outer
}; loop
&& level
> 0; --level
) {
1892 // go through all the nested do-loops and resolve index variables
1893 const parser::Name
*iv
{GetLoopIndex(*loop
)};
1895 if (auto *symbol
{ResolveOmp(*iv
, ivDSA
, currScope())}) {
1896 symbol
->set(Symbol::Flag::OmpPreDetermined
);
1897 iv
->symbol
= symbol
; // adjust the symbol within region
1898 AddToContextObjectWithDSA(*symbol
, ivDSA
);
1901 const auto &block
{std::get
<parser::Block
>(loop
->t
)};
1902 const auto it
{block
.begin()};
1903 loop
= it
!= block
.end() ? GetDoConstructIf(*it
) : nullptr;
1906 CheckAssocLoopLevel(level
, GetAssociatedClause());
1908 void OmpAttributeVisitor::CheckAssocLoopLevel(
1909 std::int64_t level
, const parser::OmpClause
*clause
) {
1910 if (clause
&& level
!= 0) {
1911 context_
.Say(clause
->source
,
1912 "The value of the parameter in the COLLAPSE or ORDERED clause must"
1913 " not be larger than the number of nested loops"
1914 " following the construct."_err_en_US
);
1918 bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct
&x
) {
1919 const auto &beginSectionsDir
{
1920 std::get
<parser::OmpBeginSectionsDirective
>(x
.t
)};
1921 const auto &beginDir
{
1922 std::get
<parser::OmpSectionsDirective
>(beginSectionsDir
.t
)};
1923 switch (beginDir
.v
) {
1924 case llvm::omp::Directive::OMPD_parallel_sections
:
1925 case llvm::omp::Directive::OMPD_sections
:
1926 PushContext(beginDir
.source
, beginDir
.v
);
1927 GetContext().withinConstruct
= true;
1932 ClearDataSharingAttributeObjects();
1936 bool OmpAttributeVisitor::Pre(const parser::OpenMPCriticalConstruct
&x
) {
1937 const auto &beginCriticalDir
{std::get
<parser::OmpCriticalDirective
>(x
.t
)};
1938 const auto &endCriticalDir
{std::get
<parser::OmpEndCriticalDirective
>(x
.t
)};
1939 PushContext(beginCriticalDir
.source
, llvm::omp::Directive::OMPD_critical
);
1940 GetContext().withinConstruct
= true;
1941 if (const auto &criticalName
{
1942 std::get
<std::optional
<parser::Name
>>(beginCriticalDir
.t
)}) {
1943 ResolveOmpName(*criticalName
, Symbol::Flag::OmpCriticalLock
);
1945 if (const auto &endCriticalName
{
1946 std::get
<std::optional
<parser::Name
>>(endCriticalDir
.t
)}) {
1947 ResolveOmpName(*endCriticalName
, Symbol::Flag::OmpCriticalLock
);
1952 bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclareTargetConstruct
&x
) {
1953 PushContext(x
.source
, llvm::omp::Directive::OMPD_declare_target
);
1954 const auto &spec
{std::get
<parser::OmpDeclareTargetSpecifier
>(x
.t
)};
1955 if (const auto *objectList
{parser::Unwrap
<parser::OmpObjectList
>(spec
.u
)}) {
1956 ResolveOmpObjectList(*objectList
, Symbol::Flag::OmpDeclareTarget
);
1957 } else if (const auto *clauseList
{
1958 parser::Unwrap
<parser::OmpClauseList
>(spec
.u
)}) {
1959 for (const auto &clause
: clauseList
->v
) {
1960 if (const auto *toClause
{std::get_if
<parser::OmpClause::To
>(&clause
.u
)}) {
1961 auto &objList
{std::get
<parser::OmpObjectList
>(toClause
->v
.t
)};
1962 ResolveOmpObjectList(objList
, Symbol::Flag::OmpDeclareTarget
);
1963 } else if (const auto *linkClause
{
1964 std::get_if
<parser::OmpClause::Link
>(&clause
.u
)}) {
1965 ResolveOmpObjectList(linkClause
->v
, Symbol::Flag::OmpDeclareTarget
);
1966 } else if (const auto *enterClause
{
1967 std::get_if
<parser::OmpClause::Enter
>(&clause
.u
)}) {
1968 ResolveOmpObjectList(enterClause
->v
, Symbol::Flag::OmpDeclareTarget
);
1975 bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclareMapperConstruct
&x
) {
1976 PushContext(x
.source
, llvm::omp::Directive::OMPD_declare_mapper
);
1980 bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate
&x
) {
1981 PushContext(x
.source
, llvm::omp::Directive::OMPD_threadprivate
);
1982 const auto &list
{std::get
<parser::OmpObjectList
>(x
.t
)};
1983 ResolveOmpObjectList(list
, Symbol::Flag::OmpThreadprivate
);
1987 bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclarativeAllocate
&x
) {
1988 PushContext(x
.source
, llvm::omp::Directive::OMPD_allocate
);
1989 const auto &list
{std::get
<parser::OmpObjectList
>(x
.t
)};
1990 ResolveOmpObjectList(list
, Symbol::Flag::OmpDeclarativeAllocateDirective
);
1994 bool OmpAttributeVisitor::Pre(const parser::OpenMPExecutableAllocate
&x
) {
1995 PushContext(x
.source
, llvm::omp::Directive::OMPD_allocate
);
1996 const auto &list
{std::get
<std::optional
<parser::OmpObjectList
>>(x
.t
)};
1998 ResolveOmpObjectList(*list
, Symbol::Flag::OmpExecutableAllocateDirective
);
2003 bool OmpAttributeVisitor::Pre(const parser::OpenMPAllocatorsConstruct
&x
) {
2004 PushContext(x
.source
, llvm::omp::Directive::OMPD_allocators
);
2005 const auto &clauseList
{std::get
<parser::OmpClauseList
>(x
.t
)};
2006 for (const auto &clause
: clauseList
.v
) {
2007 if (const auto *allocClause
{
2008 std::get_if
<parser::OmpClause::Allocate
>(&clause
.u
)}) {
2009 ResolveOmpObjectList(std::get
<parser::OmpObjectList
>(allocClause
->v
.t
),
2010 Symbol::Flag::OmpExecutableAllocateDirective
);
2016 void OmpAttributeVisitor::Post(const parser::OmpDefaultClause
&x
) {
2017 if (!dirContext_
.empty()) {
2019 case parser::OmpDefaultClause::Type::Private
:
2020 SetContextDefaultDSA(Symbol::Flag::OmpPrivate
);
2022 case parser::OmpDefaultClause::Type::Firstprivate
:
2023 SetContextDefaultDSA(Symbol::Flag::OmpFirstPrivate
);
2025 case parser::OmpDefaultClause::Type::Shared
:
2026 SetContextDefaultDSA(Symbol::Flag::OmpShared
);
2028 case parser::OmpDefaultClause::Type::None
:
2029 SetContextDefaultDSA(Symbol::Flag::OmpNone
);
2035 bool OmpAttributeVisitor::IsNestedInDirective(llvm::omp::Directive directive
) {
2036 if (dirContext_
.size() >= 1) {
2037 for (std::size_t i
= dirContext_
.size() - 1; i
> 0; --i
) {
2038 if (dirContext_
[i
- 1].directive
== directive
) {
2046 void OmpAttributeVisitor::Post(const parser::OpenMPExecutableAllocate
&x
) {
2047 bool hasAllocator
= false;
2048 // TODO: Investigate whether searching the clause list can be done with
2049 // parser::Unwrap instead of the following loop
2050 const auto &clauseList
{std::get
<parser::OmpClauseList
>(x
.t
)};
2051 for (const auto &clause
: clauseList
.v
) {
2052 if (std::get_if
<parser::OmpClause::Allocator
>(&clause
.u
)) {
2053 hasAllocator
= true;
2057 if (IsNestedInDirective(llvm::omp::Directive::OMPD_target
) && !hasAllocator
) {
2058 // TODO: expand this check to exclude the case when a requires
2059 // directive with the dynamic_allocators clause is present
2060 // in the same compilation unit (OMP5.0 2.11.3).
2061 context_
.Say(x
.source
,
2062 "ALLOCATE directives that appear in a TARGET region "
2063 "must specify an allocator clause"_err_en_US
);
2066 const auto &allocateStmt
=
2067 std::get
<parser::Statement
<parser::AllocateStmt
>>(x
.t
).statement
;
2068 if (const auto &list
{std::get
<std::optional
<parser::OmpObjectList
>>(x
.t
)}) {
2069 CheckAllNamesInAllocateStmt(
2070 std::get
<parser::Verbatim
>(x
.t
).source
, *list
, allocateStmt
);
2072 if (const auto &subDirs
{
2073 std::get
<std::optional
<std::list
<parser::OpenMPDeclarativeAllocate
>>>(
2075 for (const auto &dalloc
: *subDirs
) {
2076 CheckAllNamesInAllocateStmt(std::get
<parser::Verbatim
>(dalloc
.t
).source
,
2077 std::get
<parser::OmpObjectList
>(dalloc
.t
), allocateStmt
);
2083 void OmpAttributeVisitor::Post(const parser::OpenMPAllocatorsConstruct
&x
) {
2084 const auto &dir
{std::get
<parser::Verbatim
>(x
.t
)};
2085 const auto &clauseList
{std::get
<parser::OmpClauseList
>(x
.t
)};
2086 for (const auto &clause
: clauseList
.v
) {
2087 if (const auto *alloc
{
2088 std::get_if
<parser::OmpClause::Allocate
>(&clause
.u
)}) {
2089 CheckAllNamesInAllocateStmt(dir
.source
,
2090 std::get
<parser::OmpObjectList
>(alloc
->v
.t
),
2091 std::get
<parser::Statement
<parser::AllocateStmt
>>(x
.t
).statement
);
2093 const auto &allocMod
{
2094 std::get
<std::optional
<parser::OmpAllocateClause::AllocateModifier
>>(
2096 // TODO: As with allocate directive, exclude the case when a requires
2097 // directive with the dynamic_allocators clause is present in
2098 // the same compilation unit (OMP5.0 2.11.3).
2099 if (IsNestedInDirective(llvm::omp::Directive::OMPD_target
) &&
2100 (!allocMod
.has_value() ||
2101 std::holds_alternative
<
2102 parser::OmpAllocateClause::AllocateModifier::Align
>(
2104 context_
.Say(x
.source
,
2105 "ALLOCATORS directives that appear in a TARGET region "
2106 "must specify an allocator"_err_en_US
);
2113 static bool IsPrivatizable(const Symbol
*sym
) {
2114 auto *misc
{sym
->detailsIf
<MiscDetails
>()};
2115 return !IsProcedure(*sym
) && !IsNamedConstant(*sym
) &&
2116 !semantics::IsAssumedSizeArray(
2117 *sym
) && /* OpenMP 5.2, 5.1.1: Assumed-size arrays are shared*/
2118 !sym
->owner().IsDerivedType() &&
2119 sym
->owner().kind() != Scope::Kind::ImpliedDos
&&
2120 !sym
->detailsIf
<semantics::AssocEntityDetails
>() &&
2121 !sym
->detailsIf
<semantics::NamelistDetails
>() &&
2123 (misc
->kind() != MiscDetails::Kind::ComplexPartRe
&&
2124 misc
->kind() != MiscDetails::Kind::ComplexPartIm
&&
2125 misc
->kind() != MiscDetails::Kind::KindParamInquiry
&&
2126 misc
->kind() != MiscDetails::Kind::LenParamInquiry
&&
2127 misc
->kind() != MiscDetails::Kind::ConstructName
));
2130 void OmpAttributeVisitor::CreateImplicitSymbols(
2131 const Symbol
*symbol
, std::optional
<Symbol::Flag
> setFlag
) {
2132 if (!IsPrivatizable(symbol
)) {
2136 // Implicitly determined DSAs
2137 // OMP 5.2 5.1.1 - Variables Referenced in a Construct
2138 Symbol
*lastDeclSymbol
= nullptr;
2139 std::optional
<Symbol::Flag
> prevDSA
;
2140 for (int dirDepth
{0}; dirDepth
< (int)dirContext_
.size(); ++dirDepth
) {
2141 DirContext
&dirContext
= dirContext_
[dirDepth
];
2142 std::optional
<Symbol::Flag
> dsa
;
2144 for (auto symMap
: dirContext
.objectWithDSA
) {
2145 // if the `symbol` already has a data-sharing attribute
2146 if (symMap
.first
->name() == symbol
->name()) {
2147 dsa
= symMap
.second
;
2152 // When handling each implicit rule for a given symbol, one of the
2153 // following 3 actions may be taken:
2154 // 1. Declare a new private symbol.
2155 // 2. Create a new association symbol with no flags, that will represent
2156 // a shared symbol in the current scope. Note that symbols without
2157 // any private flags are considered as shared.
2158 // 3. Use the last declared private symbol, by inserting a new symbol
2159 // in the scope being processed, associated with it.
2160 // If no private symbol was declared previously, then no association
2161 // is needed and the symbol from the enclosing scope will be
2162 // inherited by the current one.
2164 // Because of how symbols are collected in lowering, not inserting a new
2165 // symbol in the last case could lead to the conclusion that a symbol
2166 // from an enclosing construct was declared in the current construct,
2167 // which would result in wrong privatization code being generated.
2168 // Consider the following example:
2170 // !$omp parallel default(private) ! p1
2171 // !$omp parallel default(private) shared(x) ! p2
2173 // !$omp end parallel
2174 // !$omp end parallel
2176 // If a new x symbol was not inserted in the inner parallel construct
2177 // (p2), it would use the x symbol definition from the enclosing scope.
2178 // Then, when p2's default symbols were collected in lowering, the x
2179 // symbol from the outer parallel construct (p1) would be collected, as
2180 // it would have the private flag set.
2181 // This would make x appear to be defined in p2, causing it to be
2182 // privatized in p2 and its privatization in p1 to be skipped.
2183 auto makePrivateSymbol
= [&](Symbol::Flag flag
) {
2184 const Symbol
*hostSymbol
=
2185 lastDeclSymbol
? lastDeclSymbol
: &symbol
->GetUltimate();
2186 lastDeclSymbol
= DeclareNewPrivateAccessEntity(
2187 *hostSymbol
, flag
, context_
.FindScope(dirContext
.directiveSource
));
2189 lastDeclSymbol
->set(*setFlag
);
2191 return lastDeclSymbol
;
2193 auto makeSharedSymbol
= [&](std::optional
<Symbol::Flag
> flag
= {}) {
2194 const Symbol
*hostSymbol
=
2195 lastDeclSymbol
? lastDeclSymbol
: &symbol
->GetUltimate();
2196 Symbol
&assocSymbol
= MakeAssocSymbol(symbol
->name(), *hostSymbol
,
2197 context_
.FindScope(dirContext
.directiveSource
));
2199 assocSymbol
.set(*flag
);
2202 auto useLastDeclSymbol
= [&]() {
2203 if (lastDeclSymbol
) {
2208 bool taskGenDir
= llvm::omp::taskGeneratingSet
.test(dirContext
.directive
);
2209 bool targetDir
= llvm::omp::allTargetSet
.test(dirContext
.directive
);
2210 bool parallelDir
= llvm::omp::allParallelSet
.test(dirContext
.directive
);
2211 bool teamsDir
= llvm::omp::allTeamsSet
.test(dirContext
.directive
);
2213 if (dsa
.has_value()) {
2214 if (dsa
.value() == Symbol::Flag::OmpShared
&&
2215 (parallelDir
|| taskGenDir
|| teamsDir
)) {
2216 makeSharedSymbol(Symbol::Flag::OmpShared
);
2218 // Private symbols will have been declared already.
2223 if (dirContext
.defaultDSA
== Symbol::Flag::OmpPrivate
||
2224 dirContext
.defaultDSA
== Symbol::Flag::OmpFirstPrivate
||
2225 dirContext
.defaultDSA
== Symbol::Flag::OmpShared
) {
2227 // Allowed only with parallel, teams and task generating constructs.
2228 if (!parallelDir
&& !taskGenDir
&& !teamsDir
) {
2231 if (dirContext
.defaultDSA
!= Symbol::Flag::OmpShared
) {
2232 makePrivateSymbol(dirContext
.defaultDSA
);
2236 dsa
= dirContext
.defaultDSA
;
2237 } else if (parallelDir
) {
2238 // 2) parallel -> shared
2240 dsa
= Symbol::Flag::OmpShared
;
2241 } else if (!taskGenDir
&& !targetDir
) {
2242 // 3) enclosing context
2243 useLastDeclSymbol();
2245 } else if (targetDir
) {
2246 // TODO 4) not mapped target variable -> firstprivate
2248 } else if (taskGenDir
) {
2249 // TODO 5) dummy arg in orphaned taskgen construct -> firstprivate
2250 if (prevDSA
== Symbol::Flag::OmpShared
) {
2251 // 6) shared in enclosing context -> shared
2253 dsa
= Symbol::Flag::OmpShared
;
2256 dsa
= Symbol::Flag::OmpFirstPrivate
;
2257 makePrivateSymbol(*dsa
)->set(Symbol::Flag::OmpImplicit
);
2264 // For OpenMP constructs, check all the data-refs within the constructs
2265 // and adjust the symbol for each Name if necessary
2266 void OmpAttributeVisitor::Post(const parser::Name
&name
) {
2267 auto *symbol
{name
.symbol
};
2269 if (symbol
&& !dirContext_
.empty() && GetContext().withinConstruct
) {
2270 if (IsPrivatizable(symbol
) && !IsObjectWithDSA(*symbol
)) {
2271 // TODO: create a separate function to go through the rules for
2272 // predetermined, explicitly determined, and implicitly
2273 // determined data-sharing attributes (2.15.1.1).
2274 if (Symbol
* found
{currScope().FindSymbol(name
.source
)}) {
2275 if (symbol
!= found
) {
2276 name
.symbol
= found
; // adjust the symbol within region
2277 } else if (GetContext().defaultDSA
== Symbol::Flag::OmpNone
&&
2278 !symbol
->test(Symbol::Flag::OmpThreadprivate
) &&
2279 // Exclude indices of sequential loops that are privatised in
2280 // the scope of the parallel region, and not in this scope.
2281 // TODO: check whether this should be caught in IsObjectWithDSA
2282 !symbol
->test(Symbol::Flag::OmpPrivate
)) {
2283 context_
.Say(name
.source
,
2284 "The DEFAULT(NONE) clause requires that '%s' must be listed in "
2285 "a data-sharing attribute clause"_err_en_US
,
2291 if (Symbol
* found
{currScope().FindSymbol(name
.source
)}) {
2292 if (found
->test(semantics::Symbol::Flag::OmpThreadprivate
))
2296 CreateImplicitSymbols(symbol
);
2297 } // within OpenMP construct
2300 Symbol
*OmpAttributeVisitor::ResolveName(const parser::Name
*name
) {
2301 if (auto *resolvedSymbol
{
2302 name
? GetContext().scope
.FindSymbol(name
->source
) : nullptr}) {
2303 name
->symbol
= resolvedSymbol
;
2304 return resolvedSymbol
;
2310 void OmpAttributeVisitor::ResolveOmpName(
2311 const parser::Name
&name
, Symbol::Flag ompFlag
) {
2312 if (ResolveName(&name
)) {
2313 if (auto *resolvedSymbol
{ResolveOmp(name
, ompFlag
, currScope())}) {
2314 if (dataSharingAttributeFlags
.test(ompFlag
)) {
2315 AddToContextObjectWithDSA(*resolvedSymbol
, ompFlag
);
2318 } else if (ompFlag
== Symbol::Flag::OmpCriticalLock
) {
2320 GetContext().scope
.try_emplace(name
.source
, Attrs
{}, UnknownDetails
{})};
2322 name
.symbol
= &pair
.first
->second
.get();
2326 void OmpAttributeVisitor::ResolveOmpNameList(
2327 const std::list
<parser::Name
> &nameList
, Symbol::Flag ompFlag
) {
2328 for (const auto &name
: nameList
) {
2329 ResolveOmpName(name
, ompFlag
);
2333 Symbol
*OmpAttributeVisitor::ResolveOmpCommonBlockName(
2334 const parser::Name
*name
) {
2338 if (auto *cb
{GetProgramUnitOrBlockConstructContaining(GetContext().scope
)
2339 .FindCommonBlock(name
->source
)}) {
2346 // Use this function over ResolveOmpName when an omp object's scope needs
2347 // resolving, it's symbol flag isn't important and a simple check for resolution
2348 // failure is desired. Using ResolveOmpName means needing to work with the
2349 // context to check for failure, whereas here a pointer comparison is all that's
2351 Symbol
*OmpAttributeVisitor::ResolveOmpObjectScope(const parser::Name
*name
) {
2353 // TODO: Investigate whether the following block can be replaced by, or
2354 // included in, the ResolveOmpName function
2355 if (auto *prev
{name
? GetContext().scope
.parent().FindSymbol(name
->source
)
2357 name
->symbol
= prev
;
2361 // TODO: Investigate whether the following block can be replaced by, or
2362 // included in, the ResolveOmpName function
2363 if (auto *ompSymbol
{
2364 name
? GetContext().scope
.FindSymbol(name
->source
) : nullptr}) {
2365 name
->symbol
= ompSymbol
;
2371 void OmpAttributeVisitor::ResolveOmpObjectList(
2372 const parser::OmpObjectList
&ompObjectList
, Symbol::Flag ompFlag
) {
2373 for (const auto &ompObject
: ompObjectList
.v
) {
2374 ResolveOmpObject(ompObject
, ompFlag
);
2378 void OmpAttributeVisitor::ResolveOmpObject(
2379 const parser::OmpObject
&ompObject
, Symbol::Flag ompFlag
) {
2382 [&](const parser::Designator
&designator
) {
2383 if (const auto *name
{
2384 semantics::getDesignatorNameIfDataRef(designator
)}) {
2385 if (auto *symbol
{ResolveOmp(*name
, ompFlag
, currScope())}) {
2386 auto checkExclusivelists
=
2387 [&](const Symbol
*symbol1
, Symbol::Flag firstOmpFlag
,
2388 const Symbol
*symbol2
, Symbol::Flag secondOmpFlag
) {
2389 if ((symbol1
->test(firstOmpFlag
) &&
2390 symbol2
->test(secondOmpFlag
)) ||
2391 (symbol1
->test(secondOmpFlag
) &&
2392 symbol2
->test(firstOmpFlag
))) {
2393 context_
.Say(designator
.source
,
2394 "Variable '%s' may not "
2395 "appear on both %s and %s "
2396 "clauses on a %s construct"_err_en_US
,
2398 Symbol::OmpFlagToClauseName(firstOmpFlag
),
2399 Symbol::OmpFlagToClauseName(secondOmpFlag
),
2400 parser::ToUpperCaseLetters(
2401 llvm::omp::getOpenMPDirectiveName(
2402 GetContext().directive
)
2406 if (dataCopyingAttributeFlags
.test(ompFlag
)) {
2407 CheckDataCopyingClause(*name
, *symbol
, ompFlag
);
2409 AddToContextObjectWithDSA(*symbol
, ompFlag
);
2410 if (dataSharingAttributeFlags
.test(ompFlag
)) {
2411 CheckMultipleAppearances(*name
, *symbol
, ompFlag
);
2413 if (privateDataSharingAttributeFlags
.test(ompFlag
)) {
2414 CheckObjectIsPrivatizable(*name
, *symbol
, ompFlag
);
2417 if (ompFlag
== Symbol::Flag::OmpAllocate
) {
2418 AddAllocateName(name
);
2421 if (ompFlag
== Symbol::Flag::OmpDeclarativeAllocateDirective
&&
2422 IsAllocatable(*symbol
) &&
2423 !IsNestedInDirective(llvm::omp::Directive::OMPD_allocate
)) {
2424 context_
.Say(designator
.source
,
2425 "List items specified in the ALLOCATE directive must not "
2426 "have the ALLOCATABLE attribute unless the directive is "
2427 "associated with an ALLOCATE statement"_err_en_US
);
2429 if ((ompFlag
== Symbol::Flag::OmpDeclarativeAllocateDirective
||
2431 Symbol::Flag::OmpExecutableAllocateDirective
) &&
2432 ResolveOmpObjectScope(name
) == nullptr) {
2433 context_
.Say(designator
.source
, // 2.15.3
2434 "List items must be declared in the same scoping unit "
2435 "in which the %s directive appears"_err_en_US
,
2436 parser::ToUpperCaseLetters(
2437 llvm::omp::getOpenMPDirectiveName(
2438 GetContext().directive
)
2441 if (ompFlag
== Symbol::Flag::OmpReduction
) {
2442 const Symbol
&ultimateSymbol
{symbol
->GetUltimate()};
2443 // Using variables inside of a namelist in OpenMP reductions
2444 // is allowed by the standard, but is not allowed for
2445 // privatisation. This looks like an oversight. If the
2446 // namelist is hoisted to a global, we cannot apply the
2447 // mapping for the reduction variable: resulting in incorrect
2448 // results. Disabling this hoisting could make some real
2449 // production code go slower. See discussion in #109303
2450 if (ultimateSymbol
.test(Symbol::Flag::InNamelist
)) {
2451 context_
.Say(name
->source
,
2452 "Variable '%s' in NAMELIST cannot be in a REDUCTION clause"_err_en_US
,
2456 if (ompFlag
== Symbol::Flag::OmpInclusiveScan
||
2457 ompFlag
== Symbol::Flag::OmpExclusiveScan
) {
2458 if (!symbol
->test(Symbol::Flag::OmpInScanReduction
)) {
2459 context_
.Say(name
->source
,
2460 "List item %s must appear in REDUCTION clause "
2461 "with the INSCAN modifier of the parent "
2462 "directive"_err_en_US
,
2466 if (GetContext().directive
==
2467 llvm::omp::Directive::OMPD_target_data
) {
2468 checkExclusivelists(symbol
, Symbol::Flag::OmpUseDevicePtr
,
2469 symbol
, Symbol::Flag::OmpUseDeviceAddr
);
2471 if (llvm::omp::allDistributeSet
.test(GetContext().directive
)) {
2472 checkExclusivelists(symbol
, Symbol::Flag::OmpFirstPrivate
,
2473 symbol
, Symbol::Flag::OmpLastPrivate
);
2475 if (llvm::omp::allTargetSet
.test(GetContext().directive
)) {
2476 checkExclusivelists(symbol
, Symbol::Flag::OmpIsDevicePtr
,
2477 symbol
, Symbol::Flag::OmpHasDeviceAddr
);
2478 const auto *hostAssocSym
{symbol
};
2479 if (!(symbol
->test(Symbol::Flag::OmpIsDevicePtr
) ||
2480 symbol
->test(Symbol::Flag::OmpHasDeviceAddr
))) {
2481 if (const auto *details
{
2482 symbol
->detailsIf
<HostAssocDetails
>()}) {
2483 hostAssocSym
= &details
->symbol();
2486 Symbol::Flag dataMappingAttributeFlags
[] = {
2487 Symbol::Flag::OmpMapTo
, Symbol::Flag::OmpMapFrom
,
2488 Symbol::Flag::OmpMapToFrom
, Symbol::Flag::OmpMapAlloc
,
2489 Symbol::Flag::OmpMapRelease
, Symbol::Flag::OmpMapDelete
,
2490 Symbol::Flag::OmpIsDevicePtr
,
2491 Symbol::Flag::OmpHasDeviceAddr
};
2493 Symbol::Flag dataSharingAttributeFlags
[] = {
2494 Symbol::Flag::OmpPrivate
, Symbol::Flag::OmpFirstPrivate
,
2495 Symbol::Flag::OmpLastPrivate
, Symbol::Flag::OmpShared
,
2496 Symbol::Flag::OmpLinear
};
2498 // For OMP TARGET TEAMS directive some sharing attribute
2499 // flags and mapping attribute flags can co-exist.
2500 if (!(llvm::omp::allTeamsSet
.test(GetContext().directive
) ||
2501 llvm::omp::allParallelSet
.test(
2502 GetContext().directive
))) {
2503 for (Symbol::Flag ompFlag1
: dataMappingAttributeFlags
) {
2504 for (Symbol::Flag ompFlag2
: dataSharingAttributeFlags
) {
2505 checkExclusivelists(
2506 hostAssocSym
, ompFlag1
, symbol
, ompFlag2
);
2513 // Array sections to be changed to substrings as needed
2514 if (AnalyzeExpr(context_
, designator
)) {
2515 if (std::holds_alternative
<parser::Substring
>(designator
.u
)) {
2516 context_
.Say(designator
.source
,
2517 "Substrings are not allowed on OpenMP "
2518 "directives or clauses"_err_en_US
);
2521 // other checks, more TBD
2524 [&](const parser::Name
&name
) { // common block
2525 if (auto *symbol
{ResolveOmpCommonBlockName(&name
)}) {
2526 if (!dataCopyingAttributeFlags
.test(ompFlag
)) {
2527 CheckMultipleAppearances(
2528 name
, *symbol
, Symbol::Flag::OmpCommonBlock
);
2530 // 2.15.3 When a named common block appears in a list, it has the
2531 // same meaning as if every explicit member of the common block
2532 // appeared in the list
2533 auto &details
{symbol
->get
<CommonBlockDetails
>()};
2535 for (auto &object
: details
.objects()) {
2536 if (auto *resolvedObject
{
2537 ResolveOmp(*object
, ompFlag
, currScope())}) {
2538 if (dataCopyingAttributeFlags
.test(ompFlag
)) {
2539 CheckDataCopyingClause(name
, *resolvedObject
, ompFlag
);
2541 AddToContextObjectWithDSA(*resolvedObject
, ompFlag
);
2543 details
.replace_object(*resolvedObject
, index
);
2548 context_
.Say(name
.source
, // 2.15.3
2549 "COMMON block must be declared in the same scoping unit "
2550 "in which the OpenMP directive or clause appears"_err_en_US
);
2557 Symbol
*OmpAttributeVisitor::ResolveOmp(
2558 const parser::Name
&name
, Symbol::Flag ompFlag
, Scope
&scope
) {
2559 if (ompFlagsRequireNewSymbol
.test(ompFlag
)) {
2560 return DeclarePrivateAccessEntity(name
, ompFlag
, scope
);
2562 return DeclareOrMarkOtherAccessEntity(name
, ompFlag
);
2566 Symbol
*OmpAttributeVisitor::ResolveOmp(
2567 Symbol
&symbol
, Symbol::Flag ompFlag
, Scope
&scope
) {
2568 if (ompFlagsRequireNewSymbol
.test(ompFlag
)) {
2569 return DeclarePrivateAccessEntity(symbol
, ompFlag
, scope
);
2571 return DeclareOrMarkOtherAccessEntity(symbol
, ompFlag
);
2575 Symbol
*OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
2576 const parser::Name
&name
, Symbol::Flag ompFlag
) {
2577 Symbol
*prev
{currScope().FindSymbol(name
.source
)};
2578 if (!name
.symbol
|| !prev
) {
2580 } else if (prev
!= name
.symbol
) {
2583 return DeclareOrMarkOtherAccessEntity(*prev
, ompFlag
);
2586 Symbol
*OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity(
2587 Symbol
&object
, Symbol::Flag ompFlag
) {
2588 if (ompFlagsRequireMark
.test(ompFlag
)) {
2589 object
.set(ompFlag
);
2594 static bool WithMultipleAppearancesOmpException(
2595 const Symbol
&symbol
, Symbol::Flag flag
) {
2596 return (flag
== Symbol::Flag::OmpFirstPrivate
&&
2597 symbol
.test(Symbol::Flag::OmpLastPrivate
)) ||
2598 (flag
== Symbol::Flag::OmpLastPrivate
&&
2599 symbol
.test(Symbol::Flag::OmpFirstPrivate
));
2602 void OmpAttributeVisitor::CheckMultipleAppearances(
2603 const parser::Name
&name
, const Symbol
&symbol
, Symbol::Flag ompFlag
) {
2604 const auto *target
{&symbol
};
2605 if (ompFlagsRequireNewSymbol
.test(ompFlag
)) {
2606 if (const auto *details
{symbol
.detailsIf
<HostAssocDetails
>()}) {
2607 target
= &details
->symbol();
2610 if (HasDataSharingAttributeObject(target
->GetUltimate()) &&
2611 !WithMultipleAppearancesOmpException(symbol
, ompFlag
)) {
2612 context_
.Say(name
.source
,
2613 "'%s' appears in more than one data-sharing clause "
2614 "on the same OpenMP directive"_err_en_US
,
2617 AddDataSharingAttributeObject(target
->GetUltimate());
2618 if (privateDataSharingAttributeFlags
.test(ompFlag
)) {
2619 AddPrivateDataSharingAttributeObjects(*target
);
2624 void ResolveAccParts(SemanticsContext
&context
, const parser::ProgramUnit
&node
,
2626 if (context
.IsEnabled(common::LanguageFeature::OpenACC
)) {
2627 AccAttributeVisitor
{context
, topScope
}.Walk(node
);
2631 void ResolveOmpParts(
2632 SemanticsContext
&context
, const parser::ProgramUnit
&node
) {
2633 if (context
.IsEnabled(common::LanguageFeature::OpenMP
)) {
2634 OmpAttributeVisitor
{context
}.Walk(node
);
2635 if (!context
.AnyFatalError()) {
2636 // The data-sharing attribute of the loop iteration variable for a
2637 // sequential loop (2.15.1.1) can only be determined when visiting
2638 // the corresponding DoConstruct, a second walk is to adjust the
2639 // symbols for all the data-refs of that loop iteration variable
2640 // prior to the DoConstruct.
2641 OmpAttributeVisitor
{context
}.Walk(node
);
2646 void ResolveOmpTopLevelParts(
2647 SemanticsContext
&context
, const parser::Program
&program
) {
2648 if (!context
.IsEnabled(common::LanguageFeature::OpenMP
)) {
2652 // Gather REQUIRES clauses from all non-module top-level program unit symbols,
2653 // combine them together ensuring compatibility and apply them to all these
2654 // program units. Modules are skipped because their REQUIRES clauses should be
2655 // propagated via USE statements instead.
2656 WithOmpDeclarative::RequiresFlags combinedFlags
;
2657 std::optional
<common::OmpAtomicDefaultMemOrderType
> combinedMemOrder
;
2659 // Function to go through non-module top level program units and extract
2660 // REQUIRES information to be processed by a function-like argument.
2661 auto processProgramUnits
{[&](auto processFn
) {
2662 for (const parser::ProgramUnit
&unit
: program
.v
) {
2663 if (!std::holds_alternative
<common::Indirection
<parser::Module
>>(
2665 !std::holds_alternative
<common::Indirection
<parser::Submodule
>>(
2667 !std::holds_alternative
<
2668 common::Indirection
<parser::CompilerDirective
>>(unit
.u
)) {
2669 Symbol
*symbol
{common::visit(
2670 [&context
](auto &x
) {
2671 Scope
*scope
= GetScope(context
, x
.value());
2672 return scope
? scope
->symbol() : nullptr;
2675 // FIXME There is no symbol defined for MainProgram units in certain
2676 // circumstances, so REQUIRES information has no place to be stored in
2682 [&](auto &details
) {
2683 if constexpr (std::is_convertible_v
<decltype(&details
),
2684 WithOmpDeclarative
*>) {
2685 processFn(*symbol
, details
);
2693 // Combine global REQUIRES information from all program units except modules
2695 processProgramUnits([&](Symbol
&symbol
, WithOmpDeclarative
&details
) {
2696 if (const WithOmpDeclarative::RequiresFlags
*
2697 flags
{details
.ompRequires()}) {
2698 combinedFlags
|= *flags
;
2700 if (const common::OmpAtomicDefaultMemOrderType
*
2701 memOrder
{details
.ompAtomicDefaultMemOrder()}) {
2702 if (combinedMemOrder
&& *combinedMemOrder
!= *memOrder
) {
2703 context
.Say(symbol
.scope()->sourceRange(),
2704 "Conflicting '%s' REQUIRES clauses found in compilation "
2706 parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(
2707 llvm::omp::Clause::OMPC_atomic_default_mem_order
)
2710 combinedMemOrder
= *memOrder
;
2714 // Update all program units except modules and submodules with the combined
2715 // global REQUIRES information.
2716 processProgramUnits([&](Symbol
&, WithOmpDeclarative
&details
) {
2717 if (combinedFlags
.any()) {
2718 details
.set_ompRequires(combinedFlags
);
2720 if (combinedMemOrder
) {
2721 details
.set_ompAtomicDefaultMemOrder(*combinedMemOrder
);
2726 static bool IsSymbolInCommonBlock(const Symbol
&symbol
) {
2727 // TODO Improve the performance of this predicate function.
2728 // Going through all symbols sequentially, in all common blocks, can be
2729 // slow when there are many symbols. A possible optimization is to add
2730 // an OmpInCommonBlock flag to Symbol, to make it possible to quickly
2731 // test if a given symbol is in a common block.
2732 for (const auto &cb
: symbol
.owner().commonBlocks()) {
2733 if (IsCommonBlockContaining(cb
.second
.get(), symbol
)) {
2740 static bool IsSymbolThreadprivate(const Symbol
&symbol
) {
2741 if (const auto *details
{symbol
.detailsIf
<HostAssocDetails
>()}) {
2742 return details
->symbol().test(Symbol::Flag::OmpThreadprivate
);
2744 return symbol
.test(Symbol::Flag::OmpThreadprivate
);
2747 static bool IsSymbolPrivate(const Symbol
&symbol
) {
2748 if (symbol
.test(Symbol::Flag::OmpPrivate
) ||
2749 symbol
.test(Symbol::Flag::OmpFirstPrivate
)) {
2752 // A symbol that has not gone through constructs that may privatize the
2753 // original symbol may be predetermined as private.
2754 // (OMP 5.2 5.1.1 - Variables Referenced in a Construct)
2755 if (symbol
== symbol
.GetUltimate()) {
2756 switch (symbol
.owner().kind()) {
2757 case Scope::Kind::MainProgram
:
2758 case Scope::Kind::Subprogram
:
2759 case Scope::Kind::BlockConstruct
:
2760 return !symbol
.attrs().test(Attr::SAVE
) &&
2761 !symbol
.attrs().test(Attr::PARAMETER
) && !IsAssumedShape(symbol
) &&
2762 !IsSymbolInCommonBlock(symbol
);
2770 void OmpAttributeVisitor::CheckDataCopyingClause(
2771 const parser::Name
&name
, const Symbol
&symbol
, Symbol::Flag ompFlag
) {
2772 if (ompFlag
== Symbol::Flag::OmpCopyIn
) {
2773 // List of items/objects that can appear in a 'copyin' clause must be
2775 if (!IsSymbolThreadprivate(symbol
)) {
2776 context_
.Say(name
.source
,
2777 "Non-THREADPRIVATE object '%s' in COPYIN clause"_err_en_US
,
2780 } else if (ompFlag
== Symbol::Flag::OmpCopyPrivate
&&
2781 GetContext().directive
== llvm::omp::Directive::OMPD_single
) {
2782 // A list item that appears in a 'copyprivate' clause may not appear on a
2783 // 'private' or 'firstprivate' clause on a single construct
2784 if (IsObjectWithDSA(symbol
) &&
2785 (symbol
.test(Symbol::Flag::OmpPrivate
) ||
2786 symbol
.test(Symbol::Flag::OmpFirstPrivate
))) {
2787 context_
.Say(name
.source
,
2788 "COPYPRIVATE variable '%s' may not appear on a PRIVATE or "
2789 "FIRSTPRIVATE clause on a SINGLE construct"_err_en_US
,
2791 } else if (!IsSymbolThreadprivate(symbol
) && !IsSymbolPrivate(symbol
)) {
2792 // List of items/objects that can appear in a 'copyprivate' clause must be
2793 // either 'private' or 'threadprivate' in enclosing context.
2794 context_
.Say(name
.source
,
2795 "COPYPRIVATE variable '%s' is not PRIVATE or THREADPRIVATE in "
2796 "outer context"_err_en_US
,
2802 void OmpAttributeVisitor::CheckObjectIsPrivatizable(
2803 const parser::Name
&name
, const Symbol
&symbol
, Symbol::Flag ompFlag
) {
2804 const auto &ultimateSymbol
{symbol
.GetUltimate()};
2805 llvm::StringRef clauseName
{"PRIVATE"};
2806 if (ompFlag
== Symbol::Flag::OmpFirstPrivate
) {
2807 clauseName
= "FIRSTPRIVATE";
2808 } else if (ompFlag
== Symbol::Flag::OmpLastPrivate
) {
2809 clauseName
= "LASTPRIVATE";
2812 if (ultimateSymbol
.test(Symbol::Flag::InNamelist
)) {
2813 context_
.Say(name
.source
,
2814 "Variable '%s' in NAMELIST cannot be in a %s clause"_err_en_US
,
2815 name
.ToString(), clauseName
.str());
2818 if (ultimateSymbol
.has
<AssocEntityDetails
>()) {
2819 context_
.Say(name
.source
,
2820 "Variable '%s' in ASSOCIATE cannot be in a %s clause"_err_en_US
,
2821 name
.ToString(), clauseName
.str());
2824 if (stmtFunctionExprSymbols_
.find(ultimateSymbol
) !=
2825 stmtFunctionExprSymbols_
.end()) {
2826 context_
.Say(name
.source
,
2827 "Variable '%s' in statement function expression cannot be in a "
2828 "%s clause"_err_en_US
,
2829 name
.ToString(), clauseName
.str());
2833 void OmpAttributeVisitor::CheckSourceLabel(const parser::Label
&label
) {
2834 // Get the context to check if the statement causing a jump to the 'label' is
2835 // in an enclosing OpenMP construct
2836 std::optional
<DirContext
> thisContext
{GetContextIf()};
2837 sourceLabels_
.emplace(
2838 label
, std::make_pair(currentStatementSource_
, thisContext
));
2839 // Check if the statement with 'label' to which a jump is being introduced
2840 // has already been encountered
2841 auto it
{targetLabels_
.find(label
)};
2842 if (it
!= targetLabels_
.end()) {
2843 // Check if both the statement with 'label' and the statement that causes a
2844 // jump to the 'label' are in the same scope
2845 CheckLabelContext(currentStatementSource_
, it
->second
.first
, thisContext
,
2850 // Check for invalid branch into or out of OpenMP structured blocks
2851 void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source
,
2852 const parser::CharBlock target
, std::optional
<DirContext
> sourceContext
,
2853 std::optional
<DirContext
> targetContext
) {
2854 if (targetContext
&&
2856 (sourceContext
->scope
!= targetContext
->scope
&&
2858 &targetContext
->scope
, sourceContext
->scope
)))) {
2860 .Say(source
, "invalid branch into an OpenMP structured block"_err_en_US
)
2861 .Attach(target
, "In the enclosing %s directive branched into"_en_US
,
2862 parser::ToUpperCaseLetters(
2863 llvm::omp::getOpenMPDirectiveName(targetContext
->directive
)
2866 if (sourceContext
&&
2868 (sourceContext
->scope
!= targetContext
->scope
&&
2870 &sourceContext
->scope
, targetContext
->scope
)))) {
2873 "invalid branch leaving an OpenMP structured block"_err_en_US
)
2874 .Attach(target
, "Outside the enclosing %s directive"_en_US
,
2875 parser::ToUpperCaseLetters(
2876 llvm::omp::getOpenMPDirectiveName(sourceContext
->directive
)
2881 // Goes through the names in an OmpObjectList and checks if each name appears
2882 // in the given allocate statement
2883 void OmpAttributeVisitor::CheckAllNamesInAllocateStmt(
2884 const parser::CharBlock
&source
, const parser::OmpObjectList
&ompObjectList
,
2885 const parser::AllocateStmt
&allocate
) {
2886 for (const auto &obj
: ompObjectList
.v
) {
2887 if (const auto *d
{std::get_if
<parser::Designator
>(&obj
.u
)}) {
2888 if (const auto *ref
{std::get_if
<parser::DataRef
>(&d
->u
)}) {
2889 if (const auto *n
{std::get_if
<parser::Name
>(&ref
->u
)}) {
2890 CheckNameInAllocateStmt(source
, *n
, allocate
);
2897 void OmpAttributeVisitor::CheckNameInAllocateStmt(
2898 const parser::CharBlock
&source
, const parser::Name
&name
,
2899 const parser::AllocateStmt
&allocate
) {
2900 for (const auto &allocation
:
2901 std::get
<std::list
<parser::Allocation
>>(allocate
.t
)) {
2902 const auto &allocObj
= std::get
<parser::AllocateObject
>(allocation
.t
);
2903 if (const auto *n
{std::get_if
<parser::Name
>(&allocObj
.u
)}) {
2904 if (n
->source
== name
.source
) {
2909 context_
.Say(source
,
2910 "Object '%s' in %s directive not "
2911 "found in corresponding ALLOCATE statement"_err_en_US
,
2913 parser::ToUpperCaseLetters(
2914 llvm::omp::getOpenMPDirectiveName(GetContext().directive
).str()));
2917 void OmpAttributeVisitor::AddOmpRequiresToScope(Scope
&scope
,
2918 WithOmpDeclarative::RequiresFlags flags
,
2919 std::optional
<common::OmpAtomicDefaultMemOrderType
> memOrder
) {
2920 Scope
*scopeIter
= &scope
;
2922 if (Symbol
* symbol
{scopeIter
->symbol()}) {
2924 [&](auto &details
) {
2925 // Store clauses information into the symbol for the parent and
2926 // enclosing modules, programs, functions and subroutines.
2927 if constexpr (std::is_convertible_v
<decltype(&details
),
2928 WithOmpDeclarative
*>) {
2930 if (const WithOmpDeclarative::RequiresFlags
*
2931 otherFlags
{details
.ompRequires()}) {
2932 flags
|= *otherFlags
;
2934 details
.set_ompRequires(flags
);
2937 if (details
.has_ompAtomicDefaultMemOrder() &&
2938 *details
.ompAtomicDefaultMemOrder() != *memOrder
) {
2939 context_
.Say(scopeIter
->sourceRange(),
2940 "Conflicting '%s' REQUIRES clauses found in compilation "
2942 parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(
2943 llvm::omp::Clause::OMPC_atomic_default_mem_order
)
2946 details
.set_ompAtomicDefaultMemOrder(*memOrder
);
2952 scopeIter
= &scopeIter
->parent();
2953 } while (!scopeIter
->IsGlobal());
2956 void OmpAttributeVisitor::IssueNonConformanceWarning(
2957 llvm::omp::Directive D
, parser::CharBlock source
) {
2958 std::string warnStr
;
2959 llvm::raw_string_ostream
warnStrOS(warnStr
);
2960 warnStrOS
<< "OpenMP directive "
2961 << parser::ToUpperCaseLetters(
2962 llvm::omp::getOpenMPDirectiveName(D
).str())
2963 << " has been deprecated";
2965 auto setAlternativeStr
= [&warnStrOS
](llvm::StringRef alt
) {
2966 warnStrOS
<< ", please use " << alt
<< " instead.";
2969 case llvm::omp::OMPD_master
:
2970 setAlternativeStr("MASKED");
2972 case llvm::omp::OMPD_master_taskloop
:
2973 setAlternativeStr("MASKED TASKLOOP");
2975 case llvm::omp::OMPD_master_taskloop_simd
:
2976 setAlternativeStr("MASKED TASKLOOP SIMD");
2978 case llvm::omp::OMPD_parallel_master
:
2979 setAlternativeStr("PARALLEL MASKED");
2981 case llvm::omp::OMPD_parallel_master_taskloop
:
2982 setAlternativeStr("PARALLEL MASKED TASKLOOP");
2984 case llvm::omp::OMPD_parallel_master_taskloop_simd
:
2985 setAlternativeStr("PARALLEL_MASKED TASKLOOP SIMD");
2987 case llvm::omp::OMPD_target_loop
:
2990 context_
.Warn(common::UsageWarning::OpenMPUsage
, source
, "%s"_warn_en_US
,
2993 } // namespace Fortran::semantics