Skip to content

Commit 64ed699

Browse files
committed
Reland "[SimplifyCFG] When only one case value is missing, replace default with that case (#76669)"
When the default branch is the last case, we can transform that branch into a concrete branch with an unreachable default branch. ```llvm target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" define i64 @src(i64 %0) { %2 = urem i64 %0, 4 switch i64 %2, label %5 [ i64 1, label %3 i64 2, label %3 i64 3, label %4 ] 3: ; preds = %1, %1 br label %5 4: ; preds = %1 br label %5 5: ; preds = %1, %4, %3 %.0 = phi i64 [ 2, %4 ], [ 1, %3 ], [ 0, %1 ] ret i64 %.0 } define i64 @tgt(i64 %0) { %2 = urem i64 %0, 4 switch i64 %2, label %unreachable [ i64 0, label %5 i64 1, label %3 i64 2, label %3 i64 3, label %4 ] unreachable: ; preds = %1 unreachable 3: ; preds = %1, %1 br label %5 4: ; preds = %1 br label %5 5: ; preds = %1, %4, %3 %.0 = phi i64 [ 2, %4 ], [ 1, %3 ], [ 0, %1 ] ret i64 %.0 } ``` Alive2: https://alive2.llvm.org/ce/z/Y-PGXv After transform to a lookup table, I believe `tgt` is better code. The final instructions are as follows: ```asm src: # @src and edi, 3 lea rax, [rdi - 1] cmp rax, 2 ja .LBB0_1 mov rax, qword ptr [8*rdi + .Lswitch.table.src-8] ret .LBB0_1: xor eax, eax ret tgt: # @tgt and edi, 3 mov rax, qword ptr [8*rdi + .Lswitch.table.tgt] ret .Lswitch.table.src: .quad 1 # 0x1 .quad 1 # 0x1 .quad 2 # 0x2 .Lswitch.table.tgt: .quad 0 # 0x0 .quad 1 # 0x1 .quad 1 # 0x1 .quad 2 # 0x2 ``` Godbolt: https://llvm.godbolt.org/z/borME8znd Closes #73446. (cherry picked from commit 7d81e07)
1 parent 1c90de5 commit 64ed699

File tree

3 files changed

+142
-10
lines changed

3 files changed

+142
-10
lines changed

llvm/lib/Transforms/Utils/SimplifyCFG.cpp

+28-7
Original file line numberDiff line numberDiff line change
@@ -5501,11 +5501,13 @@ static bool CasesAreContiguous(SmallVectorImpl<ConstantInt *> &Cases) {
55015501
}
55025502

