diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h --- a/flang/include/flang/Semantics/symbol.h +++ b/flang/include/flang/Semantics/symbol.h @@ -504,9 +504,11 @@ // OpenMP data-copying attribute OmpCopyIn, // OpenMP miscellaneous flags - OmpCommonBlock, OmpReduction, OmpAllocate, OmpAllocateDirective, + OmpCommonBlock, OmpReduction, OmpAllocate, + OmpDeclarativeAllocateDirective, OmpExecutableAllocateDirective, OmpDeclareSimd, OmpDeclareTarget, OmpThreadprivate, OmpDeclareReduction, - OmpFlushed, OmpCriticalLock, OmpIfSpecified, OmpNone, OmpPreDetermined, OmpAligned); + OmpFlushed, OmpCriticalLock, OmpIfSpecified, OmpNone, OmpPreDetermined, + OmpAligned); using Flags = common::EnumSet; const Scope &owner() const { return *owner_; } diff --git a/flang/include/flang/Semantics/tools.h b/flang/include/flang/Semantics/tools.h --- a/flang/include/flang/Semantics/tools.h +++ b/flang/include/flang/Semantics/tools.h @@ -131,6 +131,9 @@ inline bool IsAllocatableOrPointer(const Symbol &symbol) { return IsPointer(symbol) || IsAllocatable(symbol); } +inline bool IsSave(const Symbol &symbol) { + return symbol.attrs().test(Attr::SAVE); +} inline bool IsNamedConstant(const Symbol &symbol) { return symbol.attrs().test(Attr::PARAMETER); } diff --git a/flang/lib/Parser/type-parsers.h b/flang/lib/Parser/type-parsers.h --- a/flang/lib/Parser/type-parsers.h +++ b/flang/lib/Parser/type-parsers.h @@ -92,7 +92,6 @@ constexpr Parser expr; // R1022 constexpr Parser specificationExpr; // R1028 constexpr Parser assignmentStmt; // R1032 -constexpr Parser allocateStmt; constexpr Parser pointerAssignmentStmt; // R1033 constexpr Parser whereStmt; // R1041, R1045, R1046 constexpr Parser whereConstruct; // R1042 diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h --- a/flang/lib/Semantics/check-omp-structure.h +++ b/flang/lib/Semantics/check-omp-structure.h @@ -143,7 +143,6 @@ void Enter(const parser::OmpClause::NumTasks &); void Enter(const parser::OmpClause::NumTeams &); void Enter(const parser::OmpClause::NumThreads &); - void Enter(const parser::OmpClause::Allocator &); void Enter(const parser::OmpClause::Ordered &); void Enter(const parser::OmpClause::Priority &); void Enter(const parser::OmpClause::Private &); @@ -179,6 +178,9 @@ void CheckObjectListStructure( const parser::CharBlock &source, const parser::OmpObjectList &objList); + void CheckRestriction( + const parser::CharBlock &source, const parser::OmpObjectList &objList); + private: bool HasInvalidWorksharingNesting( const parser::CharBlock &, const OmpDirectiveSet &); diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -179,7 +179,9 @@ dirContext_.pop_back(); } +bool isPredefinedAllocator = true; void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate &x) { + isPredefinedAllocator = true; const auto &dir{std::get(x.t)}; const auto &objectList{std::get(x.t)}; PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate); @@ -187,6 +189,9 @@ } void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate &x) { + const auto &dir{std::get(x.t)}; + const auto &objectList{std::get(x.t)}; + CheckRestriction(dir.source, objectList); dirContext_.pop_back(); } @@ -213,6 +218,40 @@ } } +void OmpStructureChecker::CheckRestriction( + const parser::CharBlock &source, const parser::OmpObjectList &objList) { + for (const auto &ompObject : objList.v) { + std::visit( + common::visitors{ + [&](const parser::Designator &designator) { + if (const auto *dataRef{ + std::get_if(&designator.u)}) { + if (const auto *name{std::get_if(&dataRef->u)}) { + if (const auto *symbol{name->symbol}) { + const auto *commonBlock{FindCommonBlockContaining(*symbol)}; + const auto &scope{context_.FindScope(source)}; + const Scope &containingScope{ + GetProgramUnitContaining(scope)}; + if (!isPredefinedAllocator && + (IsSave(*symbol) || commonBlock || + containingScope.kind() == Scope::Kind::Module)) { + context_.Say(source, + "If list items within the ALLCOATE directive have " + "the " + "SAVE attribute, are a common block name, or are " + "declared in the scope of a module, " + "then only predefined memory allocator parameters can be used in the allocator clause."_err_en_US); + } + } + } + } + }, + [&](const parser::Name &name) {}, + }, + ompObject.u); + } +} + void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) { const auto &dir{std::get(x.t)}; PushContext(dir.source, llvm::omp::Directive::OMPD_declare_target); @@ -227,11 +266,19 @@ } void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) { + isPredefinedAllocator = true; const auto &dir{std::get(x.t)}; + const auto &objectList{std::get>(x.t)}; PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate); + if (objectList) + CheckObjectListStructure(dir.source, *objectList); } -void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate &) { +void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate &x) { + const auto &dir{std::get(x.t)}; + const auto &objectList{std::get>(x.t)}; + if (objectList) + CheckRestriction(dir.source, *objectList); dirContext_.pop_back(); } @@ -425,7 +472,7 @@ CHECK_SIMPLE_CLAUSE(Release, OMPC_release) CHECK_SIMPLE_CLAUSE(Relaxed, OMPC_relaxed) -CHECK_REQ_SCALAR_INT_CLAUSE(Allocator, OMPC_allocator) +// CHECK_REQ_SCALAR_INT_CLAUSE(Allocator, OMPC_allocator) CHECK_REQ_SCALAR_INT_CLAUSE(Grainsize, OMPC_grainsize) CHECK_REQ_SCALAR_INT_CLAUSE(NumTasks, OMPC_num_tasks) CHECK_REQ_SCALAR_INT_CLAUSE(NumTeams, OMPC_num_teams) @@ -505,6 +552,15 @@ } // 2.8.1 TODO: list-item attribute check } + +void OmpStructureChecker::Enter(const parser::OmpClause::Allocator &x) { + CheckAllowed(llvm::omp::Clause::OMPC_allocator); + const auto allocatorValue{GetIntValue(x.v)}; + if (*allocatorValue > 8) + isPredefinedAllocator = false; + RequiresPositiveParameter(llvm::omp::Clause::OMPC_allocator, x.v); +} + void OmpStructureChecker::Enter(const parser::OmpDefaultmapClause &x) { CheckAllowed(llvm::omp::Clause::OMPC_defaultmap); using VariableCategory = parser::OmpDefaultmapClause::VariableCategory; diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -18,6 +18,7 @@ #include "flang/Parser/parse-tree.h" #include "flang/Parser/tools.h" #include "flang/Semantics/expression.h" +#include "flang/Semantics/tools.h" #include #include @@ -233,6 +234,8 @@ bool Pre(const parser::OpenMPBlockConstruct &); void Post(const parser::OpenMPBlockConstruct &); + bool Pre(const parser::ExecutionPartConstruct &); + void Post(const parser::OmpBeginBlockDirective &) { GetContext().withinConstruct = true; } @@ -265,6 +268,8 @@ bool Pre(const parser::OpenMPExecutableAllocate &); void Post(const parser::OpenMPExecutableAllocate &); + bool Pre(const parser::AllocateObject &); + // 2.15.3 Data-Sharing Attribute Clauses void Post(const parser::OmpDefaultClause &); bool Pre(const parser::OmpClause::Shared &x) { @@ -348,16 +353,30 @@ static constexpr Symbol::Flags dataCopyingAttributeFlags{ Symbol::Flag::OmpCopyIn}; + std::vector allocateList_; // on one directive std::vector allocateNames_; // on one directive + std::vector + executableAllocateNames_; // on one directive SymbolSet privateDataSharingAttributeObjects_; // on one directive bool inTarget = false; bool hasAllocator = false; + bool executable = false; void AddAllocateName(const parser::Name *&object) { allocateNames_.push_back(object); } void ClearAllocateNames() { allocateNames_.clear(); } + void AddExecutableAllocateName(const parser::Name *&object) { + executableAllocateNames_.push_back(object); + } + void ClearExecutableAllocateNames() { executableAllocateNames_.clear(); } + + void AddAllocateListName(const parser::Name *object) { + allocateList_.push_back(object); + } + void ClearAllocateListNames() { allocateList_.clear(); } + void AddPrivateDataSharingAttributeObjects(SymbolRef object) { privateDataSharingAttributeObjects_.insert(object); } @@ -762,6 +781,11 @@ } } +bool OmpAttributeVisitor::Pre(const parser::ExecutionPartConstruct &x) { + executable = true; + return true; +} + bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) { inTarget = false; const auto &beginBlockDir{std::get(x.t)}; @@ -1021,16 +1045,32 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclarativeAllocate &x) { PushContext(x.source, llvm::omp::Directive::OMPD_allocate); const auto &list{std::get(x.t)}; - ResolveOmpObjectList(list, Symbol::Flag::OmpAllocateDirective); + ResolveOmpObjectList(list, Symbol::Flag::OmpDeclarativeAllocateDirective); return false; } +bool OmpAttributeVisitor::Pre(const parser::AllocateObject &x) { + const auto &name{std::get(x.u)}; + AddAllocateListName(&name); + return true; +} + bool OmpAttributeVisitor::Pre(const parser::OpenMPExecutableAllocate &x) { hasAllocator = false; PushContext(x.source, llvm::omp::Directive::OMPD_allocate); const auto &list{std::get>(x.t)}; + const auto &declarativeList{ + std::get>>( + x.t)}; + if (declarativeList && !list) { + context_.Say(x.source, + "Multiple directives can only be associated with an allocate statement" + " if list items are specified on each allocate directive"_err_en_US); + } if (list) - ResolveOmpObjectList(*list, Symbol::Flag::OmpAllocateDirective); + ResolveOmpObjectList(*list, Symbol::Flag::OmpExecutableAllocateDirective); + /*const auto + * &list{std::get>>(x.t)};*/ return true; } @@ -1061,6 +1101,27 @@ context_.Say(x.source, "ALLOCATE directives that appear in a TARGET region " "must specify an allocator clause"_err_en_US); + + bool allocated; + for (const auto *allocName : allocateList_) { + allocated = false; + for (const auto *allocExecName : executableAllocateNames_) { + if (allocName->source == allocExecName->source) { + allocated = true; + } + } + if (!allocated) { + context_.Say(allocName->source, + "List items specified in an ALLOCATE directive that is associated " + "with " + "an allocate statement must be variables that are allocated by the " + "allocate statement. The ALLOCATE directive requires " + "that '%s' must be allocated by the allocate statement."_err_en_US, + allocName->ToString()); + } + } + + executable = false; PopContext(); } @@ -1178,8 +1239,21 @@ AddAllocateName(name); } } + if (ompFlag == Symbol::Flag::OmpExecutableAllocateDirective || + ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective) { + AddExecutableAllocateName(name); + } + const auto *commonBlock{FindCommonBlockContaining(*symbol)}; + if (IsAllocatable(*symbol) && !executable) { + context_.Say(designator.source, // 2.15.3 + "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 (ResolveOmpObjectScope(name) == nullptr && - ompFlag == Symbol::Flag::OmpAllocateDirective) { + (ompFlag == Symbol::Flag::OmpExecutableAllocateDirective || + ompFlag == + Symbol::Flag::OmpDeclarativeAllocateDirective)) { 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);