xref: /llvm-project/llvm/test/Transforms/Attributor/allocator.ll (revision 29441e4f5fa5f5c7709f7cf180815ba97f611297)
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --version 2
2; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-annotate-decl-cs  -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
3; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
4
5%struct.Foo = type { i32, i32, i8 }
6
7@.str = private unnamed_addr constant [17 x i8] c"The value is %d\0A\00", align 1
8
9;.
10; CHECK: @.str = private unnamed_addr constant [17 x i8] c"The value is %d\0A\00", align 1
11;.
12define dso_local void @positive_alloca_1(i32 noundef %val) #0 {
13; CHECK-LABEL: define dso_local void @positive_alloca_1
14; CHECK-SAME: (i32 noundef [[VAL:%.*]]) {
15; CHECK-NEXT:  entry:
16; CHECK-NEXT:    [[VAL_ADDR1:%.*]] = alloca i8, i32 4, align 4
17; CHECK-NEXT:    [[F2:%.*]] = alloca i8, i32 4, align 4
18; CHECK-NEXT:    store i32 [[VAL]], ptr [[VAL_ADDR1]], align 4
19; CHECK-NEXT:    store i32 10, ptr [[F2]], align 4
20; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[F2]], align 4
21; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[TMP0]], 1
22; CHECK-NEXT:    store i32 [[ADD]], ptr [[F2]], align 4
23; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[F2]], align 4
24; CHECK-NEXT:    [[ADD3:%.*]] = add nsw i32 [[TMP1]], [[VAL]]
25; CHECK-NEXT:    [[CALL:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, i32 noundef [[ADD3]])
26; CHECK-NEXT:    ret void
27;
28entry:
29  %val.addr = alloca i64, align 4
30  %f = alloca %struct.Foo, align 4
31  store i32 %val, ptr %val.addr, align 4
32  %field1 = getelementptr inbounds %struct.Foo, ptr %f, i32 0, i32 0
33  store i32 10, ptr %field1, align 4
34  %field11 = getelementptr inbounds %struct.Foo, ptr %f, i32 0, i32 0
35  %0 = load i32, ptr %field11, align 4
36  %add = add nsw i32 %0, 1
37  store i32 %add, ptr %field11, align 4
38  %field12 = getelementptr inbounds %struct.Foo, ptr %f, i32 0, i32 0
39  %1 = load i32, ptr %field12, align 4
40  %2 = load i32, ptr %val.addr, align 4
41  %add3 = add nsw i32 %1, %2
42  %call = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %add3)
43  ret void
44}
45
46; TODO: change malloc like call
47; Function Attrs: noinline nounwind uwtable
48define dso_local void @positive_malloc_1(ptr noundef %val) #0 {
49; CHECK-LABEL: define dso_local void @positive_malloc_1
50; CHECK-SAME: (ptr nofree noundef readonly captures(none) [[VAL:%.*]]) {
51; CHECK-NEXT:  entry:
52; CHECK-NEXT:    [[VAL_ADDR:%.*]] = alloca ptr, align 8
53; CHECK-NEXT:    [[F:%.*]] = alloca ptr, align 8
54; CHECK-NEXT:    store ptr [[VAL]], ptr [[VAL_ADDR]], align 8
55; CHECK-NEXT:    [[CALL:%.*]] = call noalias ptr @malloc(i64 noundef 12)
56; CHECK-NEXT:    store ptr [[CALL]], ptr [[F]], align 8
57; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[VAL]], align 4
58; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[TMP0]], 10
59; CHECK-NEXT:    store i32 [[ADD]], ptr [[CALL]], align 4
60; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[CALL]], align 4
61; CHECK-NEXT:    [[CALL2:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, i32 noundef [[TMP1]])
62; CHECK-NEXT:    ret void
63;
64entry:
65  %val.addr = alloca ptr, align 8
66  %f = alloca ptr, align 8
67  store ptr %val, ptr %val.addr, align 8
68  %call = call noalias ptr @malloc(i64 noundef 12) #3
69  store ptr %call, ptr %f, align 8
70  %0 = load ptr, ptr %val.addr, align 8
71  %1 = load i32, ptr %0, align 4
72  %add = add nsw i32 %1, 10
73  %2 = load ptr, ptr %f, align 8
74  %a = getelementptr inbounds %struct.Foo, ptr %2, i32 0, i32 0
75  store i32 %add, ptr %a, align 4
76  %3 = load ptr, ptr %f, align 8
77  %a1 = getelementptr inbounds %struct.Foo, ptr %3, i32 0, i32 0
78  %4 = load i32, ptr %a1, align 4
79  %call2 = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %4)
80  ret void
81}
82
83; TODO: change malloc like call
84; Function Attrs: noinline nounwind uwtable
85define dso_local void @positive_malloc_2(ptr noundef %val) #0 {
86; CHECK-LABEL: define dso_local void @positive_malloc_2
87; CHECK-SAME: (ptr nofree noundef readonly captures(none) [[VAL:%.*]]) {
88; CHECK-NEXT:  entry:
89; CHECK-NEXT:    [[VAL_ADDR:%.*]] = alloca ptr, align 8
90; CHECK-NEXT:    [[F:%.*]] = alloca ptr, align 8
91; CHECK-NEXT:    store ptr [[VAL]], ptr [[VAL_ADDR]], align 8
92; CHECK-NEXT:    [[CALL:%.*]] = call noalias ptr @malloc(i64 noundef 60)
93; CHECK-NEXT:    store ptr [[CALL]], ptr [[F]], align 8
94; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[VAL]], align 4
95; CHECK-NEXT:    store i32 [[TMP0]], ptr [[CALL]], align 4
96; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[CALL]], align 4
97; CHECK-NEXT:    [[CALL2:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, i32 noundef [[TMP1]])
98; CHECK-NEXT:    ret void
99;
100entry:
101  %val.addr = alloca ptr, align 8
102  %x = alloca i32, align 4
103  %f = alloca ptr, align 8
104  store ptr %val, ptr %val.addr, align 8
105  store i32 15, ptr %x, align 4
106  %0 = load i32, ptr %x, align 4
107  %conv = sext i32 %0 to i64
108  %mul = mul i64 4, %conv
109  %call = call noalias ptr @malloc(i64 noundef %mul)
110  store ptr %call, ptr %f, align 8
111  %1 = load ptr, ptr %val.addr, align 8
112  %2 = load i32, ptr %1, align 4
113  %3 = load ptr, ptr %f, align 8
114  %arrayidx = getelementptr inbounds i32, ptr %3, i64 0
115  store i32 %2, ptr %arrayidx, align 4
116  %4 = load ptr, ptr %f, align 8
117  %arrayidx1 = getelementptr inbounds i32, ptr %4, i64 0
118  %5 = load i32, ptr %arrayidx1, align 4
119  %call2 = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %5)
120  ret void
121}
122
123; Function Attrs: noinline nounwind uwtable
124define dso_local ptr @negative_test_escaping_pointer(i32 noundef %val) #0 {
125; CHECK-LABEL: define dso_local ptr @negative_test_escaping_pointer
126; CHECK-SAME: (i32 noundef [[VAL:%.*]]) {
127; CHECK-NEXT:  entry:
128; CHECK-NEXT:    [[VAL_ADDR:%.*]] = alloca i32, align 4
129; CHECK-NEXT:    [[F:%.*]] = alloca ptr, align 8
130; CHECK-NEXT:    store i32 [[VAL]], ptr [[VAL_ADDR]], align 4
131; CHECK-NEXT:    [[CALL:%.*]] = call noalias ptr @malloc(i64 noundef 16)
132; CHECK-NEXT:    store ptr [[CALL]], ptr [[F]], align 8
133; CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[F]], align 8
134; CHECK-NEXT:    store i32 2, ptr [[TMP0]], align 8
135; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 10, [[VAL]]
136; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[F]], align 8
137; CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 8
138; CHECK-NEXT:    [[ADD2:%.*]] = add nsw i32 [[TMP2]], [[ADD]]
139; CHECK-NEXT:    store i32 [[ADD2]], ptr [[TMP1]], align 8
140; CHECK-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[F]], align 8
141; CHECK-NEXT:    ret ptr [[TMP3]]
142;
143entry:
144  %val.addr = alloca i32, align 4
145  %f = alloca ptr, align 8
146  store i32 %val, ptr %val.addr, align 4
147  %call = call noalias ptr @malloc(i64 noundef 16) #2
148  store ptr %call, ptr %f, align 8
149  %0 = load ptr, ptr %f, align 8
150  %field1 = getelementptr inbounds %struct.Foo, ptr %0, i32 0, i32 0
151  store i32 2, ptr %field1, align 8
152  %1 = load i32, ptr %val.addr, align 4
153  %add = add nsw i32 10, %1
154  %2 = load ptr, ptr %f, align 8
155  %field11 = getelementptr inbounds %struct.Foo, ptr %2, i32 0, i32 0
156  %3 = load i32, ptr %field11, align 8
157  %add2 = add nsw i32 %3, %add
158  store i32 %add2, ptr %field11, align 8
159  %4 = load ptr, ptr %f, align 8
160  ret ptr %4
161}
162
163
164;TODO: The allocation can be reduced here.
165;However, the offsets (load/store etc.) Need to be changed.
166; Function Attrs: noinline nounwind uwtable
167define dso_local { i64, ptr } @positive_test_not_a_single_start_offset(i32 noundef %val) #0 {
168; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
169; CHECK-LABEL: define dso_local { i64, ptr } @positive_test_not_a_single_start_offset
170; CHECK-SAME: (i32 noundef [[VAL:%.*]]) #[[ATTR0:[0-9]+]] {
171; CHECK-NEXT:  entry:
172; CHECK-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8
173; CHECK-NEXT:    [[VAL_ADDR:%.*]] = alloca i32, align 4
174; CHECK-NEXT:    store i32 [[VAL]], ptr [[VAL_ADDR]], align 4
175; CHECK-NEXT:    store i32 2, ptr [[RETVAL]], align 8
176; CHECK-NEXT:    [[FIELD3:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[RETVAL]], i32 0, i32 2
177; CHECK-NEXT:    store ptr [[VAL_ADDR]], ptr [[FIELD3]], align 8
178; CHECK-NEXT:    [[TMP0:%.*]] = load { i64, ptr }, ptr [[RETVAL]], align 8
179; CHECK-NEXT:    ret { i64, ptr } [[TMP0]]
180;
181entry:
182  %retval = alloca %struct.Foo, align 8
183  %val.addr = alloca i32, align 4
184  store i32 %val, ptr %val.addr, align 4
185  %field1 = getelementptr inbounds %struct.Foo, ptr %retval, i32 0, i32 0
186  store i32 2, ptr %field1, align 8
187  %field3 = getelementptr inbounds %struct.Foo, ptr %retval, i32 0, i32 2
188  store ptr %val.addr, ptr %field3, align 8
189  %0 = load { i64, ptr }, ptr %retval, align 8
190  ret { i64, ptr } %0
191}
192
193; Function Attrs: noinline nounwind uwtable
194define dso_local void @positive_test_reduce_array_allocation_1() {
195; CHECK-LABEL: define dso_local void @positive_test_reduce_array_allocation_1() {
196; CHECK-NEXT:  entry:
197; CHECK-NEXT:    [[ARRAY1:%.*]] = alloca i8, i32 4, align 8
198; CHECK-NEXT:    store i32 0, ptr [[ARRAY1]], align 8
199; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[ARRAY1]], align 8
200; CHECK-NEXT:    [[TMP1:%.*]] = add i32 [[TMP0]], 2
201; CHECK-NEXT:    store i32 [[TMP1]], ptr [[ARRAY1]], align 8
202; CHECK-NEXT:    [[TMP2:%.*]] = add i32 1, 2
203; CHECK-NEXT:    [[TMP3:%.*]] = load i32, ptr [[ARRAY1]], align 8
204; CHECK-NEXT:    [[TMP4:%.*]] = add i32 [[TMP2]], [[TMP3]]
205; CHECK-NEXT:    store i32 [[TMP4]], ptr [[ARRAY1]], align 8
206; CHECK-NEXT:    [[TMP5:%.*]] = load i32, ptr [[ARRAY1]], align 8
207; CHECK-NEXT:    [[CALL:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, i32 noundef [[TMP5]])
208; CHECK-NEXT:    ret void
209;
210entry:
211  %array = alloca ptr, i32 10
212  store i32 0, ptr %array
213  %0 = load i32, ptr %array
214  %1 = add i32 %0, 2
215  store i32 %1, ptr %array
216  %2 = add i32 1, 2
217  %3 = load i32, ptr %array
218  %4 = add i32 %2, %3
219  store i32 %4, ptr %array
220  %5 = load i32, ptr %array
221  %call = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %5)
222  ret void
223}
224
225
226; Function Attrs: noinline nounwind uwtable
227; TODO: Here the array size is not known at compile time.
228; However the array does not escape and is only partially used.
229; Should the optimization reduce the allocation size regardless? Based on AAPointerInfo.
230define dso_local void @baz(ptr noundef %val, i32 noundef %arrayLength) #0 {
231; CHECK-LABEL: define dso_local void @baz
232; CHECK-SAME: (ptr nofree noundef readonly captures(none) [[VAL:%.*]], i32 noundef [[ARRAYLENGTH:%.*]]) {
233; CHECK-NEXT:  entry:
234; CHECK-NEXT:    [[VAL_ADDR:%.*]] = alloca ptr, align 8
235; CHECK-NEXT:    [[ARRAYLENGTH_ADDR:%.*]] = alloca i32, align 4
236; CHECK-NEXT:    [[F:%.*]] = alloca ptr, align 8
237; CHECK-NEXT:    store ptr [[VAL]], ptr [[VAL_ADDR]], align 8
238; CHECK-NEXT:    store i32 [[ARRAYLENGTH]], ptr [[ARRAYLENGTH_ADDR]], align 4
239; CHECK-NEXT:    [[CONV:%.*]] = sext i32 [[ARRAYLENGTH]] to i64
240; CHECK-NEXT:    [[MUL:%.*]] = mul i64 4, [[CONV]]
241; CHECK-NEXT:    [[CALL:%.*]] = call noalias ptr @malloc(i64 noundef [[MUL]])
242; CHECK-NEXT:    store ptr [[CALL]], ptr [[F]], align 8
243; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[VAL]], align 4
244; CHECK-NEXT:    store i32 [[TMP0]], ptr [[CALL]], align 4
245; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[CALL]], align 4
246; CHECK-NEXT:    [[CALL2:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, i32 noundef [[TMP1]])
247; CHECK-NEXT:    ret void
248;
249entry:
250  %val.addr = alloca ptr, align 8
251  %arrayLength.addr = alloca i32, align 4
252  %f = alloca ptr, align 8
253  store ptr %val, ptr %val.addr, align 8
254  store i32 %arrayLength, ptr %arrayLength.addr, align 4
255  %0 = load i32, ptr %arrayLength.addr, align 4
256  %conv = sext i32 %0 to i64
257  %mul = mul i64 4, %conv
258  %call = call noalias ptr @malloc(i64 noundef %mul) #3
259  store ptr %call, ptr %f, align 8
260  %1 = load ptr, ptr %val.addr, align 8
261  %2 = load i32, ptr %1, align 4
262  %3 = load ptr, ptr %f, align 8
263  %arrayidx = getelementptr inbounds i32, ptr %3, i64 0
264  store i32 %2, ptr %arrayidx, align 4
265  %4 = load ptr, ptr %f, align 8
266  %arrayidx1 = getelementptr inbounds i32, ptr %4, i64 0
267  %5 = load i32, ptr %arrayidx1, align 4
268  %call2 = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %5)
269  ret void
270}
271
272;TODO: Here since only even indexes of the array are part of the output
273;We can reduce the allocation by half and make an array that's accessed contiguously
274; Function Attrs: noinline nounwind uwtable
275define dso_local void @positive_test_reduce_array_allocation_2() #0 {
276; CHECK-LABEL: define dso_local void @positive_test_reduce_array_allocation_2() {
277; CHECK-NEXT:  entry:
278; CHECK-NEXT:    [[ARRAY:%.*]] = alloca ptr, align 8
279; CHECK-NEXT:    [[I:%.*]] = alloca i32, align 4
280; CHECK-NEXT:    [[CALL:%.*]] = call noalias ptr @malloc(i64 noundef 40000)
281; CHECK-NEXT:    store ptr [[CALL]], ptr [[ARRAY]], align 8
282; CHECK-NEXT:    store i32 0, ptr [[I]], align 4
283; CHECK-NEXT:    br label [[FOR_COND:%.*]]
284; CHECK:       for.cond:
285; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[I]], align 4
286; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[TMP0]], 10000
287; CHECK-NEXT:    br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]]
288; CHECK:       for.body:
289; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[I]], align 4
290; CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[I]], align 4
291; CHECK-NEXT:    [[IDXPROM:%.*]] = sext i32 [[TMP2]] to i64
292; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDXPROM]]
293; CHECK-NEXT:    store i32 [[TMP1]], ptr [[ARRAYIDX]], align 4
294; CHECK-NEXT:    br label [[FOR_INC:%.*]]
295; CHECK:       for.inc:
296; CHECK-NEXT:    [[TMP3:%.*]] = load i32, ptr [[I]], align 4
297; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[TMP3]], 2
298; CHECK-NEXT:    store i32 [[ADD]], ptr [[I]], align 4
299; CHECK-NEXT:    br label [[FOR_COND]]
300; CHECK:       for.end:
301; CHECK-NEXT:    store i32 0, ptr [[I]], align 4
302; CHECK-NEXT:    br label [[FOR_COND1:%.*]]
303; CHECK:       for.cond1:
304; CHECK-NEXT:    [[TMP4:%.*]] = load i32, ptr [[I]], align 4
305; CHECK-NEXT:    [[CMP2:%.*]] = icmp slt i32 [[TMP4]], 10000
306; CHECK-NEXT:    br i1 [[CMP2]], label [[FOR_BODY3:%.*]], label [[FOR_END9:%.*]]
307; CHECK:       for.body3:
308; CHECK-NEXT:    [[TMP5:%.*]] = load i32, ptr [[I]], align 4
309; CHECK-NEXT:    [[IDXPROM4:%.*]] = sext i32 [[TMP5]] to i64
310; CHECK-NEXT:    [[ARRAYIDX5:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDXPROM4]]
311; CHECK-NEXT:    [[TMP6:%.*]] = load i32, ptr [[ARRAYIDX5]], align 4
312; CHECK-NEXT:    [[ADD6:%.*]] = add nsw i32 [[TMP6]], 1
313; CHECK-NEXT:    store i32 [[ADD6]], ptr [[ARRAYIDX5]], align 4
314; CHECK-NEXT:    br label [[FOR_INC7:%.*]]
315; CHECK:       for.inc7:
316; CHECK-NEXT:    [[TMP7:%.*]] = load i32, ptr [[I]], align 4
317; CHECK-NEXT:    [[ADD8:%.*]] = add nsw i32 [[TMP7]], 2
318; CHECK-NEXT:    store i32 [[ADD8]], ptr [[I]], align 4
319; CHECK-NEXT:    br label [[FOR_COND1]]
320; CHECK:       for.end9:
321; CHECK-NEXT:    store i32 0, ptr [[I]], align 4
322; CHECK-NEXT:    br label [[FOR_COND10:%.*]]
323; CHECK:       for.cond10:
324; CHECK-NEXT:    [[TMP8:%.*]] = load i32, ptr [[I]], align 4
325; CHECK-NEXT:    [[CMP11:%.*]] = icmp slt i32 [[TMP8]], 10000
326; CHECK-NEXT:    br i1 [[CMP11]], label [[FOR_BODY12:%.*]], label [[FOR_END18:%.*]]
327; CHECK:       for.body12:
328; CHECK-NEXT:    [[TMP9:%.*]] = load i32, ptr [[I]], align 4
329; CHECK-NEXT:    [[IDXPROM13:%.*]] = sext i32 [[TMP9]] to i64
330; CHECK-NEXT:    [[ARRAYIDX14:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDXPROM13]]
331; CHECK-NEXT:    [[TMP10:%.*]] = load i32, ptr [[ARRAYIDX14]], align 4
332; CHECK-NEXT:    [[CALL15:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, i32 noundef [[TMP10]])
333; CHECK-NEXT:    br label [[FOR_INC16:%.*]]
334; CHECK:       for.inc16:
335; CHECK-NEXT:    [[TMP11:%.*]] = load i32, ptr [[I]], align 4
336; CHECK-NEXT:    [[ADD17:%.*]] = add nsw i32 [[TMP11]], 2
337; CHECK-NEXT:    store i32 [[ADD17]], ptr [[I]], align 4
338; CHECK-NEXT:    br label [[FOR_COND10]]
339; CHECK:       for.end18:
340; CHECK-NEXT:    ret void
341;
342entry:
343  %array = alloca ptr, align 8
344  %i = alloca i32, align 4
345  %call = call noalias ptr @malloc(i64 noundef 40000) #3
346  store ptr %call, ptr %array, align 8
347  store i32 0, ptr %i, align 4
348  br label %for.cond
349
350for.cond:
351  %0 = load i32, ptr %i, align 4
352  %cmp = icmp slt i32 %0, 10000
353  br i1 %cmp, label %for.body, label %for.end
354
355for.body:
356  %1 = load i32, ptr %i, align 4
357  %2 = load ptr, ptr %array, align 8
358  %3 = load i32, ptr %i, align 4
359  %idxprom = sext i32 %3 to i64
360  %arrayidx = getelementptr inbounds i32, ptr %2, i64 %idxprom
361  store i32 %1, ptr %arrayidx, align 4
362  br label %for.inc
363
364for.inc:
365  %4 = load i32, ptr %i, align 4
366  %add = add nsw i32 %4, 2
367  store i32 %add, ptr %i, align 4
368  br label %for.cond
369
370for.end:
371  store i32 0, ptr %i, align 4
372  br label %for.cond1
373
374for.cond1:
375  %5 = load i32, ptr %i, align 4
376  %cmp2 = icmp slt i32 %5, 10000
377  br i1 %cmp2, label %for.body3, label %for.end9
378
379for.body3:
380  %6 = load ptr, ptr %array, align 8
381  %7 = load i32, ptr %i, align 4
382  %idxprom4 = sext i32 %7 to i64
383  %arrayidx5 = getelementptr inbounds i32, ptr %6, i64 %idxprom4
384  %8 = load i32, ptr %arrayidx5, align 4
385  %add6 = add nsw i32 %8, 1
386  store i32 %add6, ptr %arrayidx5, align 4
387  br label %for.inc7
388
389for.inc7:
390  %9 = load i32, ptr %i, align 4
391  %add8 = add nsw i32 %9, 2
392  store i32 %add8, ptr %i, align 4
393  br label %for.cond1
394
395for.end9:
396  store i32 0, ptr %i, align 4
397  br label %for.cond10
398
399for.cond10:
400  %10 = load i32, ptr %i, align 4
401  %cmp11 = icmp slt i32 %10, 10000
402  br i1 %cmp11, label %for.body12, label %for.end18
403
404for.body12:
405  %11 = load ptr, ptr %array, align 8
406  %12 = load i32, ptr %i, align 4
407  %idxprom13 = sext i32 %12 to i64
408  %arrayidx14 = getelementptr inbounds i32, ptr %11, i64 %idxprom13
409  %13 = load i32, ptr %arrayidx14, align 4
410  %call15 = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %13)
411  br label %for.inc16
412
413for.inc16:
414  %14 = load i32, ptr %i, align 4
415  %add17 = add nsw i32 %14, 2
416  store i32 %add17, ptr %i, align 4
417  br label %for.cond10
418
419for.end18:
420  ret void
421}
422
423
424define dso_local void @pthread_test(){
425; TUNIT-LABEL: define dso_local void @pthread_test() {
426; TUNIT-NEXT:    [[ARG1:%.*]] = alloca i8, align 8
427; TUNIT-NEXT:    [[THREAD:%.*]] = alloca i64, align 8
428; TUNIT-NEXT:    [[CALL1:%.*]] = call i32 @pthread_create(ptr noundef nonnull align 8 dereferenceable(8) [[THREAD]], ptr noundef align 4294967296 null, ptr noundef nonnull @pthread_allocation_should_remain_same, ptr noundef nonnull align 8 dereferenceable(1) [[ARG1]])
429; TUNIT-NEXT:    [[F1:%.*]] = alloca i8, i32 4, align 4
430; TUNIT-NEXT:    [[CALL2:%.*]] = call i32 @pthread_create(ptr noundef nonnull align 8 dereferenceable(8) [[THREAD]], ptr noundef align 4294967296 null, ptr noundef nonnull @pthread_allocation_should_be_reduced, ptr noalias nofree nonnull readnone align 4 captures(none) dereferenceable(12) undef)
431; TUNIT-NEXT:    [[F2:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4
432; TUNIT-NEXT:    [[CALL3:%.*]] = call i32 @pthread_create(ptr noundef nonnull align 8 dereferenceable(8) [[THREAD]], ptr noundef align 4294967296 null, ptr noundef nonnull @pthread_check_captured_pointer, ptr noundef nonnull align 4 dereferenceable(12) [[F2]])
433; TUNIT-NEXT:    ret void
434;
435; CGSCC-LABEL: define dso_local void @pthread_test() {
436; CGSCC-NEXT:    [[ARG1:%.*]] = alloca i8, align 8
437; CGSCC-NEXT:    [[THREAD:%.*]] = alloca i64, align 8
438; CGSCC-NEXT:    [[CALL1:%.*]] = call i32 @pthread_create(ptr noundef nonnull align 8 dereferenceable(8) [[THREAD]], ptr noundef align 4294967296 null, ptr noundef nonnull @pthread_allocation_should_remain_same, ptr noundef nonnull align 8 dereferenceable(1) [[ARG1]])
439; CGSCC-NEXT:    [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4
440; CGSCC-NEXT:    [[CALL2:%.*]] = call i32 @pthread_create(ptr noundef nonnull align 8 dereferenceable(8) [[THREAD]], ptr noundef align 4294967296 null, ptr noundef nonnull @pthread_allocation_should_be_reduced, ptr noalias nofree noundef nonnull readonly align 4 captures(none) dereferenceable(12) [[F]])
441; CGSCC-NEXT:    [[F2:%.*]] = alloca [[STRUCT_FOO]], align 4
442; CGSCC-NEXT:    [[CALL3:%.*]] = call i32 @pthread_create(ptr noundef nonnull align 8 dereferenceable(8) [[THREAD]], ptr noundef align 4294967296 null, ptr noundef nonnull @pthread_check_captured_pointer, ptr noundef nonnull align 4 dereferenceable(12) [[F2]])
443; CGSCC-NEXT:    ret void
444;
445  %arg1 = alloca i8, align 8
446  %thread = alloca i64, align 8
447  %call1 = call i32 @pthread_create(ptr nonnull %thread, ptr null, ptr nonnull @pthread_allocation_should_remain_same, ptr %arg1)
448  %f = alloca %struct.Foo, align 4
449  %call2 = call i32 @pthread_create(ptr nonnull %thread, ptr null, ptr nonnull @pthread_allocation_should_be_reduced, ptr %f)
450  %f2 = alloca %struct.Foo, align 4
451  %call3 = call i32 @pthread_create(ptr nonnull %thread, ptr null, ptr nonnull @pthread_check_captured_pointer, ptr %f2)
452  ret void
453}
454
455define internal ptr @pthread_allocation_should_remain_same(ptr %arg) {
456; CHECK-LABEL: define internal noundef nonnull align 8 dereferenceable(1) ptr @pthread_allocation_should_remain_same
457; CHECK-SAME: (ptr noundef nonnull returned align 8 dereferenceable(1) [[ARG:%.*]]) {
458; CHECK-NEXT:  entry:
459; CHECK-NEXT:    [[CALL:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, ptr noundef nonnull align 8 dereferenceable(1) [[ARG]])
460; CHECK-NEXT:    ret ptr [[ARG]]
461;
462entry:
463  %call = call i32 (ptr, ...) @printf(ptr noundef @.str, ptr noundef %arg)
464  ret ptr %arg
465}
466
467define internal void @pthread_allocation_should_be_reduced(ptr %arg) {
468;
469; TUNIT-LABEL: define internal void @pthread_allocation_should_be_reduced
470; TUNIT-SAME: (ptr noalias nofree nonnull readnone align 4 captures(none) dereferenceable(12) [[ARG:%.*]]) {
471; TUNIT-NEXT:  entry:
472; TUNIT-NEXT:    [[CALL:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, i32 undef)
473; TUNIT-NEXT:    ret void
474;
475; CGSCC-LABEL: define internal void @pthread_allocation_should_be_reduced
476; CGSCC-SAME: (ptr noalias nofree noundef nonnull readonly align 4 captures(none) dereferenceable(12) [[ARG:%.*]]) {
477; CGSCC-NEXT:  entry:
478; CGSCC-NEXT:    [[TMP0:%.*]] = load i32, ptr [[ARG]], align 4
479; CGSCC-NEXT:    [[CALL:%.*]] = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(17) @.str, i32 noundef [[TMP0]])
480; CGSCC-NEXT:    ret void
481;
482entry:
483  %field1 = getelementptr inbounds %struct.Foo, ptr %arg, i32 0, i32 0
484  %0 = load i32, ptr %field1, align 4
485  %call = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %0)
486  ret void
487}
488
489define internal void @pthread_check_captured_pointer(ptr %arg){
490; CHECK-LABEL: define internal void @pthread_check_captured_pointer
491; CHECK-SAME: (ptr noundef nonnull align 4 dereferenceable(12) [[ARG:%.*]]) {
492; CHECK-NEXT:  entry:
493; CHECK-NEXT:    call void @external_call(ptr noundef nonnull align 4 dereferenceable(12) [[ARG]])
494; CHECK-NEXT:    ret void
495;
496entry:
497  %field1 = getelementptr inbounds %struct.Foo, ptr %arg, i32 0, i32 0
498  call void @external_call(ptr %field1)
499  ret void
500}
501
502
503declare external void @external_call(ptr)
504
505declare !callback !0 dso_local i32 @pthread_create(ptr, ptr, ptr, ptr)
506!1 = !{i64 2, i64 3, i1 false}
507!0 = !{!1}
508
509declare i32 @printf(ptr noundef, ...) #1
510
511; Function Attrs: nounwind allocsize(0)
512declare noalias ptr @malloc(i64 noundef) #1
513;.
514; TUNIT: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) }
515;.
516; CGSCC: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) }
517;.
518; TUNIT: [[META0:![0-9]+]] = !{[[META1:![0-9]+]]}
519; TUNIT: [[META1]] = !{i64 2, i64 3, i1 false}
520;.
521; CGSCC: [[META0:![0-9]+]] = !{[[META1:![0-9]+]]}
522; CGSCC: [[META1]] = !{i64 2, i64 3, i1 false}
523;.
524