diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -3487,17 +3487,17 @@ // variable-name-list) // allocate-modifier -> allocator | align struct OmpAllocateClause { - TUPLE_CLASS_BOILERPLATE(OmpAllocateClause); struct AllocateModifier { - UNION_CLASS_BOILERPLATE(AllocateModifier); WRAPPER_CLASS(Allocator, ScalarIntExpr); WRAPPER_CLASS(Align, ScalarIntExpr); struct ComplexModifier { TUPLE_CLASS_BOILERPLATE(ComplexModifier); std::tuple t; }; + UNION_CLASS_BOILERPLATE(AllocateModifier); std::variant u; }; + TUPLE_CLASS_BOILERPLATE(OmpAllocateClause); std::tuple, OmpObjectList> t; }; 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 @@ -566,8 +566,9 @@ // OpenMP miscellaneous flags OmpCommonBlock, OmpReduction, OmpAligned, OmpNontemporal, OmpAllocate, OmpDeclarativeAllocateDirective, OmpExecutableAllocateDirective, - OmpDeclareSimd, OmpDeclareTarget, OmpThreadprivate, OmpDeclareReduction, - OmpFlushed, OmpCriticalLock, OmpIfSpecified, OmpNone, OmpPreDetermined); + OmpAllocatorsConstruct, OmpDeclareSimd, OmpDeclareTarget, + OmpThreadprivate, OmpDeclareReduction, OmpFlushed, OmpCriticalLock, + OmpIfSpecified, OmpNone, OmpPreDetermined); using Flags = common::EnumSet; const Scope &owner() const { return *owner_; } 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 @@ -152,6 +152,8 @@ void Leave(const parser::OpenMPDeclareTargetConstruct &); void Enter(const parser::OpenMPExecutableAllocate &); void Leave(const parser::OpenMPExecutableAllocate &); + void Enter(const parser::OpenMPAllocatorsConstruct &); + void Leave(const parser::OpenMPAllocatorsConstruct &); void Enter(const parser::OpenMPRequiresConstruct &); void Leave(const parser::OpenMPRequiresConstruct &); void Enter(const parser::OpenMPThreadprivate &); 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 @@ -287,11 +287,12 @@ (IsSaved(*symbol) || commonBlock || containingScope.kind() == Scope::Kind::Module)) { context_.Say(source, - "If list items within the ALLOCATE directive have the " + "If list items within the %s 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); + "in the allocator clause"_err_en_US, + ContextDirectiveAsFortran()); } } } @@ -1194,6 +1195,33 @@ dirContext_.pop_back(); } +void OmpStructureChecker::Enter(const parser::OpenMPAllocatorsConstruct &x) { + isPredefinedAllocator = true; + const auto &dir{std::get(x.t)}; + PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocators); + const auto &clauseList{std::get(x.t)}; + for (const auto &clause : clauseList.v) { + if (const auto *allocClause{ + parser::Unwrap(clause)}) { + CheckIsVarPartOfAnotherVar( + dir.source, std::get(allocClause->v.t)); + } + } +} + +void OmpStructureChecker::Leave(const parser::OpenMPAllocatorsConstruct &x) { + const auto &dir{std::get(x.t)}; + const auto &clauseList{std::get(x.t)}; + for (const auto &clause : clauseList.v) { + if (const auto *allocClause{ + std::get_if(&clause.u)}) { + CheckPredefinedAllocatorRestriction( + dir.source, std::get(allocClause->v.t)); + } + } + dirContext_.pop_back(); +} + void OmpStructureChecker::CheckBarrierNesting( const parser::OpenMPSimpleStandaloneConstruct &x) { // A barrier region may not be `closely nested` inside a worksharing, loop, @@ -2163,6 +2191,7 @@ void OmpStructureChecker::CheckIsVarPartOfAnotherVar( const parser::CharBlock &source, const parser::OmpObjectList &objList) { OmpDirectiveSet nonPartialVarSet{llvm::omp::Directive::OMPD_allocate, + llvm::omp::Directive::OMPD_allocators, llvm::omp::Directive::OMPD_threadprivate, llvm::omp::Directive::OMPD_declare_target}; for (const auto &ompObject : objList.v) { 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 @@ -339,6 +339,9 @@ bool Pre(const parser::OpenMPExecutableAllocate &); void Post(const parser::OpenMPExecutableAllocate &); + bool Pre(const parser::OpenMPAllocatorsConstruct &); + void Post(const parser::OpenMPAllocatorsConstruct &); + // 2.15.3 Data-Sharing Attribute Clauses void Post(const parser::OmpDefaultClause &); bool Pre(const parser::OmpClause::Shared &x) { @@ -580,6 +583,11 @@ sourceLabels_.clear(); targetLabels_.clear(); }; + void CheckAllNamesInAllocateStmt(const parser::CharBlock &source, + const parser::OmpObjectList &ompObjectList, + const parser::AllocateStmt &allocate); + void CheckNameInAllocateStmt(const parser::CharBlock &source, + const parser::Name &ompObject, const parser::AllocateStmt &allocate); bool HasSymbolInEnclosingScope(const Symbol &, Scope &); std::int64_t ordCollapseLevel{0}; @@ -1487,6 +1495,20 @@ return true; } +bool OmpAttributeVisitor::Pre(const parser::OpenMPAllocatorsConstruct &x) { + PushContext(x.source, llvm::omp::Directive::OMPD_allocators); + /*const auto &clauseList{std::get(x.t)}; + for (const auto &clause : clauseList.v) { + if (const auto *allocClause{ + std::get_if(&clause.u)}) { + if () + ResolveOmpObjectList(std::get(allocClause->v.t), + Symbol::Flag::OmpAllocatorsConstruct); + } + }*/ + return true; +} + void OmpAttributeVisitor::Post(const parser::OmpDefaultClause &x) { if (!dirContext_.empty()) { switch (x.v) { @@ -1539,6 +1561,36 @@ PopContext(); } +void OmpAttributeVisitor::Post(const parser::OpenMPAllocatorsConstruct &x) { + const auto &dir{std::get(x.t)}; + const auto &clauseList{std::get(x.t)}; + for (const auto &clause : clauseList.v) { + if (const auto *alloc{ + std::get_if(&clause.u)}) { + CheckAllNamesInAllocateStmt(dir.source, + std::get(alloc->v.t), + std::get>(x.t).statement); + + const auto &allocMod{ + std::get>( + alloc->v.t)}; + // TODO: As with allocate directive, exclude the case when a requires + // directive with the dynamic_allocators clause is present in + // the same compilation unit (OMP5.0 2.11.3). + if (IsNestedInDirective(llvm::omp::Directive::OMPD_target) && + (!allocMod.has_value() || + std::holds_alternative< + parser::OmpAllocateClause::AllocateModifier::Align>( + allocMod->u))) { + context_.Say(x.source, + "ALLOCATORS directives that appear in a TARGET region " + "must specify an allocator"_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) { @@ -1706,11 +1758,16 @@ } if ((ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective || ompFlag == - Symbol::Flag::OmpExecutableAllocateDirective) && + Symbol::Flag::OmpExecutableAllocateDirective || + ompFlag == Symbol::Flag::OmpAllocatorsConstruct) && 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); + "in which the %s directive appears"_err_en_US, + parser::ToUpperCaseLetters( + llvm::omp::getOpenMPDirectiveName( + GetContext().directive) + .str())); } } } else { @@ -1959,4 +2016,46 @@ return llvm::is_contained(symbols, symbol); } +// Goes through the names in an OmpObjectList and checks if each name appears +// in the given allocate statement +void OmpAttributeVisitor::CheckAllNamesInAllocateStmt( + const parser::CharBlock &source, const parser::OmpObjectList &ompObjectList, + const parser::AllocateStmt &allocate) { + for (const auto &obj : ompObjectList.v) { + common::visit( + common::visitors{ + [&](const parser::Designator &d) { + if (const auto *ref{std::get_if(&d.u)}) { + if (const auto *n{std::get_if(&ref->u)}) { + CheckNameInAllocateStmt(source, *n, allocate); + } + } + }, + [&](const parser::Name &n) { + CheckNameInAllocateStmt(source, n, allocate); + }, + }, + obj.u); + } +} + +void OmpAttributeVisitor::CheckNameInAllocateStmt( + const parser::CharBlock &source, const parser::Name &name, + const parser::AllocateStmt &allocate) { + for (const auto &allocation : + std::get>(allocate.t)) { + const auto &allocObj = std::get(allocation.t); + if (const auto *n{std::get_if(&allocObj.u)}) { + if (n->source == name.source) { + return; + } + } + } + context_.Say(source, + "Object '%s' in %s directive not " + "found in corresponding ALLOCATE statement"_err_en_US, + name.ToString(), + parser::ToUpperCaseLetters( + llvm::omp::getOpenMPDirectiveName(GetContext().directive).str())); +} } // namespace Fortran::semantics diff --git a/flang/test/Semantics/OpenMP/allocators01.f90 b/flang/test/Semantics/OpenMP/allocators01.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/OpenMP/allocators01.f90 @@ -0,0 +1,21 @@ +! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp +! OpenMP Version 5.2 +! 6.7 allocators construct +! A list item that appears in an allocate clause must appear as +! one of the variables that is allocated by the allocate-stmt in +! the associated allocator structured block. + +subroutine allocate() +use omp_lib + + integer, allocatable :: arr1(:), arr2(:, :), arr3(:), arr4(:, :) + + !$omp allocators allocate(arr3) + allocate(arr3(3), arr4(4, 4)) + !$omp end allocators + + !ERROR: Object 'arr1' in ALLOCATORS directive not found in corresponding ALLOCATE statement + !$omp allocators allocate(omp_default_mem_alloc: arr1, arr2) + allocate(arr2(2, 2)) + +end subroutine allocate diff --git a/flang/test/Semantics/OpenMP/allocators02.f90 b/flang/test/Semantics/OpenMP/allocators02.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/OpenMP/allocators02.f90 @@ -0,0 +1,23 @@ +! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp +! OpenMP Version 5.2 +! 6.7 allocators constructs +! allocators directives that appear in a target region must specify an allocator +! clause unless a requires directive with the dynamic_allocators clause is present +! in the same compilation unit. + +subroutine allocate() +use omp_lib + integer, allocatable :: arr(:, :) + + !$omp target private(arr) + !$omp allocators allocate(omp_default_mem_alloc: arr) + allocate(arr(2, 2)) + !$omp end target + + !$omp target private(arr) + !ERROR: ALLOCATORS directives that appear in a TARGET region must specify an allocator + !$omp allocators allocate(arr) + allocate(arr(2, 2)) + !$omp end target + +end subroutine allocate diff --git a/flang/test/Semantics/OpenMP/allocators03.f90 b/flang/test/Semantics/OpenMP/allocators03.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/OpenMP/allocators03.f90 @@ -0,0 +1,20 @@ +! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp +! OpenMP Version 5.2 +! 6.7 allocators construct +! A variable that is part of another variable (as an array or +! structure element) cannot appear in an allocatprs construct. + +subroutine allocate() +use omp_lib + + type my_type + integer, allocatable :: array(:) + end type my_type + + type(my_type) :: my_var + + !ERROR: A variable that is part of another variable (as an array or structure element) cannot appear on the ALLOCATORS directive + !$omp allocators allocate(my_var%array) + allocate(my_var%array(10)) + +end subroutine allocate diff --git a/flang/test/Semantics/OpenMP/allocators04.f90 b/flang/test/Semantics/OpenMP/allocators04.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/OpenMP/allocators04.f90 @@ -0,0 +1,15 @@ +! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp +! OpenMP Version 5.2 +! 6.7 allocators construct +! Only the allocate clause is allowed on the allocators construct + +subroutine allocate() +use omp_lib + + integer, allocatable :: arr1(:), arr2(:) + + !ERROR: PRIVATE clause is not allowed on the ALLOCATORS directive + !$omp allocators allocate(arr1) private(arr2) + allocate(arr1(23), arr2(2)) + +end subroutine allocate diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td --- a/llvm/include/llvm/Frontend/OpenMP/OMP.td +++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td @@ -1695,6 +1695,11 @@ VersionedClause ]; } +def OMP_Allocators : Directive<"allocators"> { + let allowedClauses = [ + VersionedClause + ]; +} def OMP_DeclareVariant : Directive<"declare variant"> { let allowedClauses = [ VersionedClause