diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index 61f97b855b0e5..7e752eeb4dfe4 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -267,6 +267,7 @@ struct AccEndCombinedDirective; struct OpenACCDeclarativeConstruct; struct OpenACCRoutineConstruct; struct OpenMPConstruct; +struct OpenMPLoopConstruct; struct OpenMPDeclarativeConstruct; struct OmpEndLoopDirective; struct OmpMemoryOrderClause; @@ -5021,11 +5022,13 @@ struct OpenMPBlockConstruct { }; // OpenMP directives enclosing do loop +using NestedConstruct = + std::variant>; struct OpenMPLoopConstruct { TUPLE_CLASS_BOILERPLATE(OpenMPLoopConstruct); OpenMPLoopConstruct(OmpBeginLoopDirective &&a) : t({std::move(a), std::nullopt, std::nullopt}) {} - std::tuple, + std::tuple, std::optional> t; }; diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp index e21d0effe2ef6..f58ee95eae5ff 100644 --- a/flang/lib/Lower/OpenMP/OpenMP.cpp +++ b/flang/lib/Lower/OpenMP/OpenMP.cpp @@ -4231,6 +4231,16 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, mlir::Location currentLocation = converter.genLocation(beginLoopDirective.source); + auto &optLoopCons = + std::get>(loopConstruct.t); + if (optLoopCons.has_value()) { + if (auto *ompNestedLoopCons{ + std::get_if>( + &*optLoopCons)}) { + genOMP(converter, symTable, semaCtx, eval, ompNestedLoopCons->value()); + } + } + llvm::omp::Directive directive = std::get(beginLoopDirective.t).v; const parser::CharBlock &source = diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp index ed0f227fd5b98..903287515e559 100644 --- a/flang/lib/Parser/unparse.cpp +++ b/flang/lib/Parser/unparse.cpp @@ -2926,7 +2926,8 @@ class UnparseVisitor { Walk(std::get(x.t)); Put("\n"); EndOpenMP(); - Walk(std::get>(x.t)); + Walk(std::get>>>(x.t)); Walk(std::get>(x.t)); } void Unparse(const BasedPointer &x) { diff --git a/flang/lib/Semantics/canonicalize-omp.cpp b/flang/lib/Semantics/canonicalize-omp.cpp index 5164f1dc6faab..1edcb376596b0 100644 --- a/flang/lib/Semantics/canonicalize-omp.cpp +++ b/flang/lib/Semantics/canonicalize-omp.cpp @@ -8,6 +8,7 @@ #include "canonicalize-omp.h" #include "flang/Parser/parse-tree-visitor.h" +#include "flang/Parser/parse-tree.h" // After Loop Canonicalization, rewrite OpenMP parse tree to make OpenMP // Constructs more structured which provide explicit scopes for later @@ -125,6 +126,16 @@ class CanonicalizationOfOmp { parser::Block::iterator nextIt; auto &beginDir{std::get(x.t)}; auto &dir{std::get(beginDir.t)}; + auto missingDoConstruct = [](auto &dir, auto &messages) { + messages.Say(dir.source, + "A DO loop must follow the %s directive"_err_en_US, + parser::ToUpperCaseLetters(dir.source.ToString())); + }; + auto tileUnrollError = [](auto &dir, auto &messages) { + messages.Say(dir.source, + "If a loop construct has been fully unrolled, it cannot then be tiled"_err_en_US, + parser::ToUpperCaseLetters(dir.source.ToString())); + }; nextIt = it; while (++nextIt != block.end()) { @@ -135,31 +146,95 @@ class CanonicalizationOfOmp { if (auto *doCons{GetConstructIf(*nextIt)}) { if (doCons->GetLoopControl()) { // move DoConstruct - std::get>(x.t) = + std::get>>>(x.t) = std::move(*doCons); nextIt = block.erase(nextIt); // try to match OmpEndLoopDirective - if (nextIt != block.end()) { - if (auto *endDir{ - GetConstructIf(*nextIt)}) { - std::get>(x.t) = - std::move(*endDir); - block.erase(nextIt); - } + if (auto *endDir{ + GetConstructIf(*nextIt)}) { + std::get>(x.t) = + std::move(*endDir); + nextIt = block.erase(nextIt); } } else { messages_.Say(dir.source, "DO loop after the %s directive must have loop control"_err_en_US, parser::ToUpperCaseLetters(dir.source.ToString())); } + } else if (auto *ompLoopCons{ + GetOmpIf(*nextIt)}) { + // We should allow UNROLL and TILE constructs to be inserted between an + // OpenMP Loop Construct and the DO loop itself + auto &nestedBeginDirective = + std::get(ompLoopCons->t); + auto &nestedBeginLoopDirective = + std::get(nestedBeginDirective.t); + if ((nestedBeginLoopDirective.v == llvm::omp::Directive::OMPD_unroll || + nestedBeginLoopDirective.v == + llvm::omp::Directive::OMPD_tile) && + !(nestedBeginLoopDirective.v == llvm::omp::Directive::OMPD_unroll && + dir.v == llvm::omp::Directive::OMPD_tile)) { + // iterate through the remaining block items to find the end directive + // for the unroll/tile directive. + parser::Block::iterator endIt; + endIt = nextIt; + while (endIt != block.end()) { + if (auto *endDir{ + GetConstructIf(*endIt)}) { + auto &endLoopDirective = + std::get(endDir->t); + if (endLoopDirective.v == dir.v) { + std::get>(x.t) = + std::move(*endDir); + endIt = block.erase(endIt); + continue; + } + } + ++endIt; + } + RewriteOpenMPLoopConstruct(*ompLoopCons, block, nextIt); + auto &ompLoop = std::get>(x.t); + ompLoop = + std::optional{parser::NestedConstruct{ + common::Indirection{std::move(*ompLoopCons)}}}; + nextIt = block.erase(nextIt); + } else if (nestedBeginLoopDirective.v == + llvm::omp::Directive::OMPD_unroll && + dir.v == llvm::omp::Directive::OMPD_tile) { + // if a loop has been unrolled, the user can not then tile that loop + // as it has been unrolled + parser::OmpClauseList &unrollClauseList{ + std::get(nestedBeginDirective.t)}; + if (unrollClauseList.v.empty()) { + // if the clause list is empty for an unroll construct, we assume + // the loop is being fully unrolled + tileUnrollError(dir, messages_); + } else { + // parse the clauses for the unroll directive to find the full + // clause + for (auto clause{unrollClauseList.v.begin()}; + clause != unrollClauseList.v.end(); ++clause) { + if (clause->Id() == llvm::omp::OMPC_full) { + tileUnrollError(dir, messages_); + } + } + } + } else { + messages_.Say(nestedBeginLoopDirective.source, + "Only Loop Transformation Constructs or Loop Nests can be nested within Loop Constructs"_err_en_US, + parser::ToUpperCaseLetters( + nestedBeginLoopDirective.source.ToString())); + } } else { - messages_.Say(dir.source, - "A DO loop must follow the %s directive"_err_en_US, - parser::ToUpperCaseLetters(dir.source.ToString())); + missingDoConstruct(dir, messages_); } // If we get here, we either found a loop, or issued an error message. return; } + if (nextIt == block.end()) { + missingDoConstruct(dir, messages_); + } } void RewriteOmpAllocations(parser::ExecutionPart &body) { diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp index 3abb5a304b00c..e080bce3cac3a 100644 --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -762,10 +762,13 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) { } SetLoopInfo(x); - if (const auto &doConstruct{ - std::get>(x.t)}) { - const auto &doBlock{std::get(doConstruct->t)}; - CheckNoBranching(doBlock, beginDir.v, beginDir.source); + auto &optLoopCons = std::get>(x.t); + if (optLoopCons.has_value()) { + if (const auto &doConstruct{ + std::get_if(&*optLoopCons)}) { + const auto &doBlock{std::get(doConstruct->t)}; + CheckNoBranching(doBlock, beginDir.v, beginDir.source); + } } CheckLoopItrVariableIsInt(x); CheckAssociatedLoopConstraints(x); @@ -786,12 +789,15 @@ const parser::Name OmpStructureChecker::GetLoopIndex( return std::get(x->GetLoopControl()->u).name.thing; } void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct &x) { - if (const auto &loopConstruct{ - std::get>(x.t)}) { - const parser::DoConstruct *loop{&*loopConstruct}; - if (loop && loop->IsDoNormal()) { - const parser::Name &itrVal{GetLoopIndex(loop)}; - SetLoopIv(itrVal.symbol); + auto &optLoopCons = std::get>(x.t); + if (optLoopCons.has_value()) { + if (const auto &loopConstruct{ + std::get_if(&*optLoopCons)}) { + const parser::DoConstruct *loop{&*loopConstruct}; + if (loop && loop->IsDoNormal()) { + const parser::Name &itrVal{GetLoopIndex(loop)}; + SetLoopIv(itrVal.symbol); + } } } } @@ -857,27 +863,30 @@ void OmpStructureChecker::CheckIteratorModifier(const parser::OmpIterator &x) { void OmpStructureChecker::CheckLoopItrVariableIsInt( const parser::OpenMPLoopConstruct &x) { - if (const auto &loopConstruct{ - std::get>(x.t)}) { + auto &optLoopCons = std::get>(x.t); + if (optLoopCons.has_value()) { + if (const auto &loopConstruct{ + std::get_if(&*optLoopCons)}) { - for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) { - if (loop->IsDoNormal()) { - const parser::Name &itrVal{GetLoopIndex(loop)}; - if (itrVal.symbol) { - const auto *type{itrVal.symbol->GetType()}; - if (!type->IsNumeric(TypeCategory::Integer)) { - context_.Say(itrVal.source, - "The DO loop iteration" - " variable must be of the type integer."_err_en_US, - itrVal.ToString()); + for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) { + if (loop->IsDoNormal()) { + const parser::Name &itrVal{GetLoopIndex(loop)}; + if (itrVal.symbol) { + const auto *type{itrVal.symbol->GetType()}; + if (!type->IsNumeric(TypeCategory::Integer)) { + context_.Say(itrVal.source, + "The DO loop iteration" + " variable must be of the type integer."_err_en_US, + itrVal.ToString()); + } } } + // Get the next DoConstruct if block is not empty. + const auto &block{std::get(loop->t)}; + const auto it{block.begin()}; + loop = it != block.end() ? parser::Unwrap(*it) + : nullptr; } - // Get the next DoConstruct if block is not empty. - const auto &block{std::get(loop->t)}; - const auto it{block.begin()}; - loop = it != block.end() ? parser::Unwrap(*it) - : nullptr; } } } @@ -1077,25 +1086,28 @@ void OmpStructureChecker::CheckDistLinear( // Match the loop index variables with the collected symbols from linear // clauses. - if (const auto &loopConstruct{ - std::get>(x.t)}) { - for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) { - if (loop->IsDoNormal()) { - const parser::Name &itrVal{GetLoopIndex(loop)}; - if (itrVal.symbol) { - // Remove the symbol from the collected set - indexVars.erase(&itrVal.symbol->GetUltimate()); - } - collapseVal--; - if (collapseVal == 0) { - break; + auto &optLoopCons = std::get>(x.t); + if (optLoopCons.has_value()) { + if (const auto &loopConstruct{ + std::get_if(&*optLoopCons)}) { + for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) { + if (loop->IsDoNormal()) { + const parser::Name &itrVal{GetLoopIndex(loop)}; + if (itrVal.symbol) { + // Remove the symbol from the collected set + indexVars.erase(&itrVal.symbol->GetUltimate()); + } + collapseVal--; + if (collapseVal == 0) { + break; + } } + // Get the next DoConstruct if block is not empty. + const auto &block{std::get(loop->t)}; + const auto it{block.begin()}; + loop = it != block.end() ? parser::Unwrap(*it) + : nullptr; } - // Get the next DoConstruct if block is not empty. - const auto &block{std::get(loop->t)}; - const auto it{block.begin()}; - loop = it != block.end() ? parser::Unwrap(*it) - : nullptr; } } diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp index 885c02e6ec74b..8d741734601a7 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -1796,10 +1796,13 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) { SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList)); if (beginDir.v == llvm::omp::Directive::OMPD_do) { - if (const auto &doConstruct{ - std::get>(x.t)}) { - if (doConstruct.value().IsDoWhile()) { - return true; + auto &optLoopCons = std::get>(x.t); + if (optLoopCons.has_value()) { + if (const auto &doConstruct{ + std::get_if(&*optLoopCons)}) { + if (doConstruct->IsDoWhile()) { + return true; + } } } } @@ -1962,48 +1965,69 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel( bool hasCollapseClause{ clause ? (clause->Id() == llvm::omp::OMPC_collapse) : false}; - const auto &outer{std::get>(x.t)}; - if (outer.has_value()) { - for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) { - if (loop->IsDoConcurrent()) { - // DO CONCURRENT is explicitly allowed for the LOOP construct so long as - // there isn't a COLLAPSE clause - if (isLoopConstruct) { - if (hasCollapseClause) { - // hasCollapseClause implies clause != nullptr - context_.Say(clause->source, - "DO CONCURRENT loops cannot be used with the COLLAPSE clause."_err_en_US); + auto &optLoopCons = std::get>(x.t); + if (optLoopCons.has_value()) { + if (const auto &outer{std::get_if(&*optLoopCons)}) { + for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; + --level) { + if (loop->IsDoConcurrent()) { + // DO CONCURRENT is explicitly allowed for the LOOP construct so long + // as there isn't a COLLAPSE clause + if (isLoopConstruct) { + if (hasCollapseClause) { + // hasCollapseClause implies clause != nullptr + context_.Say(clause->source, + "DO CONCURRENT loops cannot be used with the COLLAPSE clause."_err_en_US); + } + } else { + auto &stmt = + std::get>(loop->t); + context_.Say(stmt.source, + "DO CONCURRENT loops cannot form part of a loop nest."_err_en_US); } - } else { - auto &stmt = - std::get>(loop->t); - context_.Say(stmt.source, - "DO CONCURRENT loops cannot form part of a loop nest."_err_en_US); - } - } - // go through all the nested do-loops and resolve index variables - const parser::Name *iv{GetLoopIndex(*loop)}; - if (iv) { - if (auto *symbol{ResolveOmp(*iv, ivDSA, currScope())}) { - SetSymbolDSA(*symbol, {Symbol::Flag::OmpPreDetermined, ivDSA}); - iv->symbol = symbol; // adjust the symbol within region - AddToContextObjectWithDSA(*symbol, ivDSA); } + // go through all the nested do-loops and resolve index variables + const parser::Name *iv{GetLoopIndex(*loop)}; + if (iv) { + if (auto *symbol{ResolveOmp(*iv, ivDSA, currScope())}) { + SetSymbolDSA(*symbol, {Symbol::Flag::OmpPreDetermined, ivDSA}); + iv->symbol = symbol; // adjust the symbol within region + AddToContextObjectWithDSA(*symbol, ivDSA); + } - const auto &block{std::get(loop->t)}; - const auto it{block.begin()}; - loop = it != block.end() ? GetDoConstructIf(*it) : nullptr; + const auto &block{std::get(loop->t)}; + const auto it{block.begin()}; + loop = it != block.end() ? GetDoConstructIf(*it) : nullptr; + } } + CheckAssocLoopLevel(level, GetAssociatedClause()); + } else if (const auto &loop{std::get_if< + common::Indirection>( + &*optLoopCons)}) { + auto &beginDirective = + std::get(loop->value().t); + auto &beginLoopDirective = + std::get(beginDirective.t); + if (beginLoopDirective.v != llvm::omp::Directive::OMPD_unroll && + beginLoopDirective.v != llvm::omp::Directive::OMPD_tile) { + context_.Say(GetContext().directiveSource, + "Only UNROLL or TILE constructs are allowed between an OpenMP Loop Construct and a DO construct"_err_en_US, + parser::ToUpperCaseLetters(llvm::omp::getOpenMPDirectiveName( + GetContext().directive, version) + .str())); + } else { + PrivatizeAssociatedLoopIndexAndCheckLoopLevel(loop->value()); + } + } else { + context_.Say(GetContext().directiveSource, + "A DO loop must follow the %s directive"_err_en_US, + parser::ToUpperCaseLetters( + llvm::omp::getOpenMPDirectiveName(GetContext().directive, version) + .str())); } - CheckAssocLoopLevel(level, GetAssociatedClause()); - } else { - context_.Say(GetContext().directiveSource, - "A DO loop must follow the %s directive"_err_en_US, - parser::ToUpperCaseLetters( - llvm::omp::getOpenMPDirectiveName(GetContext().directive, version) - .str())); } } + void OmpAttributeVisitor::CheckAssocLoopLevel( std::int64_t level, const parser::OmpClause *clause) { if (clause && level != 0) { diff --git a/flang/test/Lower/OpenMP/nested-loop-transformation-construct01.f90 b/flang/test/Lower/OpenMP/nested-loop-transformation-construct01.f90 new file mode 100644 index 0000000000000..a76e7e52100db --- /dev/null +++ b/flang/test/Lower/OpenMP/nested-loop-transformation-construct01.f90 @@ -0,0 +1,20 @@ +! Test to ensure TODO message is emitted for tile OpenMP 5.1 Directives when they are nested. + +!RUN: not %flang -fopenmp -fopenmp-version=51 %s 2>&1 | FileCheck %s + +subroutine loop_transformation_construct + implicit none + integer :: I = 10 + integer :: x + integer :: y(I) + + !$omp do + !$omp tile + do i = 1, I + y(i) = y(i) * 5 + end do + !$omp end tile + !$omp end do +end subroutine + +!CHECK: not yet implemented: Unhandled loop directive (tile) diff --git a/flang/test/Lower/OpenMP/nested-loop-transformation-construct02.f90 b/flang/test/Lower/OpenMP/nested-loop-transformation-construct02.f90 new file mode 100644 index 0000000000000..33b7c5a917619 --- /dev/null +++ b/flang/test/Lower/OpenMP/nested-loop-transformation-construct02.f90 @@ -0,0 +1,20 @@ +! Test to ensure TODO message is emitted for unroll OpenMP 5.1 Directives when they are nested. + +!RUN: not %flang -fopenmp -fopenmp-version=51 %s 2>&1 | FileCheck %s + +program loop_transformation_construct + implicit none + integer, parameter :: I = 10 + integer :: x + integer :: y(I) + + !$omp do + !$omp unroll + do x = 1, I + y(x) = y(x) * 5 + end do + !$omp end unroll + !$omp end do +end program loop_transformation_construct + +!CHECK: not yet implemented: Unhandled loop directive (unroll) diff --git a/flang/test/Parser/OpenMP/loop-transformation-construct01.f90 b/flang/test/Parser/OpenMP/loop-transformation-construct01.f90 new file mode 100644 index 0000000000000..baffc2f6e2f1e --- /dev/null +++ b/flang/test/Parser/OpenMP/loop-transformation-construct01.f90 @@ -0,0 +1,74 @@ +! Test the Parse Tree to ensure the OpenMP Loop Transformation Constructs nest correctly with 1 nested loop. + +! RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=51 %s | FileCheck %s --check-prefix=CHECK-PARSE +! RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=51 %s | FileCheck %s --check-prefix=CHECK-UNPARSE + +subroutine loop_transformation_construct + implicit none + integer :: I = 10 + integer :: x + integer :: y(I) + + !$omp do + !$omp unroll + do i = 1, I + y(i) = y(i) * 5 + end do + !$omp end unroll + !$omp end do +end subroutine + +!CHECK-PARSE: | ExecutionPart -> Block +!CHECK-PARSE-NEXT: | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct +!CHECK-PARSE-NEXT: | | | OmpBeginLoopDirective +!CHECK-PARSE-NEXT: | | | | OmpLoopDirective -> llvm::omp::Directive = do +!CHECK-PARSE-NEXT: | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | OpenMPLoopConstruct +!CHECK-PARSE-NEXT: | | | | OmpBeginLoopDirective +!CHECK-PARSE-NEXT: | | | | | OmpLoopDirective -> llvm::omp::Directive = unroll +!CHECK-PARSE-NEXT: | | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | DoConstruct +!CHECK-PARSE-NEXT: | | | | | NonLabelDoStmt +!CHECK-PARSE-NEXT: | | | | | | LoopControl -> LoopBounds +!CHECK-PARSE-NEXT: | | | | | | | Scalar -> Name = 'i' +!CHECK-PARSE-NEXT: | | | | | | | Scalar -> Expr = '1_4' +!CHECK-PARSE-NEXT: | | | | | | | | LiteralConstant -> IntLiteralConstant = '1' +!CHECK-PARSE-NEXT: | | | | | | | Scalar -> Expr = 'i' +!CHECK-PARSE-NEXT: | | | | | | | | Designator -> DataRef -> Name = 'i' +!CHECK-PARSE-NEXT: | | | | | Block +!CHECK-PARSE-NEXT: | | | | | | ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> AssignmentStmt = 'y(int(i,kind=8))=5_4*y(int(i,kind=8))' +!CHECK-PARSE-NEXT: | | | | | | | Variable = 'y(int(i,kind=8))' +!CHECK-PARSE-NEXT: | | | | | | | | Designator -> DataRef -> ArrayElement +!CHECK-PARSE-NEXT: | | | | | | | | | DataRef -> Name = 'y' +!CHECK-PARSE-NEXT: | | | | | | | | | SectionSubscript -> Integer -> Expr = 'i' +!CHECK-PARSE-NEXT: | | | | | | | | | | Designator -> DataRef -> Name = 'i' +!CHECK-PARSE-NEXT: | | | | | | | Expr = '5_4*y(int(i,kind=8))' +!CHECK-PARSE-NEXT: | | | | | | | | Multiply +!CHECK-PARSE-NEXT: | | | | | | | | | Expr = 'y(int(i,kind=8))' +!CHECK-PARSE-NEXT: | | | | | | | | | | Designator -> DataRef -> ArrayElement +!CHECK-PARSE-NEXT: | | | | | | | | | | | DataRef -> Name = 'y' +!CHECK-PARSE-NEXT: | | | | | | | | | | | SectionSubscript -> Integer -> Expr = 'i' +!CHECK-PARSE-NEXT: | | | | | | | | | | | | Designator -> DataRef -> Name = 'i' +!CHECK-PARSE-NEXT: | | | | | | | | | Expr = '5_4' +!CHECK-PARSE-NEXT: | | | | | | | | | | LiteralConstant -> IntLiteralConstant = '5' +!CHECK-PARSE-NEXT: | | | | | EndDoStmt -> +!CHECK-PARSE-NEXT: | | | | OmpEndLoopDirective +!CHECK-PARSE-NEXT: | | | | | OmpLoopDirective -> llvm::omp::Directive = unroll +!CHECK-PARSE-NEXT: | | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | OmpEndLoopDirective +!CHECK-PARSE-NEXT: | | | | OmpLoopDirective -> llvm::omp::Directive = do +!CHECK-PARSE-NEXT: | | | | OmpClauseList -> + +!CHECK-UNPARSE: SUBROUTINE loop_transformation_construct +!CHECK-UNPARSE-NEXT: IMPLICIT NONE +!CHECK-UNPARSE-NEXT: INTEGER :: i = 10_4 +!CHECK-UNPARSE-NEXT: INTEGER x +!CHECK-UNPARSE-NEXT: INTEGER y(i) +!CHECK-UNPARSE-NEXT: !$OMP DO +!CHECK-UNPARSE-NEXT: !$OMP UNROLL +!CHECK-UNPARSE-NEXT: DO i=1_4,i +!CHECK-UNPARSE-NEXT: y(int(i,kind=8))=5_4*y(int(i,kind=8)) +!CHECK-UNPARSE-NEXT: END DO +!CHECK-UNPARSE-NEXT: !$OMP END UNROLL +!CHECK-UNPARSE-NEXT: !$OMP END DO +!CHECK-UNPARSE-NEXT: END SUBROUTINE diff --git a/flang/test/Parser/OpenMP/loop-transformation-construct02.f90 b/flang/test/Parser/OpenMP/loop-transformation-construct02.f90 new file mode 100644 index 0000000000000..b50e7183841cc --- /dev/null +++ b/flang/test/Parser/OpenMP/loop-transformation-construct02.f90 @@ -0,0 +1,85 @@ +! Test the Parse Tree to ensure the OpenMP Loop Transformation Constructs nest correctly with multiple nested loops. + +! RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=51 %s | FileCheck %s --check-prefix=CHECK-PARSE +! RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=51 %s | FileCheck %s --check-prefix=CHECK-UNPARSE + +subroutine loop_transformation_construct + implicit none + integer :: I = 10 + integer :: x + integer :: y(I) + + !$omp do + !$omp unroll + !$omp tile + do i = 1, I + y(i) = y(i) * 5 + end do + !$omp end tile + !$omp end unroll + !$omp end do +end subroutine + +!CHECK-PARSE: | ExecutionPart -> Block +!CHECK-PARSE-NEXT: | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct +!CHECK-PARSE-NEXT: | | | OmpBeginLoopDirective +!CHECK-PARSE-NEXT: | | | | OmpLoopDirective -> llvm::omp::Directive = do +!CHECK-PARSE-NEXT: | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | OpenMPLoopConstruct +!CHECK-PARSE-NEXT: | | | | OmpBeginLoopDirective +!CHECK-PARSE-NEXT: | | | | | OmpLoopDirective -> llvm::omp::Directive = unroll +!CHECK-PARSE-NEXT: | | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | OpenMPLoopConstruct +!CHECK-PARSE-NEXT: | | | | | OmpBeginLoopDirective +!CHECK-PARSE-NEXT: | | | | | | OmpLoopDirective -> llvm::omp::Directive = tile +!CHECK-PARSE-NEXT: | | | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | | DoConstruct +!CHECK-PARSE-NEXT: | | | | | | NonLabelDoStmt +!CHECK-PARSE-NEXT: | | | | | | | LoopControl -> LoopBounds +!CHECK-PARSE-NEXT: | | | | | | | | Scalar -> Name = 'i' +!CHECK-PARSE-NEXT: | | | | | | | | Scalar -> Expr = '1_4' +!CHECK-PARSE-NEXT: | | | | | | | | | LiteralConstant -> IntLiteralConstant = '1' +!CHECK-PARSE-NEXT: | | | | | | | | Scalar -> Expr = 'i' +!CHECK-PARSE-NEXT: | | | | | | | | | Designator -> DataRef -> Name = 'i' +!CHECK-PARSE-NEXT: | | | | | | Block +!CHECK-PARSE-NEXT: | | | | | | | ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> AssignmentStmt = 'y(int(i,kind=8))=5_4*y(int(i,kind=8))' +!CHECK-PARSE-NEXT: | | | | | | | | Variable = 'y(int(i,kind=8))' +!CHECK-PARSE-NEXT: | | | | | | | | | Designator -> DataRef -> ArrayElement +!CHECK-PARSE-NEXT: | | | | | | | | | | DataRef -> Name = 'y' +!CHECK-PARSE-NEXT: | | | | | | | | | | SectionSubscript -> Integer -> Expr = 'i' +!CHECK-PARSE-NEXT: | | | | | | | | | | | Designator -> DataRef -> Name = 'i' +!CHECK-PARSE-NEXT: | | | | | | | | Expr = '5_4*y(int(i,kind=8))' +!CHECK-PARSE-NEXT: | | | | | | | | | Multiply +!CHECK-PARSE-NEXT: | | | | | | | | | | Expr = 'y(int(i,kind=8))' +!CHECK-PARSE-NEXT: | | | | | | | | | | | Designator -> DataRef -> ArrayElement +!CHECK-PARSE-NEXT: | | | | | | | | | | | | DataRef -> Name = 'y' +!CHECK-PARSE-NEXT: | | | | | | | | | | | | SectionSubscript -> Integer -> Expr = 'i' +!CHECK-PARSE-NEXT: | | | | | | | | | | | | | Designator -> DataRef -> Name = 'i' +!CHECK-PARSE-NEXT: | | | | | | | | | | Expr = '5_4' +!CHECK-PARSE-NEXT: | | | | | | | | | | | LiteralConstant -> IntLiteralConstant = '5' +!CHECK-PARSE-NEXT: | | | | | | EndDoStmt -> +!CHECK-PARSE-NEXT: | | | | | OmpEndLoopDirective +!CHECK-PARSE-NEXT: | | | | | | OmpLoopDirective -> llvm::omp::Directive = tile +!CHECK-PARSE-NEXT: | | | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | OmpEndLoopDirective +!CHECK-PARSE-NEXT: | | | | | OmpLoopDirective -> llvm::omp::Directive = unroll +!CHECK-PARSE-NEXT: | | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | OmpEndLoopDirective +!CHECK-PARSE-NEXT: | | | | OmpLoopDirective -> llvm::omp::Directive = do +!CHECK-PARSE-NEXT: | | | | OmpClauseList -> + +!CHECK-UNPARSE: SUBROUTINE loop_transformation_construct +!CHECK-UNPARSE-NEXT: IMPLICIT NONE +!CHECK-UNPARSE-NEXT: INTEGER :: i = 10_4 +!CHECK-UNPARSE-NEXT: INTEGER x +!CHECK-UNPARSE-NEXT: INTEGER y(i) +!CHECK-UNPARSE-NEXT: !$OMP DO +!CHECK-UNPARSE-NEXT: !$OMP UNROLL +!CHECK-UNPARSE-NEXT: !$OMP TILE +!CHECK-UNPARSE-NEXT: DO i=1_4,i +!CHECK-UNPARSE-NEXT: y(int(i,kind=8))=5_4*y(int(i,kind=8)) +!CHECK-UNPARSE-NEXT: END DO +!CHECK-UNPARSE-NEXT: !$OMP END TILE +!CHECK-UNPARSE-NEXT: !$OMP END UNROLL +!CHECK-UNPARSE-NEXT: !$OMP END DO +!CHECK-UNPARSE-NEXT: END SUBROUTINE diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 new file mode 100644 index 0000000000000..f718efc32aabf --- /dev/null +++ b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 @@ -0,0 +1,100 @@ +! Testing the Semantics of nested Loop Transformation Constructs + +!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=51 + +subroutine loop_transformation_construct1 + implicit none + + !$omp do + !ERROR: A DO loop must follow the UNROLL directive + !$omp unroll +end subroutine + +subroutine loop_transformation_construct2 + implicit none + integer :: i = 5 + integer :: y + integer :: v(i) + + !$omp do + !$omp tile + do x = 1, i + v(x) = x(x) * 2 + end do + !$omp end tile + !$omp end do + !ERROR: The END TILE directive must follow the DO loop associated with the loop construct + !$omp end tile +end subroutine + +subroutine loop_transformation_construct2 + implicit none + integer :: i = 5 + integer :: y + integer :: v(i) + + !$omp do + !ERROR: Only Loop Transformation Constructs or Loop Nests can be nested within Loop Constructs + !$omp parallel do + do x = 1, i + v(x) = x(x) * 2 + end do +end subroutine + +subroutine loop_transformation_construct3 + implicit none + integer :: i = 5 + integer :: y + integer :: v(i) + + !$omp do + do x = 1, i + v(x) = x(x) * 2 + end do + !ERROR: A DO loop must follow the TILE directive + !$omp tile +end subroutine + +subroutine loop_transformation_construct4 + implicit none + integer :: i = 5 + integer :: y + integer :: v(i) + + !$omp do + !ERROR: If a loop construct has been fully unrolled, it cannot then be tiled + !$omp tile + !$omp unroll full + do x = 1, i + v(x) = x(x) * 2 + end do +end subroutine + +subroutine loop_transformation_construct5 + implicit none + integer :: i = 5 + integer :: y + integer :: v(i) + + !$omp do + !ERROR: If a loop construct has been fully unrolled, it cannot then be tiled + !$omp tile + !$omp unroll + do x = 1, i + v(x) = x(x) * 2 + end do +end subroutine + +subroutine loop_transformation_construct6 + implicit none + integer :: i = 5 + integer :: y + integer :: v(i) + + !$omp do + !$omp tile + !$omp unroll partial(2) + do x = 1, i + v(x) = x(x) * 2 + end do +end subroutine