[flang-commits] [flang] [flang] Keep a loop unstructured when its END DO is branched to from outside (PR #227597)

Kareem Ergawy via flang-commits flang-commits at lists.llvm.org
Wed Sep 30 01:07:43 PDT 2026


https://github.com/ergawy created https://github.com/llvm/llvm-project/pull/227597

A loop whose body branching is self-contained is lowered structurally, with the body folded into a region. Its END DO is not part of that body: it becomes the structured loop's terminator rather than a block of its own. A branch from outside the loop targeting a label on the END DO therefore had nothing to land on, and lowering crashed.

Such a loop no longer qualifies for that form. A branch from within the body is unaffected: it is CYCLE-like and lands on the boundary the structured form already provides.

>From 74a63eb67359ddadb79d30d7384bbc1096f00571 Mon Sep 17 00:00:00 2001
From: ergawy <kareem.ergawy at gmail.com>
Date: Wed, 30 Sep 2026 01:05:52 -0700
Subject: [PATCH] [flang] Keep a loop unstructured when its END DO is branched
 to from outside

A loop whose body branching is self-contained is lowered structurally, with the
body folded into a region. Its END DO is not part of that body: it becomes the
structured loop's terminator rather than a block of its own. A branch from
outside the loop targeting a label on the END DO therefore had nothing to land
on, and lowering crashed.

Such a loop no longer qualifies for that form. A branch from within the body is
unaffected: it is CYCLE-like and lands on the boundary the structured form
already provides.
---
 flang/lib/Lower/PFTBuilder.cpp                | 12 +++++
 .../test/Lower/do-loop-branch-to-loop-end.f90 | 47 +++++++++++++++++++
 2 files changed, 59 insertions(+)
 create mode 100644 flang/test/Lower/do-loop-branch-to-loop-end.f90

diff --git a/flang/lib/Lower/PFTBuilder.cpp b/flang/lib/Lower/PFTBuilder.cpp
index 2f665a6cbf693..d1fbf4e07e313 100644
--- a/flang/lib/Lower/PFTBuilder.cpp
+++ b/flang/lib/Lower/PFTBuilder.cpp
@@ -2987,6 +2987,18 @@ static bool isStructurableWithUnstructuredInternals(
     if (!check(e))
       return false;
 
+  // Condition 2 reaches the EndDoStmt too, with a wider notion of "outside". A
+  // branch from the body to it is CYCLE-like and lands on the wrap's boundary,
+  // which the structured form expresses. A branch from outside the loop is a
+  // different matter: the EndDoStmt is emitted as the structured loop's
+  // terminator, so no block is created for it and lowering has nothing to
+  // branch to.
+  if (auto it = unit.incomingBranches.find(&loop.evaluationList->back());
+      it != unit.incomingBranches.end())
+    for (const Fortran::lower::pft::Evaluation *src : it->second)
+      if (!isInLoopBody(src, loop))
+        return false;
+
   return true;
 }
 
diff --git a/flang/test/Lower/do-loop-branch-to-loop-end.f90 b/flang/test/Lower/do-loop-branch-to-loop-end.f90
new file mode 100644
index 0000000000000..7f71b91c15321
--- /dev/null
+++ b/flang/test/Lower/do-loop-branch-to-loop-end.f90
@@ -0,0 +1,47 @@
+! RUN: bbc -emit-fir -o - %s | FileCheck %s
+
+! A loop whose body branching is self-contained keeps its structured form, with
+! the body folded into an scf.execute_region. The EndDoStmt is not part of that
+! body: it is emitted as the structured loop's terminator, so no block is
+! created for it.
+!
+! That is fine for a branch from the body, which is CYCLE-like and lands on the
+! wrap's boundary. A branch from outside the loop has nowhere to land, so such a
+! loop stays unstructured and is lowered as raw blocks.
+
+! The GOTO targets the EndDoStmt from outside the loop.
+subroutine branch_to_loop_end(a)
+  real :: a(10)
+  integer :: i
+  do i = 1, 10
+     a(i) = 0.0
+20 end do
+  go to 20
+end subroutine
+
+! CHECK-LABEL: func.func @_QPbranch_to_loop_end
+! Raw blocks, so the branch has a block to target.
+! CHECK:         cf.br ^bb1
+! CHECK:       ^bb1:
+! CHECK-NOT:     fir.do_loop
+! CHECK-NOT:     scf.execute_region
+
+! The same branch from inside the body is CYCLE-like, so the loop keeps its
+! structured form. The ASSIGN makes a body statement a new block, so the body is
+! wrapped.
+subroutine cycle_to_loop_end(a, b)
+  real :: a(10), b(10)
+  integer :: i, m
+  do 42 i = 1, 10
+     assign 41 to m
+41   a(i) = b(i)
+     go to 42
+42 end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPcycle_to_loop_end
+! CHECK:         fir.do_loop
+! CHECK:           scf.execute_region no_inline {
+! CHECK:             scf.yield
+! CHECK:           }
+! CHECK:         }



More information about the flang-commits mailing list