[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