[llvm-branch-commits] [flang] [flang] Let a directive keep the loop it owns when its body branches (PR #227707)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Sep 30 06:20:53 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-openacc

@llvm/pr-subscribers-flang-fir-hlfir

Author: Kareem Ergawy (ergawy)

<details>
<summary>Changes</summary>

A loop whose branching is confined to its body keeps its structured form, but the construct holding it stayed Unstructured. A directive does not merely contain such a loop, it owns it, and its lowering reads the construct's own classification to decide whether the loop op carries its bounds. The directive was left with a bounds-free loop that nothing could partition, and the loop it owns became a second one nested inside.

Reclassify a directive construct once the loops it holds no longer need it to stay Unstructured. Children are visited first, so those loops have already been reclassified by the time the construct is reached. A construct whose branching leaves it is untouched, as is one holding a branch of its own.

Taking a loop over also means genFIR(DoConstruct) -- where a plain loop folds a body whose branching stays inside it into a region -- never runs for that loop, so fold its body through the same helper. A construct that takes over no loop, acc data or acc parallel without a loop directive, keeps having its own evaluations lowered.

The cases this enables move out of the Todo tests, which asserted the lowering was not yet implemented. The one whose EXIT leaves the loop stays there.

---

Patch is 31.13 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/227707.diff


10 Files Affected:

- (modified) flang/lib/Lower/Bridge.cpp (+14) 
- (modified) flang/lib/Lower/PFTBuilder.cpp (+33-4) 
- (removed) flang/test/Lower/OpenACC/Todo/acc-unstructured-combined-construct.f90 (-29) 
- (modified) flang/test/Lower/OpenACC/Todo/acc-unstructured-loop-construct.f90 (+8-124) 
- (added) flang/test/Lower/OpenACC/acc-directive-loop-bounds.f90 (+66) 
- (added) flang/test/Lower/OpenACC/acc-unstructured-combined-construct.f90 (+45) 
- (modified) flang/test/Lower/OpenACC/acc-unstructured-internals.f90 (+4-1) 
- (added) flang/test/Lower/OpenACC/acc-unstructured-loop-construct.f90 (+160) 
- (modified) flang/test/Lower/OpenACC/acc-unstructured.f90 (+23-16) 
- (added) flang/test/Lower/OpenMP/wsloop-directive-loop-bounds.f90 (+34) 


``````````diff
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index d4c0e547efe88..9a35b82b8f20c 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -3903,6 +3903,10 @@ class FirConverter : public Fortran::lower::AbstractConverter {
         std::get_if<Fortran::parser::OpenACCCombinedConstruct>(&acc.u);
 
     Fortran::lower::pft::Evaluation *curEval = &getEval();
+    // The loop the directive takes over, once the descent below has found it.
+    // A construct that owns no loop -- acc data, or acc parallel without a
+    // loop directive -- leaves this null and has its own evaluations lowered.
+    Fortran::lower::pft::Evaluation *absorbedLoop = nullptr;
     bool collapseForce = false;
     uint64_t collapseDepth = 1;
     uint64_t loopCount = 1;
@@ -3947,6 +3951,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
             break;
           curEval = nextDo;
         }
+      // The descent lands on the loop the directive takes over, and every
+      // level it steps through is one. A construct whose first evaluation is
+      // not a loop takes over none.
+      if (outerDo)
+        absorbedLoop = curEval;
     }
 
     // collapse(force: ...) allows statements between the loop levels the
