[llvm] [SimplifyCFG] Simplify uncond br with icmp & select (PR #165580)

Kunqiu Chen via llvm-commits llvm-commits at lists.llvm.org
Sat Nov 8 04:04:24 PST 2025


https://github.com/Camsyn updated https://github.com/llvm/llvm-project/pull/165580

>From ffc7b77221848d94e50c8dad30bbb7bcd8c500d5 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Wed, 29 Oct 2025 23:23:40 +0800
Subject: [PATCH 01/10] [SimplifyCFG] Simplify uncond br with icmp & select

---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 177 +++++++++++++++++++++-
 1 file changed, 175 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 4fac5d36ddb3f..f21a32fdb240d 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -301,7 +301,9 @@ class SimplifyCFGOpt {
 
   bool tryToSimplifyUncondBranchWithICmpInIt(ICmpInst *ICI,
                                              IRBuilder<> &Builder);
-
+  bool tryToSimplifyUncondBranchWithICmpSelectInIt(ICmpInst *ICI,
+                                                   SelectInst *Select,
+                                                   IRBuilder<> &Builder);
   bool hoistCommonCodeFromSuccessors(Instruction *TI, bool AllInstsEqOnly);
   bool hoistSuccIdenticalTerminatorToSwitchOrIf(
       Instruction *TI, Instruction *I1,
@@ -5116,6 +5118,172 @@ bool SimplifyCFGOpt::tryToSimplifyUncondBranchWithICmpInIt(
   return true;
 }
 
+/// Similar to tryToSimplifyUncondBranchWithICmpInIt, but handle a more generic
+/// case. This is called when we find an icmp instruction (a seteq/setne with a
+/// constant) and its following select instruction as the only TWO instruction
+/// in a block that ends with an uncond branch.  We are looking for a very
+/// specific pattern that occurs when "  
+///    if (A == 1) return C1;
+///    if (A == 2) return C2;
+///    if (A < 3) return C3;
+///    return C4;
+/// " gets simplified.  In this case, we merge the first two "branches of icmp"
+/// into a switch, but then the default value goes to an uncond block with a lt
+/// icmp and select in it, as InstCombine can not simplify "A < 3" as "A == 2".
+/// After SimplifyCFG and other subsequent optimizations (e.g., SCCP), we might
+/// get something like:
+///
+/// case1:
+///   switch i8 %A, label %DEFAULT [ i8 0, label %end    i8 1, label %case2 ]
+/// case2:
+///   br label %end
+/// DEFAULT:
+///   %tmp = icmp eq i8 %A, 2
+///   %val = select i1 %tmp, i8 C3, i8 C4
+///   br label %end
+/// end:
+///   _ = phi i8 [ C1, %case1 ], [ C2, %case2 ], [ %val, %DEFAULT ]
+///
+/// We prefer to split the edge to 'end' so that there are TWO entries of V3/V4
+/// to the PHI, merging the icmp & select into the switch, as follows:
+///
+/// case1:
+///   switch i8 %A, label %DEFAULT [
+///     i8 0, label %end
+///     i8 1, label %case2
+///     i8 2, label %case3
+///   ]
+/// case2:
+///   br label %end
+/// case3:
+///   br label %end
+/// DEFAULT:
+///   br label %end
+/// end:
+///   _ = phi i8 [ C1, %case1 ], [ C2, %case2 ], [ C3, %case2 ], [ C4, %DEFAULT]
+bool SimplifyCFGOpt::tryToSimplifyUncondBranchWithICmpSelectInIt(
+    ICmpInst *ICI, SelectInst *Select, IRBuilder<> &Builder) {
+  BasicBlock *BB = ICI->getParent();
+
+  // If the block has any PHIs in it or the icmp/select has multiple uses, it is too
+  // complex.
+  if (isa<PHINode>(BB->begin()) || !ICI->hasOneUse() || !Select->hasOneUse())
+    return false;
+
+  // The pattern we're looking for is where our only predecessor is a switch on
+  // 'V' and this block is the default case for the switch.  In this case we can
+  // fold the compared value into the switch to simplify things.
+  BasicBlock *Pred = BB->getSinglePredecessor();
+  if (!Pred || !isa<SwitchInst>(Pred->getTerminator()))
+    return false;
+  
+  Value *IcmpCond;
+  ConstantInt *NewCaseVal;
+  CmpPredicate Predicate;
+
+  // Match icmp X, C
+  if (!match(ICI, m_ICmp(Predicate, m_Value(IcmpCond), m_ConstantInt(NewCaseVal))))
+    return false;
+
+  Value *SelectCond, *SelectTrueVal, *SelectFalseVal;
+  // Match select Cond, TrueVal, FalseVal
+  if (!match(Select, m_Select(m_Value(SelectCond), m_Value(SelectTrueVal),
+                              m_Value(SelectFalseVal))))
+    return false;
+
+  // Check if the select condition is the same as the icmp condition.
+  if (SelectCond != ICI)
+    return false;
+
+  SwitchInst *SI = cast<SwitchInst>(Pred->getTerminator());
+  if (SI->getCondition() != IcmpCond)
+    return false;
+
+  // If BB is reachable on a non-default case, then we simply know the value of
+  // V in this block.  Substitute it and constant fold the icmp instruction
+  // away.
+  if (SI->getDefaultDest() != BB) {
+    ConstantInt *VVal = SI->findCaseDest(BB);
+    assert(VVal && "Should have a unique destination value");
+    ICI->setOperand(0, VVal);
+
+    if (Value *V = simplifyInstruction(ICI, {DL, ICI})) {
+      ICI->replaceAllUsesWith(V);
+      ICI->eraseFromParent();
+    }
+    // BB is now empty, so it is likely to simplify away.
+    return requestResimplify();
+  }
+
+  // Ok, the block is reachable from the default dest.  If the constant we're
+  // comparing exists in one of the other edges, then we can constant fold ICI
+  // and zap it.
+  if (SI->findCaseValue(NewCaseVal) != SI->case_default()) {
+    Value *V;
+    if (Predicate == ICmpInst::ICMP_EQ)
+      V = ConstantInt::getFalse(BB->getContext());
+    else
+      V = ConstantInt::getTrue(BB->getContext());
+
+    ICI->replaceAllUsesWith(V);
+    ICI->eraseFromParent();
+    // BB is now empty, so it is likely to simplify away.
+    return requestResimplify();
+  }
+
+  // The use of the select has to be in the 'end' block, by the only PHI node in
+  // the block.
+  BasicBlock *SuccBlock = BB->getTerminator()->getSuccessor(0);
+  PHINode *PHIUse = dyn_cast<PHINode>(Select->user_back());
+  if (PHIUse == nullptr || PHIUse != &SuccBlock->front() ||
+      isa<PHINode>(++BasicBlock::iterator(PHIUse)))
+    return false;
+
+  // If the icmp is a SETEQ, then the default dest gets SelectFalseVal, the new edge gets
+  // SelectTrueVal in the PHI.
+  Value *DefaultCst = SelectFalseVal;
+  Value *NewCst = SelectTrueVal;
+
+  if (ICI->getPredicate() == ICmpInst::ICMP_NE)
+    std::swap(DefaultCst, NewCst);
+
+  // Replace Select (which is used by the PHI for the default value) with
+  // SelectFalseVal or SelectTrueVal depending on if ICI is EQ or NE.
+  Select->replaceAllUsesWith(DefaultCst);
+  Select->eraseFromParent();
+  ICI->eraseFromParent();
+
+  SmallVector<DominatorTree::UpdateType, 2> Updates;
+
+  // Okay, the switch goes to this block on a default value.  Add an edge from
+  // the switch to the merge point on the compared value.
+  BasicBlock *NewBB =
+      BasicBlock::Create(BB->getContext(), "switch.edge", BB->getParent(), BB);
+  {
+    SwitchInstProfUpdateWrapper SIW(*SI);
+    auto W0 = SIW.getSuccessorWeight(0);
+    SwitchInstProfUpdateWrapper::CaseWeightOpt NewW;
+    if (W0) {
+      NewW = ((uint64_t(*W0) + 1) >> 1);
+      SIW.setSuccessorWeight(0, *NewW);
+    }
+    SIW.addCase(NewCaseVal, NewBB, NewW);
+    if (DTU)
+      Updates.push_back({DominatorTree::Insert, Pred, NewBB});
+  }
+
+  // NewBB branches to the phi block, add the uncond branch and the phi entry.
+  Builder.SetInsertPoint(NewBB);
+  Builder.SetCurrentDebugLocation(SI->getDebugLoc());
+  Builder.CreateBr(SuccBlock);
+  PHIUse->addIncoming(NewCst, NewBB);
+  if (DTU) {
+    Updates.push_back({DominatorTree::Insert, NewBB, SuccBlock});
+    DTU->applyUpdates(Updates);
+  }
+  return true;
+}
+
 /// The specified branch is a conditional branch.
 /// Check to see if it is branching on an or/and chain of icmp instructions, and
 /// fold it into a switch instruction if so.
@@ -8167,13 +8335,18 @@ bool SimplifyCFGOpt::simplifyUncondBranch(BranchInst *BI,
 
   // If the only instruction in the block is a seteq/setne comparison against a
   // constant, try to simplify the block.
-  if (ICmpInst *ICI = dyn_cast<ICmpInst>(I))
+  if (ICmpInst *ICI = dyn_cast<ICmpInst>(I)) {
     if (ICI->isEquality() && isa<ConstantInt>(ICI->getOperand(1))) {
       ++I;
       if (I->isTerminator() &&
           tryToSimplifyUncondBranchWithICmpInIt(ICI, Builder))
         return true;
+      if (isa<SelectInst>(I) && I->getNextNode()->isTerminator() &&
+          tryToSimplifyUncondBranchWithICmpSelectInIt(ICI, cast<SelectInst>(I),
+                                                      Builder))
+        return true;
     }
+  }
 
   // See if we can merge an empty landing pad block with another which is
   // equivalent.

>From 7a37f17ed38be33664be73e00f295df037ad3c5c Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Wed, 29 Oct 2025 23:59:04 +0800
Subject: [PATCH 02/10] Make formatter happy

---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index f21a32fdb240d..bbef4c83152dd 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5165,8 +5165,8 @@ bool SimplifyCFGOpt::tryToSimplifyUncondBranchWithICmpSelectInIt(
     ICmpInst *ICI, SelectInst *Select, IRBuilder<> &Builder) {
   BasicBlock *BB = ICI->getParent();
 
-  // If the block has any PHIs in it or the icmp/select has multiple uses, it is too
-  // complex.
+  // If the block has any PHIs in it or the icmp/select has multiple uses, it is
+  // too complex.
   if (isa<PHINode>(BB->begin()) || !ICI->hasOneUse() || !Select->hasOneUse())
     return false;
 
@@ -5176,13 +5176,14 @@ bool SimplifyCFGOpt::tryToSimplifyUncondBranchWithICmpSelectInIt(
   BasicBlock *Pred = BB->getSinglePredecessor();
   if (!Pred || !isa<SwitchInst>(Pred->getTerminator()))
     return false;
-  
+
   Value *IcmpCond;
   ConstantInt *NewCaseVal;
   CmpPredicate Predicate;
 
   // Match icmp X, C
-  if (!match(ICI, m_ICmp(Predicate, m_Value(IcmpCond), m_ConstantInt(NewCaseVal))))
+  if (!match(ICI,
+             m_ICmp(Predicate, m_Value(IcmpCond), m_ConstantInt(NewCaseVal))))
     return false;
 
   Value *SelectCond, *SelectTrueVal, *SelectFalseVal;
@@ -5239,8 +5240,8 @@ bool SimplifyCFGOpt::tryToSimplifyUncondBranchWithICmpSelectInIt(
       isa<PHINode>(++BasicBlock::iterator(PHIUse)))
     return false;
 
-  // If the icmp is a SETEQ, then the default dest gets SelectFalseVal, the new edge gets
-  // SelectTrueVal in the PHI.
+  // If the icmp is a SETEQ, then the default dest gets SelectFalseVal, the new
+  // edge gets SelectTrueVal in the PHI.
   Value *DefaultCst = SelectFalseVal;
   Value *NewCst = SelectTrueVal;
 

>From 8dbdccd70dd852254eb8787b23690b5cb2dbb31a Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Thu, 30 Oct 2025 22:25:08 +0800
Subject: [PATCH 03/10] Regenerate some tests

---
 .../Transforms/SimplifyCFG/ARM/switch-to-lookup-table.ll     | 4 ++--
 .../Transforms/SimplifyCFG/switch-transformations-no-lut.ll  | 5 ++---
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/llvm/test/Transforms/SimplifyCFG/ARM/switch-to-lookup-table.ll b/llvm/test/Transforms/SimplifyCFG/ARM/switch-to-lookup-table.ll
index 6def8f4eeb089..a51b816846cdc 100644
--- a/llvm/test/Transforms/SimplifyCFG/ARM/switch-to-lookup-table.ll
+++ b/llvm/test/Transforms/SimplifyCFG/ARM/switch-to-lookup-table.ll
@@ -15,8 +15,8 @@
 ; DISABLE-NOT: @{{.*}} = private unnamed_addr constant [3 x ptr] [ptr @c1, ptr @c2, ptr @c3]
 ; ENABLE:      @{{.*}} = private unnamed_addr constant [3 x ptr] [ptr @g1, ptr @g2, ptr @g3]
 ; DISABLE-NOT: @{{.*}} = private unnamed_addr constant [3 x ptr] [ptr @g1, ptr @g2, ptr @g3]
-; ENABLE:      @{{.*}} = private unnamed_addr constant [3 x ptr] [ptr @f1, ptr @f2, ptr @f3]
-; DISABLE-NOT: @{{.*}} = private unnamed_addr constant [3 x ptr] [ptr @f1, ptr @f2, ptr @f3]
+; ENABLE:      @{{.*}} = private unnamed_addr constant [4 x ptr] [ptr @f1, ptr @f2, ptr @f3, ptr @f4]
+; DISABLE-NOT: @{{.*}} = private unnamed_addr constant [4 x ptr] [ptr @f1, ptr @f2, ptr @f3, ptr @f4]
 
 target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
 target triple = "armv7a--none-eabi"
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll b/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
index 25267dcc6dbcb..48be76c19e48f 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
@@ -410,13 +410,12 @@ define i1 @single_value_with_mask(i32 %x) {
 ; OPTNOLUT-NEXT:      i32 21, label %[[END]]
 ; OPTNOLUT-NEXT:      i32 48, label %[[END]]
 ; OPTNOLUT-NEXT:      i32 16, label %[[END]]
+; OPTNOLUT-NEXT:      i32 80, label %[[END]]
 ; OPTNOLUT-NEXT:    ]
 ; OPTNOLUT:       [[DEFAULT]]:
-; OPTNOLUT-NEXT:    [[CMP:%.*]] = icmp eq i32 [[X]], 80
-; OPTNOLUT-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], i1 false, i1 true
 ; OPTNOLUT-NEXT:    br label %[[END]]
 ; OPTNOLUT:       [[END]]:
-; OPTNOLUT-NEXT:    [[RES:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ false, %[[ENTRY]] ], [ false, %[[ENTRY]] ], [ false, %[[ENTRY]] ], [ [[SEL]], %[[DEFAULT]] ]
+; OPTNOLUT-NEXT:    [[RES:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ false, %[[ENTRY]] ], [ false, %[[ENTRY]] ], [ false, %[[ENTRY]] ], [ true, %[[DEFAULT]] ], [ false, %[[ENTRY]] ]
 ; OPTNOLUT-NEXT:    ret i1 [[RES]]
 ;
 ; TTINOLUT-LABEL: define i1 @single_value_with_mask(

>From 14dc5c207aa328fd4b82393390e7200afd171619 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Thu, 30 Oct 2025 22:30:10 +0800
Subject: [PATCH 04/10] Make formatter happy

---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index bbef4c83152dd..927d17cfc6d2c 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5122,7 +5122,7 @@ bool SimplifyCFGOpt::tryToSimplifyUncondBranchWithICmpInIt(
 /// case. This is called when we find an icmp instruction (a seteq/setne with a
 /// constant) and its following select instruction as the only TWO instruction
 /// in a block that ends with an uncond branch.  We are looking for a very
-/// specific pattern that occurs when "  
+/// specific pattern that occurs when "
 ///    if (A == 1) return C1;
 ///    if (A == 2) return C2;
 ///    if (A < 3) return C3;

>From 3ecd2868a5f581b9d58276effd8e20932d5b4834 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Wed, 5 Nov 2025 00:55:36 +0800
Subject: [PATCH 05/10] Refactor to follow the review comments

---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 144 +++++-----------------
 1 file changed, 29 insertions(+), 115 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 927d17cfc6d2c..a9756b09a104e 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5013,109 +5013,9 @@ bool SimplifyCFGOpt::simplifyIndirectBrOnSelect(IndirectBrInst *IBI,
 /// the PHI, merging the third icmp into the switch.
 bool SimplifyCFGOpt::tryToSimplifyUncondBranchWithICmpInIt(
     ICmpInst *ICI, IRBuilder<> &Builder) {
-  BasicBlock *BB = ICI->getParent();
-
-  // If the block has any PHIs in it or the icmp has multiple uses, it is too
-  // complex.
-  if (isa<PHINode>(BB->begin()) || !ICI->hasOneUse())
-    return false;
-
-  Value *V = ICI->getOperand(0);
-  ConstantInt *Cst = cast<ConstantInt>(ICI->getOperand(1));
-
-  // The pattern we're looking for is where our only predecessor is a switch on
-  // 'V' and this block is the default case for the switch.  In this case we can
-  // fold the compared value into the switch to simplify things.
-  BasicBlock *Pred = BB->getSinglePredecessor();
-  if (!Pred || !isa<SwitchInst>(Pred->getTerminator()))
-    return false;
-
-  SwitchInst *SI = cast<SwitchInst>(Pred->getTerminator());
-  if (SI->getCondition() != V)
-    return false;
-
-  // If BB is reachable on a non-default case, then we simply know the value of
-  // V in this block.  Substitute it and constant fold the icmp instruction
-  // away.
-  if (SI->getDefaultDest() != BB) {
-    ConstantInt *VVal = SI->findCaseDest(BB);
-    assert(VVal && "Should have a unique destination value");
-    ICI->setOperand(0, VVal);
-
-    if (Value *V = simplifyInstruction(ICI, {DL, ICI})) {
-      ICI->replaceAllUsesWith(V);
-      ICI->eraseFromParent();
-    }
-    // BB is now empty, so it is likely to simplify away.
-    return requestResimplify();
-  }
-
-  // Ok, the block is reachable from the default dest.  If the constant we're
-  // comparing exists in one of the other edges, then we can constant fold ICI
-  // and zap it.
-  if (SI->findCaseValue(Cst) != SI->case_default()) {
-    Value *V;
-    if (ICI->getPredicate() == ICmpInst::ICMP_EQ)
-      V = ConstantInt::getFalse(BB->getContext());
-    else
-      V = ConstantInt::getTrue(BB->getContext());
-
-    ICI->replaceAllUsesWith(V);
-    ICI->eraseFromParent();
-    // BB is now empty, so it is likely to simplify away.
-    return requestResimplify();
-  }
-
-  // The use of the icmp has to be in the 'end' block, by the only PHI node in
-  // the block.
-  BasicBlock *SuccBlock = BB->getTerminator()->getSuccessor(0);
-  PHINode *PHIUse = dyn_cast<PHINode>(ICI->user_back());
-  if (PHIUse == nullptr || PHIUse != &SuccBlock->front() ||
-      isa<PHINode>(++BasicBlock::iterator(PHIUse)))
-    return false;
-
-  // If the icmp is a SETEQ, then the default dest gets false, the new edge gets
-  // true in the PHI.
-  Constant *DefaultCst = ConstantInt::getTrue(BB->getContext());
-  Constant *NewCst = ConstantInt::getFalse(BB->getContext());
-
-  if (ICI->getPredicate() == ICmpInst::ICMP_EQ)
-    std::swap(DefaultCst, NewCst);
-
-  // Replace ICI (which is used by the PHI for the default value) with true or
-  // false depending on if it is EQ or NE.
-  ICI->replaceAllUsesWith(DefaultCst);
-  ICI->eraseFromParent();
-
-  SmallVector<DominatorTree::UpdateType, 2> Updates;
-
-  // Okay, the switch goes to this block on a default value.  Add an edge from
-  // the switch to the merge point on the compared value.
-  BasicBlock *NewBB =
-      BasicBlock::Create(BB->getContext(), "switch.edge", BB->getParent(), BB);
-  {
-    SwitchInstProfUpdateWrapper SIW(*SI);
-    auto W0 = SIW.getSuccessorWeight(0);
-    SwitchInstProfUpdateWrapper::CaseWeightOpt NewW;
-    if (W0) {
-      NewW = ((uint64_t(*W0) + 1) >> 1);
-      SIW.setSuccessorWeight(0, *NewW);
-    }
-    SIW.addCase(Cst, NewBB, NewW);
-    if (DTU)
-      Updates.push_back({DominatorTree::Insert, Pred, NewBB});
-  }
-
-  // NewBB branches to the phi block, add the uncond branch and the phi entry.
-  Builder.SetInsertPoint(NewBB);
-  Builder.SetCurrentDebugLocation(SI->getDebugLoc());
-  Builder.CreateBr(SuccBlock);
-  PHIUse->addIncoming(NewCst, NewBB);
-  if (DTU) {
-    Updates.push_back({DominatorTree::Insert, NewBB, SuccBlock});
-    DTU->applyUpdates(Updates);
-  }
-  return true;
+  // Select == nullptr means we assume that there is a hidden no-op select
+  // instruction of _ = `select %icmp, true, false` just after `%icmp = ...`
+  return tryToSimplifyUncondBranchWithICmpSelectInIt(ICI, nullptr, Builder);
 }
 
 /// Similar to tryToSimplifyUncondBranchWithICmpInIt, but handle a more generic
@@ -5167,7 +5067,8 @@ bool SimplifyCFGOpt::tryToSimplifyUncondBranchWithICmpSelectInIt(
 
   // If the block has any PHIs in it or the icmp/select has multiple uses, it is
   // too complex.
-  if (isa<PHINode>(BB->begin()) || !ICI->hasOneUse() || !Select->hasOneUse())
+  if (isa<PHINode>(BB->begin()) || !ICI->hasOneUse() ||
+      (Select && !Select->hasOneUse()))
     return false;
 
   // The pattern we're looking for is where our only predecessor is a switch on
@@ -5187,14 +5088,23 @@ bool SimplifyCFGOpt::tryToSimplifyUncondBranchWithICmpSelectInIt(
     return false;
 
   Value *SelectCond, *SelectTrueVal, *SelectFalseVal;
-  // Match select Cond, TrueVal, FalseVal
-  if (!match(Select, m_Select(m_Value(SelectCond), m_Value(SelectTrueVal),
-                              m_Value(SelectFalseVal))))
-    return false;
-
-  // Check if the select condition is the same as the icmp condition.
-  if (SelectCond != ICI)
-    return false;
+  Instruction *User;
+  if (!Select) {
+    // If Select == nullptr, we can assume that there is a hidden no-op select
+    // just after icmp
+    SelectCond = ICI;
+    SelectTrueVal = Builder.getTrue();
+    SelectFalseVal = Builder.getFalse();
+    User = ICI->user_back();
+  } else {
+    SelectCond = Select->getCondition();
+    // Check if the select condition is the same as the icmp condition.
+    if (SelectCond != ICI)
+      return false;
+    SelectTrueVal = Select->getTrueValue();
+    SelectFalseVal = Select->getFalseValue();
+    User = Select->user_back();
+  }
 
   SwitchInst *SI = cast<SwitchInst>(Pred->getTerminator());
   if (SI->getCondition() != IcmpCond)
@@ -5235,7 +5145,7 @@ bool SimplifyCFGOpt::tryToSimplifyUncondBranchWithICmpSelectInIt(
   // The use of the select has to be in the 'end' block, by the only PHI node in
   // the block.
   BasicBlock *SuccBlock = BB->getTerminator()->getSuccessor(0);
-  PHINode *PHIUse = dyn_cast<PHINode>(Select->user_back());
+  PHINode *PHIUse = dyn_cast<PHINode>(User);
   if (PHIUse == nullptr || PHIUse != &SuccBlock->front() ||
       isa<PHINode>(++BasicBlock::iterator(PHIUse)))
     return false;
@@ -5250,8 +5160,12 @@ bool SimplifyCFGOpt::tryToSimplifyUncondBranchWithICmpSelectInIt(
 
   // Replace Select (which is used by the PHI for the default value) with
   // SelectFalseVal or SelectTrueVal depending on if ICI is EQ or NE.
-  Select->replaceAllUsesWith(DefaultCst);
-  Select->eraseFromParent();
+  if (!Select) {
+    Select->replaceAllUsesWith(DefaultCst);
+    Select->eraseFromParent();
+  } else {
+    ICI->replaceAllUsesWith(DefaultCst);
+  }
   ICI->eraseFromParent();
 
   SmallVector<DominatorTree::UpdateType, 2> Updates;

>From aaa7e2260f60e64d91f2b7834b870820615c9ad3 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Wed, 5 Nov 2025 15:40:28 +0800
Subject: [PATCH 06/10] Fix CI failures

---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index a9756b09a104e..3b8ce6a37f73e 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5160,7 +5160,7 @@ bool SimplifyCFGOpt::tryToSimplifyUncondBranchWithICmpSelectInIt(
 
   // Replace Select (which is used by the PHI for the default value) with
   // SelectFalseVal or SelectTrueVal depending on if ICI is EQ or NE.
-  if (!Select) {
+  if (Select) {
     Select->replaceAllUsesWith(DefaultCst);
     Select->eraseFromParent();
   } else {

>From b8d06c556b895ed6fa684beaafe06fb093065a16 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Sat, 8 Nov 2025 19:55:24 +0800
Subject: [PATCH 07/10] Add new tests

---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     |   3 +-
 .../Transforms/SimplifyCFG/switch_create.ll   | 140 ++++++++++++++++++
 2 files changed, 142 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 3b8ce6a37f73e..26b0099558ed0 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5014,7 +5014,7 @@ bool SimplifyCFGOpt::simplifyIndirectBrOnSelect(IndirectBrInst *IBI,
 bool SimplifyCFGOpt::tryToSimplifyUncondBranchWithICmpInIt(
     ICmpInst *ICI, IRBuilder<> &Builder) {
   // Select == nullptr means we assume that there is a hidden no-op select
-  // instruction of _ = `select %icmp, true, false` just after `%icmp = ...`
+  // instruction of `_ = select %icmp, true, false` just after `%icmp = icmp ...`
   return tryToSimplifyUncondBranchWithICmpSelectInIt(ICI, nullptr, Builder);
 }
 
@@ -5067,6 +5067,7 @@ bool SimplifyCFGOpt::tryToSimplifyUncondBranchWithICmpSelectInIt(
 
   // If the block has any PHIs in it or the icmp/select has multiple uses, it is
   // too complex.
+  /// TODO: support multi-phis in succ BB of select's BB.
   if (isa<PHINode>(BB->begin()) || !ICI->hasOneUse() ||
       (Select && !Select->hasOneUse()))
     return false;
diff --git a/llvm/test/Transforms/SimplifyCFG/switch_create.ll b/llvm/test/Transforms/SimplifyCFG/switch_create.ll
index ef5aee68e268e..d2f2c6851f2c8 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch_create.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch_create.ll
@@ -1314,6 +1314,146 @@ if.end:
   ret void
 }
 
+define i32 @switch_with_icmp_select_after_it(i32 %x) {
+; CHECK-LABEL: @switch_with_icmp_select_after_it(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    switch i32 [[X:%.*]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT:      i32 18, label [[END:%.*]]
+; CHECK-NEXT:      i32 21, label [[END]]
+; CHECK-NEXT:      i32 48, label [[END]]
+; CHECK-NEXT:      i32 16, label [[END]]
+; CHECK-NEXT:    ]
+; CHECK:       default:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[X]], 80
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], i32 2, i32 3
+; CHECK-NEXT:    br label [[END]]
+; CHECK:       end:
+; CHECK-NEXT:    [[RES:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ [[SEL]], [[DEFAULT]] ]
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+entry:
+  switch i32 %x, label %default [
+  i32 18, label %end
+  i32 21, label %end
+  i32 48, label %end
+  i32 16, label %end
+  ]
+default:
+  %cmp = icmp eq i32 %x, 80
+  ; Create a new switch case BB for case 80.
+  %sel = select i1 %cmp, i32 2, i32 3
+  br label %end
+end:
+  %res = phi i32 [ 1, %entry ], [ 1, %entry ], [ 1, %entry ], [ 1, %entry ], [ %sel, %default ]
+  ret i32 %res
+}
+
+define i32 @switch_with_icmp_select_after_it2(i32 %x) {
+; CHECK-LABEL: @switch_with_icmp_select_after_it2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    switch i32 [[X:%.*]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT:      i32 18, label [[END:%.*]]
+; CHECK-NEXT:      i32 21, label [[END]]
+; CHECK-NEXT:      i32 48, label [[END]]
+; CHECK-NEXT:      i32 16, label [[END]]
+; CHECK-NEXT:    ]
+; CHECK:       default:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[X]], 80
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], i32 1, i32 3
+; CHECK-NEXT:    br label [[END]]
+; CHECK:       end:
+; CHECK-NEXT:    [[RES:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ [[SEL]], [[DEFAULT]] ]
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+entry:
+  switch i32 %x, label %default [
+  i32 18, label %end
+  i32 21, label %end
+  i32 48, label %end
+  i32 16, label %end
+  ]
+default:
+  %cmp = icmp eq i32 %x, 80
+  ; Should not create new case BB
+  %sel = select i1 %cmp, i32 1, i32 3
+  br label %end
+end:
+  %res = phi i32 [ 1, %entry ], [ 1, %entry ], [ 1, %entry ], [ 1, %entry ], [ %sel, %default ]
+  ret i32 %res
+}
+
+define i32 @switch_with_icmp_select_after_it3(i32 %x) {
+; CHECK-LABEL: @switch_with_icmp_select_after_it3(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    switch i32 [[X:%.*]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT:      i32 18, label [[END:%.*]]
+; CHECK-NEXT:      i32 21, label [[END]]
+; CHECK-NEXT:      i32 48, label [[END]]
+; CHECK-NEXT:      i32 16, label [[END]]
+; CHECK-NEXT:    ]
+; CHECK:       default:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[X]], 80
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], i32 3, i32 1
+; CHECK-NEXT:    br label [[END]]
+; CHECK:       end:
+; CHECK-NEXT:    [[RES:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ [[SEL]], [[DEFAULT]] ]
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+entry:
+  switch i32 %x, label %default [
+  i32 18, label %end
+  i32 21, label %end
+  i32 48, label %end
+  i32 16, label %end
+  ]
+default:
+  %cmp = icmp eq i32 %x, 80
+  ; Should not create new case BB
+  %sel = select i1 %cmp, i32 3, i32 1
+  br label %end
+end:
+  %res = phi i32 [ 1, %entry ], [ 1, %entry ], [ 1, %entry ], [ 1, %entry ], [ %sel, %default ]
+  ret i32 %res
+}
+
+; TODO: support this case (multi-phis).
+define i32 @switch_with_icmp_select_after_it_multi_phis(i32 %x) {
+; CHECK-LABEL: @switch_with_icmp_select_after_it_multi_phis(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    switch i32 [[X:%.*]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT:      i32 18, label [[END:%.*]]
+; CHECK-NEXT:      i32 21, label [[END]]
+; CHECK-NEXT:      i32 48, label [[END]]
+; CHECK-NEXT:      i32 16, label [[END]]
+; CHECK-NEXT:    ]
+; CHECK:       default:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[X]], 80
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], i32 2, i32 3
+; CHECK-NEXT:    br label [[END]]
+; CHECK:       end:
+; CHECK-NEXT:    [[RES1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ 0, [[ENTRY]] ], [ 0, [[ENTRY]] ], [ 0, [[ENTRY]] ], [ 100, [[DEFAULT]] ]
+; CHECK-NEXT:    [[RES2:%.*]] = phi i32 [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ [[SEL]], [[DEFAULT]] ]
+; CHECK-NEXT:    [[RES:%.*]] = xor i32 [[RES1]], [[RES2]]
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+entry:
+  switch i32 %x, label %default [
+  i32 18, label %end
+  i32 21, label %end
+  i32 48, label %end
+  i32 16, label %end
+  ]
+default:
+  %cmp = icmp eq i32 %x, 80
+  %sel = select i1 %cmp, i32 2, i32 3
+  br label %end
+end:
+  %res1 = phi i32 [ 0, %entry ], [ 0, %entry ], [ 0, %entry ], [ 0, %entry ], [ 100, %default ]
+  %res2 = phi i32 [ 1, %entry ], [ 1, %entry ], [ 1, %entry ], [ 1, %entry ], [ %sel, %default ]
+  %res = xor i32 %res1, %res2
+  ret i32 %res
+}
+
 !0 = !{!"function_entry_count", i32 100}
 !1 = !{!"branch_weights", i32 6, i32 10}
 ;.

>From 79f0c928fec8e4d81cf4e9ad169479991538c5c8 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Sat, 8 Nov 2025 19:57:37 +0800
Subject: [PATCH 08/10] Regenerate the tests after patch

---
 .../Transforms/SimplifyCFG/switch_create.ll   | 26 ++++++-------------
 1 file changed, 8 insertions(+), 18 deletions(-)

diff --git a/llvm/test/Transforms/SimplifyCFG/switch_create.ll b/llvm/test/Transforms/SimplifyCFG/switch_create.ll
index d2f2c6851f2c8..64016f3a4b97c 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch_create.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch_create.ll
@@ -1322,13 +1322,14 @@ define i32 @switch_with_icmp_select_after_it(i32 %x) {
 ; CHECK-NEXT:      i32 21, label [[END]]
 ; CHECK-NEXT:      i32 48, label [[END]]
 ; CHECK-NEXT:      i32 16, label [[END]]
+; CHECK-NEXT:      i32 80, label [[SWITCH_EDGE:%.*]]
 ; CHECK-NEXT:    ]
+; CHECK:       switch.edge:
+; CHECK-NEXT:    br label [[END]]
 ; CHECK:       default:
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[X]], 80
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], i32 2, i32 3
 ; CHECK-NEXT:    br label [[END]]
 ; CHECK:       end:
-; CHECK-NEXT:    [[RES:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ [[SEL]], [[DEFAULT]] ]
+; CHECK-NEXT:    [[RES:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ 3, [[DEFAULT]] ], [ 2, [[SWITCH_EDGE]] ]
 ; CHECK-NEXT:    ret i32 [[RES]]
 ;
 entry:
@@ -1356,13 +1357,12 @@ define i32 @switch_with_icmp_select_after_it2(i32 %x) {
 ; CHECK-NEXT:      i32 21, label [[END]]
 ; CHECK-NEXT:      i32 48, label [[END]]
 ; CHECK-NEXT:      i32 16, label [[END]]
+; CHECK-NEXT:      i32 80, label [[END]]
 ; CHECK-NEXT:    ]
 ; CHECK:       default:
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[X]], 80
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], i32 1, i32 3
 ; CHECK-NEXT:    br label [[END]]
 ; CHECK:       end:
-; CHECK-NEXT:    [[RES:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ [[SEL]], [[DEFAULT]] ]
+; CHECK-NEXT:    [[RES:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ 3, [[DEFAULT]] ], [ 1, [[ENTRY]] ]
 ; CHECK-NEXT:    ret i32 [[RES]]
 ;
 entry:
@@ -1385,19 +1385,9 @@ end:
 define i32 @switch_with_icmp_select_after_it3(i32 %x) {
 ; CHECK-LABEL: @switch_with_icmp_select_after_it3(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    switch i32 [[X:%.*]], label [[DEFAULT:%.*]] [
-; CHECK-NEXT:      i32 18, label [[END:%.*]]
-; CHECK-NEXT:      i32 21, label [[END]]
-; CHECK-NEXT:      i32 48, label [[END]]
-; CHECK-NEXT:      i32 16, label [[END]]
-; CHECK-NEXT:    ]
-; CHECK:       default:
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[X]], 80
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[X:%.*]], 80
 ; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], i32 3, i32 1
-; CHECK-NEXT:    br label [[END]]
-; CHECK:       end:
-; CHECK-NEXT:    [[RES:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ 1, [[ENTRY]] ], [ [[SEL]], [[DEFAULT]] ]
-; CHECK-NEXT:    ret i32 [[RES]]
+; CHECK-NEXT:    ret i32 [[SEL]]
 ;
 entry:
   switch i32 %x, label %default [

>From 74f07b8edee3326453e12ab535830a4360ad9ea1 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Sat, 8 Nov 2025 19:59:43 +0800
Subject: [PATCH 09/10] Fix comments

---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 26b0099558ed0..48deef4f538be 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5020,7 +5020,7 @@ bool SimplifyCFGOpt::tryToSimplifyUncondBranchWithICmpInIt(
 
 /// Similar to tryToSimplifyUncondBranchWithICmpInIt, but handle a more generic
 /// case. This is called when we find an icmp instruction (a seteq/setne with a
-/// constant) and its following select instruction as the only TWO instruction
+/// constant) and its following select instruction as the only TWO instructions
 /// in a block that ends with an uncond branch.  We are looking for a very
 /// specific pattern that occurs when "
 ///    if (A == 1) return C1;

>From d4f28b0dce5ee3ced3db76447740823af74f77e0 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Sat, 8 Nov 2025 20:04:07 +0800
Subject: [PATCH 10/10] Make formatter happy

---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 48deef4f538be..b71c5b7f5b2f6 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5014,7 +5014,7 @@ bool SimplifyCFGOpt::simplifyIndirectBrOnSelect(IndirectBrInst *IBI,
 bool SimplifyCFGOpt::tryToSimplifyUncondBranchWithICmpInIt(
     ICmpInst *ICI, IRBuilder<> &Builder) {
   // Select == nullptr means we assume that there is a hidden no-op select
-  // instruction of `_ = select %icmp, true, false` just after `%icmp = icmp ...`
+  // instruction of `_ = select %icmp, true, false` after `%icmp = icmp ...`
   return tryToSimplifyUncondBranchWithICmpSelectInIt(ICI, nullptr, Builder);
 }
 



More information about the llvm-commits mailing list