xref: /llvm-project/llvm/test/Transforms/DeadStoreElimination/multiblock-captures.ll (revision f497a00da968b0ff90d8c98caa184d14b9a92495)
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2; RUN: opt < %s -passes=dse -S | FileCheck %s
3
4target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
5
6declare noalias ptr @malloc(i64)
7
8declare void @foo()
9declare void @capture(ptr)
10
11; Check that we do not remove the second store, as %m is returned.
12define ptr @test_return_captures_1() {
13; CHECK-LABEL: @test_return_captures_1(
14; CHECK-NEXT:    [[M:%.*]] = call ptr @malloc(i64 24)
15; CHECK-NEXT:    store i8 1, ptr [[M]], align 1
16; CHECK-NEXT:    ret ptr [[M]]
17;
18  %m = call ptr @malloc(i64 24)
19  store i8 0, ptr %m
20  store i8 1, ptr %m
21  ret ptr %m
22}
23
24; Same as @test_return_captures_1, but across BBs.
25define ptr @test_return_captures_2() {
26; CHECK-LABEL: @test_return_captures_2(
27; CHECK-NEXT:    [[M:%.*]] = call ptr @malloc(i64 24)
28; CHECK-NEXT:    br label [[EXIT:%.*]]
29; CHECK:       exit:
30; CHECK-NEXT:    store i8 1, ptr [[M]], align 1
31; CHECK-NEXT:    ret ptr [[M]]
32;
33  %m = call ptr @malloc(i64 24)
34  store i8 0, ptr %m
35  br label %exit
36
37exit:
38  store i8 1, ptr %m
39  ret ptr %m
40}
41
42
43%S1 = type { ptr }
44
45; We cannot remove the last store to %m, because it escapes by storing it to %E.
46define void @test_malloc_capture_1(ptr %E) {
47; CHECK-LABEL: @test_malloc_capture_1(
48; CHECK-NEXT:    [[M:%.*]] = call ptr @malloc(i64 24)
49; CHECK-NEXT:    br label [[EXIT:%.*]]
50; CHECK:       exit:
51; CHECK-NEXT:    store ptr [[M]], ptr [[E:%.*]], align 4
52; CHECK-NEXT:    store i8 1, ptr [[M]], align 1
53; CHECK-NEXT:    ret void
54;
55  %m = call ptr @malloc(i64 24)
56  br label %exit
57
58exit:
59  store ptr %m, ptr %E
60  store i8 1, ptr %m
61  ret void
62}
63
64; Check we do not eliminate either store. The first one cannot be eliminated,
65; due to the call of @capture. The second one because %m escapes.
66define ptr @test_malloc_capture_2() {
67; CHECK-LABEL: @test_malloc_capture_2(
68; CHECK-NEXT:    [[M:%.*]] = call ptr @malloc(i64 24)
69; CHECK-NEXT:    store i8 0, ptr [[M]], align 1
70; CHECK-NEXT:    call void @capture(ptr [[M]])
71; CHECK-NEXT:    br label [[EXIT:%.*]]
72; CHECK:       exit:
73; CHECK-NEXT:    store i8 1, ptr [[M]], align 1
74; CHECK-NEXT:    ret ptr [[M]]
75;
76  %m = call ptr @malloc(i64 24)
77  store i8 0, ptr %m
78  call void @capture(ptr %m)
79  br label %exit
80
81exit:
82  store i8 1, ptr %m
83  ret ptr %m
84}
85
86; We can remove the first store store i8 0, ptr %m because there are no throwing
87; instructions between the 2 stores and also %m escapes after the killing store.
88define ptr @test_malloc_capture_3() {
89; CHECK-LABEL: @test_malloc_capture_3(
90; CHECK-NEXT:    [[M:%.*]] = call ptr @malloc(i64 24)
91; CHECK-NEXT:    br label [[EXIT:%.*]]
92; CHECK:       exit:
93; CHECK-NEXT:    store i8 1, ptr [[M]], align 1
94; CHECK-NEXT:    call void @capture(ptr [[M]])
95; CHECK-NEXT:    ret ptr [[M]]
96;
97  %m = call ptr @malloc(i64 24)
98  store i8 0, ptr %m
99  br label %exit
100
101exit:
102  store i8 1, ptr %m
103  call void @capture(ptr %m)
104  ret ptr %m
105}
106
107; TODO: We could remove the first store store i8 0, ptr %m because %m escapes
108; after the killing store.
109define ptr @test_malloc_capture_4() {
110; CHECK-LABEL: @test_malloc_capture_4(
111; CHECK-NEXT:    [[M:%.*]] = call ptr @malloc(i64 24)
112; CHECK-NEXT:    store i8 0, ptr [[M]], align 1
113; CHECK-NEXT:    call void @may_throw_readnone()
114; CHECK-NEXT:    br label [[EXIT:%.*]]
115; CHECK:       exit:
116; CHECK-NEXT:    store i8 1, ptr [[M]], align 1
117; CHECK-NEXT:    call void @capture(ptr [[M]])
118; CHECK-NEXT:    ret ptr [[M]]
119;
120
121  %m = call ptr @malloc(i64 24)
122  store i8 0, ptr %m
123  call void @may_throw_readnone()
124  br label %exit
125
126exit:
127  store i8 1, ptr %m
128  call void @capture(ptr %m)
129  ret ptr %m
130}
131
132
133; We cannot remove the first store store i8 0, ptr %m because %m escapes
134; before the killing store and we may throw in between.
135define ptr @test_malloc_capture_5() {
136; CHECK-LABEL: @test_malloc_capture_5(
137; CHECK-NEXT:    [[M:%.*]] = call ptr @malloc(i64 24)
138; CHECK-NEXT:    call void @capture(ptr [[M]])
139; CHECK-NEXT:    store i8 0, ptr [[M]], align 1
140; CHECK-NEXT:    call void @may_throw_readnone()
141; CHECK-NEXT:    br label [[EXIT:%.*]]
142; CHECK:       exit:
143; CHECK-NEXT:    store i8 1, ptr [[M]], align 1
144; CHECK-NEXT:    ret ptr [[M]]
145;
146
147  %m = call ptr @malloc(i64 24)
148  call void @capture(ptr %m)
149  store i8 0, ptr %m
150  call void @may_throw_readnone()
151  br label %exit
152
153exit:
154  store i8 1, ptr %m
155  ret ptr %m
156}
157
158
159; TODO: We could remove the first store 'store i8 0, ptr %m' even though there
160; is a throwing instruction between them, because %m escapes after the killing
161; store.
162define ptr @test_malloc_capture_6() {
163; CHECK-LABEL: @test_malloc_capture_6(
164; CHECK-NEXT:    [[M:%.*]] = call ptr @malloc(i64 24)
165; CHECK-NEXT:    store i8 0, ptr [[M]], align 1
166; CHECK-NEXT:    call void @may_throw_readnone()
167; CHECK-NEXT:    br label [[EXIT:%.*]]
168; CHECK:       exit:
169; CHECK-NEXT:    store i8 1, ptr [[M]], align 1
170; CHECK-NEXT:    call void @capture(ptr [[M]])
171; CHECK-NEXT:    ret ptr [[M]]
172;
173
174  %m = call ptr @malloc(i64 24)
175  store i8 0, ptr %m
176  call void @may_throw_readnone()
177  br label %exit
178
179exit:
180  store i8 1, ptr %m
181  call void @capture(ptr %m)
182  ret ptr %m
183}
184
185; We *could* remove the first store 'store i8 0, ptr %m' even though there is a
186; throwing instruction between them, because %m escapes after the killing store.
187; But this would require using PointerMayBeCapturedBefore in
188; isInvisibleToCallerBeforeRet, which we currently do not do to limit
189; compile-time, as this appears to hardly ever lead to more stores eliminated
190; in practice.
191define ptr @test_malloc_capture_7() {
192; CHECK-LABEL: @test_malloc_capture_7(
193; CHECK-NEXT:    [[M:%.*]] = call ptr @malloc(i64 24)
194; CHECK-NEXT:    store i8 0, ptr [[M]], align 1
195; CHECK-NEXT:    call void @may_throw()
196; CHECK-NEXT:    br label [[EXIT:%.*]]
197; CHECK:       exit:
198; CHECK-NEXT:    store i8 1, ptr [[M]], align 1
199; CHECK-NEXT:    call void @capture(ptr [[M]])
200; CHECK-NEXT:    ret ptr [[M]]
201;
202
203  %m = call ptr @malloc(i64 24)
204  store i8 0, ptr %m
205  call void @may_throw()
206  br label %exit
207
208exit:
209  store i8 1, ptr %m
210  call void @capture(ptr %m)
211  ret ptr %m
212}
213
214; Stores to stack objects can be eliminated if they are not captured inside the function.
215define void @test_alloca_nocapture_1() {
216; CHECK-LABEL: @test_alloca_nocapture_1(
217; CHECK-NEXT:    call void @foo()
218; CHECK-NEXT:    br label [[EXIT:%.*]]
219; CHECK:       exit:
220; CHECK-NEXT:    ret void
221;
222  %m = alloca i8
223  store i8 0, ptr %m
224  call void @foo()
225  br label %exit
226
227exit:
228  store i8 1, ptr %m
229  ret void
230}
231
232; Cannot remove first store i8 0, ptr %m, as the call to @capture captures the object.
233define void @test_alloca_capture_1() {
234; CHECK-LABEL: @test_alloca_capture_1(
235; CHECK-NEXT:    [[M:%.*]] = alloca i8, align 1
236; CHECK-NEXT:    store i8 0, ptr [[M]], align 1
237; CHECK-NEXT:    call void @capture(ptr [[M]])
238; CHECK-NEXT:    br label [[EXIT:%.*]]
239; CHECK:       exit:
240; CHECK-NEXT:    ret void
241;
242  %m = alloca i8
243  store i8 0, ptr %m
244  call void @capture(ptr %m)
245  br label %exit
246
247exit:
248  store i8 1, ptr %m
249  ret void
250}
251
252; We can remove the last store to %m, even though it escapes because the alloca
253; becomes invalid after the function returns.
254define void @test_alloca_capture_2(ptr %E) {
255; CHECK-LABEL: @test_alloca_capture_2(
256; CHECK-NEXT:    [[M:%.*]] = alloca i8, align 1
257; CHECK-NEXT:    br label [[EXIT:%.*]]
258; CHECK:       exit:
259; CHECK-NEXT:    store ptr [[M]], ptr [[E:%.*]], align 4
260; CHECK-NEXT:    ret void
261;
262  %m = alloca i8
263  br label %exit
264
265exit:
266  store ptr %m, ptr %E
267  store i8 1, ptr %m
268  ret void
269}
270
271; Readnone functions are not modeled in MemorySSA, but could throw.
272; Make sure we do not eliminate the first store 'store i8 2, ptr %call'
273define void @malloc_capture_throw_1() {
274; CHECK-LABEL: @malloc_capture_throw_1(
275; CHECK-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 1)
276; CHECK-NEXT:    call void @may_capture(ptr [[CALL]])
277; CHECK-NEXT:    store i8 2, ptr [[CALL]], align 1
278; CHECK-NEXT:    call void @may_throw_readnone()
279; CHECK-NEXT:    store i8 3, ptr [[CALL]], align 1
280; CHECK-NEXT:    ret void
281;
282  %call = call ptr @malloc(i64 1)
283  call void @may_capture(ptr %call)
284  store i8 2, ptr %call, align 1
285  call void @may_throw_readnone()
286  store i8 3, ptr %call, align 1
287  ret void
288}
289
290; Readnone functions are not modeled in MemorySSA, but could throw.
291; Make sure we do not eliminate the first store 'store i8 2, ptr %call'
292define void @malloc_capture_throw_2() {
293; CHECK-LABEL: @malloc_capture_throw_2(
294; CHECK-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 1)
295; CHECK-NEXT:    call void @may_capture(ptr [[CALL]])
296; CHECK-NEXT:    store i8 2, ptr [[CALL]], align 1
297; CHECK-NEXT:    br label [[BB:%.*]]
298; CHECK:       bb:
299; CHECK-NEXT:    call void @may_throw_readnone()
300; CHECK-NEXT:    store i8 3, ptr [[CALL]], align 1
301; CHECK-NEXT:    ret void
302;
303  %call = call ptr @malloc(i64 1)
304  call void @may_capture(ptr %call)
305  store i8 2, ptr %call, align 1
306  br label %bb
307
308bb:
309  call void @may_throw_readnone()
310  store i8 3, ptr %call, align 1
311  ret void
312}
313
314
315declare void @may_capture(ptr)
316declare void @may_throw_readnone() readnone
317declare void @may_throw()
318