@@ -4006,6 +4015,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
 
     if (collapseForce && collapseDepth > 1) {
       genCollapseForceBody();
+    } else if (absorbedLoop && absorbedLoop->lowerBodyAsWrappedRegion()) {
+      // Taking the loop over means genFIR(DoConstruct) -- where a plain loop
+      // folds a body that branches into a region -- never runs for it. Such a
+      // body still needs that region, so fold it through the same helper.
+      genLoopBodyEvaluations(*absorbedLoop, /*unstructuredContext=*/true);
     } else if (curEval->hasNestedEvaluations()) {
       for (Fortran::lower::pft::Evaluation &e : curEval->getNestedEvaluations())
         genFIR(e);
diff --git a/flang/lib/Lower/PFTBuilder.cpp b/flang/lib/Lower/PFTBuilder.cpp
index 2f665a6cbf693..baaeb72906cbf 100644
--- a/flang/lib/Lower/PFTBuilder.cpp
+++ b/flang/lib/Lower/PFTBuilder.cpp
@@ -2990,15 +2990,36 @@ static bool isStructurableWithUnstructuredInternals(
   return true;
 }
 
+/// Return true if \p construct owes its classification entirely to the
+/// evaluations it holds, none of which is Unstructured any more.
+static bool
+holdsNothingUnstructured(const Fortran::lower::pft::Evaluation &construct) {
+  if (!construct.evaluationList)
+    return false;
+  for (const Fortran::lower::pft::Evaluation &nested :
+       *construct.evaluationList)
+    if (nested.isUnstructured())
+      return false;
+  return true;
+}
+
 /// Reclassify every qualifying loop in \p unit.
 ///
 /// Runs after branch analysis, when the incoming-branch map is complete;
 /// during analysis a branch later in the function would not yet be recorded
 /// and condition 2 would read a partial map.
 ///
-/// Ancestors are deliberately left alone. A loop reclassified here lowers to a
-/// structured op, and a structured op is legal inside an unstructured parent,
-/// so leaving the parent Unstructured is conservative but correct.
+/// Ordinary ancestors are deliberately left alone. A loop reclassified here
+/// lowers to a structured op, and a structured op is legal inside an
+/// unstructured parent, so leaving the parent Unstructured is conservative but
+/// correct.
+///
+/// A directive construct is the exception, because it does not merely contain
+/// the loop: the directive owns it, and its lowering reads the construct's own
+/// classification to decide whether the loop op carries its bounds. Left
+/// Unstructured, the construct yields a bounds-free loop that nothing can
+/// partition, and the reclassified loop inside it becomes a second, nested
+/// one. Weaken such a construct once the loops it holds no longer need it.
 static void detectStructuredWithUnstructuredInternals(
     Fortran::lower::pft::FunctionLikeUnit &unit) {
   // Such a loop is lowered with its body in an scf.execute_region: its
@@ -3015,11 +3036,19 @@ static void detectStructuredWithUnstructuredInternals(
             visit(*e.evaluationList);
 
           if (e.isA<parser::DoConstruct>() &&
-              isStructurableWithUnstructuredInternals(e, unit))
+              isStructurableWithUnstructuredInternals(e, unit)) {
             // The one place the classification weakens: detection has proven
             // Unstructured unnecessary.
             e.weakenControlFlow(Fortran::lower::pft::Evaluation::ControlFlow::
                                     StructuredWithUnstructuredInternals);
+            continue;
+          }
+
+          // Children are visited first, so the loops this construct owns have
+          // already been reclassified by the time it is reached.
+          if (e.isExecutableDirective() && holdsNothingUnstructured(e))
+            e.weakenControlFlow(Fortran::lower::pft::Evaluation::ControlFlow::
+                                    StructuredWithUnstructuredInternals);
         }
       };
   visit(unit.evaluationList);
diff --git a/flang/test/Lower/OpenACC/Todo/acc-unstructured-combined-construct.f90 b/flang/test/Lower/OpenACC/Todo/acc-unstructured-combined-construct.f90
deleted file mode 100644
index 110940d9e5069..0000000000000
--- a/flang/test/Lower/OpenACC/Todo/acc-unstructured-combined-construct.f90
+++ /dev/null
@@ -1,29 +0,0 @@
-! Each sub-file exercises a different unstructured-CFG pattern inside a
-! combined `acc parallel loop` construct (default parallelism is
-! `independent`).
-
-! RUN: bbc -fopenacc -emit-hlfir %s -o - | FileCheck %s --check-prefix=CYCLE2-OK
-! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir --emit-independent-loops-as-unstructured=false %s -o - 2>&1 | FileCheck %s --check-prefix=CYCLE2
-
-subroutine test_unstructured_collapse_cycle(a)
-  integer :: i, j, jdiag
-  real(8) :: a(:,:)
-  jdiag = 4
-  !$acc parallel loop collapse(2) copy(a)
-  do j = 1, 8
-    do i = 1, 8
-      if (i == jdiag) then
-        a(i, j) = 0.0d0
-        cycle
-      end if
-      a(i, j) = real(i + j, 8)
-    end do
-  end do
-  !$acc end parallel loop
-end subroutine
-
-! CYCLE2: not yet implemented: unstructured do loop in combined acc construct
-
-! CYCLE2-OK-LABEL: func.func @_QPtest_unstructured_collapse_cycle
-! CYCLE2-OK: acc.parallel combined(loop)
-! CYCLE2-OK: acc.loop combined(parallel)
diff --git a/flang/test/Lower/OpenACC/Todo/acc-unstructured-loop-construct.f90 b/flang/test/Lower/OpenACC/Todo/acc-unstructured-loop-construct.f90
index f26e95d235d04..a35a930a58de9 100644
--- a/flang/test/Lower/OpenACC/Todo/acc-unstructured-loop-construct.f90
+++ b/flang/test/Lower/OpenACC/Todo/acc-unstructured-loop-construct.f90
@@ -1,99 +1,12 @@
-! Each sub-file exercises a different unstructured-CFG pattern inside an
-! `acc loop` whose default parallelism resolves to `independent`.
+! An `acc loop` whose default parallelism resolves to `independent` and whose
+! body branching is not confined to it.
+!
+! The EXIT leaves the loop, so the loop stays unstructured and cannot be
+! lowered with its bounds on the op. The patterns that do keep their branching
+! inside the body live in ../acc-unstructured-loop-construct.f90.
 
