[llvm-branch-commits] [flang] [flang] Lower loops whose branching is confined to their body structurally (PR #225758)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Sep 23 06:09:39 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-openacc
Author: Kareem Ergawy (ergawy)
<details>
<summary>Changes</summary>
Such a loop was classified separately by a previous change but still lowered as a raw CFG, so its structured form was lost.
Lower it structurally instead, with its body folded into a region that can hold the branching. The loop keeps its bounds on the op, so it remains available to whatever transforms or parallelizes it. Only the body is folded: the loop control statements are emitted as they are for any structured loop, since a branch from outside may target either of them.
Loops an OpenACC or OpenMP directive owns are lowered the same way, so they keep their form too.
---
Patch is 43.56 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/225758.diff
16 Files Affected:
- (modified) flang/include/flang/Lower/AbstractConverter.h (+8)
- (modified) flang/include/flang/Lower/PFTBuilder.h (+10-6)
- (modified) flang/lib/Lower/Bridge.cpp (+119-11)
- (modified) flang/lib/Lower/OpenMP/OpenMP.cpp (+8)
- (modified) flang/lib/Lower/PFTBuilder.cpp (+4)
- (modified) flang/test/Lower/OpenACC/acc-cache.f90 (+9-10)
- (added) flang/test/Lower/OpenACC/acc-unstructured-internals.f90 (+73)
- (modified) flang/test/Lower/OpenACC/acc-unstructured.f90 (+8-9)
- (removed) flang/test/Lower/OpenMP/Todo/metadirective-loop-unstructured.f90 (-39)
- (added) flang/test/Lower/OpenMP/metadirective-loop-unstructured.f90 (+50)
- (modified) flang/test/Lower/OpenMP/wsloop-unstructured-cycle.f90 (+34-28)
- (added) flang/test/Lower/OpenMP/wsloop-unstructured-internals.f90 (+44)
- (modified) flang/test/Lower/always-execute-loop-body.f90 (+6-2)
- (added) flang/test/Lower/do-loop-branch-to-loop-header.f90 (+55)
- (modified) flang/test/Lower/do_loop_unstructured.f90 (+20-134)
- (modified) flang/test/Lower/select-case-statement.f90 (+5-2)
``````````diff
diff --git a/flang/include/flang/Lower/AbstractConverter.h b/flang/include/flang/Lower/AbstractConverter.h
index ae246d3188bd88..40d94dc9f5bfdd 100644
--- a/flang/include/flang/Lower/AbstractConverter.h
+++ b/flang/include/flang/Lower/AbstractConverter.h
@@ -384,6 +384,14 @@ class AbstractConverter {
virtual void genEval(pft::Evaluation &eval,
bool unstructuredContext = true) = 0;
+ /// Emit \p loopEval's evaluations, folding the body into an
+ /// scf.execute_region when the loop's branching is confined to that body.
+ /// The loop control statements are emitted outside any wrap, exactly as they
+ /// are for a structured loop. Used by directive lowering, which consumes the
+ /// DO itself and so never reaches genFIR(DoConstruct), where a plain loop's
+ /// body is wrapped.
+ virtual void genLoopBodyEvaluations(pft::Evaluation &loopEval) = 0;
+
/// Return options controlling lowering behavior.
const Fortran::lower::LoweringOptions &getLoweringOptions() const {
return loweringOptions;
diff --git a/flang/include/flang/Lower/PFTBuilder.h b/flang/include/flang/Lower/PFTBuilder.h
index 29f0573eb57e4d..b30f20da204232 100644
--- a/flang/include/flang/Lower/PFTBuilder.h
+++ b/flang/include/flang/Lower/PFTBuilder.h
@@ -356,17 +356,21 @@ struct Evaluation : EvaluationVariant {
controlFlow = std::min(controlFlow, kind);
}
- /// True when control flow is not fully structured, category (c) included.
- /// Existing consumers ask this to decide whether raw blocks are needed, and
- /// a category (c) construct still needs them until its body is wrapped, so
- /// it must answer true here. Use hasUnstructuredInternals() to single out
- /// category (c) itself.
- bool isUnstructured() const { return controlFlow != ControlFlow::Structured; }
+ /// True only for fully unstructured control flow, which is lowered as raw
+ /// CFG blocks.
+ bool isUnstructured() const {
+ return controlFlow == ControlFlow::Unstructured;
+ }
bool hasUnstructuredInternals() const {
return controlFlow == ControlFlow::StructuredWithUnstructuredInternals;
}
+ /// True when this construct is lowered structurally, yet its body holds
+ /// unstructured control flow that has to be folded into an
+ /// scf.execute_region.
+ bool lowerBodyAsWrappedRegion() const;
+
bool lowerAsStructured() const;
bool lowerAsUnstructured() const;
bool forceAsUnstructured() const;
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 784a87cc619979..ed862d6b3374de 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -1125,6 +1125,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
genFIR(eval, unstructuredContext);
}
+ void genLoopBodyEvaluations(
+ Fortran::lower::pft::Evaluation &loopEval) override final {
+ genLoopBodyEvaluations(loopEval, /*unstructuredContext=*/true);
+ }
+
//===--------------------------------------------------------------------===//
// Utility methods
//===--------------------------------------------------------------------===//
@@ -2637,6 +2642,97 @@ class FirConverter : public Fortran::lower::AbstractConverter {
return wrapOp;
}
+ /// Wrap a loop's *body* -- not the construct, and not the loop control -- in
+ /// an scf.execute_region. A category (c) loop keeps its structured loop op
+ /// while its self-contained raw branching lives inside the region, which may
+ /// hold as many blocks as it needs.
+ ///
+ /// The builder must already be positioned inside the loop body. On return it
+ /// is inside the region. Returns null when the loop has no such internals.
+ mlir::scf::ExecuteRegionOp
+ wrapUnstructuredBody(Fortran::lower::pft::Evaluation &eval,
+ mlir::Block *&yieldBlock,
+ mlir::Block *&savedEndDoBlock) {
+ if (!eval.lowerBodyAsWrappedRegion())
+ return nullptr;
+
+ Fortran::lower::pft::EvaluationList &list = eval.getNestedEvaluations();
+ mlir::Location loc = toLocation();
+ auto wrapOp =
+ mlir::scf::ExecuteRegionOp::create(*builder, loc, mlir::TypeRange{},
+ /*noInline=*/builder->getUnitAttr());
+ ++wrapUnstructuredCount;
+ mlir::Block *entry = builder->createBlock(&wrapOp.getRegion());
+ builder->setInsertionPointToEnd(entry);
+ // Only the body: both loop control statements live in the enclosing region
+ // and may be branched to from outside the loop, so neither may take its
+ // block from this region. Dropping only the EndDoStmt leaves the DO
+ // statement to be given a block here, which any GOTO targeting the loop
+ // head would then reference across a region boundary.
+ createEmptyBlocksIn(
+ llvm::make_range(std::next(list.begin()), std::prev(list.end())));
+ yieldBlock = builder->createBlock(&wrapOp.getRegion());
+ builder->setInsertionPointToEnd(yieldBlock);
+ mlir::scf::YieldOp::create(*builder, loc);
+
+ // A CYCLE targets the EndDoStmt, which is the boundary between the loop
+ // body and the loop control. Inside the wrap that boundary is the region's
+ // yield block, so a CYCLE leaves the region rather than branching to a
+ // block the region cannot name.
+ savedEndDoBlock = list.back().block;
+ list.back().block = yieldBlock;
+
+ builder->setInsertionPointToEnd(entry);
+ return wrapOp;
+ }
+
+ /// Emit a loop's evaluations, optionally folding the body into an
+ /// scf.execute_region. The loop control statements -- the first and last
+ /// evaluations -- are emitted exactly as they are for a structured loop:
+ /// same context flag, same region, same blocks. They may be branch targets
+ /// from outside the loop, so their blocks have to stay in the enclosing
+ /// region. Only what lies strictly between them goes inside the wrap.
+ void genLoopBodyEvaluations(Fortran::lower::pft::Evaluation &eval,
+ bool unstructuredContext) {
+ Fortran::lower::pft::EvaluationList &list = eval.getNestedEvaluations();
+ auto iter = list.begin();
+ auto end = std::prev(list.end());
+
+ // The loop control statement, outside any wrap.
+ if (iter != end) {
+ genFIR(*iter, unstructuredContext);
+ ++iter;
+ }
+
+ mlir::Block *yieldBlock = nullptr;
+ mlir::Block *savedEndDoBlock = nullptr;
+ mlir::scf::ExecuteRegionOp wrapOp =
+ wrapUnstructuredBody(eval, yieldBlock, savedEndDoBlock);
+ for (; iter != end; ++iter)
+ genFIR(*iter, unstructuredContext || wrapOp);
+ closeUnstructuredBodyWrap(wrapOp, eval, yieldBlock, savedEndDoBlock);
+ }
+
+ /// Finalize a wrap created by wrapUnstructuredBody: restore the EndDoStmt
+ /// block, fall through to the region's yield, and resume after the wrap op
+ /// so the caller emits the loop control outside it.
+ void closeUnstructuredBodyWrap(mlir::scf::ExecuteRegionOp wrapOp,
+ Fortran::lower::pft::Evaluation &eval,
+ mlir::Block *yieldBlock,
+ mlir::Block *savedEndDoBlock) {
+ if (!wrapOp)
+ return;
+
+ eval.getNestedEvaluations().back().block = savedEndDoBlock;
+
+ if (mlir::Block *current = builder->getBlock())
+ if (current->empty() ||
+ !current->back().hasTrait<mlir::OpTrait::IsTerminator>())
+ genBranch(yieldBlock);
+
+ builder->setInsertionPointAfter(wrapOp);
+ }
+
/// Finalize a wrap created by wrapUnstructuredConstruct: restore the
/// original exit block and set the insertion point after the wrap op.
void closeUnstructuredWrap(mlir::scf::ExecuteRegionOp wrapOp,
@@ -2664,9 +2760,10 @@ class FirConverter : public Fortran::lower::AbstractConverter {
// skip generating any loop — just lower the body. The IV value is
// already available from the parent acc.loop's block argument.
if (Fortran::lower::isCollapsedDoConstruct(doConstruct)) {
- auto iter = eval.getNestedEvaluations().begin();
- for (auto end = --eval.getNestedEvaluations().end(); iter != end; ++iter)
- genFIR(*iter, unstructuredContext);
+ // The parent acc.loop supplies the iteration, so no loop op is built
+ // here for a wrap to sit inside. The body may still hold self-contained
+ // raw branching, so wrap it directly.
+ genLoopBodyEvaluations(eval, unstructuredContext);
return;
}
@@ -2689,11 +2786,10 @@ class FirConverter : public Fortran::lower::AbstractConverter {
builder->getInsertionPoint()->getBlock()->getParent()) &&
"builder insertion point is not inside the newly generated loop");
- // Loop body code.
- auto iter = eval.getNestedEvaluations().begin();
- for (auto end = --eval.getNestedEvaluations().end(); iter != end;
- ++iter)
- genFIR(*iter, unstructuredContext);
+ // The acc.loop supplies the iteration, so the body is emitted here
+ // rather than through the increment-loop path below. Wrap it when it
+ // holds self-contained raw branching.
+ genLoopBodyEvaluations(eval, unstructuredContext);
builder->setInsertionPointAfter(loopOp);
return;
@@ -2842,10 +2938,12 @@ class FirConverter : public Fortran::lower::AbstractConverter {
if (!infiniteLoop && !whileCondition)
genFIRIncrementLoopBegin(incrementLoopNestInfo, doStmtEval.dirs);
+ // The loop control is structured, but the body may hold raw branching
+ // confined to it. Wrap the body, leaving the loop control outside, so the
+ // structured loop op's single-block region stays well formed.
// Loop body code.
- auto iter = eval.getNestedEvaluations().begin();
- for (auto end = --eval.getNestedEvaluations().end(); iter != end; ++iter)
- genFIR(*iter, unstructuredContext);
+ genLoopBodyEvaluations(eval, unstructuredContext);
+ auto iter = std::prev(eval.getNestedEvaluations().end());
// An EndDoStmt in unstructured code may start a new block.
Fortran::lower::pft::Evaluation &endDoEval = *iter;
@@ -6469,6 +6567,16 @@ class FirConverter : public Fortran::lower::AbstractConverter {
/// boundaries.
void createEmptyBlocks(
std::list<Fortran::lower::pft::Evaluation> &evaluationList) {
+ createEmptyBlocksIn(
+ llvm::make_range(evaluationList.begin(), evaluationList.end()));
+ }
+
+ /// createEmptyBlocks over a sub-range of an evaluation list. A body-only wrap
+ /// must not pre-create blocks for the loop control statements, which live in
+ /// the enclosing region.
+ void createEmptyBlocksIn(
+ llvm::iterator_range<Fortran::lower::pft::EvaluationList::iterator>
+ evaluationList) {
mlir::Region *region = &builder->getRegion();
for (Fortran::lower::pft::Evaluation &eval : evaluationList) {
if (eval.isNewBlock)
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index df14d13b76a4bd..edbb14a745a55a 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -1018,6 +1018,14 @@ static void genNestedEvaluations(lower::AbstractConverter &converter,
int collapseValue = 0) {
lower::pft::Evaluation *curEval = getCollapsedLoopEval(eval, collapseValue);
+ // The directive consumes the DO itself, so genFIR(DoConstruct) -- where a
+ // plain loop's body is wrapped -- is never reached. Go through the same
+ // helper here, so the loop control statements are emitted outside the wrap
+ // exactly as they are for a structured loop.
+ if (curEval->isA<parser::DoConstruct>() && curEval->hasNestedEvaluations()) {
+ converter.genLoopBodyEvaluations(*curEval);
+ return;
+ }
for (lower::pft::Evaluation &e : curEval->getNestedEvaluations())
converter.genEval(e);
}
diff --git a/flang/lib/Lower/PFTBuilder.cpp b/flang/lib/Lower/PFTBuilder.cpp
index 410bab33a80355..0a8d7a9dcd20af 100644
--- a/flang/lib/Lower/PFTBuilder.cpp
+++ b/flang/lib/Lower/PFTBuilder.cpp
@@ -1735,6 +1735,10 @@ bool Fortran::lower::pft::Evaluation::lowerAsUnstructured() const {
return isUnstructured() || clDisableStructuredFir;
}
+bool Fortran::lower::pft::Evaluation::lowerBodyAsWrappedRegion() const {
+ return hasUnstructuredInternals() && !lowerAsUnstructured();
+}
+
bool Fortran::lower::pft::Evaluation::forceAsUnstructured() const {
return clDisableStructuredFir;
}
diff --git a/flang/test/Lower/OpenACC/acc-cache.f90 b/flang/test/Lower/OpenACC/acc-cache.f90
index 12bdee38338705..151ef27a96ddc4 100644
--- a/flang/test/Lower/OpenACC/acc-cache.f90
+++ b/flang/test/Lower/OpenACC/acc-cache.f90
@@ -387,11 +387,11 @@ subroutine test_cache_nonunit_lb()
! For arr(10:20), startIdx = 10, element 15 has lowerbound = 15 - 10 = 5
! CHECK: %[[C10:.*]] = arith.constant 10 : index
-! Unstructured loop with SELECT CASE: acc.loop becomes unstructured
-! CHECK: acc.loop private({{.*}}) {
-! CHECK: cf.br ^[[HEADER:.*]]
-! CHECK: ^[[HEADER]]:
-! CHECK: cf.cond_br %{{.*}}, ^[[BODY:.*]], ^[[EXIT:.*]]
+! The SELECT CASE branches only within the loop body, so acc.loop keeps its
+! structured control and the raw blocks live in a wrap inside it.
+! CHECK: acc.loop private({{.*}})
+! CHECK: scf.execute_region no_inline {
+! CHECK: cf.br ^[[BODY:.*]]
! CHECK: ^[[BODY]]:
! Compute lowerbound = 15 - startIdx = 15 - 10 = 5
! CHECK: %[[C1:.*]] = arith.constant 1 : index
@@ -420,13 +420,12 @@ subroutine test_cache_nonunit_lb()
! CHECK: hlfir.designate %[[DECL]]#0
! CHECK: hlfir.assign
! CHECK: cf.br ^[[MERGE]]
-! All SELECT CASE branches converge, then loop back or exit
+! All SELECT CASE branches converge, then leave the wrap. The loop control is
+! acc.loop's own, so the region ends at its yield rather than a back edge.
! CHECK: ^[[MERGE]]:
-! CHECK: cf.br ^[[HEADER]]
+! CHECK: cf.br ^[[EXIT:.*]]
! CHECK: ^[[EXIT]]:
-! Scope termination: acc.yield marks end of cache scope
-! CHECK: acc.yield
-! CHECK-NEXT: } {{.*}}unstructured{{.*}}
+! CHECK: scf.yield
end subroutine
! CHECK-LABEL: func.func @_QPtest_cache_use_after_region()
diff --git a/flang/test/Lower/OpenACC/acc-unstructured-internals.f90 b/flang/test/Lower/OpenACC/acc-unstructured-internals.f90
new file mode 100644
index 00000000000000..f0be621384a6ac
--- /dev/null
+++ b/flang/test/Lower/OpenACC/acc-unstructured-internals.f90
@@ -0,0 +1,73 @@
+! RUN: bbc -fopenacc -emit-hlfir -o - %s | FileCheck %s
+
+! Loops under an OpenACC directive whose control flow is structured on the
+! outside but whose branching is confined to the body. The directive's own
+! code-gen consumes the DO, so the body is wrapped at the directive's body
+! lowering site rather than in genFIR(DoConstruct). The loop keeps its bounds
+! on the op -- control(...) rather than a cf trip-count test -- so it is still
+! available to be parallelized.
+
+! A forward GOTO raised inside a nested IF, jumping over a whole inner DO and
+! landing on the last statement of the outer loop body. Both endpoints are
+! inside the body, so the outer loop stays an acc.loop with control(...) and
+! the two inner loops stay acc.loops of their own.
+subroutine kernels_goto_over_inner(qfx, a, its, ite, jts, jte, force, flux)
+ real :: qfx(ite,jte), a(ite,jte)
+ logical :: force
+ integer :: flux
+ !$acc kernels
+ do j = jts, jte
+ do 330 i = its, ite
+ a(i,j) = a(i,j) + 1.0
+330 continue
+ if (force) then
+ if (flux .eq. 1) goto 350
+ endif
+ do i = its, ite
+ qfx(i,j) = 0.
+ enddo
+350 continue
+ enddo
+ !$acc end kernels
+end subroutine
+
+! CHECK-LABEL: func.func @_QPkernels_goto_over_inner
+! CHECK: acc.kernels {
+! CHECK: acc.loop private({{.*}}) control(%{{.*}} : i32) = (%{{.*}} : i32) to (%{{.*}} : i32) step (%{{.*}} : i32) {
+! CHECK: scf.execute_region no_inline {
+! The inner loops are untouched by the wrap around the outer body.
+! CHECK: acc.loop private({{.*}}) control(%{{.*}} : i32) = (%{{.*}} : i32) to (%{{.*}} : i32) step (%{{.*}} : i32) {
+! CHECK: acc.yield
+! The GOTO and the branches it feeds stay inside the region.
+! CHECK: cf.cond_br %{{[0-9]+}}, ^bb[[THEN:[0-9]+]], ^bb[[SKIP:[0-9]+]]
+! CHECK: acc.loop private({{.*}}) control(%{{.*}} : i32) = (%{{.*}} : i32) to (%{{.*}} : i32) step (%{{.*}} : i32) {
+! CHECK: acc.yield
+! CHECK: scf.yield
+! CHECK: acc.yield
+
+! A CYCLE targets the EndDoStmt, the boundary between the loop body and the
+! loop control, so inside the wrap it leaves the region at its yield.
+subroutine parallel_loop_cycle(a, n)
+ real :: a(n)
+ !$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-LABEL: func.func @_QPparallel_loop_cycle
+! CHECK: acc.parallel combined(loop) {
+! CHECK: acc.loop 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]]:
+! CHECK: cf.br ^bb[[EXIT:[0-9]+]]
+! CHECK: ^bb[[BODY]]:
+! CHECK: cf.br ^bb[[EXIT]]
+! CHECK: ^bb[[EXIT]]:
+! CHECK: scf.yield
+! CHECK: acc.yield
diff --git a/flang/test/Lower/OpenACC/acc-unstructured.f90 b/flang/test/Lower/OpenACC/acc-unstructured.f90
index cbb27d74cc96c2..6a95f664f78dfd 100644
--- a/flang/test/Lower/OpenACC/acc-unstructured.f90
+++ b/flang/test/Lower/OpenACC/acc-unstructured.f90
@@ -275,19 +275,18 @@ subroutine test_unstructured_collapse_cycle(a)
! Both induction variables (j and i) are privatized:
! CHECK: %[[PRIVJ:.*]] = acc.private varPtr(%{{.*}} : !fir.ref<i32>) recipe(@privatization_ref_i32) implicit(true) name("j") -> !fir.ref<i32>
! CHECK: %[[PRIVI:.*]] = acc.private varPtr(%{{.*}} : !fir.ref<i32>) recipe(@privatization_ref_i32) implicit(true) name("i") -> !fir.ref<i32>
-! No control(...) on acc.loop — bounds are not on the op:
! 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
-! Inner loop trip-count test (i) emitted as cf:
-! CHECK: arith.cmpi sgt
-! CHECK: cf.cond_br
-! The if/cycle is a structured cf branch in the body:
+! The IF-guarded CYCLE branches only within the body, so both loops keep their
+! bounds on the op -- control(...) rather than a cf trip-count test -- and the
+! raw blocks are confined to a wrap inside each body.
+! CHECK: acc.loop private({{.*}}) control(%{{.*}} : i32) = (%{{.*}} : i32) to (%{{.*}} : i32) step (%{{.*}} : i32) {
+! CHECK: scf.execute_region no_inline {
+! CHECK: acc.loop private({{.*}}) control(%{{.*}} : i32) = (%{{.*}} : i32) to (%{{.*}} : i32) step (%{{.*}} : i32) {
+! CHECK: scf.execute_region no_inline {
! CHECK: arith.cmpi eq
! CHECK: cf.cond_br
+! CHECK: scf.yield
! CHECK: acc.yield
-! CHECK: }
! `acc serial loop collapse(N)` with STOP in body: wrap-in-execute-region hides
! the unstructured if/stop and the three collapsed iterators lower as a single
diff --git a/flang/test/Lower/OpenMP/Todo/metadirective-loop-unstructured.f90 b/flang/test/Lower/OpenMP/Todo/metadirective-loop-unstructured.f90
deleted file mode 100644
index 08ebcc7b4c8eff..00000000000000
--- a/flang/test/Lower/OpenMP/Todo/metadirective-loop-unstructured.f90
+++ /dev/null
@@ -1,39 +0,0 @@
-! Defer unstructured associated loops until every selection path can give its
-! PFT blocks an independent mapping.
-
-! RUN: split-file %s %t
-! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/static.f90 2>&1 | FileCheck %s
-! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/runtime.f90 2>&1 | FileCheck %s
-
-! CHECK: not yet implemented: unstructured associated DO in loop-associated METADIRECTIVE variant
-
-!--- static.f90
-subroutine test_static(n, a, selector)
- integer :: n, a(n), selector, i
- !$omp metadirective &
- !$omp & when(implementation={vendor(llvm)}: do) &
- !$omp & otherwise(nothing)
- do i = 1, n
- go to (10, 20), selector
-10 a(i) = 1
- go to 30
-20 a(i) = 2
-30 c...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/225758
More information about the llvm-branch-commits
mailing list