diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 55e375670cc61..61d891d65346b 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -5414,11 +5414,13 @@ static bool CasesAreContiguous(SmallVectorImpl &Cases) { } static void createUnreachableSwitchDefault(SwitchInst *Switch, - DomTreeUpdater *DTU) { + DomTreeUpdater *DTU, + bool RemoveOrigDefaultBlock = true) { LLVM_DEBUG(dbgs() << "SimplifyCFG: switch default is dead.\n"); auto *BB = Switch->getParent(); auto *OrigDefaultBlock = Switch->getDefaultDest(); - OrigDefaultBlock->removePredecessor(BB); + if (RemoveOrigDefaultBlock) + OrigDefaultBlock->removePredecessor(BB); BasicBlock *NewDefaultBlock = BasicBlock::Create( BB->getContext(), BB->getName() + ".unreachabledefault", BB->getParent(), OrigDefaultBlock); @@ -5427,7 +5429,8 @@ static void createUnreachableSwitchDefault(SwitchInst *Switch, if (DTU) { SmallVector Updates; Updates.push_back({DominatorTree::Insert, BB, &*NewDefaultBlock}); - if (!is_contained(successors(BB), OrigDefaultBlock)) + if (RemoveOrigDefaultBlock && + !is_contained(successors(BB), OrigDefaultBlock)) Updates.push_back({DominatorTree::Delete, BB, &*OrigDefaultBlock}); DTU->applyUpdates(Updates); } @@ -5609,10 +5612,28 @@ static bool eliminateDeadSwitchCases(SwitchInst *SI, DomTreeUpdater *DTU, Known.getBitWidth() - (Known.Zero | Known.One).popcount(); assert(NumUnknownBits <= Known.getBitWidth()); if (HasDefault && DeadCases.empty() && - NumUnknownBits < 64 /* avoid overflow */ && - SI->getNumCases() == (1ULL << NumUnknownBits)) { - createUnreachableSwitchDefault(SI, DTU); - return true; + NumUnknownBits < 64 /* avoid overflow */) { + uint64_t AllNumCases = 1ULL << NumUnknownBits; + if (SI->getNumCases() == AllNumCases) { + createUnreachableSwitchDefault(SI, DTU); + return true; + } + // When only one case value is missing, replace default with that case. + // Eliminating the default branch will provide more opportunities for + // optimization, such as lookup tables. + if (SI->getNumCases() == AllNumCases - 1) { + assert(NumUnknownBits > 1 && "Should be canonicalized to a branch"); + uint64_t MissingCaseVal = 0; + for (const auto &Case : SI->cases()) + MissingCaseVal ^= Case.getCaseValue()->getValue().getLimitedValue(); + auto *MissingCase = + cast(ConstantInt::get(Cond->getType(), MissingCaseVal)); + SwitchInstProfUpdateWrapper SIW(*SI); + SIW.addCase(MissingCase, SI->getDefaultDest(), SIW.getSuccessorWeight(0)); + createUnreachableSwitchDefault(SI, DTU, /*RemoveOrigDefaultBlock*/ false); + SIW.setSuccessorWeight(0, 0); + return true; + } } if (DeadCases.empty()) diff --git a/llvm/test/Transforms/SimplifyCFG/switch-dead-default-lookup-table.ll b/llvm/test/Transforms/SimplifyCFG/switch-dead-default-lookup-table.ll new file mode 100644 index 0000000000000..bead0dc4c567a --- /dev/null +++ b/llvm/test/Transforms/SimplifyCFG/switch-dead-default-lookup-table.ll @@ -0,0 +1,61 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 +; RUN: opt %s -S -passes='simplifycfg' -simplifycfg-require-and-preserve-domtree=1 -switch-range-to-icmp | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + +define i64 @test_1(i64 %0) { +; CHECK-LABEL: define i64 @test_1( +; CHECK-SAME: i64 [[TMP0:%.*]]) { +; CHECK-NEXT: switch.lookup: +; CHECK-NEXT: [[TMP1:%.*]] = urem i64 [[TMP0]], 4 +; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [4 x i64], ptr @switch.table.test_1, i32 0, i64 [[TMP1]] +; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i64, ptr [[SWITCH_GEP]], align 8 +; CHECK-NEXT: ret i64 [[SWITCH_LOAD]] +; + %2 = urem i64 %0, 4 + switch i64 %2, label %5 [ + i64 1, label %3 + i64 2, label %3 + i64 3, label %4 + ] + +3: + br label %5 + +4: + br label %5 + +5: + %.0 = phi i64 [ 2, %4 ], [ 1, %3 ], [ 0, %1 ] + ret i64 %.0 +} + + +define i64 @test_2(i64 %0) { +; CHECK-LABEL: define i64 @test_2( +; CHECK-SAME: i64 [[TMP0:%.*]]) { +; CHECK-NEXT: switch.lookup: +; CHECK-NEXT: [[TMP1:%.*]] = urem i64 [[TMP0]], 4 +; CHECK-NEXT: ret i64 [[TMP1]] +; + %2 = urem i64 %0, 4 + switch i64 %2, label %6 [ + i64 1, label %3 + i64 2, label %4 + i64 3, label %5 + ] + +3: + br label %6 + +4: + br label %6 + +5: + br label %6 + +6: + %.0 = phi i64 [ 0, %1 ], [ 1, %3 ], [ 2, %4 ], [ 3, %5 ] + ret i64 %.0 +} + diff --git a/llvm/test/Transforms/SimplifyCFG/switch-dead-default.ll b/llvm/test/Transforms/SimplifyCFG/switch-dead-default.ll index 7c0d5e4f2b653..e30a535c52323 100644 --- a/llvm/test/Transforms/SimplifyCFG/switch-dead-default.ll +++ b/llvm/test/Transforms/SimplifyCFG/switch-dead-default.ll @@ -79,15 +79,15 @@ default: ret void } -; This one is a negative test - we know the value of the default, -; but that's about it +; We can replace the default branch with case 3 since it is the only case that is missing. define void @test3(i2 %a) { ; CHECK-LABEL: define void @test3( ; CHECK-SAME: i2 [[A:%.*]]) { -; CHECK-NEXT: switch i2 [[A]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: switch i2 [[A]], label [[DOTUNREACHABLEDEFAULT:%.*]] [ ; CHECK-NEXT: i2 0, label [[CASE0:%.*]] ; CHECK-NEXT: i2 1, label [[CASE1:%.*]] ; CHECK-NEXT: i2 -2, label [[CASE2:%.*]] +; CHECK-NEXT: i2 -1, label [[DEFAULT:%.*]] ; CHECK-NEXT: ] ; CHECK: common.ret: ; CHECK-NEXT: ret void @@ -100,6 +100,8 @@ define void @test3(i2 %a) { ; CHECK: case2: ; CHECK-NEXT: call void @foo(i32 2) ; CHECK-NEXT: br label [[COMMON_RET]] +; CHECK: .unreachabledefault: +; CHECK-NEXT: unreachable ; CHECK: default: ; CHECK-NEXT: call void @foo(i32 3) ; CHECK-NEXT: br label [[COMMON_RET]] @@ -122,6 +124,50 @@ default: ret void } +define void @test3_prof(i2 %a) { +; CHECK-LABEL: define void @test3_prof( +; CHECK-SAME: i2 [[A:%.*]]) { +; CHECK-NEXT: switch i2 [[A]], label [[DOTUNREACHABLEDEFAULT:%.*]] [ +; CHECK-NEXT: i2 0, label [[CASE0:%.*]] +; CHECK-NEXT: i2 1, label [[CASE1:%.*]] +; CHECK-NEXT: i2 -2, label [[CASE2:%.*]] +; CHECK-NEXT: i2 -1, label [[DEFAULT:%.*]] +; CHECK-NEXT: ], !prof [[PROF0:![0-9]+]] +; CHECK: common.ret: +; CHECK-NEXT: ret void +; CHECK: case0: +; CHECK-NEXT: call void @foo(i32 0) +; CHECK-NEXT: br label [[COMMON_RET:%.*]] +; CHECK: case1: +; CHECK-NEXT: call void @foo(i32 1) +; CHECK-NEXT: br label [[COMMON_RET]] +; CHECK: case2: +; CHECK-NEXT: call void @foo(i32 2) +; CHECK-NEXT: br label [[COMMON_RET]] +; CHECK: .unreachabledefault: +; CHECK-NEXT: unreachable +; CHECK: default: +; CHECK-NEXT: call void @foo(i32 3) +; CHECK-NEXT: br label [[COMMON_RET]] +; + switch i2 %a, label %default [i2 0, label %case0 + i2 1, label %case1 + i2 2, label %case2], !prof !0 + +case0: + call void @foo(i32 0) + ret void +case1: + call void @foo(i32 1) + ret void +case2: + call void @foo(i32 2) + ret void +default: + call void @foo(i32 3) + ret void +} + ; Negative test - check for possible overflow when computing ; number of possible cases. define void @test4(i128 %a) { @@ -267,3 +313,7 @@ default: declare void @llvm.assume(i1) +!0 = !{!"branch_weights", i32 8, i32 4, i32 2, i32 1} +;. +; CHECK: [[PROF0]] = !{!"branch_weights", i32 0, i32 4, i32 2, i32 1, i32 8} +;.