-! RUN: split-file %s %t
-
-! By default (--emit-independent-loops-as-unstructured=true), the loops are
-! lowered to `acc.loop` operations.
-! RUN: bbc -fopenacc -emit-hlfir %t/goto_one_level.f90 -o - | FileCheck %s --check-prefix=GOTO1-OK
-! RUN: bbc -fopenacc -emit-hlfir %t/goto_with_intermediate.f90 -o - | FileCheck %s --check-prefix=GOTO2-OK
-! RUN: bbc -fopenacc -emit-hlfir %t/collapse_cycle.f90 -o - | FileCheck %s --check-prefix=CCYCLE-OK
-! RUN: bbc -fopenacc -emit-hlfir %t/cache_exit.f90 -o - | FileCheck %s --check-prefix=CEXIT-OK
-! RUN: bbc -fopenacc -emit-hlfir %t/cache_select_case.f90 -o - | FileCheck %s --check-prefix=CCASE-OK
-
-! With --emit-independent-loops-as-unstructured=false, the TODO is emitted.
-! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir --emit-independent-loops-as-unstructured=false %t/goto_one_level.f90 -o - 2>&1 | FileCheck %s --check-prefix=GOTO1
-! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir --emit-independent-loops-as-unstructured=false %t/goto_with_intermediate.f90 -o - 2>&1 | FileCheck %s --check-prefix=GOTO2
-! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir --emit-independent-loops-as-unstructured=false %t/collapse_cycle.f90 -o - 2>&1 | FileCheck %s --check-prefix=CCYCLE
-! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir --emit-independent-loops-as-unstructured=false %t/cache_exit.f90 -o - 2>&1 | FileCheck %s --check-prefix=CEXIT
-! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir --emit-independent-loops-as-unstructured=false %t/cache_select_case.f90 -o - 2>&1 | FileCheck %s --check-prefix=CCASE
-
-!--- goto_one_level.f90
-
-! GOTO exits the inner `acc loop seq` (one level), landing in the body of
-! the outer `acc loop gang vector`. Outer loop defaults to `independent`.
-subroutine test_unstructured6(N, A, B)
-  implicit real*8 (a-h, o-z)
-  !$acc routine gang
-  dimension A(*), B(*)
-  !$acc loop gang vector
-  do 100 i = 1, N
-  !$acc loop seq
-    do 10 j = 1, 1000
-      if (A(i) .gt. B(i)) goto 20
-10  continue
-20  B(i) = A(i)
-100 continue
-end subroutine
-
-! GOTO1: not yet implemented: unstructured do loop in independent OpenACC loop construct
-
-! GOTO1-OK-LABEL: func.func @_QPtest_unstructured6
-! GOTO1-OK: acc.loop {{.*}}gang vector
-! GOTO1-OK: acc.loop
-
-!--- goto_with_intermediate.f90
-
-! Same as above but with intermediate code between the inner loop end and
-! the GOTO target, exercising the jump-table dispatch path.
-subroutine test_unstructured7(A, B, C, N)
-  implicit real*8 (a-h, o-z)
-  !$acc routine gang
-  dimension A(*), B(*), C(*)
-  !$acc loop gang vector
-  do 100 i = 1, N
-  !$acc loop seq
-    do 10 j = 1, 1000
-      if (A(i) .gt. B(i)) goto 20
-10  continue
-    C(i) = 999.0
-20  B(i) = A(i)
-100 continue
-end subroutine
-
-! GOTO2: not yet implemented: unstructured do loop in independent OpenACC loop construct
-
-! GOTO2-OK-LABEL: func.func @_QPtest_unstructured7
-! GOTO2-OK: acc.loop {{.*}}gang vector
-! GOTO2-OK: acc.loop
-
-!--- collapse_cycle.f90
-
-! Orphan `acc loop collapse(2)` with an early-exit (CYCLE) - defaults to
-! `independent` inside the (non-seq) acc routine.
-subroutine test_unstructured_collapse_loop_only(a)
-  !$acc routine gang
-  integer :: i, j, jdiag
-  real(8) :: a(:,:)
-  jdiag = 4
-  !$acc loop collapse(2)
-  do j = 1, 8
-    do i = 1, 8
-      if (i == jdiag) then
-        a(i, j) = 0.0d0
-        cycle
-      end if
-      a(i, j) = real(i + j, 8)
-    end do
-  end do
-end subroutine
-
-! CCYCLE: not yet implemented: unstructured do loop in independent OpenACC loop construct
-
-! CCYCLE-OK-LABEL: func.func @_QPtest_unstructured_collapse_loop_only
-! CCYCLE-OK: acc.loop
-
-!--- cache_exit.f90
+! RUN: bbc -fopenacc -emit-hlfir %s -o - | FileCheck %s --check-prefix=CEXIT-OK
+! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir --emit-independent-loops-as-unstructured=false %s -o - 2>&1 | FileCheck %s --check-prefix=CEXIT
 
 ! `acc loop` with `cache` directive and EXIT inside the body - the EXIT
 ! makes the loop unstructured. Orphan loop inside a (non-seq) acc routine
