[flang-commits] [flang] bbeb2d5 - [OpenACC][flang] Emit NYI when unstructured loops are associated with OpenACC directives (#202948)

via flang-commits flang-commits at lists.llvm.org
Fri Jun 12 08:29:37 PDT 2026


Author: Kareem Ergawy
Date: 2026-06-12T17:29:33+02:00
New Revision: bbeb2d519171b57bbabb25ca07223c286cdecea2

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

LOG: [OpenACC][flang] Emit NYI when unstructured loops are associated with OpenACC directives (#202948)

Added: 
    flang/test/Lower/OpenACC/Todo/acc-unstructured-combined-construct.f90
    flang/test/Lower/OpenACC/Todo/acc-unstructured-loop-construct.f90

Modified: 
    flang/lib/Lower/OpenACC.cpp
    flang/test/Lower/OpenACC/acc-unstructured.f90

Removed: 
    


################################################################################
diff  --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index 1c51cf7fa6ca5..51c9356cc8d46 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -1308,6 +1308,41 @@ static void gatherDeviceTypeAttrs(
         builder.getContext(), getDeviceType(deviceTypeExpr.v)));
 }
 
+// Tries to handle a clause that affects the loop's parallelism-mode tracking:
+//   - `seq` / `auto` / `independent`: append the current device_type set to
+//     the corresponding list.
+//   - `device_type`: replace the current device_type set.
+// Returns true if the clause was handled.
+static bool tryHandleLoopParModeClause(
+    fir::FirOpBuilder &builder, const Fortran::parser::AccClause &clause,
+    llvm::SmallVector<mlir::Attribute> &crtDeviceTypes,
+    llvm::SmallVector<mlir::Attribute> &seqDeviceTypes,
+    llvm::SmallVector<mlir::Attribute> &independentDeviceTypes,
+    llvm::SmallVector<mlir::Attribute> &autoDeviceTypes) {
+  if (std::get_if<Fortran::parser::AccClause::Seq>(&clause.u)) {
+    for (auto crtDeviceTypeAttr : crtDeviceTypes)
+      seqDeviceTypes.push_back(crtDeviceTypeAttr);
+    return true;
+  }
+  if (std::get_if<Fortran::parser::AccClause::Auto>(&clause.u)) {
+    for (auto crtDeviceTypeAttr : crtDeviceTypes)
+      autoDeviceTypes.push_back(crtDeviceTypeAttr);
+    return true;
+  }
+  if (std::get_if<Fortran::parser::AccClause::Independent>(&clause.u)) {
+    for (auto crtDeviceTypeAttr : crtDeviceTypes)
+      independentDeviceTypes.push_back(crtDeviceTypeAttr);
+    return true;
+  }
+  if (const auto *deviceTypeClause =
+          std::get_if<Fortran::parser::AccClause::DeviceType>(&clause.u)) {
+    crtDeviceTypes.clear();
+    gatherDeviceTypeAttrs(builder, deviceTypeClause, crtDeviceTypes);
+    return true;
+  }
+  return false;
+}
+
 static void genIfClause(Fortran::lower::AbstractConverter &converter,
                         mlir::Location clauseLocation,
                         const Fortran::parser::AccClause::If *ifClause,
@@ -1518,6 +1553,79 @@ static void determineDefaultLoopParMode(
   }
 }
 
