[flang-commits] [flang] 649c44f - [flang][OpenMP] Improve checks for DO CONCURRENT in loop constructs (#190990)

via flang-commits flang-commits at lists.llvm.org
Wed Apr 8 12:09:12 PDT 2026


Author: Krzysztof Parzyszek
Date: 2026-04-08T14:09:07-05:00
New Revision: 649c44fc1b39b7e636e74690e15729a178775af5

URL: https://github.com/llvm/llvm-project/commit/649c44fc1b39b7e636e74690e15729a178775af5
DIFF: https://github.com/llvm/llvm-project/commit/649c44fc1b39b7e636e74690e15729a178775af5.diff

LOG: [flang][OpenMP] Improve checks for DO CONCURRENT in loop constructs (#190990)

In OpenMP 6.0+ DO CONCURRENT is allowed as an alternative to a Canonical
Loop Nest. In other words, DO CONCURRENT is allowed inside loop
constructs as long as it's the only loop.

Add checks to detect DO CONCURRENT as the root of the associated loop
nest. Remove related checks from resolve-directives.cpp.

Added: 
    flang/test/Semantics/OpenMP/do-concurrent-collapse-60.f90

Modified: 
    flang/include/flang/Semantics/openmp-utils.h
    flang/lib/Semantics/check-omp-loop.cpp
    flang/lib/Semantics/openmp-utils.cpp
    flang/lib/Semantics/resolve-directives.cpp
    flang/test/Semantics/OpenMP/do-concurrent-collapse.f90

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index 3a199b6a652c8..b741c8eac3248 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -134,6 +134,12 @@ MaybeExpr MakeEvaluateExpr(const parser::OmpStylizedInstance &inp);
 bool IsLoopTransforming(llvm::omp::Directive dir);
 bool IsFullUnroll(const parser::OmpDirectiveSpecification &spec);
 
+inline bool IsDoConcurrentLegal(unsigned version) {
+  // DO CONCURRENT is allowed (as an alternative to a Canonical Loop Nest)
+  // in OpenMP 6.0+.
+  return version >= 60;
+}
+
 struct LoopControl {
   LoopControl(LoopControl &&x) = default;
   LoopControl(const LoopControl &x) = default;
@@ -246,6 +252,10 @@ struct LoopSequence {
   WithReason<bool> isWellFormedSequence() const;
   WithReason<bool> isWellFormedNest() const;
 
+  /// Return the first DO CONCURRENT loop contained in this sequence.
+  /// If there are no such loops, return nullptr.
+  const LoopSequence *getNestedDoConcurrent() const;
+
   std::vector<LoopControl> getLoopControls() const;
   // Check if this loop's bounds are invariant in each of the `outer`
   // constructs.

diff  --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index 951751228f34b..e1743ce71edb1 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -295,6 +295,8 @@ void OmpStructureChecker::CheckNestedConstruct(
     }
   }
 
+  // The loop sequence will correspond to the nest associated with the
+  // loop-associated construct being visited.
   LoopSequence sequence(body, version, true);
   auto assoc{llvm::omp::getDirectiveAssociation(dir)};
   auto needRange{GetAffectedLoopRangeWithReason(beginSpec, version)};
@@ -329,6 +331,14 @@ void OmpStructureChecker::CheckNestedConstruct(
     auto haveDepth{needPerfect ? havePerf : haveSema};
     std::string_view perfectTxt{needPerfect ? " perfect" : ""};
 
+    if (needDepth.value > 1 && IsDoConcurrentLegal(version)) {
+      if (auto *conc{sequence.getNestedDoConcurrent()}) {
+        auto &msg{context_.Say(*parser::GetSource(*conc->owner()),
+            "DO CONCURRENT must be the only affected loop in a loop nest"_err_en_US)};
+        needDepth.reason.AttachTo(msg);
+      }
+    }
+
     // If the present depth is 0, it's likely that the construct doesn't
     // have any loops in it, which would be diagnosed above.
     if (needDepth && haveDepth.value > 0) {

diff  --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 52bd66eae1dfa..13004fb4bab6b 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -1259,6 +1259,25 @@ void LoopSequence::createChildrenFromRange(
   }
 }
 
+const LoopSequence *LoopSequence::getNestedDoConcurrent() const {
+  // DO CONCURRENT loops are considered invalid code, even though they
+  // can be allowed in some circumstances.
+  if (!invalidIC_) {
+    return nullptr;
+  }
+  // The invalidIC_ will point to the DO CONCURRENT if that's the only
+  // invalid loop construct, but it may also point to DO WHILE.
+  for (auto &sequence : children()) {
+    auto &owner{DEREF(sequence.entry_->owner)};
+    if (auto *loop{parser::Unwrap<parser::DoConstruct>(owner)}) {
+      if (loop->IsDoConcurrent()) {
+        return &sequence;
+      }
+    }
+  }
+  return nullptr;
+}
+
 std::vector<LoopControl> LoopSequence::getLoopControls() const {
   if (!entry_->owner) {
     return {};
@@ -1597,7 +1616,7 @@ WithReason<bool> LoopSequence::isWellFormedSequence() const {
 WithReason<bool> LoopSequence::isWellFormedNest() const {
   // DO CONCURRENT is allowed at the top level in OpenMP 6.0+.
   if (invalidIC_) {
-    if (version_ < 60 || !IsDoConcurrent(*invalidIC_)) {
+    if (!IsDoConcurrentLegal(version_) || !IsDoConcurrent(*invalidIC_)) {
       return {false, WhyNotWellFormed(*invalidIC_, false)};
     }
   }

diff  --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 9ee402b18d9bd..e7c47406f4b72 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2330,12 +2330,6 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
     ivDSA = Symbol::Flag::OmpLastPrivate;
   }
 
-  bool isLoopConstruct{
-      GetContext().directive == llvm::omp::Directive::OMPD_loop};
-  const parser::OmpClause *clause{GetAssociatedClause()};
-  bool hasCollapseClause{
-      clause ? (clause->Id() == llvm::omp::OMPC_collapse) : false};
-
   for (auto &construct : std::get<parser::Block>(x.t)) {
     if (const auto *innermostConstruct{parser::omp::GetOmpLoop(construct)}) {
       PrivatizeAssociatedLoopIndexAndCheckLoopLevel(*innermostConstruct);
@@ -2343,17 +2337,6 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
                    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);
-            }
-          }
-        }
         // go through all the nested do-loops and resolve index variables
         if (const parser::Name *iv{GetLoopIndex(*loop)}) {
           if (!iv->symbol || !IsLocalInsideScope(*iv->symbol, currScope())) {

diff  --git a/flang/test/Semantics/OpenMP/do-concurrent-collapse-60.f90 b/flang/test/Semantics/OpenMP/do-concurrent-collapse-60.f90
new file mode 100644
index 0000000000000..4ba8e71a26323
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/do-concurrent-collapse-60.f90
@@ -0,0 +1,54 @@
+!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
+
+subroutine f
+  integer :: i
+
+  !ERROR: This construct requires a perfect nest of depth 2, but the associated nest is a perfect nest of depth 1
+  !BECAUSE: COLLAPSE clause was specified with argument 2
+  !$omp parallel do collapse(2)
+  do i = 1, 1
+    !BECAUSE: DO CONCURRENT loop is not a valid affected loop
+    do concurrent (integer :: j = 1:2)
+      print *, j
+    end do
+  end do
+
+  !BECAUSE: ORDERED clause was specified with argument 2
+  !$omp parallel do ordered(2)
+  !ERROR: DO CONCURRENT must be the only affected loop in a loop nest
+  do concurrent (integer :: j = 1:2)
+    do i = 1, 2
+      print *, i
+    end do
+  end do
+
+  !Ok, DO CONCURRENT is not an affected loop
+  !$omp parallel do
+  do i = 1, 1
+    do concurrent (integer :: j = 1:2)
+      print *, j
+    end do
+  end do
+
+  !Ok, DO CONCURRENT is the only affected loop
+  !$omp parallel do
+  do concurrent (integer :: j = 1:2)
+    print *, j
+  end do
+
+  !Ok, DO CONCURRENT is the only affected loop
+  !$omp loop
+  do concurrent (integer :: j = 1:2)
+    print *, j
+  end do
+
+  !ERROR: This construct requires a perfect nest of depth 2, but the associated nest is a perfect nest of depth 1
+  !BECAUSE: COLLAPSE clause was specified with argument 2
+  !$omp loop collapse(2)
+  do i = 1, 1
+    !BECAUSE: DO CONCURRENT loop is not a valid affected loop
+    do concurrent (integer :: j = 1:2)
+      print *, j
+    end do
+  end do
+end

diff  --git a/flang/test/Semantics/OpenMP/do-concurrent-collapse.f90 b/flang/test/Semantics/OpenMP/do-concurrent-collapse.f90
index 4c5f2a85403a0..7cd10518d845a 100644
--- a/flang/test/Semantics/OpenMP/do-concurrent-collapse.f90
+++ b/flang/test/Semantics/OpenMP/do-concurrent-collapse.f90
@@ -3,7 +3,6 @@
 integer :: i, j
 ! ERROR: This construct requires a perfect nest of depth 2, but the associated nest is a perfect nest of depth 1
 ! BECAUSE: COLLAPSE clause was specified with argument 2
-! ERROR: DO CONCURRENT loops cannot be used with the COLLAPSE clause.
 !$omp parallel do collapse(2)
 do i = 1, 1
   ! BECAUSE: DO CONCURRENT loop is not a valid affected loop
@@ -36,7 +35,6 @@
 
 ! ERROR: This construct requires a perfect nest of depth 2, but the associated nest is a perfect nest of depth 1
 ! BECAUSE: COLLAPSE clause was specified with argument 2
-! ERROR: DO CONCURRENT loops cannot be used with the COLLAPSE clause.
 !$omp loop collapse(2)
 do i = 1, 1
   ! BECAUSE: DO CONCURRENT loop is not a valid affected loop


        


More information about the flang-commits mailing list