@@ -116,32 +29,3 @@ subroutine test_cache_single_element()
 
 ! CEXIT-OK-LABEL: func.func @_QPtest_cache_single_element
 ! CEXIT-OK: acc.loop
-
-!--- cache_select_case.f90
-
-! `acc loop` with `cache` directive and SELECT CASE inside the body - the
-! SELECT CASE makes the loop's body have unstructured CFG. Orphan loop
-! inside a (non-seq) acc routine defaults to `independent`.
-subroutine test_cache_nonunit_lb()
-  !$acc routine gang
-  integer :: arr(10:20)
-  integer :: i
-
-  !$acc loop
-  do i = 10, 20
-    !$acc cache(arr(15))
-    select case (mod(i, 3))
-    case (0)
-      arr(i) = i * 2
-    case (1)
-      arr(i) = i * 3
-    case default
-      arr(i) = i
-    end select
-  end do
-end subroutine
-
-! CCASE: not yet implemented: unstructured do loop in independent OpenACC loop construct
-
-! CCASE-OK-LABEL: func.func @_QPtest_cache_nonunit_lb
-! CCASE-OK: acc.loop
diff --git a/flang/test/Lower/OpenACC/acc-directive-loop-bounds.f90 b/flang/test/Lower/OpenACC/acc-directive-loop-bounds.f90
new file mode 100644
index 0000000000000..edc1b2c0e54e3
--- /dev/null
+++ b/flang/test/Lower/OpenACC/acc-directive-loop-bounds.f90
@@ -0,0 +1,66 @@
+! A directive construct is the loop it owns: its lowering reads the construct's
+! own classification to decide whether the loop op carries its bounds. A loop
+! whose branching is confined to its body keeps its structured form, so the
+! construct holding it must be reclassified too -- otherwise the directive gets
+! a bounds-free acc.loop that nothing can partition, with the real loop nested
+! inside it.
+
+! RUN: %flang_fc1 -fopenacc -fdebug-dump-pft -o /dev/null %s 2>&1 | FileCheck %s
+! RUN: %flang_fc1 -fopenacc -emit-hlfir -o - %s | FileCheck %s --check-prefix=FIR
+
+! The CYCLE keeps its branching inside the loop body, so both the loop and the
+! construct that owns it are reclassified.
+subroutine parallel_loop_cycle(a, n)
+  real :: a(n)
+  integer :: n, i
+  !$acc parallel loop
+  do i = 1, n
+    if (a(i) > 0.0) then
+      a(i) = 1.0
+      cycle
+    end if
+    a(i) = 2.0
+  end do
+end subroutine
+
+! CHECK: Subroutine parallel_loop_cycle
+! CHECK: <<OpenACCConstruct~>>
+! CHECK: <<DoConstruct~>>
+
+! One acc.loop, and it is the directive's own: it carries the bounds, with the
+! body's branching folded into a region inside it. A second, nested acc.loop
+! here would mean the bounds landed on a loop the directive does not own.
+! FIR-LABEL: func.func @_QPparallel_loop_cycle
+! FIR:         acc.parallel combined(loop) {
+! FIR-NOT:       acc.loop
+! FIR:           acc.loop combined(parallel) private({{.*}}) control(%{{.*}} : i32) = (%{{.*}} : i32) to (%{{.*}} : i32) step (%{{.*}} : i32) {
+! FIR:             scf.execute_region no_inline {
+! FIR:               cf.cond_br
+! FIR:               scf.yield
+! FIR:             }
+! FIR-NOT:       acc.loop
+! FIR:           acc.yield
+! FIR:         }
+
+! Negative: the construct holds a GOTO of its own, so its branching is not
+! confined to the loop and it stays unstructured. The rewrite of a single-
+! statement IF body does not reach this one, so the GOTO survives.
+subroutine parallel_region_goto(a, n)
+  real :: a(n)
+  integer :: n, i
+  !$acc parallel
+  if (n > 0) then
+    a(1) = 0.0
+    goto 90
+  end if
+  !$acc loop
+  do i = 1, n
+    a(i) = 2.0
+  end do
+90 continue
+  !$acc end parallel
+end subroutine
+
+! CHECK: Subroutine parallel_region_goto
+! CHECK: <<OpenACCConstruct!>>
+! CHECK: GotoStmt!
diff --git a/flang/test/Lower/OpenACC/acc-unstructured-combined-construct.f90 b/flang/test/Lower/OpenACC/acc-unstructured-combined-construct.f90
new file mode 100644
index 0000000000000..e238aa2b06922
--- /dev/null
+++ b/flang/test/Lower/OpenACC/acc-unstructured-combined-construct.f90
@@ -0,0 +1,45 @@
+! An unstructured-CFG pattern inside a combined `acc parallel loop` construct
+! (default parallelism is `independent`).
+!
+! The IF-guarded CYCLE branches only within the loop body, so the loop keeps
+! its structured form and carries the construct that owns it along. The bounds
+! of both collapsed levels land on the directive's own acc.loop, whichever way
+! --emit-independent-loops-as-unstructured is set.
+
+! RUN: bbc -fopenacc -emit-hlfir %s -o - | FileCheck %s
+! RUN: bbc -fopenacc -emit-hlfir --emit-independent-loops-as-unstructured=false %s -o - | FileCheck %s
+
+subroutine test_unstructured_collapse_cycle(a)
+  integer :: i, j, jdiag
+  real(8) :: a(:,:)
+  jdiag = 4
+  !$acc parallel loop collapse(2) copy(a)
+  do j = 1, 8
+    do i = 1, 8
+      if (i == jdiag) then
+        a(i, j) = 0.0d0
+        cycle
+      end if
+      a(i, j) = real(i + j, 8)
+    end do
+  end do
+  !$acc end parallel loop
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_unstructured_collapse_cycle
+! CHECK:         acc.parallel combined(loop)
+! A second acc.loop nested here would mean the bounds landed on a loop the
+! directive does not own.
+! CHECK-NOT:       acc.loop
+! CHECK:           acc.loop combined(parallel) private({{.*}}) control(%{{.*}} : i32, %{{.*}} : i32) = (%{{.*}}, %{{.*}} : i32, i32) to (%{{.*}}, %{{.*}} : i32, i32) step (%{{.*}}, %{{.*}} : i32, i32) {
+! CHECK:             scf.execute_region no_inline {
+! CHECK:               cf.cond_br
+! CHECK:               scf.yield
+! CHECK:             }
+! CHECK:             acc.yield
+! CHECK:           } inclusiveUpperbound({{.*}}) collapse([2]) collapseDeviceType({{.*}}) independent
+!
+! Nothing else is nested in the compute region, which closes structured.
+! CHECK-NOT:       acc.loop
+! CHECK:         acc.yield
+! CHECK-NEXT:    }
diff --git a/flang/test/Lower/OpenACC/acc-unstructured-internals.f90 b/flang/test/Lower/OpenACC/acc-unstructured-internals.f90
index f0be621384a6a..600ad68047591 100644
--- a/flang/test/Lower/OpenACC/acc-unstructured-internals.f90
+++ b/flang/test/Lower/OpenACC/acc-unstructured-internals.f90
@@ -59,9 +59,12 @@ subroutine parallel_loop_cycle(a, n)
   end do
 end subroutine
 
