| //===----------------------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "resolve-directives.h" |
| |
| #include "check-acc-structure.h" |
| #include "check-omp-structure.h" |
| #include "resolve-names-utils.h" |
| #include "flang/Common/idioms.h" |
| #include "flang/Evaluate/fold.h" |
| #include "flang/Evaluate/type.h" |
| #include "flang/Parser/parse-tree-visitor.h" |
| #include "flang/Parser/parse-tree.h" |
| #include "flang/Parser/tools.h" |
| #include "flang/Semantics/expression.h" |
| #include <list> |
| #include <map> |
| |
| namespace Fortran::semantics { |
| |
| template <typename T> class DirectiveAttributeVisitor { |
| public: |
| explicit DirectiveAttributeVisitor(SemanticsContext &context) |
| : context_{context} {} |
| |
| template <typename A> bool Pre(const A &) { return true; } |
| template <typename A> void Post(const A &) {} |
| |
| protected: |
| struct DirContext { |
| DirContext(const parser::CharBlock &source, T d, Scope &s) |
| : directiveSource{source}, directive{d}, scope{s} {} |
| parser::CharBlock directiveSource; |
| T directive; |
| Scope &scope; |
| Symbol::Flag defaultDSA{Symbol::Flag::AccShared}; // TODOACC |
| std::map<const Symbol *, Symbol::Flag> objectWithDSA; |
| bool withinConstruct{false}; |
| std::int64_t associatedLoopLevel{0}; |
| }; |
| |
| DirContext &GetContext() { |
| CHECK(!dirContext_.empty()); |
| return dirContext_.back(); |
| } |
| std::optional<DirContext> GetContextIf() { |
| return dirContext_.empty() |
| ? std::nullopt |
| : std::make_optional<DirContext>(dirContext_.back()); |
| } |
| void PushContext(const parser::CharBlock &source, T dir) { |
| dirContext_.emplace_back(source, dir, context_.FindScope(source)); |
| } |
| void PopContext() { dirContext_.pop_back(); } |
| void SetContextDirectiveSource(parser::CharBlock &dir) { |
| GetContext().directiveSource = dir; |
| } |
| Scope &currScope() { return GetContext().scope; } |
| void SetContextDefaultDSA(Symbol::Flag flag) { |
| GetContext().defaultDSA = flag; |
| } |
| void AddToContextObjectWithDSA( |
| const Symbol &symbol, Symbol::Flag flag, DirContext &context) { |
| context.objectWithDSA.emplace(&symbol, flag); |
| } |
| void AddToContextObjectWithDSA(const Symbol &symbol, Symbol::Flag flag) { |
| AddToContextObjectWithDSA(symbol, flag, GetContext()); |
| } |
| bool IsObjectWithDSA(const Symbol &symbol) { |
| auto it{GetContext().objectWithDSA.find(&symbol)}; |
| return it != GetContext().objectWithDSA.end(); |
| } |
| void SetContextAssociatedLoopLevel(std::int64_t level) { |
| GetContext().associatedLoopLevel = level; |
| } |
| Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev, Scope &scope) { |
| const auto pair{scope.try_emplace(name, Attrs{}, HostAssocDetails{prev})}; |
| return *pair.first->second; |
| } |
| Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev) { |
| return MakeAssocSymbol(name, prev, currScope()); |
| } |
| static const parser::Name *GetDesignatorNameIfDataRef( |
| const parser::Designator &designator) { |
| const auto *dataRef{std::get_if<parser::DataRef>(&designator.u)}; |
| return dataRef ? std::get_if<parser::Name>(&dataRef->u) : nullptr; |
| } |
| void AddDataSharingAttributeObject(SymbolRef object) { |
| dataSharingAttributeObjects_.insert(object); |
| } |
| void ClearDataSharingAttributeObjects() { |
| dataSharingAttributeObjects_.clear(); |
| } |
| bool HasDataSharingAttributeObject(const Symbol &); |
| const parser::Name &GetLoopIndex(const parser::DoConstruct &); |
| const parser::DoConstruct *GetDoConstructIf( |
| const parser::ExecutionPartConstruct &); |
| Symbol *DeclarePrivateAccessEntity( |
| const parser::Name &, Symbol::Flag, Scope &); |
| Symbol *DeclarePrivateAccessEntity(Symbol &, Symbol::Flag, Scope &); |
| Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); |
| |
| UnorderedSymbolSet dataSharingAttributeObjects_; // on one directive |
| SemanticsContext &context_; |
| std::vector<DirContext> dirContext_; // used as a stack |
| }; |
| |
| class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> { |
| public: |
| explicit AccAttributeVisitor(SemanticsContext &context) |
| : DirectiveAttributeVisitor(context) {} |
| |
| template <typename A> void Walk(const A &x) { parser::Walk(x, *this); } |
| template <typename A> bool Pre(const A &) { return true; } |
| template <typename A> void Post(const A &) {} |
| |
| bool Pre(const parser::OpenACCBlockConstruct &); |
| void Post(const parser::OpenACCBlockConstruct &) { PopContext(); } |
| bool Pre(const parser::OpenACCCombinedConstruct &); |
| void Post(const parser::OpenACCCombinedConstruct &) { PopContext(); } |
| |
| bool Pre(const parser::OpenACCDeclarativeConstruct &); |
| void Post(const parser::OpenACCDeclarativeConstruct &) { PopContext(); } |
| |
| bool Pre(const parser::OpenACCRoutineConstruct &); |
| bool Pre(const parser::AccBindClause &); |
| void Post(const parser::OpenACCStandaloneDeclarativeConstruct &); |
| |
| void Post(const parser::AccBeginBlockDirective &) { |
| GetContext().withinConstruct = true; |
| } |
| |
| bool Pre(const parser::OpenACCLoopConstruct &); |
| void Post(const parser::OpenACCLoopConstruct &) { PopContext(); } |
| void Post(const parser::AccLoopDirective &) { |
| GetContext().withinConstruct = true; |
| } |
| |
| bool Pre(const parser::OpenACCStandaloneConstruct &); |
| void Post(const parser::OpenACCStandaloneConstruct &) { PopContext(); } |
| void Post(const parser::AccStandaloneDirective &) { |
| GetContext().withinConstruct = true; |
| } |
| |
| bool Pre(const parser::OpenACCCacheConstruct &); |
| void Post(const parser::OpenACCCacheConstruct &) { PopContext(); } |
| |
| void Post(const parser::AccDefaultClause &); |
| |
| bool Pre(const parser::AccClause::Attach &); |
| bool Pre(const parser::AccClause::Detach &); |
| |
| bool Pre(const parser::AccClause::Copy &x) { |
| ResolveAccObjectList(x.v, Symbol::Flag::AccCopyIn); |
| ResolveAccObjectList(x.v, Symbol::Flag::AccCopyOut); |
| return false; |
| } |
| |
| bool Pre(const parser::AccClause::Create &x) { |
| const auto &objectList{std::get<parser::AccObjectList>(x.v.t)}; |
| ResolveAccObjectList(objectList, Symbol::Flag::AccCreate); |
| return false; |
| } |
| |
| bool Pre(const parser::AccClause::Copyin &x) { |
| const auto &objectList{std::get<parser::AccObjectList>(x.v.t)}; |
| ResolveAccObjectList(objectList, Symbol::Flag::AccCopyIn); |
| return false; |
| } |
| |
| bool Pre(const parser::AccClause::Copyout &x) { |
| const auto &objectList{std::get<parser::AccObjectList>(x.v.t)}; |
| ResolveAccObjectList(objectList, Symbol::Flag::AccCopyOut); |
| return false; |
| } |
| |
| bool Pre(const parser::AccClause::Present &x) { |
| ResolveAccObjectList(x.v, Symbol::Flag::AccPresent); |
| return false; |
| } |
| bool Pre(const parser::AccClause::Private &x) { |
| ResolveAccObjectList(x.v, Symbol::Flag::AccPrivate); |
| return false; |
| } |
| bool Pre(const parser::AccClause::Firstprivate &x) { |
| ResolveAccObjectList(x.v, Symbol::Flag::AccFirstPrivate); |
| return false; |
| } |
| |
| void Post(const parser::Name &); |
| |
| private: |
| std::int64_t GetAssociatedLoopLevelFromClauses(const parser::AccClauseList &); |
| |
| static constexpr Symbol::Flags dataSharingAttributeFlags{ |
| Symbol::Flag::AccShared, Symbol::Flag::AccPrivate, |
| Symbol::Flag::AccPresent, Symbol::Flag::AccFirstPrivate, |
| Symbol::Flag::AccReduction}; |
| |
| static constexpr Symbol::Flags dataMappingAttributeFlags{ |
| Symbol::Flag::AccCreate, Symbol::Flag::AccCopyIn, |
| Symbol::Flag::AccCopyOut, Symbol::Flag::AccDelete}; |
| |
| static constexpr Symbol::Flags accFlagsRequireNewSymbol{ |
| Symbol::Flag::AccPrivate, Symbol::Flag::AccFirstPrivate, |
| Symbol::Flag::AccReduction}; |
| |
| static constexpr Symbol::Flags accFlagsRequireMark{}; |
| |
| void PrivatizeAssociatedLoopIndex(const parser::OpenACCLoopConstruct &); |
| void ResolveAccObjectList(const parser::AccObjectList &, Symbol::Flag); |
| void ResolveAccObject(const parser::AccObject &, Symbol::Flag); |
| Symbol *ResolveAcc(const parser::Name &, Symbol::Flag, Scope &); |
| Symbol *ResolveAcc(Symbol &, Symbol::Flag, Scope &); |
| Symbol *ResolveName(const parser::Name &); |
| Symbol *ResolveAccCommonBlockName(const parser::Name *); |
| Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); |
| Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag); |
| void CheckMultipleAppearances( |
| const parser::Name &, const Symbol &, Symbol::Flag); |
| void AllowOnlyArrayAndSubArray(const parser::AccObjectList &objectList); |
| void DoNotAllowAssumedSizedArray(const parser::AccObjectList &objectList); |
| void EnsureAllocatableOrPointer( |
| const llvm::acc::Clause clause, const parser::AccObjectList &objectList); |
| }; |
| |
| // Data-sharing and Data-mapping attributes for data-refs in OpenMP construct |
| class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> { |
| public: |
| explicit OmpAttributeVisitor(SemanticsContext &context) |
| : DirectiveAttributeVisitor(context) {} |
| |
| template <typename A> void Walk(const A &x) { parser::Walk(x, *this); } |
| template <typename A> bool Pre(const A &) { return true; } |
| template <typename A> void Post(const A &) {} |
| |
| template <typename A> bool Pre(const parser::Statement<A> &statement) { |
| currentStatementSource_ = statement.source; |
| // Keep track of the labels in all the labelled statements |
| if (statement.label) { |
| auto label{statement.label.value()}; |
| // Get the context to check if the labelled statement is in an |
| // enclosing OpenMP construct |
| std::optional<DirContext> thisContext{GetContextIf()}; |
| targetLabels_.emplace( |
| label, std::make_pair(currentStatementSource_, thisContext)); |
| // Check if a statement that causes a jump to the 'label' |
| // has already been encountered |
| auto range{sourceLabels_.equal_range(label)}; |
| for (auto it{range.first}; it != range.second; ++it) { |
| // Check if both the statement with 'label' and the statement that |
| // causes a jump to the 'label' are in the same scope |
| CheckLabelContext(it->second.first, currentStatementSource_, |
| it->second.second, thisContext); |
| } |
| } |
| return true; |
| } |
| |
| bool Pre(const parser::InternalSubprogram &) { |
| // Clear the labels being tracked in the previous scope |
| ClearLabels(); |
| return true; |
| } |
| |
| bool Pre(const parser::ModuleSubprogram &) { |
| // Clear the labels being tracked in the previous scope |
| ClearLabels(); |
| return true; |
| } |
| |
| bool Pre(const parser::SpecificationPart &x) { |
| Walk(std::get<std::list<parser::OpenMPDeclarativeConstruct>>(x.t)); |
| return true; |
| } |
| |
| bool Pre(const parser::StmtFunctionStmt &x) { |
| const auto &parsedExpr{std::get<parser::Scalar<parser::Expr>>(x.t)}; |
| if (const auto *expr{GetExpr(parsedExpr)}) { |
| for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) { |
| if (!IsStmtFunctionDummy(symbol)) { |
| stmtFunctionExprSymbols_.insert(symbol.GetUltimate()); |
| } |
| } |
| } |
| return true; |
| } |
| |
| bool Pre(const parser::OpenMPBlockConstruct &); |
| void Post(const parser::OpenMPBlockConstruct &); |
| bool Pre(const parser::OmpCriticalDirective &x); |
| bool Pre(const parser::OmpEndCriticalDirective &x); |
| |
| void Post(const parser::OmpBeginBlockDirective &) { |
| GetContext().withinConstruct = true; |
| } |
| |
| bool Pre(const parser::OpenMPSimpleStandaloneConstruct &); |
| void Post(const parser::OpenMPSimpleStandaloneConstruct &) { PopContext(); } |
| |
| bool Pre(const parser::OpenMPLoopConstruct &); |
| void Post(const parser::OpenMPLoopConstruct &) { PopContext(); } |
| void Post(const parser::OmpBeginLoopDirective &) { |
| GetContext().withinConstruct = true; |
| } |
| bool Pre(const parser::DoConstruct &); |
| |
| bool Pre(const parser::OpenMPSectionsConstruct &); |
| void Post(const parser::OpenMPSectionsConstruct &) { PopContext(); } |
| |
| bool Pre(const parser::OpenMPCriticalConstruct &); |
| void Post(const parser::OpenMPCriticalConstruct &) { PopContext(); } |
| |
| bool Pre(const parser::OpenMPDeclareSimdConstruct &x) { |
| PushContext(x.source, llvm::omp::Directive::OMPD_declare_simd); |
| const auto &name{std::get<std::optional<parser::Name>>(x.t)}; |
| if (name) { |
| ResolveOmpName(*name, Symbol::Flag::OmpDeclareSimd); |
| } |
| return true; |
| } |
| void Post(const parser::OpenMPDeclareSimdConstruct &) { PopContext(); } |
| bool Pre(const parser::OpenMPThreadprivate &); |
| void Post(const parser::OpenMPThreadprivate &) { PopContext(); } |
| |
| bool Pre(const parser::OpenMPDeclarativeAllocate &); |
| void Post(const parser::OpenMPDeclarativeAllocate &) { PopContext(); } |
| |
| bool Pre(const parser::OpenMPExecutableAllocate &); |
| void Post(const parser::OpenMPExecutableAllocate &); |
| |
| // 2.15.3 Data-Sharing Attribute Clauses |
| void Post(const parser::OmpDefaultClause &); |
| bool Pre(const parser::OmpClause::Shared &x) { |
| ResolveOmpObjectList(x.v, Symbol::Flag::OmpShared); |
| return false; |
| } |
| bool Pre(const parser::OmpClause::Private &x) { |
| ResolveOmpObjectList(x.v, Symbol::Flag::OmpPrivate); |
| return false; |
| } |
| bool Pre(const parser::OmpAllocateClause &x) { |
| const auto &objectList{std::get<parser::OmpObjectList>(x.t)}; |
| ResolveOmpObjectList(objectList, Symbol::Flag::OmpAllocate); |
| return false; |
| } |
| bool Pre(const parser::OmpClause::Firstprivate &x) { |
| ResolveOmpObjectList(x.v, Symbol::Flag::OmpFirstPrivate); |
| return false; |
| } |
| bool Pre(const parser::OmpClause::Lastprivate &x) { |
| ResolveOmpObjectList(x.v, Symbol::Flag::OmpLastPrivate); |
| return false; |
| } |
| bool Pre(const parser::OmpClause::Copyin &x) { |
| ResolveOmpObjectList(x.v, Symbol::Flag::OmpCopyIn); |
| return false; |
| } |
| bool Pre(const parser::OmpClause::Copyprivate &x) { |
| ResolveOmpObjectList(x.v, Symbol::Flag::OmpCopyPrivate); |
| return false; |
| } |
| bool Pre(const parser::OmpLinearClause &x) { |
| std::visit(common::visitors{ |
| [&](const parser::OmpLinearClause::WithoutModifier |
| &linearWithoutModifier) { |
| ResolveOmpNameList( |
| linearWithoutModifier.names, Symbol::Flag::OmpLinear); |
| }, |
| [&](const parser::OmpLinearClause::WithModifier |
| &linearWithModifier) { |
| ResolveOmpNameList( |
| linearWithModifier.names, Symbol::Flag::OmpLinear); |
| }, |
| }, |
| x.u); |
| return false; |
| } |
| |
| bool Pre(const parser::OmpClause::Reduction &x) { |
| const parser::OmpReductionOperator &opr{ |
| std::get<parser::OmpReductionOperator>(x.v.t)}; |
| if (const auto *procD{parser::Unwrap<parser::ProcedureDesignator>(opr.u)}) { |
| if (const auto *name{parser::Unwrap<parser::Name>(procD->u)}) { |
| if (!name->symbol) { |
| const auto namePair{currScope().try_emplace( |
| name->source, Attrs{}, ProcEntityDetails{})}; |
| auto &symbol{*namePair.first->second}; |
| name->symbol = &symbol; |
| name->symbol->set(Symbol::Flag::OmpReduction); |
| AddToContextObjectWithDSA(*name->symbol, Symbol::Flag::OmpReduction); |
| } |
| } |
| if (const auto *procRef{ |
| parser::Unwrap<parser::ProcComponentRef>(procD->u)}) { |
| ResolveOmp(*procRef->v.thing.component.symbol, |
| Symbol::Flag::OmpReduction, currScope()); |
| } |
| } |
| const auto &objList{std::get<parser::OmpObjectList>(x.v.t)}; |
| ResolveOmpObjectList(objList, Symbol::Flag::OmpReduction); |
| return false; |
| } |
| |
| bool Pre(const parser::OmpAlignedClause &x) { |
| const auto &alignedNameList{std::get<std::list<parser::Name>>(x.t)}; |
| ResolveOmpNameList(alignedNameList, Symbol::Flag::OmpAligned); |
| return false; |
| } |
| |
| bool Pre(const parser::OmpClause::Nontemporal &x) { |
| const auto &nontemporalNameList{x.v}; |
| ResolveOmpNameList(nontemporalNameList, Symbol::Flag::OmpNontemporal); |
| return false; |
| } |
| |
| bool Pre(const parser::OmpDependClause &x) { |
| if (const auto *dependSink{ |
| std::get_if<parser::OmpDependClause::Sink>(&x.u)}) { |
| const auto &dependSinkVec{dependSink->v}; |
| for (const auto &dependSinkElement : dependSinkVec) { |
| const auto &name{std::get<parser::Name>(dependSinkElement.t)}; |
| ResolveName(&name); |
| } |
| } |
| return false; |
| } |
| |
| void Post(const parser::Name &); |
| |
| // Keep track of labels in the statements that causes jumps to target labels |
| void Post(const parser::GotoStmt &gotoStmt) { CheckSourceLabel(gotoStmt.v); } |
| void Post(const parser::ComputedGotoStmt &computedGotoStmt) { |
| for (auto &label : std::get<std::list<parser::Label>>(computedGotoStmt.t)) { |
| CheckSourceLabel(label); |
| } |
| } |
| void Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) { |
| CheckSourceLabel(std::get<1>(arithmeticIfStmt.t)); |
| CheckSourceLabel(std::get<2>(arithmeticIfStmt.t)); |
| CheckSourceLabel(std::get<3>(arithmeticIfStmt.t)); |
| } |
| void Post(const parser::AssignedGotoStmt &assignedGotoStmt) { |
| for (auto &label : std::get<std::list<parser::Label>>(assignedGotoStmt.t)) { |
| CheckSourceLabel(label); |
| } |
| } |
| void Post(const parser::AltReturnSpec &altReturnSpec) { |
| CheckSourceLabel(altReturnSpec.v); |
| } |
| void Post(const parser::ErrLabel &errLabel) { CheckSourceLabel(errLabel.v); } |
| void Post(const parser::EndLabel &endLabel) { CheckSourceLabel(endLabel.v); } |
| void Post(const parser::EorLabel &eorLabel) { CheckSourceLabel(eorLabel.v); } |
| |
| const parser::OmpClause *associatedClause{nullptr}; |
| void SetAssociatedClause(const parser::OmpClause &c) { |
| associatedClause = &c; |
| } |
| const parser::OmpClause *GetAssociatedClause() { return associatedClause; } |
| |
| private: |
| std::int64_t GetAssociatedLoopLevelFromClauses(const parser::OmpClauseList &); |
| |
| static constexpr Symbol::Flags dataSharingAttributeFlags{ |
| Symbol::Flag::OmpShared, Symbol::Flag::OmpPrivate, |
| Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate, |
| Symbol::Flag::OmpReduction, Symbol::Flag::OmpLinear}; |
| |
| static constexpr Symbol::Flags privateDataSharingAttributeFlags{ |
| Symbol::Flag::OmpPrivate, Symbol::Flag::OmpFirstPrivate, |
| Symbol::Flag::OmpLastPrivate}; |
| |
| static constexpr Symbol::Flags ompFlagsRequireNewSymbol{ |
| Symbol::Flag::OmpPrivate, Symbol::Flag::OmpLinear, |
| Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate, |
| Symbol::Flag::OmpReduction, Symbol::Flag::OmpCriticalLock}; |
| |
| static constexpr Symbol::Flags ompFlagsRequireMark{ |
| Symbol::Flag::OmpThreadprivate}; |
| |
| static constexpr Symbol::Flags dataCopyingAttributeFlags{ |
| Symbol::Flag::OmpCopyIn, Symbol::Flag::OmpCopyPrivate}; |
| |
| std::vector<const parser::Name *> allocateNames_; // on one directive |
| UnorderedSymbolSet privateDataSharingAttributeObjects_; // on one directive |
| UnorderedSymbolSet stmtFunctionExprSymbols_; |
| std::multimap<const parser::Label, |
| std::pair<parser::CharBlock, std::optional<DirContext>>> |
| sourceLabels_; |
| std::map<const parser::Label, |
| std::pair<parser::CharBlock, std::optional<DirContext>>> |
| targetLabels_; |
| parser::CharBlock currentStatementSource_; |
| |
| void AddAllocateName(const parser::Name *&object) { |
| allocateNames_.push_back(object); |
| } |
| void ClearAllocateNames() { allocateNames_.clear(); } |
| |
| void AddPrivateDataSharingAttributeObjects(SymbolRef object) { |
| privateDataSharingAttributeObjects_.insert(object); |
| } |
| void ClearPrivateDataSharingAttributeObjects() { |
| privateDataSharingAttributeObjects_.clear(); |
| } |
| |
| // Predetermined DSA rules |
| void PrivatizeAssociatedLoopIndexAndCheckLoopLevel( |
| const parser::OpenMPLoopConstruct &); |
| void ResolveSeqLoopIndexInParallelOrTaskConstruct(const parser::Name &); |
| |
| bool IsNestedInDirective(llvm::omp::Directive directive); |
| void ResolveOmpObjectList(const parser::OmpObjectList &, Symbol::Flag); |
| void ResolveOmpObject(const parser::OmpObject &, Symbol::Flag); |
| Symbol *ResolveOmp(const parser::Name &, Symbol::Flag, Scope &); |
| Symbol *ResolveOmp(Symbol &, Symbol::Flag, Scope &); |
| Symbol *ResolveOmpCommonBlockName(const parser::Name *); |
| void ResolveOmpNameList(const std::list<parser::Name> &, Symbol::Flag); |
| void ResolveOmpName(const parser::Name &, Symbol::Flag); |
| Symbol *ResolveName(const parser::Name *); |
| Symbol *ResolveOmpObjectScope(const parser::Name *); |
| Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); |
| Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag); |
| void CheckMultipleAppearances( |
| const parser::Name &, const Symbol &, Symbol::Flag); |
| |
| void CheckDataCopyingClause( |
| const parser::Name &, const Symbol &, Symbol::Flag); |
| void CheckAssocLoopLevel(std::int64_t level, const parser::OmpClause *clause); |
| void CheckPrivateDSAObject( |
| const parser::Name &, const Symbol &, Symbol::Flag); |
| void CheckSourceLabel(const parser::Label &); |
| void CheckLabelContext(const parser::CharBlock, const parser::CharBlock, |
| std::optional<DirContext>, std::optional<DirContext>); |
| void ClearLabels() { |
| sourceLabels_.clear(); |
| targetLabels_.clear(); |
| }; |
| |
| bool HasSymbolInEnclosingScope(const Symbol &, Scope &); |
| std::int64_t ordCollapseLevel{0}; |
| }; |
| |
| template <typename T> |
| bool DirectiveAttributeVisitor<T>::HasDataSharingAttributeObject( |
| const Symbol &object) { |
| auto it{dataSharingAttributeObjects_.find(object)}; |
| return it != dataSharingAttributeObjects_.end(); |
| } |
| |
| template <typename T> |
| const parser::Name &DirectiveAttributeVisitor<T>::GetLoopIndex( |
| const parser::DoConstruct &x) { |
| using Bounds = parser::LoopControl::Bounds; |
| return std::get<Bounds>(x.GetLoopControl()->u).name.thing; |
| } |
| |
| template <typename T> |
| const parser::DoConstruct *DirectiveAttributeVisitor<T>::GetDoConstructIf( |
| const parser::ExecutionPartConstruct &x) { |
| return parser::Unwrap<parser::DoConstruct>(x); |
| } |
| |
| template <typename T> |
| Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity( |
| const parser::Name &name, Symbol::Flag flag, Scope &scope) { |
| if (!name.symbol) { |
| return nullptr; // not resolved by Name Resolution step, do nothing |
| } |
| name.symbol = DeclarePrivateAccessEntity(*name.symbol, flag, scope); |
| return name.symbol; |
| } |
| |
| template <typename T> |
| Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity( |
| Symbol &object, Symbol::Flag flag, Scope &scope) { |
| if (object.owner() != currScope()) { |
| auto &symbol{MakeAssocSymbol(object.name(), object, scope)}; |
| symbol.set(flag); |
| return &symbol; |
| } else { |
| object.set(flag); |
| return &object; |
| } |
| } |
| |
| bool AccAttributeVisitor::Pre(const parser::OpenACCBlockConstruct &x) { |
| const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)}; |
| const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)}; |
| switch (blockDir.v) { |
| case llvm::acc::Directive::ACCD_data: |
| case llvm::acc::Directive::ACCD_host_data: |
| case llvm::acc::Directive::ACCD_kernels: |
| case llvm::acc::Directive::ACCD_parallel: |
| case llvm::acc::Directive::ACCD_serial: |
| PushContext(blockDir.source, blockDir.v); |
| break; |
| default: |
| break; |
| } |
| ClearDataSharingAttributeObjects(); |
| return true; |
| } |
| |
| bool AccAttributeVisitor::Pre(const parser::OpenACCDeclarativeConstruct &x) { |
| if (const auto *declConstruct{ |
| std::get_if<parser::OpenACCStandaloneDeclarativeConstruct>(&x.u)}) { |
| const auto &declDir{ |
| std::get<parser::AccDeclarativeDirective>(declConstruct->t)}; |
| PushContext(declDir.source, llvm::acc::Directive::ACCD_declare); |
| } else if (const auto *routineConstruct{ |
| std::get_if<parser::OpenACCRoutineConstruct>(&x.u)}) { |
| const auto &verbatim{std::get<parser::Verbatim>(routineConstruct->t)}; |
| PushContext(verbatim.source, llvm::acc::Directive::ACCD_routine); |
| } |
| ClearDataSharingAttributeObjects(); |
| return true; |
| } |
| |
| static const parser::AccObjectList &GetAccObjectList( |
| const parser::AccClause &clause) { |
| if (const auto *copyClause = |
| std::get_if<Fortran::parser::AccClause::Copy>(&clause.u)) { |
| return copyClause->v; |
| } else if (const auto *createClause = |
| std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) { |
| const Fortran::parser::AccObjectListWithModifier &listWithModifier = |
| createClause->v; |
| const Fortran::parser::AccObjectList &accObjectList = |
| std::get<Fortran::parser::AccObjectList>(listWithModifier.t); |
| return accObjectList; |
| } else if (const auto *copyinClause = |
| std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) { |
| const Fortran::parser::AccObjectListWithModifier &listWithModifier = |
| copyinClause->v; |
| const Fortran::parser::AccObjectList &accObjectList = |
| std::get<Fortran::parser::AccObjectList>(listWithModifier.t); |
| return accObjectList; |
| } else if (const auto *copyoutClause = |
| std::get_if<Fortran::parser::AccClause::Copyout>(&clause.u)) { |
| const Fortran::parser::AccObjectListWithModifier &listWithModifier = |
| copyoutClause->v; |
| const Fortran::parser::AccObjectList &accObjectList = |
| std::get<Fortran::parser::AccObjectList>(listWithModifier.t); |
| return accObjectList; |
| } else if (const auto *presentClause = |
| std::get_if<Fortran::parser::AccClause::Present>(&clause.u)) { |
| return presentClause->v; |
| } else if (const auto *deviceptrClause = |
| std::get_if<Fortran::parser::AccClause::Deviceptr>( |
| &clause.u)) { |
| return deviceptrClause->v; |
| } else if (const auto *deviceResidentClause = |
| std::get_if<Fortran::parser::AccClause::DeviceResident>( |
| &clause.u)) { |
| return deviceResidentClause->v; |
| } else if (const auto *linkClause = |
| std::get_if<Fortran::parser::AccClause::Link>(&clause.u)) { |
| return linkClause->v; |
| } else { |
| llvm_unreachable("Clause without object list!"); |
| } |
| } |
| |
| void AccAttributeVisitor::Post( |
| const parser::OpenACCStandaloneDeclarativeConstruct &x) { |
| const auto &clauseList = std::get<parser::AccClauseList>(x.t); |
| for (const auto &clause : clauseList.v) { |
| // Restriction - line 2414 |
| DoNotAllowAssumedSizedArray(GetAccObjectList(clause)); |
| } |
| } |
| |
| bool AccAttributeVisitor::Pre(const parser::OpenACCLoopConstruct &x) { |
| const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)}; |
| const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)}; |
| const auto &clauseList{std::get<parser::AccClauseList>(beginDir.t)}; |
| if (loopDir.v == llvm::acc::Directive::ACCD_loop) { |
| PushContext(loopDir.source, loopDir.v); |
| } |
| ClearDataSharingAttributeObjects(); |
| SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList)); |
| PrivatizeAssociatedLoopIndex(x); |
| return true; |
| } |
| |
| bool AccAttributeVisitor::Pre(const parser::OpenACCStandaloneConstruct &x) { |
| const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)}; |
| switch (standaloneDir.v) { |
| case llvm::acc::Directive::ACCD_enter_data: |
| case llvm::acc::Directive::ACCD_exit_data: |
| case llvm::acc::Directive::ACCD_init: |
| case llvm::acc::Directive::ACCD_set: |
| case llvm::acc::Directive::ACCD_shutdown: |
| case llvm::acc::Directive::ACCD_update: |
| PushContext(standaloneDir.source, standaloneDir.v); |
| break; |
| default: |
| break; |
| } |
| ClearDataSharingAttributeObjects(); |
| return true; |
| } |
| |
| Symbol *AccAttributeVisitor::ResolveName(const parser::Name &name) { |
| Symbol *prev{currScope().FindSymbol(name.source)}; |
| if (prev != name.symbol) { |
| name.symbol = prev; |
| } |
| return prev; |
| } |
| |
| bool AccAttributeVisitor::Pre(const parser::OpenACCRoutineConstruct &x) { |
| const auto &optName{std::get<std::optional<parser::Name>>(x.t)}; |
| if (optName) { |
| if (!ResolveName(*optName)) { |
| context_.Say((*optName).source, |
| "No function or subroutine declared for '%s'"_err_en_US, |
| (*optName).source); |
| } |
| } |
| return true; |
| } |
| |
| bool AccAttributeVisitor::Pre(const parser::AccBindClause &x) { |
| if (const auto *name{std::get_if<parser::Name>(&x.u)}) { |
| if (!ResolveName(*name)) { |
| context_.Say(name->source, |
| "No function or subroutine declared for '%s'"_err_en_US, |
| name->source); |
| } |
| } |
| return true; |
| } |
| |
| bool AccAttributeVisitor::Pre(const parser::OpenACCCombinedConstruct &x) { |
| const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)}; |
| const auto &combinedDir{ |
| std::get<parser::AccCombinedDirective>(beginBlockDir.t)}; |
| switch (combinedDir.v) { |
| case llvm::acc::Directive::ACCD_kernels_loop: |
| case llvm::acc::Directive::ACCD_parallel_loop: |
| case llvm::acc::Directive::ACCD_serial_loop: |
| PushContext(combinedDir.source, combinedDir.v); |
| break; |
| default: |
| break; |
| } |
| ClearDataSharingAttributeObjects(); |
| return true; |
| } |
| |
| static bool IsLastNameArray(const parser::Designator &designator) { |
| const auto &name{GetLastName(designator)}; |
| const evaluate::DataRef dataRef{*(name.symbol)}; |
| return std::visit( |
| common::visitors{ |
| [](const evaluate::SymbolRef &ref) { return ref->Rank() > 0; }, |
| [](const evaluate::ArrayRef &aref) { |
| return aref.base().IsSymbol() || |
| aref.base().GetComponent().base().Rank() == 0; |
| }, |
| [](const auto &) { return false; }, |
| }, |
| dataRef.u); |
| } |
| |
| void AccAttributeVisitor::AllowOnlyArrayAndSubArray( |
| const parser::AccObjectList &objectList) { |
| for (const auto &accObject : objectList.v) { |
| std::visit( |
| common::visitors{ |
| [&](const parser::Designator &designator) { |
| if (!IsLastNameArray(designator)) { |
| context_.Say(designator.source, |
| "Only array element or subarray are allowed in %s directive"_err_en_US, |
| parser::ToUpperCaseLetters( |
| llvm::acc::getOpenACCDirectiveName( |
| GetContext().directive) |
| .str())); |
| } |
| }, |
| [&](const auto &name) { |
| context_.Say(name.source, |
| "Only array element or subarray are allowed in %s directive"_err_en_US, |
| parser::ToUpperCaseLetters( |
| llvm::acc::getOpenACCDirectiveName(GetContext().directive) |
| .str())); |
| }, |
| }, |
| accObject.u); |
| } |
| } |
| |
| void AccAttributeVisitor::DoNotAllowAssumedSizedArray( |
| const parser::AccObjectList &objectList) { |
| for (const auto &accObject : objectList.v) { |
| std::visit( |
| common::visitors{ |
| [&](const parser::Designator &designator) { |
| const auto &name{GetLastName(designator)}; |
| if (name.symbol && semantics::IsAssumedSizeArray(*name.symbol)) { |
| context_.Say(designator.source, |
| "Assumed-size dummy arrays may not appear on the %s " |
| "directive"_err_en_US, |
| parser::ToUpperCaseLetters( |
| llvm::acc::getOpenACCDirectiveName( |
| GetContext().directive) |
| .str())); |
| } |
| }, |
| [&](const auto &name) { |
| |
| }, |
| }, |
| accObject.u); |
| } |
| } |
| |
| bool AccAttributeVisitor::Pre(const parser::OpenACCCacheConstruct &x) { |
| const auto &verbatim{std::get<parser::Verbatim>(x.t)}; |
| PushContext(verbatim.source, llvm::acc::Directive::ACCD_cache); |
| ClearDataSharingAttributeObjects(); |
| |
| const auto &objectListWithModifier = |
| std::get<parser::AccObjectListWithModifier>(x.t); |
| const auto &objectList = |
| std::get<Fortran::parser::AccObjectList>(objectListWithModifier.t); |
| |
| // 2.10 Cache directive restriction: A var in a cache directive must be a |
| // single array element or a simple subarray. |
| AllowOnlyArrayAndSubArray(objectList); |
| |
| return true; |
| } |
| |
| std::int64_t AccAttributeVisitor::GetAssociatedLoopLevelFromClauses( |
| const parser::AccClauseList &x) { |
| std::int64_t collapseLevel{0}; |
| for (const auto &clause : x.v) { |
| if (const auto *collapseClause{ |
| std::get_if<parser::AccClause::Collapse>(&clause.u)}) { |
| if (const auto v{EvaluateInt64(context_, collapseClause->v)}) { |
| collapseLevel = *v; |
| } |
| } |
| } |
| |
| if (collapseLevel) { |
| return collapseLevel; |
| } |
| return 1; // default is outermost loop |
| } |
| |
| void AccAttributeVisitor::PrivatizeAssociatedLoopIndex( |
| const parser::OpenACCLoopConstruct &x) { |
| std::int64_t level{GetContext().associatedLoopLevel}; |
| if (level <= 0) { // collpase value was negative or 0 |
| return; |
| } |
| Symbol::Flag ivDSA{Symbol::Flag::AccPrivate}; |
| |
| const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)}; |
| for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) { |
| // go through all the nested do-loops and resolve index variables |
| const parser::Name &iv{GetLoopIndex(*loop)}; |
| if (auto *symbol{ResolveAcc(iv, ivDSA, currScope())}) { |
| symbol->set(Symbol::Flag::AccPreDetermined); |
| iv.symbol = symbol; // adjust the symbol within region |
| AddToContextObjectWithDSA(*symbol, ivDSA); |
| } |
| |
| const auto &block{std::get<parser::Block>(loop->t)}; |
| const auto it{block.begin()}; |
| loop = it != block.end() ? GetDoConstructIf(*it) : nullptr; |
| } |
| CHECK(level == 0); |
| } |
| |
| void AccAttributeVisitor::EnsureAllocatableOrPointer( |
| const llvm::acc::Clause clause, const parser::AccObjectList &objectList) { |
| for (const auto &accObject : objectList.v) { |
| std::visit( |
| common::visitors{ |
| [&](const parser::Designator &designator) { |
| const auto &lastName{GetLastName(designator)}; |
| if (!IsAllocatableOrPointer(*lastName.symbol)) { |
| context_.Say(designator.source, |
| "Argument `%s` on the %s clause must be a variable or " |
| "array with the POINTER or ALLOCATABLE attribute"_err_en_US, |
| lastName.symbol->name(), |
| parser::ToUpperCaseLetters( |
| llvm::acc::getOpenACCClauseName(clause).str())); |
| } |
| }, |
| [&](const auto &name) { |
| context_.Say(name.source, |
| "Argument on the %s clause must be a variable or " |
| "array with the POINTER or ALLOCATABLE attribute"_err_en_US, |
| parser::ToUpperCaseLetters( |
| llvm::acc::getOpenACCClauseName(clause).str())); |
| }, |
| }, |
| accObject.u); |
| } |
| } |
| |
| bool AccAttributeVisitor::Pre(const parser::AccClause::Attach &x) { |
| // Restriction - line 1708-1709 |
| EnsureAllocatableOrPointer(llvm::acc::Clause::ACCC_attach, x.v); |
| return true; |
| } |
| |
| bool AccAttributeVisitor::Pre(const parser::AccClause::Detach &x) { |
| // Restriction - line 1715-1717 |
| EnsureAllocatableOrPointer(llvm::acc::Clause::ACCC_detach, x.v); |
| return true; |
| } |
| |
| void AccAttributeVisitor::Post(const parser::AccDefaultClause &x) { |
| if (!dirContext_.empty()) { |
| switch (x.v) { |
| case llvm::acc::DefaultValue::ACC_Default_present: |
| SetContextDefaultDSA(Symbol::Flag::AccPresent); |
| break; |
| case llvm::acc::DefaultValue::ACC_Default_none: |
| SetContextDefaultDSA(Symbol::Flag::AccNone); |
| break; |
| } |
| } |
| } |
| |
| // For OpenACC constructs, check all the data-refs within the constructs |
| // and adjust the symbol for each Name if necessary |
| void AccAttributeVisitor::Post(const parser::Name &name) { |
| auto *symbol{name.symbol}; |
| if (symbol && !dirContext_.empty() && GetContext().withinConstruct) { |
| if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() && |
| !IsObjectWithDSA(*symbol)) { |
| if (Symbol * found{currScope().FindSymbol(name.source)}) { |
| if (symbol != found) { |
| name.symbol = found; // adjust the symbol within region |
| } else if (GetContext().defaultDSA == Symbol::Flag::AccNone) { |
| // 2.5.14. |
| context_.Say(name.source, |
| "The DEFAULT(NONE) clause requires that '%s' must be listed in " |
| "a data-mapping clause"_err_en_US, |
| symbol->name()); |
| } |
| } |
| } |
| } // within OpenACC construct |
| } |
| |
| Symbol *AccAttributeVisitor::ResolveAccCommonBlockName( |
| const parser::Name *name) { |
| if (!name) { |
| return nullptr; |
| } else if (auto *prev{ |
| GetContext().scope.parent().FindCommonBlock(name->source)}) { |
| name->symbol = prev; |
| return prev; |
| } else { |
| return nullptr; |
| } |
| } |
| |
| void AccAttributeVisitor::ResolveAccObjectList( |
| const parser::AccObjectList &accObjectList, Symbol::Flag accFlag) { |
| for (const auto &accObject : accObjectList.v) { |
| ResolveAccObject(accObject, accFlag); |
| } |
| } |
| |
| void AccAttributeVisitor::ResolveAccObject( |
| const parser::AccObject &accObject, Symbol::Flag accFlag) { |
| std::visit( |
| common::visitors{ |
| [&](const parser::Designator &designator) { |
| if (const auto *name{GetDesignatorNameIfDataRef(designator)}) { |
| if (auto *symbol{ResolveAcc(*name, accFlag, currScope())}) { |
| AddToContextObjectWithDSA(*symbol, accFlag); |
| if (dataSharingAttributeFlags.test(accFlag)) { |
| CheckMultipleAppearances(*name, *symbol, accFlag); |
| } |
| } |
| } else { |
| // Array sections to be changed to substrings as needed |
| if (AnalyzeExpr(context_, designator)) { |
| if (std::holds_alternative<parser::Substring>(designator.u)) { |
| context_.Say(designator.source, |
| "Substrings are not allowed on OpenACC " |
| "directives or clauses"_err_en_US); |
| } |
| } |
| // other checks, more TBD |
| } |
| }, |
| [&](const parser::Name &name) { // common block |
| if (auto *symbol{ResolveAccCommonBlockName(&name)}) { |
| CheckMultipleAppearances( |
| name, *symbol, Symbol::Flag::AccCommonBlock); |
| for (auto &object : symbol->get<CommonBlockDetails>().objects()) { |
| if (auto *resolvedObject{ |
| ResolveAcc(*object, accFlag, currScope())}) { |
| AddToContextObjectWithDSA(*resolvedObject, accFlag); |
| } |
| } |
| } else { |
| context_.Say(name.source, |
| "COMMON block must be declared in the same scoping unit " |
| "in which the OpenACC directive or clause appears"_err_en_US); |
| } |
| }, |
| }, |
| accObject.u); |
| } |
| |
| Symbol *AccAttributeVisitor::ResolveAcc( |
| const parser::Name &name, Symbol::Flag accFlag, Scope &scope) { |
| if (accFlagsRequireNewSymbol.test(accFlag)) { |
| return DeclarePrivateAccessEntity(name, accFlag, scope); |
| } else { |
| return DeclareOrMarkOtherAccessEntity(name, accFlag); |
| } |
| } |
| |
| Symbol *AccAttributeVisitor::ResolveAcc( |
| Symbol &symbol, Symbol::Flag accFlag, Scope &scope) { |
| if (accFlagsRequireNewSymbol.test(accFlag)) { |
| return DeclarePrivateAccessEntity(symbol, accFlag, scope); |
| } else { |
| return DeclareOrMarkOtherAccessEntity(symbol, accFlag); |
| } |
| } |
| |
| Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity( |
| const parser::Name &name, Symbol::Flag accFlag) { |
| Symbol *prev{currScope().FindSymbol(name.source)}; |
| if (!name.symbol || !prev) { |
| return nullptr; |
| } else if (prev != name.symbol) { |
| name.symbol = prev; |
| } |
| return DeclareOrMarkOtherAccessEntity(*prev, accFlag); |
| } |
| |
| Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity( |
| Symbol &object, Symbol::Flag accFlag) { |
| if (accFlagsRequireMark.test(accFlag)) { |
| object.set(accFlag); |
| } |
| return &object; |
| } |
| |
| static bool WithMultipleAppearancesAccException( |
| const Symbol &symbol, Symbol::Flag flag) { |
| return false; // Place holder |
| } |
| |
| void AccAttributeVisitor::CheckMultipleAppearances( |
| const parser::Name &name, const Symbol &symbol, Symbol::Flag accFlag) { |
| const auto *target{&symbol}; |
| if (accFlagsRequireNewSymbol.test(accFlag)) { |
| if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) { |
| target = &details->symbol(); |
| } |
| } |
| if (HasDataSharingAttributeObject(*target) && |
| !WithMultipleAppearancesAccException(symbol, accFlag)) { |
| context_.Say(name.source, |
| "'%s' appears in more than one data-sharing clause " |
| "on the same OpenACC directive"_err_en_US, |
| name.ToString()); |
| } else { |
| AddDataSharingAttributeObject(*target); |
| } |
| } |
| |
| bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) { |
| const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)}; |
| const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; |
| switch (beginDir.v) { |
| case llvm::omp::Directive::OMPD_master: |
| case llvm::omp::Directive::OMPD_ordered: |
| case llvm::omp::Directive::OMPD_parallel: |
| case llvm::omp::Directive::OMPD_single: |
| case llvm::omp::Directive::OMPD_target: |
| case llvm::omp::Directive::OMPD_target_data: |
| case llvm::omp::Directive::OMPD_task: |
| case llvm::omp::Directive::OMPD_taskgroup: |
| case llvm::omp::Directive::OMPD_teams: |
| case llvm::omp::Directive::OMPD_workshare: |
| case llvm::omp::Directive::OMPD_parallel_workshare: |
| case llvm::omp::Directive::OMPD_target_teams: |
| case llvm::omp::Directive::OMPD_target_parallel: |
| PushContext(beginDir.source, beginDir.v); |
| break; |
| default: |
| // TODO others |
| break; |
| } |
| ClearDataSharingAttributeObjects(); |
| ClearPrivateDataSharingAttributeObjects(); |
| ClearAllocateNames(); |
| return true; |
| } |
| |
| void OmpAttributeVisitor::Post(const parser::OpenMPBlockConstruct &x) { |
| const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)}; |
| const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; |
| switch (beginDir.v) { |
| case llvm::omp::Directive::OMPD_parallel: |
| case llvm::omp::Directive::OMPD_single: |
| case llvm::omp::Directive::OMPD_target: |
| case llvm::omp::Directive::OMPD_task: |
| case llvm::omp::Directive::OMPD_teams: |
| case llvm::omp::Directive::OMPD_parallel_workshare: |
| case llvm::omp::Directive::OMPD_target_teams: |
| case llvm::omp::Directive::OMPD_target_parallel: { |
| bool hasPrivate; |
| for (const auto *allocName : allocateNames_) { |
| hasPrivate = false; |
| for (auto privateObj : privateDataSharingAttributeObjects_) { |
| const Symbol &symbolPrivate{*privateObj}; |
| if (allocName->source == symbolPrivate.name()) { |
| hasPrivate = true; |
| break; |
| } |
| } |
| if (!hasPrivate) { |
| context_.Say(allocName->source, |
| "The ALLOCATE clause requires that '%s' must be listed in a " |
| "private " |
| "data-sharing attribute clause on the same directive"_err_en_US, |
| allocName->ToString()); |
| } |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| PopContext(); |
| } |
| |
| bool OmpAttributeVisitor::Pre( |
| const parser::OpenMPSimpleStandaloneConstruct &x) { |
| const auto &standaloneDir{ |
| std::get<parser::OmpSimpleStandaloneDirective>(x.t)}; |
| switch (standaloneDir.v) { |
| case llvm::omp::Directive::OMPD_barrier: |
| case llvm::omp::Directive::OMPD_ordered: |
| case llvm::omp::Directive::OMPD_target_enter_data: |
| case llvm::omp::Directive::OMPD_target_exit_data: |
| case llvm::omp::Directive::OMPD_target_update: |
| case llvm::omp::Directive::OMPD_taskwait: |
| case llvm::omp::Directive::OMPD_taskyield: |
| PushContext(standaloneDir.source, standaloneDir.v); |
| break; |
| default: |
| break; |
| } |
| ClearDataSharingAttributeObjects(); |
| return true; |
| } |
| |
| bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) { |
| const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; |
| const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; |
| const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)}; |
| switch (beginDir.v) { |
| case llvm::omp::Directive::OMPD_distribute: |
| case llvm::omp::Directive::OMPD_distribute_parallel_do: |
| case llvm::omp::Directive::OMPD_distribute_parallel_do_simd: |
| case llvm::omp::Directive::OMPD_distribute_simd: |
| case llvm::omp::Directive::OMPD_do: |
| case llvm::omp::Directive::OMPD_do_simd: |
| case llvm::omp::Directive::OMPD_parallel_do: |
| case llvm::omp::Directive::OMPD_parallel_do_simd: |
| case llvm::omp::Directive::OMPD_simd: |
| case llvm::omp::Directive::OMPD_target_parallel_do: |
| case llvm::omp::Directive::OMPD_target_parallel_do_simd: |
| case llvm::omp::Directive::OMPD_target_teams_distribute: |
| case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do: |
| case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd: |
| case llvm::omp::Directive::OMPD_target_teams_distribute_simd: |
| case llvm::omp::Directive::OMPD_target_simd: |
| case llvm::omp::Directive::OMPD_taskloop: |
| case llvm::omp::Directive::OMPD_taskloop_simd: |
| case llvm::omp::Directive::OMPD_teams_distribute: |
| case llvm::omp::Directive::OMPD_teams_distribute_parallel_do: |
| case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd: |
| case llvm::omp::Directive::OMPD_teams_distribute_simd: |
| PushContext(beginDir.source, beginDir.v); |
| break; |
| default: |
| break; |
| } |
| ClearDataSharingAttributeObjects(); |
| SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList)); |
| |
| if (beginDir.v == llvm::omp::Directive::OMPD_do) { |
| if (const auto &doConstruct{ |
| std::get<std::optional<parser::DoConstruct>>(x.t)}) { |
| if (doConstruct.value().IsDoWhile()) { |
| return true; |
| } |
| } |
| } |
| PrivatizeAssociatedLoopIndexAndCheckLoopLevel(x); |
| ordCollapseLevel = GetAssociatedLoopLevelFromClauses(clauseList) + 1; |
| return true; |
| } |
| |
| void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct( |
| const parser::Name &iv) { |
| auto targetIt{dirContext_.rbegin()}; |
| for (;; ++targetIt) { |
| if (targetIt == dirContext_.rend()) { |
| return; |
| } |
| if (llvm::omp::parallelSet.test(targetIt->directive) || |
| llvm::omp::taskGeneratingSet.test(targetIt->directive)) { |
| break; |
| } |
| } |
| if (auto *symbol{ResolveOmp(iv, Symbol::Flag::OmpPrivate, targetIt->scope)}) { |
| targetIt++; |
| symbol->set(Symbol::Flag::OmpPreDetermined); |
| iv.symbol = symbol; // adjust the symbol within region |
| for (auto it{dirContext_.rbegin()}; it != targetIt; ++it) { |
| AddToContextObjectWithDSA(*symbol, Symbol::Flag::OmpPrivate, *it); |
| } |
| } |
| } |
| |
| // [OMP-4.5]2.15.1.1 Data-sharing Attribute Rules - Predetermined |
| // - A loop iteration variable for a sequential loop in a parallel |
| // or task generating construct is private in the innermost such |
| // construct that encloses the loop |
| // Loop iteration variables are not well defined for DO WHILE loop. |
| // Use of DO CONCURRENT inside OpenMP construct is unspecified behavior |
| // till OpenMP-5.0 standard. |
| // In above both cases we skip the privatization of iteration variables. |
| bool OmpAttributeVisitor::Pre(const parser::DoConstruct &x) { |
| // TODO:[OpenMP 5.1] DO CONCURRENT indices are private |
| if (x.IsDoNormal()) { |
| if (!dirContext_.empty() && GetContext().withinConstruct) { |
| if (const auto &iv{GetLoopIndex(x)}; iv.symbol) { |
| if (!iv.symbol->test(Symbol::Flag::OmpPreDetermined)) { |
| ResolveSeqLoopIndexInParallelOrTaskConstruct(iv); |
| } else { |
| // TODO: conflict checks with explicitly determined DSA |
| } |
| ordCollapseLevel--; |
| if (ordCollapseLevel) { |
| if (const auto *details{iv.symbol->detailsIf<HostAssocDetails>()}) { |
| const Symbol *tpSymbol = &details->symbol(); |
| if (tpSymbol->test(Symbol::Flag::OmpThreadprivate)) { |
| context_.Say(iv.source, |
| "Loop iteration variable %s is not allowed in THREADPRIVATE."_err_en_US, |
| iv.ToString()); |
| } |
| } |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| std::int64_t OmpAttributeVisitor::GetAssociatedLoopLevelFromClauses( |
| const parser::OmpClauseList &x) { |
| std::int64_t orderedLevel{0}; |
| std::int64_t collapseLevel{0}; |
| |
| const parser::OmpClause *ordClause{nullptr}; |
| const parser::OmpClause *collClause{nullptr}; |
| |
| for (const auto &clause : x.v) { |
| if (const auto *orderedClause{ |
| std::get_if<parser::OmpClause::Ordered>(&clause.u)}) { |
| if (const auto v{EvaluateInt64(context_, orderedClause->v)}) { |
| orderedLevel = *v; |
| } |
| ordClause = &clause; |
| } |
| if (const auto *collapseClause{ |
| std::get_if<parser::OmpClause::Collapse>(&clause.u)}) { |
| if (const auto v{EvaluateInt64(context_, collapseClause->v)}) { |
| collapseLevel = *v; |
| } |
| collClause = &clause; |
| } |
| } |
| |
| if (orderedLevel && (!collapseLevel || orderedLevel >= collapseLevel)) { |
| SetAssociatedClause(*ordClause); |
| return orderedLevel; |
| } else if (!orderedLevel && collapseLevel) { |
| SetAssociatedClause(*collClause); |
| return collapseLevel; |
| } // orderedLevel < collapseLevel is an error handled in structural checks |
| return 1; // default is outermost loop |
| } |
| |
| // 2.15.1.1 Data-sharing Attribute Rules - Predetermined |
| // - The loop iteration variable(s) in the associated do-loop(s) of a do, |
| // parallel do, taskloop, or distribute construct is (are) private. |
| // - The loop iteration variable in the associated do-loop of a simd construct |
| // with just one associated do-loop is linear with a linear-step that is the |
| // increment of the associated do-loop. |
| // - The loop iteration variables in the associated do-loops of a simd |
| // construct with multiple associated do-loops are lastprivate. |
| void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel( |
| const parser::OpenMPLoopConstruct &x) { |
| std::int64_t level{GetContext().associatedLoopLevel}; |
| if (level <= 0) { |
| return; |
| } |
| Symbol::Flag ivDSA; |
| if (!llvm::omp::simdSet.test(GetContext().directive)) { |
| ivDSA = Symbol::Flag::OmpPrivate; |
| } else if (level == 1) { |
| ivDSA = Symbol::Flag::OmpLinear; |
| } else { |
| ivDSA = Symbol::Flag::OmpLastPrivate; |
| } |
| |
| const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)}; |
| for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) { |
| // go through all the nested do-loops and resolve index variables |
| const parser::Name &iv{GetLoopIndex(*loop)}; |
| if (auto *symbol{ResolveOmp(iv, ivDSA, currScope())}) { |
| symbol->set(Symbol::Flag::OmpPreDetermined); |
| iv.symbol = symbol; // adjust the symbol within region |
| AddToContextObjectWithDSA(*symbol, ivDSA); |
| } |
| |
| const auto &block{std::get<parser::Block>(loop->t)}; |
| const auto it{block.begin()}; |
| loop = it != block.end() ? GetDoConstructIf(*it) : nullptr; |
| } |
| CheckAssocLoopLevel(level, GetAssociatedClause()); |
| } |
| void OmpAttributeVisitor::CheckAssocLoopLevel( |
| std::int64_t level, const parser::OmpClause *clause) { |
| if (clause && level != 0) { |
| context_.Say(clause->source, |
| "The value of the parameter in the COLLAPSE or ORDERED clause must" |
| " not be larger than the number of nested loops" |
| " following the construct."_err_en_US); |
| } |
| } |
| |
| bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct &x) { |
| const auto &beginSectionsDir{ |
| std::get<parser::OmpBeginSectionsDirective>(x.t)}; |
| const auto &beginDir{ |
| std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)}; |
| switch (beginDir.v) { |
| case llvm::omp::Directive::OMPD_parallel_sections: |
| case llvm::omp::Directive::OMPD_sections: |
| PushContext(beginDir.source, beginDir.v); |
| break; |
| default: |
| break; |
| } |
| ClearDataSharingAttributeObjects(); |
| return true; |
| } |
| |
| bool OmpAttributeVisitor::Pre(const parser::OmpCriticalDirective &x) { |
| const auto &name{std::get<std::optional<parser::Name>>(x.t)}; |
| if (name) { |
| ResolveOmpName(*name, Symbol::Flag::OmpCriticalLock); |
| } |
| return true; |
| } |
| |
| bool OmpAttributeVisitor::Pre(const parser::OmpEndCriticalDirective &x) { |
| const auto &name{std::get<std::optional<parser::Name>>(x.t)}; |
| if (name) { |
| ResolveOmpName(*name, Symbol::Flag::OmpCriticalLock); |
| } |
| return true; |
| } |
| |
| bool OmpAttributeVisitor::Pre(const parser::OpenMPCriticalConstruct &x) { |
| const auto &criticalDir{std::get<parser::OmpCriticalDirective>(x.t)}; |
| PushContext(criticalDir.source, llvm::omp::Directive::OMPD_critical); |
| return true; |
| } |
| |
| bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate &x) { |
| PushContext(x.source, llvm::omp::Directive::OMPD_threadprivate); |
| const auto &list{std::get<parser::OmpObjectList>(x.t)}; |
| ResolveOmpObjectList(list, Symbol::Flag::OmpThreadprivate); |
| return true; |
| } |
| |
| bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclarativeAllocate &x) { |
| PushContext(x.source, llvm::omp::Directive::OMPD_allocate); |
| const auto &list{std::get<parser::OmpObjectList>(x.t)}; |
| ResolveOmpObjectList(list, Symbol::Flag::OmpDeclarativeAllocateDirective); |
| return false; |
| } |
| |
| bool OmpAttributeVisitor::Pre(const parser::OpenMPExecutableAllocate &x) { |
| PushContext(x.source, llvm::omp::Directive::OMPD_allocate); |
| const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)}; |
| if (list) { |
| ResolveOmpObjectList(*list, Symbol::Flag::OmpExecutableAllocateDirective); |
| } |
| return true; |
| } |
| |
| void OmpAttributeVisitor::Post(const parser::OmpDefaultClause &x) { |
| if (!dirContext_.empty()) { |
| switch (x.v) { |
| case parser::OmpDefaultClause::Type::Private: |
| SetContextDefaultDSA(Symbol::Flag::OmpPrivate); |
| break; |
| case parser::OmpDefaultClause::Type::Firstprivate: |
| SetContextDefaultDSA(Symbol::Flag::OmpFirstPrivate); |
| break; |
| case parser::OmpDefaultClause::Type::Shared: |
| SetContextDefaultDSA(Symbol::Flag::OmpShared); |
| break; |
| case parser::OmpDefaultClause::Type::None: |
| SetContextDefaultDSA(Symbol::Flag::OmpNone); |
| break; |
| } |
| } |
| } |
| |
| bool OmpAttributeVisitor::IsNestedInDirective(llvm::omp::Directive directive) { |
| if (dirContext_.size() >= 1) { |
| for (std::size_t i = dirContext_.size() - 1; i > 0; --i) { |
| if (dirContext_[i - 1].directive == directive) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| void OmpAttributeVisitor::Post(const parser::OpenMPExecutableAllocate &x) { |
| bool hasAllocator = false; |
| // TODO: Investigate whether searching the clause list can be done with |
| // parser::Unwrap instead of the following loop |
| const auto &clauseList{std::get<parser::OmpClauseList>(x.t)}; |
| for (const auto &clause : clauseList.v) { |
| if (std::get_if<parser::OmpClause::Allocator>(&clause.u)) { |
| hasAllocator = true; |
| } |
| } |
| |
| if (IsNestedInDirective(llvm::omp::Directive::OMPD_target) && !hasAllocator) { |
| // TODO: expand this check to exclude the case when a requires |
| // directive with the dynamic_allocators clause is present |
| // in the same compilation unit (OMP5.0 2.11.3). |
| context_.Say(x.source, |
| "ALLOCATE directives that appear in a TARGET region " |
| "must specify an allocator clause"_err_en_US); |
| } |
| PopContext(); |
| } |
| |
| // For OpenMP constructs, check all the data-refs within the constructs |
| // and adjust the symbol for each Name if necessary |
| void OmpAttributeVisitor::Post(const parser::Name &name) { |
| auto *symbol{name.symbol}; |
| if (symbol && !dirContext_.empty() && GetContext().withinConstruct) { |
| if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() && |
| !IsObjectWithDSA(*symbol)) { |
| // TODO: create a separate function to go through the rules for |
| // predetermined, explicitly determined, and implicitly |
| // determined data-sharing attributes (2.15.1.1). |
| if (Symbol * found{currScope().FindSymbol(name.source)}) { |
| if (symbol != found) { |
| name.symbol = found; // adjust the symbol within region |
| } else if (GetContext().defaultDSA == Symbol::Flag::OmpNone) { |
| context_.Say(name.source, |
| "The DEFAULT(NONE) clause requires that '%s' must be listed in " |
| "a data-sharing attribute clause"_err_en_US, |
| symbol->name()); |
| } |
| } |
| } |
| } // within OpenMP construct |
| } |
| |
| Symbol *OmpAttributeVisitor::ResolveName(const parser::Name *name) { |
| if (auto *resolvedSymbol{ |
| name ? GetContext().scope.FindSymbol(name->source) : nullptr}) { |
| name->symbol = resolvedSymbol; |
| return resolvedSymbol; |
| } else { |
| return nullptr; |
| } |
| } |
| |
| void OmpAttributeVisitor::ResolveOmpName( |
| const parser::Name &name, Symbol::Flag ompFlag) { |
| if (ResolveName(&name)) { |
| if (auto *resolvedSymbol{ResolveOmp(name, ompFlag, currScope())}) { |
| if (dataSharingAttributeFlags.test(ompFlag)) { |
| AddToContextObjectWithDSA(*resolvedSymbol, ompFlag); |
| } |
| } |
| } else if (ompFlagsRequireNewSymbol.test(ompFlag)) { |
| const auto pair{GetContext().scope.try_emplace( |
| name.source, Attrs{}, ObjectEntityDetails{})}; |
| CHECK(pair.second); |
| name.symbol = &pair.first->second.get(); |
| } else { |
| DIE("OpenMP Name resolution failed"); |
| } |
| } |
| |
| void OmpAttributeVisitor::ResolveOmpNameList( |
| const std::list<parser::Name> &nameList, Symbol::Flag ompFlag) { |
| for (const auto &name : nameList) { |
| ResolveOmpName(name, ompFlag); |
| } |
| } |
| |
| Symbol *OmpAttributeVisitor::ResolveOmpCommonBlockName( |
| const parser::Name *name) { |
| if (auto *prev{name |
| ? GetContext().scope.parent().FindCommonBlock(name->source) |
| : nullptr}) { |
| name->symbol = prev; |
| return prev; |
| } |
| // Check if the Common Block is declared in the current scope |
| if (auto *commonBlockSymbol{ |
| name ? GetContext().scope.FindCommonBlock(name->source) : nullptr}) { |
| name->symbol = commonBlockSymbol; |
| return commonBlockSymbol; |
| } |
| return nullptr; |
| } |
| |
| // Use this function over ResolveOmpName when an omp object's scope needs |
| // resolving, it's symbol flag isn't important and a simple check for resolution |
| // failure is desired. Using ResolveOmpName means needing to work with the |
| // context to check for failure, whereas here a pointer comparison is all that's |
| // needed. |
| Symbol *OmpAttributeVisitor::ResolveOmpObjectScope(const parser::Name *name) { |
| |
| // TODO: Investigate whether the following block can be replaced by, or |
| // included in, the ResolveOmpName function |
| if (auto *prev{name ? GetContext().scope.parent().FindSymbol(name->source) |
| : nullptr}) { |
| name->symbol = prev; |
| return nullptr; |
| } |
| |
| // TODO: Investigate whether the following block can be replaced by, or |
| // included in, the ResolveOmpName function |
| if (auto *ompSymbol{ |
| name ? GetContext().scope.FindSymbol(name->source) : nullptr}) { |
| name->symbol = ompSymbol; |
| return ompSymbol; |
| } |
| return nullptr; |
| } |
| |
| void OmpAttributeVisitor::ResolveOmpObjectList( |
| const parser::OmpObjectList &ompObjectList, Symbol::Flag ompFlag) { |
| for (const auto &ompObject : ompObjectList.v) { |
| ResolveOmpObject(ompObject, ompFlag); |
| } |
| } |
| |
| void OmpAttributeVisitor::ResolveOmpObject( |
| const parser::OmpObject &ompObject, Symbol::Flag ompFlag) { |
| std::visit( |
| common::visitors{ |
| [&](const parser::Designator &designator) { |
| if (const auto *name{GetDesignatorNameIfDataRef(designator)}) { |
| if (auto *symbol{ResolveOmp(*name, ompFlag, currScope())}) { |
| if (dataCopyingAttributeFlags.test(ompFlag)) { |
| CheckDataCopyingClause(*name, *symbol, ompFlag); |
| } else { |
| AddToContextObjectWithDSA(*symbol, ompFlag); |
| if (dataSharingAttributeFlags.test(ompFlag)) { |
| CheckMultipleAppearances(*name, *symbol, ompFlag); |
| } |
| if (privateDataSharingAttributeFlags.test(ompFlag)) { |
| CheckPrivateDSAObject(*name, *symbol, ompFlag); |
| } |
| |
| if (ompFlag == Symbol::Flag::OmpAllocate) { |
| AddAllocateName(name); |
| } |
| } |
| if (ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective && |
| IsAllocatable(*symbol)) { |
| context_.Say(designator.source, |
| "List items specified in the ALLOCATE directive must not " |
| "have the ALLOCATABLE attribute unless the directive is " |
| "associated with an ALLOCATE statement"_err_en_US); |
| } |
| if ((ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective || |
| ompFlag == |
| Symbol::Flag::OmpExecutableAllocateDirective) && |
| ResolveOmpObjectScope(name) == nullptr) { |
| context_.Say(designator.source, // 2.15.3 |
| "List items must be declared in the same scoping unit " |
| "in which the ALLOCATE directive appears"_err_en_US); |
| } |
| } |
| } else { |
| // Array sections to be changed to substrings as needed |
| if (AnalyzeExpr(context_, designator)) { |
| if (std::holds_alternative<parser::Substring>(designator.u)) { |
| context_.Say(designator.source, |
| "Substrings are not allowed on OpenMP " |
| "directives or clauses"_err_en_US); |
| } |
| } |
| // other checks, more TBD |
| } |
| }, |
| [&](const parser::Name &name) { // common block |
| if (auto *symbol{ResolveOmpCommonBlockName(&name)}) { |
| if (!dataCopyingAttributeFlags.test(ompFlag)) { |
| CheckMultipleAppearances( |
| name, *symbol, Symbol::Flag::OmpCommonBlock); |
| } |
| // 2.15.3 When a named common block appears in a list, it has the |
| // same meaning as if every explicit member of the common block |
| // appeared in the list |
| for (auto &object : symbol->get<CommonBlockDetails>().objects()) { |
| if (auto *resolvedObject{ |
| ResolveOmp(*object, ompFlag, currScope())}) { |
| if (dataCopyingAttributeFlags.test(ompFlag)) { |
| CheckDataCopyingClause(name, *resolvedObject, ompFlag); |
| } else { |
| AddToContextObjectWithDSA(*resolvedObject, ompFlag); |
| } |
| } |
| } |
| } else { |
| context_.Say(name.source, // 2.15.3 |
| "COMMON block must be declared in the same scoping unit " |
| "in which the OpenMP directive or clause appears"_err_en_US); |
| } |
| }, |
| }, |
| ompObject.u); |
| } |
| |
| Symbol *OmpAttributeVisitor::ResolveOmp( |
| const parser::Name &name, Symbol::Flag ompFlag, Scope &scope) { |
| if (ompFlagsRequireNewSymbol.test(ompFlag)) { |
| return DeclarePrivateAccessEntity(name, ompFlag, scope); |
| } else { |
| return DeclareOrMarkOtherAccessEntity(name, ompFlag); |
| } |
| } |
| |
| Symbol *OmpAttributeVisitor::ResolveOmp( |
| Symbol &symbol, Symbol::Flag ompFlag, Scope &scope) { |
| if (ompFlagsRequireNewSymbol.test(ompFlag)) { |
| return DeclarePrivateAccessEntity(symbol, ompFlag, scope); |
| } else { |
| return DeclareOrMarkOtherAccessEntity(symbol, ompFlag); |
| } |
| } |
| |
| Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity( |
| const parser::Name &name, Symbol::Flag ompFlag) { |
| Symbol *prev{currScope().FindSymbol(name.source)}; |
| if (!name.symbol || !prev) { |
| return nullptr; |
| } else if (prev != name.symbol) { |
| name.symbol = prev; |
| } |
| return DeclareOrMarkOtherAccessEntity(*prev, ompFlag); |
| } |
| |
| Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity( |
| Symbol &object, Symbol::Flag ompFlag) { |
| if (ompFlagsRequireMark.test(ompFlag)) { |
| object.set(ompFlag); |
| } |
| return &object; |
| } |
| |
| static bool WithMultipleAppearancesOmpException( |
| const Symbol &symbol, Symbol::Flag flag) { |
| return (flag == Symbol::Flag::OmpFirstPrivate && |
| symbol.test(Symbol::Flag::OmpLastPrivate)) || |
| (flag == Symbol::Flag::OmpLastPrivate && |
| symbol.test(Symbol::Flag::OmpFirstPrivate)); |
| } |
| |
| void OmpAttributeVisitor::CheckMultipleAppearances( |
| const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) { |
| const auto *target{&symbol}; |
| if (ompFlagsRequireNewSymbol.test(ompFlag)) { |
| if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) { |
| target = &details->symbol(); |
| } |
| } |
| if (HasDataSharingAttributeObject(*target) && |
| !WithMultipleAppearancesOmpException(symbol, ompFlag)) { |
| context_.Say(name.source, |
| "'%s' appears in more than one data-sharing clause " |
| "on the same OpenMP directive"_err_en_US, |
| name.ToString()); |
| } else { |
| AddDataSharingAttributeObject(*target); |
| if (privateDataSharingAttributeFlags.test(ompFlag)) { |
| AddPrivateDataSharingAttributeObjects(*target); |
| } |
| } |
| } |
| |
| void ResolveAccParts( |
| SemanticsContext &context, const parser::ProgramUnit &node) { |
| if (context.IsEnabled(common::LanguageFeature::OpenACC)) { |
| AccAttributeVisitor{context}.Walk(node); |
| } |
| } |
| |
| void ResolveOmpParts( |
| SemanticsContext &context, const parser::ProgramUnit &node) { |
| if (context.IsEnabled(common::LanguageFeature::OpenMP)) { |
| OmpAttributeVisitor{context}.Walk(node); |
| if (!context.AnyFatalError()) { |
| // The data-sharing attribute of the loop iteration variable for a |
| // sequential loop (2.15.1.1) can only be determined when visiting |
| // the corresponding DoConstruct, a second walk is to adjust the |
| // symbols for all the data-refs of that loop iteration variable |
| // prior to the DoConstruct. |
| OmpAttributeVisitor{context}.Walk(node); |
| } |
| } |
| } |
| |
| void OmpAttributeVisitor::CheckDataCopyingClause( |
| const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) { |
| const auto *checkSymbol{&symbol}; |
| if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) { |
| checkSymbol = &details->symbol(); |
| } |
| |
| if (ompFlag == Symbol::Flag::OmpCopyIn) { |
| // List of items/objects that can appear in a 'copyin' clause must be |
| // 'threadprivate' |
| if (!checkSymbol->test(Symbol::Flag::OmpThreadprivate)) { |
| context_.Say(name.source, |
| "Non-THREADPRIVATE object '%s' in COPYIN clause"_err_en_US, |
| checkSymbol->name()); |
| } |
| } else if (ompFlag == Symbol::Flag::OmpCopyPrivate && |
| GetContext().directive == llvm::omp::Directive::OMPD_single) { |
| // A list item that appears in a 'copyprivate' clause may not appear on a |
| // 'private' or 'firstprivate' clause on a single construct |
| if (IsObjectWithDSA(symbol) && |
| (symbol.test(Symbol::Flag::OmpPrivate) || |
| symbol.test(Symbol::Flag::OmpFirstPrivate))) { |
| context_.Say(name.source, |
| "COPYPRIVATE variable '%s' may not appear on a PRIVATE or " |
| "FIRSTPRIVATE clause on a SINGLE construct"_err_en_US, |
| symbol.name()); |
| } else { |
| // List of items/objects that can appear in a 'copyprivate' clause must be |
| // either 'private' or 'threadprivate' in enclosing context. |
| if (!checkSymbol->test(Symbol::Flag::OmpThreadprivate) && |
| !(HasSymbolInEnclosingScope(symbol, currScope()) && |
| symbol.test(Symbol::Flag::OmpPrivate))) { |
| context_.Say(name.source, |
| "COPYPRIVATE variable '%s' is not PRIVATE or THREADPRIVATE in " |
| "outer context"_err_en_US, |
| symbol.name()); |
| } |
| } |
| } |
| } |
| |
| void OmpAttributeVisitor::CheckPrivateDSAObject( |
| const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) { |
| const auto &ultimateSymbol{symbol.GetUltimate()}; |
| llvm::StringRef clauseName{"PRIVATE"}; |
| if (ompFlag == Symbol::Flag::OmpFirstPrivate) { |
| clauseName = "FIRSTPRIVATE"; |
| } else if (ompFlag == Symbol::Flag::OmpLastPrivate) { |
| clauseName = "LASTPRIVATE"; |
| } |
| |
| if (ultimateSymbol.test(Symbol::Flag::InNamelist)) { |
| context_.Say(name.source, |
| "Variable '%s' in NAMELIST cannot be in a %s clause"_err_en_US, |
| name.ToString(), clauseName.str()); |
| } |
| |
| if (stmtFunctionExprSymbols_.find(ultimateSymbol) != |
| stmtFunctionExprSymbols_.end()) { |
| context_.Say(name.source, |
| "Variable '%s' in STATEMENT FUNCTION expression cannot be in a " |
| "%s clause"_err_en_US, |
| name.ToString(), clauseName.str()); |
| } |
| } |
| |
| void OmpAttributeVisitor::CheckSourceLabel(const parser::Label &label) { |
| // Get the context to check if the statement causing a jump to the 'label' is |
| // in an enclosing OpenMP construct |
| std::optional<DirContext> thisContext{GetContextIf()}; |
| sourceLabels_.emplace( |
| label, std::make_pair(currentStatementSource_, thisContext)); |
| // Check if the statement with 'label' to which a jump is being introduced |
| // has already been encountered |
| auto it{targetLabels_.find(label)}; |
| if (it != targetLabels_.end()) { |
| // Check if both the statement with 'label' and the statement that causes a |
| // jump to the 'label' are in the same scope |
| CheckLabelContext(currentStatementSource_, it->second.first, thisContext, |
| it->second.second); |
| } |
| } |
| |
| // Check for invalid branch into or out of OpenMP structured blocks |
| void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source, |
| const parser::CharBlock target, std::optional<DirContext> sourceContext, |
| std::optional<DirContext> targetContext) { |
| if (targetContext && |
| (!sourceContext || |
| (sourceContext->scope != targetContext->scope && |
| !DoesScopeContain( |
| &targetContext->scope, sourceContext->scope)))) { |
| context_ |
| .Say(source, "invalid branch into an OpenMP structured block"_err_en_US) |
| .Attach(target, "In the enclosing %s directive branched into"_en_US, |
| parser::ToUpperCaseLetters( |
| llvm::omp::getOpenMPDirectiveName(targetContext->directive) |
| .str())); |
| } |
| if (sourceContext && |
| (!targetContext || |
| (sourceContext->scope != targetContext->scope && |
| !DoesScopeContain( |
| &sourceContext->scope, targetContext->scope)))) { |
| context_ |
| .Say(source, |
| "invalid branch leaving an OpenMP structured block"_err_en_US) |
| .Attach(target, "Outside the enclosing %s directive"_en_US, |
| parser::ToUpperCaseLetters( |
| llvm::omp::getOpenMPDirectiveName(sourceContext->directive) |
| .str())); |
| } |
| } |
| |
| bool OmpAttributeVisitor::HasSymbolInEnclosingScope( |
| const Symbol &symbol, Scope &scope) { |
| const auto symbols{scope.parent().GetSymbols()}; |
| auto it{std::find(symbols.begin(), symbols.end(), symbol)}; |
| return it != symbols.end(); |
| } |
| |
| } // namespace Fortran::semantics |