[flang-commits] [flang] [flang][PFT] record every target of multiway branches (PR #210012)

Kareem Ergawy via flang-commits flang-commits at lists.llvm.org
Thu Jul 16 02:08:55 PDT 2026


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

The Evaluation node stored branch targets in a single `Evaluation *` (`controlSuccessor`). `markBranchTarget` therefore silently dropped every target after the first when lowering a computed `GO TO` or arithmetic `IF`, so any analysis that consulted `controlSuccessor` to enumerate branch targets saw only the first label.

Add `extraControlSuccessors` alongside
`controlSuccessor`. `markBranchTarget` still fills `controlSuccessor` with the first target, but appends each subsequent distinct target to `extraControlSuccessors`. Extend the PFT dumper to print the extra targets after the first ("-> N, M, K"), so PFT dumps expose every branch target.

>From a4728f462ae2e8da63b701836d33083e7d2d2f65 Mon Sep 17 00:00:00 2001
From: ergawy <kareem.ergawy at gmail.com>
Date: Tue, 14 Jul 2026 07:43:33 -0700
Subject: [PATCH] [flang][PFT] record every target of multiway branches

The Evaluation node stored branch targets in a single `Evaluation *`
(`controlSuccessor`). `markBranchTarget` therefore silently dropped every
target after the first when lowering a computed `GO TO` or arithmetic `IF`,
so any analysis that consulted `controlSuccessor` to enumerate branch
targets saw only the first label.

Add `extraControlSuccessors` alongside
`controlSuccessor`. `markBranchTarget` still fills `controlSuccessor` with the
first target, but appends each subsequent distinct target to
`extraControlSuccessors`. Extend the PFT dumper to print the extra targets
after the first ("-> N, M, K"), so PFT dumps expose every branch target.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
 flang/include/flang/Lower/PFTBuilder.h        | 15 +++++--
 flang/lib/Lower/PFTBuilder.cpp                | 18 ++++++--
 .../Lower/pre-fir-tree-multiway-branch.f90    | 41 +++++++++++++++++++
 flang/test/Lower/pre-fir-tree02.f90           |  4 +-
 4 files changed, 69 insertions(+), 9 deletions(-)
 create mode 100644 flang/test/Lower/pre-fir-tree-multiway-branch.f90

diff --git a/flang/include/flang/Lower/PFTBuilder.h b/flang/include/flang/Lower/PFTBuilder.h
index 0aabd8e642e44..29b859ff9ace4 100644
--- a/flang/include/flang/Lower/PFTBuilder.h
+++ b/flang/include/flang/Lower/PFTBuilder.h
@@ -330,10 +330,13 @@ struct Evaluation : EvaluationVariant {
   // nodes. Members such as lexicalSuccessor and block are applicable only
   // to these nodes, plus some directives. The controlSuccessor member is
   // used for nonlexical successors, such as linking to a GOTO target. For
-  // multiway branches, it is set to the first target. Successor and exit
-  // links always target statements or directives. An internal Construct
-  // node has a constructExit link that applies to exits from anywhere within
-  // the construct.
+  // multiway branches (computed GO TO, arithmetic IF), it is set to the
+  // first target and any additional targets are recorded in
+  // extraControlSuccessors so analyses that need to see every branch target
+  // (e.g. wrappability of an unstructured construct) can enumerate them all.
+  // Successor and exit links always target statements or directives. An
+  // internal Construct node has a constructExit link that applies to exits
+  // from anywhere within the construct.
   //
   // An unstructured construct is one that contains some form of goto. This
   // is indicated by the isUnstructured member flag, which may be set on a
@@ -365,6 +368,10 @@ struct Evaluation : EvaluationVariant {
   Evaluation *parentConstruct{nullptr};  // set for nodes below the top level
   Evaluation *lexicalSuccessor{nullptr}; // set for leaf nodes, some directives
   Evaluation *controlSuccessor{nullptr}; // set for some leaf nodes
+  // Additional branch targets for multiway branches (computed GO TO,
+  // arithmetic IF). Empty for single-target branches; the first target is in
+  // controlSuccessor and the remaining ones are stored here in source order.
+  llvm::SmallVector<Evaluation *, 0> extraControlSuccessors;
   Evaluation *constructExit{nullptr};    // set for constructs
   bool isNewBlock{false};                // evaluation begins a new basic block
   bool isUnstructured{false};  // evaluation has unstructured control flow
diff --git a/flang/lib/Lower/PFTBuilder.cpp b/flang/lib/Lower/PFTBuilder.cpp
index 0af0ff9e7d758..3a0c0c3d71075 100644
--- a/flang/lib/Lower/PFTBuilder.cpp
+++ b/flang/lib/Lower/PFTBuilder.cpp
@@ -853,6 +853,11 @@ class PFTBuilder {
     sourceEvaluation.isUnstructured = true;
     if (!sourceEvaluation.controlSuccessor)
       sourceEvaluation.controlSuccessor = &targetEvaluation;
+    else if (sourceEvaluation.controlSuccessor != &targetEvaluation &&
+             llvm::find(sourceEvaluation.extraControlSuccessors,
+                        &targetEvaluation) ==
+                 sourceEvaluation.extraControlSuccessors.end())
+      sourceEvaluation.extraControlSuccessors.push_back(&targetEvaluation);
     targetEvaluation.isNewBlock = true;
     // If this is a branch into the body of a construct (usually illegal,
     // but allowed in some legacy cases), then the targetEvaluation and its
@@ -1376,12 +1381,19 @@ class PFTDumper {
       outputStream << newBlock << name << bang;
     if (eval.negateCondition)
       outputStream << " [negate]";
-    if (eval.constructExit)
+    if (eval.constructExit) {
       outputStream << " -> " << eval.constructExit->printIndex;
-    else if (eval.controlSuccessor)
+    } else if (eval.controlSuccessor) {
       outputStream << " -> " << eval.controlSuccessor->printIndex;
-    else if (eval.isA<parser::EntryStmt>() && eval.lexicalSuccessor)
+      // Multiway branches (computed GO TO, arithmetic IF) put additional
+      // targets in extraControlSuccessors; print them so PFT dumps expose
+      // every target rather than only the first.
+      for (const Fortran::lower::pft::Evaluation *extra :
+           eval.extraControlSuccessors)
+        outputStream << ", " << extra->printIndex;
+    } else if (eval.isA<parser::EntryStmt>() && eval.lexicalSuccessor) {
       outputStream << " -> " << eval.lexicalSuccessor->printIndex;
+    }
     bool extraNewline = false;
     if (!eval.position.empty())
       outputStream << ": " << eval.position.ToString();
diff --git a/flang/test/Lower/pre-fir-tree-multiway-branch.f90 b/flang/test/Lower/pre-fir-tree-multiway-branch.f90
new file mode 100644
index 0000000000000..e9c8abebd253d
--- /dev/null
+++ b/flang/test/Lower/pre-fir-tree-multiway-branch.f90
@@ -0,0 +1,41 @@
+! RUN: bbc -pft-test -o %t %s | FileCheck %s
+
+! Verify that multiway branches (computed GO TO, arithmetic IF) record
+! every branch target on the source evaluation, not only the first one.
+! The dumper prints the first target after "->" and any additional targets
+! after commas. Repeated labels are deduplicated so an ArithmeticIfStmt
+! whose two branches share a label lists that label only once as an
+! extra successor.
+
+! CHECK-LABEL: Subroutine multi_target_goto
+subroutine multi_target_goto(sel)
+  integer :: sel
+  ! CHECK: ComputedGotoStmt{{.*}} -> {{[0-9]+}}, {{[0-9]+}}, {{[0-9]+}}
+  go to (10, 20, 30), sel
+10 print *, "one"
+20 print *, "two"
+30 print *, "three"
+end subroutine
+
+! CHECK-LABEL: Subroutine arith_if_three_distinct
+subroutine arith_if_three_distinct(x)
+  real :: x
+  ! CHECK: ArithmeticIfStmt{{.*}} -> {{[0-9]+}}, {{[0-9]+}}, {{[0-9]+}}
+  if (x) 10, 20, 30
+10 print *, "neg"
+20 print *, "zero"
+30 print *, "pos"
+end subroutine
+
+! CHECK-LABEL: Subroutine arith_if_repeated_label
+subroutine arith_if_repeated_label(x)
+  real :: x
+  ! The two negative/zero branches share label 10. Dedup means the source
+  ! lists exactly two distinct successors (10 and 20), not three -- the
+  ! trailing ':' anchors the check so a third comma-separated target would
+  ! fail the match.
+  ! CHECK: ArithmeticIfStmt{{.*}} -> {{[0-9]+}}, {{[0-9]+}}:
+  if (x) 10, 10, 20
+10 print *, "np"
+20 print *, "pos"
+end subroutine
diff --git a/flang/test/Lower/pre-fir-tree02.f90 b/flang/test/Lower/pre-fir-tree02.f90
index d61dc801eea60..bef9abfde87bd 100644
--- a/flang/test/Lower/pre-fir-tree02.f90
+++ b/flang/test/Lower/pre-fir-tree02.f90
@@ -290,7 +290,7 @@ subroutine sub2()
 2 continue
   i = i+1
 3 j = j+1
-! CHECK: ArithmeticIfStmt
+! CHECK: ArithmeticIfStmt{{.*}} -> {{[0-9]+}}, {{[0-9]+}}, {{[0-9]+}}
   if (j-i) 3, 4, 5
   ! CHECK: GotoStmt
 4  goto 6
@@ -301,7 +301,7 @@ subroutine sub2()
 ! WILLCHECK: AssignedGotoStmt
 !66  go to label (5, 6)
 
-! CHECK: ComputedGotoStmt
+! CHECK: ComputedGotoStmt{{.*}} -> {{[0-9]+}}, {{[0-9]+}}
   go to (5, 6), 1 + mod(i, 2)
 5 j = j + 1
 6 i = i + j/2



More information about the flang-commits mailing list