[llvm] [DFAJumpThreading] Do not thread over blocks with multiple phi definitions (PR #195512)
Hongyu Chen via llvm-commits
llvm-commits at lists.llvm.org
Sun May 3 02:00:42 PDT 2026
https://github.com/XChy created https://github.com/llvm/llvm-project/pull/195512
Fixes #195088
For the reduced case in the issue, there are 4 threading paths:
```
< then, case2, lbl_entry, switch_bb > [ 0, case2 ]
< case2, lbl_entry, switch_bb > [ 0, case2 ]
< then, case2, switch_bb > [ 1, case2 ]
< case2, switch_bb > [ 0, case2 ]
```
But the first path and the third path have a conflict: `then->case2` cannot be diverged into `then->case2.0` and `then->case2.1` at the same time, as jumping from `then` to `case2` does not really define a unique exiting state. Multiple phi definition causes two exiting states (0 and 1) for `then->case2`.
The root cause is that the block with multiple definitions cannot be regarded as a determinator.
>From 9b06f87e84e973e584c04ef8bec2c4c85948e03a Mon Sep 17 00:00:00 2001
From: XChy <xxs_chy at outlook.com>
Date: Sun, 3 May 2026 15:58:09 +0800
Subject: [PATCH 1/2] precommit tests
---
.../DFAJumpThreading/single-block-defs.ll | 68 +++++++++++++++++++
1 file changed, 68 insertions(+)
create mode 100644 llvm/test/Transforms/DFAJumpThreading/single-block-defs.ll
diff --git a/llvm/test/Transforms/DFAJumpThreading/single-block-defs.ll b/llvm/test/Transforms/DFAJumpThreading/single-block-defs.ll
new file mode 100644
index 0000000000000..2bd64857ce740
--- /dev/null
+++ b/llvm/test/Transforms/DFAJumpThreading/single-block-defs.ll
@@ -0,0 +1,68 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=dfa-jump-threading -dfa-max-out-use-blocks=5 %s | FileCheck %s
+
+define i64 @multiple-phi-def() {
+; CHECK-LABEL: define i64 @multiple-phi-def() {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LBL_ENTRY:.*]]
+; CHECK: [[LBL_ENTRY]]:
+; CHECK-NEXT: [[PHI3:%.*]] = phi i16 [ 0, %[[ENTRY]] ], [ poison, %[[CASE2:.*]] ], [ poison, %[[CASE2_JT1:.*]] ]
+; CHECK-NEXT: br i1 true, label %[[THEN:.*]], label %[[SWITCH_BB:.*]]
+; CHECK: [[LBL_ENTRY_JT0:.*]]:
+; CHECK-NEXT: [[PHI3_JT0:%.*]] = phi i16 [ [[PHI1_JT0:%.*]], %[[CASE2_JT0:.*]] ]
+; CHECK-NEXT: br i1 true, label %[[THEN]], label %[[SWITCH_BB_JT0:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: br label %[[CASE2_JT0]]
+; CHECK: [[CASE2]]:
+; CHECK-NEXT: br i1 true, label %[[SWITCH_BB]], label %[[LBL_ENTRY]]
+; CHECK: [[CASE2_JT1]]:
+; CHECK-NEXT: br i1 true, label %[[SWITCH_BB_JT1:.*]], label %[[LBL_ENTRY]]
+; CHECK: [[CASE2_JT0]]:
+; CHECK-NEXT: [[PHI1_JT0]] = phi i16 [ 0, %[[THEN]] ], [ 0, %[[SWITCH_BB]] ]
+; CHECK-NEXT: [[PHI2_JT0:%.*]] = phi i16 [ 1, %[[THEN]] ], [ 0, %[[SWITCH_BB]] ]
+; CHECK-NEXT: br i1 true, label %[[SWITCH_BB_JT0]], label %[[LBL_ENTRY_JT0]]
+; CHECK: [[SWITCH_BB]]:
+; CHECK-NEXT: [[PHI_OF_SWITCH:%.*]] = phi i16 [ [[PHI3]], %[[LBL_ENTRY]] ], [ poison, %[[CASE2]] ]
+; CHECK-NEXT: switch i16 [[PHI_OF_SWITCH]], label %[[DEFAULT_BB:.*]] [
+; CHECK-NEXT: i16 1, label %[[CASE1:.*]]
+; CHECK-NEXT: i16 2, label %[[CASE2_JT0]]
+; CHECK-NEXT: ]
+; CHECK: [[SWITCH_BB_JT1]]:
+; CHECK-NEXT: [[PHI_OF_SWITCH_JT1:%.*]] = phi i16 [ poison, %[[CASE2_JT1]] ]
+; CHECK-NEXT: br label %[[CASE1]]
+; CHECK: [[SWITCH_BB_JT0]]:
+; CHECK-NEXT: [[PHI_OF_SWITCH_JT0:%.*]] = phi i16 [ [[PHI2_JT0]], %[[CASE2_JT0]] ], [ [[PHI3_JT0]], %[[LBL_ENTRY_JT0]] ]
+; CHECK-NEXT: br label %[[DEFAULT_BB]]
+; CHECK: [[CASE1]]:
+; CHECK-NEXT: ret i64 1
+; CHECK: [[DEFAULT_BB]]:
+; CHECK-NEXT: ret i64 0
+;
+entry:
+ br label %lbl_entry
+
+lbl_entry: ; preds = %case2, %entry
+ %phi3 = phi i16 [ 0, %entry ], [ %phi1, %case2 ]
+ br i1 true, label %then, label %switch_bb
+
+then: ; preds = %lbl_entry
+ br label %case2
+
+case2: ; preds = %switch_bb, %then
+ %phi1 = phi i16 [ 0, %then ], [ 0, %switch_bb ]
+ %phi2 = phi i16 [ 1, %then ], [ 0, %switch_bb ]
+ br i1 true, label %switch_bb, label %lbl_entry
+
+switch_bb: ; preds = %case2, %lbl_entry
+ %phi_of_switch = phi i16 [ %phi3, %lbl_entry ], [ %phi2, %case2 ]
+ switch i16 %phi_of_switch, label %default_bb [
+ i16 1, label %case1
+ i16 2, label %case2
+ ]
+
+case1: ; preds = %switch_bb
+ ret i64 1
+
+default_bb: ; preds = %switch_bb
+ ret i64 0
+}
>From c78d1badf9166e0a688456a2cc9d9f1e717ef457 Mon Sep 17 00:00:00 2001
From: XChy <xxs_chy at outlook.com>
Date: Sun, 3 May 2026 16:53:09 +0800
Subject: [PATCH 2/2] [DFAJumpThreading] Do not thread over blocks with
multiple phi definitions
---
.../Transforms/Scalar/DFAJumpThreading.cpp | 19 ++++++++++++-
.../DFAJumpThreading/single-block-defs.ll | 27 +++++--------------
2 files changed, 25 insertions(+), 21 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp b/llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp
index 2be6c5ac2277d..1d54b52920495 100644
--- a/llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp
+++ b/llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp
@@ -60,6 +60,7 @@
#include "llvm/Transforms/Scalar/DFAJumpThreading.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Analysis/AssumptionCache.h"
@@ -756,6 +757,7 @@ struct AllSwitchPaths {
/// PHI nodes in those blocks that define the state.
StateDefMap getStateDefMap() const {
StateDefMap Res;
+ DenseSet<const BasicBlock *> MultipleDefBBs;
PHINode *FirstDef = dyn_cast<PHINode>(Switch->getOperand(0));
assert(FirstDef && "The first definition must be a phi.");
@@ -765,8 +767,12 @@ struct AllSwitchPaths {
while (!Stack.empty()) {
PHINode *CurPhi = Stack.pop_back_val();
+ BasicBlock *CurDefBlock = CurPhi->getParent();
+
+ auto [_, Inserted] = Res.try_emplace(CurDefBlock, CurPhi);
+ if (!Inserted)
+ MultipleDefBBs.insert(CurDefBlock);
- Res[CurPhi->getParent()] = CurPhi;
SeenValues.insert(CurPhi);
for (BasicBlock *IncomingBB : CurPhi->blocks()) {
@@ -782,6 +788,17 @@ struct AllSwitchPaths {
}
}
+ // NOTE: If multiple phi definitions exist in a block, we cannot
+ // thread the paths with such block by simple cloning. For example:
+ // < then, det, lbl_entry, switch_bb > [ 0, det ]
+ // < then, det, switch_bb > [ 1, det ]
+ // In this case, it is impossible to diverge then->det into then->det.0 and
+ // then->det.1 by simple path cloning.
+ for (auto *BB : MultipleDefBBs) {
+ LLVM_DEBUG(dbgs() << "Not a state-defining block: Multiple defs in "
+ << BB->getNameOrAsOperand() << "\n");
+ Res.erase(BB);
+ }
return Res;
}
diff --git a/llvm/test/Transforms/DFAJumpThreading/single-block-defs.ll b/llvm/test/Transforms/DFAJumpThreading/single-block-defs.ll
index 2bd64857ce740..a91d3af048bed 100644
--- a/llvm/test/Transforms/DFAJumpThreading/single-block-defs.ll
+++ b/llvm/test/Transforms/DFAJumpThreading/single-block-defs.ll
@@ -6,33 +6,20 @@ define i64 @multiple-phi-def() {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: br label %[[LBL_ENTRY:.*]]
; CHECK: [[LBL_ENTRY]]:
-; CHECK-NEXT: [[PHI3:%.*]] = phi i16 [ 0, %[[ENTRY]] ], [ poison, %[[CASE2:.*]] ], [ poison, %[[CASE2_JT1:.*]] ]
-; CHECK-NEXT: br i1 true, label %[[THEN:.*]], label %[[SWITCH_BB:.*]]
-; CHECK: [[LBL_ENTRY_JT0:.*]]:
-; CHECK-NEXT: [[PHI3_JT0:%.*]] = phi i16 [ [[PHI1_JT0:%.*]], %[[CASE2_JT0:.*]] ]
-; CHECK-NEXT: br i1 true, label %[[THEN]], label %[[SWITCH_BB_JT0:.*]]
+; CHECK-NEXT: [[PHI3:%.*]] = phi i16 [ 0, %[[ENTRY]] ], [ [[PHI1:%.*]], %[[CASE2_JT0:.*]] ]
+; CHECK-NEXT: br i1 true, label %[[THEN:.*]], label %[[SWITCH_BB_JT0:.*]]
; CHECK: [[THEN]]:
; CHECK-NEXT: br label %[[CASE2_JT0]]
-; CHECK: [[CASE2]]:
-; CHECK-NEXT: br i1 true, label %[[SWITCH_BB]], label %[[LBL_ENTRY]]
-; CHECK: [[CASE2_JT1]]:
-; CHECK-NEXT: br i1 true, label %[[SWITCH_BB_JT1:.*]], label %[[LBL_ENTRY]]
; CHECK: [[CASE2_JT0]]:
-; CHECK-NEXT: [[PHI1_JT0]] = phi i16 [ 0, %[[THEN]] ], [ 0, %[[SWITCH_BB]] ]
-; CHECK-NEXT: [[PHI2_JT0:%.*]] = phi i16 [ 1, %[[THEN]] ], [ 0, %[[SWITCH_BB]] ]
-; CHECK-NEXT: br i1 true, label %[[SWITCH_BB_JT0]], label %[[LBL_ENTRY_JT0]]
-; CHECK: [[SWITCH_BB]]:
-; CHECK-NEXT: [[PHI_OF_SWITCH:%.*]] = phi i16 [ [[PHI3]], %[[LBL_ENTRY]] ], [ poison, %[[CASE2]] ]
+; CHECK-NEXT: [[PHI1]] = phi i16 [ 0, %[[THEN]] ], [ 0, %[[SWITCH_BB_JT0]] ]
+; CHECK-NEXT: [[PHI2:%.*]] = phi i16 [ 1, %[[THEN]] ], [ 0, %[[SWITCH_BB_JT0]] ]
+; CHECK-NEXT: br i1 true, label %[[SWITCH_BB_JT0]], label %[[LBL_ENTRY]]
+; CHECK: [[SWITCH_BB_JT0]]:
+; CHECK-NEXT: [[PHI_OF_SWITCH:%.*]] = phi i16 [ [[PHI3]], %[[LBL_ENTRY]] ], [ [[PHI2]], %[[CASE2_JT0]] ]
; CHECK-NEXT: switch i16 [[PHI_OF_SWITCH]], label %[[DEFAULT_BB:.*]] [
; CHECK-NEXT: i16 1, label %[[CASE1:.*]]
; CHECK-NEXT: i16 2, label %[[CASE2_JT0]]
; CHECK-NEXT: ]
-; CHECK: [[SWITCH_BB_JT1]]:
-; CHECK-NEXT: [[PHI_OF_SWITCH_JT1:%.*]] = phi i16 [ poison, %[[CASE2_JT1]] ]
-; CHECK-NEXT: br label %[[CASE1]]
-; CHECK: [[SWITCH_BB_JT0]]:
-; CHECK-NEXT: [[PHI_OF_SWITCH_JT0:%.*]] = phi i16 [ [[PHI2_JT0]], %[[CASE2_JT0]] ], [ [[PHI3_JT0]], %[[LBL_ENTRY_JT0]] ]
-; CHECK-NEXT: br label %[[DEFAULT_BB]]
; CHECK: [[CASE1]]:
; CHECK-NEXT: ret i64 1
; CHECK: [[DEFAULT_BB]]:
More information about the llvm-commits
mailing list