+// Returns true when the acc loop being constructed will have `independent`
+// parallelism semantics for the default device_type (i.e., DeviceType::None).
+//
+// `directive` is the OpenACC directive that the loop is associated with.
+//
+// The NYI for unstructured loops nested in an acc loop / combined construct is
+// only meaningful when the user has promised parallelism. For `auto` and `seq`
+// the user has not made that promise, so falling back to unstructured CFG
+// inside the acc.loop is acceptable.
+static bool
+loopWillBeIndependent(Fortran::lower::AbstractConverter &converter,
+                      const Fortran::parser::AccClauseList &accClauseList,
+                      llvm::acc::Directive directive) {
+  fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+
+  // Walk the clauses and collect seq/auto/independent attributes per
+  // device_type. Other clauses are ignored.
+  llvm::SmallVector<mlir::Attribute> seqDeviceTypes, independentDeviceTypes,
+      autoDeviceTypes;
+  llvm::SmallVector<mlir::Attribute> crtDeviceTypes;
+  crtDeviceTypes.push_back(mlir::acc::DeviceTypeAttr::get(
+      builder.getContext(), mlir::acc::DeviceType::None));
+
+  for (const Fortran::parser::AccClause &clause : accClauseList.v)
+    tryHandleLoopParModeClause(builder, clause, crtDeviceTypes, seqDeviceTypes,
+                               independentDeviceTypes, autoDeviceTypes);
+
+  auto hasDeviceNone = [](mlir::Attribute attr) -> bool {
+    return mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr).getValue() ==
+           mlir::acc::DeviceType::None;
+  };
+
+  if (llvm::any_of(independentDeviceTypes, hasDeviceNone))
+    return true;
+  if (llvm::any_of(seqDeviceTypes, hasDeviceNone) ||
+      llvm::any_of(autoDeviceTypes, hasDeviceNone))
+    return false;
+
+  // No explicit parallelism clause for the default device_type. Defer to the
+  // directive: combined constructs imply a parallelism mode for the loop;
+  // a standalone `acc loop` defers to its enclosing compute op (or the routine
+  // attribute for orphaned loops).
+  switch (directive) {
+  case llvm::acc::ACCD_parallel_loop:
+    return true;
+  case llvm::acc::ACCD_kernels_loop:
+  case llvm::acc::ACCD_serial_loop:
+    return false;
+  case llvm::acc::ACCD_loop: {
+    // The OpenACC spec treats any orphan loop as `independent` by default,
+    // but a parallelism promise only really exists when the enclosing
+    // function is declared `acc routine` or there an `acc parallel` parent.
+    // Orphan loops in plain (non-acc-routine) functions won't run as device
+    // code, so serializing their unstructured CFG should be harmless.
+    mlir::Region *currentRegion = builder.getBlock()->getParent();
+    mlir::Operation *parentOp =
+        mlir::acc::getEnclosingComputeOp(*currentRegion);
+
+    if (mlir::isa_and_present<mlir::acc::ParallelOp>(parentOp))
+      return true;
+
+    if (parentOp) // KernelsOp / SerialOp
+      return false;
+
+    // Orphan loop in an `acc routine`.
+    return mlir::acc::isAccRoutine(builder.getFunction().getOperation()) &&
+           !isInsideSeqOpenACCRoutine(builder);
+  }
+  default:
+    llvm_unreachable("unexpected directive for loopWillBeIndependent");
+  }
+}
+
 // Helper to visit Bounds of DO LOOP nest.
 //
 // When `markInnerCollapsed` is true (the default), inner DOs that are absorbed
@@ -2240,21 +2348,11 @@ static mlir::acc::LoopOp createLoopOp(
                     reductionOperands, /*async=*/{},
                     /*asyncDeviceTypes=*/{}, /*asyncOnlyDeviceTypes=*/{},
                     &dataMap);
