[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