xref: /llvm-project/llvm/test/Transforms/InstCombine/trivial-dse-calls.ll (revision 29441e4f5fa5f5c7709f7cf180815ba97f611297)
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2; RUN: opt -passes=instcombine -S < %s | FileCheck %s
3
4declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture)
5declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture)
6
7declare void @unknown()
8declare void @f(ptr)
9declare void @f2(ptr, ptr)
10declare ptr @f3(ptr, ptr)
11
12; Basic case for DSEing a trivially dead writing call
13define void @test_dead() {
14; CHECK-LABEL: @test_dead(
15; CHECK-NEXT:    ret void
16;
17  %a = alloca i32, align 4
18  call void @f(ptr writeonly nocapture %a) argmemonly nounwind willreturn
19  ret void
20}
21
22; Add in canonical lifetime intrinsics
23define void @test_lifetime() {
24; CHECK-LABEL: @test_lifetime(
25; CHECK-NEXT:    ret void
26;
27  %a = alloca i32, align 4
28  call void @llvm.lifetime.start.p0(i64 4, ptr %a)
29  call void @f(ptr writeonly nocapture %a) argmemonly nounwind willreturn
30  call void @llvm.lifetime.end.p0(i64 4, ptr %a)
31  ret void
32}
33
34; Add some unknown calls just to point out that this is use based, not
35; instruction order sensitive
36define void @test_lifetime2() {
37; CHECK-LABEL: @test_lifetime2(
38; CHECK-NEXT:    call void @unknown()
39; CHECK-NEXT:    call void @unknown()
40; CHECK-NEXT:    ret void
41;
42  %a = alloca i32, align 4
43  call void @llvm.lifetime.start.p0(i64 4, ptr %a)
44  call void @unknown()
45  call void @f(ptr writeonly nocapture %a) argmemonly nounwind willreturn
46  call void @unknown()
47  call void @llvm.lifetime.end.p0(i64 4, ptr %a)
48  ret void
49}
50
51; As long as the result is unused, we can even remove reads of the alloca
52; itself since the write will be dropped.
53define void @test_dead_readwrite() {
54; CHECK-LABEL: @test_dead_readwrite(
55; CHECK-NEXT:    ret void
56;
57  %a = alloca i32, align 4
58  call void @f(ptr nocapture %a) argmemonly nounwind willreturn
59  ret void
60}
61
62define i32 @test_neg_read_after() {
63; CHECK-LABEL: @test_neg_read_after(
64; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
65; CHECK-NEXT:    call void @f(ptr nonnull writeonly captures(none) [[A]]) #[[ATTR3:[0-9]+]]
66; CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[A]], align 4
67; CHECK-NEXT:    ret i32 [[RES]]
68;
69  %a = alloca i32, align 4
70  call void @f(ptr writeonly nocapture %a) argmemonly nounwind willreturn
71  %res = load i32, ptr %a
72  ret i32 %res
73}
74
75
76define void @test_neg_infinite_loop() {
77; CHECK-LABEL: @test_neg_infinite_loop(
78; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
79; CHECK-NEXT:    call void @f(ptr nonnull writeonly captures(none) [[A]]) #[[ATTR4:[0-9]+]]
80; CHECK-NEXT:    ret void
81;
82  %a = alloca i32, align 4
83  call void @f(ptr writeonly nocapture %a) argmemonly nounwind
84  ret void
85}
86
87define void @test_neg_throw() {
88; CHECK-LABEL: @test_neg_throw(
89; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
90; CHECK-NEXT:    call void @f(ptr nonnull writeonly captures(none) [[A]]) #[[ATTR5:[0-9]+]]
91; CHECK-NEXT:    ret void
92;
93  %a = alloca i32, align 4
94  call void @f(ptr writeonly nocapture %a) argmemonly willreturn
95  ret void
96}
97
98define void @test_neg_extra_write() {
99; CHECK-LABEL: @test_neg_extra_write(
100; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
101; CHECK-NEXT:    call void @f(ptr nonnull writeonly captures(none) [[A]]) #[[ATTR6:[0-9]+]]
102; CHECK-NEXT:    ret void
103;
104  %a = alloca i32, align 4
105  call void @f(ptr writeonly nocapture %a) nounwind willreturn
106  ret void
107}
108
109; In this case, we can't remove a1 because we need to preserve the write to
110; a2, and if we leave the call around, we need memory to pass to the first arg.
111define void @test_neg_unmodeled_write() {
112; CHECK-LABEL: @test_neg_unmodeled_write(
113; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
114; CHECK-NEXT:    [[A2:%.*]] = alloca i32, align 4
115; CHECK-NEXT:    call void @f2(ptr nonnull writeonly captures(none) [[A]], ptr nonnull [[A2]]) #[[ATTR3]]
116; CHECK-NEXT:    ret void
117;
118  %a = alloca i32, align 4
119  %a2 = alloca i32, align 4
120  call void @f2(ptr nocapture writeonly %a, ptr %a2) argmemonly nounwind willreturn
121  ret void
122}
123
124define i32 @test_neg_captured_by_call() {
125; CHECK-LABEL: @test_neg_captured_by_call(
126; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
127; CHECK-NEXT:    [[A2:%.*]] = alloca ptr, align 4
128; CHECK-NEXT:    call void @f2(ptr nonnull writeonly [[A]], ptr nonnull [[A2]]) #[[ATTR3]]
129; CHECK-NEXT:    [[A_COPY_CAST:%.*]] = load ptr, ptr [[A2]], align 8
130; CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[A_COPY_CAST]], align 4
131; CHECK-NEXT:    ret i32 [[RES]]
132;
133  %a = alloca i32, align 4
134  %a2 = alloca ptr, align 4
135  call void @f2(ptr writeonly %a, ptr %a2) argmemonly nounwind willreturn
136  %a_copy_cast = load ptr, ptr %a2
137  %res = load i32, ptr %a_copy_cast
138  ret i32 %res
139}
140
141define i32 @test_neg_captured_before() {
142; CHECK-LABEL: @test_neg_captured_before(
143; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
144; CHECK-NEXT:    call void @f(ptr nonnull writeonly captures(none) [[A]]) #[[ATTR3]]
145; CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[A]], align 4
146; CHECK-NEXT:    ret i32 [[RES]]
147;
148  %a = alloca i32, align 4
149  %a2 = alloca ptr, align 4
150  store ptr %a, ptr %a2
151  call void @f(ptr writeonly nocapture %a) argmemonly nounwind willreturn
152  %a_copy_cast = load ptr, ptr %a2
153  %res = load i32, ptr %a_copy_cast
154  ret i32 %res
155}
156
157; Show that reading from unrelated memory is okay
158define void @test_unreleated_read() {
159; CHECK-LABEL: @test_unreleated_read(
160; CHECK-NEXT:    ret void
161;
162  %a = alloca i32, align 4
163  %a2 = alloca i32, align 4
164  call void @f2(ptr nocapture writeonly %a, ptr nocapture readonly %a2) argmemonly nounwind willreturn
165  ret void
166}
167
168; Removing a capture is also okay. The capture can only be in the return value
169; (which is unused) or written into the dead out parameter.
170define void @test_unrelated_capture() {
171; CHECK-LABEL: @test_unrelated_capture(
172; CHECK-NEXT:    ret void
173;
174  %a = alloca i32, align 4
175  %a2 = alloca i32, align 4
176  call ptr @f3(ptr nocapture writeonly %a, ptr readonly %a2) argmemonly nounwind willreturn
177  ret void
178}
179
180; Cannot remove call, as %a2 is captured via the return value.
181define i8 @test_neg_unrelated_capture_used_via_return() {
182; CHECK-LABEL: @test_neg_unrelated_capture_used_via_return(
183; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
184; CHECK-NEXT:    [[A2:%.*]] = alloca i32, align 4
185; CHECK-NEXT:    [[CAPTURE:%.*]] = call ptr @f3(ptr nonnull writeonly captures(none) [[A]], ptr nonnull readonly [[A2]]) #[[ATTR3]]
186; CHECK-NEXT:    [[V:%.*]] = load i8, ptr [[CAPTURE]], align 1
187; CHECK-NEXT:    ret i8 [[V]]
188;
189  %a = alloca i32, align 4
190  %a2 = alloca i32, align 4
191  %capture = call ptr @f3(ptr nocapture writeonly %a, ptr readonly %a2) argmemonly nounwind willreturn
192  %v = load i8, ptr %capture
193  ret i8 %v
194}
195
196; As long as the result is unused, we can even remove reads of the alloca
197; itself since the write will be dropped.
198define void @test_self_read() {
199; CHECK-LABEL: @test_self_read(
200; CHECK-NEXT:    ret void
201;
202  %a = alloca i32, align 4
203  call void @f2(ptr nocapture writeonly %a, ptr nocapture readonly %a) argmemonly nounwind willreturn
204  ret void
205}
206
207
208declare void @removable_readnone() readnone nounwind willreturn
209declare void @removable_ro() readonly nounwind willreturn
210
211define void @test_readnone() {
212; CHECK-LABEL: @test_readnone(
213; CHECK-NEXT:    ret void
214;
215  call void @removable_readnone()
216  ret void
217}
218
219define void @test_readnone_with_deopt() {
220; CHECK-LABEL: @test_readnone_with_deopt(
221; CHECK-NEXT:    ret void
222;
223  call void @removable_readnone() [ "deopt"() ]
224  ret void
225}
226
227define void @test_readonly() {
228; CHECK-LABEL: @test_readonly(
229; CHECK-NEXT:    ret void
230;
231  call void @removable_ro()
232  ret void
233}
234
235define void @test_readonly_with_deopt() {
236; CHECK-LABEL: @test_readonly_with_deopt(
237; CHECK-NEXT:    ret void
238;
239  call void @removable_ro() [ "deopt"() ]
240  ret void
241}
242