[llvm] [SimplifyCFG] Fold the contiguous wrapping cases into ICmp. (PR #161000)

via llvm-commits llvm-commits at lists.llvm.org
Sun Oct 5 21:28:55 PDT 2025


https://github.com/dianqk updated https://github.com/llvm/llvm-project/pull/161000

>From dc8ea1ec5010a044548dd33cc53bd53190e4d29a Mon Sep 17 00:00:00 2001
From: dianqk <dianqk at dianqk.net>
Date: Sat, 27 Sep 2025 15:56:24 +0800
Subject: [PATCH 01/11] Pre-commit test cases

---
 .../SimplifyCFG/switch-range-to-icmp.ll       | 98 +++++++++++++++++++
 1 file changed, 98 insertions(+)

diff --git a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
index 8f2ae2d054f1e..73771b48e21bc 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
@@ -188,4 +188,102 @@ exit:
   ret void
 }
 
+define i32 @wrapping_known_range(i8 range(i8 0, 6) %arg) {
+; CHECK-LABEL: @wrapping_known_range(
+; CHECK-NEXT:    switch i8 [[ARG:%.*]], label [[ELSE:%.*]] [
+; CHECK-NEXT:      i8 0, label [[IF:%.*]]
+; CHECK-NEXT:      i8 4, label [[IF]]
+; CHECK-NEXT:      i8 5, label [[IF]]
+; CHECK-NEXT:    ]
+; CHECK:       common.ret:
+; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ [[I0:%.*]], [[IF]] ], [ [[I1:%.*]], [[ELSE]] ]
+; CHECK-NEXT:    ret i32 [[COMMON_RET_OP]]
+; CHECK:       if:
+; CHECK-NEXT:    [[I0]] = call i32 @f(i32 0)
+; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
+; CHECK:       else:
+; CHECK-NEXT:    [[I1]] = call i32 @f(i32 1)
+; CHECK-NEXT:    br label [[COMMON_RET]]
+;
+  switch i8 %arg, label %else [
+  i8 0, label %if
+  i8 4, label %if
+  i8 5, label %if
+  ]
+
+if:
+  %i0 = call i32 @f(i32 0)
+  ret i32 %i0
+
+else:
+  %i1 = call i32 @f(i32 1)
+  ret i32 %i1
+}
+
+define i32 @wrapping_range(i8 %arg) {
+; CHECK-LABEL: @wrapping_range(
+; CHECK-NEXT:    switch i8 [[ARG:%.*]], label [[ELSE:%.*]] [
+; CHECK-NEXT:      i8 0, label [[IF:%.*]]
+; CHECK-NEXT:      i8 -3, label [[IF]]
+; CHECK-NEXT:      i8 -2, label [[IF]]
+; CHECK-NEXT:      i8 -1, label [[IF]]
+; CHECK-NEXT:    ]
+; CHECK:       common.ret:
+; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ [[I0:%.*]], [[IF]] ], [ [[I1:%.*]], [[ELSE]] ]
+; CHECK-NEXT:    ret i32 [[COMMON_RET_OP]]
+; CHECK:       if:
+; CHECK-NEXT:    [[I0]] = call i32 @f(i32 0)
+; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
+; CHECK:       else:
+; CHECK-NEXT:    [[I1]] = call i32 @f(i32 1)
+; CHECK-NEXT:    br label [[COMMON_RET]]
+;
+  switch i8 %arg, label %else [
+  i8 0, label %if
+  i8 -3, label %if
+  i8 -2, label %if
+  i8 -1, label %if
+  ]
+
+if:
+  %i0 = call i32 @f(i32 0)
+  ret i32 %i0
+
+else:
+  %i1 = call i32 @f(i32 1)
+  ret i32 %i1
+}
+
+define i32 @no_continuous_wrapping_range(i8 %arg) {
+; CHECK-LABEL: @no_continuous_wrapping_range(
+; CHECK-NEXT:    switch i8 [[ARG:%.*]], label [[ELSE:%.*]] [
+; CHECK-NEXT:      i8 0, label [[IF:%.*]]
+; CHECK-NEXT:      i8 -3, label [[IF]]
+; CHECK-NEXT:      i8 -1, label [[IF]]
+; CHECK-NEXT:    ]
+; CHECK:       common.ret:
+; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ [[I0:%.*]], [[IF]] ], [ [[I1:%.*]], [[ELSE]] ]
+; CHECK-NEXT:    ret i32 [[COMMON_RET_OP]]
+; CHECK:       if:
+; CHECK-NEXT:    [[I0]] = call i32 @f(i32 0)
+; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
+; CHECK:       else:
+; CHECK-NEXT:    [[I1]] = call i32 @f(i32 1)
+; CHECK-NEXT:    br label [[COMMON_RET]]
+;
+  switch i8 %arg, label %else [
+  i8 0, label %if
+  i8 -3, label %if
+  i8 -1, label %if
+  ]
+
+if:
+  %i0 = call i32 @f(i32 0)
+  ret i32 %i0
+
+else:
+  %i1 = call i32 @f(i32 1)
+  ret i32 %i1
+}
+
 declare void @bar(ptr nonnull dereferenceable(4))

>From 4010221271dcd360e36317a263c3373ea39a84b6 Mon Sep 17 00:00:00 2001
From: dianqk <dianqk at dianqk.net>
Date: Fri, 26 Sep 2025 23:12:09 +0800
Subject: [PATCH 02/11] [SimplifyCFG] Fold the contiguous wrapping cases into
 ICmp.

---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     | 73 +++++++++++++++----
 .../SimplifyCFG/switch-range-to-icmp.ll       | 17 ++---
 2 files changed, 64 insertions(+), 26 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 8bba634521e3e..a208ba610cd5b 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5718,15 +5718,49 @@ bool SimplifyCFGOpt::simplifyUnreachable(UnreachableInst *UI) {
   return Changed;
 }
 
