[flang-commits] [flang] [flang][PFT-to-MLIR] Wrap unstructured Fortran constructs in scf.execute_region (PR #208635)
Kareem Ergawy via flang-commits
flang-commits at lists.llvm.org
Fri Jul 10 00:11:51 PDT 2026
https://github.com/ergawy created https://github.com/llvm/llvm-project/pull/208635
Extend the PFT-to-MLIR (HLFIR/FIR) lowering so unstructured DO and IF constructs are emitted inside scf.execute_region, hiding their multi-block CFG behind a single op. OpenACC and OpenMP lowerings that reject multi-block content (e.g. the "unstructured do loop in combined acc construct" TODO in OpenACC.cpp) now see a structured op instead.
Flag: -mmlir --wrap-unstructured-constructs-in-execute-region (default on).
An evaluation is wrappable iff all of the following hold:
* wrap flag on
* eval is parser::DoConstruct or parser::IfConstruct
* eval.isUnstructured
* branchesAreInternal(eval) -- every controlSuccessor in the subtree targets a nested eval or the constructExit
* !hasIncomingBranch(eval) -- no outside eval branches into the body (PFT's synthetic IfConstruct around `if(c) goto X` absorbs label targets between the IF and X; the incoming-branch check excludes such wrappers when an outer GOTO names one of those labels)
* does not contain a ReturnStmt -- its lowering creates the function's final block in the current region, which would mis-parent func.return
* not an infinite DO and does not contain one (no LoopControl in any nested DO): the wrap's yield is unreachable and the body has no write-shaped side effects, so RegionDCE treats the wrap as trivially dead and drops it. Excluding the whole enclosing construct keeps such infinite loops visible in the parent CFG.
* not the body DO of an enclosing OpenACCLoopConstruct or OpenACCCombinedConstruct -- nor one of the N collapsed iterator DOs reached by walking down through `collapse(N)`. Such DOs are driven directly into acc.loop by the OpenACC lowering, so wrapping them would hide the iteration from the acc.loop op.
Diagnostics
-----------
Emit per-wrap and per-function diagnostics on stderr so the wrapping is observable from a single compile invocation:
[wrap-unstructured] wrapped DO at <loc>
[wrap-unstructured] wrapped IF at <loc>
[wrap-unstructured] summary: N execute_region(s) wrapping unstructured
constructs at <loc>
The counter is reset per function and the summary is suppressed when no wraps fire.
Co-authored-by: Claude Opus 4.7 <noreply at anthropic.com>
>From 2eb2db183fcb7830ab523ed2af315caf8719026d Mon Sep 17 00:00:00 2001
From: ergawy <kareem.ergawy at gmail.com>
Date: Tue, 16 Jun 2026 08:16:40 -0700
Subject: [PATCH] [flang][PFT-to-MLIR] Wrap unstructured Fortran constructs in
scf.execute_region
Extend the PFT-to-MLIR (HLFIR/FIR) lowering so unstructured DO and IF
constructs are emitted inside scf.execute_region, hiding their multi-block
CFG behind a single op. OpenACC and OpenMP lowerings that reject
multi-block content (e.g. the "unstructured do loop in combined acc
construct" TODO in OpenACC.cpp) now see a structured op instead.
Flag: -mmlir --wrap-unstructured-constructs-in-execute-region (default on).
An evaluation is wrappable iff all of the following hold:
* wrap flag on
* eval is parser::DoConstruct or parser::IfConstruct
* eval.isUnstructured
* branchesAreInternal(eval) -- every controlSuccessor in the subtree
targets a nested eval or the constructExit
* !hasIncomingBranch(eval) -- no outside eval branches into the body
(PFT's synthetic IfConstruct around `if(c) goto X` absorbs label
targets between the IF and X; the incoming-branch check excludes
such wrappers when an outer GOTO names one of those labels)
* does not contain a ReturnStmt -- its lowering creates the function's
final block in the current region, which would mis-parent func.return
* not an infinite DO and does not contain one (no LoopControl in any
nested DO): the wrap's yield is unreachable and the body has no
write-shaped side effects, so RegionDCE treats the wrap as trivially
dead and drops it. Excluding the whole enclosing construct keeps
such infinite loops visible in the parent CFG.
* not the body DO of an enclosing OpenACCLoopConstruct or
OpenACCCombinedConstruct -- nor one of the N collapsed iterator DOs
reached by walking down through `collapse(N)`. Such DOs are driven
directly into acc.loop by the OpenACC lowering, so wrapping them
would hide the iteration from the acc.loop op.
Diagnostics
-----------
Emit per-wrap and per-function diagnostics on stderr so the wrapping is
observable from a single compile invocation:
[wrap-unstructured] wrapped DO at <loc>
[wrap-unstructured] wrapped IF at <loc>
[wrap-unstructured] summary: N execute_region(s) wrapping unstructured
constructs at <loc>
The counter is reset per function and the summary is suppressed when no
wraps fire.
Co-authored-by: Claude Opus 4.7 <noreply at anthropic.com>
---
flang/include/flang/Lower/PFTBuilder.h | 10 +
flang/lib/Lower/Bridge.cpp | 103 +++++++-
flang/lib/Lower/PFTBuilder.cpp | 236 +++++++++++++++++-
flang/lib/Lower/Runtime.cpp | 8 +-
.../assumed_shape_with_value_keyword.f90 | 17 +-
flang/test/HLFIR/optional_dummy.f90 | 23 +-
.../parallel-private-reduction-worstcase.f90 | 3 +-
.../Lower/HLFIR/intrinsic-subroutines.f90 | 41 +--
flang/test/Lower/MIF/change_team2.f90 | 27 +-
.../acc-unstructured-combined-construct.f90 | 64 +----
.../Todo/do-loops-to-acc-loops-todo.f90 | 19 --
flang/test/Lower/OpenACC/acc-terminator.f90 | 3 +-
flang/test/Lower/OpenACC/acc-unstructured.f90 | 201 +++++++++++++--
flang/test/Lower/OpenMP/loop-compound.f90 | 30 ++-
.../test/Lower/OpenMP/parallel-reduction3.f90 | 29 ++-
.../test/Lower/OpenMP/stop-stmt-in-region.f90 | 69 ++---
flang/test/Lower/OpenMP/unstructured.f90 | 205 +++++++--------
.../test/Lower/OpenMP/wsloop-unstructured.f90 | 35 ++-
flang/test/Lower/branching-directive.f90 | 45 +++-
.../Lower/do_loop_execute_region_wrap.f90 | 111 ++++++++
flang/test/Lower/do_loop_unstructured.f90 | 125 +++-------
flang/test/Lower/fail_image.f90 | 19 +-
flang/test/Lower/ifconvert.f90 | 6 +-
flang/test/Lower/mixed_loops.f90 | 108 ++++----
flang/test/Lower/nsw.f90 | 19 +-
flang/test/Lower/pre-fir-tree02.f90 | 4 +-
flang/test/Lower/while_loop.f90 | 15 +-
27 files changed, 1058 insertions(+), 517 deletions(-)
create mode 100644 flang/test/Lower/do_loop_execute_region_wrap.f90
diff --git a/flang/include/flang/Lower/PFTBuilder.h b/flang/include/flang/Lower/PFTBuilder.h
index 0aabd8e642e44..cb23c6ea5686a 100644
--- a/flang/include/flang/Lower/PFTBuilder.h
+++ b/flang/include/flang/Lower/PFTBuilder.h
@@ -874,6 +874,16 @@ void visitAllSymbols(const FunctionLikeUnit &funit,
void visitAllSymbols(const Evaluation &eval,
std::function<void(const semantics::Symbol &)> callBack);
+/// Return true if every control-flow successor nested under \p construct
+/// points to an evaluation that is itself nested under \p construct or is \p
+/// construct's own constructExit. A false return means at least one branch
+/// leaves the construct (e.g. a GOTO to an outer label).
+bool branchesAreInternal(const Evaluation &construct);
+
+/// Return true when \p eval is an unstructured DO or IF construct that can
+/// folded into a self-contained scf.execute_region.
+bool isWrappableConstruct(const Evaluation &eval);
+
} // namespace Fortran::lower::pft
namespace Fortran::lower {
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 8658a6fb29a44..8aaf47c64d6c4 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -2536,6 +2536,50 @@ class FirConverter : public Fortran::lower::AbstractConverter {
}
}
+ /// Wrap an unstructured construct's CFG in a self-contained
+ /// scf.execute_region and set the builder insertion point inside it. Returns
+ /// the created op (null if the construct isn't wrappable).
+ mlir::scf::ExecuteRegionOp
+ wrapUnstructuredConstruct(Fortran::lower::pft::Evaluation &eval,
+ mlir::Block *&savedExitBlock) {
+ if (!Fortran::lower::pft::isWrappableConstruct(eval))
+ return nullptr;
+
+ 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);
+ createEmptyBlocks(eval.getNestedEvaluations());
+ mlir::Block *yieldBlock = builder->createBlock(&wrapOp.getRegion());
+ builder->setInsertionPointToEnd(yieldBlock);
+ mlir::scf::YieldOp::create(*builder, loc);
+
+ if (eval.constructExit) {
+ savedExitBlock = eval.constructExit->block;
+ eval.constructExit->block = yieldBlock;
+ }
+
+ builder->setInsertionPointToEnd(entry);
+ return 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,
+ Fortran::lower::pft::Evaluation &eval,
+ mlir::Block *savedExitBlock) {
+ if (!wrapOp)
+ return;
+
+ if (eval.constructExit)
+ eval.constructExit->block = savedExitBlock;
+
+ builder->setInsertionPointAfter(wrapOp);
+ }
+
/// Generate FIR for a DO construct. There are six variants:
/// - unstructured infinite and while loops
/// - structured and unstructured increment loops
@@ -2545,6 +2589,19 @@ class FirConverter : public Fortran::lower::AbstractConverter {
Fortran::lower::pft::Evaluation &eval = getEval();
bool unstructuredContext = eval.lowerAsUnstructured();
+ mlir::Block *savedExitBlock = nullptr;
+ mlir::scf::ExecuteRegionOp wrapOp =
+ unstructuredContext ? wrapUnstructuredConstruct(eval, savedExitBlock)
+ : nullptr;
+
+ if (wrapOp) {
+ LLVM_DEBUG({
+ llvm::dbgs() << "[wrap-unstructured] wrapped DO at ";
+ wrapOp.getLoc().print(llvm::dbgs());
+ llvm::dbgs() << "\n";
+ });
+ }
+
// If this do-loop was absorbed by a collapse clause on a parent acc.loop,
// skip generating any loop — just lower the body. The IV value is
// already available from the parent acc.loop's block argument.
@@ -2717,6 +2774,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
for (IncrementLoopInfo &info : incrementLoopNestInfo)
if (auto loopOp = mlir::dyn_cast_if_present<fir::DoLoopOp>(info.loopOp))
attachAttributesToDoLoopOperations(loopOp, doStmtEval.dirs);
+
+ closeUnstructuredWrap(wrapOp, eval, savedExitBlock);
}
/// Generate FIR to evaluate loop control values (lower, upper and step).
@@ -3195,7 +3254,18 @@ class FirConverter : public Fortran::lower::AbstractConverter {
return;
}
- // Unstructured branch sequence.
+ mlir::Block *savedExitBlock = nullptr;
+ mlir::scf::ExecuteRegionOp wrapOp =
+ wrapUnstructuredConstruct(eval, savedExitBlock);
+
+ if (wrapOp) {
+ LLVM_DEBUG({
+ llvm::dbgs() << "[wrap-unstructured] wrapped IF at ";
+ wrapOp.getLoc().print(llvm::dbgs());
+ llvm::dbgs() << "\n";
+ });
+ }
+
llvm::SmallVector<Fortran::lower::pft::Evaluation *> exits, fallThroughs;
collectFinalEvaluations(eval, exits, fallThroughs);
@@ -3227,6 +3297,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
}
}
}
+
+ closeUnstructuredWrap(wrapOp, eval, savedExitBlock);
}
void genCaseOrRankConstruct() {
@@ -6414,7 +6486,16 @@ class FirConverter : public Fortran::lower::AbstractConverter {
if (eval.isNewBlock)
eval.block = builder->createBlock(region);
if (eval.isConstruct() || eval.isDirective()) {
- if (eval.lowerAsUnstructured()) {
+ if (Fortran::lower::pft::isWrappableConstruct(eval)) {
+ // The wrap owns internal blocks; only create the entry block here
+ // so the enclosing CFG can branch to it.
+ if (eval.hasNestedEvaluations()) {
+ Fortran::lower::pft::Evaluation &constructStmt =
+ eval.getFirstNestedEvaluation();
+ if (constructStmt.isNewBlock)
+ constructStmt.block = builder->createBlock(region);
+ }
+ } else if (eval.lowerAsUnstructured()) {
createEmptyBlocks(eval.getNestedEvaluations());
} else if (eval.hasNestedEvaluations()) {
// A structured construct that is a target starts a new block.
@@ -6596,8 +6677,21 @@ class FirConverter : public Fortran::lower::AbstractConverter {
entryIndex < last; ++entryIndex) {
funit.setActiveEntry(entryIndex);
startNewFunction(funit); // the entry point for lowering this procedure
+ wrapUnstructuredCount = 0;
+
for (Fortran::lower::pft::Evaluation &eval : funit.evaluationList)
genFIR(eval);
+
+ if (wrapUnstructuredCount > 0) {
+ LLVM_DEBUG({
+ llvm::dbgs()
+ << "[wrap-unstructured] summary: " << wrapUnstructuredCount
+ << " execute_region(s) wrapping unstructured constructs at ";
+ toLocation().print(llvm::dbgs());
+ llvm::dbgs() << "\n";
+ });
+ }
+
endNewFunction(funit);
}
funit.setActiveEntry(0);
@@ -7004,6 +7098,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
Fortran::parser::CharBlock currentPosition;
TypeInfoConverter typeInfoConverter;
+ /// Counter of `scf.execute_region` ops created when
+ /// `--wrap-unstructured-constructs-in-execute-region` is enabled for the
+ /// currently-lowered function.
+ unsigned wrapUnstructuredCount = 0;
+
// Stack to manage object deallocation and finalization at construct exits.
llvm::SmallVector<ConstructContext> activeConstructStack;
diff --git a/flang/lib/Lower/PFTBuilder.cpp b/flang/lib/Lower/PFTBuilder.cpp
index 0af0ff9e7d758..fae120e742b23 100644
--- a/flang/lib/Lower/PFTBuilder.cpp
+++ b/flang/lib/Lower/PFTBuilder.cpp
@@ -17,6 +17,7 @@
#include "llvm/ADT/IntervalMap.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
+#include <limits>
#define DEBUG_TYPE "flang-pft"
@@ -24,6 +25,11 @@ static llvm::cl::opt<bool> clDisableStructuredFir(
"no-structured-fir", llvm::cl::desc("disable generation of structured FIR"),
llvm::cl::init(false), llvm::cl::Hidden);
+llvm::cl::opt<bool> wrapUnstructuredConstructsInExecuteRegion(
+ "wrap-unstructured-constructs-in-execute-region", llvm::cl::init(true),
+ llvm::cl::desc("try to wrap unstructured constructs' CFGs in "
+ "self-contained MLIR regions"));
+
using namespace Fortran;
namespace {
@@ -1213,8 +1219,11 @@ class PFTBuilder {
if (eval.evaluationList)
analyzeBranches(&eval, *eval.evaluationList);
- // Propagate isUnstructured flag to enclosing construct.
- if (parentConstruct && eval.isUnstructured)
+ // Propagate isUnstructured flag to enclosing construct -- unless the
+ // wrap pass will fold this construct into a self-contained
+ // scf.execute_region, in which case the parent sees only a single op.
+ if (parentConstruct && eval.isUnstructured &&
+ !lower::pft::isWrappableConstruct(eval))
parentConstruct->isUnstructured = true;
// The successor of a branch starts a new block.
@@ -2334,3 +2343,226 @@ void Fortran::lower::pft::visitAllSymbols(
parser::Walk(functionParserNode, visitor);
});
}
+
+/// True if \p target lies within \p construct. In `strict` mode, a null
+/// target and the construct's own exit block count as *outside*; in
+/// non-strict mode they count as *inside* (a natural fall-through that
+/// doesn't leave the construct).
+static bool isInsideConstruct(const Fortran::lower::pft::Evaluation *target,
+ const Fortran::lower::pft::Evaluation &construct,
+ bool strict) {
+ if (!target)
+ return !strict;
+
+ if (target == construct.constructExit)
+ return !strict;
+
+ for (const Fortran::lower::pft::Evaluation *p = target; p;
+ p = p->parentConstruct)
+ if (p == &construct)
+ return true;
+
+ return false;
+}
+
+bool Fortran::lower::pft::branchesAreInternal(
+ const Fortran::lower::pft::Evaluation &construct) {
+ std::function<bool(const Fortran::lower::pft::EvaluationList &)> walk =
+ [&](const Fortran::lower::pft::EvaluationList &list) -> bool {
+ for (const Fortran::lower::pft::Evaluation &nested : list) {
+ if (nested.controlSuccessor &&
+ !isInsideConstruct(nested.controlSuccessor, construct,
+ /*strict=*/false))
+ return false;
+
+ if (nested.evaluationList && !walk(*nested.evaluationList))
+ return false;
+ }
+
+ return true;
+ };
+
+ if (!construct.evaluationList)
+ return false;
+
+ return walk(*construct.evaluationList);
+}
+
+/// True if any eval outside \p construct branches strictly inside it.
+/// Such branches would cross the scf.execute_region boundary at lowering.
+static bool
+hasIncomingBranch(const Fortran::lower::pft::Evaluation &construct) {
+ const Fortran::lower::pft::FunctionLikeUnit *funit =
+ construct.getOwningProcedure();
+ if (!funit)
+ return false;
+
+ std::function<bool(const Fortran::lower::pft::EvaluationList &)> walk =
+ [&](const Fortran::lower::pft::EvaluationList &list) -> bool {
+ for (const Fortran::lower::pft::Evaluation &e : list) {
+ if (!isInsideConstruct(&e, construct, /*strict=*/true) &&
+ e.controlSuccessor &&
+ isInsideConstruct(e.controlSuccessor, construct, /*strict=*/true))
+ return true;
+
+ if (e.evaluationList && walk(*e.evaluationList))
+ return true;
+ }
+
+ return false;
+ };
+
+ // TODO: This is potentially expensive and we might need to prevent running
+ // this walk more than once somehow.
+ return walk(funit->evaluationList);
+}
+
+/// True for a `do; end do` (no bounds, no while-condition), or for any
+/// construct that contains one. Such bodies can't reach the wrap's yield
+/// block, and the wrap has only reads/branches inside — RegionDCE then drops
+/// the whole scf.execute_region. Excluding the entire enclosing construct
+/// keeps the infinite loop visible in the parent CFG.
+static bool
+containsInfiniteDoConstruct(const Fortran::lower::pft::Evaluation &eval) {
+ auto isInfinite = [](const parser::DoConstruct *d) {
+ return d && !d->GetLoopControl().has_value();
+ };
+
+ if (isInfinite(eval.getIf<parser::DoConstruct>()))
+ return true;
+
+ if (!eval.evaluationList)
+ return false;
+
+ std::function<bool(const Fortran::lower::pft::EvaluationList &)> walk =
+ [&](const Fortran::lower::pft::EvaluationList &list) -> bool {
+ for (const Fortran::lower::pft::Evaluation &e : list) {
+ if (isInfinite(e.getIf<parser::DoConstruct>()))
+ return true;
+
+ if (e.evaluationList && walk(*e.evaluationList))
+ return true;
+ }
+
+ return false;
+ };
+
+ return walk(*eval.evaluationList);
+}
+
+/// True if \p eval is a DoConstruct driven directly into an enclosing acc.loop
+/// by the OpenACCLoopConstruct / OpenACCCombinedConstruct lowering — the
+/// immediate body DO, or one of the N collapsed iterator DOs reached by
+/// walking down from the body DO under a `collapse(N)` clause.
+static bool isAccLoopBody(const Fortran::lower::pft::Evaluation &eval) {
+ const auto *doConstruct = eval.getIf<parser::DoConstruct>();
+ if (!doConstruct)
+ return false;
+ // N from `collapse(N)`, or 1 if no clause. eval at depth d from the body
+ // (d == 0 means eval IS the body) is a collapsed iterator iff d < N. If the
+ // Collapse value isn't a compile-time constant, be conservative and treat
+ // every DO in the chain as collapsed (INT64_MAX) — wrapping is opt-in and
+ // a false "is collapsed" is safer than a false "is not".
+ auto collapseN = [](const parser::AccClauseList &cl) -> int64_t {
+ for (const parser::AccClause &c : cl.v)
+ if (const auto *cc = std::get_if<parser::AccClause::Collapse>(&c.u)) {
+ const auto &intExpr = std::get<parser::ScalarIntConstantExpr>(cc->v.t);
+ if (const auto *expr = semantics::GetExpr(intExpr))
+ if (auto v = evaluate::ToInt64(*expr))
+ return *v;
+ return std::numeric_limits<int64_t>::max();
+ }
+ return 1;
+ };
+
+ // candidates[k] is the DoConstruct at depth k above (and including) eval.
+ llvm::SmallVector<const parser::DoConstruct *, 4> candidates{doConstruct};
+
+ for (const Fortran::lower::pft::Evaluation *p = eval.parentConstruct; p;
+ p = p->parentConstruct) {
+ if (const auto *d = p->getIf<parser::DoConstruct>()) {
+ candidates.push_back(d);
+ continue;
+ }
+
+ if (const auto *acc = p->getIf<parser::OpenACCConstruct>()) {
+ const parser::DoConstruct *body = nullptr;
+ int64_t n = 1;
+ if (const auto *loop =
+ std::get_if<parser::OpenACCLoopConstruct>(&acc->u)) {
+ if (const auto &b =
+ std::get<std::optional<parser::DoConstruct>>(loop->t))
+ body = &b.value();
+ n = collapseN(std::get<parser::AccClauseList>(std::get<0>(loop->t).t));
+ } else if (const auto *comb =
+ std::get_if<parser::OpenACCCombinedConstruct>(&acc->u)) {
+ if (const auto &b =
+ std::get<std::optional<parser::DoConstruct>>(comb->t))
+ body = &b.value();
+ n = collapseN(std::get<parser::AccClauseList>(std::get<0>(comb->t).t));
+ }
+
+ if (body) {
+ // body is at index `candidates.size()-1` (the outermost candidate);
+ // eval at depth (candidates.size()-1) from body. Collapsed iff < N.
+ auto it = llvm::find(candidates, body);
+ if (it != candidates.end()) {
+ int64_t depth = std::distance(candidates.begin(), it);
+ if (depth < n)
+ return true;
+ }
+ }
+ }
+
+ break;
+ }
+
+ return false;
+}
+
+/// True if any nested eval is a ReturnStmt. Its lowering creates the
+/// function's final block in the current region, which would mis-parent
+/// the func.return if that region is the wrap's.
+static bool
+containsReturnStmt(const Fortran::lower::pft::Evaluation &construct) {
+ if (!construct.evaluationList)
+ return false;
+
+ std::function<bool(const Fortran::lower::pft::EvaluationList &)> walk =
+ [&](const Fortran::lower::pft::EvaluationList &list) -> bool {
+ for (const Fortran::lower::pft::Evaluation &e : list) {
+ if (e.isA<parser::ReturnStmt>())
+ return true;
+
+ if (e.evaluationList && walk(*e.evaluationList))
+ return true;
+ }
+
+ return false;
+ };
+
+ return walk(*construct.evaluationList);
+}
+
+bool Fortran::lower::pft::isWrappableConstruct(
+ const Fortran::lower::pft::Evaluation &eval) {
+ if (!wrapUnstructuredConstructsInExecuteRegion)
+ return false;
+
+ if (!eval.isUnstructured)
+ return false;
+
+ if (!(eval.isA<Fortran::parser::DoConstruct>() ||
+ eval.isA<Fortran::parser::IfConstruct>()))
+ return false;
+
+ // Wrapping requires self-contained CFG.
+ //
+ // Note: Loops attached to OpenACC constructs are not wrappable since
+ // genOpenACCLoopFromDoConstruct takes over code-gen when a DoConstruct is
+ // attached to an OpenACC directive. We might extend wrapping to such
+ // unstructured loops later on if needed.
+ return Fortran::lower::pft::branchesAreInternal(eval) &&
+ !hasIncomingBranch(eval) && !containsReturnStmt(eval) &&
+ !containsInfiniteDoConstruct(eval) && !isAccLoopBody(eval);
+}
diff --git a/flang/lib/Lower/Runtime.cpp b/flang/lib/Lower/Runtime.cpp
index ca1a1aca89606..ebbb9e3f74f03 100644
--- a/flang/lib/Lower/Runtime.cpp
+++ b/flang/lib/Lower/Runtime.cpp
@@ -19,6 +19,7 @@
#include "flang/Runtime/stop.h"
#include "flang/Semantics/tools.h"
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
+#include "mlir/Dialect/SCF/IR/SCF.h"
#include "llvm/Support/Debug.h"
#include <optional>
@@ -31,8 +32,11 @@ using namespace Fortran::runtime;
static void genUnreachable(fir::FirOpBuilder &builder, mlir::Location loc) {
mlir::Block *curBlock = builder.getBlock();
mlir::Operation *parentOp = curBlock->getParentOp();
- if (parentOp->getDialect()->getNamespace() ==
- mlir::omp::OpenMPDialect::getDialectNamespace())
+
+ if (mlir::isa<mlir::scf::ExecuteRegionOp>(parentOp))
+ fir::UnreachableOp::create(builder, loc);
+ else if (parentOp->getDialect()->getNamespace() ==
+ mlir::omp::OpenMPDialect::getDialectNamespace())
Fortran::lower::genOpenMPTerminator(builder, parentOp, loc);
else if (Fortran::lower::isInsideOpenACCComputeConstruct(builder))
Fortran::lower::genOpenACCTerminator(builder, parentOp, loc);
diff --git a/flang/test/HLFIR/assumed_shape_with_value_keyword.f90 b/flang/test/HLFIR/assumed_shape_with_value_keyword.f90
index 89f83863b560c..9e6cd57da785a 100644
--- a/flang/test/HLFIR/assumed_shape_with_value_keyword.f90
+++ b/flang/test/HLFIR/assumed_shape_with_value_keyword.f90
@@ -134,14 +134,17 @@ subroutine test_optional3(x)
! CHECK-LABEL: func.func @_QPtest_optional3(
! CHECK-SAME: %[[ARG0:.*]]: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "x", fir.optional}) {
! CHECK: %[[VAL_0:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<optional, value>, uniq_name = "_QFtest_optional3Ex"} : (!fir.box<!fir.array<?xf32>>, !fir.dscope) -> (!fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>)
-! CHECK: %[[VAL_1:.*]] = fir.is_present %[[VAL_0]]#1 : (!fir.box<!fir.array<?xf32>>) -> i1
-! CHECK: cf.cond_br %[[VAL_1]], ^bb1, ^bb2
+! CHECK: scf.execute_region no_inline {
+! CHECK: %[[VAL_1:.*]] = fir.is_present %[[VAL_0]]#1 : (!fir.box<!fir.array<?xf32>>) -> i1
+! CHECK: cf.cond_br %[[VAL_1]], ^bb1, ^bb2
! CHECK: b1: // pred: ^bb0
-! CHECK: %[[C0_I32:.*]] = arith.constant 0 : i32
-! CHECK: %[[FALSE:.*]] = arith.constant false
-! CHECK: %[[FALSE_0:.*]] = arith.constant false
-! CHECK: fir.call @_FortranAStopStatement(%[[C0_I32]], %[[FALSE]], %[[FALSE]]_0) fastmath<contract> : (i32, i1, i1) -> ()
-! CHECK: fir.unreachable
+! CHECK: %[[C0_I32:.*]] = arith.constant 0 : i32
+! CHECK: %[[FALSE:.*]] = arith.constant false
+! CHECK: %[[FALSE_0:.*]] = arith.constant false
+! CHECK: fir.call @_FortranAStopStatement(%[[C0_I32]], %[[FALSE]], %[[FALSE]]_0) fastmath<contract> : (i32, i1, i1) -> ()
+! CHECK: fir.unreachable
! CHECK: b2: // pred: ^bb0
+! CHECK: scf.yield
+! CHECK: }
! CHECK: return
! CHECK: }
diff --git a/flang/test/HLFIR/optional_dummy.f90 b/flang/test/HLFIR/optional_dummy.f90
index 86ddeb9ba135a..04751cae9f3cf 100644
--- a/flang/test/HLFIR/optional_dummy.f90
+++ b/flang/test/HLFIR/optional_dummy.f90
@@ -6,17 +6,18 @@
! CHECK-LABEL: func.func @_QPtest(
! CHECK-SAME: %[[VAL_0:.*]]: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "ext_buf", fir.contiguous, fir.optional}) {
! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<contiguous, optional>, uniq_name = "_QFtestEext_buf"} : (!fir.box<!fir.array<?xi32>>, !fir.dscope) -> (!fir.box<!fir.array<?xi32>>, !fir.box<!fir.array<?xi32>>)
-! CHECK: %[[VAL_2:.*]] = fir.is_present %[[VAL_1]]#1 : (!fir.box<!fir.array<?xi32>>) -> i1
-! CHECK: cf.cond_br %[[VAL_2]], ^bb1, ^bb2
-! CHECK: ^bb1:
-! CHECK: %[[VAL_3:.*]] = arith.constant 0 : i32
-! CHECK: %[[VAL_4:.*]] = arith.constant false
-! CHECK: %[[VAL_5:.*]] = arith.constant false
-! CHECK: fir.call @_FortranAStopStatement(%[[VAL_3]], %[[VAL_4]], %[[VAL_5]]) fastmath<contract> : (i32, i1, i1) -> ()
-! CHECK: fir.unreachable
-! CHECK: ^bb2:
-! CHECK: cf.br ^bb3
-! CHECK: ^bb3:
+! CHECK: scf.execute_region no_inline {
+! CHECK: %[[VAL_2:.*]] = fir.is_present %[[VAL_1]]#1 : (!fir.box<!fir.array<?xi32>>) -> i1
+! CHECK: cf.cond_br %[[VAL_2]], ^bb1, ^bb2
+! CHECK: ^bb1:
+! CHECK: %[[VAL_3:.*]] = arith.constant 0 : i32
+! CHECK: %[[VAL_4:.*]] = arith.constant false
+! CHECK: %[[VAL_5:.*]] = arith.constant false
+! CHECK: fir.call @_FortranAStopStatement(%[[VAL_3]], %[[VAL_4]], %[[VAL_5]]) fastmath<contract> : (i32, i1, i1) -> ()
+! CHECK: fir.unreachable
+! CHECK: ^bb2:
+! CHECK: scf.yield
+! CHECK: }
! CHECK: return
! CHECK: }
subroutine test(ext_buf)
diff --git a/flang/test/Integration/OpenMP/parallel-private-reduction-worstcase.f90 b/flang/test/Integration/OpenMP/parallel-private-reduction-worstcase.f90
index 94080d1d4f975..f9ea1a962d3c9 100644
--- a/flang/test/Integration/OpenMP/parallel-private-reduction-worstcase.f90
+++ b/flang/test/Integration/OpenMP/parallel-private-reduction-worstcase.f90
@@ -148,7 +148,7 @@ subroutine worst_case(a, b, c, d)
! CHECK: omp.par.region31: ; preds = %omp.par.region29
! CHECK-NEXT: br label %omp.region.cont28
-! CHECK: omp.region.cont28: ; preds = %omp.par.region30, %omp.par.region31
+! CHECK: omp.region.cont28: ; preds = %omp.par.region31
! [omp parallel region done, call into the runtime to complete reduction]
! CHECK: %[[VAL_233:.*]] = call i32 @__kmpc_reduce(
! CHECK: switch i32 %[[VAL_233]], label %reduce.finalize [
@@ -197,6 +197,7 @@ subroutine worst_case(a, b, c, d)
! CHECK: omp.par.region30: ; preds = %omp.par.region29
! CHECK-NEXT: call void @_FortranAStopStatement
+! CHECK-NEXT: unreachable
! CHECK: omp.reduction.neutral25: ; preds = %omp.reduction.neutral24
! [source length was zero: finish initializing array]
diff --git a/flang/test/Lower/HLFIR/intrinsic-subroutines.f90 b/flang/test/Lower/HLFIR/intrinsic-subroutines.f90
index cbc56ca1e395b..cbd0f65ff0e20 100644
--- a/flang/test/Lower/HLFIR/intrinsic-subroutines.f90
+++ b/flang/test/Lower/HLFIR/intrinsic-subroutines.f90
@@ -64,25 +64,28 @@ program main
! CHECK: %[[VAL_34:.*]] = arith.select %[[VAL_33]], %[[VAL_20]], %[[VAL_32]] : i32
! CHECK: fir.store %[[VAL_34]] to %[[VAL_19]] : !fir.ref<i32>
! CHECK: }
-! CHECK: %[[VAL_35:.*]] = arith.constant 5 : i32
-! CHECK: %[[VAL_36:.*]] = hlfir.elemental %[[VAL_8]] unordered : (!fir.shape<1>) -> !hlfir.expr<3x!fir.logical<4>> {
-! CHECK: ^bb0(%[[VAL_37:.*]]: index):
-! CHECK: %[[VAL_38:.*]] = hlfir.designate %[[VAL_9]]#0 (%[[VAL_37]]) : (!fir.ref<!fir.array<3xi32>>, index) -> !fir.ref<i32>
-! CHECK: %[[VAL_39:.*]] = fir.load %[[VAL_38]] : !fir.ref<i32>
-! CHECK: %[[VAL_40:.*]] = arith.cmpi ne, %[[VAL_39]], %[[VAL_35]] : i32
-! CHECK: %[[VAL_41:.*]] = fir.convert %[[VAL_40]] : (i1) -> !fir.logical<4>
-! CHECK: hlfir.yield_element %[[VAL_41]] : !fir.logical<4>
+! CHECK: scf.execute_region no_inline {
+! CHECK: %[[VAL_35:.*]] = arith.constant 5 : i32
+! CHECK: %[[VAL_36:.*]] = hlfir.elemental %[[VAL_8]] unordered : (!fir.shape<1>) -> !hlfir.expr<3x!fir.logical<4>> {
+! CHECK: ^bb0(%[[VAL_37:.*]]: index):
+! CHECK: %[[VAL_38:.*]] = hlfir.designate %[[VAL_9]]#0 (%[[VAL_37]]) : (!fir.ref<!fir.array<3xi32>>, index) -> !fir.ref<i32>
+! CHECK: %[[VAL_39:.*]] = fir.load %[[VAL_38]] : !fir.ref<i32>
+! CHECK: %[[VAL_40:.*]] = arith.cmpi ne, %[[VAL_39]], %[[VAL_35]] : i32
+! CHECK: %[[VAL_41:.*]] = fir.convert %[[VAL_40]] : (i1) -> !fir.logical<4>
+! CHECK: hlfir.yield_element %[[VAL_41]] : !fir.logical<4>
+! CHECK: }
+! CHECK: %[[VAL_42:.*]] = hlfir.any %[[VAL_36]] : (!hlfir.expr<3x!fir.logical<4>>) -> !fir.logical<4>
+! CHECK: hlfir.destroy %[[VAL_36]] : !hlfir.expr<3x!fir.logical<4>>
+! CHECK: %[[VAL_44:.*]] = fir.convert %[[VAL_42]] : (!fir.logical<4>) -> i1
+! CHECK: cf.cond_br %[[VAL_44]], ^bb1, ^bb2
+! CHECK: ^bb1:
+! CHECK: %[[VAL_45:.*]] = arith.constant 1 : i32
+! CHECK: %[[VAL_46:.*]] = arith.constant false
+! CHECK: %[[VAL_47:.*]] = arith.constant false
+! CHECK: fir.call @_FortranAStopStatement(%[[VAL_45]], %[[VAL_46]], %[[VAL_47]]) fastmath<contract> : (i32, i1, i1) -> ()
+! CHECK: fir.unreachable
+! CHECK: ^bb2:
+! CHECK: scf.yield
! CHECK: }
-! CHECK: %[[VAL_42:.*]] = hlfir.any %[[VAL_43:.*]] : (!hlfir.expr<3x!fir.logical<4>>) -> !fir.logical<4>
-! CHECK: hlfir.destroy %[[VAL_43]] : !hlfir.expr<3x!fir.logical<4>>
-! CHECK: %[[VAL_44:.*]] = fir.convert %[[VAL_42]] : (!fir.logical<4>) -> i1
-! CHECK: cf.cond_br %[[VAL_44]], ^bb1, ^bb2
-! CHECK: ^bb1:
-! CHECK: %[[VAL_45:.*]] = arith.constant 1 : i32
-! CHECK: %[[VAL_46:.*]] = arith.constant false
-! CHECK: %[[VAL_47:.*]] = arith.constant false
-! CHECK: fir.call @_FortranAStopStatement(%[[VAL_45]], %[[VAL_46]], %[[VAL_47]]) fastmath<contract> : (i32, i1, i1) -> ()
-! CHECK: fir.unreachable
-! CHECK: ^bb2:
! CHECK: return
! CHECK: }
diff --git a/flang/test/Lower/MIF/change_team2.f90 b/flang/test/Lower/MIF/change_team2.f90
index 58131cd98c455..42d1f50cc10aa 100644
--- a/flang/test/Lower/MIF/change_team2.f90
+++ b/flang/test/Lower/MIF/change_team2.f90
@@ -13,18 +13,21 @@
if (team_number() /= new_team) STOP 1
end team
! COARRAY: mif.change_team %[[TEAM:.*]] : ({{.*}}) {
- ! COARRAY: %[[VAL_1:.*]] = mif.team_number : () -> i64
- ! COARRAY: %[[VAL_2:.*]] = fir.convert %[[VAL_1]] : (i64) -> i32
- ! COARRAY: %[[VAL_3:.*]] = fir.load %[[VAR_1:.*]]#0 : !fir.ref<i32>
- ! COARRAY: %[[VAL_4:.*]] = arith.cmpi ne, %[[VAL_2]], %[[VAL_3]] : i32
- ! COARRAY: cf.cond_br %[[VAL_4]], ^bb1, ^bb2
- ! COARRAY: ^bb1: // pred: ^bb0
- ! COARRAY: %[[C1_I32:.*]] = arith.constant 1 : i32
- ! COARRAY: %[[FALSE_1:.*]] = arith.constant false
- ! COARRAY: %[[FALSE_2:.*]] = arith.constant false
- ! COARRAY: fir.call @_FortranAStopStatement(%[[C1_I32]], %[[FALSE_1]], %[[FALSE_2]]) fastmath<contract> : (i32, i1, i1) -> ()
- ! COARRAY: fir.unreachable
- ! COARRAY: ^bb2: // pred: ^bb0
+ ! COARRAY: scf.execute_region no_inline {
+ ! COARRAY: %[[VAL_1:.*]] = mif.team_number : () -> i64
+ ! COARRAY: %[[VAL_2:.*]] = fir.convert %[[VAL_1]] : (i64) -> i32
+ ! COARRAY: %[[VAL_3:.*]] = fir.load %[[VAR_1:.*]]#0 : !fir.ref<i32>
+ ! COARRAY: %[[VAL_4:.*]] = arith.cmpi ne, %[[VAL_2]], %[[VAL_3]] : i32
+ ! COARRAY: cf.cond_br %[[VAL_4]], ^bb1, ^bb2
+ ! COARRAY: ^bb1: // pred: ^bb0
+ ! COARRAY: %[[C1_I32:.*]] = arith.constant 1 : i32
+ ! COARRAY: %[[FALSE_1:.*]] = arith.constant false
+ ! COARRAY: %[[FALSE_2:.*]] = arith.constant false
+ ! COARRAY: fir.call @_FortranAStopStatement(%[[C1_I32]], %[[FALSE_1]], %[[FALSE_2]]) fastmath<contract> : (i32, i1, i1) -> ()
+ ! COARRAY: fir.unreachable
+ ! COARRAY: ^bb2: // pred: ^bb0
+ ! COARRAY: scf.yield
+ ! COARRAY: }
! COARRAY: mif.end_team : () -> ()
! COARRAY: }
diff --git a/flang/test/Lower/OpenACC/Todo/acc-unstructured-combined-construct.f90 b/flang/test/Lower/OpenACC/Todo/acc-unstructured-combined-construct.f90
index 74ba49f0cf5f3..110940d9e5069 100644
--- a/flang/test/Lower/OpenACC/Todo/acc-unstructured-combined-construct.f90
+++ b/flang/test/Lower/OpenACC/Todo/acc-unstructured-combined-construct.f90
@@ -2,46 +2,9 @@
! combined `acc parallel loop` construct (default parallelism is
! `independent`).
-! RUN: split-file %s %t
+! 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
-! By default (--emit-independent-loops-as-unstructured=true), the loops are
-! lowered to combined `acc.parallel` + `acc.loop` operations.
-! RUN: bbc -fopenacc -emit-hlfir %t/stop_collapse1.f90 -o - | FileCheck %s --check-prefix=STOP1-OK
-! RUN: bbc -fopenacc -emit-hlfir %t/cycle_collapse2.f90 -o - | FileCheck %s --check-prefix=CYCLE2-OK
-! RUN: bbc -fopenacc -emit-hlfir %t/stop_collapse3.f90 -o - | FileCheck %s --check-prefix=STOP3-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/stop_collapse1.f90 -o - 2>&1 | FileCheck %s --check-prefix=STOP1
-! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir --emit-independent-loops-as-unstructured=false %t/cycle_collapse2.f90 -o - 2>&1 | FileCheck %s --check-prefix=CYCLE2
-! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir --emit-independent-loops-as-unstructured=false %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
-
-! STOP1-OK-LABEL: func.func @_QPtest_unstructured2
-! STOP1-OK: acc.parallel combined(loop)
-! STOP1-OK: acc.loop combined(parallel)
-
-!--- 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(:,:)
@@ -64,26 +27,3 @@ subroutine test_unstructured_collapse_cycle(a)
! CYCLE2-OK-LABEL: func.func @_QPtest_unstructured_collapse_cycle
! CYCLE2-OK: acc.parallel combined(loop)
! CYCLE2-OK: acc.loop combined(parallel)
-
-!--- 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
-
-! STOP3-OK-LABEL: func.func @_QPtest_unstructured_collapse_stop
-! STOP3-OK: acc.parallel combined(loop)
-! STOP3-OK: acc.loop combined(parallel)
diff --git a/flang/test/Lower/OpenACC/Todo/do-loops-to-acc-loops-todo.f90 b/flang/test/Lower/OpenACC/Todo/do-loops-to-acc-loops-todo.f90
index 9cb62f0dc1a9a..d59091a975822 100644
--- a/flang/test/Lower/OpenACC/Todo/do-loops-to-acc-loops-todo.f90
+++ b/flang/test/Lower/OpenACC/Todo/do-loops-to-acc-loops-todo.f90
@@ -1,5 +1,4 @@
! RUN: split-file %s %t
-! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/do_loop_with_stop.f90 -o - 2>&1 | FileCheck %s --check-prefix=CHECK1
! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/do_loop_with_cycle_goto.f90 -o - 2>&1 | FileCheck %s --check-prefix=CHECK2
! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/nested_goto_loop.f90 -o - 2>&1 | FileCheck %s --check-prefix=CHECK3
! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/nested_loop_with_inner_goto.f90 -o - 2>&1 | FileCheck %s --check-prefix=CHECK4
@@ -7,24 +6,6 @@
! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/collapse_gt.f90 -o - 2>&1 | FileCheck %s --check-prefix=CHECK8
! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/collapse_nested.f90 -o - 2>&1 | FileCheck %s --check-prefix=CHECK6
-//--- do_loop_with_stop.f90
-
-subroutine do_loop_with_stop()
- integer :: i
- integer, parameter :: n = 10
- real, dimension(n) :: a, b
-
- !$acc kernels
- do i = 1, n
- a(i) = b(i) + 1.0
- if (i == 5) stop
- end do
- !$acc end kernels
-
-! CHECK1: not yet implemented: unstructured do loop in acc kernels
-
-end subroutine
-
//--- do_loop_with_cycle_goto.f90
subroutine do_loop_with_cycle_goto()
diff --git a/flang/test/Lower/OpenACC/acc-terminator.f90 b/flang/test/Lower/OpenACC/acc-terminator.f90
index 65df424da5642..fae147cbcc04d 100644
--- a/flang/test/Lower/OpenACC/acc-terminator.f90
+++ b/flang/test/Lower/OpenACC/acc-terminator.f90
@@ -48,7 +48,8 @@ program main
! CHECK: acc.host_data
! CHECK: acc.terminator
! CHECK: fir.call @_FortranAStopStatement
-! CHECK: fir.unreachable
+! CHECK-NEXT: fir.unreachable
+! CHECK: scf.yield
! CHECK: acc.parallel
! CHECK-COUNT-3: acc.yield
! CHECK: acc.terminator
diff --git a/flang/test/Lower/OpenACC/acc-unstructured.f90 b/flang/test/Lower/OpenACC/acc-unstructured.f90
index 115626da9d160..0bb3ec2691b20 100644
--- a/flang/test/Lower/OpenACC/acc-unstructured.f90
+++ b/flang/test/Lower/OpenACC/acc-unstructured.f90
@@ -39,6 +39,11 @@ subroutine test_unstructured1(a, b, c)
! CHECK: fir.call @_FortranAStopStatementText
+! Body looks unstructured (if/stop) but the wrap-in-execute-region pass hides
+! the unstructured CFG inside scf.execute_region, so the DOs lower as
+! structured acc.loop control(...) = ... (no `unstructured` attribute). GOTO
+! exiting a combined OpenACC region is not yet implemented in lowering, so
+! there's no genuinely-unstructured counterpart for this combined form.
subroutine test_unstructured2(a, b, c)
integer :: i, j, k
real :: a(:,:,:), b(:,:,:), c(:,:,:)
@@ -53,13 +58,10 @@ subroutine test_unstructured2(a, b, c)
end do
! CHECK-LABEL: func.func @_QPtest_unstructured2
-! CHECK: acc.serial
-! CHECK: acc.loop combined(serial) private(%{{.*}} : !fir.ref<i32>) {
+! CHECK: acc.serial combined(loop) {
+! CHECK: acc.loop combined(serial) private({{.*}}) control({{.*}}) = ({{.*}}) to ({{.*}}) step ({{.*}}) {
+! CHECK: scf.execute_region
! CHECK: fir.call @_FortranAStopStatementText
-! CHECK: acc.yield
-! CHECK: acc.yield
-! CHECK: }
-! CHECK: acc.yield
end subroutine
@@ -85,9 +87,10 @@ subroutine test_unstructured3(a, b, c)
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.
+! Body looks unstructured at the source level (if/stop), but the PFT-to-MLIR
+! wrap-in-execute-region pass hides the if/stop CFG inside scf.execute_region,
+! so genACCDataOp sees a structured body and (with only an if-clause and no
+! data clauses) hits the early-return that skips acc.data.
subroutine test_unstructured4(a, n)
integer :: n, i, j
real :: a(:)
@@ -105,8 +108,30 @@ subroutine test_unstructured4(a, n)
end subroutine
! CHECK-LABEL: func.func @_QPtest_unstructured4
-! CHECK: acc.data if(%{{.*}}) {
+! CHECK-NOT: acc.data
+! CHECK: fir.do_loop
+! CHECK: scf.execute_region
! CHECK: fir.call @_FortranAStopStatementText
+
+! Body is genuinely unstructured: GOTO exits the data region, so the
+! enclosing if-construct has an external branch and is not wrappable.
+! eval.lowerAsUnstructured() remains true and acc.data must be emitted.
+subroutine test_unstructured4_goto(a, n)
+ integer :: n, i
+ real :: a(:)
+ logical :: use_gpu
+
+ use_gpu = .true.
+ !$acc data if(use_gpu)
+ do i = 1, n
+ if (a(i) > 0.0) goto 100
+ end do
+ !$acc end data
+100 continue
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_unstructured4_goto
+! CHECK: acc.data if(%{{.*}}) {
! CHECK: acc.terminator
! CHECK: }
@@ -265,8 +290,9 @@ subroutine test_unstructured_collapse_cycle(a)
! CHECK: acc.yield
! CHECK: }
-! 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).
+! `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
+! structured acc.loop control(...) (no `unstructured` attribute).
subroutine test_unstructured_collapse_stop(a)
integer :: i, j, k
real :: a(:,:,:)
@@ -285,9 +311,11 @@ 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(serial) private(%{{.*}}, %{{.*}}, %{{.*}} : !fir.ref<i32>, !fir.ref<i32>, !fir.ref<i32>) {
+! CHECK: acc.loop combined(serial) private({{.*}}) control({{.*}}) = ({{.*}}) to ({{.*}}) step ({{.*}}) {
+! CHECK: scf.execute_region
! CHECK: fir.call @_FortranAStopStatementText
-! CHECK: }
+! CHECK-NOT: unstructured
+! CHECK: } attributes {collapse = [3]{{.*}}}
! Test orphaned `acc loop collapse(N)`
subroutine test_unstructured_collapse_loop_only(a)
@@ -311,7 +339,9 @@ subroutine test_unstructured_collapse_loop_only(a)
! 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).
+! Standalone `acc loop seq` with STOP: wrap-in-execute-region hides the
+! if/stop and the DO lowers as structured acc.loop control(...) (no
+! `unstructured` attribute).
subroutine test_unstructured_loop_seq_stop(a)
integer :: i, j
real :: a(:,:,:)
@@ -324,11 +354,33 @@ subroutine test_unstructured_loop_seq_stop(a)
end subroutine
! CHECK-LABEL: func.func @_QPtest_unstructured_loop_seq_stop
-! CHECK: acc.loop private({{.*}})
+! CHECK: acc.loop private({{.*}}) control({{.*}}) = ({{.*}}) to ({{.*}}) step ({{.*}}) {
+! CHECK: scf.execute_region
! CHECK: fir.call @_FortranAStopStatementText
+! CHECK-NOT: unstructured
+! CHECK: } attributes {{{.*}}seq = [#acc.device_type<none>]{{.*}}}
+
+! Same loop but the if-construct has a GOTO exiting all loops, so the
+! if-construct is not wrappable, the DO remains unstructured, and acc.loop
+! emits the unstructured form with the `unstructured` attribute.
+subroutine test_unstructured_loop_seq_goto(a)
+ integer :: i, j
+ real :: a(:,:,:)
+ !$acc loop seq
+ do i = 1, 10
+ do j = 1, 10
+ if (a(1,2,3) > 10.0) goto 100
+ end do
+ end do
+100 continue
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_unstructured_loop_seq_goto
+! CHECK: acc.loop private({{.*}}) {
+! CHECK: cf.br
! CHECK: } attributes {{{.*}}seq = [#acc.device_type<none>], unstructured}
-! Standalone `acc loop auto` with STOP in body (explicit `auto` clause).
+! Standalone `acc loop auto` with STOP: same wrap-makes-structured behavior.
subroutine test_unstructured_loop_auto_stop(a)
integer :: i, j
real :: a(:,:,:)
@@ -341,12 +393,32 @@ subroutine test_unstructured_loop_auto_stop(a)
end subroutine
! CHECK-LABEL: func.func @_QPtest_unstructured_loop_auto_stop
-! CHECK: acc.loop private({{.*}})
+! CHECK: acc.loop private({{.*}}) control({{.*}}) = ({{.*}}) to ({{.*}}) step ({{.*}}) {
+! CHECK: scf.execute_region
! CHECK: fir.call @_FortranAStopStatementText
+! CHECK-NOT: unstructured
+! CHECK: } attributes {auto_ = [#acc.device_type<none>]{{.*}}}
+
+! Same loop with GOTO exit: genuinely unstructured, `unstructured` attribute
+! is emitted on acc.loop.
+subroutine test_unstructured_loop_auto_goto(a)
+ integer :: i, j
+ real :: a(:,:,:)
+ !$acc loop auto
+ do i = 1, 10
+ do j = 1, 10
+ if (a(1,2,3) > 10.0) goto 100
+ end do
+ end do
+100 continue
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_unstructured_loop_auto_goto
+! CHECK: acc.loop private({{.*}}) {
+! CHECK: cf.br
! 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).
+! Standalone `acc loop` inside `acc serial` with STOP: wrap-makes-structured.
subroutine test_unstructured_loop_in_serial_stop(a)
integer :: i, j
real :: a(:,:,:)
@@ -362,11 +434,11 @@ subroutine test_unstructured_loop_in_serial_stop(a)
! CHECK-LABEL: func.func @_QPtest_unstructured_loop_in_serial_stop
! CHECK: acc.serial
-! CHECK: acc.loop private({{.*}})
+! CHECK: acc.loop private({{.*}}) control({{.*}}) = ({{.*}}) to ({{.*}}) step ({{.*}}) {
+! CHECK: scf.execute_region
! 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.
+! Orphan `acc loop` inside a `seq` acc routine with STOP: wrap-makes-structured.
subroutine test_unstructured_orphan_loop_in_seq_routine(a)
integer :: i, j
real :: a(:,:,:)
@@ -380,7 +452,88 @@ subroutine test_unstructured_orphan_loop_in_seq_routine(a)
end subroutine
! CHECK-LABEL: func.func @_QPtest_unstructured_orphan_loop_in_seq_routine
-! CHECK: acc.loop private({{.*}})
+! CHECK: acc.loop private({{.*}}) control({{.*}}) = ({{.*}}) to ({{.*}}) step ({{.*}}) {
+! CHECK: scf.execute_region
! CHECK: fir.call @_FortranAStopStatementText
+
+! Same orphan loop with GOTO exit: genuinely unstructured.
+subroutine test_unstructured_orphan_loop_in_seq_routine_goto(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) goto 100
+ end do
+ end do
+100 continue
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_unstructured_orphan_loop_in_seq_routine_goto
+! CHECK: acc.loop private({{.*}}) {
+! CHECK: cf.br
! CHECK: } attributes {{{.*}}seq = [#acc.device_type<none>], unstructured}
+! DO loop with STOP inside `!$acc kernels`. Previously flagged as
+! "unstructured do loop in acc kernels" (TODO); wrap-in-execute-region now
+! hides the if/stop CFG inside scf.execute_region so the DO itself lowers as
+! a structured acc.loop with control bounds.
+subroutine test_unstructured_kernels_do_stop()
+ integer :: i
+ integer, parameter :: n = 10
+ real, dimension(n) :: a, b
+
+ !$acc kernels
+ do i = 1, n
+ a(i) = b(i) + 1.0
+ if (i == 5) stop
+ end do
+ !$acc end kernels
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_unstructured_kernels_do_stop
+! CHECK: acc.kernels
+! CHECK: acc.loop private({{.*}}) control({{.*}}) = ({{.*}}) to ({{.*}}) step ({{.*}}) {
+! CHECK: scf.execute_region
+! CHECK: fir.call @_FortranAStopStatement
+
+! `!$acc parallel loop` (combined construct) with STOP in the innermost
+! body. wrap-in-execute-region hides the if/stop CFG so the combined
+! construct still lowers as structured acc.parallel + acc.loop.
+subroutine test_unstructured_parallel_loop_stop(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
+
+! CHECK-LABEL: func.func @_QPtest_unstructured_parallel_loop_stop
+! CHECK: acc.parallel combined(loop)
+! CHECK: acc.loop combined(parallel)
+
+! `!$acc parallel loop collapse(3)` with STOP in the innermost body. Same
+! wrap behavior as above with an added collapse clause.
+subroutine test_unstructured_parallel_loop_collapse3_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
+
+! CHECK-LABEL: func.func @_QPtest_unstructured_parallel_loop_collapse3_stop
+! CHECK: acc.parallel combined(loop)
+! CHECK: acc.loop combined(parallel)
diff --git a/flang/test/Lower/OpenMP/loop-compound.f90 b/flang/test/Lower/OpenMP/loop-compound.f90
index 8c025ab237920..b55235b3a4718 100644
--- a/flang/test/Lower/OpenMP/loop-compound.f90
+++ b/flang/test/Lower/OpenMP/loop-compound.f90
@@ -242,19 +242,23 @@ program main
! CHECK-NEXT: omp.wsloop
! CHECK-NEXT: omp.loop_nest
!
- ! Verify the conrol-flow of the unstructured inner loop.
- ! CHECK: cf.br ^[[BB1:.*]]
- ! CHECK: ^[[BB1]]:
- ! CHECK: cf.br ^[[BB2:.*]]
- ! CHECK: ^[[BB2]]:
- ! CHECK: cf.cond_br %{{.*}}, ^[[BB3:.*]], ^[[BB6:.*]]
- ! CHECK: ^[[BB3]]:
- ! CHECK: cf.cond_br %{{.*}}, ^[[BB4:.*]], ^[[BB5:.*]]
- ! CHECK: ^[[BB4]]:
- ! CHECK: cf.br ^[[BB6]]
- ! CHECK: ^[[BB5]]:
- ! CHECK: cf.br ^[[BB2]]
- ! CHECK: ^[[BB6]]:
+ ! Verify the control-flow of the unstructured inner loop, wrapped in an
+ ! scf.execute_region.
+ ! CHECK: scf.execute_region no_inline {
+ ! CHECK: cf.br ^[[BB1:.*]]
+ ! CHECK: ^[[BB1]]:
+ ! CHECK: cf.br ^[[BB2:.*]]
+ ! CHECK: ^[[BB2]]:
+ ! CHECK: cf.cond_br %{{.*}}, ^[[BB3:.*]], ^[[BB6:.*]]
+ ! CHECK: ^[[BB3]]:
+ ! CHECK: cf.cond_br %{{.*}}, ^[[BB4:.*]], ^[[BB5:.*]]
+ ! CHECK: ^[[BB4]]:
+ ! CHECK: cf.br ^[[BB6]]
+ ! CHECK: ^[[BB5]]:
+ ! CHECK: cf.br ^[[BB2]]
+ ! CHECK: ^[[BB6]]:
+ ! CHECK-NEXT: scf.yield
+ ! CHECK: }
! CHECK-NEXT: omp.yield
!$omp target teams distribute parallel do
do i = 1, 10
diff --git a/flang/test/Lower/OpenMP/parallel-reduction3.f90 b/flang/test/Lower/OpenMP/parallel-reduction3.f90
index 7437e1d35a624..ea6bf6cdf3ac9 100644
--- a/flang/test/Lower/OpenMP/parallel-reduction3.f90
+++ b/flang/test/Lower/OpenMP/parallel-reduction3.f90
@@ -104,19 +104,22 @@
! CHECK: }
! CHECK: omp.terminator
! CHECK: }
-! CHECK: %[[VAL_39:.*]] = arith.constant 1 : index
-! CHECK: %[[VAL_40:.*]] = hlfir.designate %[[VAL_12]]#0 (%[[VAL_39]]) : (!fir.box<!fir.array<?xi32>>, index) -> !fir.ref<i32>
-! CHECK: %[[VAL_41:.*]] = fir.load %[[VAL_40]] : !fir.ref<i32>
-! CHECK: %[[VAL_42:.*]] = arith.constant 5050 : i32
-! CHECK: %[[VAL_43:.*]] = arith.cmpi ne, %[[VAL_41]], %[[VAL_42]] : i32
-! CHECK: cf.cond_br %[[VAL_43]], ^bb1, ^bb2
-! CHECK: ^bb1:
-! CHECK: %[[VAL_44:.*]] = arith.constant 1 : i32
-! CHECK: %[[VAL_45:.*]] = arith.constant false
-! CHECK: %[[VAL_46:.*]] = arith.constant false
-! CHECK: fir.call @_FortranAStopStatement(%[[VAL_44]], %[[VAL_45]], %[[VAL_46]]) fastmath<contract> : (i32, i1, i1) -> ()
-! CHECK: fir.unreachable
-! CHECK: ^bb2:
+! CHECK: scf.execute_region no_inline {
+! CHECK: %[[VAL_39:.*]] = arith.constant 1 : index
+! CHECK: %[[VAL_40:.*]] = hlfir.designate %[[VAL_12]]#0 (%[[VAL_39]]) : (!fir.box<!fir.array<?xi32>>, index) -> !fir.ref<i32>
+! CHECK: %[[VAL_41:.*]] = fir.load %[[VAL_40]] : !fir.ref<i32>
+! CHECK: %[[VAL_42:.*]] = arith.constant 5050 : i32
+! CHECK: %[[VAL_43:.*]] = arith.cmpi ne, %[[VAL_41]], %[[VAL_42]] : i32
+! CHECK: cf.cond_br %[[VAL_43]], ^bb1, ^bb2
+! CHECK: ^bb1:
+! CHECK: %[[VAL_44:.*]] = arith.constant 1 : i32
+! CHECK: %[[VAL_45:.*]] = arith.constant false
+! CHECK: %[[VAL_46:.*]] = arith.constant false
+! CHECK: fir.call @_FortranAStopStatement(%[[VAL_44]], %[[VAL_45]], %[[VAL_46]]) fastmath<contract> : (i32, i1, i1) -> ()
+! CHECK: fir.unreachable
+! CHECK: ^bb2:
+! CHECK: scf.yield
+! CHECK: }
! CHECK: return
! CHECK: }
! CHECK: func.func private @_FortranAStopStatement(i32, i1, i1) attributes {fir.runtime}
diff --git a/flang/test/Lower/OpenMP/stop-stmt-in-region.f90 b/flang/test/Lower/OpenMP/stop-stmt-in-region.f90
index 67cd8b17f484b..8acf1eaf387f8 100644
--- a/flang/test/Lower/OpenMP/stop-stmt-in-region.f90
+++ b/flang/test/Lower/OpenMP/stop-stmt-in-region.f90
@@ -48,17 +48,20 @@ subroutine test_stop_in_region2()
! CHECK: omp.parallel {
! CHECK: %[[VAL_1:.*]] = arith.constant 3 : i32
! CHECK: hlfir.assign %[[VAL_1]] to %[[VAL_0_DECL]]#0 : i32, !fir.ref<i32>
-! CHECK: %[[VAL_2:.*]] = fir.load %[[VAL_0_DECL]]#0 : !fir.ref<i32>
-! CHECK: %[[VAL_3:.*]] = arith.constant 1 : i32
-! CHECK: %[[VAL_4:.*]] = arith.cmpi sgt, %[[VAL_2]], %[[VAL_3]] : i32
-! CHECK: cf.cond_br %[[VAL_4]], ^bb1, ^bb2
-! CHECK: ^bb1:
-! CHECK: %[[VAL_5:.*]] = fir.load %[[VAL_0_DECL]]#0 : !fir.ref<i32>
-! CHECK: %[[VAL_6:.*]] = arith.constant false
-! CHECK: %[[VAL_7:.*]] = arith.constant false
-! CHECK: fir.call @_FortranAStopStatement(%[[VAL_5]], %[[VAL_6]], %[[VAL_7]]) {{.*}} : (i32, i1, i1) -> ()
-! CHECK: omp.terminator
-! CHECK: ^bb2:
+! CHECK: scf.execute_region no_inline {
+! CHECK: %[[VAL_2:.*]] = fir.load %[[VAL_0_DECL]]#0 : !fir.ref<i32>
+! CHECK: %[[VAL_3:.*]] = arith.constant 1 : i32
+! CHECK: %[[VAL_4:.*]] = arith.cmpi sgt, %[[VAL_2]], %[[VAL_3]] : i32
+! CHECK: cf.cond_br %[[VAL_4]], ^bb1, ^bb2
+! CHECK: ^bb1:
+! CHECK: %[[VAL_5:.*]] = fir.load %[[VAL_0_DECL]]#0 : !fir.ref<i32>
+! CHECK: %[[VAL_6:.*]] = arith.constant false
+! CHECK: %[[VAL_7:.*]] = arith.constant false
+! CHECK: fir.call @_FortranAStopStatement(%[[VAL_5]], %[[VAL_6]], %[[VAL_7]]) {{.*}} : (i32, i1, i1) -> ()
+! CHECK: fir.unreachable
+! CHECK: ^bb2:
+! CHECK: scf.yield
+! CHECK: }
! CHECK: omp.terminator
! CHECK: }
! CHECK: return
@@ -88,26 +91,25 @@ subroutine test_stop_in_region3()
! CHECK-NEXT: omp.loop_nest (%[[VAL_6:.*]]) : i32 = (%[[VAL_3]]) to (%[[VAL_4]]) inclusive step (%[[VAL_5]]) {
! CHECK: %[[VAL_0_DECL:.*]]:2 = hlfir.declare %[[VAL_0]] {uniq_name = "_QFtest_stop_in_region4Ei"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
! CHECK: hlfir.assign %[[VAL_6]] to %[[VAL_0_DECL]]#0 : i32, !fir.ref<i32>
-! CHECK: cf.br ^bb1
-! CHECK: ^bb1:
! CHECK: %[[VAL_7:.*]] = arith.constant 3 : i32
! CHECK: hlfir.assign %[[VAL_7]] to %[[VAL_2_DECL]]#0 : i32, !fir.ref<i32>
-! CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_2_DECL]]#0 : !fir.ref<i32>
-! CHECK: %[[VAL_9:.*]] = arith.constant 1 : i32
-! CHECK: %[[VAL_10:.*]] = arith.cmpi sgt, %[[VAL_8]], %[[VAL_9]] : i32
-! CHECK: cf.cond_br %[[VAL_10]], ^bb2, ^bb3
-! CHECK: ^bb2:
-! CHECK: %[[VAL_11:.*]] = fir.load %[[VAL_2_DECL]]#0 : !fir.ref<i32>
-! CHECK: %[[VAL_12:.*]] = arith.constant false
-! CHECK: %[[VAL_13:.*]] = arith.constant false
-! CHECK: fir.call @_FortranAStopStatement(%[[VAL_11]], %[[VAL_12]], %[[VAL_13]]) {{.*}} : (i32, i1, i1) -> ()
-! CHECK: omp.yield
-! CHECK: ^bb3:
+! CHECK: scf.execute_region no_inline {
+! CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_2_DECL]]#0 : !fir.ref<i32>
+! CHECK: %[[VAL_9:.*]] = arith.constant 1 : i32
+! CHECK: %[[VAL_10:.*]] = arith.cmpi sgt, %[[VAL_8]], %[[VAL_9]] : i32
+! CHECK: cf.cond_br %[[VAL_10]], ^bb1, ^bb2
+! CHECK: ^bb1:
+! CHECK: %[[VAL_11:.*]] = fir.load %[[VAL_2_DECL]]#0 : !fir.ref<i32>
+! CHECK: %[[VAL_12:.*]] = arith.constant false
+! CHECK: %[[VAL_13:.*]] = arith.constant false
+! CHECK: fir.call @_FortranAStopStatement(%[[VAL_11]], %[[VAL_12]], %[[VAL_13]]) {{.*}} : (i32, i1, i1) -> ()
+! CHECK: fir.unreachable
+! CHECK: ^bb2:
+! CHECK: scf.yield
+! CHECK: }
! CHECK: omp.yield
! CHECK: }
! CHECK: }
-! CHECK: cf.br ^bb1
-! CHECK: ^bb1:
! CHECK: return
! CHECK: }
@@ -139,12 +141,15 @@ subroutine test_stop_in_region5()
!CHECK-LABEL: func.func @_QPtest_stop_in_region6
!CHECK: omp.parallel {
-!CHECK: cf.cond_br %{{.*}}, ^[[BB1:.*]], ^[[BB2:.*]]
-!CHECK: ^[[BB1]]:
-!CHECK: {{.*}}fir.call @_FortranAStopStatement({{.*}}, {{.*}}, {{.*}}) fastmath<contract> : (i32, i1, i1) -> ()
-!CHECK: omp.terminator
-!CHECK: ^[[BB2]]:
-!CHECK: {{.*}}fir.call @_FortranAStopStatement({{.*}}, {{.*}}, {{.*}}) fastmath<contract> : (i32, i1, i1) -> ()
+!CHECK: scf.execute_region no_inline {
+!CHECK: cf.cond_br %{{.*}}, ^[[BB1:.*]], ^[[BB2:.*]]
+!CHECK: ^[[BB1]]:
+!CHECK: {{.*}}fir.call @_FortranAStopStatement({{.*}}, {{.*}}, {{.*}}) fastmath<contract> : (i32, i1, i1) -> ()
+!CHECK: fir.unreachable
+!CHECK: ^[[BB2]]:
+!CHECK: {{.*}}fir.call @_FortranAStopStatement({{.*}}, {{.*}}, {{.*}}) fastmath<contract> : (i32, i1, i1) -> ()
+!CHECK: fir.unreachable
+!CHECK: }
!CHECK: omp.terminator
!CHECK: }
!CHECK: return
diff --git a/flang/test/Lower/OpenMP/unstructured.f90 b/flang/test/Lower/OpenMP/unstructured.f90
index fa33abbb6cd4a..b60c1f5b7ef82 100644
--- a/flang/test/Lower/OpenMP/unstructured.f90
+++ b/flang/test/Lower/OpenMP/unstructured.f90
@@ -64,42 +64,45 @@ subroutine ss2(n) ! unstructured OpenMP construct; loop exit inside construct
! CHECK: %[[ALLOCA_K:.*]] = fir.alloca i32 {bindc_name = "k", pinned}
! CHECK: %[[K_DECL:.*]]:2 = hlfir.declare %[[ALLOCA_K]] {uniq_name = "_QFss3Ek"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
-! CHECK: br ^bb1
-! CHECK: ^bb1: // 2 preds: ^bb0, ^bb3
-! CHECK: cond_br %{{[0-9]*}}, ^bb2, ^bb4
-! CHECK: ^bb2: // pred: ^bb1
-
-! CHECK: omp.wsloop private(@{{.*}} %{{.*}}#0 -> %[[ALLOCA_2:.*]] : !fir.ref<i32>) {
-! CHECK: omp.loop_nest (%[[ARG1:.*]]) : {{.*}} {
-! CHECK: %[[OMP_LOOP_K_DECL:.*]]:2 = hlfir.declare %[[ALLOCA_2]] {uniq_name = "_QFss3Ek"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
-! CHECK: hlfir.assign %[[ARG1]] to %[[OMP_LOOP_K_DECL]]#0 : i32, !fir.ref<i32>
-! CHECK: @_FortranAioBeginExternalListOutput
-! CHECK: %[[LOAD_1:.*]] = fir.load %[[OMP_LOOP_K_DECL]]#0 : !fir.ref<i32>
-! CHECK: @_FortranAioOutputInteger32(%{{.*}}, %[[LOAD_1]])
-! CHECK: omp.yield
+! CHECK: fir.do_loop
+! CHECK: omp.wsloop private(@{{.*}} %{{.*}}#0 -> %[[ALLOCA_2:.*]] : !fir.ref<i32>) {
+! CHECK: omp.loop_nest (%[[ARG1:.*]]) : {{.*}} {
+! CHECK: %[[OMP_LOOP_K_DECL:.*]]:2 = hlfir.declare %[[ALLOCA_2]] {uniq_name = "_QFss3Ek"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: hlfir.assign %[[ARG1]] to %[[OMP_LOOP_K_DECL]]#0 : i32, !fir.ref<i32>
+! CHECK: @_FortranAioBeginExternalListOutput
+! CHECK: %[[LOAD_1:.*]] = fir.load %[[OMP_LOOP_K_DECL]]#0 : !fir.ref<i32>
+! CHECK: @_FortranAioOutputInteger32(%{{.*}}, %[[LOAD_1]])
+! CHECK: omp.yield
+! CHECK: }
! CHECK: }
-! CHECK: }
-! CHECK: omp.wsloop private(@{{.*}} %{{.*}}#0 -> %[[ALLOCA_1:.*]] : !fir.ref<i32>) {
-! CHECK: omp.loop_nest (%[[ARG2:.*]]) : {{.*}} {
-! CHECK: %[[OMP_LOOP_J_DECL:.*]]:2 = hlfir.declare %[[ALLOCA_1]] {uniq_name = "_QFss3Ej"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
-! CHECK: hlfir.assign %[[ARG2]] to %[[OMP_LOOP_J_DECL]]#0 : i32, !fir.ref<i32>
-! CHECK: br ^bb1
-! CHECK: ^bb2: // 2 preds: ^bb1, ^bb5
-! CHECK: cond_br %{{[0-9]*}}, ^bb3, ^bb6
-! CHECK: ^bb3: // pred: ^bb2
-! CHECK: cond_br %{{[0-9]*}}, ^bb4, ^bb5
-! CHECK: ^bb4: // pred: ^bb3
-! CHECK: @_FortranAioBeginExternalListOutput
-! CHECK: %[[LOAD_2:.*]] = fir.load %[[K_DECL]]#0 : !fir.ref<i32>
-! CHECK: @_FortranAioOutputInteger32(%{{.*}}, %[[LOAD_2]])
-! CHECK: br ^bb2
-! CHECK: ^bb6: // 2 preds: ^bb2, ^bb4
-! CHECK: omp.yield
+! CHECK: omp.wsloop private(@{{.*}} %{{.*}}#0 -> %[[ALLOCA_1:.*]] : !fir.ref<i32>) {
+! CHECK: omp.loop_nest (%[[ARG2:.*]]) : {{.*}} {
+! CHECK: %[[OMP_LOOP_J_DECL:.*]]:2 = hlfir.declare %[[ALLOCA_1]] {uniq_name = "_QFss3Ej"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: hlfir.assign %[[ARG2]] to %[[OMP_LOOP_J_DECL]]#0 : i32, !fir.ref<i32>
+! CHECK: scf.execute_region no_inline {
+! CHECK: cf.br ^bb1
+! CHECK: ^bb1:
+! CHECK: cf.br ^bb2
+! CHECK: ^bb2:
+! CHECK: cf.cond_br %{{[0-9]*}}, ^bb3, ^bb6
+! CHECK: ^bb3:
+! CHECK: cf.cond_br %{{[0-9]*}}, ^bb4, ^bb5
+! CHECK: ^bb4:
+! CHECK: cf.br ^bb6
+! CHECK: ^bb5:
+! CHECK: @_FortranAioBeginExternalListOutput
+! CHECK: %[[LOAD_2:.*]] = fir.load %[[K_DECL]]#0 : !fir.ref<i32>
+! CHECK: @_FortranAioOutputInteger32(%{{.*}}, %[[LOAD_2]])
+! CHECK: cf.br ^bb2
+! CHECK: ^bb6:
+! CHECK: scf.yield
+! CHECK: }
+! CHECK: omp.yield
+! CHECK: }
! CHECK: }
+! CHECK: fir.result
! CHECK: }
-! CHECK: br ^bb1
-! CHECK: ^bb4: // pred: ^bb1
! CHECK: omp.terminator
! CHECK: }
! CHECK: }
@@ -158,18 +161,19 @@ subroutine ss4(n) ! CYCLE in OpenMP wsloop constructs
! CHECK: omp.parallel private(@{{.*}} %{{.*}}#0 -> %{{.*}} : {{.*}}) {
! CHECK: omp.wsloop private({{.*}}) {
! CHECK: omp.loop_nest {{.*}} {
-! CHECK: br ^[[BB1:.*]]
-! CHECK: ^[[BB1]]:
-! CHECK: br ^[[BB2:.*]]
-! CHECK: ^[[BB2]]:
-! CHECK: cond_br %{{.*}}, ^[[BB3:.*]], ^[[BB6:.*]]
-! CHECK: ^[[BB3]]:
-! CHECK: cond_br %{{.*}}, ^[[BB4:.*]], ^[[BB3:.*]]
-! CHECK: ^[[BB4]]:
-! CHECK: br ^[[BB6]]
-! CHECK: ^[[BB3]]:
-! CHECK: br ^[[BB2]]
-! CHECK: ^[[BB6]]:
+! CHECK: scf.execute_region no_inline {
+! CHECK: cf.br ^[[BB1:.*]]
+! CHECK: ^[[BB1]]:
+! CHECK: cf.cond_br %{{.*}}, ^[[BB2:.*]], ^[[BB5:.*]]
+! CHECK: ^[[BB2]]:
+! CHECK: cf.cond_br %{{.*}}, ^[[BB3:.*]], ^[[BB4:.*]]
+! CHECK: ^[[BB3]]:
+! CHECK: cf.br ^[[BB5]]
+! CHECK: ^[[BB4]]:
+! CHECK: cf.br ^[[BB1]]
+! CHECK: ^[[BB5]]:
+! CHECK: scf.yield
+! CHECK: }
! CHECK: omp.yield
! CHECK: }
! CHECK: }
@@ -193,30 +197,28 @@ subroutine ss5() ! EXIT inside OpenMP wsloop (inside parallel)
end
! CHECK-LABEL: func @_QPss6() {
-! CHECK: omp.parallel private(@{{.*}} %{{.*}}#0 -> %{{.*}} : {{.*}}) {
-! CHECK: br ^[[BB1_OUTER:.*]]
-! CHECK: ^[[BB1_OUTER]]:
-! CHECK: cond_br %{{.*}}, ^[[BB2_OUTER:.*]], ^[[BB3_OUTER:.*]]
-! CHECK: ^[[BB2_OUTER]]:
-! CHECK: omp.wsloop private({{.*}}) {
-! CHECK: omp.loop_nest {{.*}} {
-! CHECK: br ^[[BB1:.*]]
-! CHECK: ^[[BB1]]:
-! CHECK: br ^[[BB2:.*]]
-! CHECK: ^[[BB2]]:
-! CHECK: cond_br %{{.*}}, ^[[BB3:.*]], ^[[BB6:.*]]
-! CHECK: ^[[BB3]]:
-! CHECK: cond_br %{{.*}}, ^[[BB4:.*]], ^[[BB5:.*]]
-! CHECK: ^[[BB4]]:
-! CHECK: br ^[[BB6]]
-! CHECK: ^[[BB5]]
-! CHECK: br ^[[BB2]]
-! CHECK: ^[[BB6]]:
-! CHECK: omp.yield
+! CHECK: omp.parallel private(@{{.*}} %{{.*}}#0 -> %{{.*}}, @{{.*}} %{{.*}}#0 -> %{{.*}} : {{.*}}, {{.*}}) {
+! CHECK: fir.do_loop
+! CHECK: omp.wsloop private({{.*}}) {
+! CHECK: omp.loop_nest {{.*}} {
+! CHECK: scf.execute_region no_inline {
+! CHECK: cf.br ^[[BB1:.*]]
+! CHECK: ^[[BB1]]:
+! CHECK: cf.cond_br %{{.*}}, ^[[BB2:.*]], ^[[BB5:.*]]
+! CHECK: ^[[BB2]]:
+! CHECK: cf.cond_br %{{.*}}, ^[[BB3:.*]], ^[[BB4:.*]]
+! CHECK: ^[[BB3]]:
+! CHECK: cf.br ^[[BB5]]
+! CHECK: ^[[BB4]]:
+! CHECK: cf.br ^[[BB1]]
+! CHECK: ^[[BB5]]:
+! CHECK: scf.yield
+! CHECK: }
+! CHECK: omp.yield
+! CHECK: }
! CHECK: }
+! CHECK: fir.result
! CHECK: }
-! CHECK: br ^[[BB1_OUTER]]
-! CHECK: ^[[BB3_OUTER]]:
! CHECK: omp.terminator
! CHECK: }
subroutine ss6() ! EXIT inside OpenMP wsloop in a do loop (inside parallel)
@@ -239,33 +241,31 @@ subroutine ss6() ! EXIT inside OpenMP wsloop in a do loop (inside parallel)
end
! CHECK-LABEL: func @_QPss7() {
-! CHECK: br ^[[BB1_OUTER:.*]]
-! CHECK: ^[[BB1_OUTER]]:
-! CHECK: cond_br %{{.*}}, ^[[BB2_OUTER:.*]], ^[[BB3_OUTER:.*]]
-! CHECK-NEXT: ^[[BB2_OUTER:.*]]:
-! CHECK: omp.parallel {
-! CHECK: omp.wsloop private({{.*}}) {
-! CHECK: omp.loop_nest {{.*}} {
-! CHECK: br ^[[BB1:.*]]
-! CHECK-NEXT: ^[[BB1]]:
-! CHECK: br ^[[BB2:.*]]
-! CHECK-NEXT: ^[[BB2]]:
-! CHECK: cond_br %{{.*}}, ^[[BB3:.*]], ^[[BB6:.*]]
-! CHECK-NEXT: ^[[BB3]]:
-! CHECK: cond_br %{{.*}}, ^[[BB4:.*]], ^[[BB5:.*]]
-! CHECK-NEXT: ^[[BB4]]:
-! CHECK: br ^[[BB6]]
-! CHECK-NEXT: ^[[BB5]]:
-! CHECK: br ^[[BB2]]
-! CHECK-NEXT: ^[[BB6]]:
-! CHECK: omp.yield
+! CHECK: fir.do_loop
+! CHECK: omp.parallel {
+! CHECK: omp.wsloop private({{.*}}) {
+! CHECK: omp.loop_nest {{.*}} {
+! CHECK: scf.execute_region no_inline {
+! CHECK: cf.br ^[[BB1:.*]]
+! CHECK: ^[[BB1]]:
+! CHECK: cf.cond_br %{{.*}}, ^[[BB2:.*]], ^[[BB5:.*]]
+! CHECK: ^[[BB2]]:
+! CHECK: cf.cond_br %{{.*}}, ^[[BB3:.*]], ^[[BB4:.*]]
+! CHECK: ^[[BB3]]:
+! CHECK: cf.br ^[[BB5]]
+! CHECK: ^[[BB4]]:
+! CHECK: cf.br ^[[BB1]]
+! CHECK: ^[[BB5]]:
+! CHECK: scf.yield
+! CHECK: }
+! CHECK: omp.yield
+! CHECK: }
! CHECK: }
+! CHECK: omp.terminator
! CHECK: }
-! CHECK: omp.terminator
+! CHECK: fir.result
! CHECK: }
-! CHECK: br ^[[BB1_OUTER]]
-! CHECK-NEXT: ^[[BB3_OUTER]]:
-! CHECK-NEXT: return
+! CHECK: return
subroutine ss7() ! EXIT inside OpenMP parallel do (inside do loop)
integer :: x
do i = 1, 3
@@ -286,18 +286,19 @@ subroutine ss7() ! EXIT inside OpenMP parallel do (inside do loop)
! CHECK: omp.parallel {
! CHECK: omp.wsloop private({{.*}}) {
! CHECK: omp.loop_nest {{.*}} {
-! CHECK: br ^[[BB1:.*]]
-! CHECK-NEXT: ^[[BB1]]:
-! CHECK: br ^[[BB2:.*]]
-! CHECK: ^[[BB2]]:
-! CHECK: cond_br %{{.*}}, ^[[BB3:.*]], ^[[BB6:.*]]
-! CHECK: ^[[BB3]]:
-! CHECK: cond_br %{{.*}}, ^[[BB4:.*]], ^[[BB5:.*]]
-! CHECK: ^[[BB4]]:
-! CHECK-NEXT: br ^[[BB6]]
-! CHECK: ^[[BB5]]:
-! CHECK: br ^[[BB2]]
-! CHECK-NEXT: ^[[BB6]]:
+! CHECK: scf.execute_region no_inline {
+! CHECK: cf.br ^[[BB1:.*]]
+! CHECK: ^[[BB1]]:
+! CHECK: cf.cond_br %{{.*}}, ^[[BB2:.*]], ^[[BB5:.*]]
+! CHECK: ^[[BB2]]:
+! CHECK: cf.cond_br %{{.*}}, ^[[BB3:.*]], ^[[BB4:.*]]
+! CHECK: ^[[BB3]]:
+! CHECK: cf.br ^[[BB5]]
+! CHECK: ^[[BB4]]:
+! CHECK: cf.br ^[[BB1]]
+! CHECK: ^[[BB5]]:
+! CHECK: scf.yield
+! CHECK: }
! CHECK: omp.yield
! CHECK: }
! CHECK: }
diff --git a/flang/test/Lower/OpenMP/wsloop-unstructured.f90 b/flang/test/Lower/OpenMP/wsloop-unstructured.f90
index 6174718c08758..964a980b4ddb4 100644
--- a/flang/test/Lower/OpenMP/wsloop-unstructured.f90
+++ b/flang/test/Lower/OpenMP/wsloop-unstructured.f90
@@ -32,32 +32,31 @@ end subroutine sub
! CHECK: omp.wsloop private({{.*}}) {
! CHECK-NEXT: omp.loop_nest (%[[VAL_53:.*]], %[[VAL_54:.*]]) : i32 = ({{.*}}) to ({{.*}}) inclusive step ({{.*}}) {
! [...]
-! CHECK: cf.br ^bb1
-! CHECK: ^bb1:
-! CHECK: cf.br ^bb2
-! CHECK: ^bb2:
+! CHECK: scf.execute_region no_inline {
+! CHECK: cf.br ^bb1
+! CHECK: ^bb1:
! [...]
-! CHECK: cf.br ^bb3
-! CHECK: ^bb3:
+! CHECK: cf.br ^bb2
+! CHECK: ^bb2:
! [...]
-! CHECK: %[[VAL_63:.*]] = arith.cmpi sgt, %{{.*}}, %{{.*}} : i32
-! CHECK: cf.cond_br %[[VAL_63]], ^bb4, ^bb7
-! CHECK: ^bb4:
+! CHECK: %[[VAL_63:.*]] = arith.cmpi sgt, %{{.*}}, %{{.*}} : i32
+! CHECK: cf.cond_br %[[VAL_63]], ^bb3, ^bb6
+! CHECK: ^bb3:
! [...]
-! CHECK: %[[VAL_76:.*]] = arith.cmpf olt, %{{.*}}, %{{.*}} fastmath<contract> : f32
-! CHECK: cf.cond_br %[[VAL_76]], ^bb5, ^bb6
-! CHECK: ^bb5:
-! CHECK: cf.br ^bb7
-! CHECK: ^bb6:
+! CHECK: %[[VAL_76:.*]] = arith.cmpf olt, %{{.*}}, %{{.*}} fastmath<contract> : f32
+! CHECK: cf.cond_br %[[VAL_76]], ^bb4, ^bb5
+! CHECK: ^bb4:
+! CHECK: cf.br ^bb6
+! CHECK: ^bb5:
! [...]
-! CHECK: cf.br ^bb3
-! CHECK: ^bb7:
+! CHECK: cf.br ^bb2
+! CHECK: ^bb6:
+! CHECK: scf.yield
+! CHECK: }
! CHECK: omp.yield
! CHECK: }
! CHECK: }
! CHECK: omp.terminator
! CHECK: }
-! CHECK: cf.br ^bb1
-! CHECK: ^bb1:
! CHECK: return
! CHECK: }
diff --git a/flang/test/Lower/branching-directive.f90 b/flang/test/Lower/branching-directive.f90
index 69270d7bcbe96..e4e511f33918c 100644
--- a/flang/test/Lower/branching-directive.f90
+++ b/flang/test/Lower/branching-directive.f90
@@ -2,15 +2,21 @@
!https://github.com/llvm/llvm-project/issues/91526
+! The if-construct is unstructured (stop in else) but wrappable, so the
+! PFT-to-MLIR pass hides its CFG inside scf.execute_region. The THEN branch
+! correctly branches to the wrap's merge (yield) block rather than falling
+! into the ELSE block — the original bug fixed by issue 91526 stays fixed.
!CHECK-LABEL: func.func @_QPsimple1
+!CHECK: scf.execute_region
!CHECK: cf.cond_br %{{[0-9]+}}, ^bb[[THEN:[0-9]+]], ^bb[[ELSE:[0-9]+]]
!CHECK: ^bb[[THEN]]:
!CHECK: omp.parallel
-!CHECK: cf.br ^bb[[ENDIF:[0-9]+]]
+!CHECK: cf.br ^bb[[MERGE:[0-9]+]]
!CHECK: ^bb[[ELSE]]:
!CHECK: fir.call @_FortranAStopStatement
!CHECK: fir.unreachable
-!CHECK: ^bb[[ENDIF]]:
+!CHECK: ^bb[[MERGE]]:
+!CHECK: scf.yield
!CHECK: return
subroutine simple1(y)
@@ -26,7 +32,11 @@ subroutine simple1(y)
end if
end subroutine
-!CHECK-LABEL: func.func @_QPsimple2
+! Same scenario but the if-construct has a GOTO exiting the construct, so
+! branchesAreInternal is false and the wrap is skipped — the original
+! unstructured CFG with fir.unreachable is emitted.
+!CHECK-LABEL: func.func @_QPsimple1_goto
+!CHECK-NOT: scf.execute_region
!CHECK: cf.cond_br %{{[0-9]+}}, ^bb[[THEN:[0-9]+]], ^bb[[ELSE:[0-9]+]]
!CHECK: ^bb[[THEN]]:
!CHECK: omp.parallel
@@ -35,6 +45,35 @@ subroutine simple1(y)
!CHECK: fir.call @_FortranAStopStatement
!CHECK: fir.unreachable
!CHECK: ^bb[[ENDIF]]:
+!CHECK: return
+
+subroutine simple1_goto(y)
+ implicit none
+ logical, intent(in) :: y
+ integer :: i
+ if (y) then
+ !$omp parallel
+ i = 1
+ !$omp end parallel
+ goto 100
+ else
+ stop 1
+ end if
+ i = 2
+100 continue
+end subroutine
+
+!CHECK-LABEL: func.func @_QPsimple2
+!CHECK: scf.execute_region
+!CHECK: cf.cond_br %{{[0-9]+}}, ^bb[[THEN:[0-9]+]], ^bb[[ELSE:[0-9]+]]
+!CHECK: ^bb[[THEN]]:
+!CHECK: omp.parallel
+!CHECK: cf.br ^bb[[MERGE:[0-9]+]]
+!CHECK: ^bb[[ELSE]]:
+!CHECK: fir.call @_FortranAStopStatement
+!CHECK: fir.unreachable
+!CHECK: ^bb[[MERGE]]:
+!CHECK: scf.yield
!CHECK: fir.call @_FortranAioOutputReal64
!CHECK: return
subroutine simple2(x, yn)
diff --git a/flang/test/Lower/do_loop_execute_region_wrap.f90 b/flang/test/Lower/do_loop_execute_region_wrap.f90
new file mode 100644
index 0000000000000..74dd20cf88a12
--- /dev/null
+++ b/flang/test/Lower/do_loop_execute_region_wrap.f90
@@ -0,0 +1,111 @@
+! RUN: bbc -emit-hlfir --wrap-unstructured-constructs-in-execute-region %s -o - | FileCheck %s
+
+! An unstructured IF inside a DO is self-contained, so isUnstructured does
+! not propagate to the DO. The DO lowers as fir.do_loop and the IF's blocks
+! are wrapped in an scf.execute_region inside its body.
+subroutine wrapped_unstructured(n, a)
+ integer :: n, i
+ real :: a(n)
+ do i = 1, n
+ if (a(i) > 0.0) stop
+ a(i) = real(i)
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPwrapped_unstructured
+! CHECK: fir.do_loop
+! CHECK: scf.execute_region no_inline {
+! CHECK: cf.cond_br
+! CHECK: fir.call @_FortranAStopStatement
+! CHECK: fir.unreachable
+! CHECK: scf.yield
+! CHECK: }
+! CHECK: fir.result
+
+! Unstructured DO with a GOTO targeting a label outside the construct that
+! is reachable on a path distinct from the loop's natural exit: not wrapped.
+! The GOTO jumps past the post-loop store directly to label 99, so the
+! loop body has an outgoing edge that is neither another loop block nor the
+! construct exit. The wrap pass bails out and the CFG stays flat.
+subroutine not_wrapped_outer_label(n, a)
+ integer :: n, i
+ real :: a(n)
+ do i = 1, n
+ if (a(i) > 0.0) goto 99
+ a(i) = real(i)
+ end do
+ a(1) = -1.0
+99 continue
+end subroutine
+
+! CHECK-LABEL: func.func @_QPnot_wrapped_outer_label
+! CHECK-NOT: scf.execute_region
+! CHECK: cf.cond_br
+! CHECK: return
+
+! A plain, structured DO with no early exits: lowered as fir.do_loop,
+! never reaches the wrap path (the loop is not unstructured at all).
+subroutine structured(n, a)
+ integer :: n, i
+ real :: a(n)
+ do i = 1, n
+ a(i) = real(i)
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPstructured
+! CHECK-NOT: scf.execute_region
+! CHECK: fir.do_loop
+! CHECK: return
+
+! Nested DOs whose only unstructuredness comes from a self-contained IF.
+! Both DOs lower as fir.do_loop; only the IF is wrapped in scf.execute_region
+! at the innermost level.
+subroutine outer_structured_inner_wrapped(n, a)
+ integer :: n, i, j
+ real :: a(n)
+ do i = 1, n
+ do j = 1, n
+ if (a(j) > 0.0) stop
+ a(j) = real(i + j)
+ end do
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPouter_structured_inner_wrapped
+! CHECK: fir.do_loop
+! CHECK: fir.do_loop
+! CHECK: scf.execute_region no_inline {
+! CHECK: cf.cond_br
+! CHECK: fir.call @_FortranAStopStatement
+! CHECK: fir.unreachable
+! CHECK: scf.yield
+! CHECK: }
+! CHECK: fir.result
+! CHECK: fir.result
+
+! Structured outer DO containing an unstructured inner DO (the inner DO has
+! an EXIT that targets its own construct exit). isUnstructured does not
+! propagate from the inner DO to the outer DO, so the outer lowers as
+! fir.do_loop. The inner DO's blocks would otherwise have nowhere to live
+! inside the outer's single-block region, so the pre-wrap path creates an
+! scf.execute_region around the inner DO's CFG.
+subroutine outer_fir_do_loop_inner_unstructured_do(n, a)
+ integer :: n, i, j
+ real :: a(n)
+ do i = 1, n
+ do j = 1, n
+ if (a(j) > 0.0) exit
+ end do
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPouter_fir_do_loop_inner_unstructured_do
+! CHECK: fir.do_loop
+! CHECK: scf.execute_region no_inline {
+! CHECK: cf.br
+! CHECK: cf.cond_br
+! CHECK: cf.cond_br
+! CHECK: scf.yield
+! CHECK: }
+! CHECK: fir.result
diff --git a/flang/test/Lower/do_loop_unstructured.f90 b/flang/test/Lower/do_loop_unstructured.f90
index 4182ab0b6aefa..99b8cddd083fa 100644
--- a/flang/test/Lower/do_loop_unstructured.f90
+++ b/flang/test/Lower/do_loop_unstructured.f90
@@ -100,91 +100,41 @@ subroutine nested_unstructured()
end do
end do
end subroutine
+! With the wrap-unstructured-constructs-in-execute-region pass, the innermost
+! k-loop is the only one classified unstructured (the `goto 404`/`404 continue`
+! pattern). It gets wrapped in scf.execute_region, and the outer i and j
+! loops fold back to fir.do_loop.
! CHECK-LABEL: nested_unstructured
! CHECK: %[[TRIP_VAR_K_REF:.*]] = fir.alloca i32
-! CHECK: %[[TRIP_VAR_J_REF:.*]] = fir.alloca i32
-! CHECK: %[[TRIP_VAR_I_REF:.*]] = fir.alloca i32
! CHECK: %[[LOOP_VAR_I_REF:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFnested_unstructuredEi"}
! CHECK: %[[LOOP_VAR_I_DECL:.*]]:2 = hlfir.declare %[[LOOP_VAR_I_REF]]
! CHECK: %[[LOOP_VAR_J_REF:.*]] = fir.alloca i32 {bindc_name = "j", uniq_name = "_QFnested_unstructuredEj"}
! CHECK: %[[LOOP_VAR_J_DECL:.*]]:2 = hlfir.declare %[[LOOP_VAR_J_REF]]
! CHECK: %[[LOOP_VAR_K_REF:.*]] = fir.alloca i32 {bindc_name = "k", uniq_name = "_QFnested_unstructuredEk"}
! CHECK: %[[LOOP_VAR_K_DECL:.*]]:2 = hlfir.declare %[[LOOP_VAR_K_REF]]
-! CHECK: %[[I_START:.*]] = arith.constant 1 : i32
-! CHECK: %[[I_END:.*]] = arith.constant 100 : i32
-! CHECK: %[[I_STEP:.*]] = arith.constant 1 : i32
-! CHECK: %[[TMP1:.*]] = arith.subi %[[I_END]], %[[I_START]] : i32
-! CHECK: %[[TMP2:.*]] = arith.addi %[[TMP1]], %[[I_STEP]] : i32
-! CHECK: %[[TRIP_COUNT_I:.*]] = arith.divsi %[[TMP2]], %[[I_STEP]] : i32
-! CHECK: fir.store %[[TRIP_COUNT_I]] to %[[TRIP_VAR_I_REF]] : !fir.ref<i32>
-! CHECK: fir.store %[[I_START]] to %[[LOOP_VAR_I_DECL]]#0 : !fir.ref<i32>
-! CHECK: cf.br ^[[HEADER_I:.*]]
-! CHECK: ^[[HEADER_I]]:
-! CHECK: %[[TRIP_VAR_I:.*]] = fir.load %[[TRIP_VAR_I_REF]] : !fir.ref<i32>
-! CHECK: %[[ZERO_1:.*]] = arith.constant 0 : i32
-! CHECK: %[[COND_I:.*]] = arith.cmpi sgt, %[[TRIP_VAR_I]], %[[ZERO_1]] : i32
-! CHECK: cf.cond_br %[[COND_I]], ^[[BODY_I:.*]], ^[[EXIT_I:.*]]
-! CHECK: ^[[BODY_I]]:
-! CHECK: %[[J_START:.*]] = arith.constant 1 : i32
-! CHECK: %[[J_END:.*]] = arith.constant 200 : i32
-! CHECK: %[[J_STEP:.*]] = arith.constant 1 : i32
-! CHECK: %[[TMP3:.*]] = arith.subi %[[J_END]], %[[J_START]] : i32
-! CHECK: %[[TMP4:.*]] = arith.addi %[[TMP3]], %[[J_STEP]] : i32
-! CHECK: %[[TRIP_COUNT_J:.*]] = arith.divsi %[[TMP4]], %[[J_STEP]] : i32
-! CHECK: fir.store %[[TRIP_COUNT_J]] to %[[TRIP_VAR_J_REF]] : !fir.ref<i32>
-! CHECK: fir.store %[[J_START]] to %[[LOOP_VAR_J_DECL]]#0 : !fir.ref<i32>
-! CHECK: cf.br ^[[HEADER_J:.*]]
-! CHECK: ^[[HEADER_J]]:
-! CHECK: %[[TRIP_VAR_J:.*]] = fir.load %[[TRIP_VAR_J_REF]] : !fir.ref<i32>
-! CHECK: %[[ZERO_2:.*]] = arith.constant 0 : i32
-! CHECK: %[[COND_J:.*]] = arith.cmpi sgt, %[[TRIP_VAR_J]], %[[ZERO_2]] : i32
-! CHECK: cf.cond_br %[[COND_J]], ^[[BODY_J:.*]], ^[[EXIT_J:.*]]
-! CHECK: ^[[BODY_J]]:
-! CHECK: %[[K_START:.*]] = arith.constant 1 : i32
-! CHECK: %[[K_END:.*]] = arith.constant 300 : i32
-! CHECK: %[[K_STEP:.*]] = arith.constant 1 : i32
-! CHECK: %[[TMP3:.*]] = arith.subi %[[K_END]], %[[K_START]] : i32
-! CHECK: %[[TMP4:.*]] = arith.addi %[[TMP3]], %[[K_STEP]] : i32
-! CHECK: %[[TRIP_COUNT_K:.*]] = arith.divsi %[[TMP4]], %[[K_STEP]] : i32
-! CHECK: fir.store %[[TRIP_COUNT_K]] to %[[TRIP_VAR_K_REF]] : !fir.ref<i32>
-! CHECK: fir.store %[[K_START]] to %[[LOOP_VAR_K_DECL]]#0 : !fir.ref<i32>
-! CHECK: cf.br ^[[HEADER_K:.*]]
-! CHECK: ^[[HEADER_K]]:
-! CHECK: %[[TRIP_VAR_K:.*]] = fir.load %[[TRIP_VAR_K_REF]] : !fir.ref<i32>
-! CHECK: %[[ZERO_2:.*]] = arith.constant 0 : i32
-! CHECK: %[[COND_K:.*]] = arith.cmpi sgt, %[[TRIP_VAR_K]], %[[ZERO_2]] : i32
-! CHECK: cf.cond_br %[[COND_K]], ^[[BODY_K:.*]], ^[[EXIT_K:.*]]
-! CHECK: ^[[BODY_K]]:
-! CHECK: %[[TRIP_VAR_K:.*]] = fir.load %[[TRIP_VAR_K_REF]] : !fir.ref<i32>
-! CHECK: %[[ONE_1:.*]] = arith.constant 1 : i32
-! CHECK: %[[TRIP_VAR_K_NEXT:.*]] = arith.subi %[[TRIP_VAR_K]], %[[ONE_1]] : i32
-! CHECK: fir.store %[[TRIP_VAR_K_NEXT]] to %[[TRIP_VAR_K_REF]] : !fir.ref<i32>
-! CHECK: %[[LOOP_VAR_K:.*]] = fir.load %[[LOOP_VAR_K_DECL]]#0 : !fir.ref<i32>
-! CHECK: %[[K_STEP_2:.*]] = arith.constant 1 : i32
-! CHECK: %[[LOOP_VAR_K_NEXT:.*]] = arith.addi %[[LOOP_VAR_K]], %[[K_STEP_2]] overflow<nsw> : i32
-! CHECK: fir.store %[[LOOP_VAR_K_NEXT]] to %[[LOOP_VAR_K_DECL]]#0 : !fir.ref<i32>
-! CHECK: cf.br ^[[HEADER_K]]
-! CHECK: ^[[EXIT_K]]:
-! CHECK: %[[TRIP_VAR_J:.*]] = fir.load %[[TRIP_VAR_J_REF]] : !fir.ref<i32>
-! CHECK: %[[ONE_1:.*]] = arith.constant 1 : i32
-! CHECK: %[[TRIP_VAR_J_NEXT:.*]] = arith.subi %[[TRIP_VAR_J]], %[[ONE_1]] : i32
-! CHECK: fir.store %[[TRIP_VAR_J_NEXT]] to %[[TRIP_VAR_J_REF]] : !fir.ref<i32>
-! CHECK: %[[LOOP_VAR_J:.*]] = fir.load %[[LOOP_VAR_J_DECL]]#0 : !fir.ref<i32>
-! CHECK: %[[J_STEP_2:.*]] = arith.constant 1 : i32
-! CHECK: %[[LOOP_VAR_J_NEXT:.*]] = arith.addi %[[LOOP_VAR_J]], %[[J_STEP_2]] overflow<nsw> : i32
-! CHECK: fir.store %[[LOOP_VAR_J_NEXT]] to %[[LOOP_VAR_J_DECL]]#0 : !fir.ref<i32>
-! CHECK: cf.br ^[[HEADER_J]]
-! CHECK: ^[[EXIT_J]]:
-! CHECK: %[[TRIP_VAR_I:.*]] = fir.load %[[TRIP_VAR_I_REF]] : !fir.ref<i32>
-! CHECK: %[[ONE_1:.*]] = arith.constant 1 : i32
-! CHECK: %[[TRIP_VAR_I_NEXT:.*]] = arith.subi %[[TRIP_VAR_I]], %[[ONE_1]] : i32
-! CHECK: fir.store %[[TRIP_VAR_I_NEXT]] to %[[TRIP_VAR_I_REF]] : !fir.ref<i32>
-! CHECK: %[[LOOP_VAR_I:.*]] = fir.load %[[LOOP_VAR_I_DECL]]#0 : !fir.ref<i32>
-! CHECK: %[[I_STEP_2:.*]] = arith.constant 1 : i32
-! CHECK: %[[LOOP_VAR_I_NEXT:.*]] = arith.addi %[[LOOP_VAR_I]], %[[I_STEP_2]] overflow<nsw> : i32
-! CHECK: fir.store %[[LOOP_VAR_I_NEXT]] to %[[LOOP_VAR_I_DECL]]#0 : !fir.ref<i32>
-! CHECK: cf.br ^[[HEADER_I]]
-! CHECK: ^[[EXIT_I]]:
+! CHECK: fir.do_loop %{{[^ ]+}} = %{{.*}} to %{{.*}} step %{{.*}} iter_args(%{{[^ ]+}} = %{{.*}}) -> (i32) {
+! CHECK: fir.do_loop %{{[^ ]+}} = %{{.*}} to %{{.*}} step %{{.*}} iter_args(%{{[^ ]+}} = %{{.*}}) -> (i32) {
+! CHECK: scf.execute_region no_inline {
+! CHECK: cf.br ^[[HEADER_K:.*]]
+! CHECK: ^[[HEADER_K]]:
+! CHECK: %[[TRIP_COUNT_K:.*]] = arith.divsi %{{.*}}, %{{.*}} : i32
+! CHECK: fir.store %[[TRIP_COUNT_K]] to %[[TRIP_VAR_K_REF]] : !fir.ref<i32>
+! CHECK: fir.store %{{.*}} to %[[LOOP_VAR_K_DECL]]#0 : !fir.ref<i32>
+! CHECK: cf.br ^[[HEADER_K_BODY:.*]]
+! CHECK: ^[[HEADER_K_BODY]]:
+! CHECK: %[[TRIP_VAR_K:.*]] = fir.load %[[TRIP_VAR_K_REF]] : !fir.ref<i32>
+! CHECK: %[[COND_K:.*]] = arith.cmpi sgt, %[[TRIP_VAR_K]], %{{.*}} : i32
+! CHECK: cf.cond_br %[[COND_K]], ^[[BODY_K:.*]], ^[[EXIT_K:.*]]
+! CHECK: ^[[BODY_K]]:
+! CHECK: cf.br ^{{.*}}
+! CHECK: cf.br ^[[HEADER_K_BODY]]
+! CHECK: ^[[EXIT_K]]:
+! CHECK: scf.yield
+! CHECK: }
+! CHECK: fir.result %{{.*}} : i32
+! CHECK: }
+! CHECK: fir.result %{{.*}} : i32
+! CHECK: }
! CHECK: return
! Test the existence of a structured loop inside an unstructured loop.
@@ -247,13 +197,14 @@ subroutine unstructured_do_concurrent
end
! CHECK-LABEL: func.func @_QPunstructured_do_concurrent
! CHECK: %[[ITER_VAR:.*]] = fir.alloca i32
-
-! CHECK: ^[[HEADER]]:
-! CHECK: %{{.*}} = fir.load %[[ITER_VAR]] : !fir.ref<i32>
-! CHECK: cf.cond_br %{{.*}}, ^[[BODY:.*]], ^[[EXIT:.*]]
-
-! CHECK: ^[[BODY]]:
-! CHECK-NEXT: %{{.*}} = fir.alloca !fir.logical<4> {bindc_name = "success", {{.*}}}
-
-! CHECK: ^[[EXIT]]:
-! CHECK-NEXT: return
+! CHECK: scf.execute_region no_inline {
+! CHECK: cf.br ^[[HEADER:.*]]
+! CHECK: ^[[HEADER]]:
+! CHECK: %{{.*}} = fir.load %[[ITER_VAR]] : !fir.ref<i32>
+! CHECK: cf.cond_br %{{.*}}, ^[[BODY:.*]], ^[[EXIT:.*]]
+! CHECK: ^[[BODY]]:
+! CHECK-NEXT: %{{.*}} = fir.alloca !fir.logical<4> {bindc_name = "success", {{.*}}}
+! CHECK: ^[[EXIT]]:
+! CHECK-NEXT: scf.yield
+! CHECK: }
+! CHECK: return
diff --git a/flang/test/Lower/fail_image.f90 b/flang/test/Lower/fail_image.f90
index ae5152907c2ba..549f00e8d62cb 100644
--- a/flang/test/Lower/fail_image.f90
+++ b/flang/test/Lower/fail_image.f90
@@ -5,19 +5,20 @@
subroutine fail_image_test(fail)
logical :: fail
! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %[[ARG0]]
-! CHECK: %[[VAL_2:.*]] = fir.load %[[VAL_1]]#0 : !fir.ref<!fir.logical<4>>
-! CHECK: %[[VAL_3:.*]] = fir.convert %[[VAL_2]] : (!fir.logical<4>) -> i1
-! CHECK: cf.cond_br %[[VAL_3]], ^[[BB1:.*]], ^[[BB2:.*]]
-! CHECK: ^[[BB1]]:
+! CHECK: scf.execute_region no_inline {
+! CHECK: %[[VAL_2:.*]] = fir.load %[[VAL_1]]#0 : !fir.ref<!fir.logical<4>>
+! CHECK: %[[VAL_3:.*]] = fir.convert %[[VAL_2]] : (!fir.logical<4>) -> i1
+! CHECK: cf.cond_br %[[VAL_3]], ^[[BB1:.*]], ^[[BB2:.*]]
+! CHECK: ^[[BB1]]:
if (fail) then
-! CHECK: fir.call @_FortranAFailImageStatement() {{.*}}: () -> ()
+! CHECK: fir.call @_FortranAFailImageStatement() {{.*}}: () -> ()
! CHECK-NEXT: fir.unreachable
FAIL IMAGE
end if
-! CHECK: ^[[BB2]]:
-! CHECK-NEXT: cf.br ^[[BB3:.*]]
-! CHECK-NEXT: ^[[BB3]]:
-! CHECK-NEXT: return
+! CHECK: ^[[BB2]]:
+! CHECK-NEXT: scf.yield
+! CHECK: }
+! CHECK: return
return
end subroutine
! CHECK-LABEL: func.func private @_FortranAFailImageStatement() attributes {fir.runtime}
diff --git a/flang/test/Lower/ifconvert.f90 b/flang/test/Lower/ifconvert.f90
index cbd1e79bd0114..256bb37c4e19f 100644
--- a/flang/test/Lower/ifconvert.f90
+++ b/flang/test/Lower/ifconvert.f90
@@ -41,7 +41,7 @@
! CHECK: 15 PrintStmt: print*
print*
- ! CHECK:<<DoConstruct!>> -> 30
+ ! CHECK:<<DoConstruct>> -> 30
! CHECK: 16 NonLabelDoStmt -> 29: outer: do i = 1, 3
! CHECK: <<DoConstruct!>> -> 29
! CHECK: 17 ^NonLabelDoStmt -> 28: inner: do j = 1, 5
@@ -62,7 +62,7 @@
! CHECK: 28 ^EndDoStmt -> 17: 3 end do inner
! CHECK: <<End DoConstruct!>>
! CHECK: 29 ^EndDoStmt -> 16: end do outer
- ! CHECK:<<End DoConstruct!>>
+ ! CHECK:<<End DoConstruct>>
outer: do i = 1, 3
inner: do j = 1, 5
if (j <= 1 .or. j >= 5) cycle inner
@@ -72,7 +72,7 @@
3 end do inner
end do outer
- ! CHECK: 30 ^PrintStmt: print*
+ ! CHECK: 30 PrintStmt: print*
print*
! CHECK:<<DoConstruct>> -> 40
diff --git a/flang/test/Lower/mixed_loops.f90 b/flang/test/Lower/mixed_loops.f90
index 11534ae9490d0..2a5aa40ff22cd 100644
--- a/flang/test/Lower/mixed_loops.f90
+++ b/flang/test/Lower/mixed_loops.f90
@@ -1,63 +1,47 @@
! RUN: %flang_fc1 -emit-hlfir -o - %s | FileCheck %s
! Test while loop inside do loop.
+! With the wrap-unstructured-constructs-in-execute-region pass, the inner
+! `do while` is the only unstructured construct: it gets wrapped, and the
+! outer counted `do` folds back to fir.do_loop.
! CHECK-LABEL: while_inside_do_loop
subroutine while_inside_do_loop
- ! CHECK-DAG: %[[T_REF:.*]] = fir.alloca i32
! CHECK-DAG: %[[I_ADDR:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFwhile_inside_do_loopEi"}
! CHECK-DAG: %[[I:.*]]:2 = hlfir.declare %[[I_ADDR]]
! CHECK-DAG: %[[J_ADDR:.*]] = fir.alloca i32 {bindc_name = "j", uniq_name = "_QFwhile_inside_do_loopEj"}
! CHECK-DAG: %[[J:.*]]:2 = hlfir.declare %[[J_ADDR]]
integer :: i, j
- ! CHECK-DAG: %[[C8:.*]] = arith.constant 8 : i32
- ! CHECK-DAG: %[[C13:.*]] = arith.constant 13 : i32
- ! CHECK-DAG: %[[C1:.*]] = arith.constant 1 : i32
- ! CHECK: %[[DIFF:.*]] = arith.subi %[[C13]], %[[C8]] : i32
- ! CHECK: %[[RANGE:.*]] = arith.addi %[[DIFF]], %[[C1]] : i32
- ! CHECK: %[[HIGH:.*]] = arith.divsi %[[RANGE]], %[[C1]] : i32
- ! CHECK: fir.store %[[HIGH]] to %[[T_REF]] : !fir.ref<i32>
- ! CHECK: fir.store %[[C8]] to %[[I]]#0 : !fir.ref<i32>
-
- ! CHECK: cf.br ^[[HDR1:.*]]
- ! CHECK: ^[[HDR1]]: // 2 preds: ^{{.*}}, ^[[EXIT2:.*]]
- ! CHECK: %[[T:.*]] = fir.load %[[T_REF]] : !fir.ref<i32>
- ! CHECK: %[[C0:.*]] = arith.constant 0 : i32
- ! CHECK: %[[COND:.*]] = arith.cmpi sgt, %[[T]], %[[C0]] : i32
- ! CHECK: cf.cond_br %[[COND]], ^[[BODY1:.*]], ^[[EXIT1:.*]]
+ ! CHECK: fir.do_loop %{{[^ ]+}} = %{{.*}} to %{{.*}} step %{{.*}} iter_args(%[[I_IV:.*]] = %{{.*}}) -> (i32) {
+ ! CHECK: fir.store %[[I_IV]] to %[[I]]#0 : !fir.ref<i32>
do i=8,13
- ! CHECK: ^[[BODY1]]: // pred: ^[[HDR1]]
- ! CHECK: %[[C3:.*]] = arith.constant 3 : i32
- ! CHECK: hlfir.assign %[[C3]] to %[[J]]#0 : i32, !fir.ref<i32>
+ ! CHECK: %[[C3:.*]] = arith.constant 3 : i32
+ ! CHECK: hlfir.assign %[[C3]] to %[[J]]#0 : i32, !fir.ref<i32>
j=3
- ! CHECK: cf.br ^[[HDR2:.*]]
- ! CHECK: ^[[HDR2]]: // 2 preds: ^[[BODY1]], ^[[BODY2:.*]]
- ! CHECK: %[[JVAL:.*]] = fir.load %[[J]]#0 : !fir.ref<i32>
- ! CHECK: %[[IVAL:.*]] = fir.load %[[I]]#0 : !fir.ref<i32>
- ! CHECK: %[[COND2:.*]] = arith.cmpi slt, %[[JVAL]], %[[IVAL]] : i32
- ! CHECK: cf.cond_br %[[COND2]], ^[[BODY2:.*]], ^[[EXIT2]]
+ ! CHECK: scf.execute_region no_inline {
+ ! CHECK: cf.br ^[[HDR2:.*]]
+ ! CHECK: ^[[HDR2]]: // 2 preds: ^{{.*}}, ^[[BODY2:.*]]
+ ! CHECK: %[[JVAL:.*]] = fir.load %[[J]]#0 : !fir.ref<i32>
+ ! CHECK: %[[IVAL:.*]] = fir.load %[[I]]#0 : !fir.ref<i32>
+ ! CHECK: %[[COND2:.*]] = arith.cmpi slt, %[[JVAL]], %[[IVAL]] : i32
+ ! CHECK: cf.cond_br %[[COND2]], ^[[BODY2]], ^[[EXIT2:.*]]
do while (j .lt. i)
- ! CHECK: ^[[BODY2]]: // pred: ^[[HDR2]]
- ! CHECK: %[[C2:.*]] = arith.constant 2 : i32
- ! CHECK: %[[JVAL2:.*]] = fir.load %[[J]]#0 : !fir.ref<i32>
- ! CHECK: %[[INC2:.*]] = arith.muli %[[C2]], %[[JVAL2]] : i32
- ! CHECK: hlfir.assign %[[INC2]] to %[[J]]#0 : i32, !fir.ref<i32>
+ ! CHECK: ^[[BODY2]]: // pred: ^[[HDR2]]
+ ! CHECK: %[[C2:.*]] = arith.constant 2 : i32
+ ! CHECK: %[[JVAL2:.*]] = fir.load %[[J]]#0 : !fir.ref<i32>
+ ! CHECK: %[[INC2:.*]] = arith.muli %[[C2]], %[[JVAL2]] : i32
+ ! CHECK: hlfir.assign %[[INC2]] to %[[J]]#0 : i32, !fir.ref<i32>
j=j*2
- ! CHECK: cf.br ^[[HDR2]]
+ ! CHECK: cf.br ^[[HDR2]]
end do
-
- ! CHECK: ^[[EXIT2]]: // pred: ^[[HDR2]]
- ! CHECK: %[[T2:.*]] = fir.load %[[T_REF]] : !fir.ref<i32>
- ! CHECK: %[[TDEC:.*]] = arith.subi %[[T2]], {{.*}} : i32
- ! CHECK: fir.store %[[TDEC]] to %[[T_REF]]
- ! CHECK: %[[I3:.*]] = fir.load %[[I]]#0 : !fir.ref<i32>
- ! CHECK: %[[IINC:.*]] = arith.addi %[[I3]], {{.*}} overflow<nsw> : i32
- ! CHECK: fir.store %[[IINC]] to %[[I]]#0 : !fir.ref<i32>
- ! CHECK: cf.br ^[[HDR1]]
+ ! CHECK: ^[[EXIT2]]:
+ ! CHECK: scf.yield
+ ! CHECK: }
+ ! CHECK: fir.result %{{.*}} : i32
end do
+ ! CHECK: }
- ! CHECK: ^[[EXIT1]]: // pred: ^[[HDR1]]
! CHECK: %[[IPRINT:.*]] = fir.load %[[I]]#0 : !fir.ref<i32>
! CHECK: fir.call @_FortranAioOutputInteger32(%{{.*}}, %[[IPRINT]])
! CHECK: %[[JPRINT:.*]] = fir.load %[[J]]#0 : !fir.ref<i32>
@@ -78,37 +62,39 @@ subroutine do_inside_while_loop
! CHECK: hlfir.assign %[[C3]] to %[[J]]#0 : i32, !fir.ref<i32>
j=3
- ! CHECK: cf.br ^[[HDR1:.*]]
+ ! The outer `do while` is wrapped in scf.execute_region; the inner counted
+ ! `do` lowers as fir.do_loop inside the wrap.
+ ! CHECK: scf.execute_region no_inline {
+ ! CHECK: cf.br ^[[HDR1:.*]]
! CHECK: ^[[HDR1]]: // 2 preds: ^{{.*}}, ^[[BODY1:.*]]
- ! CHECK: %[[JVAL:.*]] = fir.load %[[J]]#0 : !fir.ref<i32>
- ! CHECK: %[[UL:.*]] = arith.constant 21 : i32
- ! CHECK: %[[COND:.*]] = arith.cmpi slt, %[[JVAL]], %[[UL]] : i32
- ! CHECK: cf.cond_br %[[COND]], ^[[BODY1]], ^[[EXIT1:.*]]
+ ! CHECK: %[[JVAL:.*]] = fir.load %[[J]]#0 : !fir.ref<i32>
+ ! CHECK: %[[UL:.*]] = arith.constant 21 : i32
+ ! CHECK: %[[COND:.*]] = arith.cmpi slt, %[[JVAL]], %[[UL]] : i32
+ ! CHECK: cf.cond_br %[[COND]], ^[[BODY1]], ^[[EXIT1:.*]]
do while (j .lt. 21)
! CHECK: ^[[BODY1]]: // pred: ^[[HDR1]]
- ! CHECK-DAG: %[[C8:.*]] = arith.constant 8 : i32
- ! CHECK-DAG: %[[C13:.*]] = arith.constant 13 : i32
- ! CHECK-DAG: %[[C1:.*]] = arith.constant 1 : index
- ! CHECK: %{{.*}} = fir.do_loop %{{.*}} = {{.*}} to {{.*}} step {{.*}} iter_args(%[[I_IV:.*]] = {{.*}}) -> (i32) {
- ! CHECK: fir.store %[[I_IV]] to %[[I]]#0 : !fir.ref<i32>
- ! CHECK: %[[C2:.*]] = arith.constant 2 : i32
- ! CHECK: %[[J2VAL:.*]] = fir.load %[[J]]#0 : !fir.ref<i32>
- ! CHECK: %[[JINC:.*]] = arith.muli %[[C2]], %[[J2VAL]] : i32
- ! CHECK: hlfir.assign %[[JINC]] to %[[J]]#0 : i32, !fir.ref<i32>
- ! CHECK: %[[I_IVLOAD:.*]] = fir.load %[[I]]#0 : !fir.ref<i32>
- ! CHECK: %[[I_IVINC:.*]] = arith.addi %[[I_IVLOAD]], {{.*}} overflow<nsw> : i32
- ! CHECK: fir.result %[[I_IVINC]] : i32
+ ! CHECK: %{{.*}} = fir.do_loop %{{.*}} = {{.*}} to {{.*}} step {{.*}} iter_args(%[[I_IV:.*]] = {{.*}}) -> (i32) {
+ ! CHECK: fir.store %[[I_IV]] to %[[I]]#0 : !fir.ref<i32>
+ ! CHECK: %[[C2:.*]] = arith.constant 2 : i32
+ ! CHECK: %[[J2VAL:.*]] = fir.load %[[J]]#0 : !fir.ref<i32>
+ ! CHECK: %[[JINC:.*]] = arith.muli %[[C2]], %[[J2VAL]] : i32
+ ! CHECK: hlfir.assign %[[JINC]] to %[[J]]#0 : i32, !fir.ref<i32>
+ ! CHECK: %[[I_IVLOAD:.*]] = fir.load %[[I]]#0 : !fir.ref<i32>
+ ! CHECK: %[[I_IVINC:.*]] = arith.addi %[[I_IVLOAD]], {{.*}} overflow<nsw> : i32
+ ! CHECK: fir.result %[[I_IVINC]] : i32
do i=8,13
j=j*2
- ! CHECK: fir.store %{{.*}} to %[[I]]#0 : !fir.ref<i32>
+ ! CHECK: fir.store %{{.*}} to %[[I]]#0 : !fir.ref<i32>
end do
- ! CHECK: cf.br ^[[HDR1]]
+ ! CHECK: cf.br ^[[HDR1]]
end do
+ ! CHECK: ^[[EXIT1]]:
+ ! CHECK: scf.yield
+ ! CHECK: }
- ! CHECK: ^[[EXIT1]]: // pred: ^[[HDR1]]
! CHECK: %[[IPRINT:.*]] = fir.load %[[I]]#0 : !fir.ref<i32>
! CHECK: fir.call @_FortranAioOutputInteger32(%{{.*}}, %[[IPRINT]])
! CHECK: %[[JPRINT:.*]] = fir.load %[[J]]#0 : !fir.ref<i32>
diff --git a/flang/test/Lower/nsw.f90 b/flang/test/Lower/nsw.f90
index e113c26a9dc80..4bc932ac38508 100644
--- a/flang/test/Lower/nsw.f90
+++ b/flang/test/Lower/nsw.f90
@@ -106,15 +106,16 @@ subroutine loop_params2(a,lb,ub,st)
! CHECK: %[[VAL_14:.*]] = fir.declare %{{.*}}i"} : (!fir.ref<i32>) -> !fir.ref<i32>
! CHECK: %[[VAL_16:.*]] = fir.declare %{{.*}}ii"} : (!fir.ref<f32>) -> !fir.ref<f32>
! CHECK: %[[VAL_17:.*]] = fir.declare %{{.*}}st"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32>
-! CHECK: %[[VAL_18:.*]] = fir.load %[[VAL_11]] : !fir.ref<i32>
-! CHECK: %[[VAL_20:.*]] = fir.load %[[VAL_12]] : !fir.ref<i32>
-! CHECK: %[[VAL_29:.*]] = arith.addi %[[VAL_18]], %[[VAL_5]] overflow<nsw> : i32
-! CHECK: %[[VAL_30:.*]] = fir.convert %[[VAL_29]] : (i32) -> f32
-! CHECK: %[[VAL_31:.*]] = arith.subi %[[VAL_20]], %[[VAL_5]] overflow<nsw> : i32
-! CHECK: %[[VAL_32:.*]] = fir.convert %[[VAL_31]] : (i32) -> f32
-! CHECK: %[[VAL_33:.*]] = fir.load %[[VAL_17]] : !fir.ref<i32>
-! CHECK: %[[VAL_34:.*]] = arith.muli %[[VAL_33]], %[[VAL_4]] overflow<nsw> : i32
-! CHECK: %[[VAL_35:.*]] = fir.convert %[[VAL_34]] : (i32) -> f32
+! CHECK: scf.execute_region no_inline {
+! CHECK: %[[VAL_18:.*]] = fir.load %[[VAL_11]] : !fir.ref<i32>
+! CHECK: %[[VAL_29:.*]] = arith.addi %[[VAL_18]], %[[VAL_5]] overflow<nsw> : i32
+! CHECK: %[[VAL_30:.*]] = fir.convert %[[VAL_29]] : (i32) -> f32
+! CHECK: %[[VAL_20:.*]] = fir.load %[[VAL_12]] : !fir.ref<i32>
+! CHECK: %[[VAL_31:.*]] = arith.subi %[[VAL_20]], %[[VAL_5]] overflow<nsw> : i32
+! CHECK: %[[VAL_32:.*]] = fir.convert %[[VAL_31]] : (i32) -> f32
+! CHECK: %[[VAL_33:.*]] = fir.load %[[VAL_17]] : !fir.ref<i32>
+! CHECK: %[[VAL_34:.*]] = arith.muli %[[VAL_33]], %[[VAL_4]] overflow<nsw> : i32
+! CHECK: %[[VAL_35:.*]] = fir.convert %[[VAL_34]] : (i32) -> f32
! CHECK: fir.store %[[VAL_35]] to %[[VAL_9]] : !fir.ref<f32>
! CHECK: %[[VAL_36:.*]] = arith.subf %[[VAL_32]], %[[VAL_30]] fastmath<contract> : f32
! CHECK: %[[VAL_37:.*]] = arith.addf %[[VAL_36]], %[[VAL_35]] fastmath<contract> : f32
diff --git a/flang/test/Lower/pre-fir-tree02.f90 b/flang/test/Lower/pre-fir-tree02.f90
index d61dc801eea60..e302fc377a337 100644
--- a/flang/test/Lower/pre-fir-tree02.f90
+++ b/flang/test/Lower/pre-fir-tree02.f90
@@ -306,7 +306,7 @@ subroutine sub2()
5 j = j + 1
6 i = i + j/2
- ! CHECK: <<DoConstruct!>>
+ ! CHECK: <<DoConstruct>>
do1: do k=1,10
! CHECK: <<DoConstruct!>>
do2: do l=5,20
@@ -317,7 +317,7 @@ subroutine sub2()
end do do2
! CHECK: <<End DoConstruct!>>
end do do1
- ! CHECK: <<End DoConstruct!>>
+ ! CHECK: <<End DoConstruct>>
! CHECK: PauseStmt
pause 7
diff --git a/flang/test/Lower/while_loop.f90 b/flang/test/Lower/while_loop.f90
index 8114394a39919..8f2291f74a8ed 100644
--- a/flang/test/Lower/while_loop.f90
+++ b/flang/test/Lower/while_loop.f90
@@ -11,6 +11,7 @@ subroutine simple_loop
! CHECK: hlfir.assign %[[C5]] to %[[I]]#0
i = 5
+ ! CHECK: scf.execute_region no_inline {
! CHECK: cf.br ^[[BB1:.*]]
! CHECK: ^[[BB1]]: // 2 preds: ^{{.*}}, ^[[BB2:.*]]
! CHECK: %[[IVAL:.*]] = fir.load %[[I]]#0 : !fir.ref<i32>
@@ -23,11 +24,13 @@ subroutine simple_loop
! CHECK: %[[INC:.*]] = arith.subi %[[IVAL2]], %[[C2]] : i32
! CHECK: hlfir.assign %[[INC]] to %[[I]]#0 : i32, !fir.ref<i32>
! CHECK: cf.br ^[[BB1]]
+ ! CHECK: ^[[BB3]]: // pred: ^[[BB1]]
+ ! CHECK: scf.yield
+ ! CHECK: }
do while (i .gt. 1)
i = i - 2
end do
- ! CHECK: ^[[BB3]]: // pred: ^[[BB1]]
! CHECK: %[[IVAL3:.*]] = fir.load %[[I]]#0 : !fir.ref<i32>
! CHECK: fir.call @_FortranAioOutputInteger32(%{{.*}}, %[[IVAL3]])
print *, i
@@ -46,8 +49,9 @@ subroutine while_inside_while_loop
! CHECK: hlfir.assign %[[C13]] to %[[I]]#0
i = 13
+ ! CHECK: scf.execute_region no_inline {
! CHECK: cf.br ^[[HDR1:.*]]
- ! CHECK: ^[[HDR1]]: // 2 preds: ^{{.*}}, ^[[EXIT2:.*]]
+ ! CHECK: ^[[HDR1]]: // 2 preds: ^{{.*}}, ^{{.*}}
! CHECK: %[[IVAL:.*]] = fir.load %[[I]]#0 : !fir.ref<i32>
! CHECK: %[[C8:.*]] = arith.constant 8 : i32
! CHECK: %[[COND:.*]] = arith.cmpi sgt, %[[IVAL]], %[[C8]] : i32
@@ -64,8 +68,9 @@ subroutine while_inside_while_loop
! CHECK: hlfir.assign %[[C3]] to %[[J]]#0
j = 3
+ ! CHECK: scf.execute_region no_inline {
! CHECK: cf.br ^[[HDR2:.*]]
- ! CHECK: ^[[HDR2]]: // 2 preds: ^[[BODY1]], ^[[BODY2:.*]]
+ ! CHECK: ^[[HDR2]]: // 2 preds: ^{{.*}}, ^[[BODY2:.*]]
! CHECK: %[[JVAL:.*]] = fir.load %[[J]]#0 : !fir.ref<i32>
! CHECK: %[[IVAL3:.*]] = fir.load %[[I]]#0 : !fir.ref<i32>
! CHECK: %[[COND2:.*]] = arith.cmpi slt, %[[JVAL]], %[[IVAL3]] : i32
@@ -81,10 +86,14 @@ subroutine while_inside_while_loop
end do
! CHECK: ^[[EXIT2]]: // pred: ^[[HDR2]]
+ ! CHECK: scf.yield
+ ! CHECK: }
! CHECK: cf.br ^[[HDR1]]
end do
! CHECK: ^[[EXIT1]]: // pred: ^[[HDR1]]
+ ! CHECK: scf.yield
+ ! CHECK: }
! CHECK: %[[IPRINT:.*]] = fir.load %[[I]]#0 : !fir.ref<i32>
! CHECK: fir.call @_FortranAioOutputInteger32(%{{.*}}, %[[IPRINT]])
! CHECK: %[[JPRINT:.*]] = fir.load %[[J]]#0 : !fir.ref<i32>
More information about the flang-commits
mailing list