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 /flang/lib/Semantics/check-omp-loop.cpp | |
| 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>
Diffstat (limited to 'flang/lib/Semantics/check-omp-loop.cpp')
| -rw-r--r-- | flang/lib/Semantics/check-omp-loop.cpp | 139 |
1 files changed, 106 insertions, 33 deletions
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()}; |
