[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