-static bool casesAreContiguous(SmallVectorImpl<ConstantInt *> &Cases) {
+static bool casesAreContiguous(Value *Condition,
+                               SmallVectorImpl<ConstantInt *> &Cases,
+                               ConstantInt *&ContiguousCasesMin,
+                               ConstantInt *&ContiguousCasesMax,
+                               bool &IsWrapping) {
   assert(Cases.size() >= 1);
 
   array_pod_sort(Cases.begin(), Cases.end(), constantIntSortPredicate);
-  for (size_t I = 1, E = Cases.size(); I != E; ++I) {
-    if (Cases[I - 1]->getValue() != Cases[I]->getValue() + 1)
+  auto Min = Cases.back()->getValue();
+  auto Max = Cases.front()->getValue();
+  auto Offset = Max - Min;
+  auto ContiguousOffset = Cases.size() - 1;
+  if (Offset == ContiguousOffset) {
+    ContiguousCasesMin = Cases.back();
+    ContiguousCasesMax = Cases.front();
+    return true;
+  }
+  ConstantRange CR = computeConstantRange(Condition, /*ForSigned=*/false);
+  // If this is a wrapping contiguous range, that is, [Min, OtherMin] +
+  // [OtherMax, Max] (also [OtherMax, OtherMin]), [OtherMin+1, OtherMax-1] is a
+  // contiguous range for the other destination. N.B. If CR is not a full range,
+  // Max+1 is not equal to Min. It's not continuous in arithmetic.
+  if (Max == CR.getUnsignedMax() && Min == CR.getUnsignedMin()) {
+    assert(Cases.size() >= 2);
+    auto *It =
+        std::adjacent_find(Cases.begin(), Cases.end(), [](auto L, auto R) {
+          return L->getValue() != R->getValue() + 1;
+        });
+    if (It == Cases.end())
       return false;
+    auto *OtherMax = *It;
+    auto *OtherMin = *(It + 1);
+    if ((Max - OtherMax->getValue()) + (OtherMin->getValue() - Min) ==
+        Cases.size() - 2) {
+      ContiguousCasesMin = cast<ConstantInt>(
+          ConstantInt::get(OtherMin->getType(), OtherMin->getValue() + 1));
+      ContiguousCasesMax = cast<ConstantInt>(
+          ConstantInt::get(OtherMax->getType(), OtherMax->getValue() - 1));
+      IsWrapping = true;
+      return true;
+    }
   }
-  return true;
+  return false;
 }
 
 static void createUnreachableSwitchDefault(SwitchInst *Switch,
@@ -5797,25 +5831,34 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
   assert(!CasesA.empty() || HasDefault);
 
   // Figure out if one of the sets of cases form a contiguous range.
-  SmallVectorImpl<ConstantInt *> *ContiguousCases = nullptr;
+  ConstantInt *ContiguousCasesMin = nullptr;
+  ConstantInt *ContiguousCasesMax = nullptr;
   BasicBlock *ContiguousDest = nullptr;
   BasicBlock *OtherDest = nullptr;
-  if (!CasesA.empty() && casesAreContiguous(CasesA)) {
-    ContiguousCases = &CasesA;
+  bool IsWrapping = false;
+  if (!CasesA.empty() &&
+      casesAreContiguous(SI->getCondition(), CasesA, ContiguousCasesMin,
+                         ContiguousCasesMax, IsWrapping)) {
     ContiguousDest = DestA;
     OtherDest = DestB;
-  } else if (casesAreContiguous(CasesB)) {
-    ContiguousCases = &CasesB;
+  } else if (casesAreContiguous(SI->getCondition(), CasesB, ContiguousCasesMin,
+                                ContiguousCasesMax, IsWrapping)) {
     ContiguousDest = DestB;
     OtherDest = DestA;
   } else
     return false;
 
+  if (IsWrapping)
+    std::swap(ContiguousDest, OtherDest);
+
   // Start building the compare and branch.
 
-  Constant *Offset = ConstantExpr::getNeg(ContiguousCases->back());
-  Constant *NumCases =
-      ConstantInt::get(Offset->getType(), ContiguousCases->size());
+  auto ContiguousCasesSize =
+      (ContiguousCasesMax->getValue() - ContiguousCasesMin->getValue())
+          .getZExtValue() +
+      1;
+  Constant *Offset = ConstantExpr::getNeg(ContiguousCasesMin);
+  Constant *NumCases = ConstantInt::get(Offset->getType(), ContiguousCasesSize);
 
   Value *Sub = SI->getCondition();
   if (!Offset->isNullValue())
@@ -5823,7 +5866,7 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
 
   Value *Cmp;
   // If NumCases overflowed, then all possible values jump to the successor.
-  if (NumCases->isNullValue() && !ContiguousCases->empty())
+  if (NumCases->isNullValue() && ContiguousCasesSize != 0)
     Cmp = ConstantInt::getTrue(SI->getContext());
   else
     Cmp = Builder.CreateICmpULT(Sub, NumCases, "switch");
@@ -5853,14 +5896,14 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
 
   // Prune obsolete incoming values off the successors' PHI nodes.
   for (auto BBI = ContiguousDest->begin(); isa<PHINode>(BBI); ++BBI) {
-    unsigned PreviousEdges = ContiguousCases->size();
+    unsigned PreviousEdges = ContiguousCasesSize;
     if (ContiguousDest == SI->getDefaultDest())
       ++PreviousEdges;
     for (unsigned I = 0, E = PreviousEdges - 1; I != E; ++I)
       cast<PHINode>(BBI)->removeIncomingValue(SI->getParent());
   }
   for (auto BBI = OtherDest->begin(); isa<PHINode>(BBI); ++BBI) {
-    unsigned PreviousEdges = SI->getNumCases() - ContiguousCases->size();
+    unsigned PreviousEdges = SI->getNumCases() - ContiguousCasesSize;
     if (OtherDest == SI->getDefaultDest())
       ++PreviousEdges;
     for (unsigned I = 0, E = PreviousEdges - 1; I != E; ++I)
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
index 73771b48e21bc..c6904fe273f09 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
@@ -190,11 +190,9 @@ exit:
 
 define i32 @wrapping_known_range(i8 range(i8 0, 6) %arg) {
 ; CHECK-LABEL: @wrapping_known_range(
-; CHECK-NEXT:    switch i8 [[ARG:%.*]], label [[ELSE:%.*]] [
-; CHECK-NEXT:      i8 0, label [[IF:%.*]]
-; CHECK-NEXT:      i8 4, label [[IF]]
-; CHECK-NEXT:      i8 5, label [[IF]]
-; CHECK-NEXT:    ]
+; CHECK-NEXT:    [[ARG_OFF:%.*]] = add i8 [[ARG:%.*]], -1
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i8 [[ARG_OFF]], 3
+; CHECK-NEXT:    br i1 [[SWITCH]], label [[ELSE:%.*]], label [[IF:%.*]]
 ; CHECK:       common.ret:
 ; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ [[I0:%.*]], [[IF]] ], [ [[I1:%.*]], [[ELSE]] ]
 ; CHECK-NEXT:    ret i32 [[COMMON_RET_OP]]
@@ -222,12 +220,9 @@ else:
 
 define i32 @wrapping_range(i8 %arg) {
 ; CHECK-LABEL: @wrapping_range(
-; CHECK-NEXT:    switch i8 [[ARG:%.*]], label [[ELSE:%.*]] [
-; CHECK-NEXT:      i8 0, label [[IF:%.*]]
-; CHECK-NEXT:      i8 -3, label [[IF]]
-; CHECK-NEXT:      i8 -2, label [[IF]]
-; CHECK-NEXT:      i8 -1, label [[IF]]
-; CHECK-NEXT:    ]
+; CHECK-NEXT:    [[ARG_OFF:%.*]] = add i8 [[ARG:%.*]], -1
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i8 [[ARG_OFF]], -4
+; CHECK-NEXT:    br i1 [[SWITCH]], label [[ELSE:%.*]], label [[IF:%.*]]
 ; CHECK:       common.ret:
 ; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ [[I0:%.*]], [[IF]] ], [ [[I1:%.*]], [[ELSE]] ]
 ; CHECK-NEXT:    ret i32 [[COMMON_RET_OP]]

>From c56155d16d2ffcc56ee57f94bf537b0a72d0a3ea Mon Sep 17 00:00:00 2001
From: dianqk <dianqk at dianqk.net>
Date: Sat, 27 Sep 2025 22:35:46 +0800
Subject: [PATCH 03/11] Fix the count of pruning incoming values

---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     | 21 ++++++++++--------
 .../SimplifyCFG/switch-range-to-icmp.ll       | 22 +++++++++++++++++++
 2 files changed, 34 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index a208ba610cd5b..eae32471ad03b 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5836,6 +5836,8 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
   BasicBlock *ContiguousDest = nullptr;
   BasicBlock *OtherDest = nullptr;
   bool IsWrapping = false;
+  SmallVectorImpl<ConstantInt *> *ContiguousCases = &CasesA;
+  SmallVectorImpl<ConstantInt *> *OtherCases = &CasesB;
   if (!CasesA.empty() &&
       casesAreContiguous(SI->getCondition(), CasesA, ContiguousCasesMin,
                          ContiguousCasesMax, IsWrapping)) {
@@ -5845,20 +5847,21 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
                                 ContiguousCasesMax, IsWrapping)) {
     ContiguousDest = DestB;
     OtherDest = DestA;
+    std::swap(ContiguousCases, OtherCases);
   } else
     return false;
 
-  if (IsWrapping)
+  if (IsWrapping) {
     std::swap(ContiguousDest, OtherDest);
+    std::swap(ContiguousCases, OtherCases);
+  }
 
   // Start building the compare and branch.
 
-  auto ContiguousCasesSize =
-      (ContiguousCasesMax->getValue() - ContiguousCasesMin->getValue())
-          .getZExtValue() +
-      1;
   Constant *Offset = ConstantExpr::getNeg(ContiguousCasesMin);
-  Constant *NumCases = ConstantInt::get(Offset->getType(), ContiguousCasesSize);
+  Constant *NumCases = ConstantInt::get(Offset->getType(),
+                                        ContiguousCasesMax->getValue() -
+                                            ContiguousCasesMin->getValue() + 1);
 
   Value *Sub = SI->getCondition();
   if (!Offset->isNullValue())
@@ -5866,7 +5869,7 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
 
   Value *Cmp;
   // If NumCases overflowed, then all possible values jump to the successor.
-  if (NumCases->isNullValue() && ContiguousCasesSize != 0)
+  if (NumCases->isNullValue() && !ContiguousCases->empty())
     Cmp = ConstantInt::getTrue(SI->getContext());
   else
     Cmp = Builder.CreateICmpULT(Sub, NumCases, "switch");
@@ -5896,14 +5899,14 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
 
   // Prune obsolete incoming values off the successors' PHI nodes.
   for (auto BBI = ContiguousDest->begin(); isa<PHINode>(BBI); ++BBI) {
-    unsigned PreviousEdges = ContiguousCasesSize;
+    unsigned PreviousEdges = ContiguousCases->size();
     if (ContiguousDest == SI->getDefaultDest())
       ++PreviousEdges;
     for (unsigned I = 0, E = PreviousEdges - 1; I != E; ++I)
       cast<PHINode>(BBI)->removeIncomingValue(SI->getParent());
   }
   for (auto BBI = OtherDest->begin(); isa<PHINode>(BBI); ++BBI) {
-    unsigned PreviousEdges = SI->getNumCases() - ContiguousCasesSize;
+    unsigned PreviousEdges = OtherCases->size();
     if (OtherDest == SI->getDefaultDest())
       ++PreviousEdges;
     for (unsigned I = 0, E = PreviousEdges - 1; I != E; ++I)
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
index c6904fe273f09..c9d9e9abdcba9 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
@@ -249,6 +249,28 @@ else:
   ret i32 %i1
 }
 
+define i8 @wrapping_range_phi(i8 %arg) {
+; CHECK-LABEL: @wrapping_range_phi(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ARG_OFF:%.*]] = add i8 [[ARG:%.*]], -1
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i8 [[ARG_OFF]], -2
+; CHECK-NEXT:    [[SPEC_SELECT:%.*]] = select i1 [[SWITCH]], i8 0, i8 1
+; CHECK-NEXT:    ret i8 [[SPEC_SELECT]]
+;
+entry:
+  switch i8 %arg, label %else [
+  i8 0, label %if
+  i8 -1, label %if
+  ]
+
+if:
+  %i = phi i8 [ 0, %else ], [ 1, %entry ], [ 1, %entry ]
+  ret i8 %i
+
+else:
+  br label %if
+}
+
 define i32 @no_continuous_wrapping_range(i8 %arg) {
 ; CHECK-LABEL: @no_continuous_wrapping_range(
 ; CHECK-NEXT:    switch i8 [[ARG:%.*]], label [[ELSE:%.*]] [

>From 41c4da0887d037223b49c1f15d05e0fa743371b0 Mon Sep 17 00:00:00 2001
From: dianqk <dianqk at dianqk.net>
Date: Thu, 2 Oct 2025 20:28:56 +0800
Subject: [PATCH 04/11] [SimplifyCFG] Cases to the default destination cannot
 be contiguous cases

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

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index eae32471ad03b..d427aae77a75e 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5838,7 +5838,8 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
   bool IsWrapping = false;
   SmallVectorImpl<ConstantInt *> *ContiguousCases = &CasesA;
   SmallVectorImpl<ConstantInt *> *OtherCases = &CasesB;
-  if (!CasesA.empty() &&
+  // Correctness: Cases to the default destination cannot be contiguous cases.
+  if (!HasDefault && !CasesA.empty() &&
       casesAreContiguous(SI->getCondition(), CasesA, ContiguousCasesMin,
                          ContiguousCasesMax, IsWrapping)) {
     ContiguousDest = DestA;

>From 0ce3e6eac8f589686b23de75b7cbc6e9b4ca108e Mon Sep 17 00:00:00 2001
From: dianqk <dianqk at dianqk.net>
Date: Fri, 3 Oct 2025 13:17:01 +0800
Subject: [PATCH 05/11] Add more tests

---
 .../SimplifyCFG/switch-range-to-icmp.ll       | 69 +++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
index c9d9e9abdcba9..b715bf8d037f0 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
@@ -303,4 +303,73 @@ else:
   ret i32 %i1
 }
 
+define i32 @one_case_1(i32 %x) {
+; CHECK-LABEL: @one_case_1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[X_OFF:%.*]] = add i32 [[X:%.*]], -5
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i32 [[X_OFF]], 3
+; CHECK-NEXT:    br i1 [[SWITCH]], label [[A:%.*]], label [[B:%.*]]
+; CHECK:       common.ret:
+; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ [[TMP0:%.*]], [[A]] ], [ [[TMP1:%.*]], [[B]] ]
+; CHECK-NEXT:    ret i32 [[COMMON_RET_OP]]
+; CHECK:       a:
+; CHECK-NEXT:    [[TMP0]] = call i32 @f(i32 0)
+; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
+; CHECK:       b:
+; CHECK-NEXT:    [[TMP1]] = call i32 @f(i32 1)
+; CHECK-NEXT:    br label [[COMMON_RET]]
+;
+entry:
+  switch i32 %x, label %unreachable [
+  i32 5, label %a
+  i32 6, label %a
+  i32 7, label %a
+  i32 10, label %b
+  ]
+
+unreachable:
+  unreachable
+a:
+  %0 = call i32 @f(i32 0)
+  ret i32 %0
+b:
+  %1 = call i32 @f(i32 1)
+  ret i32 %1
+}
+
+define i32 @one_case_2(i32 %x) {
+; CHECK-LABEL: @one_case_2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[X_OFF:%.*]] = add i32 [[X:%.*]], -5
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i32 [[X_OFF]], 1
+; CHECK-NEXT:    br i1 [[SWITCH]], label [[A:%.*]], label [[B:%.*]]
+; CHECK:       common.ret:
+; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ [[TMP0:%.*]], [[A]] ], [ [[TMP1:%.*]], [[B]] ]
+; CHECK-NEXT:    ret i32 [[COMMON_RET_OP]]
+; CHECK:       a:
+; CHECK-NEXT:    [[TMP0]] = call i32 @f(i32 0)
+; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
+; CHECK:       b:
+; CHECK-NEXT:    [[TMP1]] = call i32 @f(i32 1)
+; CHECK-NEXT:    br label [[COMMON_RET]]
+;
+entry:
+  switch i32 %x, label %unreachable [
+  i32 5, label %a
+  i32 10, label %b
+  i32 11, label %b
+  i32 12, label %b
+  i32 13, label %b
+  ]
+
+unreachable:
+  unreachable
+a:
+  %0 = call i32 @f(i32 0)
+  ret i32 %0
+b:
+  %1 = call i32 @f(i32 1)
+  ret i32 %1
+}
+
 declare void @bar(ptr nonnull dereferenceable(4))

>From dd4fdf60a9d11b96b3162ad143d709144ae78946 Mon Sep 17 00:00:00 2001
From: dianqk <dianqk at dianqk.net>
Date: Fri, 3 Oct 2025 13:18:45 +0800
Subject: [PATCH 06/11] [SimplifyCFG] Improve one case

---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     | 20 ++++++++++++++++---
 .../SimplifyCFG/switch-range-to-icmp.ll       |  6 +++---
 2 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index d427aae77a75e..0f3f1befd6c05 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5838,10 +5838,24 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
   bool IsWrapping = false;
   SmallVectorImpl<ConstantInt *> *ContiguousCases = &CasesA;
   SmallVectorImpl<ConstantInt *> *OtherCases = &CasesB;
+
+  // Only one icmp is needed when there is only one case.
+  if (!HasDefault && CasesA.size() == 1) {
+    ContiguousCasesMax = CasesA[0];
+    ContiguousCasesMin = CasesA[0];
+    ContiguousDest = DestA;
+    OtherDest = DestB;
+  } else if (CasesB.size() == 1) {
+    ContiguousCasesMax = CasesB[0];
+    ContiguousCasesMin = CasesB[0];
+    ContiguousDest = DestB;
+    OtherDest = DestA;
+    std::swap(ContiguousCases, OtherCases);
+  }
   // Correctness: Cases to the default destination cannot be contiguous cases.
-  if (!HasDefault && !CasesA.empty() &&
-      casesAreContiguous(SI->getCondition(), CasesA, ContiguousCasesMin,
-                         ContiguousCasesMax, IsWrapping)) {
+  else if (!HasDefault && !CasesA.empty() &&
+           casesAreContiguous(SI->getCondition(), CasesA, ContiguousCasesMin,
+                              ContiguousCasesMax, IsWrapping)) {
     ContiguousDest = DestA;
     OtherDest = DestB;
   } else if (casesAreContiguous(SI->getCondition(), CasesB, ContiguousCasesMin,
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
index b715bf8d037f0..9cb24904b6a18 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
@@ -306,11 +306,11 @@ else:
 define i32 @one_case_1(i32 %x) {
 ; CHECK-LABEL: @one_case_1(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[X_OFF:%.*]] = add i32 [[X:%.*]], -5
-; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i32 [[X_OFF]], 3
+; CHECK-NEXT:    [[X_OFF:%.*]] = add i32 [[X:%.*]], -10
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i32 [[X_OFF]], 1
 ; CHECK-NEXT:    br i1 [[SWITCH]], label [[A:%.*]], label [[B:%.*]]
 ; CHECK:       common.ret:
-; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ [[TMP0:%.*]], [[A]] ], [ [[TMP1:%.*]], [[B]] ]
+; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ [[TMP0:%.*]], [[B]] ], [ [[TMP1:%.*]], [[A]] ]
 ; CHECK-NEXT:    ret i32 [[COMMON_RET_OP]]
 ; CHECK:       a:
 ; CHECK-NEXT:    [[TMP0]] = call i32 @f(i32 0)

>From bfc9e920f57c69e1e288399b9feaa043e768fc83 Mon Sep 17 00:00:00 2001
From: dianqk <dianqk at dianqk.net>
Date: Fri, 3 Oct 2025 13:50:22 +0800
Subject: [PATCH 07/11] Add more tests

---
 .../SimplifyCFG/switch-range-to-icmp.ll       | 32 +++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
index 9cb24904b6a18..c9438133352da 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
@@ -218,6 +218,38 @@ else:
   ret i32 %i1
 }
 
+define i32 @wrapping_known_range_2(i8 range(i8 0, 6) %arg) {
+; CHECK-LABEL: @wrapping_known_range_2(
+; CHECK-NEXT:    [[ARG_OFF:%.*]] = add i8 [[ARG:%.*]], -1
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i8 [[ARG_OFF]], 1
+; CHECK-NEXT:    br i1 [[SWITCH]], label [[ELSE:%.*]], label [[IF:%.*]]
+; CHECK:       common.ret:
+; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ [[I0:%.*]], [[IF]] ], [ [[I1:%.*]], [[ELSE]] ]
+; CHECK-NEXT:    ret i32 [[COMMON_RET_OP]]
+; CHECK:       if:
+; CHECK-NEXT:    [[I0]] = call i32 @f(i32 0)
+; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
+; CHECK:       else:
+; CHECK-NEXT:    [[I1]] = call i32 @f(i32 1)
+; CHECK-NEXT:    br label [[COMMON_RET]]
+;
+  switch i8 %arg, label %else [
+  i8 0, label %if
+  i8 2, label %if
+  i8 3, label %if
+  i8 4, label %if
+  i8 5, label %if
+  ]
+
+if:
+  %i0 = call i32 @f(i32 0)
+  ret i32 %i0
+
+else:
+  %i1 = call i32 @f(i32 1)
+  ret i32 %i1
+}
+
 define i32 @wrapping_range(i8 %arg) {
 ; CHECK-LABEL: @wrapping_range(
 ; CHECK-NEXT:    [[ARG_OFF:%.*]] = add i8 [[ARG:%.*]], -1

>From 9864e17297498ca100cf8c3d56beb05f7e36ea13 Mon Sep 17 00:00:00 2001
From: dianqk <dianqk at dianqk.net>
Date: Fri, 3 Oct 2025 18:46:14 +0800
Subject: [PATCH 08/11] [SimplifyCFG] Use icmp eq for one case

---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     | 26 +++++++++++--------
 .../Coroutines/coro-catchswitch-cleanuppad.ll |  4 +--
 .../SimplifyCFG/switch-dead-default.ll        | 12 +++------
 .../SimplifyCFG/switch-range-to-icmp.ll       |  9 +++----
 4 files changed, 24 insertions(+), 27 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 0f3f1befd6c05..c51e7ae89dac0 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5877,18 +5877,22 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
   Constant *NumCases = ConstantInt::get(Offset->getType(),
                                         ContiguousCasesMax->getValue() -
                                             ContiguousCasesMin->getValue() + 1);
-
-  Value *Sub = SI->getCondition();
-  if (!Offset->isNullValue())
-    Sub = Builder.CreateAdd(Sub, Offset, Sub->getName() + ".off");
-
-  Value *Cmp;
+  BranchInst *NewBI;
+  if (NumCases->isOneValue()) {
+    assert(ContiguousCasesMax->getValue() == ContiguousCasesMin->getValue());
+    Value *Cmp = Builder.CreateICmpEQ(SI->getCondition(), ContiguousCasesMin);
+    NewBI = Builder.CreateCondBr(Cmp, ContiguousDest, OtherDest);
+  }
   // If NumCases overflowed, then all possible values jump to the successor.
-  if (NumCases->isNullValue() && !ContiguousCases->empty())
-    Cmp = ConstantInt::getTrue(SI->getContext());
-  else
-    Cmp = Builder.CreateICmpULT(Sub, NumCases, "switch");
-  BranchInst *NewBI = Builder.CreateCondBr(Cmp, ContiguousDest, OtherDest);
+  else if (NumCases->isNullValue() && !ContiguousCases->empty()) {
+    NewBI = Builder.CreateBr(ContiguousDest);
+  } else {
+    Value *Sub = SI->getCondition();
+    if (!Offset->isNullValue())
+      Sub = Builder.CreateAdd(Sub, Offset, Sub->getName() + ".off");
+    Value *Cmp = Builder.CreateICmpULT(Sub, NumCases, "switch");
+    NewBI = Builder.CreateCondBr(Cmp, ContiguousDest, OtherDest);
+  }
 
   // Update weight for the newly-created conditional branch.
   if (hasBranchWeightMD(*SI)) {
diff --git a/llvm/test/Transforms/Coroutines/coro-catchswitch-cleanuppad.ll b/llvm/test/Transforms/Coroutines/coro-catchswitch-cleanuppad.ll
index d0e7c1c29eb32..e1e1611ee3362 100644
--- a/llvm/test/Transforms/Coroutines/coro-catchswitch-cleanuppad.ll
+++ b/llvm/test/Transforms/Coroutines/coro-catchswitch-cleanuppad.ll
@@ -80,8 +80,8 @@ cleanup2:
 ; CHECK: cleanup2.corodispatch:
 ; CHECK:   %1 = phi i8 [ 0, %handler2 ], [ 1, %catch.dispatch.2 ]
 ; CHECK:   %2 = cleanuppad within %h1 []
-; CHECK:   %switch = icmp ult i8 %1, 1
-; CHECK:   br i1 %switch, label %cleanup2.from.handler2, label %cleanup2.from.catch.dispatch.2
+; CHECK:   %3 = icmp eq i8 %1, 0
+; CHECK:   br i1 %3, label %cleanup2.from.handler2, label %cleanup2.from.catch.dispatch.2
 
 ; CHECK: cleanup2.from.handler2:
 ; CHECK:   %valueB.reload = load i32, ptr %valueB.spill.addr, align 4
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-dead-default.ll b/llvm/test/Transforms/SimplifyCFG/switch-dead-default.ll
index 4a457cc177e85..a0e29dd19dd84 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-dead-default.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-dead-default.ll
@@ -7,8 +7,7 @@ declare void @foo(i32)
 define void @test(i1 %a) {
 ; CHECK-LABEL: define void @test(
 ; CHECK-SAME: i1 [[A:%.*]]) {
-; CHECK-NEXT:    [[A_OFF:%.*]] = add i1 [[A]], true
-; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i1 [[A_OFF]], true
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp eq i1 [[A]], true
 ; CHECK-NEXT:    br i1 [[SWITCH]], label [[TRUE:%.*]], label [[FALSE:%.*]]
 ; CHECK:       common.ret:
 ; CHECK-NEXT:    ret void
@@ -209,8 +208,7 @@ define void @test5(i8 %a) {
 ; CHECK-SAME: i8 [[A:%.*]]) {
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[A]], 2
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[CMP]])
-; CHECK-NEXT:    [[A_OFF:%.*]] = add i8 [[A]], -1
-; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i8 [[A_OFF]], 1
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp eq i8 [[A]], 1
 ; CHECK-NEXT:    br i1 [[SWITCH]], label [[TRUE:%.*]], label [[FALSE:%.*]]
 ; CHECK:       common.ret:
 ; CHECK-NEXT:    ret void
@@ -243,8 +241,7 @@ define void @test6(i8 %a) {
 ; CHECK-NEXT:    [[AND:%.*]] = and i8 [[A]], -2
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[AND]], -2
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[CMP]])
-; CHECK-NEXT:    [[A_OFF:%.*]] = add i8 [[A]], 1
-; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i8 [[A_OFF]], 1
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp eq i8 [[A]], -1
 ; CHECK-NEXT:    br i1 [[SWITCH]], label [[TRUE:%.*]], label [[FALSE:%.*]]
 ; CHECK:       common.ret:
 ; CHECK-NEXT:    ret void
@@ -279,8 +276,7 @@ define void @test7(i8 %a) {
 ; CHECK-NEXT:    [[AND:%.*]] = and i8 [[A]], -2
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[AND]], -2
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[CMP]])
-; CHECK-NEXT:    [[A_OFF:%.*]] = add i8 [[A]], 1
-; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i8 [[A_OFF]], 1
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp eq i8 [[A]], -1
 ; CHECK-NEXT:    br i1 [[SWITCH]], label [[TRUE:%.*]], label [[FALSE:%.*]]
 ; CHECK:       common.ret:
 ; CHECK-NEXT:    ret void
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
index c9438133352da..0fc3c19edd1f3 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-range-to-icmp.ll
@@ -220,8 +220,7 @@ else:
 
 define i32 @wrapping_known_range_2(i8 range(i8 0, 6) %arg) {
 ; CHECK-LABEL: @wrapping_known_range_2(
-; CHECK-NEXT:    [[ARG_OFF:%.*]] = add i8 [[ARG:%.*]], -1
-; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i8 [[ARG_OFF]], 1
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp eq i8 [[ARG:%.*]], 1
 ; CHECK-NEXT:    br i1 [[SWITCH]], label [[ELSE:%.*]], label [[IF:%.*]]
 ; CHECK:       common.ret:
 ; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ [[I0:%.*]], [[IF]] ], [ [[I1:%.*]], [[ELSE]] ]
@@ -338,8 +337,7 @@ else:
 define i32 @one_case_1(i32 %x) {
 ; CHECK-LABEL: @one_case_1(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[X_OFF:%.*]] = add i32 [[X:%.*]], -10
-; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i32 [[X_OFF]], 1
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp eq i32 [[X:%.*]], 10
 ; CHECK-NEXT:    br i1 [[SWITCH]], label [[A:%.*]], label [[B:%.*]]
 ; CHECK:       common.ret:
 ; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ [[TMP0:%.*]], [[B]] ], [ [[TMP1:%.*]], [[A]] ]
@@ -372,8 +370,7 @@ b:
 define i32 @one_case_2(i32 %x) {
 ; CHECK-LABEL: @one_case_2(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[X_OFF:%.*]] = add i32 [[X:%.*]], -5
-; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i32 [[X_OFF]], 1
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp eq i32 [[X:%.*]], 5
 ; CHECK-NEXT:    br i1 [[SWITCH]], label [[A:%.*]], label [[B:%.*]]
 ; CHECK:       common.ret:
 ; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi i32 [ [[TMP0:%.*]], [[A]] ], [ [[TMP1:%.*]], [[B]] ]

>From 2288724c860a9380f13ed6010a29df5aaefcf378 Mon Sep 17 00:00:00 2001
From: dianqk <dianqk at dianqk.net>
Date: Sat, 4 Oct 2025 14:23:37 +0800
Subject: [PATCH 09/11] Address feedback

---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 153 ++++++++++++----------
 1 file changed, 83 insertions(+), 70 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index c51e7ae89dac0..fc24246718d2b 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5718,22 +5718,35 @@ bool SimplifyCFGOpt::simplifyUnreachable(UnreachableInst *UI) {
   return Changed;
 }
 
-static bool casesAreContiguous(Value *Condition,
-                               SmallVectorImpl<ConstantInt *> &Cases,
-                               ConstantInt *&ContiguousCasesMin,
-                               ConstantInt *&ContiguousCasesMax,
-                               bool &IsWrapping) {
+struct ContiguousCasesResult {
+  ConstantInt *Min;
+  ConstantInt *Max;
+  BasicBlock *Dest;
+  BasicBlock *OtherDest;
+  SmallVectorImpl<ConstantInt *> *Cases;
+  SmallVectorImpl<ConstantInt *> *OtherCases;
+};
+
+static std::optional<ContiguousCasesResult>
+findContiguousCases(Value *Condition, SmallVectorImpl<ConstantInt *> &Cases,
+                    SmallVectorImpl<ConstantInt *> &OtherCases,
+                    BasicBlock *Dest, BasicBlock *OtherDest) {
   assert(Cases.size() >= 1);
 
   array_pod_sort(Cases.begin(), Cases.end(), constantIntSortPredicate);
-  auto Min = Cases.back()->getValue();
-  auto Max = Cases.front()->getValue();
-  auto Offset = Max - Min;
-  auto ContiguousOffset = Cases.size() - 1;
+  APInt Min = Cases.back()->getValue();
+  APInt Max = Cases.front()->getValue();
+  APInt Offset = Max - Min;
+  size_t ContiguousOffset = Cases.size() - 1;
   if (Offset == ContiguousOffset) {
-    ContiguousCasesMin = Cases.back();
-    ContiguousCasesMax = Cases.front();
-    return true;
+    return ContiguousCasesResult{
+        /*Min=*/Cases.back(),
+        /*Max=*/Cases.front(),
+        /*Dest=*/Dest,
+        /*OtherDest=*/OtherDest,
+        /*Cases=*/&Cases,
+        /*OtherCases=*/&OtherCases,
+    };
   }
   ConstantRange CR = computeConstantRange(Condition, /*ForSigned=*/false);
   // If this is a wrapping contiguous range, that is, [Min, OtherMin] +
@@ -5747,20 +5760,24 @@ static bool casesAreContiguous(Value *Condition,
           return L->getValue() != R->getValue() + 1;
         });
     if (It == Cases.end())
-      return false;
-    auto *OtherMax = *It;
-    auto *OtherMin = *(It + 1);
+      return std::nullopt;
+    auto [OtherMax, OtherMin] = std::make_pair(*It, *std::next(It));
     if ((Max - OtherMax->getValue()) + (OtherMin->getValue() - Min) ==
         Cases.size() - 2) {
-      ContiguousCasesMin = cast<ConstantInt>(
-          ConstantInt::get(OtherMin->getType(), OtherMin->getValue() + 1));
-      ContiguousCasesMax = cast<ConstantInt>(
-          ConstantInt::get(OtherMax->getType(), OtherMax->getValue() - 1));
-      IsWrapping = true;
-      return true;
+      return ContiguousCasesResult{
+          /*Min=*/cast<ConstantInt>(
+              ConstantInt::get(OtherMin->getType(), OtherMin->getValue() + 1)),
+          /*Max=*/
+          cast<ConstantInt>(
+              ConstantInt::get(OtherMax->getType(), OtherMax->getValue() - 1)),
+          /*Dest=*/OtherDest,
+          /*OtherDest=*/Dest,
+          /*Cases=*/&OtherCases,
+          /*OtherCases=*/&Cases,
+      };
     }
   }
-  return false;
+  return std::nullopt;
 }
 
 static void createUnreachableSwitchDefault(SwitchInst *Switch,
@@ -5797,7 +5814,6 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
   bool HasDefault = !SI->defaultDestUnreachable();
 
   auto *BB = SI->getParent();
-
   // Partition the cases into two sets with different destinations.
   BasicBlock *DestA = HasDefault ? SI->getDefaultDest() : nullptr;
   BasicBlock *DestB = nullptr;
@@ -5831,67 +5847,64 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
   assert(!CasesA.empty() || HasDefault);
 
   // Figure out if one of the sets of cases form a contiguous range.
-  ConstantInt *ContiguousCasesMin = nullptr;
-  ConstantInt *ContiguousCasesMax = nullptr;
-  BasicBlock *ContiguousDest = nullptr;
-  BasicBlock *OtherDest = nullptr;
-  bool IsWrapping = false;
-  SmallVectorImpl<ConstantInt *> *ContiguousCases = &CasesA;
-  SmallVectorImpl<ConstantInt *> *OtherCases = &CasesB;
+  std::optional<ContiguousCasesResult> ContiguousCases;
 
   // Only one icmp is needed when there is only one case.
-  if (!HasDefault && CasesA.size() == 1) {
-    ContiguousCasesMax = CasesA[0];
-    ContiguousCasesMin = CasesA[0];
-    ContiguousDest = DestA;
-    OtherDest = DestB;
-  } else if (CasesB.size() == 1) {
-    ContiguousCasesMax = CasesB[0];
-    ContiguousCasesMin = CasesB[0];
-    ContiguousDest = DestB;
-    OtherDest = DestA;
-    std::swap(ContiguousCases, OtherCases);
-  }
+  if (!HasDefault && CasesA.size() == 1)
+    ContiguousCases = ContiguousCasesResult{
+        /*Min=*/CasesA[0],
+        /*Max=*/CasesA[0],
+        /*Dest=*/DestA,
+        /*OtherDest=*/DestB,
+        /*Cases=*/&CasesA,
+        /*OtherCases=*/&CasesB,
+    };
+  else if (CasesB.size() == 1)
+    ContiguousCases = ContiguousCasesResult{
+        /*Min=*/CasesB[0],
+        /*Max=*/CasesB[0],
+        /*Dest=*/DestB,
+        /*OtherDest=*/DestA,
+        /*Cases=*/&CasesB,
+        /*OtherCases=*/&CasesA,
+    };
+
   // Correctness: Cases to the default destination cannot be contiguous cases.
-  else if (!HasDefault && !CasesA.empty() &&
-           casesAreContiguous(SI->getCondition(), CasesA, ContiguousCasesMin,
-                              ContiguousCasesMax, IsWrapping)) {
-    ContiguousDest = DestA;
-    OtherDest = DestB;
-  } else if (casesAreContiguous(SI->getCondition(), CasesB, ContiguousCasesMin,
-                                ContiguousCasesMax, IsWrapping)) {
-    ContiguousDest = DestB;
-    OtherDest = DestA;
-    std::swap(ContiguousCases, OtherCases);
-  } else
+  if (!ContiguousCases && !HasDefault && !CasesA.empty())
+    if (auto Result = findContiguousCases(SI->getCondition(), CasesA, CasesB,
+                                          DestA, DestB))
+      ContiguousCases = *Result;
+
+  if (!ContiguousCases)
+    if (auto Result = findContiguousCases(SI->getCondition(), CasesB, CasesA,
+                                          DestB, DestA))
+      ContiguousCases = *Result;
+
+  if (!ContiguousCases)
     return false;
 
-  if (IsWrapping) {
-    std::swap(ContiguousDest, OtherDest);
-    std::swap(ContiguousCases, OtherCases);
-  }
+  auto [Min, Max, Dest, OtherDest, Cases, OtherCases] = *ContiguousCases;
 
   // Start building the compare and branch.
 
-  Constant *Offset = ConstantExpr::getNeg(ContiguousCasesMin);
+  Constant *Offset = ConstantExpr::getNeg(Min);
   Constant *NumCases = ConstantInt::get(Offset->getType(),
-                                        ContiguousCasesMax->getValue() -
-                                            ContiguousCasesMin->getValue() + 1);
+                                        Max->getValue() - Min->getValue() + 1);
   BranchInst *NewBI;
   if (NumCases->isOneValue()) {
-    assert(ContiguousCasesMax->getValue() == ContiguousCasesMin->getValue());
-    Value *Cmp = Builder.CreateICmpEQ(SI->getCondition(), ContiguousCasesMin);
-    NewBI = Builder.CreateCondBr(Cmp, ContiguousDest, OtherDest);
+    assert(Max->getValue() == Min->getValue());
+    Value *Cmp = Builder.CreateICmpEQ(SI->getCondition(), Min);
+    NewBI = Builder.CreateCondBr(Cmp, Dest, OtherDest);
   }
   // If NumCases overflowed, then all possible values jump to the successor.
-  else if (NumCases->isNullValue() && !ContiguousCases->empty()) {
-    NewBI = Builder.CreateBr(ContiguousDest);
+  else if (NumCases->isNullValue() && !Cases->empty()) {
+    NewBI = Builder.CreateBr(Dest);
   } else {
     Value *Sub = SI->getCondition();
     if (!Offset->isNullValue())
       Sub = Builder.CreateAdd(Sub, Offset, Sub->getName() + ".off");
     Value *Cmp = Builder.CreateICmpULT(Sub, NumCases, "switch");
-    NewBI = Builder.CreateCondBr(Cmp, ContiguousDest, OtherDest);
+    NewBI = Builder.CreateCondBr(Cmp, Dest, OtherDest);
   }
 
   // Update weight for the newly-created conditional branch.
@@ -5902,7 +5915,7 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
       uint64_t TrueWeight = 0;
       uint64_t FalseWeight = 0;
       for (size_t I = 0, E = Weights.size(); I != E; ++I) {
-        if (SI->getSuccessor(I) == ContiguousDest)
+        if (SI->getSuccessor(I) == Dest)
           TrueWeight += Weights[I];
         else
           FalseWeight += Weights[I];
@@ -5917,9 +5930,9 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
   }
 
   // Prune obsolete incoming values off the successors' PHI nodes.
-  for (auto BBI = ContiguousDest->begin(); isa<PHINode>(BBI); ++BBI) {
-    unsigned PreviousEdges = ContiguousCases->size();
-    if (ContiguousDest == SI->getDefaultDest())
+  for (auto BBI = Dest->begin(); isa<PHINode>(BBI); ++BBI) {
+    unsigned PreviousEdges = Cases->size();
+    if (Dest == SI->getDefaultDest())
       ++PreviousEdges;
     for (unsigned I = 0, E = PreviousEdges - 1; I != E; ++I)
       cast<PHINode>(BBI)->removeIncomingValue(SI->getParent());

>From 7c8dcd05d9f63ad33897a1cb04be48f2ac319a04 Mon Sep 17 00:00:00 2001
From: dianqk <dianqk at dianqk.net>
Date: Sun, 5 Oct 2025 14:33:08 +0800
Subject: [PATCH 10/11] Address feedback

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

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index fc24246718d2b..075f1186bfbfd 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5868,17 +5868,14 @@ bool SimplifyCFGOpt::turnSwitchRangeIntoICmp(SwitchInst *SI,
         /*Cases=*/&CasesB,
         /*OtherCases=*/&CasesA,
     };
-
   // Correctness: Cases to the default destination cannot be contiguous cases.
-  if (!ContiguousCases && !HasDefault && !CasesA.empty())
-    if (auto Result = findContiguousCases(SI->getCondition(), CasesA, CasesB,
-                                          DestA, DestB))
-      ContiguousCases = *Result;
+  else if (!HasDefault)
+    ContiguousCases =
+        findContiguousCases(SI->getCondition(), CasesA, CasesB, DestA, DestB);
 
   if (!ContiguousCases)
-    if (auto Result = findContiguousCases(SI->getCondition(), CasesB, CasesA,
-                                          DestB, DestA))
-      ContiguousCases = *Result;
+    ContiguousCases =
+        findContiguousCases(SI->getCondition(), CasesB, CasesA, DestB, DestA);
 
   if (!ContiguousCases)
     return false;

>From b89285e2098ff83d48ea3a088ae48c04b3786d48 Mon Sep 17 00:00:00 2001
From: dianqk <dianqk at dianqk.net>
Date: Mon, 6 Oct 2025 12:28:38 +0800
Subject: [PATCH 11/11] Address feedback

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

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 075f1186bfbfd..debfe914ec803 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -5734,8 +5734,8 @@ findContiguousCases(Value *Condition, SmallVectorImpl<ConstantInt *> &Cases,
   assert(Cases.size() >= 1);
 
   array_pod_sort(Cases.begin(), Cases.end(), constantIntSortPredicate);
-  APInt Min = Cases.back()->getValue();
-  APInt Max = Cases.front()->getValue();
+  const APInt &Min = Cases.back()->getValue();
+  const APInt &Max = Cases.front()->getValue();
   APInt Offset = Max - Min;
   size_t ContiguousOffset = Cases.size() - 1;
   if (Offset == ContiguousOffset) {



More information about the llvm-commits mailing list