55035503
static void createUnreachableSwitchDefault(SwitchInst *Switch,
5504-
DomTreeUpdater *DTU) {
5504+
DomTreeUpdater *DTU,
5505+
bool RemoveOrigDefaultBlock = true) {
55055506
LLVM_DEBUG(dbgs() << "SimplifyCFG: switch default is dead.\n");
55065507
auto *BB = Switch->getParent();
55075508
auto *OrigDefaultBlock = Switch->getDefaultDest();
5508-
OrigDefaultBlock->removePredecessor(BB);
5509+
if (RemoveOrigDefaultBlock)
5510+
OrigDefaultBlock->removePredecessor(BB);
55095511
BasicBlock *NewDefaultBlock = BasicBlock::Create(
55105512
BB->getContext(), BB->getName() + ".unreachabledefault", BB->getParent(),
55115513
OrigDefaultBlock);
@@ -5514,7 +5516,8 @@ static void createUnreachableSwitchDefault(SwitchInst *Switch,
55145516
if (DTU) {
55155517
SmallVector<DominatorTree::UpdateType, 2> Updates;
55165518
Updates.push_back({DominatorTree::Insert, BB, &*NewDefaultBlock});
5517-
if (!is_contained(successors(BB), OrigDefaultBlock))
5519+
if (RemoveOrigDefaultBlock &&
5520+
!is_contained(successors(BB), OrigDefaultBlock))
55185521
Updates.push_back({DominatorTree::Delete, BB, &*OrigDefaultBlock});
55195522
DTU->applyUpdates(Updates);
55205523
}
@@ -5696,10 +5699,28 @@ static bool eliminateDeadSwitchCases(SwitchInst *SI, DomTreeUpdater *DTU,
56965699
Known.getBitWidth() - (Known.Zero | Known.One).popcount();
56975700
assert(NumUnknownBits <= Known.getBitWidth());
56985701
if (HasDefault && DeadCases.empty() &&
5699-
NumUnknownBits < 64 /* avoid overflow */ &&
5700-
SI->getNumCases() == (1ULL << NumUnknownBits)) {
5701-
createUnreachableSwitchDefault(SI, DTU);
5702-
return true;
5702+
NumUnknownBits < 64 /* avoid overflow */) {
5703+
uint64_t AllNumCases = 1ULL << NumUnknownBits;
5704+
if (SI->getNumCases() == AllNumCases) {
5705+
createUnreachableSwitchDefault(SI, DTU);
5706+
return true;
5707+
}
5708+
// When only one case value is missing, replace default with that case.
5709+
// Eliminating the default branch will provide more opportunities for
5710+
// optimization, such as lookup tables.
5711+
if (SI->getNumCases() == AllNumCases - 1) {
5712+
assert(NumUnknownBits > 1 && "Should be canonicalized to a branch");
5713+
uint64_t MissingCaseVal = 0;
5714+
for (const auto &Case : SI->cases())
5715+
MissingCaseVal ^= Case.getCaseValue()->getValue().getLimitedValue();
5716+
auto *MissingCase =
5717+
cast<ConstantInt>(ConstantInt::get(Cond->getType(), MissingCaseVal));
5718+
SwitchInstProfUpdateWrapper SIW(*SI);
5719+
SIW.addCase(MissingCase, SI->getDefaultDest(), SIW.getSuccessorWeight(0));
5720+
createUnreachableSwitchDefault(SI, DTU, /*RemoveOrigDefaultBlock*/ false);
5721+
SIW.setSuccessorWeight(0, 0);
5722+
return true;
5723+
}
57035724
}
57045725

57055726
if (DeadCases.empty())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
2+
; RUN: opt %s -S -passes='simplifycfg<switch-to-lookup>' -simplifycfg-require-and-preserve-domtree=1 -switch-range-to-icmp | FileCheck %s
3+
4+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
5+
6+
define i64 @test_1(i64 %0) {
7+
; CHECK-LABEL: define i64 @test_1(
8+
; CHECK-SAME: i64 [[TMP0:%.*]]) {
9+
; CHECK-NEXT: switch.lookup:
10+
; CHECK-NEXT: [[TMP1:%.*]] = urem i64 [[TMP0]], 4
11+
; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [4 x i64], ptr @switch.table.test_1, i32 0, i64 [[TMP1]]
12+
; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i64, ptr [[SWITCH_GEP]], align 8
13+
; CHECK-NEXT: ret i64 [[SWITCH_LOAD]]
14+
;
15+
%2 = urem i64 %0, 4
16+
switch i64 %2, label %5 [
17+
i64 1, label %3
18+
i64 2, label %3
19+
i64 3, label %4
20+
]
21+
22+
3:
23+
br label %5
24+
25+
4:
26+
br label %5
27+
28+
5:
29+
%.0 = phi i64 [ 2, %4 ], [ 1, %3 ], [ 0, %1 ]
30+
ret i64 %.0
31+
}
32+
33+
34+
define i64 @test_2(i64 %0) {
35+
; CHECK-LABEL: define i64 @test_2(
36+
; CHECK-SAME: i64 [[TMP0:%.*]]) {
37+
; CHECK-NEXT: switch.lookup:
38+
; CHECK-NEXT: [[TMP1:%.*]] = urem i64 [[TMP0]], 4
39+
; CHECK-NEXT: ret i64 [[TMP1]]
40+
;
41+
%2 = urem i64 %0, 4
42+
switch i64 %2, label %6 [
43+
i64 1, label %3
44+
i64 2, label %4
45+
i64 3, label %5
46+
]
47+
48+
3:
49+
br label %6
50+
51+
4:
52+
br label %6
53+
54+
5:
55+
br label %6
56+
57+
6:
58+
%.0 = phi i64 [ 0, %1 ], [ 1, %3 ], [ 2, %4 ], [ 3, %5 ]
59+
ret i64 %.0
60+
}
61+

llvm/test/Transforms/SimplifyCFG/switch-dead-default.ll

+53-3
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,15 @@ default:
7979
ret void
8080
}
8181

82-
; This one is a negative test - we know the value of the default,
83-
; but that's about it
82+
; We can replace the default branch with case 3 since it is the only case that is missing.
8483
define void @test3(i2 %a) {
8584
; CHECK-LABEL: define void @test3(
8685
; CHECK-SAME: i2 [[A:%.*]]) {
87-
; CHECK-NEXT: switch i2 [[A]], label [[DEFAULT:%.*]] [
86+
; CHECK-NEXT: switch i2 [[A]], label [[DOTUNREACHABLEDEFAULT:%.*]] [
8887
; CHECK-NEXT: i2 0, label [[CASE0:%.*]]
8988
; CHECK-NEXT: i2 1, label [[CASE1:%.*]]
9089
; CHECK-NEXT: i2 -2, label [[CASE2:%.*]]
90+
; CHECK-NEXT: i2 -1, label [[DEFAULT:%.*]]
9191
; CHECK-NEXT: ]
9292
; CHECK: common.ret:
9393
; CHECK-NEXT: ret void
@@ -100,6 +100,8 @@ define void @test3(i2 %a) {
100100
; CHECK: case2:
101101
; CHECK-NEXT: call void @foo(i32 2)
102102
; CHECK-NEXT: br label [[COMMON_RET]]
103+
; CHECK: .unreachabledefault:
104+
; CHECK-NEXT: unreachable
103105
; CHECK: default:
104106
; CHECK-NEXT: call void @foo(i32 3)
105107
; CHECK-NEXT: br label [[COMMON_RET]]
@@ -122,6 +124,50 @@ default:
122124
ret void
123125
}
124126

127+
define void @test3_prof(i2 %a) {
128+
; CHECK-LABEL: define void @test3_prof(
129+
; CHECK-SAME: i2 [[A:%.*]]) {
130+
; CHECK-NEXT: switch i2 [[A]], label [[DOTUNREACHABLEDEFAULT:%.*]] [
131+
; CHECK-NEXT: i2 0, label [[CASE0:%.*]]
132+
; CHECK-NEXT: i2 1, label [[CASE1:%.*]]
133+
; CHECK-NEXT: i2 -2, label [[CASE2:%.*]]
134+
; CHECK-NEXT: i2 -1, label [[DEFAULT:%.*]]
135+
; CHECK-NEXT: ], !prof [[PROF0:![0-9]+]]
136+
; CHECK: common.ret:
137+
; CHECK-NEXT: ret void
138+
; CHECK: case0:
139+
; CHECK-NEXT: call void @foo(i32 0)
140+
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
141+
; CHECK: case1:
142+
; CHECK-NEXT: call void @foo(i32 1)
143+
; CHECK-NEXT: br label [[COMMON_RET]]
144+
; CHECK: case2:
145+
; CHECK-NEXT: call void @foo(i32 2)
146+
; CHECK-NEXT: br label [[COMMON_RET]]
147+
; CHECK: .unreachabledefault:
148+
; CHECK-NEXT: unreachable
149+
; CHECK: default:
150+
; CHECK-NEXT: call void @foo(i32 3)
151+
; CHECK-NEXT: br label [[COMMON_RET]]
152+
;
153+
switch i2 %a, label %default [i2 0, label %case0
154+
i2 1, label %case1
155+
i2 2, label %case2], !prof !0
156+
157+
case0:
158+
call void @foo(i32 0)
159+
ret void
160+
case1:
161+
call void @foo(i32 1)
162+
ret void
163+
case2:
164+
call void @foo(i32 2)
165+
ret void
166+
default:
167+
call void @foo(i32 3)
168+
ret void
169+
}
170+
125171
; Negative test - check for possible overflow when computing
126172
; number of possible cases.
127173
define void @test4(i128 %a) {
@@ -267,3 +313,7 @@ default:
267313

268314
declare void @llvm.assume(i1)
269315

316+
!0 = !{!"branch_weights", i32 8, i32 4, i32 2, i32 1}
317+
;.
318+
; CHECK: [[PROF0]] = !{!"branch_weights", i32 0, i32 4, i32 2, i32 1, i32 8}
319+
;.

0 commit comments

Comments
 (0)