[llvm] [SimplifyCFG] Improve reduceSwitchRange to avoid unnecessary subtractions (PR #198374)
Drew Kersnar via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 8 12:53:44 PDT 2026
https://github.com/dakersnar updated https://github.com/llvm/llvm-project/pull/198374
>From 19bb66626f27847d5591f541ca75c98bf3b04970 Mon Sep 17 00:00:00 2001
From: Drew Kersnar <dkersnar at nvidia.com>
Date: Wed, 13 May 2026 16:24:24 +0000
Subject: [PATCH 1/7] Add new test without changes
---
.../Transforms/SimplifyCFG/rangereduce.ll | 104 ++++++++++++++++++
1 file changed, 104 insertions(+)
diff --git a/llvm/test/Transforms/SimplifyCFG/rangereduce.ll b/llvm/test/Transforms/SimplifyCFG/rangereduce.ll
index 5444a730bab1c..690543faf5644 100644
--- a/llvm/test/Transforms/SimplifyCFG/rangereduce.ll
+++ b/llvm/test/Transforms/SimplifyCFG/rangereduce.ll
@@ -317,6 +317,110 @@ three:
ret i32 99783
}
+; Base=0 is profitable here: shifting out the common low zero bits produces a
+; dense range without subtracting the local minimum first.
+define void @test10(i32 %a) {
+; CHECK-LABEL: @test10(
+; CHECK-NEXT: [[TMP1:%.*]] = sub i32 [[A:%.*]], 32
+; CHECK-NEXT: [[TMP2:%.*]] = call i32 @llvm.fshl.i32(i32 [[TMP1]], i32 [[TMP1]], i32 27)
+; CHECK-NEXT: switch i32 [[TMP2]], label [[COMMON_RET:%.*]] [
+; CHECK-NEXT: i32 0, label [[ONE:%.*]]
+; CHECK-NEXT: i32 1, label [[TWO:%.*]]
+; CHECK-NEXT: i32 2, label [[THREE:%.*]]
+; CHECK-NEXT: i32 3, label [[FOUR:%.*]]
+; CHECK-NEXT: ]
+; CHECK: common.ret:
+; CHECK-NEXT: ret void
+; CHECK: one:
+; CHECK-NEXT: call void @side_effect(i32 32)
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: two:
+; CHECK-NEXT: call void @side_effect(i32 64)
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: three:
+; CHECK-NEXT: call void @side_effect(i32 96)
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: four:
+; CHECK-NEXT: call void @side_effect(i32 128)
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+ switch i32 %a, label %def [
+ i32 32, label %one
+ i32 64, label %two
+ i32 96, label %three
+ i32 128, label %four
+ ]
+
+def:
+ ret void
+
+one:
+ call void @side_effect(i32 32)
+ ret void
+two:
+ call void @side_effect(i32 64)
+ ret void
+three:
+ call void @side_effect(i32 96)
+ ret void
+four:
+ call void @side_effect(i32 128)
+ ret void
+}
+
+; Base=0 is not dense after shifting, so this should still subtract the local
+; minimum before reducing the range.
+define void @test11(i32 %a) {
+; CHECK-LABEL: @test11(
+; CHECK-NEXT: [[TMP1:%.*]] = sub i32 [[A:%.*]], 40
+; CHECK-NEXT: [[TMP2:%.*]] = call i32 @llvm.fshl.i32(i32 [[TMP1]], i32 [[TMP1]], i32 27)
+; CHECK-NEXT: switch i32 [[TMP2]], label [[COMMON_RET:%.*]] [
+; CHECK-NEXT: i32 0, label [[ONE:%.*]]
+; CHECK-NEXT: i32 1, label [[TWO:%.*]]
+; CHECK-NEXT: i32 2, label [[THREE:%.*]]
+; CHECK-NEXT: i32 3, label [[FOUR:%.*]]
+; CHECK-NEXT: ]
+; CHECK: common.ret:
+; CHECK-NEXT: ret void
+; CHECK: one:
+; CHECK-NEXT: call void @side_effect(i32 40)
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: two:
+; CHECK-NEXT: call void @side_effect(i32 72)
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: three:
+; CHECK-NEXT: call void @side_effect(i32 104)
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: four:
+; CHECK-NEXT: call void @side_effect(i32 136)
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+ switch i32 %a, label %def [
+ i32 40, label %one
+ i32 72, label %two
+ i32 104, label %three
+ i32 136, label %four
+ ]
+
+def:
+ ret void
+
+one:
+ call void @side_effect(i32 40)
+ ret void
+two:
+ call void @side_effect(i32 72)
+ ret void
+three:
+ call void @side_effect(i32 104)
+ ret void
+four:
+ call void @side_effect(i32 136)
+ ret void
+}
+
+declare void @side_effect(i32)
+
!0 = !{!"function_entry_count", i32 100}
!1 = !{!"branch_weights", i32 5, i32 7, i32 11, i32 13, i32 17}
;.
>From 256f866074e3a217dcbcda1078c39a826f538f5d Mon Sep 17 00:00:00 2001
From: Drew Kersnar <dkersnar at nvidia.com>
Date: Wed, 13 May 2026 17:18:00 +0000
Subject: [PATCH 2/7] [SimplifyCFG] Improve reduceSwitchRange to avoid
unnecessary subtractions
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 57 ++++++++++---------
.../Transforms/SimplifyCFG/rangereduce.ll | 21 ++++---
2 files changed, 41 insertions(+), 37 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index f1d47abe79365..2ef57437c1de8 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7582,31 +7582,35 @@ static bool reduceSwitchRange(SwitchInst *SI, IRBuilder<> &Builder,
if (isSwitchDense(Values))
return false;
- // First, transform the values such that they start at zero and ascend.
+ auto TryBase = [&](int64_t CandidateBase, unsigned &CandidateShift) {
+ SmallVector<int64_t, 4> ReducedValues;
+ ReducedValues.reserve(Values.size());
+ uint64_t ReducedBits = 0;
+ for (int64_t V : Values) {
+ uint64_t Reduced = (uint64_t)V - (uint64_t)CandidateBase;
+ ReducedBits |= Reduced;
+ ReducedValues.push_back((int64_t)Reduced);
+ }
+
+ CandidateShift = llvm::countr_zero(ReducedBits);
+ if (CandidateShift >= 64)
+ return false;
+ if (CandidateShift > 0)
+ for (auto &V : ReducedValues)
+ V = (int64_t)((uint64_t)V >> CandidateShift);
+
+ return isSwitchDense(ReducedValues);
+ };
+
+ // Choose the base to subtract from the switch condition. The default is the
+ // local minimum, but prefer Base=0 when the case values are still dense
+ // after shifting out their common low zero bits without subtracting a base.
+ // This avoids creating an unnecessary `(condition - local_min)` expression.
int64_t Base = Values[0];
- for (auto &V : Values)
- V -= (uint64_t)(Base);
-
- // Now we have signed numbers that have been shifted so that, given enough
- // precision, there are no negative values. Since the rest of the transform
- // is bitwise only, we switch now to an unsigned representation.
-
- // This transform can be done speculatively because it is so cheap - it
- // results in a single rotate operation being inserted.
-
- // countTrailingZeros(0) returns 64. As Values is guaranteed to have more than
- // one element and LLVM disallows duplicate cases, Shift is guaranteed to be
- // less than 64.
- unsigned Shift = 64;
- for (auto &V : Values)
- Shift = std::min(Shift, (unsigned)llvm::countr_zero((uint64_t)V));
- assert(Shift < 64);
- if (Shift > 0)
- for (auto &V : Values)
- V = (int64_t)((uint64_t)V >> Shift);
-
- if (!isSwitchDense(Values))
- // Transform didn't create a dense switch.
+ unsigned Shift = 0;
+ if (Base >= 0 && TryBase(0, Shift))
+ Base = 0;
+ else if (!TryBase(Base, Shift))
return false;
// The obvious transform is to shift the switch condition right and emit a
@@ -7621,8 +7625,9 @@ static bool reduceSwitchRange(SwitchInst *SI, IRBuilder<> &Builder,
auto *Ty = cast<IntegerType>(SI->getCondition()->getType());
Builder.SetInsertPoint(SI);
- Value *Sub =
- Builder.CreateSub(SI->getCondition(), ConstantInt::getSigned(Ty, Base));
+ Value *Sub = SI->getCondition();
+ if (Base != 0)
+ Sub = Builder.CreateSub(Sub, ConstantInt::getSigned(Ty, Base));
Value *Rot = Builder.CreateIntrinsic(
Ty, Intrinsic::fshl,
{Sub, Sub, ConstantInt::get(Ty, Ty->getBitWidth() - Shift)});
diff --git a/llvm/test/Transforms/SimplifyCFG/rangereduce.ll b/llvm/test/Transforms/SimplifyCFG/rangereduce.ll
index 690543faf5644..5358e6ec68ddb 100644
--- a/llvm/test/Transforms/SimplifyCFG/rangereduce.ll
+++ b/llvm/test/Transforms/SimplifyCFG/rangereduce.ll
@@ -286,12 +286,12 @@ three:
define i32 @test9(i32 %a) {
; CHECK-LABEL: @test9(
-; CHECK-NEXT: [[TMP1:%.*]] = sub i32 [[A:%.*]], 6
-; CHECK-NEXT: [[TMP2:%.*]] = call i32 @llvm.fshl.i32(i32 [[TMP1]], i32 [[TMP1]], i32 31)
-; CHECK-NEXT: [[TMP3:%.*]] = icmp ult i32 [[TMP2]], 8
+; CHECK-NEXT: [[TMP2:%.*]] = call i32 @llvm.fshl.i32(i32 [[TMP1:%.*]], i32 [[TMP1]], i32 31)
+; CHECK-NEXT: [[SWITCH_TABLEIDX:%.*]] = sub i32 [[TMP2]], 3
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ult i32 [[SWITCH_TABLEIDX]], 8
; CHECK-NEXT: br i1 [[TMP3]], label [[SWITCH_LOOKUP:%.*]], label [[COMMON_RET:%.*]]
; CHECK: switch.lookup:
-; CHECK-NEXT: [[TMP4:%.*]] = zext nneg i32 [[TMP2]] to i64
+; CHECK-NEXT: [[TMP4:%.*]] = zext nneg i32 [[SWITCH_TABLEIDX]] to i64
; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [8 x i32], ptr @switch.table.test9, i64 0, i64 [[TMP4]]
; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i32, ptr [[SWITCH_GEP]], align 4
; CHECK-NEXT: br label [[COMMON_RET]]
@@ -321,13 +321,12 @@ three:
; dense range without subtracting the local minimum first.
define void @test10(i32 %a) {
; CHECK-LABEL: @test10(
-; CHECK-NEXT: [[TMP1:%.*]] = sub i32 [[A:%.*]], 32
-; CHECK-NEXT: [[TMP2:%.*]] = call i32 @llvm.fshl.i32(i32 [[TMP1]], i32 [[TMP1]], i32 27)
-; CHECK-NEXT: switch i32 [[TMP2]], label [[COMMON_RET:%.*]] [
-; CHECK-NEXT: i32 0, label [[ONE:%.*]]
-; CHECK-NEXT: i32 1, label [[TWO:%.*]]
-; CHECK-NEXT: i32 2, label [[THREE:%.*]]
-; CHECK-NEXT: i32 3, label [[FOUR:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.fshl.i32(i32 [[A:%.*]], i32 [[A]], i32 27)
+; CHECK-NEXT: switch i32 [[TMP1]], label [[COMMON_RET:%.*]] [
+; CHECK-NEXT: i32 1, label [[ONE:%.*]]
+; CHECK-NEXT: i32 2, label [[TWO:%.*]]
+; CHECK-NEXT: i32 3, label [[THREE:%.*]]
+; CHECK-NEXT: i32 4, label [[FOUR:%.*]]
; CHECK-NEXT: ]
; CHECK: common.ret:
; CHECK-NEXT: ret void
>From e84d0f0aefe2805497bd2e69447cfe6520917ef4 Mon Sep 17 00:00:00 2001
From: Drew Kersnar <dkersnar at nvidia.com>
Date: Wed, 20 May 2026 15:47:13 +0000
Subject: [PATCH 3/7] Reviewer feedback
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 53 +++++++++++++++--------
1 file changed, 35 insertions(+), 18 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 2ef57437c1de8..5a82eb2470b2a 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7582,7 +7582,13 @@ static bool reduceSwitchRange(SwitchInst *SI, IRBuilder<> &Builder,
if (isSwitchDense(Values))
return false;
- auto TryBase = [&](int64_t CandidateBase, unsigned &CandidateShift) {
+ struct RangeReduction {
+ int64_t Base;
+ unsigned Shift;
+ };
+
+ auto TryGetRangeReductionForBase = [&](int64_t CandidateBase)
+ -> std::optional<RangeReduction> {
SmallVector<int64_t, 4> ReducedValues;
ReducedValues.reserve(Values.size());
uint64_t ReducedBits = 0;
@@ -7592,25 +7598,34 @@ static bool reduceSwitchRange(SwitchInst *SI, IRBuilder<> &Builder,
ReducedValues.push_back((int64_t)Reduced);
}
- CandidateShift = llvm::countr_zero(ReducedBits);
+ unsigned CandidateShift = llvm::countr_zero(ReducedBits);
if (CandidateShift >= 64)
- return false;
+ return std::nullopt;
if (CandidateShift > 0)
for (auto &V : ReducedValues)
V = (int64_t)((uint64_t)V >> CandidateShift);
- return isSwitchDense(ReducedValues);
+ if (!isSwitchDense(ReducedValues))
+ return std::nullopt;
+
+ return RangeReduction{CandidateBase, CandidateShift};
+ };
+
+ auto GetRangeReduction = [&]() -> std::optional<RangeReduction> {
+ // Choose the base to subtract from the switch condition. The default is the
+ // local minimum, but prefer Base=0 when the case values are still dense
+ // after shifting out their common low zero bits without subtracting a base.
+ // This avoids creating an unnecessary `(condition - local_min)` expression.
+ int64_t LocalMin = Values[0];
+ if (LocalMin >= 0) {
+ if (auto Reduction = TryGetRangeReductionForBase(0))
+ return Reduction;
+ }
+ return TryGetRangeReductionForBase(LocalMin);
};
- // Choose the base to subtract from the switch condition. The default is the
- // local minimum, but prefer Base=0 when the case values are still dense
- // after shifting out their common low zero bits without subtracting a base.
- // This avoids creating an unnecessary `(condition - local_min)` expression.
- int64_t Base = Values[0];
- unsigned Shift = 0;
- if (Base >= 0 && TryBase(0, Shift))
- Base = 0;
- else if (!TryBase(Base, Shift))
+ std::optional<RangeReduction> Reduction = GetRangeReduction();
+ if (!Reduction)
return false;
// The obvious transform is to shift the switch condition right and emit a
@@ -7626,17 +7641,19 @@ static bool reduceSwitchRange(SwitchInst *SI, IRBuilder<> &Builder,
auto *Ty = cast<IntegerType>(SI->getCondition()->getType());
Builder.SetInsertPoint(SI);
Value *Sub = SI->getCondition();
- if (Base != 0)
- Sub = Builder.CreateSub(Sub, ConstantInt::getSigned(Ty, Base));
+ if (Reduction->Base != 0)
+ Sub = Builder.CreateSub(Sub, ConstantInt::getSigned(Ty, Reduction->Base));
Value *Rot = Builder.CreateIntrinsic(
Ty, Intrinsic::fshl,
- {Sub, Sub, ConstantInt::get(Ty, Ty->getBitWidth() - Shift)});
+ {Sub, Sub, ConstantInt::get(Ty, Ty->getBitWidth() - Reduction->Shift)});
SI->replaceUsesOfWith(SI->getCondition(), Rot);
for (auto Case : SI->cases()) {
auto *Orig = Case.getCaseValue();
- auto Sub = Orig->getValue() - APInt(Ty->getBitWidth(), Base, true);
- Case.setValue(cast<ConstantInt>(ConstantInt::get(Ty, Sub.lshr(Shift))));
+ auto Sub =
+ Orig->getValue() - APInt(Ty->getBitWidth(), Reduction->Base, true);
+ Case.setValue(
+ cast<ConstantInt>(ConstantInt::get(Ty, Sub.lshr(Reduction->Shift))));
}
return true;
}
>From be6c5edf226b547b29b8283c8d56276cdf5307a4 Mon Sep 17 00:00:00 2001
From: Drew Kersnar <dkersnar at nvidia.com>
Date: Wed, 20 May 2026 16:26:51 +0000
Subject: [PATCH 4/7] Fix formatting
---
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 5a82eb2470b2a..ce1819242a7c0 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7587,8 +7587,8 @@ static bool reduceSwitchRange(SwitchInst *SI, IRBuilder<> &Builder,
unsigned Shift;
};
- auto TryGetRangeReductionForBase = [&](int64_t CandidateBase)
- -> std::optional<RangeReduction> {
+ auto TryGetRangeReductionForBase =
+ [&](int64_t CandidateBase) -> std::optional<RangeReduction> {
SmallVector<int64_t, 4> ReducedValues;
ReducedValues.reserve(Values.size());
uint64_t ReducedBits = 0;
>From 8667e836d6cffcef842bbee12b8e0512ab906938 Mon Sep 17 00:00:00 2001
From: Drew Kersnar <dkersnar at nvidia.com>
Date: Fri, 22 May 2026 16:53:56 +0000
Subject: [PATCH 5/7] Address reviewer feedback
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 110 ++++++++++--------
.../Transforms/SimplifyCFG/rangereduce.ll | 19 +--
2 files changed, 69 insertions(+), 60 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index ce1819242a7c0..20f8573a9a487 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7058,6 +7058,40 @@ static bool isSwitchDense(ArrayRef<int64_t> Values) {
return isSwitchDense(Values.size(), Range);
}
+static std::optional<unsigned>
+getDenseSwitchRangeReductionShift(ArrayRef<int64_t> Values, int64_t Base) {
+ assert(llvm::all_of(Values, [Base](int64_t V) { return V >= Base; }) &&
+ "Base must not exceed any switch case value");
+ assert(Values.size() > 1 && "expected multiple switch cases");
+
+ // First, transform the values by subtracting Base.
+ SmallVector<int64_t, 4> ReducedValues(Values);
+ uint64_t ReducedValuesOr = 0;
+ for (auto &V : ReducedValues) {
+ uint64_t Reduced = (uint64_t)V - (uint64_t)Base;
+ ReducedValuesOr |= Reduced;
+ V = (int64_t)Reduced;
+ }
+
+ // Conceptually, the reduced values are non-negative distances from Base.
+ // Since the rest of the transform is bitwise only, treat them as unsigned
+ // bit patterns from here.
+
+ // countr_zero(0) returns 64. As Values is guaranteed to have more than
+ // one element and LLVM disallows duplicate cases, ReducedValuesOr will
+ // have at least one bit set, so Shift will be less than 64.
+ unsigned Shift = llvm::countr_zero(ReducedValuesOr);
+ assert(Shift < 64);
+ if (Shift > 0)
+ for (auto &V : ReducedValues)
+ V = (int64_t)((uint64_t)V >> Shift);
+
+ if (!isSwitchDense(ReducedValues))
+ return std::nullopt;
+
+ return Shift;
+}
+
/// Determine whether a lookup table should be built for this switch, based on
/// the number of cases, size of the table, and the types of the results.
// TODO: We could support larger than legal types by limiting based on the
@@ -7582,50 +7616,23 @@ static bool reduceSwitchRange(SwitchInst *SI, IRBuilder<> &Builder,
if (isSwitchDense(Values))
return false;
- struct RangeReduction {
- int64_t Base;
- unsigned Shift;
- };
-
- auto TryGetRangeReductionForBase =
- [&](int64_t CandidateBase) -> std::optional<RangeReduction> {
- SmallVector<int64_t, 4> ReducedValues;
- ReducedValues.reserve(Values.size());
- uint64_t ReducedBits = 0;
- for (int64_t V : Values) {
- uint64_t Reduced = (uint64_t)V - (uint64_t)CandidateBase;
- ReducedBits |= Reduced;
- ReducedValues.push_back((int64_t)Reduced);
- }
-
- unsigned CandidateShift = llvm::countr_zero(ReducedBits);
- if (CandidateShift >= 64)
- return std::nullopt;
- if (CandidateShift > 0)
- for (auto &V : ReducedValues)
- V = (int64_t)((uint64_t)V >> CandidateShift);
-
- if (!isSwitchDense(ReducedValues))
- return std::nullopt;
-
- return RangeReduction{CandidateBase, CandidateShift};
- };
-
- auto GetRangeReduction = [&]() -> std::optional<RangeReduction> {
- // Choose the base to subtract from the switch condition. The default is the
- // local minimum, but prefer Base=0 when the case values are still dense
- // after shifting out their common low zero bits without subtracting a base.
- // This avoids creating an unnecessary `(condition - local_min)` expression.
- int64_t LocalMin = Values[0];
- if (LocalMin >= 0) {
- if (auto Reduction = TryGetRangeReductionForBase(0))
- return Reduction;
- }
- return TryGetRangeReductionForBase(LocalMin);
- };
-
- std::optional<RangeReduction> Reduction = GetRangeReduction();
- if (!Reduction)
+ // Find a Base and corresponding Shift that results in a dense switch range.
+ // Values[0] is the local minimum.
+ int64_t Base = Values[0];
+ std::optional<unsigned> Shift;
+ // Prefer Base=0 when the case values are still dense after shifting out their
+ // common low zero bits without subtracting a base. This avoids creating an
+ // unnecessary `(condition - local_min)` expression.
+ if (Base >= 0) {
+ Shift = getDenseSwitchRangeReductionShift(Values, /*Base=*/0);
+ if (Shift)
+ Base = 0;
+ }
+ // Otherwise, try the local minimum as the base.
+ if (!Shift)
+ Shift = getDenseSwitchRangeReductionShift(Values, Base);
+
+ if (!Shift)
return false;
// The obvious transform is to shift the switch condition right and emit a
@@ -7637,23 +7644,24 @@ static bool reduceSwitchRange(SwitchInst *SI, IRBuilder<> &Builder,
// shift and puts the shifted-off bits in the uppermost bits. If any of these
// are nonzero then the switch condition will be very large and will hit the
// default case.
+ //
+ // This transform can be done speculatively because it is so cheap - it
+ // results in a single rotate operation being inserted.
auto *Ty = cast<IntegerType>(SI->getCondition()->getType());
Builder.SetInsertPoint(SI);
Value *Sub = SI->getCondition();
- if (Reduction->Base != 0)
- Sub = Builder.CreateSub(Sub, ConstantInt::getSigned(Ty, Reduction->Base));
+ if (Base != 0)
+ Sub = Builder.CreateSub(Sub, ConstantInt::getSigned(Ty, Base));
Value *Rot = Builder.CreateIntrinsic(
Ty, Intrinsic::fshl,
- {Sub, Sub, ConstantInt::get(Ty, Ty->getBitWidth() - Reduction->Shift)});
+ {Sub, Sub, ConstantInt::get(Ty, Ty->getBitWidth() - *Shift)});
SI->replaceUsesOfWith(SI->getCondition(), Rot);
for (auto Case : SI->cases()) {
auto *Orig = Case.getCaseValue();
- auto Sub =
- Orig->getValue() - APInt(Ty->getBitWidth(), Reduction->Base, true);
- Case.setValue(
- cast<ConstantInt>(ConstantInt::get(Ty, Sub.lshr(Reduction->Shift))));
+ auto Sub = Orig->getValue() - APInt(Ty->getBitWidth(), Base, true);
+ Case.setValue(cast<ConstantInt>(ConstantInt::get(Ty, Sub.lshr(*Shift))));
}
return true;
}
diff --git a/llvm/test/Transforms/SimplifyCFG/rangereduce.ll b/llvm/test/Transforms/SimplifyCFG/rangereduce.ll
index 5358e6ec68ddb..61ed03831724c 100644
--- a/llvm/test/Transforms/SimplifyCFG/rangereduce.ll
+++ b/llvm/test/Transforms/SimplifyCFG/rangereduce.ll
@@ -286,13 +286,13 @@ three:
define i32 @test9(i32 %a) {
; CHECK-LABEL: @test9(
-; CHECK-NEXT: [[TMP2:%.*]] = call i32 @llvm.fshl.i32(i32 [[TMP1:%.*]], i32 [[TMP1]], i32 31)
-; CHECK-NEXT: [[SWITCH_TABLEIDX:%.*]] = sub i32 [[TMP2]], 3
-; CHECK-NEXT: [[TMP3:%.*]] = icmp ult i32 [[SWITCH_TABLEIDX]], 8
-; CHECK-NEXT: br i1 [[TMP3]], label [[SWITCH_LOOKUP:%.*]], label [[COMMON_RET:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.fshl.i32(i32 [[A:%.*]], i32 [[A]], i32 31)
+; CHECK-NEXT: [[SWITCH_TABLEIDX:%.*]] = sub i32 [[TMP1]], 3
+; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[SWITCH_TABLEIDX]], 8
+; CHECK-NEXT: br i1 [[TMP2]], label [[SWITCH_LOOKUP:%.*]], label [[COMMON_RET:%.*]]
; CHECK: switch.lookup:
-; CHECK-NEXT: [[TMP4:%.*]] = zext nneg i32 [[SWITCH_TABLEIDX]] to i64
-; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [8 x i32], ptr @switch.table.test9, i64 0, i64 [[TMP4]]
+; CHECK-NEXT: [[TMP3:%.*]] = zext nneg i32 [[SWITCH_TABLEIDX]] to i64
+; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [8 x i32], ptr @switch.table.test9, i64 0, i64 [[TMP3]]
; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i32, ptr [[SWITCH_GEP]], align 4
; CHECK-NEXT: br label [[COMMON_RET]]
; CHECK: common.ret:
@@ -318,11 +318,12 @@ three:
}
; Base=0 is profitable here: shifting out the common low zero bits produces a
-; dense range without subtracting the local minimum first.
+; dense range without subtracting the local minimum first. SimplifyCFG does not
+; form a lookup table here, so the reduced switch is tested directly.
define void @test10(i32 %a) {
; CHECK-LABEL: @test10(
-; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.fshl.i32(i32 [[A:%.*]], i32 [[A]], i32 27)
-; CHECK-NEXT: switch i32 [[TMP1]], label [[COMMON_RET:%.*]] [
+; CHECK-NEXT: [[TMP:%.*]] = call i32 @llvm.fshl.i32(i32 [[A:%.*]], i32 [[A]], i32 27)
+; CHECK-NEXT: switch i32 [[TMP]], label [[COMMON_RET:%.*]] [
; CHECK-NEXT: i32 1, label [[ONE:%.*]]
; CHECK-NEXT: i32 2, label [[TWO:%.*]]
; CHECK-NEXT: i32 3, label [[THREE:%.*]]
>From 346288668524624c9d660b62391e9158a083a22e Mon Sep 17 00:00:00 2001
From: Drew Kersnar <dkersnar at nvidia.com>
Date: Tue, 2 Jun 2026 16:41:18 +0000
Subject: [PATCH 6/7] Reviewer feedback
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 20f8573a9a487..42b5074934581 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7060,9 +7060,9 @@ static bool isSwitchDense(ArrayRef<int64_t> Values) {
static std::optional<unsigned>
getDenseSwitchRangeReductionShift(ArrayRef<int64_t> Values, int64_t Base) {
- assert(llvm::all_of(Values, [Base](int64_t V) { return V >= Base; }) &&
- "Base must not exceed any switch case value");
assert(Values.size() > 1 && "expected multiple switch cases");
+ if (!llvm::all_of(Values, [Base](int64_t V) { return V >= Base; }))
+ return std::nullopt;
// First, transform the values by subtracting Base.
SmallVector<int64_t, 4> ReducedValues(Values);
@@ -7623,13 +7623,9 @@ static bool reduceSwitchRange(SwitchInst *SI, IRBuilder<> &Builder,
// Prefer Base=0 when the case values are still dense after shifting out their
// common low zero bits without subtracting a base. This avoids creating an
// unnecessary `(condition - local_min)` expression.
- if (Base >= 0) {
- Shift = getDenseSwitchRangeReductionShift(Values, /*Base=*/0);
- if (Shift)
- Base = 0;
- }
- // Otherwise, try the local minimum as the base.
- if (!Shift)
+ if ((Shift = getDenseSwitchRangeReductionShift(Values, /*Base=*/0)))
+ Base = 0;
+ else if (Base != 0)
Shift = getDenseSwitchRangeReductionShift(Values, Base);
if (!Shift)
>From 6a5c41b27408455509078e29082de9893fb3df8b Mon Sep 17 00:00:00 2001
From: Drew Kersnar <dkersnar at nvidia.com>
Date: Thu, 4 Jun 2026 23:07:19 +0000
Subject: [PATCH 7/7] Use the more conservative density measurement for Base=0
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 3 ++-
llvm/test/Transforms/SimplifyCFG/rangereduce.ll | 12 ++++++------
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index e783ee21fe830..0fee66d1cec88 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7683,7 +7683,8 @@ static bool reduceSwitchRange(SwitchInst *SI, IRBuilder<> &Builder,
// Prefer Base=0 when the case values are still dense after shifting out their
// common low zero bits without subtracting a base. This avoids creating an
// unnecessary `(condition - local_min)` expression.
- if ((Shift = getDenseSwitchRangeReductionShift(Values, /*Base=*/0, OptSize)))
+ if ((Shift = getDenseSwitchRangeReductionShift(Values, /*Base=*/0,
+ /*OptSize=*/true)))
Base = 0;
else if (Base != 0)
Shift = getDenseSwitchRangeReductionShift(Values, Base, OptSize);
diff --git a/llvm/test/Transforms/SimplifyCFG/rangereduce.ll b/llvm/test/Transforms/SimplifyCFG/rangereduce.ll
index 084e6d9a45122..fa9da7b9a28bf 100644
--- a/llvm/test/Transforms/SimplifyCFG/rangereduce.ll
+++ b/llvm/test/Transforms/SimplifyCFG/rangereduce.ll
@@ -286,13 +286,13 @@ three:
define i32 @test9(i32 %a) {
; CHECK-LABEL: @test9(
-; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.fshl.i32(i32 [[A:%.*]], i32 [[A]], i32 31)
-; CHECK-NEXT: [[SWITCH_TABLEIDX:%.*]] = sub i32 [[TMP1]], 3
-; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[SWITCH_TABLEIDX]], 28
-; CHECK-NEXT: br i1 [[TMP2]], label [[SWITCH_LOOKUP:%.*]], label [[COMMON_RET:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = sub i32 [[A:%.*]], 6
+; CHECK-NEXT: [[TMP2:%.*]] = call i32 @llvm.fshl.i32(i32 [[TMP1]], i32 [[TMP1]], i32 31)
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ult i32 [[TMP2]], 28
+; CHECK-NEXT: br i1 [[TMP3]], label [[SWITCH_LOOKUP:%.*]], label [[COMMON_RET:%.*]]
; CHECK: switch.lookup:
-; CHECK-NEXT: [[TMP3:%.*]] = zext nneg i32 [[SWITCH_TABLEIDX]] to i64
-; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [28 x i32], ptr @switch.table.test9, i64 0, i64 [[TMP3]]
+; CHECK-NEXT: [[TMP4:%.*]] = zext nneg i32 [[TMP2]] to i64
+; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [28 x i32], ptr @switch.table.test9, i64 0, i64 [[TMP4]]
; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i32, ptr [[SWITCH_GEP]], align 4
; CHECK-NEXT: br label [[COMMON_RET]]
; CHECK: common.ret:
More information about the llvm-commits
mailing list