diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index 6dd4f2492cf22..a21029b1f509f 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -5260,7 +5260,7 @@ using NestedConstruct = struct OpenMPLoopConstruct { TUPLE_CLASS_BOILERPLATE(OpenMPLoopConstruct); OpenMPLoopConstruct(OmpBeginLoopDirective &&a) - : t({std::move(a), std::nullopt, std::nullopt}) {} + : t({std::move(a), std::list(), std::nullopt}) {} const OmpBeginLoopDirective &BeginDir() const { return std::get(t); @@ -5268,7 +5268,7 @@ struct OpenMPLoopConstruct { const std::optional &EndDir() const { return std::get>(t); } - std::tuple, + std::tuple, std::optional> t; }; diff --git a/flang/include/flang/Semantics/openmp-directive-sets.h b/flang/include/flang/Semantics/openmp-directive-sets.h index 01e8481e05721..609a7be700c28 100644 --- a/flang/include/flang/Semantics/openmp-directive-sets.h +++ b/flang/include/flang/Semantics/openmp-directive-sets.h @@ -275,10 +275,17 @@ static const OmpDirectiveSet loopConstructSet{ Directive::OMPD_teams_distribute_parallel_do_simd, Directive::OMPD_teams_distribute_simd, Directive::OMPD_teams_loop, + Directive::OMPD_fuse, Directive::OMPD_tile, Directive::OMPD_unroll, }; +static const OmpDirectiveSet loopTransformationSet{ + Directive::OMPD_tile, + Directive::OMPD_unroll, + Directive::OMPD_fuse, +}; + static const OmpDirectiveSet nonPartialVarSet{ Directive::OMPD_allocate, Directive::OMPD_allocators, diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp index 71067283d13f7..acbdf3f4e84c9 100644 --- a/flang/lib/Lower/OpenMP/OpenMP.cpp +++ b/flang/lib/Lower/OpenMP/OpenMP.cpp @@ -3471,6 +3471,13 @@ static void genOMPDispatch(lower::AbstractConverter &converter, case llvm::omp::Directive::OMPD_tile: genTileOp(converter, symTable, stmtCtx, semaCtx, eval, loc, queue, item); break; + case llvm::omp::Directive::OMPD_fuse: { + unsigned version = semaCtx.langOptions().OpenMPVersion; + if (!semaCtx.langOptions().OpenMPSimd) + TODO(loc, "Unhandled loop directive (" + + llvm::omp::getOpenMPDirectiveName(dir, version) + ")"); + break; + } case llvm::omp::Directive::OMPD_unroll: genUnrollOp(converter, symTable, stmtCtx, semaCtx, eval, loc, queue, item); break; @@ -3918,12 +3925,12 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, mlir::Location currentLocation = converter.genLocation(beginSpec.source); - auto &optLoopCons = - std::get>(loopConstruct.t); - if (optLoopCons.has_value()) { + auto &loopConsList = + std::get>(loopConstruct.t); + for (auto &loopCons : loopConsList) { if (auto *ompNestedLoopCons{ std::get_if>( - &*optLoopCons)}) { + &loopCons)}) { llvm::omp::Directive nestedDirective = parser::omp::GetOmpDirectiveName(*ompNestedLoopCons).v; switch (nestedDirective) { diff --git a/flang/lib/Lower/OpenMP/Utils.cpp b/flang/lib/Lower/OpenMP/Utils.cpp index 6487f599df72a..7ae0c01c21987 100644 --- a/flang/lib/Lower/OpenMP/Utils.cpp +++ b/flang/lib/Lower/OpenMP/Utils.cpp @@ -631,13 +631,13 @@ static void processTileSizesFromOpenMPConstruct( if (!ompCons) return; if (auto *ompLoop{std::get_if(&ompCons->u)}) { - const auto &nestedOptional = - std::get>(ompLoop->t); - assert(nestedOptional.has_value() && + const auto &loopConsList = + std::get>(ompLoop->t); + assert(loopConsList.size() == 1 && "Expected a DoConstruct or OpenMPLoopConstruct"); const auto *innerConstruct = std::get_if>( - &(nestedOptional.value())); + &(loopConsList.front())); if (innerConstruct) { const auto &innerLoopDirective = innerConstruct->value(); const parser::OmpDirectiveSpecification &innerBeginSpec = diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp index c0472ad3c0692..10452694d100d 100644 --- a/flang/lib/Parser/openmp-parsers.cpp +++ b/flang/lib/Parser/openmp-parsers.cpp @@ -2037,6 +2037,7 @@ static constexpr DirectiveSet GetLoopDirectives() { unsigned(Directive::OMPD_teams_distribute_parallel_do_simd), unsigned(Directive::OMPD_teams_distribute_simd), unsigned(Directive::OMPD_teams_loop), + unsigned(Directive::OMPD_fuse), unsigned(Directive::OMPD_tile), unsigned(Directive::OMPD_unroll), }; diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp index b172e429c84e8..d8f8c4b270e27 100644 --- a/flang/lib/Parser/unparse.cpp +++ b/flang/lib/Parser/unparse.cpp @@ -2725,8 +2725,7 @@ class UnparseVisitor { } void Unparse(const OpenMPLoopConstruct &x) { Walk(std::get(x.t)); - 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 c884658bf464a..3142813b75dfc 100644 --- a/flang/lib/Semantics/canonicalize-omp.cpp +++ b/flang/lib/Semantics/canonicalize-omp.cpp @@ -9,6 +9,7 @@ #include "canonicalize-omp.h" #include "flang/Parser/parse-tree-visitor.h" #include "flang/Parser/parse-tree.h" +#include "flang/Semantics/openmp-directive-sets.h" #include "flang/Semantics/semantics.h" // After Loop Canonicalization, rewrite OpenMP parse tree to make OpenMP @@ -137,33 +138,45 @@ class CanonicalizationOfOmp { "A DO loop must follow the %s directive"_err_en_US, parser::ToUpperCaseLetters(dirName.source.ToString())); }; - auto tileUnrollError = [](const parser::OmpDirectiveName &dirName, - parser::Messages &messages) { + auto transformUnrollError = [](const parser::OmpDirectiveName &dirName, + parser::Messages &messages) { messages.Say(dirName.source, - "If a loop construct has been fully unrolled, it cannot then be tiled"_err_en_US, + "If a loop construct has been fully unrolled, it cannot then be further transformed"_err_en_US, parser::ToUpperCaseLetters(dirName.source.ToString())); }; + auto missingEndFuse = [](auto &dir, auto &messages) { + messages.Say(dir.source, + "The %s construct requires the END FUSE directive"_err_en_US, + parser::ToUpperCaseLetters(dir.source.ToString())); + }; + + bool endFuseNeeded = beginName.v == llvm::omp::Directive::OMPD_fuse; nextIt = it; - while (++nextIt != block.end()) { + nextIt++; + while (nextIt != block.end()) { // Ignore compiler directives. - if (GetConstructIf(*nextIt)) + if (GetConstructIf(*nextIt)) { + nextIt++; continue; + } if (auto *doCons{GetConstructIf(*nextIt)}) { if (doCons->GetLoopControl()) { // move DoConstruct - std::get>>>(x.t) = - std::move(*doCons); + std::get>(x.t).push_back( + 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); - nextIt = block.erase(nextIt); + auto &endDirName = endDir->DirName(); + if (endDirName.v != llvm::omp::Directive::OMPD_fuse) { + std::get>(x.t) = + std::move(*endDir); + nextIt = block.erase(nextIt); + } } } } else { @@ -173,53 +186,48 @@ class CanonicalizationOfOmp { } } 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 + // We should allow loop transformation constructs to be inserted between + // an OpenMP Loop Construct and the DO loop itself auto &nestedBeginDirective = ompLoopCons->BeginDir(); auto &nestedBeginName = nestedBeginDirective.DirName(); - if ((nestedBeginName.v == llvm::omp::Directive::OMPD_unroll || - nestedBeginName.v == llvm::omp::Directive::OMPD_tile) && - !(nestedBeginName.v == llvm::omp::Directive::OMPD_unroll && - beginName.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 &endDirName = endDir->DirName(); - if (endDirName.v == beginName.v) { - std::get>(x.t) = - std::move(*endDir); - endIt = block.erase(endIt); - continue; + if (llvm::omp::loopTransformationSet.test(nestedBeginName.v)) { + if (nestedBeginName.v == llvm::omp::Directive::OMPD_unroll && + llvm::omp::loopTransformationSet.test(beginName.v)) { + // if a loop has been unrolled, the user can not then transform that + // loop as it has been unrolled + const parser::OmpClauseList &unrollClauseList{ + nestedBeginDirective.Clauses()}; + if (unrollClauseList.v.empty()) { + // if the clause list is empty for an unroll construct, we assume + // the loop is being fully unrolled + transformUnrollError(beginName, messages_); + } else { + // parse the clauses for the unroll directive to find the full + // clause + for (auto &clause : unrollClauseList.v) { + if (clause.Id() == llvm::omp::OMPC_full) { + transformUnrollError(beginName, messages_); + } } } - ++endIt; } RewriteOpenMPLoopConstruct(*ompLoopCons, block, nextIt); - auto &ompLoop = std::get>(x.t); - ompLoop = - std::optional{parser::NestedConstruct{ - common::Indirection{std::move(*ompLoopCons)}}}; + auto &loopConsList = + std::get>(x.t); + loopConsList.push_back(parser::NestedConstruct{ + common::Indirection{std::move(*ompLoopCons)}}); nextIt = block.erase(nextIt); - } else if (nestedBeginName.v == llvm::omp::Directive::OMPD_unroll && - beginName.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 - const parser::OmpClauseList &unrollClauseList{ - nestedBeginDirective.Clauses()}; - if (unrollClauseList.v.empty()) { - // if the clause list is empty for an unroll construct, we assume - // the loop is being fully unrolled - tileUnrollError(beginName, messages_); - } else { - // parse the clauses for the unroll directive to find the full - // clause - for (auto &clause : unrollClauseList.v) { - if (clause.Id() == llvm::omp::OMPC_full) { - tileUnrollError(beginName, messages_); + // check the following block item to find the end directive + // for the loop transform directive. + if (nextIt != block.end()) { + if (auto *endDir{ + GetConstructIf(*nextIt)}) { + auto &endDirName = endDir->DirName(); + if (endDirName.v == beginName.v && + endDirName.v != llvm::omp::Directive::OMPD_fuse) { + std::get>(x.t) = + std::move(*endDir); + nextIt = block.erase(nextIt); } } } @@ -231,11 +239,29 @@ class CanonicalizationOfOmp { } else { missingDoConstruct(beginName, messages_); } + + if (endFuseNeeded && nextIt != block.end()) { + if (auto *endDir{ + GetConstructIf(*nextIt)}) { + auto &endDirName = endDir->DirName(); + if (endDirName.v == llvm::omp::Directive::OMPD_fuse) { + endFuseNeeded = false; + std::get>(x.t) = + std::move(*endDir); + nextIt = block.erase(nextIt); + } + } + } + if (endFuseNeeded) + continue; // If we get here, we either found a loop, or issued an error message. return; } if (nextIt == block.end()) { - missingDoConstruct(beginName, messages_); + if (endFuseNeeded) + missingEndFuse(beginName, messages_); + else + missingDoConstruct(beginName, messages_); } } diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp index aaaa2d6e78280..a4737e971c890 100644 --- a/flang/lib/Semantics/check-omp-loop.cpp +++ b/flang/lib/Semantics/check-omp-loop.cpp @@ -285,10 +285,9 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) { } SetLoopInfo(x); - auto &optLoopCons = std::get>(x.t); - if (optLoopCons.has_value()) { - if (const auto &doConstruct{ - std::get_if(&*optLoopCons)}) { + auto &loopConsList = std::get>(x.t); + for (auto &loopCons : loopConsList) { + if (const auto &doConstruct{std::get_if(&loopCons)}) { const auto &doBlock{std::get(doConstruct->t)}; CheckNoBranching(doBlock, beginName.v, beginName.source); } @@ -305,6 +304,11 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) { beginName.v == llvm::omp::Directive::OMPD_distribute_simd) { CheckDistLinear(x); } + if (beginName.v == llvm::omp::Directive::OMPD_fuse) { + CheckLooprangeBounds(x); + } else { + CheckNestedFuse(x); + } } const parser::Name OmpStructureChecker::GetLoopIndex( @@ -314,10 +318,10 @@ const parser::Name OmpStructureChecker::GetLoopIndex( } void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct &x) { - auto &optLoopCons = std::get>(x.t); - if (optLoopCons.has_value()) { + auto &loopConsList = std::get>(x.t); + if (loopConsList.size() == 1) { if (const auto &loopConstruct{ - std::get_if(&*optLoopCons)}) { + std::get_if(&loopConsList.front())}) { const parser::DoConstruct *loop{&*loopConstruct}; if (loop && loop->IsDoNormal()) { const parser::Name &itrVal{GetLoopIndex(loop)}; @@ -329,10 +333,10 @@ void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct &x) { void OmpStructureChecker::CheckLoopItrVariableIsInt( const parser::OpenMPLoopConstruct &x) { - auto &optLoopCons = std::get>(x.t); - if (optLoopCons.has_value()) { + auto &loopConsList = std::get>(x.t); + for (auto &loopCons : loopConsList) { if (const auto &loopConstruct{ - std::get_if(&*optLoopCons)}) { + std::get_if(&loopCons)}) { for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) { if (loop->IsDoNormal()) { @@ -417,10 +421,11 @@ void OmpStructureChecker::CheckDistLinear( // Match the loop index variables with the collected symbols from linear // clauses. - auto &optLoopCons = std::get>(x.t); - if (optLoopCons.has_value()) { + auto &loopConsList = std::get>(x.t); + for (auto &loopCons : loopConsList) { + std::int64_t curCollapseVal{collapseVal}; if (const auto &loopConstruct{ - std::get_if(&*optLoopCons)}) { + std::get_if(&loopCons)}) { for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) { if (loop->IsDoNormal()) { const parser::Name &itrVal{GetLoopIndex(loop)}; @@ -428,8 +433,8 @@ void OmpStructureChecker::CheckDistLinear( // Remove the symbol from the collected set indexVars.erase(&itrVal.symbol->GetUltimate()); } - collapseVal--; - if (collapseVal == 0) { + curCollapseVal--; + if (curCollapseVal == 0) { break; } } @@ -452,6 +457,67 @@ void OmpStructureChecker::CheckDistLinear( } } +void OmpStructureChecker::CheckLooprangeBounds( + const parser::OpenMPLoopConstruct &x) { + const parser::OmpClauseList &clauseList{x.BeginDir().Clauses()}; + if (clauseList.v.empty()) { + return; + } + for (auto &clause : clauseList.v) { + if (const auto *lrClause{ + std::get_if(&clause.u)}) { + auto first{GetIntValue(std::get<0>((lrClause->v).t))}; + auto count{GetIntValue(std::get<1>((lrClause->v).t))}; + if (!first || !count) { + return; + } + auto &loopConsList{std::get>(x.t)}; + if (*first > 0 && *count > 0 && + loopConsList.size() < (unsigned)(*first + *count - 1)) { + context_.Say(clause.source, + "The loop range indicated in the %s clause must not be out of the bounds of the Loop Sequence following the construct."_err_en_US, + parser::ToUpperCaseLetters(clause.source.ToString())); + } + return; + } + } +} + +void OmpStructureChecker::CheckNestedFuse( + const parser::OpenMPLoopConstruct &x) { + auto &loopConsList{std::get>(x.t)}; + assert(loopConsList.size() == 1 && "Not Expecting a loop sequence"); + auto &loopCons{loopConsList.front()}; + const auto &ompConstruct{ + std::get_if>(&loopCons)}; + if (!ompConstruct) { + return; + } + const parser::OmpClauseList &clauseList{ + ompConstruct->value().BeginDir().Clauses()}; + if (clauseList.v.empty()) { + return; + } + for (auto &clause : clauseList.v) { + if (const auto *lrClause{ + std::get_if(&clause.u)}) { + auto count{GetIntValue(std::get<1>((lrClause->v).t))}; + if (!count) { + return; + } + auto &nestedLoopConsList{std::get>( + ompConstruct->value().t)}; + if (nestedLoopConsList.size() > (unsigned)(*count)) { + context_.Say(x.BeginDir().DirName().source, + "The loop sequence following the %s construct must be fully fused first."_err_en_US, + parser::ToUpperCaseLetters( + x.BeginDir().DirName().source.ToString())); + } + return; + } + } +} + void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &x) { const parser::OmpClauseList &clauseList{x.BeginDir().Clauses()}; diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp index 41416304c1ea6..832f20aa22f72 100644 --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -3444,9 +3444,11 @@ CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Safelen, OMPC_safelen) CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Simdlen, OMPC_simdlen) void OmpStructureChecker::Enter(const parser::OmpClause::Looprange &x) { - context_.Say(GetContext().clauseSource, - "LOOPRANGE clause is not implemented yet"_err_en_US, - ContextDirectiveAsFortran()); + CheckAllowedClause(llvm::omp::Clause::OMPC_looprange); + auto &first = std::get<0>(x.v.t); + auto &count = std::get<1>(x.v.t); + RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_looprange, count); + RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_looprange, first); } // Restrictions specific to each clause are implemented apart from the diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h index 7426559e77ff7..69f255fb0bca5 100644 --- a/flang/lib/Semantics/check-omp-structure.h +++ b/flang/lib/Semantics/check-omp-structure.h @@ -313,6 +313,8 @@ class OmpStructureChecker : public OmpStructureCheckerBase { void CheckAtomicWrite(const parser::OpenMPAtomicConstruct &x); void CheckAtomicUpdate(const parser::OpenMPAtomicConstruct &x); + void CheckLooprangeBounds(const parser::OpenMPLoopConstruct &x); + void CheckNestedFuse(const parser::OpenMPLoopConstruct &x); void CheckDistLinear(const parser::OpenMPLoopConstruct &x); void CheckSIMDNest(const parser::OpenMPConstruct &x); void CheckTargetNest(const parser::OpenMPConstruct &x); diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp index 196755e2912a8..267e72a7c2398 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -496,7 +496,10 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor { void Post(const parser::OpenMPSimpleStandaloneConstruct &) { PopContext(); } bool Pre(const parser::OpenMPLoopConstruct &); - void Post(const parser::OpenMPLoopConstruct &) { PopContext(); } + void Post(const parser::OpenMPLoopConstruct &) { + ordCollapseLevel++; + PopContext(); + } void Post(const parser::OmpBeginLoopDirective &) { GetContext().withinConstruct = true; } @@ -1995,6 +1998,7 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) { case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd: case llvm::omp::Directive::OMPD_teams_distribute_simd: case llvm::omp::Directive::OMPD_teams_loop: + case llvm::omp::Directive::OMPD_fuse: case llvm::omp::Directive::OMPD_tile: case llvm::omp::Directive::OMPD_unroll: PushContext(beginName.source, beginName.v); @@ -2014,13 +2018,13 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) { SetContextAssociatedLoopLevel(GetNumAffectedLoopsFromLoopConstruct(x)); if (beginName.v == llvm::omp::Directive::OMPD_do) { - auto &optLoopCons = std::get>(x.t); - if (optLoopCons.has_value()) { - if (const auto &doConstruct{ - std::get_if(&*optLoopCons)}) { - if (doConstruct->IsDoWhile()) { - return true; - } + auto &loopConsList = std::get>(x.t); + assert(loopConsList.size() == 1 && + "Expected a single DoConstruct or OpenMPLoopConstruct"); + if (const auto &doConstruct{ + std::get_if(&loopConsList.front())}) { + if (doConstruct->IsDoWhile()) { + return true; } } } @@ -2178,17 +2182,17 @@ void OmpAttributeVisitor::CollectNumAffectedLoopsFromInnerLoopContruct( llvm::SmallVector &levels, llvm::SmallVector &clauses) { - const auto &nestedOptional = - std::get>(x.t); - assert(nestedOptional.has_value() && + const auto &nestedList = std::get>(x.t); + assert(nestedList.size() >= 1 && "Expected a DoConstruct or OpenMPLoopConstruct"); - const auto *innerConstruct = - std::get_if>( - &(nestedOptional.value())); + for (auto &nest : nestedList) { + const auto *innerConstruct = + std::get_if>(&nest); - if (innerConstruct) { - CollectNumAffectedLoopsFromLoopConstruct( - innerConstruct->value(), levels, clauses); + if (innerConstruct) { + CollectNumAffectedLoopsFromLoopConstruct( + innerConstruct->value(), levels, clauses); + } } } @@ -2255,84 +2259,85 @@ void OmpAttributeVisitor::CheckPerfectNestAndRectangularLoop( // such as loop transformations const parser::NestedConstruct *innermostAssocRegion{nullptr}; const parser::OpenMPLoopConstruct *innermostConstruct{&x}; - while (const auto &innerAssocStmt{ - std::get>( - innermostConstruct->t)}) { - innermostAssocRegion = &(innerAssocStmt.value()); + auto &loopConsList{ + std::get>(innermostConstruct->t)}; + for (auto &loopCons : loopConsList) { + innermostAssocRegion = &loopCons; if (const auto *innerConstruct{ std::get_if>( innermostAssocRegion)}) { - innermostConstruct = &innerConstruct->value(); + CheckPerfectNestAndRectangularLoop(innerConstruct->value()); + return; } else { - break; - } - } - if (!innermostAssocRegion) - return; - const auto &outer{std::get_if(innermostAssocRegion)}; - if (!outer) - return; + if (!innermostAssocRegion) + continue; + const auto &outer{std::get_if(innermostAssocRegion)}; + if (!outer) + continue; - llvm::SmallVector ivs; - int curLevel{0}; - const parser::DoConstruct *loop{outer}; - while (true) { - auto [iv, lb, ub, step] = GetLoopBounds(*loop); - - if (lb) - checkExprHasSymbols(ivs, lb); - if (ub) - checkExprHasSymbols(ivs, ub); - if (step) - checkExprHasSymbols(ivs, step); - if (iv) { - if (auto *symbol{currScope().FindSymbol(iv->source)}) - ivs.push_back(symbol); - } + llvm::SmallVector ivs; + int curLevel{0}; + const parser::DoConstruct *loop{outer}; + while (true) { + auto [iv, lb, ub, step] = GetLoopBounds(*loop); + + if (lb) + checkExprHasSymbols(ivs, lb); + if (ub) + checkExprHasSymbols(ivs, ub); + if (step) + checkExprHasSymbols(ivs, step); + if (iv) { + if (auto *symbol{currScope().FindSymbol(iv->source)}) + ivs.push_back(symbol); + } - // Stop after processing all affected loops - if (curLevel + 1 >= dirDepth) - break; + // Stop after processing all affected loops + if (curLevel + 1 >= dirDepth) + break; - // Recurse into nested loop - const auto &block{std::get(loop->t)}; - if (block.empty()) { - // Insufficient number of nested loops already reported by - // CheckAssocLoopLevel() - break; - } + // Recurse into nested loop + const auto &block{std::get(loop->t)}; + if (block.empty()) { + // Insufficient number of nested loops already reported by + // CheckAssocLoopLevel() + break; + } - loop = GetDoConstructIf(block.front()); - if (!loop) { - // Insufficient number of nested loops already reported by - // CheckAssocLoopLevel() - break; - } + loop = GetDoConstructIf(block.front()); + if (!loop) { + // Insufficient number of nested loops already reported by + // CheckAssocLoopLevel() + break; + } - auto checkPerfectNest = [&, this]() { - if (block.empty()) - return; - auto last = block.end(); - --last; + auto checkPerfectNest = [&, this]() { + if (block.empty()) + return; + auto last = block.end(); + --last; - // A trailing CONTINUE is not considered part of the loop body - if (parser::Unwrap(*last)) - --last; + // A trailing CONTINUE is not considered part of the loop body + if (parser::Unwrap(*last)) + --last; - // In a perfectly nested loop, the nested loop must be the only statement - if (last == block.begin()) - return; + // In a perfectly nested loop, the nested loop must be the only + // statement + if (last == block.begin()) + return; - // Non-perfectly nested loop - // TODO: Point to non-DO statement, directiveSource as a note - context_.Say(dirContext.directiveSource, - "Canonical loop nest must be perfectly nested."_err_en_US); - }; + // Non-perfectly nested loop + // TODO: Point to non-DO statement, directiveSource as a note + context_.Say(dirContext.directiveSource, + "Canonical loop nest must be perfectly nested."_err_en_US); + }; - checkPerfectNest(); + checkPerfectNest(); - ++curLevel; + ++curLevel; + } + } } } @@ -2365,76 +2370,78 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel( const parser::OmpClause *clause{GetAssociatedClause()}; bool hasCollapseClause{ clause ? (clause->Id() == llvm::omp::OMPC_collapse) : false}; - const parser::OpenMPLoopConstruct *innerMostLoop = &x; - const parser::NestedConstruct *innerMostNest = nullptr; - while (auto &optLoopCons{ - std::get>(innerMostLoop->t)}) { - innerMostNest = &(optLoopCons.value()); - if (const auto *innerLoop{ - std::get_if>( - innerMostNest)}) { - innerMostLoop = &(innerLoop->value()); - } else - break; - } - if (innerMostNest) { - if (const auto &outer{std::get_if(innerMostNest)}) { - 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); + const parser::OpenMPLoopConstruct *innerMostLoop = &x; + auto &loopConsList = + std::get>(innerMostLoop->t); + for (auto &loopCons : loopConsList) { + std::int64_t curLevel{level}; + const parser::NestedConstruct *innerMostNest = nullptr; + if (const auto &innerloop{std::get_if(&loopCons)}) { + innerMostNest = &loopCons; + } else if (const auto *innerLoop{std::get_if< + common::Indirection>( + &loopCons)}) { + PrivatizeAssociatedLoopIndexAndCheckLoopLevel(innerLoop->value()); + } + + if (innerMostNest) { + if (const auto &outer{std::get_if(innerMostNest)}) { + for (const parser::DoConstruct *loop{&*outer}; loop && curLevel > 0; + --curLevel) { + 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>( - innerMostNest)}) { - const parser::OmpDirectiveSpecification &beginSpec{ - loop->value().BeginDir()}; - const parser::OmpDirectiveName &beginName{beginSpec.DirName()}; - if (beginName.v != llvm::omp::Directive::OMPD_unroll && - beginName.v != llvm::omp::Directive::OMPD_tile) { + CheckAssocLoopLevel(curLevel, GetAssociatedClause()); + } else if (const auto *loop{std::get_if< + common::Indirection>( + innerMostNest)}) { + const parser::OmpDirectiveSpecification &beginSpec{ + loop->value().BeginDir()}; + const parser::OmpDirectiveName &beginName{beginSpec.DirName()}; + if (!llvm::omp::loopTransformationSet.test(beginName.v)) { + context_.Say(GetContext().directiveSource, + "Only Loop Transformation 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, - "Only UNROLL or TILE constructs are allowed between an OpenMP Loop Construct and a DO construct"_err_en_US, + "A DO loop must follow the %s directive"_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())); } } } diff --git a/flang/lib/Semantics/rewrite-parse-tree.cpp b/flang/lib/Semantics/rewrite-parse-tree.cpp index 5b7dab309eda7..391099d5a866b 100644 --- a/flang/lib/Semantics/rewrite-parse-tree.cpp +++ b/flang/lib/Semantics/rewrite-parse-tree.cpp @@ -195,20 +195,26 @@ void RewriteMutator::OpenMPSimdOnly( ++it; continue; } - auto &nest = - std::get>(ompLoop->t); - - if (auto *doConstruct = - std::get_if(&nest.value())) { - auto &loopBody = std::get(doConstruct->t); - // We can only remove some constructs from a loop when it's _not_ a - // OpenMP simd loop - OpenMPSimdOnly(loopBody, /*isNonSimdLoopBody=*/true); - auto newDoConstruct = std::move(*doConstruct); - auto newLoop = parser::ExecutionPartConstruct{ - parser::ExecutableConstruct{std::move(newDoConstruct)}}; + auto &nestList = + std::get>(ompLoop->t); + + std::list doList; + for (auto &nest : nestList) { + if (auto *doConstruct = std::get_if(&nest)) { + auto &loopBody = std::get(doConstruct->t); + // We can only remove some constructs from a loop when it's _not_ + // a OpenMP simd loop + OpenMPSimdOnly(loopBody, /*isNonSimdLoopBody=*/true); + auto newDoConstruct = std::move(*doConstruct); + auto newLoop = parser::ExecutionPartConstruct{ + parser::ExecutableConstruct{std::move(newDoConstruct)}}; + doList.insert(doList.end(), std::move(newLoop)); + } + } + if (!doList.empty()) { it = block.erase(it); - block.insert(it, std::move(newLoop)); + for (auto &newLoop : doList) + block.insert(it, std::move(newLoop)); continue; } } else if (auto *ompCon{std::get_if( @@ -386,13 +392,15 @@ bool RewriteMutator::Pre(parser::OpenMPLoopConstruct &ompLoop) { // If we're looking at a non-simd OpenMP loop, we need to explicitly // call OpenMPSimdOnly on the nested loop block while indicating where // the block comes from. - auto &nest = std::get>(ompLoop.t); - if (!nest.has_value()) { + auto &nestList = std::get>(ompLoop.t); + if (nestList.empty()) { return true; } - if (auto *doConstruct = std::get_if(&*nest)) { - auto &innerBlock = std::get(doConstruct->t); - OpenMPSimdOnly(innerBlock, /*isNonSimdLoopBody=*/true); + for (auto &nest : nestList) { + if (auto *doConstruct = std::get_if(&nest)) { + auto &innerBlock = std::get(doConstruct->t); + OpenMPSimdOnly(innerBlock, /*isNonSimdLoopBody=*/true); + } } } return true; diff --git a/flang/test/Parser/OpenMP/fail-looprange.f90 b/flang/test/Parser/OpenMP/fail-looprange.f90 new file mode 100644 index 0000000000000..ebe3480b44f12 --- /dev/null +++ b/flang/test/Parser/OpenMP/fail-looprange.f90 @@ -0,0 +1,11 @@ +! RUN: not %flang_fc1 -fsyntax-only -fopenmp %s 2>&1 | FileCheck %s + +! CHECK: error: expected end of line +!$omp fuse looprange + +! CHECK: error: expected end of line +!$omp fuse looprange(1) + +! CHECK: error: expected end of line +!$omp fuse looprange(1,2,3) +end diff --git a/flang/test/Parser/OpenMP/fuse-looprange.f90 b/flang/test/Parser/OpenMP/fuse-looprange.f90 new file mode 100644 index 0000000000000..75ec15fddd65f --- /dev/null +++ b/flang/test/Parser/OpenMP/fuse-looprange.f90 @@ -0,0 +1,38 @@ +! RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=60 %s | FileCheck --ignore-case %s +! RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=60 %s | FileCheck --check-prefix="PARSE-TREE" %s + +subroutine openmp_fuse(x) + + integer, intent(inout)::x + +!CHECK: !$omp fuse looprange +!$omp fuse looprange(1,2) +!CHECK: do + do x = 1, 100 + call F1() +!CHECK: end do + end do +!CHECK: do + do x = 1, 100 + call F1() +!CHECK: end do + end do +!CHECK: do + do x = 1, 100 + call F1() +!CHECK: end do + end do +!CHECK: !$omp end fuse +!$omp end fuse + +!PARSE-TREE: OpenMPConstruct -> OpenMPLoopConstruct +!PARSE-TREE: OmpBeginLoopDirective +!PARSE-TREE: OmpDirectiveName -> llvm::omp::Directive = fuse +!PARSE-TREE: OmpClauseList -> OmpClause -> Looprange -> OmpLoopRangeClause +!PARSE-TREE: Scalar -> Integer -> Constant -> Expr = '1_4' +!PARSE-TREE: LiteralConstant -> IntLiteralConstant = '1' +!PARSE-TREE: Scalar -> Integer -> Constant -> Expr = '2_4' +!PARSE-TREE: LiteralConstant -> IntLiteralConstant = '2' + +END subroutine openmp_fuse + diff --git a/flang/test/Parser/OpenMP/fuse01.f90 b/flang/test/Parser/OpenMP/fuse01.f90 new file mode 100644 index 0000000000000..98ce0e33797b5 --- /dev/null +++ b/flang/test/Parser/OpenMP/fuse01.f90 @@ -0,0 +1,28 @@ +! RUN: %flang_fc1 -fdebug-unparse -fopenmp %s | FileCheck --ignore-case %s +! RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp %s | FileCheck --check-prefix="PARSE-TREE" %s + +subroutine openmp_fuse(x) + + integer, intent(inout)::x + +!CHECK: !$omp fuse +!$omp fuse +!CHECK: do + do x = 1, 100 + call F1() +!CHECK: end do + end do +!CHECK: do + do x = 1, 100 + call F1() +!CHECK: end do + end do +!CHECK: !$omp end fuse +!$omp end fuse + +!PARSE-TREE: OpenMPConstruct -> OpenMPLoopConstruct +!PARSE-TREE: OmpBeginLoopDirective +!PARSE-TREE: OmpDirectiveName -> llvm::omp::Directive = fuse + +END subroutine openmp_fuse + diff --git a/flang/test/Parser/OpenMP/fuse02.f90 b/flang/test/Parser/OpenMP/fuse02.f90 new file mode 100644 index 0000000000000..7a46f4fe0f5cc --- /dev/null +++ b/flang/test/Parser/OpenMP/fuse02.f90 @@ -0,0 +1,95 @@ +! Test the Parse Tree to ensure the OpenMP Loop Transformation Construct Fuse can be constructed on another Fuse + +! 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 fuse_on_fuse + implicit none + integer :: I = 10 + integer :: j + + !$omp fuse + !$omp fuse + do i = 1, I + continue + end do + do j = 1, I + continue + end do + !$omp end fuse + do j = 1, I + continue + end do + !$omp end fuse +end subroutine + +!CHECK-PARSE: | ExecutionPart -> Block +!CHECK-PARSE-NEXT: | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct +!CHECK-PARSE-NEXT: | | | OmpBeginLoopDirective +!CHECK-PARSE-NEXT: | | | | OmpDirectiveName -> llvm::omp::Directive = fuse +!CHECK-PARSE-NEXT: | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | Flags = None +!CHECK-PARSE-NEXT: | | | OpenMPLoopConstruct +!CHECK-PARSE-NEXT: | | | | OmpBeginLoopDirective +!CHECK-PARSE-NEXT: | | | | | OmpDirectiveName -> llvm::omp::Directive = fuse +!CHECK-PARSE-NEXT: | | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | | Flags = None +!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 -> ContinueStmt +!CHECK-PARSE-NEXT: | | | | | EndDoStmt -> +!CHECK-PARSE-NEXT: | | | | DoConstruct +!CHECK-PARSE-NEXT: | | | | | NonLabelDoStmt +!CHECK-PARSE-NEXT: | | | | | | LoopControl -> LoopBounds +!CHECK-PARSE-NEXT: | | | | | | | Scalar -> Name = 'j' +!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 -> ContinueStmt +!CHECK-PARSE-NEXT: | | | | | EndDoStmt -> +!CHECK-PARSE-NEXT: | | | | OmpEndLoopDirective +!CHECK-PARSE-NEXT: | | | | | OmpDirectiveName -> llvm::omp::Directive = fuse +!CHECK-PARSE-NEXT: | | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | | Flags = None +!CHECK-PARSE-NEXT: | | | DoConstruct +!CHECK-PARSE-NEXT: | | | | NonLabelDoStmt +!CHECK-PARSE-NEXT: | | | | | LoopControl -> LoopBounds +!CHECK-PARSE-NEXT: | | | | | | Scalar -> Name = 'j' +!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 -> ContinueStmt +!CHECK-PARSE-NEXT: | | | | EndDoStmt -> +!CHECK-PARSE-NEXT: | | | OmpEndLoopDirective +!CHECK-PARSE-NEXT: | | | | OmpDirectiveName -> llvm::omp::Directive = fuse +!CHECK-PARSE-NEXT: | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | Flags = None + +!CHECK-UNPARSE: SUBROUTINE fuse_on_fuse +!CHECK-UNPARSE-NEXT: IMPLICIT NONE +!CHECK-UNPARSE-NEXT: INTEGER :: i = 10_4 +!CHECK-UNPARSE-NEXT: INTEGER j +!CHECK-UNPARSE-NEXT: !$OMP FUSE +!CHECK-UNPARSE-NEXT: !$OMP FUSE +!CHECK-UNPARSE-NEXT: DO i=1_4,i +!CHECK-UNPARSE-NEXT: CONTINUE +!CHECK-UNPARSE-NEXT: END DO +!CHECK-UNPARSE-NEXT: DO j=1_4,i +!CHECK-UNPARSE-NEXT: CONTINUE +!CHECK-UNPARSE-NEXT: END DO +!CHECK-UNPARSE-NEXT: !$OMP END FUSE +!CHECK-UNPARSE-NEXT: DO j=1_4,i +!CHECK-UNPARSE-NEXT: CONTINUE +!CHECK-UNPARSE-NEXT: END DO +!CHECK-UNPARSE-NEXT: !$OMP END FUSE diff --git a/flang/test/Parser/OpenMP/loop-transformation-construct04.f90 b/flang/test/Parser/OpenMP/loop-transformation-construct04.f90 new file mode 100644 index 0000000000000..65a5f2143e6aa --- /dev/null +++ b/flang/test/Parser/OpenMP/loop-transformation-construct04.f90 @@ -0,0 +1,78 @@ +! Test the Parse Tree to ensure the OpenMP Loop Transformation Construct Fuse constructs a correct sequence. + +! 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 :: j + + !$omp do + !$omp fuse + do i = 1, I + continue + end do + do j = 1, I + continue + end do + !$omp end fuse + !$omp end do +end subroutine + +!CHECK-PARSE: | ExecutionPart -> Block +!CHECK-PARSE-NEXT: | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct +!CHECK-PARSE-NEXT: | | | OmpBeginLoopDirective +!CHECK-PARSE-NEXT: | | | | OmpDirectiveName -> llvm::omp::Directive = do +!CHECK-PARSE-NEXT: | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | Flags = None +!CHECK-PARSE-NEXT: | | | OpenMPLoopConstruct +!CHECK-PARSE-NEXT: | | | | OmpBeginLoopDirective +!CHECK-PARSE-NEXT: | | | | | OmpDirectiveName -> llvm::omp::Directive = fuse +!CHECK-PARSE-NEXT: | | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | | Flags = None +!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 -> ContinueStmt +!CHECK-PARSE-NEXT: | | | | | EndDoStmt -> +!CHECK-PARSE-NEXT: | | | | DoConstruct +!CHECK-PARSE-NEXT: | | | | | NonLabelDoStmt +!CHECK-PARSE-NEXT: | | | | | | LoopControl -> LoopBounds +!CHECK-PARSE-NEXT: | | | | | | | Scalar -> Name = 'j' +!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 -> ContinueStmt +!CHECK-PARSE-NEXT: | | | | | EndDoStmt -> +!CHECK-PARSE-NEXT: | | | | OmpEndLoopDirective +!CHECK-PARSE-NEXT: | | | | | OmpDirectiveName -> llvm::omp::Directive = fuse +!CHECK-PARSE-NEXT: | | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | | Flags = None +!CHECK-PARSE-NEXT: | | | OmpEndLoopDirective +!CHECK-PARSE-NEXT: | | | | OmpDirectiveName -> llvm::omp::Directive = do +!CHECK-PARSE-NEXT: | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | Flags = None + +!CHECK-UNPARSE: SUBROUTINE loop_transformation_construct +!CHECK-UNPARSE-NEXT: IMPLICIT NONE +!CHECK-UNPARSE-NEXT: INTEGER :: i = 10_4 +!CHECK-UNPARSE-NEXT: INTEGER j +!CHECK-UNPARSE-NEXT: !$OMP DO +!CHECK-UNPARSE-NEXT: !$OMP FUSE +!CHECK-UNPARSE-NEXT: DO i=1_4,i +!CHECK-UNPARSE-NEXT: CONTINUE +!CHECK-UNPARSE-NEXT: END DO +!CHECK-UNPARSE-NEXT: DO j=1_4,i +!CHECK-UNPARSE-NEXT: CONTINUE +!CHECK-UNPARSE-NEXT: END DO +!CHECK-UNPARSE-NEXT: !$OMP END FUSE +!CHECK-UNPARSE-NEXT: !$OMP END DO diff --git a/flang/test/Parser/OpenMP/loop-transformation-construct05.f90 b/flang/test/Parser/OpenMP/loop-transformation-construct05.f90 new file mode 100644 index 0000000000000..b1e1f91766640 --- /dev/null +++ b/flang/test/Parser/OpenMP/loop-transformation-construct05.f90 @@ -0,0 +1,87 @@ +! Test the Parse Tree to ensure the OpenMP Loop Transformation Construct Fuse constructs a correct sequence +! and can correctly combine with loop nests + +! 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 :: j + + !$omp do + !$omp fuse + do i = 1, I + continue + end do + !$omp tile sizes(2) + do j = 1, I + continue + end do + !$omp end fuse + !$omp end do +end subroutine + +!CHECK-PARSE: | ExecutionPart -> Block +!CHECK-PARSE-NEXT: | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct +!CHECK-PARSE-NEXT: | | | OmpBeginLoopDirective +!CHECK-PARSE-NEXT: | | | | OmpDirectiveName -> llvm::omp::Directive = do +!CHECK-PARSE-NEXT: | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | Flags = None +!CHECK-PARSE-NEXT: | | | OpenMPLoopConstruct +!CHECK-PARSE-NEXT: | | | | OmpBeginLoopDirective +!CHECK-PARSE-NEXT: | | | | | OmpDirectiveName -> llvm::omp::Directive = fuse +!CHECK-PARSE-NEXT: | | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | | Flags = None +!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 -> ContinueStmt +!CHECK-PARSE-NEXT: | | | | | EndDoStmt -> +!CHECK-PARSE-NEXT: | | | | OpenMPLoopConstruct +!CHECK-PARSE-NEXT: | | | | | OmpBeginLoopDirective +!CHECK-PARSE-NEXT: | | | | | | OmpDirectiveName -> llvm::omp::Directive = tile +!CHECK-PARSE-NEXT: | | | | | | OmpClauseList -> OmpClause -> Sizes -> Scalar -> Integer -> Expr = '2_4' +!CHECK-PARSE-NEXT: | | | | | | | LiteralConstant -> IntLiteralConstant = '2' +!CHECK-PARSE-NEXT: | | | | | | Flags = None +!CHECK-PARSE-NEXT: | | | | | DoConstruct +!CHECK-PARSE-NEXT: | | | | | | NonLabelDoStmt +!CHECK-PARSE-NEXT: | | | | | | | LoopControl -> LoopBounds +!CHECK-PARSE-NEXT: | | | | | | | | Scalar -> Name = 'j' +!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 -> ContinueStmt +!CHECK-PARSE-NEXT: | | | | | | EndDoStmt -> +!CHECK-PARSE-NEXT: | | | | OmpEndLoopDirective +!CHECK-PARSE-NEXT: | | | | | OmpDirectiveName -> llvm::omp::Directive = fuse +!CHECK-PARSE-NEXT: | | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | | Flags = None +!CHECK-PARSE-NEXT: | | | OmpEndLoopDirective +!CHECK-PARSE-NEXT: | | | | OmpDirectiveName -> llvm::omp::Directive = do +!CHECK-PARSE-NEXT: | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | Flags = None + +!CHECK-UNPARSE: SUBROUTINE loop_transformation_construct +!CHECK-UNPARSE-NEXT: IMPLICIT NONE +!CHECK-UNPARSE-NEXT: INTEGER :: i = 10_4 +!CHECK-UNPARSE-NEXT: INTEGER j +!CHECK-UNPARSE-NEXT: !$OMP DO +!CHECK-UNPARSE-NEXT: !$OMP FUSE +!CHECK-UNPARSE-NEXT: DO i=1_4,i +!CHECK-UNPARSE-NEXT: CONTINUE +!CHECK-UNPARSE-NEXT: END DO +!CHECK-UNPARSE-NEXT: !$OMP TILE +!CHECK-UNPARSE-NEXT: DO j=1_4,i +!CHECK-UNPARSE-NEXT: CONTINUE +!CHECK-UNPARSE-NEXT: END DO +!CHECK-UNPARSE-NEXT: !$OMP END FUSE +!CHECK-UNPARSE-NEXT: !$OMP END DO diff --git a/flang/test/Semantics/OpenMP/loop-transformation-clauses01.f90 b/flang/test/Semantics/OpenMP/loop-transformation-clauses01.f90 new file mode 100644 index 0000000000000..9ca0e8cfc9af1 --- /dev/null +++ b/flang/test/Semantics/OpenMP/loop-transformation-clauses01.f90 @@ -0,0 +1,66 @@ +! Testing the Semantics of clauses on loop transformation directives + +!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60 + + +subroutine loop_transformation_construct1 + implicit none + integer, parameter:: i = 5 + integer :: x + integer :: a + integer :: v(i) + + !ERROR: At most one LOOPRANGE clause can appear on the FUSE directive + !$omp fuse looprange(1,2) looprange(1,2) + do x = 1, i + v(x) = x * 2 + end do + do x = 1, i + v(x) = x * 2 + end do + !$omp end fuse + + !ERROR: The loop range indicated in the LOOPRANGE(5,2) clause must not be out of the bounds of the Loop Sequence following the construct. + !$omp fuse looprange(5,2) + do x = 1, i + v(x) = x * 2 + end do + do x = 1, i + v(x) = x * 2 + end do + !$omp end fuse + + !ERROR: The parameter of the LOOPRANGE clause must be a constant positive integer expression + !$omp fuse looprange(0,1) + do x = 1, i + v(x) = x * 2 + end do + do x = 1, i + v(x) = x * 2 + end do + !$omp end fuse + + !ERROR: The parameter of the LOOPRANGE clause must be a constant positive integer expression + !$omp fuse looprange(1,-1) + do x = 1, i + v(x) = x * 2 + end do + do x = 1, i + v(x) = x * 2 + end do + !$omp end fuse + + !ERROR: Must be a constant value + !$omp fuse looprange(a,2) + do x = 1, i + v(x) = x * 2 + end do + !$omp end fuse + + !ERROR: Must be a constant value + !$omp fuse looprange(1,a) + do x = 1, i + v(x) = x * 2 + end do + !$omp end fuse +end subroutine diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 index f718efc32aabf..927831a06d5fa 100644 --- a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 +++ b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 @@ -62,7 +62,7 @@ subroutine loop_transformation_construct4 integer :: v(i) !$omp do - !ERROR: If a loop construct has been fully unrolled, it cannot then be tiled + !ERROR: If a loop construct has been fully unrolled, it cannot then be further transformed !$omp tile !$omp unroll full do x = 1, i @@ -77,7 +77,7 @@ subroutine loop_transformation_construct5 integer :: v(i) !$omp do - !ERROR: If a loop construct has been fully unrolled, it cannot then be tiled + !ERROR: If a loop construct has been fully unrolled, it cannot then be further transformed !$omp tile !$omp unroll do x = 1, i diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90 new file mode 100644 index 0000000000000..d82fc3668198d --- /dev/null +++ b/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90 @@ -0,0 +1,93 @@ +! Testing the Semantics of loop sequences combined with +! nested Loop Transformation Constructs + +!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60 + +subroutine loop_transformation_construct1 + implicit none + + !$omp do + !ERROR: The FUSE construct requires the END FUSE directive + !$omp fuse +end subroutine + +subroutine loop_transformation_construct2 + implicit none + + !$omp do + !ERROR: A DO loop must follow the FUSE directive + !$omp fuse + !$omp end fuse +end subroutine + +subroutine loop_transformation_construct3 + implicit none + integer :: i = 5 + integer :: y + integer :: v(i) + + !$omp do + !$omp fuse + do x = 1, i + v(x) = x(x) * 2 + end do + do x = 1, i + v(x) = x(x) * 2 + end do + !$omp end fuse + !$omp end do + !ERROR: The END FUSE directive must follow the DO loop associated with the loop construct + !$omp end fuse +end subroutine + +subroutine loop_transformation_construct4 + 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 FUSE directive + !$omp fuse + !$omp end fuse +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 further transformed + !$omp fuse + !$omp unroll full + do x = 1, i + v(x) = x(x) * 2 + end do + do x = 1, i + v(x) = x(x) * 2 + end do + !$omp end fuse +end subroutine + +subroutine loop_transformation_construct6 + implicit none + integer :: i = 5 + integer :: y + integer :: v(i) + + !$omp do + !$omp fuse looprange(1,1) + !$omp unroll partial(2) + do x = 1, i + v(x) = x(x) * 2 + end do + do x = 1, i + v(x) = x(x) * 2 + end do + !$omp end fuse +end subroutine diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90 new file mode 100644 index 0000000000000..5e459c7985523 --- /dev/null +++ b/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90 @@ -0,0 +1,39 @@ +! Testing the Semantic failure of forming loop sequences under regular OpenMP directives + +!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60 + +subroutine loop_transformation_construct1 + implicit none + integer :: i = 5 + integer :: y + integer :: v(i) + + ! Only 1 do loop is associated with the OMP DO directive so the END DO directive is unmatched + !$omp do + do x = 1, i + v(x) = x(x) * 2 + end do + do x = 1, i + v(x) = x(x) * 2 + end do + !ERROR: The END DO directive must follow the DO loop associated with the loop construct + !$omp end do +end subroutine + +subroutine loop_transformation_construct2 + implicit none + integer :: i = 5 + integer :: y + integer :: v(i) + + ! Only 1 do loop is associated with the OMP TILE directive so the END TILE directive is unmatched + !$omp tile sizes(2) + do x = 1, i + v(x) = x(x) * 2 + end do + do x = 1, i + v(x) = x(x) * 2 + end do + !ERROR: The END TILE directive must follow the DO loop associated with the loop construct + !$omp end tile +end subroutine diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct04.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct04.f90 new file mode 100644 index 0000000000000..2856247329f3b --- /dev/null +++ b/flang/test/Semantics/OpenMP/loop-transformation-construct04.f90 @@ -0,0 +1,47 @@ +! Testing the Semantic failure of forming loop sequences under regular OpenMP directives + +!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60 + +subroutine loop_transformation_construct3 + implicit none + integer, parameter :: i = 5 + integer :: x + integer :: v(i) + + !ERROR: The loop sequence following the DO construct must be fully fused first. + !$omp do + !$omp fuse looprange(1,2) + do x = 1, i + v(x) = x * 2 + end do + do x = 1, i + v(x) = x * 2 + end do + do x = 1, i + v(x) = x * 2 + end do + !$omp end fuse + !$omp end do +end subroutine + +subroutine loop_transformation_construct4 + implicit none + integer, parameter :: i = 5 + integer :: x + integer :: v(i) + + !ERROR: The loop sequence following the TILE construct must be fully fused first. + !$omp tile sizes(2) + !$omp fuse looprange(1,2) + do x = 1, i + v(x) = x * 2 + end do + do x = 1, i + v(x) = x * 2 + end do + do x = 1, i + v(x) = x * 2 + end do + !$omp end fuse + !$omp end tile +end subroutine diff --git a/flang/test/Semantics/OpenMP/tile02.f90 b/flang/test/Semantics/OpenMP/tile02.f90 index 676796375353f..096a0f349932e 100644 --- a/flang/test/Semantics/OpenMP/tile02.f90 +++ b/flang/test/Semantics/OpenMP/tile02.f90 @@ -6,7 +6,7 @@ subroutine on_unroll implicit none integer i - !ERROR: If a loop construct has been fully unrolled, it cannot then be tiled + !ERROR: If a loop construct has been fully unrolled, it cannot then be further transformed !$omp tile sizes(2) !$omp unroll do i = 1, 5