[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