diff options
| author | Ferran Toda <f.toda.c@gmail.com> | 2025-11-21 15:16:30 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-11-21 08:16:30 -0600 |
| commit | f4ebee0ca980f807de32841288b3785dadbc471d (patch) | |
| tree | 41dfe79d995fe844eb861098488d84119a2c4cd6 | |
| parent | a2dc4e02e7ba77ddcb0afca0304535d8f142c98b (diff) | |
[Flang][OpenMP] Add semantic support for Loop Sequences and OpenMP loop fuse (#161213)
This patch adds semantics for the `omp fuse` directive in flang, as
specified in OpenMP 6.0. This patch also enables semantic support for
loop sequences which are needed for the fuse directive along with
semantics for the `looprange` clause. These changes are only semantic.
Relevant tests have been added , and previous behavior is retained with
no changes.
---------
Co-authored-by: Ferran Toda <ferran.todacasaban@bsc.es>
Co-authored-by: Krzysztof Parzyszek <Krzysztof.Parzyszek@amd.com>
23 files changed, 963 insertions, 216 deletions
diff --git a/flang/include/flang/Parser/openmp-utils.h b/flang/include/flang/Parser/openmp-utils.h index 36556f8dd7f4..7396e57144b9 100644 --- a/flang/include/flang/Parser/openmp-utils.h +++ b/flang/include/flang/Parser/openmp-utils.h @@ -123,6 +123,9 @@ template <typename T> OmpDirectiveName GetOmpDirectiveName(const T &x) { const OpenMPDeclarativeConstruct *GetOmp(const DeclarationConstruct &x); const OpenMPConstruct *GetOmp(const ExecutionPartConstruct &x); +const OpenMPLoopConstruct *GetOmpLoop(const ExecutionPartConstruct &x); +const DoConstruct *GetDoConstruct(const ExecutionPartConstruct &x); + const OmpObjectList *GetOmpObjectList(const OmpClause &clause); template <typename T> diff --git a/flang/include/flang/Semantics/openmp-directive-sets.h b/flang/include/flang/Semantics/openmp-directive-sets.h index 01e8481e0572..609a7be700c2 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 2e43e3d94cb8..b11a1a14db06 100644 --- a/flang/lib/Lower/OpenMP/OpenMP.cpp +++ b/flang/lib/Lower/OpenMP/OpenMP.cpp @@ -3507,6 +3507,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; @@ -3962,22 +3969,24 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, mlir::Location currentLocation = converter.genLocation(beginSpec.source); - if (const parser::OpenMPLoopConstruct *ompNestedLoopCons = - loopConstruct.GetNestedConstruct()) { - llvm::omp::Directive nestedDirective = - parser::omp::GetOmpDirectiveName(*ompNestedLoopCons).v; - switch (nestedDirective) { - case llvm::omp::Directive::OMPD_tile: - // Skip OMPD_tile since the tile sizes will be retrieved when - // generating the omp.loop_nest op. - break; - default: { - unsigned version = semaCtx.langOptions().OpenMPVersion; - TODO(currentLocation, - "Applying a loop-associated on the loop generated by the " + - llvm::omp::getOpenMPDirectiveName(nestedDirective, version) + - " construct"); - } + for (auto &construct : std::get<parser::Block>(loopConstruct.t)) { + if (const parser::OpenMPLoopConstruct *ompNestedLoopCons = + parser::omp::GetOmpLoop(construct)) { + llvm::omp::Directive nestedDirective = + parser::omp::GetOmpDirectiveName(*ompNestedLoopCons).v; + switch (nestedDirective) { + case llvm::omp::Directive::OMPD_tile: + // Skip OMPD_tile since the tile sizes will be retrieved when + // generating the omp.loop_nest op. + break; + default: { + unsigned version = semaCtx.langOptions().OpenMPVersion; + TODO(currentLocation, + "Applying a loop-associated on the loop generated by the " + + llvm::omp::getOpenMPDirectiveName(nestedDirective, version) + + " construct"); + } + } } } diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp index e2da60ed19de..231eea8841d4 100644 --- a/flang/lib/Parser/openmp-parsers.cpp +++ b/flang/lib/Parser/openmp-parsers.cpp @@ -2260,6 +2260,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/openmp-utils.cpp b/flang/lib/Parser/openmp-utils.cpp index 2424828293c7..dfe8dbdd5ac9 100644 --- a/flang/lib/Parser/openmp-utils.cpp +++ b/flang/lib/Parser/openmp-utils.cpp @@ -41,6 +41,23 @@ const OpenMPConstruct *GetOmp(const ExecutionPartConstruct &x) { return nullptr; } +const OpenMPLoopConstruct *GetOmpLoop(const ExecutionPartConstruct &x) { + if (auto *construct{GetOmp(x)}) { + if (auto *omp{std::get_if<OpenMPLoopConstruct>(&construct->u)}) { + return omp; + } + } + return nullptr; +} +const DoConstruct *GetDoConstruct(const ExecutionPartConstruct &x) { + if (auto *y{std::get_if<ExecutableConstruct>(&x.u)}) { + if (auto *z{std::get_if<common::Indirection<DoConstruct>>(&y->u)}) { + return &z->value(); + } + } + return nullptr; +} + const OmpObjectList *GetOmpObjectList(const OmpClause &clause) { // Clauses with OmpObjectList as its data member using MemberObjectListClauses = std::tuple<OmpClause::Copyin, diff --git a/flang/lib/Semantics/canonicalize-omp.cpp b/flang/lib/Semantics/canonicalize-omp.cpp index 0cec1969e097..f7c53d6d8f4c 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 @@ -136,20 +137,30 @@ private: "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; auto &body{std::get<parser::Block>(x.t)}; nextIt = it; - while (++nextIt != block.end()) { + nextIt++; + while (nextIt != block.end()) { // Ignore compiler directives. - if (GetConstructIf<parser::CompilerDirective>(*nextIt)) + if (GetConstructIf<parser::CompilerDirective>(*nextIt)) { + nextIt++; continue; + } if (auto *doCons{GetConstructIf<parser::DoConstruct>(*nextIt)}) { if (doCons->GetLoopControl()) { @@ -160,9 +171,12 @@ private: if (nextIt != block.end()) { if (auto *endDir{ GetConstructIf<parser::OmpEndLoopDirective>(*nextIt)}) { - std::get<std::optional<parser::OmpEndLoopDirective>>(x.t) = - std::move(*endDir); - nextIt = block.erase(nextIt); + auto &endDirName = endDir->DirName(); + if (endDirName.v != llvm::omp::Directive::OMPD_fuse) { + std::get<std::optional<parser::OmpEndLoopDirective>>(x.t) = + std::move(*endDir); + nextIt = block.erase(nextIt); + } } } } else { @@ -172,50 +186,45 @@ private: } } else if (auto *ompLoopCons{ GetOmpIf<parser::OpenMPLoopConstruct>(*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<parser::OmpEndLoopDirective>(*endIt)}) { - auto &endDirName = endDir->DirName(); - if (endDirName.v == beginName.v) { - std::get<std::optional<parser::OmpEndLoopDirective>>(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); body.push_back(std::move(*nextIt)); 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<parser::OmpEndLoopDirective>(*nextIt)}) { + auto &endDirName = endDir->DirName(); + if (endDirName.v == beginName.v && + endDirName.v != llvm::omp::Directive::OMPD_fuse) { + std::get<std::optional<parser::OmpEndLoopDirective>>(x.t) = + std::move(*endDir); + nextIt = block.erase(nextIt); } } } @@ -227,11 +236,29 @@ private: } else { missingDoConstruct(beginName, messages_); } + + if (endFuseNeeded && nextIt != block.end()) { + if (auto *endDir{ + GetConstructIf<parser::OmpEndLoopDirective>(*nextIt)}) { + auto &endDirName = endDir->DirName(); + if (endDirName.v == llvm::omp::Directive::OMPD_fuse) { + endFuseNeeded = false; + std::get<std::optional<parser::OmpEndLoopDirective>>(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 3d3596b50088..13581008433a 100644 --- a/flang/lib/Semantics/check-omp-loop.cpp +++ b/flang/lib/Semantics/check-omp-loop.cpp @@ -285,9 +285,11 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) { } SetLoopInfo(x); - if (const auto *doConstruct{x.GetNestedLoop()}) { - const auto &doBlock{std::get<parser::Block>(doConstruct->t)}; - CheckNoBranching(doBlock, beginName.v, beginName.source); + for (auto &construct : std::get<parser::Block>(x.t)) { + if (const auto *doConstruct{parser::omp::GetDoConstruct(construct)}) { + const auto &doBlock{std::get<parser::Block>(doConstruct->t)}; + CheckNoBranching(doBlock, beginName.v, beginName.source); + } } CheckLoopItrVariableIsInt(x); CheckAssociatedLoopConstraints(x); @@ -301,6 +303,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( @@ -320,24 +327,28 @@ void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct &x) { void OmpStructureChecker::CheckLoopItrVariableIsInt( const parser::OpenMPLoopConstruct &x) { - for (const parser::DoConstruct *loop{x.GetNestedLoop()}; 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 (auto &construct : std::get<parser::Block>(x.t)) { + for (const parser::DoConstruct *loop{ + parser::omp::GetDoConstruct(construct)}; + 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<parser::Block>(loop->t)}; + const auto it{block.begin()}; + loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it) + : nullptr; } - // Get the next DoConstruct if block is not empty. - const auto &block{std::get<parser::Block>(loop->t)}; - const auto it{block.begin()}; - loop = - it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it) : nullptr; } } @@ -401,23 +412,28 @@ void OmpStructureChecker::CheckDistLinear( // Match the loop index variables with the collected symbols from linear // clauses. - for (const parser::DoConstruct *loop{x.GetNestedLoop()}; 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; + for (auto &construct : std::get<parser::Block>(x.t)) { + std::int64_t curCollapseVal{collapseVal}; + for (const parser::DoConstruct *loop{ + parser::omp::GetDoConstruct(construct)}; + 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()); + } + curCollapseVal--; + if (curCollapseVal == 0) { + break; + } } + // Get the next DoConstruct if block is not empty. + const auto &block{std::get<parser::Block>(loop->t)}; + const auto it{block.begin()}; + loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it) + : nullptr; } - // Get the next DoConstruct if block is not empty. - const auto &block{std::get<parser::Block>(loop->t)}; - const auto it{block.begin()}; - loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it) - : nullptr; } // Show error for the remaining variables @@ -430,6 +446,63 @@ 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<parser::OmpClause::Looprange>(&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<parser::Block>(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<parser::Block>(x.t)}; + assert(loopConsList.size() == 1 && "Not Expecting a loop sequence"); + const auto *ompConstruct{parser::omp::GetOmpLoop(loopConsList.front())}; + if (!ompConstruct) { + return; + } + const parser::OmpClauseList &clauseList{ompConstruct->BeginDir().Clauses()}; + if (clauseList.v.empty()) { + return; + } + for (auto &clause : clauseList.v) { + if (const auto *lrClause{ + std::get_if<parser::OmpClause::Looprange>(&clause.u)}) { + auto count{GetIntValue(std::get<1>((lrClause->v).t))}; + if (!count) { + return; + } + auto &nestedLoopConsList{std::get<parser::Block>(ompConstruct->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 37b4404cc598..63751fd0c8ab 100644 --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -3401,9 +3401,11 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Sizes &c) { } 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 1b84bc5dda47..a4d74398378d 100644 --- a/flang/lib/Semantics/check-omp-structure.h +++ b/flang/lib/Semantics/check-omp-structure.h @@ -316,6 +316,8 @@ private: 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 c4d103613b58..48b23ad07762 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -525,7 +525,10 @@ public: 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; } @@ -2028,6 +2031,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); @@ -2205,8 +2209,11 @@ void OmpAttributeVisitor::CollectNumAffectedLoopsFromInnerLoopContruct( const parser::OpenMPLoopConstruct &x, llvm::SmallVector<std::int64_t> &levels, llvm::SmallVector<const parser::OmpClause *> &clauses) { - if (auto *innerConstruct{x.GetNestedConstruct()}) { - CollectNumAffectedLoopsFromLoopConstruct(*innerConstruct, levels, clauses); + for (auto &construct : std::get<parser::Block>(x.t)) { + if (auto *innerConstruct{parser::omp::GetOmpLoop(construct)}) { + CollectNumAffectedLoopsFromLoopConstruct( + *innerConstruct, levels, clauses); + } } } @@ -2271,74 +2278,74 @@ void OmpAttributeVisitor::CheckPerfectNestAndRectangularLoop( // Find the associated region by skipping nested loop-associated constructs // such as loop transformations - const parser::OpenMPLoopConstruct *innermostConstruct{&x}; - while (auto *nested{innermostConstruct->GetNestedConstruct()}) { - innermostConstruct = nested; - } - - const auto *outer{innermostConstruct->GetNestedLoop()}; - if (!outer) - return; - - llvm::SmallVector<Symbol *> 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); - } + for (auto &construct : std::get<parser::Block>(x.t)) { + if (const auto *innermostConstruct{parser::omp::GetOmpLoop(construct)}) { + CheckPerfectNestAndRectangularLoop(*innermostConstruct); + } else if (const auto *doConstruct{ + parser::omp::GetDoConstruct(construct)}) { + + llvm::SmallVector<Symbol *> ivs; + int curLevel{0}; + const auto *loop{doConstruct}; + 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<parser::Block>(loop->t)}; - if (block.empty()) { - // Insufficient number of nested loops already reported by - // CheckAssocLoopLevel() - break; - } + // Recurse into nested loop + const auto &block{std::get<parser::Block>(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<parser::ContinueStmt>(*last)) - --last; + // A trailing CONTINUE is not considered part of the loop body + if (parser::Unwrap<parser::ContinueStmt>(*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; + } + } } } @@ -2372,50 +2379,51 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel( bool hasCollapseClause{ clause ? (clause->Id() == llvm::omp::OMPC_collapse) : false}; - const parser::OpenMPLoopConstruct *innerMostNest = &x; - while (auto *nested{innerMostNest->GetNestedConstruct()}) { - innerMostNest = nested; - } - - if (const auto *outer{innerMostNest->GetNestedLoop()}) { - 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); + for (auto &construct : std::get<parser::Block>(x.t)) { + if (const auto *innermostConstruct{parser::omp::GetOmpLoop(construct)}) { + PrivatizeAssociatedLoopIndexAndCheckLoopLevel(*innermostConstruct); + } else if (const auto *doConstruct{ + parser::omp::GetDoConstruct(construct)}) { + for (const parser::DoConstruct *loop{&*doConstruct}; 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<parser::Statement<parser::NonLabelDoStmt>>(loop->t); + context_.Say(stmt.source, + "DO CONCURRENT loops cannot form part of a loop nest."_err_en_US); } - } else { - auto &stmt = - std::get<parser::Statement<parser::NonLabelDoStmt>>(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<parser::Block>(loop->t)}; - const auto it{block.begin()}; - loop = it != block.end() ? GetDoConstructIf(*it) : nullptr; + const auto &block{std::get<parser::Block>(loop->t)}; + const auto it{block.begin()}; + loop = it != block.end() ? GetDoConstructIf(*it) : nullptr; + } } + 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())); } - 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())); } } diff --git a/flang/lib/Semantics/rewrite-parse-tree.cpp b/flang/lib/Semantics/rewrite-parse-tree.cpp index b5a07680a337..285eaac1e2c8 100644 --- a/flang/lib/Semantics/rewrite-parse-tree.cpp +++ b/flang/lib/Semantics/rewrite-parse-tree.cpp @@ -9,6 +9,7 @@ #include "rewrite-parse-tree.h" #include "flang/Common/indirection.h" +#include "flang/Parser/openmp-utils.h" #include "flang/Parser/parse-tree-visitor.h" #include "flang/Parser/parse-tree.h" #include "flang/Parser/tools.h" @@ -195,18 +196,24 @@ void RewriteMutator::OpenMPSimdOnly( ++it; continue; } - if (auto *doConstruct = - const_cast<parser::DoConstruct *>(ompLoop->GetNestedLoop())) { - auto &loopBody = std::get<parser::Block>(doConstruct->t); - // We can only remove some constructs from a loop when it's _not_ a - // OpenMP simd loop - OpenMPSimdOnly(const_cast<parser::Block &>(loopBody), - /*isNonSimdLoopBody=*/true); - - auto newLoop = parser::ExecutionPartConstruct{ - parser::ExecutableConstruct{std::move(*doConstruct)}}; + std::list<parser::ExecutionPartConstruct> doList; + for (auto &construct : std::get<parser::Block>(ompLoop->t)) { + if (auto *doConstruct = const_cast<parser::DoConstruct *>( + parser::omp::GetDoConstruct(construct))) { + auto &loopBody = std::get<parser::Block>(doConstruct->t); + // We can only remove some constructs from a loop when it's _not_ + // a OpenMP simd loop + OpenMPSimdOnly(const_cast<parser::Block &>(loopBody), + /*isNonSimdLoopBody=*/true); + auto newLoop = parser::ExecutionPartConstruct{ + parser::ExecutableConstruct{std::move(*doConstruct)}}; + 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<parser::OpenMPSectionsConstruct>( @@ -384,10 +391,12 @@ 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. - if (auto *doConstruct = - const_cast<parser::DoConstruct *>(ompLoop.GetNestedLoop())) { - auto &innerBlock = std::get<parser::Block>(doConstruct->t); - OpenMPSimdOnly(innerBlock, /*isNonSimdLoopBody=*/true); + for (auto &construct : std::get<parser::Block>(ompLoop.t)) { + if (auto *doConstruct = parser::omp::GetDoConstruct(construct)) { + auto &innerBlock = std::get<parser::Block>(doConstruct->t); + OpenMPSimdOnly(const_cast<parser::Block &>(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 000000000000..ebe3480b44f1 --- /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 000000000000..75ec15fddd65 --- /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 000000000000..98ce0e33797b --- /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 000000000000..cc3de48dd658 --- /dev/null +++ b/flang/test/Parser/OpenMP/fuse02.f90 @@ -0,0 +1,97 @@ +! 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: | | | 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: | | | | | Block +!CHECK-PARSE-NEXT: | | | | | | ExecutionPartConstruct -> ExecutableConstruct -> 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: | | | | | | ExecutionPartConstruct -> ExecutableConstruct -> 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: | | | | ExecutionPartConstruct -> ExecutableConstruct -> 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 000000000000..e37e2bbfe155 --- /dev/null +++ b/flang/test/Parser/OpenMP/loop-transformation-construct04.f90 @@ -0,0 +1,80 @@ +! 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: | | | 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: | | | | | Block +!CHECK-PARSE-NEXT: | | | | | | ExecutionPartConstruct -> ExecutableConstruct -> 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: | | | | | | ExecutionPartConstruct -> ExecutableConstruct -> 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 000000000000..6d3303841d50 --- /dev/null +++ b/flang/test/Parser/OpenMP/loop-transformation-construct05.f90 @@ -0,0 +1,90 @@ +! 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: | | | 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: | | | | | Block +!CHECK-PARSE-NEXT: | | | | | | ExecutionPartConstruct -> ExecutableConstruct -> 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: | | | | | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> 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: | | | | | | | Block +!CHECK-PARSE-NEXT: | | | | | | | | ExecutionPartConstruct -> ExecutableConstruct -> 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 000000000000..9ca0e8cfc9af --- /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 f718efc32aab..927831a06d5f 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 000000000000..7cf7b15c41a6 --- /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, parameter :: i = 5 + integer :: x + integer :: v(i) + + !$omp do + !$omp fuse + do x = 1, i + v(x) = v(x) * 2 + end do + do x = 1, i + v(x) = v(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, parameter :: i = 5 + integer :: x + integer :: v(i) + + !$omp do + do x = 1, i + v(x) = v(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, parameter :: i = 5 + integer :: x + 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) = v(x) * 2 + end do + do x = 1, i + v(x) = v(x) * 2 + end do + !$omp end fuse +end subroutine + +subroutine loop_transformation_construct6 + implicit none + integer, parameter :: i = 5 + integer :: x + integer :: v(i) + + !$omp do + !$omp fuse looprange(1,1) + !$omp unroll partial(2) + do x = 1, i + v(x) = v(x) * 2 + end do + do x = 1, i + v(x) = v(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 000000000000..88c3bd2bae4e --- /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, parameter :: i = 5 + integer :: x + 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) = v(x) * 2 + end do + do x = 1, i + v(x) = v(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, parameter :: i = 5 + integer :: x + 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) = v(x) * 2 + end do + do x = 1, i + v(x) = v(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 000000000000..2856247329f3 --- /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 676796375353..096a0f349932 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 |
