[flang-commits] [flang] 28d621e - [flang] Lowering fortran structured do-while loops to `scf.while` (#177476)

via flang-commits flang-commits at lists.llvm.org
Tue Jan 27 07:16:00 PST 2026


Author: Susan Tan (ス-ザン タン)
Date: 2026-01-27T10:15:55-05:00
New Revision: 28d621e752f898643bb16beee9607b95dad1d753

URL: https://github.com/llvm/llvm-project/commit/28d621e752f898643bb16beee9607b95dad1d753
DIFF: https://github.com/llvm/llvm-project/commit/28d621e752f898643bb16beee9607b95dad1d753.diff

LOG: [flang] Lowering fortran structured do-while loops to `scf.while` (#177476)

WIP. Implemented structured do-while loops (i.e., the only control-flow
edge that can leave the outer loop is the one taken when the DO WHILE
condition becomes false) lowering to `scf.while`.

Added: 
    flang/test/Lower/do-while-to-scf-while.f90

Modified: 
    flang/lib/Lower/Bridge.cpp
    flang/lib/Lower/PFTBuilder.cpp

Removed: 
    


################################################################################
diff  --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 1d8d89776c33d..a82e266ee6912 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -67,6 +67,7 @@
 #include "flang/Support/Flags.h"
 #include "flang/Support/Version.h"
 #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
+#include "mlir/Dialect/SCF/IR/SCF.h"
 #include "mlir/IR/BuiltinAttributes.h"
 #include "mlir/IR/Matchers.h"
 #include "mlir/IR/PatternMatch.h"
@@ -1644,6 +1645,48 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     genConditionalBranch(cond, trueTarget->block, falseTarget->block);
   }
 
+  void
+  genDoWhileAsSCFWhile(const Fortran::parser::ScalarLogicalExpr &whileCondition,
+                       Fortran::lower::pft::Evaluation &doConstructEval,
+                       Fortran::lower::pft::Evaluation &doStmtEval) {
+    mlir::Location loc = toLocation();
+
+    auto scfWhile =
+        mlir::scf::WhileOp::create(*builder, loc,
+                                   /*resultTypes=*/mlir::TypeRange{},
+                                   /*inits=*/mlir::ValueRange{});
+
+    // Fill the "before" region: compute condition.
+    mlir::Block *beforeBlock =
+        builder->createBlock(&scfWhile.getBefore(), scfWhile.getBefore().end());
+    builder->setInsertionPointToStart(beforeBlock);
+    Fortran::lower::StatementContext stmtCtx;
+    mlir::Value cond = createFIRExpr(
+        loc, Fortran::semantics::GetExpr(whileCondition), stmtCtx);
+    stmtCtx.finalizeAndReset();
+    cond = builder->createConvert(loc, builder->getI1Type(), cond);
+    mlir::scf::ConditionOp::create(*builder, loc, cond, mlir::ValueRange{});
+
+    // Fill the "after" region: loop body.
+    mlir::Block *afterBlock =
+        builder->createBlock(&scfWhile.getAfter(), scfWhile.getAfter().end());
+    builder->setInsertionPointToStart(afterBlock);
+
+    // Lower nested evaluations excluding the loop control statement (the
+    // NonLabelDoStmt) and the EndDoStmt.
+    auto iter = doConstructEval.getNestedEvaluations().begin();
+    auto end = doConstructEval.getNestedEvaluations().end();
+    assert(iter != end && "malformed DoConstruct evaluation list");
+    ++iter; // skip the NonLabelDoStmt
+    assert(iter != end && "malformed DoConstruct evaluation list");
+    auto endDoIter = std::prev(end);
+    for (; iter != endDoIter; ++iter)
+      genFIR(*iter, /*unstructuredContext=*/false);
+
+    mlir::scf::YieldOp::create(*builder, loc);
+    builder->setInsertionPointAfter(scfWhile);
+  }
+
   /// Return the nearest active ancestor construct of \p eval, or nullptr.
   Fortran::lower::pft::Evaluation *
   getActiveAncestor(const Fortran::lower::pft::Evaluation &eval) {
@@ -2491,6 +2534,17 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     } else if ((whileCondition =
                     std::get_if<Fortran::parser::ScalarLogicalExpr>(
                         &loopControl->u))) {
+      // Optionally lower a restricted subset of DO WHILE loops directly to
+      // scf.while. This subset excludes early-exit constructs (EXIT/CYCLE/GOTO,
+      // etc.) by requiring that the loop body is structured (as decided by the
+      // PFT branch analysis), allowing the loop to exit only when the condition
+      // becomes false.
+      if (!unstructuredContext) {
+        maybeStartBlock(preheaderBlock); // no block or empty block
+        genDoWhileAsSCFWhile(*whileCondition, eval, doStmtEval);
+        return;
+      }
+
       assert(unstructuredContext && "while loop must be unstructured");
       maybeStartBlock(preheaderBlock); // no block or empty block
       startBlock(headerBlock);

diff  --git a/flang/lib/Lower/PFTBuilder.cpp b/flang/lib/Lower/PFTBuilder.cpp
index 308db726f073c..7e9f8b5de0827 100644
--- a/flang/lib/Lower/PFTBuilder.cpp
+++ b/flang/lib/Lower/PFTBuilder.cpp
@@ -28,6 +28,10 @@ static llvm::cl::opt<bool> clDisableStructuredFir(
 using namespace Fortran;
 
 namespace {
+static llvm::cl::opt<bool> lowerDoWhileToSCFWhile(
+    "lower-do-while-to-scf-while", llvm::cl::init(false),
+    llvm::cl::desc("lower structured DO WHILE loops to scf.while"),
+    llvm::cl::Hidden);
 /// Helpers to unveil parser node inside Fortran::parser::Statement<>,
 /// Fortran::parser::UnlabeledStatement, and Fortran::common::Indirection<>
 template <typename A>
@@ -1060,7 +1064,10 @@ class PFTBuilder {
                 eval.isUnstructured = true; // real-valued loop control
             } else if (std::get_if<parser::ScalarLogicalExpr>(
                            &loopControl->u)) {
-              eval.isUnstructured = true; // while loop
+              // Leave DO WHILE structured when -lower-do-while-to-scf-while is
+              // enabled; branch analysis will mark unstructured cases.
+              if (!lowerDoWhileToSCFWhile)
+                eval.isUnstructured = true; // while loop
             }
           },
           [&](const parser::EndDoStmt &) {

diff  --git a/flang/test/Lower/do-while-to-scf-while.f90 b/flang/test/Lower/do-while-to-scf-while.f90
new file mode 100644
index 0000000000000..d2f38d6e09694
--- /dev/null
+++ b/flang/test/Lower/do-while-to-scf-while.f90
@@ -0,0 +1,87 @@
+! RUN: bbc -emit-fir -hlfir=false -lower-do-while-to-scf-while %s -o - | FileCheck %s
+
+! CHECK-LABEL: func.func @_QPsimple_do_while()
+! CHECK: scf.while
+! CHECK: scf.condition
+! CHECK: scf.yield
+subroutine simple_do_while()
+  implicit none
+  integer :: i
+
+  i = 1
+  do while (i <= 10)
+    print *, "i =", i
+    i = i + 1
+  end do
+end subroutine simple_do_while
+
+! CHECK-LABEL: func.func @_QPdo_while_with_exit()
+! CHECK-NOT: scf.while
+! CHECK: cf.cond_br
+subroutine do_while_with_exit()
+  implicit none
+  integer :: i
+
+  i = 1
+  do while (i <= 10)
+    if (i == 5) exit
+    i = i + 1
+  end do
+end subroutine do_while_with_exit
+
+! CHECK-LABEL: func.func @_QPnested_do_while()
+! CHECK: scf.while
+! CHECK: scf.while
+subroutine nested_do_while()
+  implicit none
+  integer :: i, j
+
+  i = 1
+  do while (i <= 3)
+    j = 1
+    do while (j <= 2)
+      j = j + 1
+    end do
+    i = i + 1
+  end do
+end subroutine nested_do_while
+
+! CHECK-LABEL: func.func @_QPdo_while_goto_internal_forward()
+! CHECK: scf.while
+subroutine do_while_goto_internal_forward()
+  implicit none
+  integer :: i, sum
+
+  i = 0
+  sum = 0
+  do while (i < 10)
+    i = i + 1
+
+    if (mod(i, 2) == 0) goto 100
+    sum = sum + i
+
+100 continue
+  end do
+  print *, "sum=", sum
+end subroutine do_while_goto_internal_forward
+
+! CHECK-LABEL: func.func @_QPdo_while_goto_internal_backedge()
+! CHECK-NOT: scf.while
+! CHECK: cf.cond_br
+! CHECK: cf.br
+subroutine do_while_goto_internal_backedge()
+  implicit none
+  integer :: i, sum
+
+  i = 0
+  sum = 0
+  do while (i < 5)
+    i = i + 1
+
+10  continue
+    sum = sum + 1
+    if (sum < 3) goto 10
+  end do
+  print *, "sum=", sum
+end subroutine do_while_goto_internal_backedge
+


        


More information about the flang-commits mailing list