[flang-commits] [flang] [flang] Fix crash when GoTo exits acc.loop region (PR #187613)
via flang-commits
flang-commits at lists.llvm.org
Fri Mar 20 11:55:28 PDT 2026
https://github.com/khaki3 updated https://github.com/llvm/llvm-project/pull/187613
>From dcdad97b8f4b60d55f32f07d7eeba95d63426bbb Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Thu, 19 Mar 2026 17:14:13 -0700
Subject: [PATCH 1/5] [flang] Fix crash when GoTo exits acc.loop region
When a GoTo inside an $Acc Loop Seq targets a label outside the
acc.loop's MLIR region, the lowering generated an illegal cross-region
cf.br. This caused runRegionDCE's recursive propagateLiveness to enter
a cyclic traversal through the dangling reference, overflowing the
compiler stack.
Fix by generating acc.yield (proper region exit) instead of cf.br when
the GoTo target block is in a different region than the current one.
This reuses the existing early-exit mechanism already used for RETURN
statements inside acc.loop.
Made-with: Cursor
---
flang/lib/Lower/Bridge.cpp | 11 ++++++++-
.../test/Lower/OpenACC/acc-loop-goto-exit.f90 | 24 +++++++++++++++++++
2 files changed, 34 insertions(+), 1 deletion(-)
create mode 100644 flang/test/Lower/OpenACC/acc-loop-goto-exit.f90
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 7459f814a0d4d..e14912017ae7d 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -6239,7 +6239,16 @@ class FirConverter : public Fortran::lower::AbstractConverter {
genConstructExitBranch(*getEval().controlSuccessor);
}
void genFIR(const Fortran::parser::GotoStmt &) {
- genConstructExitBranch(*getEval().controlSuccessor);
+ auto &targetEval = *getEval().controlSuccessor;
+ if (Fortran::lower::isInOpenACCLoop(*builder)) {
+ mlir::Block *targetBlock = targetEval.block;
+ mlir::Region *currentRegion = &builder->getRegion();
+ if (targetBlock && targetBlock->getParent() != currentRegion) {
+ mlir::acc::YieldOp::create(*builder, toLocation());
+ return;
+ }
+ }
+ genConstructExitBranch(targetEval);
}
// Nop statements - No code, or code is generated at the construct level.
diff --git a/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90 b/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90
new file mode 100644
index 0000000000000..88fc30d79c3d1
--- /dev/null
+++ b/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90
@@ -0,0 +1,24 @@
+! Test that GoTo exiting an acc.loop seq region generates acc.yield
+! instead of an illegal cross-region cf.br that would crash the compiler.
+! RUN: bbc -fopenacc -emit-fir %s -o - | FileCheck %s
+
+! CHECK-LABEL: func.func @_QPfoo
+subroutine foo(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
+ ! CHECK: acc.loop gang vector
+ ! CHECK: acc.loop
+ !$acc loop seq
+ do 10 j = 1, 1000
+ if (A(i) .gt. B(i)) goto 20
+10 continue
+ ! The GoTo crossing the acc.loop region boundary must generate
+ ! acc.yield (not cf.br) to properly exit the inner acc.loop.
+ ! CHECK: acc.yield
+ ! CHECK: acc.yield
+20 B(i) = A(i)
+100 continue
+end subroutine
>From 3cf4d749406a8c2a153a65157d94d1505aaa4fa0 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Thu, 19 Mar 2026 22:03:36 -0700
Subject: [PATCH 2/5] [flang] Update lit test: use -emit-hlfir and fix CHECK
lines
Made-with: Cursor
---
flang/test/Lower/OpenACC/acc-loop-goto-exit.f90 | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90 b/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90
index 88fc30d79c3d1..fc7b036b0cef1 100644
--- a/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90
+++ b/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90
@@ -1,6 +1,6 @@
! Test that GoTo exiting an acc.loop seq region generates acc.yield
! instead of an illegal cross-region cf.br that would crash the compiler.
-! RUN: bbc -fopenacc -emit-fir %s -o - | FileCheck %s
+! RUN: bbc -fopenacc -emit-hlfir %s -o - | FileCheck %s
! CHECK-LABEL: func.func @_QPfoo
subroutine foo(N, A, B)
@@ -10,15 +10,17 @@ subroutine foo(N, A, B)
!$acc loop gang vector
do 100 i = 1, N
! CHECK: acc.loop gang vector
- ! CHECK: acc.loop
+ ! CHECK: acc.loop {{.*}} {
!$acc loop seq
do 10 j = 1, 1000
if (A(i) .gt. B(i)) goto 20
10 continue
! The GoTo crossing the acc.loop region boundary must generate
- ! acc.yield (not cf.br) to properly exit the inner acc.loop.
+ ! acc.yield to properly exit the inner acc.loop, not an illegal
+ ! cross-region cf.br that would crash the compiler.
! CHECK: acc.yield
! CHECK: acc.yield
+ ! CHECK: }
20 B(i) = A(i)
100 continue
end subroutine
>From 17b8cc322a7b5680b42779134c79def31fb10957 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Thu, 19 Mar 2026 22:10:45 -0700
Subject: [PATCH 3/5] [flang] Improve lit test: use -emit-hlfir, check
seq+unstructured attrs
Made-with: Cursor
---
flang/test/Lower/OpenACC/acc-loop-goto-exit.f90 | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90 b/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90
index fc7b036b0cef1..ff6d7ca4aef25 100644
--- a/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90
+++ b/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90
@@ -9,18 +9,18 @@ subroutine foo(N, A, B)
dimension A(*), B(*)
!$acc loop gang vector
do 100 i = 1, N
- ! CHECK: acc.loop gang vector
- ! CHECK: acc.loop {{.*}} {
!$acc loop seq
do 10 j = 1, 1000
if (A(i) .gt. B(i)) goto 20
10 continue
- ! The GoTo crossing the acc.loop region boundary must generate
- ! acc.yield to properly exit the inner acc.loop, not an illegal
- ! cross-region cf.br that would crash the compiler.
- ! CHECK: acc.yield
- ! CHECK: acc.yield
- ! CHECK: }
20 B(i) = A(i)
100 continue
end subroutine
+
+! Verify the inner acc.loop has seq and unstructured attributes,
+! and that it contains acc.yield (from the GoTo cross-region exit).
+! CHECK: acc.loop gang vector
+! CHECK: acc.loop
+! CHECK: acc.yield
+! CHECK: acc.yield
+! CHECK: } attributes {seq = [#acc.device_type<none>], unstructured}
>From 5db6701c4765f9eb8d4c627830e87fcf25377349 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Thu, 19 Mar 2026 22:16:23 -0700
Subject: [PATCH 4/5] [flang] Use CHECK-NEXT in lit test for precise matching
Made-with: Cursor
---
flang/test/Lower/OpenACC/acc-loop-goto-exit.f90 | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90 b/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90
index ff6d7ca4aef25..696051b9bb534 100644
--- a/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90
+++ b/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90
@@ -17,10 +17,17 @@ subroutine foo(N, A, B)
100 continue
end subroutine
-! Verify the inner acc.loop has seq and unstructured attributes,
-! and that it contains acc.yield (from the GoTo cross-region exit).
+! Verify the inner acc.loop (seq) contains acc.yield for the GoTo exit.
+! The GoTo target is outside the acc.loop region, so it must yield
+! instead of generating an illegal cross-region cf.br.
+
! CHECK: acc.loop gang vector
! CHECK: acc.loop
+! The GoTo comparison and branch:
+! CHECK: arith.cmpf ogt
+! CHECK-NEXT: cf.cond_br %{{.*}}, ^[[EXIT:bb[0-9]+]], ^
+! CHECK-NEXT: ^[[EXIT]]:
+! CHECK-NEXT: acc.yield
+! Normal loop end yield and closing:
! CHECK: acc.yield
-! CHECK: acc.yield
-! CHECK: } attributes {seq = [#acc.device_type<none>], unstructured}
+! CHECK-NEXT: } attributes {seq = [#acc.device_type<none>], unstructured}
>From 8316f4a274f0b96c3dc77d864e07e1f70ea003ba Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Fri, 20 Mar 2026 11:53:24 -0700
Subject: [PATCH 5/5] [flang][OpenACC] Generalize cross-region GOTO exit
handling for all ACC ops
Replace the acc.loop-specific GOTO exit handling in genFIR(GotoStmt)
with a generalized genOpenACCRegionExitBranch helper called from
genBranch. This catches cross-region branches at the lowest level and
handles all ACC region ops (data, parallel, kernels, serial, loop,
host_data) by generating the appropriate terminator (acc.terminator or
acc.yield).
Also fix acc.data creation when the construct has no data clauses but
contains unstructured control flow: skip the early return so the
acc.data region is created and blocks are properly managed.
For GOTOs that exit multiple region levels, a TODO diagnostic is
emitted.
Made-with: Cursor
---
flang/include/flang/Lower/OpenACC.h | 8 ++
flang/lib/Lower/Bridge.cpp | 60 ++++++-------
flang/lib/Lower/OpenACC.cpp | 86 ++++++++++++-------
.../Todo/acc-goto-multi-level-exit.f90 | 42 +++++++++
.../test/Lower/OpenACC/acc-loop-goto-exit.f90 | 33 -------
flang/test/Lower/OpenACC/acc-unstructured.f90 | 76 ++++++++++++++++
6 files changed, 207 insertions(+), 98 deletions(-)
create mode 100644 flang/test/Lower/OpenACC/Todo/acc-goto-multi-level-exit.f90
delete mode 100644 flang/test/Lower/OpenACC/acc-loop-goto-exit.f90
diff --git a/flang/include/flang/Lower/OpenACC.h b/flang/include/flang/Lower/OpenACC.h
index c2a950f36c5a7..8e0f3da22ba9e 100644
--- a/flang/include/flang/Lower/OpenACC.h
+++ b/flang/include/flang/Lower/OpenACC.h
@@ -138,6 +138,14 @@ void setInsertionPointAfterOpenACCLoopIfInside(fir::FirOpBuilder &);
void genEarlyReturnInOpenACCLoop(fir::FirOpBuilder &, mlir::Location);
+/// If \p targetBlock is outside the ACC region containing the current
+/// insertion point, generate the appropriate region terminator
+/// (acc.terminator or acc.yield) instead of a cross-region branch.
+/// Returns true if the exit was handled, false if no ACC region boundary
+/// is crossed.
+bool genOpenACCRegionExitBranch(fir::FirOpBuilder &, mlir::Location,
+ mlir::Block *targetBlock);
+
/// Generates an OpenACC loop from a do construct in order to
/// properly capture the loop bounds, parallelism determination mode,
/// and to privatize the loop variables.
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index e14912017ae7d..0d6fc00b4411b 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -1609,6 +1609,9 @@ class FirConverter : public Fortran::lower::AbstractConverter {
void genBranch(mlir::Block *targetBlock) {
assert(targetBlock && "missing unconditional target block");
+ if (Fortran::lower::genOpenACCRegionExitBranch(*builder, toLocation(),
+ targetBlock))
+ return;
mlir::cf::BranchOp::create(*builder, toLocation(), targetBlock);
}
@@ -6239,16 +6242,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
genConstructExitBranch(*getEval().controlSuccessor);
}
void genFIR(const Fortran::parser::GotoStmt &) {
- auto &targetEval = *getEval().controlSuccessor;
- if (Fortran::lower::isInOpenACCLoop(*builder)) {
- mlir::Block *targetBlock = targetEval.block;
- mlir::Region *currentRegion = &builder->getRegion();
- if (targetBlock && targetBlock->getParent() != currentRegion) {
- mlir::acc::YieldOp::create(*builder, toLocation());
- return;
- }
- }
- genConstructExitBranch(targetEval);
+ genConstructExitBranch(*getEval().controlSuccessor);
}
// Nop statements - No code, or code is generated at the construct level.
@@ -6256,29 +6250,29 @@ class FirConverter : public Fortran::lower::AbstractConverter {
// calls does block management, possibly starting a new block, and possibly
// generating a branch to end a block. So these calls may still be required
// for that functionality.
- void genFIR(const Fortran::parser::AssociateStmt &) {} // nop
- void genFIR(const Fortran::parser::BlockStmt &) {} // nop
- void genFIR(const Fortran::parser::CaseStmt &) {} // nop
- void genFIR(const Fortran::parser::ContinueStmt &) {} // nop
- void genFIR(const Fortran::parser::ElseIfStmt &) {} // nop
- void genFIR(const Fortran::parser::ElseStmt &) {} // nop
- void genFIR(const Fortran::parser::EndAssociateStmt &) {} // nop
- void genFIR(const Fortran::parser::EndBlockStmt &) {} // nop
- void genFIR(const Fortran::parser::EndDoStmt &) {} // nop
- void genFIR(const Fortran::parser::EndFunctionStmt &) {} // nop
- void genFIR(const Fortran::parser::EndIfStmt &) {} // nop
- void genFIR(const Fortran::parser::EndMpSubprogramStmt &) {} // nop
- void genFIR(const Fortran::parser::EndProgramStmt &) {} // nop
- void genFIR(const Fortran::parser::EndSelectStmt &) {} // nop
- void genFIR(const Fortran::parser::EndSubroutineStmt &) {} // nop
- void genFIR(const Fortran::parser::EntryStmt &) {} // nop
- void genFIR(const Fortran::parser::IfStmt &) {} // nop
- void genFIR(const Fortran::parser::IfThenStmt &) {} // nop
- void genFIR(const Fortran::parser::NonLabelDoStmt &) {} // nop
- void genFIR(const Fortran::parser::OmpEndLoopDirective &) {} // nop
- void genFIR(const Fortran::parser::SelectTypeStmt &) {} // nop
- void genFIR(const Fortran::parser::TypeGuardStmt &) {} // nop
- void genFIR(const Fortran::parser::ChangeTeamStmt &stmt) {} // nop
+ void genFIR(const Fortran::parser::AssociateStmt &) {} // nop
+ void genFIR(const Fortran::parser::BlockStmt &) {} // nop
+ void genFIR(const Fortran::parser::CaseStmt &) {} // nop
+ void genFIR(const Fortran::parser::ContinueStmt &) {} // nop
+ void genFIR(const Fortran::parser::ElseIfStmt &) {} // nop
+ void genFIR(const Fortran::parser::ElseStmt &) {} // nop
+ void genFIR(const Fortran::parser::EndAssociateStmt &) {} // nop
+ void genFIR(const Fortran::parser::EndBlockStmt &) {} // nop
+ void genFIR(const Fortran::parser::EndDoStmt &) {} // nop
+ void genFIR(const Fortran::parser::EndFunctionStmt &) {} // nop
+ void genFIR(const Fortran::parser::EndIfStmt &) {} // nop
+ void genFIR(const Fortran::parser::EndMpSubprogramStmt &) {} // nop
+ void genFIR(const Fortran::parser::EndProgramStmt &) {} // nop
+ void genFIR(const Fortran::parser::EndSelectStmt &) {} // nop
+ void genFIR(const Fortran::parser::EndSubroutineStmt &) {} // nop
+ void genFIR(const Fortran::parser::EntryStmt &) {} // nop
+ void genFIR(const Fortran::parser::IfStmt &) {} // nop
+ void genFIR(const Fortran::parser::IfThenStmt &) {} // nop
+ void genFIR(const Fortran::parser::NonLabelDoStmt &) {} // nop
+ void genFIR(const Fortran::parser::OmpEndLoopDirective &) {} // nop
+ void genFIR(const Fortran::parser::SelectTypeStmt &) {} // nop
+ void genFIR(const Fortran::parser::TypeGuardStmt &) {} // nop
+ void genFIR(const Fortran::parser::ChangeTeamStmt &stmt) {} // nop
void genFIR(const Fortran::parser::EndChangeTeamStmt &stmt) {} // nop
/// Generate FIR for Evaluation \p eval.
diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index e54b3ac9b21bd..29aa57666ebab 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -1759,7 +1759,7 @@ void AccDataMap::remapDataOperandSymbols(
llvm::cast<hlfir::DeclareOp>(*computeDef).setSkipRebox(true);
symbolMap.addVariableDefinition(
- symbol, llvm::cast<fir::FortranVariableOpInterface>(computeDef), true);
+ symbol, llvm::cast<fir::FortranVariableOpInterface>(computeDef));
}
for (const auto &comp : components) {
@@ -2768,7 +2768,8 @@ static void genACCDataOp(Fortran::lower::AbstractConverter &converter,
addOperands(operands, operandSegments, waitOperands);
addOperands(operands, operandSegments, dataClauseOperands);
- if (dataClauseOperands.empty() && !hasDefaultNone && !hasDefaultPresent)
+ if (dataClauseOperands.empty() && !hasDefaultNone && !hasDefaultPresent &&
+ !eval.lowerAsUnstructured())
return;
auto dataOp = createRegionOp<mlir::acc::DataOp, mlir::acc::TerminatorOp>(
@@ -2857,40 +2858,41 @@ genACCHostDataOp(Fortran::lower::AbstractConverter &converter,
for (const Fortran::parser::AccClause &clause : accClauseList.v) {
if (const auto *useDevice =
std::get_if<Fortran::parser::AccClause::UseDevice>(&clause.u)) {
- // For CUDA Fortran interoperability, extra symbols are used in the
- // host_data region. Look for them and bind their values with the symbols
- // in the outer scope.
- const Fortran::parser::AccObjectList &objectList{useDevice->v};
- for (const auto &accObject : objectList.v) {
- const Fortran::semantics::Symbol *newSym = nullptr;
- if (const auto *designator =
- std::get_if<Fortran::parser::Designator>(&accObject.u)) {
- if (const auto *name =
- Fortran::parser::GetDesignatorNameIfDataRef(*designator)) {
- newSym = name->symbol;
- } else if (const auto *arrayElement =
- Fortran::parser::Unwrap<Fortran::parser::ArrayElement>(
- *designator)) {
- const Fortran::parser::Name &name =
- Fortran::parser::GetLastName(arrayElement->Base());
- newSym = name.symbol;
- } else if (const auto *component = Fortran::parser::Unwrap<
- Fortran::parser::StructureComponent>(*designator)) {
- const Fortran::parser::DataRef &base{component->Base()};
+ // When CUDA Fortran is enabled, extra symbols are used in the host_data
+ // region. Look for them and bind their values with the symbols in the
+ // outer scope.
+ if (semanticsContext.IsEnabled(Fortran::common::LanguageFeature::CUDA)) {
+ const Fortran::parser::AccObjectList &objectList{useDevice->v};
+ for (const auto &accObject : objectList.v) {
+ const Fortran::semantics::Symbol *newSym = nullptr;
+ if (const auto *designator =
+ std::get_if<Fortran::parser::Designator>(&accObject.u)) {
if (const auto *name =
- std::get_if<Fortran::parser::Name>(&base.u)) {
+ Fortran::parser::GetDesignatorNameIfDataRef(*designator)) {
newSym = name->symbol;
+ } else if (const auto *arrayElement = Fortran::parser::Unwrap<
+ Fortran::parser::ArrayElement>(*designator)) {
+ const Fortran::parser::Name &name =
+ Fortran::parser::GetLastName(arrayElement->Base());
+ newSym = name.symbol;
+ } else if (const auto *component = Fortran::parser::Unwrap<
+ Fortran::parser::StructureComponent>(*designator)) {
+ const Fortran::parser::DataRef &base{component->Base()};
+ if (const auto *name =
+ std::get_if<Fortran::parser::Name>(&base.u)) {
+ newSym = name->symbol;
+ }
}
+ } else if (const auto *name =
+ std::get_if<Fortran::parser::Name>(&accObject.u)) {
+ newSym = name->symbol;
+ }
+ if (newSym) {
+ const Fortran::semantics::Symbol *origSym =
+ localSymbols.lookupSymbolByName(newSym->name().ToString());
+ if (origSym)
+ localSymbols.copySymbolBinding(*origSym, *newSym);
}
- } else if (const auto *name =
- std::get_if<Fortran::parser::Name>(&accObject.u)) {
- newSym = name->symbol;
- }
- if (newSym) {
- const Fortran::semantics::Symbol *origSym =
- localSymbols.lookupSymbolByName(newSym->name().ToString());
- if (origSym && *origSym != *newSym)
- localSymbols.copySymbolBinding(*origSym, *newSym);
}
}
genDataOperandOperations<mlir::acc::UseDeviceOp>(
@@ -4560,6 +4562,26 @@ void Fortran::lower::genEarlyReturnInOpenACCLoop(fir::FirOpBuilder &builder,
mlir::acc::YieldOp::create(builder, loc, yieldValue);
}
+bool Fortran::lower::genOpenACCRegionExitBranch(fir::FirOpBuilder &builder,
+ mlir::Location loc,
+ mlir::Block *targetBlock) {
+ mlir::Block *currentBlock = builder.getBlock();
+ if (!currentBlock || !targetBlock)
+ return false;
+ mlir::Region *curRegion = currentBlock->getParent();
+ mlir::Region *targetRegion = targetBlock->getParent();
+ if (curRegion == targetRegion)
+ return false;
+ mlir::Operation *parentOp = curRegion->getParentOp();
+ if (!mlir::isa<ACC_COMPUTE_CONSTRUCT_OPS, ACC_DATA_CONSTRUCT_STRUCTURED_OPS,
+ mlir::acc::LoopOp>(parentOp))
+ return false;
+ if (targetRegion != curRegion->getParentRegion())
+ TODO(loc, "GOTO exiting multiple OpenACC region levels");
+ genOpenACCTerminator(builder, parentOp, loc);
+ return true;
+}
+
uint64_t Fortran::lower::getLoopCountForCollapseAndTile(
const Fortran::parser::AccClauseList &clauseList) {
uint64_t collapseLoopCount = getCollapseSizeAndForce(clauseList).first;
diff --git a/flang/test/Lower/OpenACC/Todo/acc-goto-multi-level-exit.f90 b/flang/test/Lower/OpenACC/Todo/acc-goto-multi-level-exit.f90
new file mode 100644
index 0000000000000..9ea56a752ccb8
--- /dev/null
+++ b/flang/test/Lower/OpenACC/Todo/acc-goto-multi-level-exit.f90
@@ -0,0 +1,42 @@
+! RUN: split-file %s %t
+! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/acc_loop_multi_level.f90 -o - 2>&1 | FileCheck %s --check-prefix=CHECK1
+! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/acc_data_multi_level.f90 -o - 2>&1 | FileCheck %s --check-prefix=CHECK2
+
+//--- acc_loop_multi_level.f90
+
+subroutine acc_loop_multi_level(a, n)
+ integer :: n, i, j
+ real :: a(*)
+
+ !$acc parallel
+ !$acc loop seq
+ do i = 1, n
+ do j = 1, n
+ if (a(j) > 0.0) goto 999
+ end do
+ end do
+ !$acc end parallel
+999 continue
+end subroutine
+
+! CHECK1: not yet implemented: GOTO exiting multiple OpenACC region levels
+
+//--- acc_data_multi_level.f90
+
+subroutine acc_data_multi_level(a, n)
+ integer :: n, i, j
+ real :: a(*)
+
+ !$acc parallel
+ !$acc data
+ do i = 1, n
+ do j = 1, n
+ if (a(j) > 0.0) goto 999
+ end do
+ end do
+ !$acc end data
+ !$acc end parallel
+999 continue
+end subroutine
+
+! CHECK2: not yet implemented: GOTO exiting multiple OpenACC region levels
diff --git a/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90 b/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90
deleted file mode 100644
index 696051b9bb534..0000000000000
--- a/flang/test/Lower/OpenACC/acc-loop-goto-exit.f90
+++ /dev/null
@@ -1,33 +0,0 @@
-! Test that GoTo exiting an acc.loop seq region generates acc.yield
-! instead of an illegal cross-region cf.br that would crash the compiler.
-! RUN: bbc -fopenacc -emit-hlfir %s -o - | FileCheck %s
-
-! CHECK-LABEL: func.func @_QPfoo
-subroutine foo(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
-
-! Verify the inner acc.loop (seq) contains acc.yield for the GoTo exit.
-! The GoTo target is outside the acc.loop region, so it must yield
-! instead of generating an illegal cross-region cf.br.
-
-! CHECK: acc.loop gang vector
-! CHECK: acc.loop
-! The GoTo comparison and branch:
-! CHECK: arith.cmpf ogt
-! CHECK-NEXT: cf.cond_br %{{.*}}, ^[[EXIT:bb[0-9]+]], ^
-! CHECK-NEXT: ^[[EXIT]]:
-! CHECK-NEXT: acc.yield
-! Normal loop end yield and closing:
-! CHECK: acc.yield
-! CHECK-NEXT: } attributes {seq = [#acc.device_type<none>], unstructured}
diff --git a/flang/test/Lower/OpenACC/acc-unstructured.f90 b/flang/test/Lower/OpenACC/acc-unstructured.f90
index bbbf8974a6c30..966f82eb88ff8 100644
--- a/flang/test/Lower/OpenACC/acc-unstructured.f90
+++ b/flang/test/Lower/OpenACC/acc-unstructured.f90
@@ -84,3 +84,79 @@ subroutine test_unstructured3(a, b, c)
! CHECK: acc.yield
end subroutine
+
+! Test that acc.data is still created when there are no data clauses but the
+! construct contains unstructured control flow. Without this, the early return
+! in genACCDataOp skips acc.data creation, leaving orphaned blocks.
+subroutine test_unstructured4(a, n)
+ integer :: n, i, j
+ real :: a(:)
+ logical :: use_gpu
+
+ use_gpu = .true.
+ !$acc data if(use_gpu)
+ do i = 1, n
+ do j = 1, n
+ if (a(j) > 0.0) stop 'unstructured'
+ end do
+ end do
+ !$acc end data
+
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_unstructured4
+! CHECK: acc.data if(%{{.*}}) {
+! CHECK: fir.call @_FortranAStopStatementText
+! CHECK: acc.terminator
+! CHECK: }
+
+! Test that GOTO exiting acc.data (one level) generates acc.terminator
+! instead of an invalid cross-region branch.
+subroutine test_unstructured5(a, n)
+ integer :: n, i, j
+ real :: a(:)
+ logical :: use_gpu
+
+ use_gpu = .true.
+ !$acc data if(use_gpu)
+ do i = 1, n
+ do j = 1, n
+ if (a(j) > 0.0) goto 999
+ end do
+ end do
+ !$acc end data
+999 continue
+
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_unstructured5
+! CHECK: acc.data if(%{{.*}}) {
+! CHECK: acc.terminator
+! CHECK: acc.terminator
+! CHECK: }
+
+! Test that GOTO exiting acc.loop (one level) generates acc.yield
+! instead of an invalid cross-region branch.
+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
+
+! CHECK-LABEL: func.func @_QPtest_unstructured6
+! CHECK: acc.loop gang vector
+! CHECK: acc.loop
+! CHECK: arith.cmpf ogt
+! CHECK-NEXT: cf.cond_br %{{.*}}, ^[[EXIT:bb[0-9]+]], ^
+! CHECK-NEXT: ^[[EXIT]]:
+! CHECK-NEXT: acc.yield
+! CHECK: acc.yield
+! CHECK-NEXT: } attributes {seq = [#acc.device_type<none>], unstructured}
More information about the flang-commits
mailing list