[llvm] [SimplifyCFG] Fold the contiguous wrapping cases into ICmp. (PR #161000)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Oct 3 03:47:36 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 1/8] 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 2/8] [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 3/8] 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 4/8] [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 5/8] 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 6/8] [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 7/8] 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 8/8] [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]] ]
More information about the llvm-commits
mailing list