Skip to content

Conversation

@kparzysz
Copy link
Contributor

Example based on the gfortran test a.6.1.f90

  do 100 i = 1,10
  !$omp do
    do 100 j = 1,10
      call work(i,j)
    100 continue

During canonicalization of label-DO loops, if the body of an OpenMP construct ends with a label, treat the label as ending the construct itself.

This will also allow handling of cases like

  do 100 i = 1, 10
  !$omp atomic write
  100 x = i

which we were unable to before.

Example based on the gfortran test a.6.1.f90
```
  do 100 i = 1,10
  !$omp do
    do 100 j = 1,10
      call work(i,j)
    100 continue
```

During canonicalization of label-DO loops, if the body of an OpenMP
construct ends with a label, treat the label as ending the construct
itself.

This will also allow handling of cases like
```
  do 100 i = 1, 10
  !$omp atomic write
  100 x = i
```
which we were unable to before.
@kparzysz kparzysz requested a review from tblah November 22, 2025 22:32
@llvmbot llvmbot added flang Flang issues not falling into any other category flang:openmp flang:semantics flang:parser labels Nov 22, 2025
@llvmbot
Copy link
Member

llvmbot commented Nov 22, 2025

@llvm/pr-subscribers-flang-parser

@llvm/pr-subscribers-flang-semantics

Author: Krzysztof Parzyszek (kparzysz)

Changes

Example based on the gfortran test a.6.1.f90

  do 100 i = 1,10
  !$omp do
    do 100 j = 1,10
      call work(i,j)
    100 continue

During canonicalization of label-DO loops, if the body of an OpenMP construct ends with a label, treat the label as ending the construct itself.

This will also allow handling of cases like

  do 100 i = 1, 10
  !$omp atomic write
  100 x = i

which we were unable to before.


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

5 Files Affected:

  • (modified) flang/include/flang/Parser/openmp-utils.h (+1)
  • (modified) flang/lib/Parser/openmp-utils.cpp (+39)
  • (modified) flang/lib/Semantics/canonicalize-do.cpp (+14-3)
  • (added) flang/test/Parser/OpenMP/atomic-label-do.f90 (+39)
  • (added) flang/test/Parser/OpenMP/cross-label-do.f90 (+48)
diff --git a/flang/include/flang/Parser/openmp-utils.h b/flang/include/flang/Parser/openmp-utils.h
index d4b739f4c9529..b72164e6cef4b 100644
--- a/flang/include/flang/Parser/openmp-utils.h
+++ b/flang/include/flang/Parser/openmp-utils.h
@@ -135,6 +135,7 @@ template <typename T> struct IsStatement<Statement<T>> {
 };
 
 std::optional<Label> GetStatementLabel(const ExecutionPartConstruct &x);
+std::optional<Label> GetFinalLabel(const OpenMPConstruct &x);
 
 const OmpObjectList *GetOmpObjectList(const OmpClause &clause);
 
diff --git a/flang/lib/Parser/openmp-utils.cpp b/flang/lib/Parser/openmp-utils.cpp
index 3201e5149e27c..ab2ed0641f4c7 100644
--- a/flang/lib/Parser/openmp-utils.cpp
+++ b/flang/lib/Parser/openmp-utils.cpp
@@ -15,6 +15,7 @@
 #include "flang/Common/indirection.h"
 #include "flang/Common/template.h"
 #include "flang/Common/visit.h"
+#include "flang/Parser/tools.h"
 
 #include <tuple>
 #include <type_traits>
@@ -77,6 +78,44 @@ std::optional<Label> GetStatementLabel(const ExecutionPartConstruct &x) {
   return GetStatementLabelHelper(x);
 }
 
