summaryrefslogtreecommitdiff
path: root/flang/lib/Semantics/check-omp-loop.cpp
diff options
context:
space:
mode:
authorFerran Toda <f.toda.c@gmail.com>2025-11-21 15:16:30 +0100
committerGitHub <noreply@github.com>2025-11-21 08:16:30 -0600
commitf4ebee0ca980f807de32841288b3785dadbc471d (patch)
tree41dfe79d995fe844eb861098488d84119a2c4cd6 /flang/lib/Semantics/check-omp-loop.cpp
parenta2dc4e02e7ba77ddcb0afca0304535d8f142c98b (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.cpp139
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()};