diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp --- a/flang/lib/Parser/unparse.cpp +++ b/flang/lib/Parser/unparse.cpp @@ -2325,6 +2325,14 @@ EndOpenMP(); } void Unparse(const OpenMPExecutableAllocate &x) { + const auto &fields = + std::get>>( + x.t); + if (fields) { + for (const auto &decl : *fields) { + Walk(decl); + } + } BeginOpenMP(); Word("!$OMP ALLOCATE"); Walk(" (", std::get>(x.t), ")"); diff --git a/flang/lib/Semantics/canonicalize-omp.cpp b/flang/lib/Semantics/canonicalize-omp.cpp --- a/flang/lib/Semantics/canonicalize-omp.cpp +++ b/flang/lib/Semantics/canonicalize-omp.cpp @@ -15,7 +15,9 @@ // 1. move structured DoConstruct and OmpEndLoopDirective into // OpenMPLoopConstruct. Compilation will not proceed in case of errors // after this pass. -// 2. TBD +// 2. Associate declarative OMP allocation directives with their +// respective executable allocation directive +// 3. TBD namespace Fortran::semantics { using namespace parser::literals; @@ -46,6 +48,8 @@ } // Block list } + void Post(parser::ExecutionPart &body) { RewriteOmpAllocations(body); } + private: template T *GetConstructIf(parser::ExecutionPartConstruct &x) { if (auto *y{std::get_if(&x.u)}) { @@ -56,6 +60,15 @@ return nullptr; } + template T *GetOmpIf(parser::ExecutionPartConstruct &x) { + if (auto *construct{GetConstructIf(x)}) { + if (auto *omp{std::get_if(&construct->u)}) { + return omp; + } + } + return nullptr; + } + void RewriteOpenMPLoopConstruct(parser::OpenMPLoopConstruct &x, parser::Block &block, parser::Block::iterator it) { // Check the sequence of DoConstruct and OmpEndLoopDirective @@ -106,6 +119,36 @@ parser::ToUpperCaseLetters(dir.source.ToString())); } + void RewriteOmpAllocations(parser::ExecutionPart &body) { + // Rewrite leading declarative allocations so they are nested + // within their respective executable allocate directive + // + // Original: + // ExecutionPartConstruct -> OpenMPDeclarativeAllocate + // ExecutionPartConstruct -> OpenMPDeclarativeAllocate + // ExecutionPartConstruct -> OpenMPExecutableAllocate + // + // After rewriting: + // ExecutionPartConstruct -> OpenMPExecutableAllocate + // ExecutionPartConstruct -> OpenMPDeclarativeAllocate + // ExecutionPartConstruct -> OpenMPDeclarativeAllocate + for (auto it = body.v.rbegin(); it != body.v.rend();) { + if (auto *exec = GetOmpIf(*(it++))) { + parser::OpenMPDeclarativeAllocate *decl; + std::list subAllocates; + while (it != body.v.rend() && + (decl = GetOmpIf(*it))) { + subAllocates.push_front(std::move(*decl)); + it = decltype(it)(body.v.erase(std::next(it).base())); + } + if (!subAllocates.empty()) { + std::get>>( + exec->t) = {std::move(subAllocates)}; + } + } + } + } + parser::Messages &messages_; }; 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 @@ -1686,7 +1686,8 @@ } } if (ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective && - IsAllocatable(*symbol)) { + IsAllocatable(*symbol) && + !IsNestedInDirective(llvm::omp::Directive::OMPD_allocate)) { context_.Say(designator.source, "List items specified in the ALLOCATE directive must not " "have the ALLOCATABLE attribute unless the directive is " diff --git a/flang/test/Parser/OpenMP/allocate-tree-spec-part.f90 b/flang/test/Parser/OpenMP/allocate-tree-spec-part.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Parser/OpenMP/allocate-tree-spec-part.f90 @@ -0,0 +1,65 @@ +! RUN: %flang_fc1 -fopenmp -fdebug-dump-parse-tree %s | FileCheck %s +! Ensures associated declarative OMP allocations in the specification +! part are kept there + +program allocate_tree + use omp_lib + integer, allocatable :: zarray(:, :) + integer :: z, t, f +!$omp allocate(f) allocator(omp_default_mem_alloc) + f = 2 +!$omp allocate(zarray) allocator(omp_const_mem_alloc) +!$omp allocate(z) allocator(omp_large_cap_mem_alloc) +!$omp allocate(a) allocator(omp_default_mem_alloc) +!$omp allocate + allocate (zarray(z,t)) +end program allocate_tree + +!CHECK: | SpecificationPart +!CHECK-NEXT: | | UseStmt +!CHECK-NEXT: | | | Name = 'omp_lib' +!CHECK-NEXT: | | ImplicitPart -> +!CHECK-NEXT: | | DeclarationConstruct -> SpecificationConstruct -> TypeDeclarationStmt +!CHECK-NEXT: | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec -> +!CHECK-NEXT: | | | AttrSpec -> Allocatable +!CHECK-NEXT: | | | EntityDecl +!CHECK-NEXT: | | | | Name = 'zarray' +!CHECK-NEXT: | | | | ArraySpec -> DeferredShapeSpecList -> int +!CHECK-NEXT: | | DeclarationConstruct -> SpecificationConstruct -> TypeDeclarationStmt +!CHECK-NEXT: | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec -> +!CHECK-NEXT: | | | EntityDecl +!CHECK-NEXT: | | | | Name = 'z' +!CHECK-NEXT: | | | EntityDecl +!CHECK-NEXT: | | | | Name = 't' +!CHECK-NEXT: | | | EntityDecl +!CHECK-NEXT: | | | | Name = 'f' +!CHECK-NEXT: | | DeclarationConstruct -> SpecificationConstruct -> OpenMPDeclarativeConstruct -> OpenMPDeclarativeAllocate +!CHECK-NEXT: | | | Verbatim +!CHECK-NEXT: | | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'f' +!CHECK-NEXT: | | | OmpClauseList -> OmpClause -> Allocator -> Scalar -> Integer -> Expr = +!CHECK-NEXT: | | | | Designator -> DataRef -> Name = 'omp_default_mem_alloc' +!CHECK-NEXT: | ExecutionPart -> Block +!CHECK-NEXT: | | ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> AssignmentStmt = +!CHECK-NEXT: | | | Variable = 'f' +!CHECK-NEXT: | | | | Designator -> DataRef -> Name = 'f' +!CHECK-NEXT: | | | Expr = +!CHECK-NEXT: | | | | LiteralConstant -> IntLiteralConstant = '2' +!CHECK-NEXT: | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPExecutableAllocate +!CHECK-NEXT: | | | Verbatim +!CHECK-NEXT: | | | OmpClauseList -> +!CHECK-NEXT: | | | OpenMPDeclarativeAllocate +!CHECK-NEXT: | | | | Verbatim +!CHECK-NEXT: | | | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'zarray' +!CHECK-NEXT: | | | | OmpClauseList -> OmpClause -> Allocator -> Scalar -> Integer -> Expr = +!CHECK-NEXT: | | | | | Designator -> DataRef -> Name = 'omp_const_mem_alloc' +!CHECK-NEXT: | | | OpenMPDeclarativeAllocate +!CHECK-NEXT: | | | | Verbatim +!CHECK-NEXT: | | | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'z' +!CHECK-NEXT: | | | | OmpClauseList -> OmpClause -> Allocator -> Scalar -> Integer -> Expr = +!CHECK-NEXT: | | | | | Designator -> DataRef -> Name = 'omp_large_cap_mem_alloc' +!CHECK-NEXT: | | | OpenMPDeclarativeAllocate +!CHECK-NEXT: | | | | Verbatim +!CHECK-NEXT: | | | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'a' +!CHECK-NEXT: | | | | OmpClauseList -> OmpClause -> Allocator -> Scalar -> Integer -> Expr = +!CHECK-NEXT: | | | | | Designator -> DataRef -> Name = 'omp_default_mem_alloc' +!CHECK-NEXT: | | | AllocateStmt diff --git a/flang/test/Parser/OpenMP/allocate-tree.f90 b/flang/test/Parser/OpenMP/allocate-tree.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Parser/OpenMP/allocate-tree.f90 @@ -0,0 +1,40 @@ +! RUN: %flang_fc1 -fopenmp -fdebug-dump-parse-tree %s | FileCheck %s +! Ensures associated declarative OMP allocations are nested in their +! corresponding executable allocate directive + +program allocate_tree + use omp_lib + integer, allocatable :: zarray(:, :) + integer :: z, t + + z = 3 + t = 2 + +!$omp allocate(zarray) allocator(omp_const_mem_alloc) +!$omp allocate(z) allocator(omp_large_cap_mem_alloc) +!$omp allocate(a) allocator(omp_default_mem_alloc) +!$omp allocate + allocate (zarray(z,t)) + +end program allocate_tree + + +!CHECK: | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPExecutableAllocate +!CHECK-NEXT: | | | Verbatim +!CHECK-NEXT: | | | OmpClauseList -> +!CHECK-NEXT: | | | OpenMPDeclarativeAllocate +!CHECK-NEXT: | | | | Verbatim +!CHECK-NEXT: | | | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'zarray' +!CHECK-NEXT: | | | | OmpClauseList -> OmpClause -> Allocator -> Scalar -> Integer -> Expr = +!CHECK-NEXT: | | | | | Designator -> DataRef -> Name = 'omp_const_mem_alloc' +!CHECK-NEXT: | | | OpenMPDeclarativeAllocate +!CHECK-NEXT: | | | | Verbatim +!CHECK-NEXT: | | | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'z' +!CHECK-NEXT: | | | | OmpClauseList -> OmpClause -> Allocator -> Scalar -> Integer -> Expr = +!CHECK-NEXT: | | | | | Designator -> DataRef -> Name = 'omp_large_cap_mem_alloc' +!CHECK-NEXT: | | | OpenMPDeclarativeAllocate +!CHECK-NEXT: | | | | Verbatim +!CHECK-NEXT: | | | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'a' +!CHECK-NEXT: | | | | OmpClauseList -> OmpClause -> Allocator -> Scalar -> Integer -> Expr = +!CHECK-NEXT: | | | | | Designator -> DataRef -> Name = 'omp_default_mem_alloc' +!CHECK-NEXT: | | | AllocateStmt