+static std::optional<Label> GetFinalLabel(const Block &x) {
+  if (!x.empty()) {
+    const ExecutionPartConstruct &last{x.back()};
+    if (auto *omp{Unwrap<OpenMPConstruct>(last)}) {
+      return GetFinalLabel(*omp);
+    } else if (auto *doLoop{Unwrap<DoConstruct>(last)}) {
+      return GetFinalLabel(std::get<Block>(doLoop->t));
+    } else {
+      return GetStatementLabel(x.back());
+    }
+  } else {
+    return std::nullopt;
+  }
+}
+
+std::optional<Label> GetFinalLabel(const OpenMPConstruct &x) {
+  return common::visit(
+      [](auto &&s) -> std::optional<Label> {
+        using TypeS = llvm::remove_cvref_t<decltype(s)>;
+        if constexpr (std::is_same_v<TypeS, OpenMPSectionsConstruct>) {
+          auto &list{std::get<std::list<OpenMPConstruct>>(s.t)};
+          if (!list.empty()) {
+            return GetFinalLabel(list.back());
+          } else {
+            return std::nullopt;
+          }
+        } else if constexpr ( //
+            std::is_same_v<TypeS, OpenMPLoopConstruct> ||
+            std::is_same_v<TypeS, OpenMPSectionConstruct> ||
+            std::is_base_of_v<OmpBlockConstruct, TypeS>) {
+          return GetFinalLabel(std::get<Block>(s.t));
+        } else {
+          return std::nullopt;
+        }
+      },
+      x.u);
+}
+
 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-do.cpp b/flang/lib/Semantics/canonicalize-do.cpp
index ef20cffc3e0c3..d98ea48f67f29 100644
--- a/flang/lib/Semantics/canonicalize-do.cpp
+++ b/flang/lib/Semantics/canonicalize-do.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "canonicalize-do.h"
+#include "flang/Parser/openmp-utils.h"
 #include "flang/Parser/parse-tree-visitor.h"
 
 namespace Fortran::parser {
@@ -87,6 +88,12 @@ class CanonicalizationOfDoLoops {
                 [&](Statement<ActionStmt> &actionStmt) {
                   CanonicalizeIfMatch(block, stack, i, actionStmt);
                 },
+                [&](common::Indirection<OpenMPConstruct> &construct) {
+                  // If the body of the OpenMP construct ends with a label,
+                  // treat the label as ending the construct itself.
+                  CanonicalizeIfMatch(
+                      block, stack, i, omp::GetFinalLabel(construct.value()));
+                },
             },
             executableConstruct->u);
       }
