Index: llvm/lib/Transforms/Utils/SimplifyCFG.cpp =================================================================== --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -169,7 +169,7 @@ cl::desc("Allow SimplifyCFG to merge invokes together when appropriate")); static cl::opt 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"); @@ -5678,9 +5678,8 @@ return true; } -// Helper function that checks if it is possible to transform a switch with only -// two cases (or two cases + default) that produces a result into a select. -// Example: +// Helper function that checks if it is possible to transform a switch with: +// 1. two cases (or two cases + default) that produces a result into a select. // switch (a) { // case 10: %0 = icmp eq i32 %a, 10 // return 10; %1 = select i1 %0, i32 10, i32 4 @@ -5689,14 +5688,23 @@ // default: // return 4; // } -// TODO: Handle switches with more than 2 cases that map to the same result. +// 2. n bits group cases map to the same result into a select. +// case 0, 4 (bit 1 group) -> select !!(and -5), result, default +// case 0, 2, 4, 6 (bit 0, 1 group) -> select !!(and -7), result, default +// case 0, 2, 8, 10 (bit 0, bit 2 group) -> select !!(and -11), result, default +// TODO: If minimal case value is not 0 and only two cases have the same +// value, both of two transform get 4 IR instructions with similar perf +// select (c - min(c1, c2)) & (-abs(c1 - c2) - 1), result, default +// select (c == c1 || c == c2), result, default +// And for now instcombine can't recognize the equivalence between the 2 +// patterns We may need re-order the transforms in the furture static Value *foldSwitchToSelect(const SwitchCaseResultVectorTy &ResultVector, Constant *DefaultResult, Value *Condition, IRBuilder<> &Builder) { - // If we are selecting between only two cases transform into a simple - // select or a two-way select if default is possible. if (ResultVector.size() == 2 && ResultVector[0].second.size() == 1 && ResultVector[1].second.size() == 1) { + // If we are selecting between only two cases transform into a simple + // select or a two-way select if default is possible. ConstantInt *const FirstCase = ResultVector[0].second[0]; ConstantInt *const SecondCase = ResultVector[1].second[0]; @@ -5714,15 +5722,43 @@ SelectValue, "switch.select"); } - // Handle the degenerate case where two cases have the same value. - if (ResultVector.size() == 1 && ResultVector[0].second.size() == 2 && - DefaultResult) { - Value *Cmp1 = Builder.CreateICmpEQ( - Condition, ResultVector[0].second[0], "switch.selectcmp.case1"); - Value *Cmp2 = Builder.CreateICmpEQ( - Condition, ResultVector[0].second[1], "switch.selectcmp.case2"); - Value *Cmp = Builder.CreateOr(Cmp1, Cmp2, "switch.selectcmp"); - return Builder.CreateSelect(Cmp, ResultVector[0].first, DefaultResult); + if (ResultVector.size() == 1 && DefaultResult) { + unsigned CaseCount = ResultVector[0].second.size(); + if (isPowerOf2_32(CaseCount)) { + ConstantInt *MinCaseVal = ResultVector[0].second[0]; + APInt BitMask = APInt::getZero(MinCaseVal->getBitWidth()); + // find mininal value + for (auto Case : ResultVector[0].second) { + if (Case->getValue().slt(MinCaseVal->getValue())) + MinCaseVal = Case; + } + + // Mark the bits case number touched + for (auto Case : ResultVector[0].second) { + 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 (ResultVector[0].second.size() == 2) { + Value *Cmp1 = Builder.CreateICmpEQ(Condition, ResultVector[0].second[0], + "switch.selectcmp.case1"); + Value *Cmp2 = Builder.CreateICmpEQ(Condition, ResultVector[0].second[1], + "switch.selectcmp.case2"); + Value *Cmp = Builder.CreateOr(Cmp1, Cmp2, "switch.selectcmp"); + return Builder.CreateSelect(Cmp, ResultVector[0].first, DefaultResult); + } } return nullptr; Index: llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll =================================================================== --- llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll +++ llvm/test/Transforms/SimplifyCFG/switch-to-select-two-case.ll @@ -69,11 +69,11 @@ define i1 @switch_to_and0(i8 %0) { ; CHECK-LABEL: @switch_to_and0( -; 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 @@ -92,16 +92,9 @@ define i1 @switch_to_and1(i32 %i) { ; CHECK-LABEL: @switch_to_and1( ; 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: @@ -123,16 +116,9 @@ define i1 @switch_to_and2(i32 %i) { ; CHECK-LABEL: @switch_to_and2( ; 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: @@ -154,17 +140,11 @@ define i1 @switch_to_and3(i32 %i) { ; CHECK-LABEL: @switch_to_and3( ; 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 [ @@ -185,11 +165,11 @@ define i8 @switch_to_and4_negcase(i32 %i) { ; CHECK-LABEL: @switch_to_and4_negcase( ; 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 [