[llvm] 338fd8b - [SimplifyCFG] Transform switch to select when common bits uniquely identify one case (#145233)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 2 09:16:16 PDT 2025


Author: Gábor Spaits
Date: 2025-07-02T18:16:12+02:00
New Revision: 338fd8b12ce67eff73ed0a5c2174bee077fcbcbe

URL: https://github.com/llvm/llvm-project/commit/338fd8b12ce67eff73ed0a5c2174bee077fcbcbe
DIFF: https://github.com/llvm/llvm-project/commit/338fd8b12ce67eff73ed0a5c2174bee077fcbcbe.diff

LOG: [SimplifyCFG] Transform switch to select when common bits uniquely identify one case (#145233)

Fix #141753 .

This patch introduces a new check, that tries to decide if the
conjunction of all the values uniquely identify the accepted values by
the switch.

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 147d2060e8509..a75f29000ca18 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -6198,7 +6198,7 @@ static bool initializeUniqueCases(SwitchInst *SI, PHINode *&PHI,
 // TODO: Handle switches with more than 2 cases that map to the same result.
 static Value *foldSwitchToSelect(const SwitchCaseResultVectorTy &ResultVector,
                                  Constant *DefaultResult, Value *Condition,
-                                 IRBuilder<> &Builder) {
+                                 IRBuilder<> &Builder, const DataLayout &DL) {
   // If we are selecting between only two cases transform into a simple
   // select or a two-way select if default is possible.
   // Example:
@@ -6234,10 +6234,33 @@ static Value *foldSwitchToSelect(const SwitchCaseResultVectorTy &ResultVector,
     // 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 there are bits that are set exclusively by CaseValues, we
+      // can transform the switch into a select if the conjunction of
+      // all the values uniquely identify CaseValues.
+      APInt AndMask = APInt::getAllOnes(MinCaseVal->getBitWidth());
+
+      // Find the minimum value and compute the and of all the case values.
+      for (auto *Case : CaseValues) {
         if (Case->getValue().slt(MinCaseVal->getValue()))
           MinCaseVal = Case;
+        AndMask &= Case->getValue();
+      }
+      KnownBits Known = computeKnownBits(Condition, DL);
+
+      if (!AndMask.isZero() && Known.getMaxValue().uge(AndMask)) {
+        // Compute the number of bits that are free to vary.
+        unsigned FreeBits = Known.countMaxActiveBits() - AndMask.popcount();
+
+        // Check if the number of values covered by the mask is equal
+        // to the number of cases.
+        if (FreeBits == Log2_32(CaseCount)) {
+          Value *And = Builder.CreateAnd(Condition, AndMask);
+          Value *Cmp = Builder.CreateICmpEQ(
+              And, Constant::getIntegerValue(And->getType(), AndMask));
+          return Builder.CreateSelect(Cmp, ResultVector[0].first,
+                                      DefaultResult);
+        }
+      }
 
       // Mark the bits case number touched.
       APInt BitMask = APInt::getZero(MinCaseVal->getBitWidth());
@@ -6325,7 +6348,7 @@ static bool trySwitchToSelect(SwitchInst *SI, IRBuilder<> &Builder,
   assert(PHI != nullptr && "PHI for value select not found");
   Builder.SetInsertPoint(SI);
   Value *SelectValue =
-      foldSwitchToSelect(UniqueResults, DefaultResult, Cond, Builder);
+      foldSwitchToSelect(UniqueResults, DefaultResult, Cond, Builder, DL);
   if (!SelectValue)
     return false;
 

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 50998e447b71d..fe2e897125eb8 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll
@@ -309,3 +309,444 @@ end:
   %t0 = phi i8 [ 42, %case1 ], [ 42, %case2 ], [ 44, %case3 ], [ 44, %case4 ]
   ret i8 %t0
 }
+
+define i1 @no_range(i8 %f) {
+; CHECK-LABEL: @no_range(
+; CHECK-NEXT:  bb3:
+; CHECK-NEXT:    [[TMP0:%.*]] = and i8 [[F:%.*]], 60
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8 [[TMP0]], 60
+; CHECK-NEXT:    [[TMP2:%.*]] = select i1 [[TMP1]], i1 true, i1 false
+; CHECK-NEXT:    ret i1 [[TMP2]]
+;
+  switch i8 %f, label %bb1 [
+  i8 60, label %bb2
+  i8 61, label %bb2
+  i8 62, label %bb2
+  i8 63, label %bb2
+  i8 124, label %bb2
+  i8 188, label %bb2
+  i8 252, label %bb2
+  i8 189, label %bb2
+  i8 190, label %bb2
+  i8 191, label %bb2
+  i8 125, label %bb2
+  i8 126, label %bb2
+  i8 127, label %bb2
+  i8 253, label %bb2
+  i8 254, label %bb2
+  i8 255, label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}
+
+define i1 @negative_no_range(i8 %f) {
+; CHECK-LABEL: @negative_no_range(
+; CHECK-NEXT:    switch i8 [[F:%.*]], label [[BB3:%.*]] [
+; CHECK-NEXT:      i8 52, label [[BB2:%.*]]
+; CHECK-NEXT:      i8 61, label [[BB2]]
+; CHECK-NEXT:      i8 62, label [[BB2]]
+; CHECK-NEXT:      i8 63, label [[BB2]]
+; CHECK-NEXT:      i8 124, label [[BB2]]
+; CHECK-NEXT:      i8 -68, label [[BB2]]
+; CHECK-NEXT:      i8 -4, label [[BB2]]
+; CHECK-NEXT:      i8 -67, label [[BB2]]
+; CHECK-NEXT:      i8 -66, label [[BB2]]
+; CHECK-NEXT:      i8 -65, label [[BB2]]
+; CHECK-NEXT:      i8 125, label [[BB2]]
+; CHECK-NEXT:      i8 126, label [[BB2]]
+; CHECK-NEXT:      i8 127, label [[BB2]]
+; CHECK-NEXT:      i8 -3, label [[BB2]]
+; CHECK-NEXT:      i8 -2, label [[BB2]]
+; CHECK-NEXT:      i8 -1, label [[BB2]]
+; CHECK-NEXT:    ]
+; CHECK:       bb2:
+; CHECK-NEXT:    br label [[BB3]]
+; CHECK:       bb3:
+; CHECK-NEXT:    [[_0_SROA_0_0:%.*]] = phi i1 [ true, [[BB2]] ], [ false, [[TMP0:%.*]] ]
+; CHECK-NEXT:    ret i1 [[_0_SROA_0_0]]
+;
+  switch i8 %f, label %bb1 [
+  i8 52, label %bb2
+  i8 61, label %bb2
+  i8 62, label %bb2
+  i8 63, label %bb2
+  i8 124, label %bb2
+  i8 188, label %bb2
+  i8 252, label %bb2
+  i8 189, label %bb2
+  i8 190, label %bb2
+  i8 191, label %bb2
+  i8 125, label %bb2
+  i8 126, label %bb2
+  i8 127, label %bb2
+  i8 253, label %bb2
+  i8 254, label %bb2
+  i8 255, label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}
+
+; Using ranges.
+
+define i1 @range0to4odd(i8 range(i8 0, 4) %f) {
+; CHECK-LABEL: @range0to4odd(
+; CHECK-NEXT:  bb3:
+; CHECK-NEXT:    [[TMP0:%.*]] = and i8 [[F:%.*]], 1
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8 [[TMP0]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = select i1 [[TMP1]], i1 true, i1 false
+; CHECK-NEXT:    ret i1 [[TMP2]]
+;
+  switch i8 %f, label %bb1 [
+  i8 1, label %bb2
+  i8 3, label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}
+
+define i1 @range1to4odd(i8 range(i8 1, 4) %f) {
+; CHECK-LABEL: @range1to4odd(
+; CHECK-NEXT:  bb3:
+; CHECK-NEXT:    [[TMP0:%.*]] = and i8 [[F:%.*]], 1
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8 [[TMP0]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = select i1 [[TMP1]], i1 true, i1 false
+; CHECK-NEXT:    ret i1 [[TMP2]]
+;
+  switch i8 %f, label %bb1 [
+  i8 1, label %bb2
+  i8 3, label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}
+
+define i1 @range0to8odd(i8 range(i8 0, 8) %f) {
+; CHECK-LABEL: @range0to8odd(
+; CHECK-NEXT:  bb3:
+; CHECK-NEXT:    [[TMP0:%.*]] = and i8 [[F:%.*]], 1
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8 [[TMP0]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = select i1 [[TMP1]], i1 true, i1 false
+; CHECK-NEXT:    ret i1 [[TMP2]]
+;
+  switch i8 %f, label %bb1 [
+  i8 1, label %bb2
+  i8 3, label %bb2
+  i8 5, label %bb2
+  i8 7, label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}
+
+define i1 @range0to8most_significant_bit(i8 range(i8 0, 8) %f) {
+; CHECK-LABEL: @range0to8most_significant_bit(
+; CHECK-NEXT:  bb3:
+; CHECK-NEXT:    [[TMP0:%.*]] = and i8 [[F:%.*]], 4
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8 [[TMP0]], 4
+; CHECK-NEXT:    [[TMP2:%.*]] = select i1 [[TMP1]], i1 true, i1 false
+; CHECK-NEXT:    ret i1 [[TMP2]]
+;
+  switch i8 %f, label %bb1 [
+  i8 4, label %bb2
+  i8 5, label %bb2
+  i8 6, label %bb2
+  i8 7, label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}
+
+define i1 @range0to15_middle_two_bits(i8 range(i8 0, 16) %f) {
+; CHECK-LABEL: @range0to15_middle_two_bits(
+; CHECK-NEXT:  bb3:
+; CHECK-NEXT:    [[TMP0:%.*]] = and i8 [[F:%.*]], 6
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8 [[TMP0]], 6
+; CHECK-NEXT:    [[TMP2:%.*]] = select i1 [[TMP1]], i1 true, i1 false
+; CHECK-NEXT:    ret i1 [[TMP2]]
+;
+  switch i8 %f, label %bb1 [
+  i8 6, label %bb2
+  i8 7, label %bb2
+  i8 14, label %bb2
+  i8 15, label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}
+
+define i1 @negative_range0to15(i8 range(i8 0, 16) %f) {
+; CHECK-LABEL: @negative_range0to15(
+; CHECK-NEXT:    switch i8 [[F:%.*]], label [[BB3:%.*]] [
+; CHECK-NEXT:      i8 6, label [[BB2:%.*]]
+; CHECK-NEXT:      i8 7, label [[BB2]]
+; CHECK-NEXT:      i8 14, label [[BB2]]
+; CHECK-NEXT:    ]
+; CHECK:       bb2:
+; CHECK-NEXT:    br label [[BB3]]
+; CHECK:       bb3:
+; CHECK-NEXT:    [[_0_SROA_0_0:%.*]] = phi i1 [ true, [[BB2]] ], [ false, [[TMP0:%.*]] ]
+; CHECK-NEXT:    ret i1 [[_0_SROA_0_0]]
+;
+  switch i8 %f, label %bb1 [
+  i8 6, label %bb2
+  i8 7, label %bb2
+  i8 14, label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}
+
+define i1 @negative_range0to15_pow_2(i8 range(i8 0, 16) %f) {
+; CHECK-LABEL: @negative_range0to15_pow_2(
+; CHECK-NEXT:  bb3:
+; CHECK-NEXT:    [[TMP0:%.*]] = sub i8 [[F:%.*]], 6
+; CHECK-NEXT:    [[SWITCH_AND:%.*]] = and i8 [[TMP0]], -2
+; CHECK-NEXT:    [[SWITCH_SELECTCMP:%.*]] = icmp eq i8 [[SWITCH_AND]], 0
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[SWITCH_SELECTCMP]], i1 true, i1 false
+; CHECK-NEXT:    ret i1 [[TMP1]]
+;
+  switch i8 %f, label %bb1 [
+  i8 6, label %bb2
+  i8 7, label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}
+
+define i1 @negative_range0to5even(i8 range(i8 0, 5) %f) {
+; CHECK-LABEL: @negative_range0to5even(
+; CHECK-NEXT:  bb3:
+; CHECK-NEXT:    [[TMP0:%.*]] = sub i8 [[F:%.*]], 2
+; CHECK-NEXT:    [[SWITCH_AND:%.*]] = and i8 [[TMP0]], -3
+; CHECK-NEXT:    [[SWITCH_SELECTCMP:%.*]] = icmp eq i8 [[SWITCH_AND]], 0
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[SWITCH_SELECTCMP]], i1 true, i1 false
+; CHECK-NEXT:    ret i1 [[TMP1]]
+;
+  switch i8 %f, label %bb1 [
+  i8 2, label %bb2
+  i8 4, label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}
+
+define i1 @range0to15_corner_case(i8 range(i8 0, 16) %f) {
+; CHECK-LABEL: @range0to15_corner_case(
+; CHECK-NEXT:  bb3:
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i8 [[F:%.*]], 15
+; CHECK-NEXT:    [[DOT:%.*]] = select i1 [[COND]], i1 true, i1 false
+; CHECK-NEXT:    ret i1 [[DOT]]
+;
+  switch i8 %f, label %bb1 [
+  i8 15, label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}
+
+define i1 @negative_range0to15_corner_case(i8 range(i8 0, 16) %f) {
+; CHECK-LABEL: @negative_range0to15_corner_case(
+; CHECK-NEXT:  bb3:
+; CHECK-NEXT:    [[SWITCH_SELECTCMP_CASE1:%.*]] = icmp eq i8 [[F:%.*]], 15
+; CHECK-NEXT:    [[SWITCH_SELECTCMP_CASE2:%.*]] = icmp eq i8 [[F]], 8
+; CHECK-NEXT:    [[SWITCH_SELECTCMP:%.*]] = or i1 [[SWITCH_SELECTCMP_CASE1]], [[SWITCH_SELECTCMP_CASE2]]
+; CHECK-NEXT:    [[TMP0:%.*]] = select i1 [[SWITCH_SELECTCMP]], i1 true, i1 false
+; CHECK-NEXT:    ret i1 [[TMP0]]
+;
+  switch i8 %f, label %bb1 [
+  i8 15, label %bb2
+  i8 8,  label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}
+
+; Out of range scenarios. Check if the cases, that have a value out of range
+; are eliminated and the optimization is performed.
+
+define i1 @range0to15_out_of_range_non_prime(i8 range(i8 0, 16) %f) {
+; CHECK-LABEL: @range0to15_out_of_range_non_prime(
+; CHECK-NEXT:  bb3:
+; CHECK-NEXT:    [[TMP0:%.*]] = and i8 [[F:%.*]], 6
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8 [[TMP0]], 6
+; CHECK-NEXT:    [[TMP2:%.*]] = select i1 [[TMP1]], i1 true, i1 false
+; CHECK-NEXT:    ret i1 [[TMP2]]
+;
+  switch i8 %f, label %bb1 [
+  i8 6, label %bb2
+  i8 7, label %bb2
+  i8 14, label %bb2
+  i8 15, label %bb2
+  i8 22, label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}
+
+define i1 @range0to15_out_of_range_non_prime_more(i8 range(i8 0, 16) %f) {
+; CHECK-LABEL: @range0to15_out_of_range_non_prime_more(
+; CHECK-NEXT:  bb3:
+; CHECK-NEXT:    [[TMP0:%.*]] = and i8 [[F:%.*]], 6
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8 [[TMP0]], 6
+; CHECK-NEXT:    [[TMP2:%.*]] = select i1 [[TMP1]], i1 true, i1 false
+; CHECK-NEXT:    ret i1 [[TMP2]]
+;
+  switch i8 %f, label %bb1 [
+  i8 6, label %bb2
+  i8 7, label %bb2
+  i8 14, label %bb2
+  i8 15, label %bb2
+  i8 22, label %bb2
+  i8 23, label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}
+
+define i1 @negative_range0to15_out_of_range_non_prime(i8 range(i8 0, 16) %f) {
+; CHECK-LABEL: @negative_range0to15_out_of_range_non_prime(
+; CHECK-NEXT:    switch i8 [[F:%.*]], label [[BB3:%.*]] [
+; CHECK-NEXT:      i8 6, label [[BB2:%.*]]
+; CHECK-NEXT:      i8 14, label [[BB2]]
+; CHECK-NEXT:      i8 15, label [[BB2]]
+; CHECK-NEXT:    ]
+; CHECK:       bb2:
+; CHECK-NEXT:    br label [[BB3]]
+; CHECK:       bb3:
+; CHECK-NEXT:    [[TMP2:%.*]] = phi i1 [ true, [[BB2]] ], [ false, [[TMP0:%.*]] ]
+; CHECK-NEXT:    ret i1 [[TMP2]]
+;
+  switch i8 %f, label %bb1 [
+  i8 6, label %bb2
+  i8 14, label %bb2
+  i8 15, label %bb2
+  i8 23, label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}
+
+define i1 @negative_range0to15_out_of_range(i8 range(i8 0, 16) %f) {
+; CHECK-LABEL: @negative_range0to15_out_of_range(
+; CHECK-NEXT:    switch i8 [[F:%.*]], label [[BB3:%.*]] [
+; CHECK-NEXT:      i8 6, label [[BB2:%.*]]
+; CHECK-NEXT:      i8 7, label [[BB2]]
+; CHECK-NEXT:      i8 14, label [[BB2]]
+; CHECK-NEXT:    ]
+; CHECK:       bb2:
+; CHECK-NEXT:    br label [[BB3]]
+; CHECK:       bb3:
+; CHECK-NEXT:    [[_0_SROA_0_0:%.*]] = phi i1 [ true, [[BB2]] ], [ false, [[TMP0:%.*]] ]
+; CHECK-NEXT:    ret i1 [[_0_SROA_0_0]]
+;
+  switch i8 %f, label %bb1 [
+  i8 6, label %bb2
+  i8 7, label %bb2
+  i8 14, label %bb2
+  i8 150, label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}
+
+define i1 @negative_range0to15_all_out_of_range(i8 range(i8 0, 16) %f) {
+; CHECK-LABEL: @negative_range0to15_all_out_of_range(
+; CHECK-NEXT:  bb1:
+; CHECK-NEXT:    ret i1 false
+;
+  switch i8 %f, label %bb1 [
+  i8 22, label %bb2
+  i8 23, label %bb2
+  i8 30, label %bb2
+  i8 31, label %bb2
+  ]
+bb1:
+  br label %bb3
+bb2:
+  br label %bb3
+bb3:
+  %phi = phi i1 [ false, %bb1 ], [ true, %bb2 ]
+  ret i1 %phi
+}


        


More information about the llvm-commits mailing list