@@ -97,10 +104,14 @@ class CanonicalizationOfDoLoops {
   template <typename T>
   void CanonicalizeIfMatch(Block &originalBlock, std::vector<LabelInfo> &stack,
       Block::iterator &i, Statement<T> &statement) {
-    if (!stack.empty() && statement.label &&
-        stack.back().label == *statement.label) {
+    CanonicalizeIfMatch(originalBlock, stack, i, statement.label);
+  }
+
+  void CanonicalizeIfMatch(Block &originalBlock, std::vector<LabelInfo> &stack,
+      Block::iterator &i, std::optional<Label> label) {
+    if (!stack.empty() && label && stack.back().label == *label) {
       auto currentLabel{stack.back().label};
-      if constexpr (std::is_same_v<T, common::Indirection<EndDoStmt>>) {
+      if (Unwrap<EndDoStmt>(*i)) {
         std::get<ExecutableConstruct>(i->u).u = Statement<ActionStmt>{
             std::optional<Label>{currentLabel}, ContinueStmt{}};
       }
diff --git a/flang/test/Parser/OpenMP/atomic-label-do.f90 b/flang/test/Parser/OpenMP/atomic-label-do.f90
new file mode 100644
index 0000000000000..06197587b2d19
--- /dev/null
+++ b/flang/test/Parser/OpenMP/atomic-label-do.f90
@@ -0,0 +1,39 @@
+!RUN: %flang_fc1 -fdebug-unparse -fopenmp %s | FileCheck --ignore-case --check-prefix="UNPARSE" %s
+!RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp %s | FileCheck --check-prefix="PARSE-TREE" %s
+
+subroutine f
+  integer :: i, x
+  do 100 i = 1, 10
+  !$omp atomic write
+  100 x = i
+end
+
+!UNPARSE: SUBROUTINE f
+!UNPARSE:  INTEGER i, x
+!UNPARSE:  DO i=1_4,10_4
+!UNPARSE: !$OMP ATOMIC WRITE
+!UNPARSE:   100  x=i
+!UNPARSE:  END DO
+!UNPARSE: END SUBROUTINE
+
+!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> DoConstruct
+!PARSE-TREE: | NonLabelDoStmt
+!PARSE-TREE: | | LoopControl -> LoopBounds
+!PARSE-TREE: | | | Scalar -> Name = 'i'
+!PARSE-TREE: | | | Scalar -> Expr = '1_4'
+!PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '1'
+!PARSE-TREE: | | | Scalar -> Expr = '10_4'
+!PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '10'
+!PARSE-TREE: | Block
+!PARSE-TREE: | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPAtomicConstruct
+!PARSE-TREE: | | | OmpBeginDirective
+!PARSE-TREE: | | | | OmpDirectiveName -> llvm::omp::Directive = atomic
+!PARSE-TREE: | | | | OmpClauseList -> OmpClause -> Write
+!PARSE-TREE: | | | | Flags = None
+!PARSE-TREE: | | | Block
+!PARSE-TREE: | | | | ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> AssignmentStmt = 'x=i'
+!PARSE-TREE: | | | | | Variable = 'x'
+!PARSE-TREE: | | | | | | Designator -> DataRef -> Name = 'x'
+!PARSE-TREE: | | | | | Expr = 'i'
+!PARSE-TREE: | | | | | | Designator -> DataRef -> Name = 'i'
+!PARSE-TREE: | EndDoStmt ->
diff --git a/flang/test/Parser/OpenMP/cross-label-do.f90 b/flang/test/Parser/OpenMP/cross-label-do.f90
new file mode 100644
index 0000000000000..52ac264756dfc
--- /dev/null
+++ b/flang/test/Parser/OpenMP/cross-label-do.f90
@@ -0,0 +1,48 @@
+!RUN: %flang_fc1 -fdebug-unparse -fopenmp %s | FileCheck --ignore-case --check-prefix="UNPARSE" %s
+!RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp %s | FileCheck --check-prefix="PARSE-TREE" %s
+
+subroutine f00
+  integer :: i, j
+  do 100 i = 1,10
+!$omp do
+    do 100 j = 1,10
+    100 continue
+end
+
+!UNPARSE:  SUBROUTINE f00
+!UNPARSE:   INTEGER i, j
+!UNPARSE:   DO i=1_4,10_4
+!UNPARSE: !$OMP DO
+!UNPARSE:    DO j=1_4,10_4
+!UNPARSE:     100 CONTINUE
+!UNPARSE:    END DO
+!UNPARSE:   END DO
+!UNPARSE:  END SUBROUTINE
+
+!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> DoConstruct
+!PARSE-TREE: | NonLabelDoStmt
+!PARSE-TREE: | | LoopControl -> LoopBounds
+!PARSE-TREE: | | | Scalar -> Name = 'i'
+!PARSE-TREE: | | | Scalar -> Expr = '1_4'
+!PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '1'
+!PARSE-TREE: | | | Scalar -> Expr = '10_4'
+!PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '10'
+!PARSE-TREE: | Block
+!PARSE-TREE: | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct
+!PARSE-TREE: | | | OmpBeginLoopDirective
+!PARSE-TREE: | | | | OmpDirectiveName -> llvm::omp::Directive = do
+!PARSE-TREE: | | | | OmpClauseList ->
+!PARSE-TREE: | | | | Flags = None
+!PARSE-TREE: | | | Block
+!PARSE-TREE: | | | | ExecutionPartConstruct -> ExecutableConstruct -> DoConstruct
+!PARSE-TREE: | | | | | NonLabelDoStmt
+!PARSE-TREE: | | | | | | LoopControl -> LoopBounds
+!PARSE-TREE: | | | | | | | Scalar -> Name = 'j'
+!PARSE-TREE: | | | | | | | Scalar -> Expr = '1_4'
+!PARSE-TREE: | | | | | | | | LiteralConstant -> IntLiteralConstant = '1'
+!PARSE-TREE: | | | | | | | Scalar -> Expr = '10_4'
+!PARSE-TREE: | | | | | | | | LiteralConstant -> IntLiteralConstant = '10'
+!PARSE-TREE: | | | | | Block
+!PARSE-TREE: | | | | | | ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> ContinueStmt
+!PARSE-TREE: | | | | | EndDoStmt ->
+!PARSE-TREE: | EndDoStmt ->

@kparzysz
Copy link
Contributor Author

I'm going to commit it, because it unbreaks some CI buildbots (those that run gfortran test suite). Still, please leave comments if you have any concerns.

@kparzysz kparzysz merged commit c81a189 into main Nov 22, 2025
15 checks passed
@kparzysz kparzysz deleted the users/kparzysz/label-do-fix branch November 22, 2025 22:51
@llvm-ci
Copy link
Collaborator

llvm-ci commented Nov 22, 2025

LLVM Buildbot has detected a new failure on builder flang-x86_64-windows running on minipc-ryzen-win while building flang at step 6 "build-unified-tree".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/166/builds/4172

Here is the relevant piece of the build log for the reference
Step 6 (build-unified-tree) failure: build (failure)
...
2.364 [158/6/3] Linking CXX executable bin\llvm-config.exe
8.801 [157/6/4] Building CXX object lib\Object\CMakeFiles\LLVMObject.dir\IRSymtab.cpp.obj
8.967 [156/6/5] Linking CXX static library lib\LLVMObject.lib
9.793 [155/6/6] Linking CXX executable bin\llvm-ar.exe
9.876 [154/6/7] Generating ../../bin/llvm-ranlib.exe
9.969 [153/6/8] Generating ../../bin/llvm-lib.exe
10.958 [152/6/9] Linking CXX executable bin\llvm-nm.exe
12.049 [151/6/10] Linking CXX executable bin\llvm-objdump.exe
12.926 [150/6/11] Linking CXX executable bin\llvm-rc.exe
12.953 [149/6/12] Building CXX object tools\flang\lib\Semantics\CMakeFiles\FortranSemantics.dir\canonicalize-do.cpp.obj
FAILED: [code=2] tools/flang/lib/Semantics/CMakeFiles/FortranSemantics.dir/canonicalize-do.cpp.obj 
ccache C:\PROGRA~1\MICROS~2\2022\COMMUN~1\VC\Tools\MSVC\1444~1.352\bin\Hostx64\x64\cl.exe  /nologo /TP -DFLANG_INCLUDE_TESTS=1 -DGTEST_HAS_RTTI=0 -DUNICODE -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS -D_GLIBCXX_ASSERTIONS -D_HAS_EXCEPTIONS=0 -D_SCL_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -IC:\buildbot\flang-x86_64-windows\build\tools\flang\lib\Semantics -IC:\buildbot\flang-x86_64-windows\llvm-project\flang\lib\Semantics -IC:\buildbot\flang-x86_64-windows\llvm-project\flang\include -IC:\buildbot\flang-x86_64-windows\build\tools\flang\include -IC:\buildbot\flang-x86_64-windows\build\include -IC:\buildbot\flang-x86_64-windows\llvm-project\llvm\include -external:IC:\buildbot\flang-x86_64-windows\llvm-project\flang\..\mlir\include -external:IC:\buildbot\flang-x86_64-windows\build\tools\mlir\include -external:IC:\buildbot\flang-x86_64-windows\build\tools\clang\include -external:IC:\buildbot\flang-x86_64-windows\llvm-project\llvm\..\clang\include -external:W0 /DWIN32 /D_WINDOWS   /Zc:inline /Zc:preprocessor /Zc:__cplusplus /Oi /bigobj /permissive- /W4 -wd4141 -wd4146 -wd4244 -wd4267 -wd4291 -wd4351 -wd4456 -wd4457 -wd4458 -wd4459 -wd4503 -wd4624 -wd4722 -wd4100 -wd4127 -wd4512 -wd4505 -wd4610 -wd4510 -wd4702 -wd4245 -wd4706 -wd4310 -wd4701 -wd4703 -wd4389 -wd4611 -wd4805 -wd4204 -wd4577 -wd4091 -wd4592 -wd4319 -wd4709 -wd5105 -wd4324 -wd4251 -wd4275 -w14062 -we4238 /Gw /O2 /Ob2  -std:c++17 -MD  /EHs-c- /GR- -UNDEBUG /showIncludes /Fotools\flang\lib\Semantics\CMakeFiles\FortranSemantics.dir\canonicalize-do.cpp.obj /Fdtools\flang\lib\Semantics\CMakeFiles\FortranSemantics.dir\FortranSemantics.pdb /FS -c C:\buildbot\flang-x86_64-windows\llvm-project\flang\lib\Semantics\canonicalize-do.cpp
..\llvm-project\flang\lib\Semantics\canonicalize-do.cpp(114): error C2065: 'Unwrap': undeclared identifier
..\llvm-project\flang\lib\Semantics\canonicalize-do.cpp(114): error C2275: 'Fortran::parser::EndDoStmt': expected an expression instead of a type
13.697 [149/5/13] Linking CXX executable bin\llvm-objcopy.exe
27.225 [149/4/14] Building CXX object lib\CodeGen\AsmPrinter\CMakeFiles\LLVMAsmPrinter.dir\AsmPrinter.cpp.obj
28.830 [149/3/15] Building CXX object lib\LTO\CMakeFiles\LLVMLTO.dir\LTO.cpp.obj
127.422 [149/2/16] Building CXX object tools\flang\lib\Semantics\CMakeFiles\FortranSemantics.dir\check-omp-loop.cpp.obj
296.703 [149/1/17] Building CXX object tools\flang\lib\Semantics\CMakeFiles\FortranSemantics.dir\check-omp-atomic.cpp.obj
ninja: build stopped: subcommand failed.
Cache directory:      C:\Users\buildbot-worker\AppData\Local\ccache
Config file:          C:\Users\buildbot-worker\AppData\Local\ccache\ccache.conf
System config file:   C:\ProgramData\ccache\ccache.conf
Stats updated:        11/22/25 14:59:36
Cacheable calls:          6 /   7 (85.71%)
  Hits:                   1 /   6 (16.67%)
    Direct:               1 /   1 (100.0%)
    Preprocessed:         0 /   1 ( 0.00%)
  Misses:                 5 /   6 (83.33%)
Uncacheable calls:        1 /   7 (14.29%)
  Compilation failed:     1 /   1 (100.0%)
Successful lookups:
  Direct:                 1 /   7 (14.29%)
  Preprocessed:           0 /   6 ( 0.00%)
Local storage:
  Cache size (GB):      5.0 / 5.0 (101.0%)
  Files:              19093
  Cleanups:               5
  Hits:                   1 /   6 (16.67%)
  Misses:                 5 /   6 (83.33%)
  Reads:                 14
  Writes:                10

@kparzysz
Copy link
Contributor Author

The gfortran regression failure in a.6.2.f90 is because the test unexpectedly passes. PR to disable this test: llvm/llvm-test-suite#296

aadeshps-mcw pushed a commit to aadeshps-mcw/llvm-project that referenced this pull request Nov 26, 2025
…llvm#169191)

Example based on the gfortran test a.6.1.f90
```
  do 100 i = 1,10
  !$omp do
    do 100 j = 1,10
      call work(i,j)
    100 continue
```

During canonicalization of label-DO loops, if the body of an OpenMP
construct ends with a label, treat the label as ending the construct
itself.

This will also allow handling of cases like
```
  do 100 i = 1, 10
  !$omp atomic write
  100 x = i
```
which we were unable to before.
Priyanshu3820 pushed a commit to Priyanshu3820/llvm-project that referenced this pull request Nov 26, 2025
…llvm#169191)

Example based on the gfortran test a.6.1.f90
```
  do 100 i = 1,10
  !$omp do
    do 100 j = 1,10
      call work(i,j)
    100 continue
```

During canonicalization of label-DO loops, if the body of an OpenMP
construct ends with a label, treat the label as ending the construct
itself.

This will also allow handling of cases like
```
  do 100 i = 1, 10
  !$omp atomic write
  100 x = i
```
which we were unable to before.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

flang:openmp flang:parser flang:semantics flang Flang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants