[llvm] 00871e2 - [SimplifyCFG] Try to fold switch with single result value and power-of-2 cases to mask+select
via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 14 09:17:39 PDT 2022
Author: chenglin.bi
Date: 2022-04-15T00:10:00+08:00
New Revision: 00871e2f4f9fdebcfe84f998c17c47465079bc67
URL: https://github.com/llvm/llvm-project/commit/00871e2f4f9fdebcfe84f998c17c47465079bc67
DIFF: https://github.com/llvm/llvm-project/commit/00871e2f4f9fdebcfe84f998c17c47465079bc67.diff
LOG: [SimplifyCFG] Try to fold switch with single result value and power-of-2 cases to mask+select
When switch with 2^n cases go to one result, check if the 2^n cases can be covered by n bit masks.
If yes we can use "and condition, ~mask" to simplify the switch
case 0 2 4 6 -> and condition, -7
https://alive2.llvm.org/ce/z/jjH_0N
case 0 2 8 10 -> and condition, -11
https://alive2.llvm.org/ce/z/K7E-2V
case 2 4 8 12 -> and (sub condition, 2), -11
https://alive2.llvm.org/ce/z/CrxbYg
Fix one case of https://github.com/llvm/llvm-project/issues/39957
Reviewed By: spatel
Differential Revision: https://reviews.llvm.org/D122485
Added:
Modified:
llvm/lib/Transforms/Utils/SimplifyCFG.cpp
llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 76444afc7f87e..fb942bbd0574d 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -169,7 +169,7 @@ static cl::opt<bool> EnableMergeCompatibleInvokes(
cl::desc("Allow SimplifyCFG to merge invokes together when appropriate"));
static cl::opt<unsigned> MaxSwitchCasesPerResult(
- "max-switch-cases-per-result", cl::Hidden, cl::init(2),
+ "max-switch-cases-per-result", cl::Hidden, cl::init(16),
cl::desc("Limit cases to analyze when converting a switch to select"));
STATISTIC(NumBitMaps, "Number of switch instructions turned into bitmaps");
@@ -5710,15 +5710,46 @@ static Value *foldSwitchToSelect(const SwitchCaseResultVectorTy &ResultVector,
}
// Handle the degenerate case where two cases have the same result value.
- if (ResultVector.size() == 1 && ResultVector[0].second.size() == 2 &&
- DefaultResult) {
+ if (ResultVector.size() == 1 && DefaultResult) {
ArrayRef<ConstantInt *> CaseValues = ResultVector[0].second;
- Value *Cmp1 = Builder.CreateICmpEQ(Condition, CaseValues[0],
- "switch.selectcmp.case1");
- Value *Cmp2 = Builder.CreateICmpEQ(Condition, CaseValues[1],
- "switch.selectcmp.case2");
- Value *Cmp = Builder.CreateOr(Cmp1, Cmp2, "switch.selectcmp");
- return Builder.CreateSelect(Cmp, ResultVector[0].first, DefaultResult);
+ unsigned CaseCount = CaseValues.size();
+ // n bits group cases map to the same result:
+ // case 0,4 -> Cond & 0b1..1011 == 0 ? result : default
+ // case 0,2,4,6 -> Cond & 0b1..1001 == 0 ? result : default
+ // case 0,2,8,10 -> Cond & 0b1..0101 == 0 ? result : default
+ if (isPowerOf2_32(CaseCount)) {
+ ConstantInt *MinCaseVal = CaseValues[0];
+ // Find mininal value.
+ for (auto Case : CaseValues)
+ if (Case->getValue().slt(MinCaseVal->getValue()))
+ MinCaseVal = Case;
+
+ // Mark the bits case number touched.
+ APInt BitMask = APInt::getZero(MinCaseVal->getBitWidth());
+ for (auto Case : CaseValues)
+ BitMask |= (Case->getValue() - MinCaseVal->getValue());
+
+ // Check if cases with the same result can cover all number
+ // in touched bits.
+ if (BitMask.countPopulation() == Log2_32(CaseCount)) {
+ if (!MinCaseVal->isNullValue())
+ Condition = Builder.CreateSub(Condition, MinCaseVal);
+ Value *And = Builder.CreateAnd(Condition, ~BitMask, "switch.and");
+ Value *Cmp = Builder.CreateICmpEQ(
+ And, Constant::getNullValue(And->getType()), "switch.selectcmp");
+ return Builder.CreateSelect(Cmp, ResultVector[0].first, DefaultResult);
+ }
+ }
+
+ // Handle the degenerate case where two cases have the same value.
+ if (CaseValues.size() == 2) {
+ Value *Cmp1 = Builder.CreateICmpEQ(Condition, CaseValues[0],
+ "switch.selectcmp.case1");
+ Value *Cmp2 = Builder.CreateICmpEQ(Condition, CaseValues[1],
+ "switch.selectcmp.case2");
+ Value *Cmp = Builder.CreateOr(Cmp1, Cmp2, "switch.selectcmp");
+ return Builder.CreateSelect(Cmp, ResultVector[0].first, DefaultResult);
+ }
}
return nullptr;
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll b/llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll
index c6e2476552e38..f15ce0e6adfed 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll
@@ -69,9 +69,8 @@ return:
define i1 @switch_to_select_same2_case_results_
diff erent_default(i8 %0) {
; CHECK-LABEL: @switch_to_select_same2_case_results_
diff erent_default(
-; CHECK-NEXT: [[SWITCH_SELECTCMP_CASE1:%.*]] = icmp eq i8 [[TMP0:%.*]], 4
-; CHECK-NEXT: [[SWITCH_SELECTCMP_CASE2:%.*]] = icmp eq i8 [[TMP0]], 0
-; CHECK-NEXT: [[SWITCH_SELECTCMP:%.*]] = or i1 [[SWITCH_SELECTCMP_CASE1]], [[SWITCH_SELECTCMP_CASE2]]
+; CHECK-NEXT: [[SWITCH_AND:%.*]] = and i8 [[TMP0:%.*]], -5
+; CHECK-NEXT: [[SWITCH_SELECTCMP:%.*]] = icmp eq i8 [[SWITCH_AND]], 0
; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[SWITCH_SELECTCMP]], i1 true, i1 false
; CHECK-NEXT: ret i1 [[TMP2]]
;
@@ -90,11 +89,11 @@ define i1 @switch_to_select_same2_case_results_
diff erent_default(i8 %0) {
define i1 @switch_to_select_same2_case_results_
diff erent_default_and_positive_offset_for_case(i8 %0) {
; CHECK-LABEL: @switch_to_select_same2_case_results_
diff erent_default_and_positive_offset_for_case(
-; CHECK-NEXT: [[SWITCH_SELECTCMP_CASE1:%.*]] = icmp eq i8 [[TMP0:%.*]], 43
-; CHECK-NEXT: [[SWITCH_SELECTCMP_CASE2:%.*]] = icmp eq i8 [[TMP0]], 45
-; CHECK-NEXT: [[SWITCH_SELECTCMP:%.*]] = or i1 [[SWITCH_SELECTCMP_CASE1]], [[SWITCH_SELECTCMP_CASE2]]
-; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[SWITCH_SELECTCMP]], i1 true, i1 false
-; CHECK-NEXT: ret i1 [[TMP2]]
+; CHECK-NEXT: [[TMP2:%.*]] = sub i8 [[TMP0:%.*]], 43
+; CHECK-NEXT: [[SWITCH_AND:%.*]] = and i8 [[TMP2]], -3
+; CHECK-NEXT: [[SWITCH_SELECTCMP:%.*]] = icmp eq i8 [[SWITCH_AND]], 0
+; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[SWITCH_SELECTCMP]], i1 true, i1 false
+; CHECK-NEXT: ret i1 [[TMP3]]
;
switch i8 %0, label %2 [
i8 43, label %3
@@ -112,11 +111,11 @@ define i1 @switch_to_select_same2_case_results_
diff erent_default_and_positive_of
define i8 @switch_to_select_same2_case_results_
diff erent_default_and_negative_offset_for_case(i32 %i) {
; CHECK-LABEL: @switch_to_select_same2_case_results_
diff erent_default_and_negative_offset_for_case(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[SWITCH_SELECTCMP_CASE1:%.*]] = icmp eq i32 [[I:%.*]], -3
-; CHECK-NEXT: [[SWITCH_SELECTCMP_CASE2:%.*]] = icmp eq i32 [[I]], -5
-; CHECK-NEXT: [[SWITCH_SELECTCMP:%.*]] = or i1 [[SWITCH_SELECTCMP_CASE1]], [[SWITCH_SELECTCMP_CASE2]]
-; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[SWITCH_SELECTCMP]], i8 3, i8 42
-; CHECK-NEXT: ret i8 [[TMP0]]
+; CHECK-NEXT: [[TMP0:%.*]] = sub i32 [[I:%.*]], -5
+; CHECK-NEXT: [[SWITCH_AND:%.*]] = and i32 [[TMP0]], -3
+; CHECK-NEXT: [[SWITCH_SELECTCMP:%.*]] = icmp eq i32 [[SWITCH_AND]], 0
+; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[SWITCH_SELECTCMP]], i8 3, i8 42
+; CHECK-NEXT: ret i8 [[TMP1]]
;
entry:
switch i32 %i, label %default [
@@ -135,16 +134,9 @@ end:
define i1 @switch_to_select_same4_case_results_
diff erent_default(i32 %i) {
; CHECK-LABEL: @switch_to_select_same4_case_results_
diff erent_default(
; CHECK-NEXT: entry:
-; CHECK-NEXT: switch i32 [[I:%.*]], label [[LOR_RHS:%.*]] [
-; CHECK-NEXT: i32 0, label [[LOR_END:%.*]]
-; CHECK-NEXT: i32 2, label [[LOR_END]]
-; CHECK-NEXT: i32 4, label [[LOR_END]]
-; CHECK-NEXT: i32 6, label [[LOR_END]]
-; CHECK-NEXT: ]
-; CHECK: lor.rhs:
-; CHECK-NEXT: br label [[LOR_END]]
-; CHECK: lor.end:
-; CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ true, [[ENTRY:%.*]] ], [ false, [[LOR_RHS]] ], [ true, [[ENTRY]] ], [ true, [[ENTRY]] ], [ true, [[ENTRY]] ]
+; CHECK-NEXT: [[SWITCH_AND:%.*]] = and i32 [[I:%.*]], -7
+; CHECK-NEXT: [[SWITCH_SELECTCMP:%.*]] = icmp eq i32 [[SWITCH_AND]], 0
+; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[SWITCH_SELECTCMP]], i1 true, i1 false
; CHECK-NEXT: ret i1 [[TMP0]]
;
entry:
@@ -166,16 +158,9 @@ lor.end:
define i1 @switch_to_select_same4_case_results_
diff erent_default_alt_bitmask(i32 %i) {
; CHECK-LABEL: @switch_to_select_same4_case_results_
diff erent_default_alt_bitmask(
; CHECK-NEXT: entry:
-; CHECK-NEXT: switch i32 [[I:%.*]], label [[LOR_RHS:%.*]] [
-; CHECK-NEXT: i32 0, label [[LOR_END:%.*]]
-; CHECK-NEXT: i32 2, label [[LOR_END]]
-; CHECK-NEXT: i32 8, label [[LOR_END]]
-; CHECK-NEXT: i32 10, label [[LOR_END]]
-; CHECK-NEXT: ]
-; CHECK: lor.rhs:
-; CHECK-NEXT: br label [[LOR_END]]
-; CHECK: lor.end:
-; CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ true, [[ENTRY:%.*]] ], [ false, [[LOR_RHS]] ], [ true, [[ENTRY]] ], [ true, [[ENTRY]] ], [ true, [[ENTRY]] ]
+; CHECK-NEXT: [[SWITCH_AND:%.*]] = and i32 [[I:%.*]], -11
+; CHECK-NEXT: [[SWITCH_SELECTCMP:%.*]] = icmp eq i32 [[SWITCH_AND]], 0
+; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[SWITCH_SELECTCMP]], i1 true, i1 false
; CHECK-NEXT: ret i1 [[TMP0]]
;
entry:
@@ -197,17 +182,11 @@ lor.end:
define i1 @switch_to_select_same4_case_results_
diff erent_default_positive_offset(i32 %i) {
; CHECK-LABEL: @switch_to_select_same4_case_results_
diff erent_default_positive_offset(
; CHECK-NEXT: entry:
-; CHECK-NEXT: switch i32 [[I:%.*]], label [[LOR_RHS:%.*]] [
-; CHECK-NEXT: i32 2, label [[LOR_END:%.*]]
-; CHECK-NEXT: i32 4, label [[LOR_END]]
-; CHECK-NEXT: i32 10, label [[LOR_END]]
-; CHECK-NEXT: i32 12, label [[LOR_END]]
-; CHECK-NEXT: ]
-; CHECK: lor.rhs:
-; CHECK-NEXT: br label [[LOR_END]]
-; CHECK: lor.end:
-; CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ true, [[ENTRY:%.*]] ], [ false, [[LOR_RHS]] ], [ true, [[ENTRY]] ], [ true, [[ENTRY]] ], [ true, [[ENTRY]] ]
-; CHECK-NEXT: ret i1 [[TMP0]]
+; CHECK-NEXT: [[TMP0:%.*]] = sub i32 [[I:%.*]], 2
+; CHECK-NEXT: [[SWITCH_AND:%.*]] = and i32 [[TMP0]], -11
+; CHECK-NEXT: [[SWITCH_SELECTCMP:%.*]] = icmp eq i32 [[SWITCH_AND]], 0
+; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[SWITCH_SELECTCMP]], i1 true, i1 false
+; CHECK-NEXT: ret i1 [[TMP1]]
;
entry:
switch i32 %i, label %lor.rhs [
@@ -285,6 +264,7 @@ lor.end:
ret i1 %0
}
+; TODO: we can produce the optimal code when there is no default also
define i8 @switch_to_select_two_case_results_no_default(i32 %i) {
; CHECK-LABEL: @switch_to_select_two_case_results_no_default(
; CHECK-NEXT: entry:
More information about the llvm-commits
mailing list