[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