diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index 73956338a31fe..5ee7ecc61a4e2 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -3544,17 +3544,17 @@ struct OmpInReductionClause { // 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/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp index b4b838a45f7ea..b26f3e23f9ec7 100644 --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -311,11 +311,12 @@ void OmpStructureChecker::CheckPredefinedAllocatorRestriction( (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()); } } } @@ -1140,6 +1141,39 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Allocator &x) { RequiresPositiveParameter(llvm::omp::Clause::OMPC_allocator, x.v); } +void OmpStructureChecker::Enter(const parser::OmpClause::Allocate &x) { + CheckAllowed(llvm::omp::Clause::OMPC_allocate); + if (const auto &modifier{ + std::get>( + x.v.t)}) { + common::visit( + common::visitors{ + [&](const parser::OmpAllocateClause::AllocateModifier::Allocator + &y) { + RequiresPositiveParameter(llvm::omp::Clause::OMPC_allocate, y.v); + isPredefinedAllocator = GetIntValue(y.v).has_value(); + }, + [&](const parser::OmpAllocateClause::AllocateModifier:: + ComplexModifier &y) { + const auto &alloc = std::get< + parser::OmpAllocateClause::AllocateModifier::Allocator>(y.t); + const auto &align = + std::get( + y.t); + RequiresPositiveParameter( + llvm::omp::Clause::OMPC_allocate, alloc.v); + RequiresPositiveParameter( + llvm::omp::Clause::OMPC_allocate, align.v); + isPredefinedAllocator = GetIntValue(alloc.v).has_value(); + }, + [&](const parser::OmpAllocateClause::AllocateModifier::Align &y) { + RequiresPositiveParameter(llvm::omp::Clause::OMPC_allocate, y.v); + }, + }, + modifier->u); + } +} + void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) { const auto &dir{std::get(x.t)}; PushContext(dir.source, llvm::omp::Directive::OMPD_declare_target); @@ -1218,6 +1252,33 @@ void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate &x) { 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, @@ -1893,7 +1954,6 @@ CHECK_SIMPLE_CLAUSE(AcqRel, OMPC_acq_rel) CHECK_SIMPLE_CLAUSE(Acquire, OMPC_acquire) CHECK_SIMPLE_CLAUSE(AtomicDefaultMemOrder, OMPC_atomic_default_mem_order) CHECK_SIMPLE_CLAUSE(Affinity, OMPC_affinity) -CHECK_SIMPLE_CLAUSE(Allocate, OMPC_allocate) CHECK_SIMPLE_CLAUSE(Capture, OMPC_capture) CHECK_SIMPLE_CLAUSE(Default, OMPC_default) CHECK_SIMPLE_CLAUSE(Depobj, OMPC_depobj) @@ -2189,6 +2249,7 @@ bool OmpStructureChecker::IsDataRefTypeParamInquiry( 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/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h index b99a19013c8c1..195b0c2326f54 100644 --- a/flang/lib/Semantics/check-omp-structure.h +++ b/flang/lib/Semantics/check-omp-structure.h @@ -152,6 +152,8 @@ class OmpStructureChecker 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/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp index 401ffee6badc9..747f1e414eb21 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -364,6 +364,9 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor { 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) { @@ -606,6 +609,11 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor { 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}; @@ -1548,6 +1556,19 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPExecutableAllocate &x) { 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)}) { + ResolveOmpObjectList(std::get(allocClause->v.t), + Symbol::Flag::OmpExecutableAllocateDirective); + } + } + return true; +} + void OmpAttributeVisitor::Post(const parser::OmpDefaultClause &x) { if (!dirContext_.empty()) { switch (x.v) { @@ -1600,6 +1621,36 @@ void OmpAttributeVisitor::Post(const parser::OpenMPExecutableAllocate &x) { 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) { @@ -1772,7 +1823,11 @@ void OmpAttributeVisitor::ResolveOmpObject( 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 { @@ -2021,4 +2076,39 @@ bool OmpAttributeVisitor::HasSymbolInEnclosingScope( 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) { + if (const auto *d{std::get_if(&obj.u)}) { + if (const auto *ref{std::get_if(&d->u)}) { + if (const auto *n{std::get_if(&ref->u)}) { + CheckNameInAllocateStmt(source, *n, allocate); + } + } + } + } +} + +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/allocate-clause01.f90 b/flang/test/Semantics/OpenMP/allocate-clause01.f90 new file mode 100644 index 0000000000000..7e2f64397f738 --- /dev/null +++ b/flang/test/Semantics/OpenMP/allocate-clause01.f90 @@ -0,0 +1,22 @@ +! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp +! OpenMP Version 5.2 +! The allocate clause's allocator modifier must be of type allocator_handle +! and the align modifier must be constant, positive integer expression + +subroutine allocate() + use omp_lib + + integer, allocatable :: a, b, c + + !ERROR: The parameter of the ALLOCATE clause must be a positive integer expression + !$omp allocators allocate(-1: a) + allocate(a) + + !ERROR: The parameter of the ALLOCATE clause must be a positive integer expression + !$omp allocators allocate(allocator(-2), align(-3): b) + allocate(b) + + !ERROR: The parameter of the ALLOCATE clause must be a positive integer expression + !$omp allocators allocate(align(-4): c) + allocate(c) +end subroutine diff --git a/flang/test/Semantics/OpenMP/allocators01.f90 b/flang/test/Semantics/OpenMP/allocators01.f90 new file mode 100644 index 0000000000000..d674438b2ab20 --- /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 index 0000000000000..32e7bc18ad99b --- /dev/null +++ b/flang/test/Semantics/OpenMP/allocators02.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/allocators03.f90 b/flang/test/Semantics/OpenMP/allocators03.f90 new file mode 100644 index 0000000000000..533434c2ace0f --- /dev/null +++ b/flang/test/Semantics/OpenMP/allocators03.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/flang/test/Semantics/OpenMP/allocators04.f90 b/flang/test/Semantics/OpenMP/allocators04.f90 new file mode 100644 index 0000000000000..d4467a2052a7d --- /dev/null +++ b/flang/test/Semantics/OpenMP/allocators04.f90 @@ -0,0 +1,29 @@ +! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp +! OpenMP Version 5.2 +! Inherited from 2.11.3 allocate Directive +! If list items within the ALLOCATE 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 +! SAVE and common block names can't be declared as allocatable, only module scope variables are tested + +module AllocateModule + integer, allocatable :: a, b +end module + +subroutine allocate() + use omp_lib + use AllocateModule + + integer(kind=omp_allocator_handle_kind) :: custom_allocator + type(omp_alloctrait) :: trait(1) + + trait(1)%key = fallback + trait(1)%value = default_mem_fb + custom_allocator = omp_init_allocator(omp_default_mem_space, 1, trait) + + !$omp allocators allocate(omp_default_mem_alloc: a) + allocate(a) + + !ERROR: If list items within the ALLOCATORS 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 + !$omp allocators allocate(custom_allocator: b) + allocate(b) +end subroutine diff --git a/flang/test/Semantics/OpenMP/allocators05.f90 b/flang/test/Semantics/OpenMP/allocators05.f90 new file mode 100644 index 0000000000000..684eef6950495 --- /dev/null +++ b/flang/test/Semantics/OpenMP/allocators05.f90 @@ -0,0 +1,24 @@ +! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp +! OpenMP Version 5.2 +! Inherited from 2.11.3 allocate directive +! allocate 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 :: i + integer, allocatable :: a(:), b(:) + integer, parameter :: LEN = 2 + + !$omp target private(a, b) + !ERROR: List items must be declared in the same scoping unit in which the ALLOCATORS directive appears + !$omp allocators allocate(omp_default_mem_alloc: a) + allocate(a(LEN)) + !ERROR: ALLOCATORS directives that appear in a TARGET region must specify an allocator + !ERROR: List items must be declared in the same scoping unit in which the ALLOCATORS directive appears + !$omp allocators allocate(b) + allocate(b(LEN)) + !$omp end target +end subroutine diff --git a/flang/test/Semantics/OpenMP/allocators06.f90 b/flang/test/Semantics/OpenMP/allocators06.f90 new file mode 100644 index 0000000000000..17e168d4cfe0c --- /dev/null +++ b/flang/test/Semantics/OpenMP/allocators06.f90 @@ -0,0 +1,16 @@ +! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp +! OpenMP Version 5.2 +! Inherited from 2.11.3 allocate directive +! The allocate directive must appear in the same scope as the declarations of +! each of its list items and must follow all such declarations. + +subroutine allocate() + use omp_lib + integer, allocatable :: a +contains + subroutine test() + !ERROR: List items must be declared in the same scoping unit in which the ALLOCATORS directive appears + !$omp allocators allocate(omp_default_mem_alloc: a) + allocate(a) + end subroutine +end subroutine diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td index 68f7eca4daffb..f7a980476bac0 100644 --- a/llvm/include/llvm/Frontend/OpenMP/OMP.td +++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td @@ -1727,6 +1727,11 @@ def OMP_Allocate : Directive<"allocate"> { VersionedClause ]; } +def OMP_Allocators : Directive<"allocators"> { + let allowedClauses = [ + VersionedClause + ]; +} def OMP_DeclareVariant : Directive<"declare variant"> { let allowedClauses = [ VersionedClause