[flang-commits] [flang] b0dc60f - [flang][PFT] Mark ASSIGN'd labels as assigned GO TO targets (#218674)

via flang-commits flang-commits at lists.llvm.org
Wed Aug 26 06:06:13 PDT 2026


Author: Kareem Ergawy
Date: 2026-08-26T15:06:07+02:00
New Revision: b0dc60f01ab0057eea54ce82ab5498118a60567c

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

LOG: [flang][PFT] Mark ASSIGN'd labels as assigned GO TO targets (#218674)

An assigned GO TO with an explicit label list only marked the listed
labels as branch targets. Lowering is more permissive: genFIR for
AssignedGotoStmt in flang/lib/Lower/Bridge.cpp builds the switch from
the labels ASSIGN'd to the variable and ignores the list.

A construct holding an ASSIGN'd label that the list omits therefore
looked wrappable, was placed in an scf.execute_region, and the branch
then crossed a region boundary:
```
  error: 'fir.select' op branching to block of a different region
```
Mark both sets so the wrappability analysis sees every escape.

Added: 
    flang/test/Lower/assigned-goto-target-in-loop.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 3d71a4dbf9b1b..c6c230af7cf58 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -2239,6 +2239,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     // Relax the requirement that the GOTO variable must have a value in the
     // label list when a list is present, and allow a branch to any non-format
     // target that has an ASSIGN statement for the variable.
+    //
+    // (Both PFT builder and MLIR lowering bridge apply the same relaxation)
     mlir::Location loc = toLocation();
     Fortran::lower::pft::Evaluation &eval = getEval();
     Fortran::lower::pft::FunctionLikeUnit &owningProc =

diff  --git a/flang/lib/Lower/PFTBuilder.cpp b/flang/lib/Lower/PFTBuilder.cpp
index a967073c0e8a9..351ace166c8c0 100644
--- a/flang/lib/Lower/PFTBuilder.cpp
+++ b/flang/lib/Lower/PFTBuilder.cpp
@@ -1052,9 +1052,13 @@ class PFTBuilder {
             }
           },
           [&](const parser::AssignedGotoStmt &s) {
-            // Mark every possible target of the assigned GO TO so that
-            // wrappability analyses can see any escape from an enclosing
-            // construct.
+            // See Fortran 90 Clause 8.2.4.
+            // Relax the requirement that the GOTO variable must have a value in
+            // the label list when a list is present, and allow a branch to any
+            // non-format target that has an ASSIGN statement for the variable.
+            //
+            // (Both PFT builder and MLIR lowering bridge apply the same
+            // relaxation)
             auto markIfBranchTarget = [&](parser::Label label) {
               assert(label && "missing branch target label");
               auto iter{labelEvaluationMap->find(label)};
@@ -1065,24 +1069,16 @@ class PFTBuilder {
               if (semanticsContext.IsRecordedBranchTarget(target->position))
                 markBranchTarget(eval, *target);
             };
-            const auto &labelList = std::get<std::list<parser::Label>>(s.t);
-            if (!labelList.empty()) {
-              // Explicit target list: `go to v, (l1, l2, ...)`.
-              for (const auto &label : labelList)
-                markIfBranchTarget(label);
-            } else {
-              // No explicit list (`go to v`): fall back to the set of labels
-              // that have been previously ASSIGN'd to v.
-              // TODO: This may miss assignments that appear later in program
-              // order, but it matches the information available at this point
-              // in the walk.
-              const auto *sym = std::get<parser::Name>(s.t).symbol;
-              if (sym) {
-                auto iter = assignSymbolLabelMap->find(*sym);
-                if (iter != assignSymbolLabelMap->end())
-                  for (auto label : iter->second)
-                    markIfBranchTarget(label);
-              }
+            for (const auto &label : std::get<std::list<parser::Label>>(s.t))
+              markIfBranchTarget(label);
+            // TODO: This may miss assignments that appear later in program
+            // order, but it matches the information available at this point in
+            // the walk.
+            if (const auto *sym = std::get<parser::Name>(s.t).symbol) {
+              auto iter = assignSymbolLabelMap->find(*sym);
+              if (iter != assignSymbolLabelMap->end())
+                for (auto label : iter->second)
+                  markIfBranchTarget(label);
             }
             eval.isUnstructured = true;
             markSuccessorAsNewBlock(eval);

diff  --git a/flang/test/Lower/assigned-goto-target-in-loop.f90 b/flang/test/Lower/assigned-goto-target-in-loop.f90
new file mode 100644
index 0000000000000..858a6f98a330d
--- /dev/null
+++ b/flang/test/Lower/assigned-goto-target-in-loop.f90
@@ -0,0 +1,27 @@
+! RUN: %flang_fc1 -emit-hlfir -mmlir --wrap-unstructured-constructs-in-execute-region -o - %s | \
+! RUN:   FileCheck %s --implicit-check-not=scf.execute_region
+
+! An assigned GO TO may branch to any non-FORMAT label that has been ASSIGN'd to
+! the variable, even one absent from the statement's explicit label list; see the
+! relaxation in genFIR(AssignedGotoStmt) in flang/lib/Lower/Bridge.cpp.  Such a
+! target can sit inside a DO construct, as label 20 does here, so the construct
+! must not be wrapped in scf.execute_region -- the branch would then cross a
+! region boundary and the fir.select would fail verification with "branching to
+! block of a 
diff erent region".
+
+subroutine assigned_goto_into_loop(a, n)
+  integer :: n, i, k
+  real :: a(n)
+  do i = 1, n
+     assign 20 to k
+20   a(i) = a(i) + 1.0
+  end do
+  go to k, (30, 40)
+30 a(1) = 0.0
+40 continue
+end subroutine
+
+! CHECK-LABEL: func.func @_QPassigned_goto_into_loop(
+! The switch admits label 20, which is ASSIGN'd but absent from the (30, 40)
+! list.  Compiling at all shows its target block is in the branch's own region.
+! CHECK: fir.select %{{.*}} : i32 [20, ^bb{{[0-9]+}}, unit, ^bb{{[0-9]+}}]


        


More information about the flang-commits mailing list