+! The directive owns this loop, so the bounds belong on its own acc.loop --
+! the one carrying combined(parallel) -- and not on a second one nested inside.
 ! CHECK-LABEL: func.func @_QPparallel_loop_cycle
 ! CHECK:         acc.parallel combined(loop) {
-! CHECK:           acc.loop private({{.*}}) control(%{{.*}} : i32) = (%{{.*}} : i32) to (%{{.*}} : i32) step (%{{.*}} : i32) {
+! CHECK-NOT:       acc.loop
+! CHECK:           acc.loop combined(parallel) private({{.*}}) control(%{{.*}} : i32) = (%{{.*}} : i32) to (%{{.*}} : i32) step (%{{.*}} : i32) {
 ! CHECK:             scf.execute_region no_inline {
 ! CHECK:               cf.cond_br %{{[0-9]+}}, ^bb[[CYCLE:[0-9]+]], ^bb[[BODY:[0-9]+]]
 ! CHECK:             ^bb[[CYCLE]]:
diff --git a/flang/test/Lower/OpenACC/acc-unstructured-loop-construct.f90 b/flang/test/Lower/OpenACC/acc-unstructured-loop-construct.f90
new file mode 100644
index 0000000000000..b5ac87ae2bb23
--- /dev/null
+++ b/flang/test/Lower/OpenACC/acc-unstructured-loop-construct.f90
@@ -0,0 +1,160 @@
+! Each sub-file exercises a different unstructured-CFG pattern inside an
+! `acc loop` whose default parallelism resolves to `independent`.
+!
+! In each, the branching is confined to the loop body, so the loop keeps its
+! structured form and carries the construct that owns it along: the directive's
+! own acc.loop holds the bounds. That holds either way
+! --emit-independent-loops-as-unstructured is set.
+
+! RUN: split-file %s %t
+
+! RUN: bbc -fopenacc -emit-hlfir %t/goto_one_level.f90 -o - | FileCheck %s --check-prefix=GOTO1
+! RUN: bbc -fopenacc -emit-hlfir %t/goto_with_intermediate.f90 -o - | FileCheck %s --check-prefix=GOTO2
+! RUN: bbc -fopenacc -emit-hlfir %t/collapse_cycle.f90 -o - | FileCheck %s --check-prefix=CCYCLE
+! RUN: bbc -fopenacc -emit-hlfir %t/cache_select_case.f90 -o - | FileCheck %s --check-prefix=CCASE
+
+! RUN: bbc -fopenacc -emit-hlfir --emit-independent-loops-as-unstructured=false %t/goto_one_level.f90 -o - | FileCheck %s --check-prefix=GOTO1
+! RUN: bbc -fopenacc -emit-hlfir --emit-independent-loops-as-unstructured=false %t/goto_with_intermediate.f90 -o - | FileCheck %s --check-prefix=GOTO2
+! RUN: bbc -fopenacc -emit-hlfir --emit-independent-loops-as-unstructured=false %t/collapse_cycle.f90 -o - | FileCheck %s --check-prefix=CCYCLE
+! RUN: bbc -fopenacc -emit-hlfir --emit-independent-loops-as-unstructured=false %t/cache_select_case.f90 -o - | FileCheck %s --check-prefix=CCASE
+
+!--- goto_one_level.f90
+
+! GOTO exits the inner `acc loop seq` (one level), landing in the body of
+! the outer `acc loop gang vector`. Outer loop defaults to `independent`.
+subroutine test_unstructured6(N, A, B)
+  implicit real*8 (a-h, o-z)
+  !$acc routine gang
+  dimension A(*), B(*)
+  !$acc loop gang vector
+  do 100 i = 1, N
+  !$acc loop seq
+    do 10 j = 1, 1000
+      if (A(i) .gt. B(i)) goto 20
+10  continue
+20  B(i) = A(i)
+100 continue
+end subroutine
+
+! The outer loop the directive owns: bounds on the op, and its body, which the
+! GOTO branches within, folded into a region.
+! GOTO1-LABEL: func.func @_QPtest_unstructured6
+! GOTO1:         acc.loop gang vector private({{.*}}) control(%{{....
[truncated]

``````````

</details>


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


More information about the llvm-branch-commits mailing list