[llvm] [SimplifyCFG] Use range check in simplifyBranchOnICmpChain if possible (PR #165105)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 28 10:33:22 PDT 2025


https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/165105

>From a9be6d4fc253167524c473ce4a677dd2b640883a Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sat, 25 Oct 2025 23:43:30 +0800
Subject: [PATCH 1/2] [SimplifyCFG] Use range check in
 simplifyBranchOnICmpChain if possible

---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp    |  73 +++++---
 llvm/test/Transforms/SimplifyCFG/pr165088.ll | 186 +++++++++++++++++++
 2 files changed, 233 insertions(+), 26 deletions(-)
 create mode 100644 llvm/test/Transforms/SimplifyCFG/pr165088.ll

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index d831c2737e5f8..50272ddc46699 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5228,32 +5228,53 @@ bool SimplifyCFGOpt::simplifyBranchOnICmpChain(BranchInst *BI,
         CompVal, DL.getIntPtrType(CompVal->getType()), "magicptr");
   }
 
-  // Create the new switch instruction now.
-  SwitchInst *New = Builder.CreateSwitch(CompVal, DefaultBB, Values.size());
-  if (HasProfile) {
-    // We know the weight of the default case. We don't know the weight of the
-    // other cases, but rather than completely lose profiling info, we split
-    // the remaining probability equally over them.
-    SmallVector<uint32_t> NewWeights(Values.size() + 1);
-    NewWeights[0] = BranchWeights[1]; // this is the default, and we swapped if
-                                      // TrueWhenEqual.
-    for (auto &V : drop_begin(NewWeights))
-      V = BranchWeights[0] / Values.size();
-    setBranchWeights(*New, NewWeights, /*IsExpected=*/false);
-  }
-
-  // Add all of the 'cases' to the switch instruction.
-  for (ConstantInt *Val : Values)
-    New->addCase(Val, EdgeBB);
-
-  // We added edges from PI to the EdgeBB.  As such, if there were any
-  // PHI nodes in EdgeBB, they need entries to be added corresponding to
-  // the number of edges added.
-  for (BasicBlock::iterator BBI = EdgeBB->begin(); isa<PHINode>(BBI); ++BBI) {
-    PHINode *PN = cast<PHINode>(BBI);
-    Value *InVal = PN->getIncomingValueForBlock(BB);
-    for (unsigned i = 0, e = Values.size() - 1; i != e; ++i)
-      PN->addIncoming(InVal, BB);
+  // Check if we can represent the values can be represented as a contiguous
+  // range. If so, we use a range check + conditional branch instead of a
+  // switch.
+  if (Values.front()->getValue() - Values.back()->getValue() ==
+      Values.size() - 1) {
+    ConstantRange RangeToCheck = ConstantRange::getNonEmpty(
+        Values.back()->getValue(), Values.front()->getValue() + 1);
+    APInt Offset, RHS;
+    ICmpInst::Predicate Pred;
+    RangeToCheck.getEquivalentICmp(Pred, RHS, Offset);
+    Value *X = CompVal;
+    if (!Offset.isZero())
+      X = Builder.CreateAdd(X, ConstantInt::get(CompVal->getType(), Offset));
+    Value *Cond =
+        Builder.CreateICmp(Pred, X, ConstantInt::get(CompVal->getType(), RHS));
+    BranchInst *NewBI = Builder.CreateCondBr(Cond, EdgeBB, DefaultBB);
+    if (HasProfile)
+      setBranchWeights(*NewBI, BranchWeights, /*IsExpected=*/false);
+    // We don't need to update PHI nodes since we don't add any new edges.
+  } else {
+    // Create the new switch instruction now.
+    SwitchInst *New = Builder.CreateSwitch(CompVal, DefaultBB, Values.size());
+    if (HasProfile) {
+      // We know the weight of the default case. We don't know the weight of the
+      // other cases, but rather than completely lose profiling info, we split
+      // the remaining probability equally over them.
+      SmallVector<uint32_t> NewWeights(Values.size() + 1);
+      NewWeights[0] = BranchWeights[1]; // this is the default, and we swapped
+                                        // if TrueWhenEqual.
+      for (auto &V : drop_begin(NewWeights))
+        V = BranchWeights[0] / Values.size();
+      setBranchWeights(*New, NewWeights, /*IsExpected=*/false);
+    }
+
+    // Add all of the 'cases' to the switch instruction.
+    for (ConstantInt *Val : Values)
+      New->addCase(Val, EdgeBB);
+
+    // We added edges from PI to the EdgeBB.  As such, if there were any
+    // PHI nodes in EdgeBB, they need entries to be added corresponding to
+    // the number of edges added.
+    for (BasicBlock::iterator BBI = EdgeBB->begin(); isa<PHINode>(BBI); ++BBI) {
+      PHINode *PN = cast<PHINode>(BBI);
+      Value *InVal = PN->getIncomingValueForBlock(BB);
+      for (unsigned i = 0, e = Values.size() - 1; i != e; ++i)
+        PN->addIncoming(InVal, BB);
+    }
   }
 
   // Erase the old branch instruction.
diff --git a/llvm/test/Transforms/SimplifyCFG/pr165088.ll b/llvm/test/Transforms/SimplifyCFG/pr165088.ll
new file mode 100644
index 0000000000000..4514a1927b586
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/pr165088.ll
@@ -0,0 +1,186 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes="simplifycfg<switch-range-to-icmp>" < %s | FileCheck %s
+
+; Avoid getting stuck in the cycle pr165088_cycle_[1-4].
+
+define void @pr165088_cycle_1(i8 %x) {
+; CHECK-LABEL: define void @pr165088_cycle_1(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp ult i8 [[X]], 2
+; CHECK-NEXT:    br i1 [[TMP0]], label %[[BLOCK2:.*]], label %[[BLOCK3:.*]]
+; CHECK:       [[BLOCK1:.*]]:
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ugt i8 [[X]], 1
+; CHECK-NEXT:    br i1 [[COND2]], label %[[BLOCK3]], label %[[BLOCK2]]
+; CHECK:       [[BLOCK2]]:
+; CHECK-NEXT:    br label %[[BLOCK3]]
+; CHECK:       [[BLOCK3]]:
+; CHECK-NEXT:    [[COND3:%.*]] = icmp eq i8 [[X]], 0
+; CHECK-NEXT:    br i1 [[COND3]], label %[[EXIT:.*]], label %[[BLOCK1]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %switch = icmp uge i8 %x, 2
+  %cond1 = icmp ugt i8 %x, 1
+  %or.cond = and i1 %switch, %cond1
+  br i1 %or.cond, label %block3, label %block2
+
+block1:
+  %cond2 = icmp ugt i8 %x, 1
+  br i1 %cond2, label %block3, label %block2
+
+block2:
+  br label %block3
+
+block3:
+  %cond3 = icmp eq i8 %x, 0
+  br i1 %cond3, label %exit, label %block1
+
+exit:
+  ret void
+}
+
+define void @pr165088_cycle_2(i8 %x) {
+; CHECK-LABEL: define void @pr165088_cycle_2(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i8 [[X]], 2
+; CHECK-NEXT:    br i1 [[SWITCH]], label %[[BLOCK2:.*]], label %[[BLOCK3:.*]]
+; CHECK:       [[BLOCK1:.*]]:
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ugt i8 [[X]], 1
+; CHECK-NEXT:    br i1 [[COND2]], label %[[BLOCK3]], label %[[BLOCK2]]
+; CHECK:       [[BLOCK2]]:
+; CHECK-NEXT:    br label %[[BLOCK3]]
+; CHECK:       [[BLOCK3]]:
+; CHECK-NEXT:    [[COND3:%.*]] = icmp eq i8 [[X]], 0
+; CHECK-NEXT:    br i1 [[COND3]], label %[[EXIT:.*]], label %[[BLOCK1]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  switch i8 %x, label %block3 [
+  i8 1, label %block2
+  i8 0, label %block2
+  ]
+
+block1:                                              ; preds = %block3
+  %cond2 = icmp ugt i8 %x, 1
+  br i1 %cond2, label %block3, label %block2
+
+block2:                                              ; preds = %entry, %entry, %block1
+  br label %block3
+
+block3:                                              ; preds = %entry, %block2, %block1
+  %cond3 = icmp eq i8 %x, 0
+  br i1 %cond3, label %exit, label %block1
+
+exit:                                             ; preds = %block3
+  ret void
+}
+
+define void @pr165088_cycle_3(i8 %x) {
+; CHECK-LABEL: define void @pr165088_cycle_3(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    br label %[[BLOCK3:.*]]
+; CHECK:       [[BLOCK3]]:
+; CHECK-NEXT:    [[COND3:%.*]] = icmp eq i8 [[X]], 0
+; CHECK-NEXT:    br i1 [[COND3]], label %[[EXIT:.*]], label %[[BLOCK3]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  switch i8 %x, label %block1 [
+  i8 1, label %block2
+  i8 0, label %block2
+  ]
+
+block1:                                              ; preds = %entry, %block3
+  %cond2 = icmp ugt i8 %x, 1
+  br i1 %cond2, label %block3, label %block2
+
+block2:                                              ; preds = %entry, %entry, %block1
+  br label %block3
+
+block3:                                              ; preds = %block2, %block1
+  %cond3 = icmp eq i8 %x, 0
+  br i1 %cond3, label %exit, label %block1
+
+exit:                                             ; preds = %block3
+  ret void
+}
+
+define void @pr165088_cycle_4(i8 %x) {
+; CHECK-LABEL: define void @pr165088_cycle_4(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp ult i8 [[X]], 2
+; CHECK-NEXT:    br i1 [[TMP0]], label %[[BLOCK2:.*]], label %[[BLOCK3:.*]]
+; CHECK:       [[BLOCK1:.*]]:
+; CHECK-NEXT:    [[COND2_OLD:%.*]] = icmp ugt i8 [[X]], 1
+; CHECK-NEXT:    br i1 [[COND2_OLD]], label %[[BLOCK3]], label %[[BLOCK2]]
+; CHECK:       [[BLOCK2]]:
+; CHECK-NEXT:    br label %[[BLOCK3]]
+; CHECK:       [[BLOCK3]]:
+; CHECK-NEXT:    [[COND3:%.*]] = icmp eq i8 [[X]], 0
+; CHECK-NEXT:    br i1 [[COND3]], label %[[EXIT:.*]], label %[[BLOCK1]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %switch = icmp ult i8 %x, 2
+  br i1 %switch, label %block2, label %block1
+
+block1:                                              ; preds = %entry, %block3
+  %cond2 = icmp ugt i8 %x, 1
+  br i1 %cond2, label %block3, label %block2
+
+block2:                                              ; preds = %entry, %block1
+  br label %block3
+
+block3:                                              ; preds = %block2, %block1
+  %cond3 = icmp eq i8 %x, 0
+  br i1 %cond3, label %exit, label %block1
+
+exit:                                             ; preds = %block3
+  ret void
+}
+
+define void @pr165088_original(i8 %x) {
+; CHECK-LABEL: define void @pr165088_original(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp ult i8 [[X]], 2
+; CHECK-NEXT:    br i1 [[TMP0]], label %[[BLOCK2:.*]], label %[[BLOCK3:.*]]
+; CHECK:       [[BLOCK1:.*]]:
+; CHECK-NEXT:    [[COND3_OLD_OLD:%.*]] = icmp ugt i8 [[X]], 1
+; CHECK-NEXT:    br i1 [[COND3_OLD_OLD]], label %[[BLOCK3]], label %[[BLOCK2]]
+; CHECK:       [[BLOCK2]]:
+; CHECK-NEXT:    br label %[[BLOCK3]]
+; CHECK:       [[BLOCK3]]:
+; CHECK-NEXT:    [[COND4:%.*]] = icmp eq i8 [[X]], 0
+; CHECK-NEXT:    br i1 [[COND4]], label %[[EXIT:.*]], label %[[BLOCK1]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %cond = icmp ne i8 %x, 0
+  %cond3 = icmp ne i8 %x, 0
+  %or.cond = and i1 %cond, %cond3
+  br i1 %or.cond, label %block3, label %block2
+
+block1:                                              ; preds = %block3
+  %cond3.old = icmp ugt i8 %x, 1
+  br i1 %cond3.old, label %block3, label %block2
+
+block2:                                              ; preds = %block1, %entry
+  br label %block3
+
+block3:                                              ; preds = %block2, %block1, %entry
+  %cond4 = icmp eq i8 %x, 0
+  br i1 %cond4, label %exit, label %block1
+
+exit:                                             ; preds = %block3
+  ret void
+}

>From 10254d4f63bf3b1cc0ad94349f7414c2fba35bb3 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Wed, 29 Oct 2025 01:33:06 +0800
Subject: [PATCH 2/2] [SimplifyCFG] Fix comments. NFC.

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

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 50272ddc46699..3cf7750b25eb8 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5228,9 +5228,8 @@ bool SimplifyCFGOpt::simplifyBranchOnICmpChain(BranchInst *BI,
         CompVal, DL.getIntPtrType(CompVal->getType()), "magicptr");
   }
 
-  // Check if we can represent the values can be represented as a contiguous
-  // range. If so, we use a range check + conditional branch instead of a
-  // switch.
+  // Check if we can represent the values as a contiguous range. If so, we use a
+  // range check + conditional branch instead of a switch.
   if (Values.front()->getValue() - Values.back()->getValue() ==
       Values.size() - 1) {
     ConstantRange RangeToCheck = ConstantRange::getNonEmpty(



More information about the llvm-commits mailing list