1 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2 ; RUN: opt -dse -S < %s | FileCheck %s
4 declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture)
5 declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture)
7 declare void @unknown()
9 declare void @f2(i8*, i8*)
10 declare i8* @f3(i8*, i8*)
12 ; Basic case for DSEing a trivially dead writing call
13 define void @test_dead() {
14 ; CHECK-LABEL: @test_dead(
15 ; CHECK-NEXT: ret void
17 %a = alloca i32, align 4
18 %bitcast = bitcast i32* %a to i8*
19 call void @f(i8* writeonly nocapture %bitcast) argmemonly nounwind willreturn
23 ; Add in canonical lifetime intrinsics
24 define void @test_lifetime() {
25 ; CHECK-LABEL: @test_lifetime(
26 ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
27 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[A]] to i8*
28 ; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[BITCAST]])
29 ; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* [[BITCAST]])
30 ; CHECK-NEXT: ret void
32 %a = alloca i32, align 4
33 %bitcast = bitcast i32* %a to i8*
34 call void @llvm.lifetime.start.p0i8(i64 4, i8* %bitcast)
35 call void @f(i8* writeonly nocapture %bitcast) argmemonly nounwind willreturn
36 call void @llvm.lifetime.end.p0i8(i64 4, i8* %bitcast)
40 ; Add some unknown calls just to point out that this is use based, not
41 ; instruction order sensitive
42 define void @test_lifetime2() {
43 ; CHECK-LABEL: @test_lifetime2(
44 ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
45 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[A]] to i8*
46 ; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[BITCAST]])
47 ; CHECK-NEXT: call void @unknown()
48 ; CHECK-NEXT: call void @unknown()
49 ; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* [[BITCAST]])
50 ; CHECK-NEXT: ret void
52 %a = alloca i32, align 4
53 %bitcast = bitcast i32* %a to i8*
54 call void @llvm.lifetime.start.p0i8(i64 4, i8* %bitcast)
56 call void @f(i8* writeonly nocapture %bitcast) argmemonly nounwind willreturn
58 call void @llvm.lifetime.end.p0i8(i64 4, i8* %bitcast)
62 ; As long as the result is unused, we can even remove reads of the alloca
63 ; itself since the write will be dropped.
64 define void @test_dead_readwrite() {
65 ; CHECK-LABEL: @test_dead_readwrite(
66 ; CHECK-NEXT: ret void
68 %a = alloca i32, align 4
69 %bitcast = bitcast i32* %a to i8*
70 call void @f(i8* nocapture %bitcast) argmemonly nounwind willreturn
74 define i32 @test_neg_read_after() {
75 ; CHECK-LABEL: @test_neg_read_after(
76 ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
77 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[A]] to i8*
78 ; CHECK-NEXT: call void @f(i8* nocapture writeonly [[BITCAST]]) #[[ATTR1:[0-9]+]]
79 ; CHECK-NEXT: [[RES:%.*]] = load i32, i32* [[A]], align 4
80 ; CHECK-NEXT: ret i32 [[RES]]
82 %a = alloca i32, align 4
83 %bitcast = bitcast i32* %a to i8*
84 call void @f(i8* writeonly nocapture %bitcast) argmemonly nounwind willreturn
85 %res = load i32, i32* %a
90 define void @test_neg_infinite_loop() {
91 ; CHECK-LABEL: @test_neg_infinite_loop(
92 ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
93 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[A]] to i8*
94 ; CHECK-NEXT: call void @f(i8* nocapture writeonly [[BITCAST]]) #[[ATTR2:[0-9]+]]
95 ; CHECK-NEXT: ret void
97 %a = alloca i32, align 4
98 %bitcast = bitcast i32* %a to i8*
99 call void @f(i8* writeonly nocapture %bitcast) argmemonly nounwind
103 define void @test_neg_throw() {
104 ; CHECK-LABEL: @test_neg_throw(
105 ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
106 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[A]] to i8*
107 ; CHECK-NEXT: call void @f(i8* nocapture writeonly [[BITCAST]]) #[[ATTR3:[0-9]+]]
108 ; CHECK-NEXT: ret void
110 %a = alloca i32, align 4
111 %bitcast = bitcast i32* %a to i8*
112 call void @f(i8* writeonly nocapture %bitcast) argmemonly willreturn
116 define void @test_neg_extra_write() {
117 ; CHECK-LABEL: @test_neg_extra_write(
118 ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
119 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[A]] to i8*
120 ; CHECK-NEXT: call void @f(i8* nocapture writeonly [[BITCAST]]) #[[ATTR4:[0-9]+]]
121 ; CHECK-NEXT: ret void
123 %a = alloca i32, align 4
124 %bitcast = bitcast i32* %a to i8*
125 call void @f(i8* writeonly nocapture %bitcast) nounwind willreturn
129 ; In this case, we can't remove a1 because we need to preserve the write to
130 ; a2, and if we leave the call around, we need memory to pass to the first arg.
131 define void @test_neg_unmodeled_write() {
132 ; CHECK-LABEL: @test_neg_unmodeled_write(
133 ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
134 ; CHECK-NEXT: [[A2:%.*]] = alloca i32, align 4
135 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[A]] to i8*
136 ; CHECK-NEXT: [[BITCAST2:%.*]] = bitcast i32* [[A2]] to i8*
137 ; CHECK-NEXT: call void @f2(i8* nocapture writeonly [[BITCAST]], i8* [[BITCAST2]]) #[[ATTR1]]
138 ; CHECK-NEXT: ret void
140 %a = alloca i32, align 4
141 %a2 = alloca i32, align 4
142 %bitcast = bitcast i32* %a to i8*
143 %bitcast2 = bitcast i32* %a2 to i8*
144 call void @f2(i8* nocapture writeonly %bitcast, i8* %bitcast2) argmemonly nounwind willreturn
148 define i32 @test_neg_captured_by_call() {
149 ; CHECK-LABEL: @test_neg_captured_by_call(
150 ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
151 ; CHECK-NEXT: [[A2:%.*]] = alloca i8*, align 4
152 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[A]] to i8*
153 ; CHECK-NEXT: [[BITCAST2:%.*]] = bitcast i8** [[A2]] to i8*
154 ; CHECK-NEXT: call void @f2(i8* writeonly [[BITCAST]], i8* [[BITCAST2]]) #[[ATTR1]]
155 ; CHECK-NEXT: [[A_COPY_CAST:%.*]] = load i8*, i8** [[A2]], align 8
156 ; CHECK-NEXT: [[A_COPY:%.*]] = bitcast i8* [[A_COPY_CAST]] to i32*
157 ; CHECK-NEXT: [[RES:%.*]] = load i32, i32* [[A_COPY]], align 4
158 ; CHECK-NEXT: ret i32 [[RES]]
160 %a = alloca i32, align 4
161 %a2 = alloca i8*, align 4
162 %bitcast = bitcast i32* %a to i8*
163 %bitcast2 = bitcast i8** %a2 to i8*
164 call void @f2(i8* writeonly %bitcast, i8* %bitcast2) argmemonly nounwind willreturn
165 %a_copy_cast = load i8*, i8** %a2
166 %a_copy = bitcast i8* %a_copy_cast to i32*
167 %res = load i32, i32* %a_copy
171 define i32 @test_neg_captured_before() {
172 ; CHECK-LABEL: @test_neg_captured_before(
173 ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
174 ; CHECK-NEXT: [[A2:%.*]] = alloca i8*, align 4
175 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[A]] to i8*
176 ; CHECK-NEXT: [[BITCAST2:%.*]] = bitcast i8** [[A2]] to i8*
177 ; CHECK-NEXT: store i8* [[BITCAST]], i8** [[A2]], align 8
178 ; CHECK-NEXT: call void @f(i8* nocapture writeonly [[BITCAST]]) #[[ATTR1]]
179 ; CHECK-NEXT: [[A_COPY_CAST:%.*]] = load i8*, i8** [[A2]], align 8
180 ; CHECK-NEXT: [[A_COPY:%.*]] = bitcast i8* [[A_COPY_CAST]] to i32*
181 ; CHECK-NEXT: [[RES:%.*]] = load i32, i32* [[A_COPY]], align 4
182 ; CHECK-NEXT: ret i32 [[RES]]
184 %a = alloca i32, align 4
185 %a2 = alloca i8*, align 4
186 %bitcast = bitcast i32* %a to i8*
187 %bitcast2 = bitcast i8** %a2 to i8*
188 store i8* %bitcast, i8** %a2
189 call void @f(i8* writeonly nocapture %bitcast) argmemonly nounwind willreturn
190 %a_copy_cast = load i8*, i8** %a2
191 %a_copy = bitcast i8* %a_copy_cast to i32*
192 %res = load i32, i32* %a_copy
196 ; Callee might be dead, but op bundle has unknown semantics and thus isn't.
197 define void @test_new_op_bundle() {
198 ; CHECK-LABEL: @test_new_op_bundle(
199 ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
200 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[A]] to i8*
201 ; CHECK-NEXT: call void @f(i8* nocapture writeonly [[BITCAST]]) #[[ATTR1]] [ "unknown"(i8* [[BITCAST]]) ]
202 ; CHECK-NEXT: ret void
204 %a = alloca i32, align 4
205 %bitcast = bitcast i32* %a to i8*
206 call void @f(i8* writeonly nocapture %bitcast) argmemonly nounwind willreturn ["unknown" (i8* %bitcast)]
210 ; Show that reading from unrelated memory is okay
211 define void @test_unreleated_read() {
212 ; CHECK-LABEL: @test_unreleated_read(
213 ; CHECK-NEXT: ret void
215 %a = alloca i32, align 4
216 %a2 = alloca i32, align 4
217 %bitcast = bitcast i32* %a to i8*
218 %bitcast2 = bitcast i32* %a2 to i8*
219 call void @f2(i8* nocapture writeonly %bitcast, i8* nocapture readonly %bitcast2) argmemonly nounwind willreturn
223 ; Removing a capture is also okay. The capture can only be in the return value
224 ; (which is unused) or written into the dead out parameter.
225 define void @test_unrelated_capture() {
226 ; CHECK-LABEL: @test_unrelated_capture(
227 ; CHECK-NEXT: ret void
229 %a = alloca i32, align 4
230 %a2 = alloca i32, align 4
231 %bitcast = bitcast i32* %a to i8*
232 %bitcast2 = bitcast i32* %a2 to i8*
233 call i8* @f3(i8* nocapture writeonly %bitcast, i8* readonly %bitcast2) argmemonly nounwind willreturn
237 ; Cannot remove call, as %bitcast2 is captured via the return value.
238 define i8 @test_neg_unrelated_capture_used_via_return() {
239 ; CHECK-LABEL: @test_neg_unrelated_capture_used_via_return(
240 ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
241 ; CHECK-NEXT: [[A2:%.*]] = alloca i32, align 4
242 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[A]] to i8*
243 ; CHECK-NEXT: [[BITCAST2:%.*]] = bitcast i32* [[A2]] to i8*
244 ; CHECK-NEXT: [[CAPTURE:%.*]] = call i8* @f3(i8* nocapture writeonly [[BITCAST]], i8* readonly [[BITCAST2]]) #[[ATTR1]]
245 ; CHECK-NEXT: [[V:%.*]] = load i8, i8* [[CAPTURE]], align 1
246 ; CHECK-NEXT: ret i8 [[V]]
248 %a = alloca i32, align 4
249 %a2 = alloca i32, align 4
250 %bitcast = bitcast i32* %a to i8*
251 %bitcast2 = bitcast i32* %a2 to i8*
252 %capture = call i8* @f3(i8* nocapture writeonly %bitcast, i8* readonly %bitcast2) argmemonly nounwind willreturn
253 %v = load i8, i8* %capture
257 ; As long as the result is unused, we can even remove reads of the alloca
258 ; itself since the write will be dropped.
259 define void @test_self_read() {
260 ; CHECK-LABEL: @test_self_read(
261 ; CHECK-NEXT: ret void
263 %a = alloca i32, align 4
264 %bitcast = bitcast i32* %a to i8*
265 call void @f2(i8* nocapture writeonly %bitcast, i8* nocapture readonly %bitcast) argmemonly nounwind willreturn
269 ; We can remove the call because while we don't know the size of the write done
270 ; by the call, we do know the following store writes to the entire contents of
272 define i32 @test_dse_overwrite() {
273 ; CHECK-LABEL: @test_dse_overwrite(
274 ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
275 ; CHECK-NEXT: store i32 0, i32* [[A]], align 4
276 ; CHECK-NEXT: [[V:%.*]] = load i32, i32* [[A]], align 4
277 ; CHECK-NEXT: ret i32 [[V]]
279 %a = alloca i32, align 4
280 %bitcast = bitcast i32* %a to i8*
281 call void @f(i8* writeonly nocapture %bitcast) argmemonly nounwind willreturn
283 %v = load i32, i32* %a
287 ; Negative case where we can read part of the value written by @f.
288 define i32 @test_neg_dse_partial_overwrite() {
289 ; CHECK-LABEL: @test_neg_dse_partial_overwrite(
290 ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
291 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[A]] to i8*
292 ; CHECK-NEXT: call void @f(i8* nocapture writeonly [[BITCAST]]) #[[ATTR1]]
293 ; CHECK-NEXT: store i8 0, i8* [[BITCAST]], align 1
294 ; CHECK-NEXT: [[V:%.*]] = load i32, i32* [[A]], align 4
295 ; CHECK-NEXT: ret i32 [[V]]
297 %a = alloca i32, align 4
298 %bitcast = bitcast i32* %a to i8*
299 call void @f(i8* writeonly nocapture %bitcast) argmemonly nounwind willreturn
300 store i8 0, i8* %bitcast
301 %v = load i32, i32* %a
305 ; Negative case where we don't know the size of a, and thus can't use the
306 ; full overwrite reasoning
307 define i32 @test_neg_dse_unsized(i32* %a) {
308 ; CHECK-LABEL: @test_neg_dse_unsized(
309 ; CHECK-NEXT: [[BITCAST:%.*]] = bitcast i32* [[A:%.*]] to i8*
310 ; CHECK-NEXT: call void @f(i8* nocapture writeonly [[BITCAST]]) #[[ATTR1]]
311 ; CHECK-NEXT: store i32 0, i32* [[A]], align 4
312 ; CHECK-NEXT: [[V:%.*]] = load i32, i32* [[A]], align 4
313 ; CHECK-NEXT: ret i32 [[V]]
315 %bitcast = bitcast i32* %a to i8*
316 call void @f(i8* writeonly nocapture %bitcast) argmemonly nounwind willreturn
318 %v = load i32, i32* %a
324 ; Same as test_dse_overwrite, but with a non-alloca object.
325 define void @test_dse_non_alloca() {
326 ; CHECK-LABEL: @test_dse_non_alloca(
327 ; CHECK-NEXT: store i8 0, i8* @G, align 1
328 ; CHECK-NEXT: ret void
330 call void @f(i8* writeonly nocapture @G) argmemonly nounwind willreturn