[flang-commits] [flang] [flang][PFT-to-MLIR] Wrap unstructured Fortran constructs in scf.execute_region (PR #208635)

via flang-commits flang-commits at lists.llvm.org
Fri Jul 10 00:12:28 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-openacc

@llvm/pr-subscribers-flang-fir-hlfir

Author: Kareem Ergawy (ergawy)

<details>
<summary>Changes</summary>

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@<!-- -->anthropic.com>

---

Patch is 96.06 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/208635.diff


27 Files Affected:

- (modified) flang/include/flang/Lower/PFTBuilder.h (+10) 
- (modified) flang/lib/Lower/Bridge.cpp (+101-2) 
- (modified) flang/lib/Lower/PFTBuilder.cpp (+234-2) 
- (modified) flang/lib/Lower/Runtime.cpp (+6-2) 
- (modified) flang/test/HLFIR/assumed_shape_with_value_keyword.f90 (+10-7) 
- (modified) flang/test/HLFIR/optional_dummy.f90 (+12-11) 
- (modified) flang/test/Integration/OpenMP/parallel-private-reduction-worstcase.f90 (+2-1) 
- (modified) flang/test/Lower/HLFIR/intrinsic-subroutines.f90 (+22-19) 
- (modified) flang/test/Lower/MIF/change_team2.f90 (+15-12) 
- (modified) flang/test/Lower/OpenACC/Todo/acc-unstructured-combined-construct.f90 (+2-62) 
- (modified) flang/test/Lower/OpenACC/Todo/do-loops-to-acc-loops-todo.f90 (-19) 
- (modified) flang/test/Lower/OpenACC/acc-terminator.f90 (+2-1) 
- (modified) flang/test/Lower/OpenACC/acc-unstructured.f90 (+177-24) 
- (modified) flang/test/Lower/OpenMP/loop-compound.f90 (+17-13) 
- (modified) flang/test/Lower/OpenMP/parallel-reduction3.f90 (+16-13) 
- (modified) flang/test/Lower/OpenMP/stop-stmt-in-region.f90 (+37-32) 
- (modified) flang/test/Lower/OpenMP/unstructured.f90 (+103-102) 
- (modified) flang/test/Lower/OpenMP/wsloop-unstructured.f90 (+17-18) 
- (modified) flang/test/Lower/branching-directive.f90 (+42-3) 
- (added) flang/test/Lower/do_loop_execute_region_wrap.f90 (+111) 
- (modified) flang/test/Lower/do_loop_unstructured.f90 (+38-87) 
- (modified) flang/test/Lower/fail_image.f90 (+10-9) 
- (modified) flang/test/Lower/ifconvert.f90 (+3-3) 
- (modified) flang/test/Lower/mixed_loops.f90 (+47-61) 
- (modified) flang/test/Lower/nsw.f90 (+10-9) 
- (modified) flang/test/Lower/pre-fir-tree02.f90 (+2-2) 
- (modified) flang/test/Lower/while_loop.f90 (+12-3) 


``````````diff
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(%[[...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/208635


More information about the flang-commits mailing list