-    } else if (std::get_if<Fortran::parser::AccClause::Seq>(&clause.u)) {
-      for (auto crtDeviceTypeAttr : crtDeviceTypes)
-        seqDeviceTypes.push_back(crtDeviceTypeAttr);
-    } else if (std::get_if<Fortran::parser::AccClause::Independent>(
-                   &clause.u)) {
-      for (auto crtDeviceTypeAttr : crtDeviceTypes)
-        independentDeviceTypes.push_back(crtDeviceTypeAttr);
-    } else if (std::get_if<Fortran::parser::AccClause::Auto>(&clause.u)) {
-      for (auto crtDeviceTypeAttr : crtDeviceTypes)
-        autoDeviceTypes.push_back(crtDeviceTypeAttr);
-    } else if (const auto *deviceTypeClause =
-                   std::get_if<Fortran::parser::AccClause::DeviceType>(
-                       &clause.u)) {
-      crtDeviceTypes.clear();
-      gatherDeviceTypeAttrs(builder, deviceTypeClause, crtDeviceTypes);
+    } else if (tryHandleLoopParModeClause(
+                   builder, clause, crtDeviceTypes, seqDeviceTypes,
+                   independentDeviceTypes, autoDeviceTypes)) {
+      // Updates to the relevant variables is already handled in
+      // tryHandleLoopParModeClause.
     } else if (const auto *collapseClause =
                    std::get_if<Fortran::parser::AccClause::Collapse>(
                        &clause.u)) {
@@ -2373,12 +2471,17 @@ genACC(Fortran::lower::AbstractConverter &converter,
 
   assert(loopDirective.v == llvm::acc::ACCD_loop &&
          "Unsupported OpenACC loop construct");
-  (void)loopDirective;
 
   const auto &accClauseList =
       std::get<Fortran::parser::AccClauseList>(beginLoopDirective.t);
   const auto &outerDoConstruct =
       std::get<std::optional<Fortran::parser::DoConstruct>>(loopConstruct.t);
+
+  if (outerDoConstruct.has_value() && eval.lowerAsUnstructured() &&
+      loopWillBeIndependent(converter, accClauseList, loopDirective.v))
+    TODO(currentLocation,
+         "unstructured do loop in independent OpenACC loop construct");
+
   auto loopOp = createLoopOp(converter, currentLocation, semanticsContext,
                              stmtCtx, *outerDoConstruct, eval, accClauseList,
                              /*combinedConstructs=*/{});
@@ -3148,6 +3251,10 @@ genACC(Fortran::lower::AbstractConverter &converter,
       converter.genLocation(beginCombinedDirective.source);
   Fortran::lower::StatementContext stmtCtx;
 
+  if (outerDoConstruct.has_value() && eval.lowerAsUnstructured() &&
+      loopWillBeIndependent(converter, accClauseList, combinedDirective.v))
+    TODO(currentLocation, "unstructured do loop in combined acc construct");
+
   if (combinedDirective.v == llvm::acc::ACCD_kernels_loop) {
     createComputeOp<mlir::acc::KernelsOp>(
         converter, currentLocation, eval, semanticsContext, stmtCtx,

diff  --git a/flang/test/Lower/OpenACC/Todo/acc-unstructured-combined-construct.f90 b/flang/test/Lower/OpenACC/Todo/acc-unstructured-combined-construct.f90
new file mode 100644
index 0000000000000..da06ce8c2d170
--- /dev/null
+++ b/flang/test/Lower/OpenACC/Todo/acc-unstructured-combined-construct.f90
@@ -0,0 +1,69 @@
+! Each sub-file exercises a 
diff erent unstructured-CFG pattern inside a
+! combined `acc parallel loop` construct (default parallelism is
+! `independent`).
+
+! RUN: split-file %s %t
+! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/stop_collapse1.f90 -o - 2>&1 | FileCheck %s --check-prefix=STOP1
+! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/cycle_collapse2.f90 -o - 2>&1 | FileCheck %s --check-prefix=CYCLE2
+! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/stop_collapse3.f90 -o - 2>&1 | FileCheck %s --check-prefix=STOP3
+
+!--- stop_collapse1.f90
+
+! `acc parallel loop` with STOP in the body. Loop defaults to `independent`.
+subroutine test_unstructured2(a, b, c)
+  integer :: i, j, k
+  real :: a(:,:,:), b(:,:,:), c(:,:,:)
+
+  !$acc parallel loop
+  do i = 1, 10
+    do j = 1, 10
+      do k = 1, 10
+        if (a(1,2,3) > 10) stop 'just to be unstructured'
+      end do
+    end do
+  end do
+
+end subroutine
+
+! STOP1: not yet implemented: unstructured do loop in combined acc construct
+
+!--- cycle_collapse2.f90
+
+! `acc parallel loop collapse(2)` with an early-exit (CYCLE).
+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
+
+!--- stop_collapse3.f90
+
+! `acc parallel loop collapse(3)` with STOP - the collapse=3 form of the
+! STOP scenario above.
+subroutine test_unstructured_collapse_stop(a)
+  integer :: i, j, k
+  real :: a(:,:,:)
+  !$acc parallel loop collapse(3)
+  do i = 1, 10
+    do j = 1, 10
+      do k = 1, 10
+        if (a(1,2,3) > 10) stop 'just to be unstructured'
+      end do
+    end do
+  end do
+end subroutine
+
+! STOP3: not yet implemented: unstructured do loop in combined acc construct

diff  --git a/flang/test/Lower/OpenACC/Todo/acc-unstructured-loop-construct.f90 b/flang/test/Lower/OpenACC/Todo/acc-unstructured-loop-construct.f90
new file mode 100644
index 0000000000000..285d970250c09
--- /dev/null
+++ b/flang/test/Lower/OpenACC/Todo/acc-unstructured-loop-construct.f90
@@ -0,0 +1,120 @@
+! Each sub-file exercises a 
diff erent unstructured-CFG pattern inside an
+! `acc loop` whose default parallelism resolves to `independent`.
+
+! RUN: split-file %s %t
+! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/goto_one_level.f90 -o - 2>&1 | FileCheck %s --check-prefix=GOTO1
+! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/goto_with_intermediate.f90 -o - 2>&1 | FileCheck %s --check-prefix=GOTO2
+! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/collapse_cycle.f90 -o - 2>&1 | FileCheck %s --check-prefix=CCYCLE
+! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/cache_exit.f90 -o - 2>&1 | FileCheck %s --check-prefix=CEXIT
+! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %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
+
+!--- 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
+
+!--- 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
+
+!--- cache_exit.f90
+
+! `acc loop` with `cache` directive and EXIT inside the body - the EXIT
+! makes the loop unstructured. Orphan loop inside a (non-seq) acc routine
+! defaults to `independent`.
+subroutine test_cache_single_element()
+  !$acc routine gang
+  integer, parameter :: n = 10
+  real, dimension(n) :: a, b
+  integer :: i
+
+  !$acc loop
+  do i = 1, n
+    !$acc cache(b(i))
+    a(i) = b(i)
+    if (a(i) > 100.0) exit
+  end do
+end subroutine
+
+! CEXIT: not yet implemented: unstructured do loop in independent OpenACC loop construct
+
+!--- 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

diff  --git a/flang/test/Lower/OpenACC/acc-unstructured.f90 b/flang/test/Lower/OpenACC/acc-unstructured.f90
index ce58ae90bdc35..115626da9d160 100644
--- a/flang/test/Lower/OpenACC/acc-unstructured.f90
+++ b/flang/test/Lower/OpenACC/acc-unstructured.f90
@@ -43,7 +43,7 @@ subroutine test_unstructured2(a, b, c)
   integer :: i, j, k
   real :: a(:,:,:), b(:,:,:), c(:,:,:)
 
-  !$acc parallel loop
+  !$acc serial loop
   do i = 1, 10
     do j = 1, 10
       do k = 1, 10
@@ -53,12 +53,12 @@ subroutine test_unstructured2(a, b, c)
   end do
 
 ! CHECK-LABEL: func.func @_QPtest_unstructured2
-! CHECK: acc.parallel
-! CHECK: acc.loop combined(parallel) private(%{{.*}} : !fir.ref<i32>) {
+! CHECK: acc.serial
+! CHECK: acc.loop combined(serial) private(%{{.*}} : !fir.ref<i32>) {
 ! CHECK: fir.call @_FortranAStopStatementText
 ! CHECK: acc.yield
 ! CHECK: acc.yield
-! CHECK: } attributes {independent = [#acc.device_type<none>], unstructured}
+! CHECK: }
 ! CHECK: acc.yield
 
 end subroutine
@@ -142,7 +142,7 @@ subroutine test_unstructured5(a, n)
 ! instead of an invalid cross-region branch.
 subroutine test_unstructured6(N, A, B)
   implicit real*8 (a-h, o-z)
-  !$acc routine gang
+  !$acc routine seq
   dimension A(*), B(*)
   !$acc loop gang vector
   do 100 i = 1, N
@@ -166,7 +166,7 @@ subroutine test_unstructured6(N, A, B)
 ! target. A jump table (exit selector + dispatch) skips the intermediate code.
 subroutine test_unstructured7(A, B, C, N)
   implicit real*8 (a-h, o-z)
-  !$acc routine gang
+  !$acc routine seq
   dimension A(*), B(*), C(*)
   !$acc loop gang vector
   do 100 i = 1, N
@@ -223,7 +223,7 @@ subroutine test_unstructured8(a, n)
 ! CHECK: arith.cmpi eq
 ! CHECK: cf.cond_br
 
-! Test that `acc parallel loop collapse(N)` whose body has an early-exit
+! Test that `acc serial loop collapse(N)` whose body has an early-exit
 ! (here, `if (cond) then ... cycle ... end if`) lowers cleanly. The
 ! corresponding acc.loop must privatize all N induction variables, carry
 ! both `collapse = [N]` and `unstructured` attributes, and emit the
@@ -233,7 +233,7 @@ subroutine test_unstructured_collapse_cycle(a)
   integer :: i, j, jdiag
   real(8) :: a(:,:)
   jdiag = 4
-  !$acc parallel loop collapse(2) copy(a)
+  !$acc serial loop collapse(2) copy(a)
   do j = 1, 8
     do i = 1, 8
       if (i == jdiag) then
@@ -243,16 +243,16 @@ subroutine test_unstructured_collapse_cycle(a)
       a(i, j) = real(i + j, 8)
     end do
   end do
-  !$acc end parallel loop
+  !$acc end serial loop
 end subroutine
 
 ! CHECK-LABEL: func.func @_QPtest_unstructured_collapse_cycle
-! CHECK: acc.parallel combined(loop)
+! CHECK: acc.serial combined(loop)
 ! Both induction variables (j and i) are privatized:
 ! CHECK: %[[PRIVJ:.*]] = acc.private varPtr(%{{.*}} : !fir.ref<i32>) recipe(@privatization_ref_i32) -> !fir.ref<i32> {implicit = true, name = "j"}
 ! CHECK: %[[PRIVI:.*]] = acc.private varPtr(%{{.*}} : !fir.ref<i32>) recipe(@privatization_ref_i32) -> !fir.ref<i32> {implicit = true, name = "i"}
 ! No control(...) on acc.loop — bounds are not on the op:
-! CHECK: acc.loop combined(parallel) private(%[[PRIVJ]], %[[PRIVI]] : !fir.ref<i32>, !fir.ref<i32>) {
+! CHECK: acc.loop combined(serial) private(%[[PRIVJ]], %[[PRIVI]] : !fir.ref<i32>, !fir.ref<i32>) {
 ! Outer loop trip-count test (j) emitted as cf:
 ! CHECK: arith.cmpi sgt
 ! CHECK: cf.cond_br
@@ -263,14 +263,14 @@ subroutine test_unstructured_collapse_cycle(a)
 ! CHECK: arith.cmpi eq
 ! CHECK: cf.cond_br
 ! CHECK: acc.yield
-! CHECK: } attributes {collapse = [2], collapseDeviceType = [#acc.device_type<none>], independent = [#acc.device_type<none>], unstructured}
+! CHECK: }
 
-! Test that `acc parallel loop collapse(N)` lowers cleanly when the early-exit
+! Test that `acc serial loop collapse(N)` lowers cleanly when the early-exit
 ! is a STOP (the form already covered for collapse=1 by test_unstructured2).
 subroutine test_unstructured_collapse_stop(a)
   integer :: i, j, k
   real :: a(:,:,:)
-  !$acc parallel loop collapse(3)
+  !$acc serial loop collapse(3)
   do i = 1, 10
     do j = 1, 10
       do k = 1, 10
@@ -285,9 +285,9 @@ subroutine test_unstructured_collapse_stop(a)
 ! CHECK: acc.private varPtr(%{{.*}} : !fir.ref<i32>) recipe(@privatization_ref_i32) -> !fir.ref<i32> {implicit = true, name = "i"}
 ! CHECK: acc.private varPtr(%{{.*}} : !fir.ref<i32>) recipe(@privatization_ref_i32) -> !fir.ref<i32> {implicit = true, name = "j"}
 ! CHECK: acc.private varPtr(%{{.*}} : !fir.ref<i32>) recipe(@privatization_ref_i32) -> !fir.ref<i32> {implicit = true, name = "k"}
-! CHECK: acc.loop combined(parallel) private(%{{.*}}, %{{.*}}, %{{.*}} : !fir.ref<i32>, !fir.ref<i32>, !fir.ref<i32>) {
+! CHECK: acc.loop combined(serial) private(%{{.*}}, %{{.*}}, %{{.*}} : !fir.ref<i32>, !fir.ref<i32>, !fir.ref<i32>) {
 ! CHECK: fir.call @_FortranAStopStatementText
-! CHECK: } attributes {collapse = [3], collapseDeviceType = [#acc.device_type<none>], independent = [#acc.device_type<none>], unstructured}
+! CHECK: }
 
 ! Test orphaned `acc loop collapse(N)`
 subroutine test_unstructured_collapse_loop_only(a)
@@ -310,3 +310,77 @@ subroutine test_unstructured_collapse_loop_only(a)
 ! Standalone acc.loop (no `combined(...)`):
 ! CHECK: acc.loop private(%{{.*}}, %{{.*}} : !fir.ref<i32>, !fir.ref<i32>) {
 ! CHECK: } attributes {collapse = [2], collapseDeviceType = [#acc.device_type<none>], independent = [#acc.device_type<none>], unstructured}
+
+! Standalone `acc loop seq` with STOP in body (explicit `seq` clause).
+subroutine test_unstructured_loop_seq_stop(a)
+  integer :: i, j
+  real :: a(:,:,:)
+  !$acc loop seq
+  do i = 1, 10
+    do j = 1, 10
+      if (a(1,2,3) > 10.0) stop 'unstructured'
+    end do
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_unstructured_loop_seq_stop
+! CHECK: acc.loop private({{.*}})
+! CHECK: fir.call @_FortranAStopStatementText
+! CHECK: } attributes {{{.*}}seq = [#acc.device_type<none>], unstructured}
+
+! Standalone `acc loop auto` with STOP in body (explicit `auto` clause).
+subroutine test_unstructured_loop_auto_stop(a)
+  integer :: i, j
+  real :: a(:,:,:)
+  !$acc loop auto
+  do i = 1, 10
+    do j = 1, 10
+      if (a(1,2,3) > 10.0) stop 'unstructured'
+    end do
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_unstructured_loop_auto_stop
+! CHECK: acc.loop private({{.*}})
+! CHECK: fir.call @_FortranAStopStatementText
+! CHECK: } attributes {auto_ = [#acc.device_type<none>], {{.*}}unstructured}
+
+! Standalone `acc loop` inside `acc serial` with STOP in body (loop is `seq`
+! by default because parent compute construct is serial).
+subroutine test_unstructured_loop_in_serial_stop(a)
+  integer :: i, j
+  real :: a(:,:,:)
+  !$acc serial
+  !$acc loop
+  do i = 1, 10
+    do j = 1, 10
+      if (a(1,2,3) > 10.0) stop 'unstructured'
+    end do
+  end do
+  !$acc end serial
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_unstructured_loop_in_serial_stop
+! CHECK: acc.serial
+! CHECK: acc.loop private({{.*}})
+! CHECK: fir.call @_FortranAStopStatementText
+! CHECK: } attributes {{{.*}}seq = [#acc.device_type<none>], unstructured}
+
+! Orphan `acc loop` inside a `seq` acc routine: loop is `seq` by default.
+subroutine test_unstructured_orphan_loop_in_seq_routine(a)
+  integer :: i, j
+  real :: a(:,:,:)
+  !$acc routine seq
+  !$acc loop
+  do i = 1, 10
+    do j = 1, 10
+      if (a(1,2,3) > 10.0) stop 'unstructured'
+    end do
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_unstructured_orphan_loop_in_seq_routine
+! CHECK: acc.loop private({{.*}})
+! CHECK: fir.call @_FortranAStopStatementText
+! CHECK: } attributes {{{.*}}seq = [#acc.device_type<none>], unstructured}
+


        


More information about the flang-commits mailing list