[llvm-branch-commits] [flang] [flang][OpenMP] Refactor CountGeneratedNests, NFC (PR #185293)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sun Mar 8 08:41:01 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-semantics

Author: Krzysztof Parzyszek (kparzysz)

<details>
<summary>Changes</summary>

Extract handling of individual constructs into a helper function. Change the base count type to `int64_t` to match the type used in GetIntValue.
Rename the function to GetNumGeneratedNests.

---
Full diff: https://github.com/llvm/llvm-project/pull/185293.diff


3 Files Affected:

- (modified) flang/include/flang/Semantics/openmp-utils.h (+4) 
- (modified) flang/lib/Semantics/check-omp-loop.cpp (+18-45) 
- (modified) flang/lib/Semantics/openmp-utils.cpp (+58) 


``````````diff
diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index a10d826e4050c..7f6c2824a986a 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -117,6 +117,10 @@ MaybeExpr MakeEvaluateExpr(const parser::OmpStylizedInstance &inp);
 
 bool IsLoopTransforming(llvm::omp::Directive dir);
 bool IsFullUnroll(const parser::OpenMPLoopConstruct &x);
+
+std::optional<int64_t> GetNumGeneratedNestsFrom(
+    const parser::ExecutionPartConstruct &epc,
+    std::optional<int64_t> nestedCount);
 } // namespace omp
 } // namespace Fortran::semantics
 
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index d6e5a3f0aa7fb..45f4798d0c3c6 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -38,9 +38,9 @@
 #include <variant>
 
 namespace Fortran::semantics {
-static std::optional<size_t> CountGeneratedNests(
+static std::optional<int64_t> GetNumGeneratedNests(
     const parser::ExecutionPartConstruct &epc);
-static std::optional<size_t> CountGeneratedNests(const parser::Block &block);
+static std::optional<int64_t> GetNumGeneratedNests(const parser::Block &block);
 } // namespace Fortran::semantics
 
 namespace {
@@ -248,7 +248,7 @@ void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) {
 
 // Count the number of loop nests generated by `epc`. This is just a helper
 // function for counting the number of loop nests in a parser::Block.
-static std::optional<size_t> CountGeneratedNests(
+static std::optional<int64_t> GetNumGeneratedNests(
     const parser::ExecutionPartConstruct &epc) {
   if (parser::Unwrap<parser::DoConstruct>(epc)) {
     return 1;
@@ -258,56 +258,29 @@ static std::optional<size_t> CountGeneratedNests(
   const parser::OmpDirectiveSpecification &beginSpec{omp.BeginDir()};
   llvm::omp::Directive dir{beginSpec.DirName().v};
 
-  // TODO: Handle split, apply.
-  if (IsFullUnroll(omp)) {
-    return std::nullopt;
-  }
-  if (dir == llvm::omp::Directive::OMPD_fuse) {
-    auto nestedCount{CountGeneratedNests(std::get<parser::Block>(omp.t))};
-    // If there are no loops nested inside of FUSE, then the construct is
-    // invalid. This case will be diagnosed when analyzing the body of the FUSE
-    // construct itself, not when checking a construct in which the FUSE is
-    // nested.
-    // Returning std::nullopt prevents error messages caused by the same
-    // problem from being emitted for every enclosing loop construct, for
-    // example:
-    //   !$omp do         ! error: this should contain a loop (superfluous)
-    //   !$omp fuse       ! error: this should contain a loop
-    //   !$omp end fuse
-    if (!nestedCount || *nestedCount == 0) {
-      return std::nullopt;
-    }
-    auto *clause{
-        parser::omp::FindClause(beginSpec, llvm::omp::Clause::OMPC_looprange)};
-    if (!clause) {
-      return 1;
-    }
-
-    auto *loopRange{parser::Unwrap<parser::OmpLooprangeClause>(*clause)};
-    std::optional<int64_t> count{GetIntValue(std::get<1>(loopRange->t))};
-    if (!count || *count <= 0) {
-      return std::nullopt;
-    }
-    if (static_cast<size_t>(*count) <= *nestedCount) {
-      return 1 + *nestedCount - static_cast<size_t>(*count);
-    }
-    return std::nullopt;
+  switch (dir) {
+  case llvm::omp::Directive::OMPD_fuse:
+  case llvm::omp::Directive::OMPD_nothing:
+    return GetNumGeneratedNestsFrom(
+        epc, GetNumGeneratedNests(std::get<parser::Block>(omp.t)));
+  default:
+    break;
   }
 
   // For every other loop construct return 1.
   return 1;
 }
 
-static std::optional<size_t> CountGeneratedNests(const parser::Block &block) {
+static std::optional<int64_t> GetNumGeneratedNests(const parser::Block &block) {
   // Count the number of loops in the associated block. If there are any
   // malformed construct in there, getting the number may be meaningless.
   // These issues will be diagnosed elsewhere, and we should not emit any
   // messages about a potentially incorrect loop count.
   // In such cases reset the count to nullopt. Once it becomes nullopt,
   // keep it that way.
-  std::optional<size_t> numLoops{0};
+  std::optional<int64_t> numLoops{0};
   for (auto &epc : LoopRange(block, LoopRange::Step::Over)) {
-    if (auto genCount{CountGeneratedNests(epc)}) {
+    if (auto genCount{GetNumGeneratedNests(epc)}) {
       *numLoops += *genCount;
     } else {
       numLoops = std::nullopt;
@@ -363,7 +336,7 @@ void OmpStructureChecker::CheckNestedConstruct(
 
   // Check if a loop-nest-associated construct has only one top-level loop
   // in it.
-  if (std::optional<size_t> numLoops{CountGeneratedNests(body)}) {
+  if (std::optional<int64_t> numLoops{GetNumGeneratedNests(body)}) {
     if (*numLoops == 0) {
       context_.Say(beginSpec.DirName().source,
           "This construct should contain a DO-loop or a loop-nest-generating OpenMP construct"_err_en_US);
@@ -371,7 +344,7 @@ void OmpStructureChecker::CheckNestedConstruct(
       auto assoc{llvm::omp::getDirectiveAssociation(beginSpec.DirName().v)};
       if (*numLoops > 1 && assoc == llvm::omp::Association::LoopNest) {
         context_.Say(beginSpec.DirName().source,
-            "This construct applies to a loop nest, but has a loop sequence of length %zu"_err_en_US,
+            "This construct applies to a loop nest, but has a loop sequence of length %ld"_err_en_US,
             *numLoops);
       }
     }
@@ -585,11 +558,11 @@ void OmpStructureChecker::CheckLooprangeBounds(
     if (!first || !count || *first <= 0 || *count <= 0) {
       return;
     }
-    auto requiredCount{static_cast<size_t>(*first + *count - 1)};
-    if (auto loopCount{CountGeneratedNests(std::get<parser::Block>(x.t))}) {
+    int64_t requiredCount{*first + *count - 1};
+    if (auto loopCount{GetNumGeneratedNests(std::get<parser::Block>(x.t))}) {
       if (*loopCount < requiredCount) {
         context_.Say(clause->source,
-            "The specified loop range requires %zu loops, but the loop sequence has a length of %zu"_err_en_US,
+            "The specified loop range requires %ld loops, but the loop sequence has a length of %ld"_err_en_US,
             requiredCount, *loopCount);
       }
     }
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index dbc7e216c4788..533242287a667 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -550,4 +550,62 @@ bool IsFullUnroll(const parser::OpenMPLoopConstruct &x) {
   }
   return false;
 }
+
+std::optional<int64_t> GetNumGeneratedNestsFrom(
+    const parser::ExecutionPartConstruct &epc,
+    std::optional<int64_t> nestedCount) {
+  if (parser::Unwrap<parser::DoConstruct>(epc)) {
+    return 1;
+  }
+
+  auto &omp{DEREF(parser::Unwrap<parser::OpenMPLoopConstruct>(epc))};
+  const parser::OmpDirectiveSpecification &beginSpec{omp.BeginDir()};
+  llvm::omp::Directive dir{beginSpec.DirId()};
+  if (!IsLoopTransforming(dir)) {
+    return 0;
+  }
+
+  // TODO: Handle split, apply.
+  if (IsFullUnroll(omp)) {
+    return std::nullopt;
+  }
+
+  if (dir == llvm::omp::Directive::OMPD_fuse) {
+    // If there are no loops nested inside of FUSE, then the construct is
+    // invalid. This case will be diagnosed when analyzing the body of the FUSE
+    // construct itself, not when checking a construct in which the FUSE is
+    // nested.
+    // Returning std::nullopt prevents error messages caused by the same
+    // problem from being emitted for every enclosing loop construct, for
+    // example:
+    //   !$omp do         ! error: this should contain a loop (superfluous)
+    //   !$omp fuse       ! error: this should contain a loop
+    //   !$omp end fuse
+    if (!nestedCount || *nestedCount == 0) {
+      return std::nullopt;
+    }
+    auto *clause{
+        parser::omp::FindClause(beginSpec, llvm::omp::Clause::OMPC_looprange)};
+    if (!clause) {
+      return 1;
+    }
+
+    auto *loopRange{parser::Unwrap<parser::OmpLooprangeClause>(*clause)};
+    std::optional<int64_t> count{GetIntValue(std::get<1>(loopRange->t))};
+    if (!count || *count <= 0) {
+      return std::nullopt;
+    }
+    if (*count <= *nestedCount) {
+      return 1 + *nestedCount - *count;
+    }
+    return std::nullopt;
+  }
+
+  if (dir == llvm::omp::Directive::OMPD_nothing) {
+    return nestedCount;
+  }
+
+  // For every other loop construct return 1.
+  return 1;
+}
 } // namespace Fortran::semantics::omp

``````````

</details>


https://github.com/llvm/llvm-project/pull/185293


More information about the llvm-branch-commits mailing list