[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