Skip to content

Commit d200bd1

Browse files
authored
Reland "[SimplifyCFG] Hoist common instructions on switch" (#67077)
This relands commit 96ea48f.
1 parent aa70f4d commit d200bd1

File tree

7 files changed

+961
-317
lines changed

7 files changed

+961
-317
lines changed

llvm/lib/Transforms/Utils/SimplifyCFG.cpp

Lines changed: 225 additions & 146 deletions
Large diffs are not rendered by default.

llvm/test/CodeGen/AArch64/patchable-function-entry-bti.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ entry:
7070
i64 4, label %sw.bb4
7171
]
7272
sw.bb0:
73-
call void asm sideeffect "", ""()
73+
call void asm sideeffect "nop", ""()
7474
ret void
7575
sw.bb1:
7676
call void asm sideeffect "", ""()

llvm/test/Transforms/SimplifyCFG/HoistCode.ll

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,9 @@ F: ; preds = %0
1919

2020
define void @foo_switch(i64 %C, ptr %P) {
2121
; CHECK-LABEL: @foo_switch(
22-
; CHECK-NEXT: switch i64 [[C:%.*]], label [[BB0:%.*]] [
23-
; CHECK-NEXT: i64 1, label [[BB1:%.*]]
24-
; CHECK-NEXT: i64 2, label [[BB2:%.*]]
25-
; CHECK-NEXT: ]
26-
; CHECK: common.ret:
27-
; CHECK-NEXT: ret void
28-
; CHECK: bb0:
22+
; CHECK-NEXT: common.ret:
2923
; CHECK-NEXT: store i32 7, ptr [[P:%.*]], align 4
30-
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
31-
; CHECK: bb1:
32-
; CHECK-NEXT: store i32 7, ptr [[P]], align 4
33-
; CHECK-NEXT: br label [[COMMON_RET]]
34-
; CHECK: bb2:
35-
; CHECK-NEXT: store i32 7, ptr [[P]], align 4
36-
; CHECK-NEXT: br label [[COMMON_RET]]
24+
; CHECK-NEXT: ret void
3725
;
3826
switch i64 %C, label %bb0 [
3927
i64 1, label %bb1

llvm/test/Transforms/SimplifyCFG/hoist-common-code-with-unreachable.ll

Lines changed: 84 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,8 @@
44
define i1 @common_instr_with_unreachable(i64 %a, i64 %b, i64 %c) {
55
; CHECK-LABEL: @common_instr_with_unreachable(
66
; CHECK-NEXT: start:
7-
; CHECK-NEXT: switch i64 [[A:%.*]], label [[UNREACHABLE:%.*]] [
8-
; CHECK-NEXT: i64 0, label [[BB0:%.*]]
9-
; CHECK-NEXT: i64 1, label [[BB1:%.*]]
10-
; CHECK-NEXT: i64 2, label [[BB2:%.*]]
11-
; CHECK-NEXT: ]
12-
; CHECK: unreachable:
13-
; CHECK-NEXT: unreachable
14-
; CHECK: bb0:
157
; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[B:%.*]], [[C:%.*]]
16-
; CHECK-NEXT: br label [[EXIT:%.*]]
17-
; CHECK: bb1:
18-
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[B]], [[C]]
19-
; CHECK-NEXT: br label [[EXIT]]
20-
; CHECK: bb2:
21-
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i64 [[B]], [[C]]
22-
; CHECK-NEXT: br label [[EXIT]]
23-
; CHECK: exit:
24-
; CHECK-NEXT: [[RESULT:%.*]] = phi i1 [ [[TMP0]], [[BB0]] ], [ [[TMP1]], [[BB1]] ], [ [[TMP2]], [[BB2]] ]
25-
; CHECK-NEXT: ret i1 [[RESULT]]
8+
; CHECK-NEXT: ret i1 [[TMP0]]
269
;
2710
start:
2811
switch i64 %a, label %unreachable [
@@ -54,46 +37,126 @@ exit: ; preds = %bb2, %bb1, %bb0
5437
define i1 @common_instr_with_unreachable_2(i64 %a, i64 %b, i64 %c) {
5538
; CHECK-LABEL: @common_instr_with_unreachable_2(
5639
; CHECK-NEXT: start:
57-
; CHECK-NEXT: switch i64 [[A:%.*]], label [[BB1:%.*]] [
40+
; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[B:%.*]], [[C:%.*]]
41+
; CHECK-NEXT: ret i1 [[TMP0]]
42+
;
43+
start:
44+
switch i64 %a, label %bb1 [
45+
i64 0, label %bb0
46+
i64 1, label %unreachable
47+
i64 2, label %bb2
48+
]
49+
50+
unreachable:
51+
unreachable
52+
53+
bb0: ; preds = %start
54+
%0 = icmp eq i64 %b, %c
55+
br label %exit
56+
57+
bb1: ; preds = %start
58+
%1 = icmp eq i64 %b, %c
59+
br label %exit
60+
61+
bb2: ; preds = %start
62+
%2 = icmp eq i64 %b, %c
63+
br label %exit
64+
65+
exit: ; preds = %bb2, %bb1, %bb0
66+
%result = phi i1 [ %0, %bb0 ], [ %1, %bb1 ], [ %2, %bb2 ]
67+
ret i1 %result
68+
}
69+
70+
define i1 @not_only_unreachable(i64 %a, i64 %b, i64 %c) {
71+
; CHECK-LABEL: @not_only_unreachable(
72+
; CHECK-NEXT: start:
73+
; CHECK-NEXT: switch i64 [[A:%.*]], label [[UNREACHABLE:%.*]] [
5874
; CHECK-NEXT: i64 0, label [[BB0:%.*]]
75+
; CHECK-NEXT: i64 1, label [[BB1:%.*]]
5976
; CHECK-NEXT: i64 2, label [[BB2:%.*]]
6077
; CHECK-NEXT: ]
78+
; CHECK: unreachable:
79+
; CHECK-NEXT: call void @no_return()
80+
; CHECK-NEXT: unreachable
6181
; CHECK: bb0:
6282
; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[B:%.*]], [[C:%.*]]
83+
; CHECK-NEXT: call void @foo()
6384
; CHECK-NEXT: br label [[EXIT:%.*]]
6485
; CHECK: bb1:
6586
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[B]], [[C]]
87+
; CHECK-NEXT: call void @foo()
6688
; CHECK-NEXT: br label [[EXIT]]
6789
; CHECK: bb2:
6890
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i64 [[B]], [[C]]
91+
; CHECK-NEXT: call void @foo()
6992
; CHECK-NEXT: br label [[EXIT]]
7093
; CHECK: exit:
7194
; CHECK-NEXT: [[RESULT:%.*]] = phi i1 [ [[TMP0]], [[BB0]] ], [ [[TMP1]], [[BB1]] ], [ [[TMP2]], [[BB2]] ]
7295
; CHECK-NEXT: ret i1 [[RESULT]]
7396
;
7497
start:
75-
switch i64 %a, label %bb1 [
98+
switch i64 %a, label %unreachable [
7699
i64 0, label %bb0
77-
i64 1, label %unreachable
100+
i64 1, label %bb1
78101
i64 2, label %bb2
79102
]
80103

81104
unreachable:
105+
call void @no_return()
82106
unreachable
83107

84108
bb0: ; preds = %start
85109
%0 = icmp eq i64 %b, %c
110+
call void @foo()
86111
br label %exit
87112

88113
bb1: ; preds = %start
89114
%1 = icmp eq i64 %b, %c
115+
call void @foo()
90116
br label %exit
91117

92118
bb2: ; preds = %start
93119
%2 = icmp eq i64 %b, %c
120+
call void @foo()
94121
br label %exit
95122

96123
exit: ; preds = %bb2, %bb1, %bb0
97124
%result = phi i1 [ %0, %bb0 ], [ %1, %bb1 ], [ %2, %bb2 ]
98125
ret i1 %result
99126
}
127+
128+
; If we can hoist a musttail call,
129+
; we can and have to hoist subsequent bitcast and ret instructions.
130+
define ptr @switch_musttail_call(ptr %arg) {
131+
; CHECK-LABEL: @switch_musttail_call(
132+
; CHECK-NEXT: bb:
133+
; CHECK-NEXT: [[P0:%.*]] = musttail call ptr @musttail_call(ptr [[ARG:%.*]])
134+
; CHECK-NEXT: ret ptr [[P0]]
135+
;
136+
bb:
137+
%load = load i16, ptr %arg, align 2
138+
switch i16 %load, label %unreachable [
139+
i16 0, label %bb0
140+
i16 1, label %bb1
141+
i16 2, label %bb2
142+
]
143+
144+
unreachable:
145+
unreachable
146+
147+
bb0:
148+
%p0 = musttail call ptr @musttail_call(ptr %arg)
149+
ret ptr %p0
150+
151+
bb1:
152+
%p1 = musttail call ptr @musttail_call(ptr %arg)
153+
ret ptr %p1
154+
155+
bb2:
156+
%p2 = musttail call ptr @musttail_call(ptr %arg)
157+
ret ptr %p2
158+
}
159+
160+
declare void @no_return()
161+
declare void @foo()
162+
declare ptr @musttail_call(ptr)

llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,11 @@ F: ; preds = %0
2626

2727
define void @test_switch(i64 %i, ptr %Q) {
2828
; CHECK-LABEL: @test_switch(
29-
; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
30-
; CHECK-NEXT: i64 1, label [[BB1:%.*]]
31-
; CHECK-NEXT: i64 2, label [[BB2:%.*]]
32-
; CHECK-NEXT: ]
33-
; CHECK: common.ret:
34-
; CHECK-NEXT: ret void
35-
; CHECK: bb0:
29+
; CHECK-NEXT: common.ret:
3630
; CHECK-NEXT: store i32 1, ptr [[Q:%.*]], align 4
3731
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[Q]], align 4
3832
; CHECK-NEXT: call void @bar(i32 [[A]])
39-
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
40-
; CHECK: bb1:
41-
; CHECK-NEXT: store i32 1, ptr [[Q]], align 4
42-
; CHECK-NEXT: [[B:%.*]] = load i32, ptr [[Q]], align 4
43-
; CHECK-NEXT: call void @bar(i32 [[B]])
44-
; CHECK-NEXT: br label [[COMMON_RET]]
45-
; CHECK: bb2:
46-
; CHECK-NEXT: store i32 1, ptr [[Q]], align 4
47-
; CHECK-NEXT: [[C:%.*]] = load i32, ptr [[Q]], align 4
48-
; CHECK-NEXT: call void @bar(i32 [[C]])
49-
; CHECK-NEXT: br label [[COMMON_RET]]
33+
; CHECK-NEXT: ret void
5034
;
5135
switch i64 %i, label %bb0 [
5236
i64 1, label %bb1
@@ -69,25 +53,41 @@ bb2: ; preds = %0
6953
ret void
7054
}
7155

72-
define i1 @common_instr_on_switch(i64 %a, i64 %b, i64 %c) unnamed_addr {
73-
; CHECK-LABEL: @common_instr_on_switch(
74-
; CHECK-NEXT: start:
75-
; CHECK-NEXT: switch i64 [[A:%.*]], label [[BB0:%.*]] [
56+
; We ensure that we examine all instructions during each iteration to confirm the presence of a terminating one.
57+
define void @test_switch_reach_terminator(i64 %i, ptr %p) {
58+
; CHECK-LABEL: @test_switch_reach_terminator(
59+
; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
7660
; CHECK-NEXT: i64 1, label [[BB1:%.*]]
77-
; CHECK-NEXT: i64 2, label [[BB2:%.*]]
61+
; CHECK-NEXT: i64 2, label [[COMMON_RET:%.*]]
7862
; CHECK-NEXT: ]
63+
; CHECK: common.ret:
64+
; CHECK-NEXT: ret void
7965
; CHECK: bb0:
80-
; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[B:%.*]], [[C:%.*]]
81-
; CHECK-NEXT: br label [[EXIT:%.*]]
66+
; CHECK-NEXT: store i32 1, ptr [[P:%.*]], align 4
67+
; CHECK-NEXT: br label [[COMMON_RET]]
8268
; CHECK: bb1:
83-
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[B]], [[C]]
84-
; CHECK-NEXT: br label [[EXIT]]
85-
; CHECK: bb2:
86-
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i64 [[B]], [[C]]
87-
; CHECK-NEXT: br label [[EXIT]]
88-
; CHECK: exit:
89-
; CHECK-NEXT: [[RESULT:%.*]] = phi i1 [ [[TMP0]], [[BB0]] ], [ [[TMP1]], [[BB1]] ], [ [[TMP2]], [[BB2]] ]
90-
; CHECK-NEXT: ret i1 [[RESULT]]
69+
; CHECK-NEXT: store i32 2, ptr [[P]], align 4
70+
; CHECK-NEXT: br label [[COMMON_RET]]
71+
;
72+
switch i64 %i, label %bb0 [
73+
i64 1, label %bb1
74+
i64 2, label %bb2
75+
]
76+
bb0: ; preds = %0
77+
store i32 1, ptr %p
78+
ret void
79+
bb1: ; preds = %0
80+
store i32 2, ptr %p
81+
ret void
82+
bb2: ; preds = %0
83+
ret void
84+
}
85+
86+
define i1 @common_instr_on_switch(i64 %a, i64 %b, i64 %c) unnamed_addr {
87+
; CHECK-LABEL: @common_instr_on_switch(
88+
; CHECK-NEXT: start:
89+
; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[B:%.*]], [[C:%.*]]
90+
; CHECK-NEXT: ret i1 [[TMP0]]
9191
;
9292
start:
9393
switch i64 %a, label %bb0 [

0 commit comments

Comments
 (0)