xref: /llvm-project/llvm/test/Transforms/Attributor/nofree.ll (revision 29441e4f5fa5f5c7709f7cf180815ba97f611297)
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
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
5target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
6
7; Test cases specifically designed for the "nofree" function attribute.
8; We use FIXME's to indicate problems and missing attributes.
9
10; Free functions
11declare void @free(ptr nocapture) local_unnamed_addr #1
12declare noalias ptr @realloc(ptr nocapture, i64) local_unnamed_addr #0
13declare void @_ZdaPv(ptr) local_unnamed_addr #2
14
15
16; TEST 1 (positive case)
17define void @only_return() #0 {
18; CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable
19; CHECK-LABEL: define {{[^@]+}}@only_return
20; CHECK-SAME: () #[[ATTR3:[0-9]+]] {
21; CHECK-NEXT:    ret void
22;
23  ret void
24}
25
26
27; TEST 2 (negative case)
28; Only free
29; void only_free(char* p) {
30;    free(p);
31; }
32
33define void @only_free(ptr nocapture %0) local_unnamed_addr #0 {
34; CHECK: Function Attrs: noinline nounwind uwtable
35; CHECK-LABEL: define {{[^@]+}}@only_free
36; CHECK-SAME: (ptr captures(none) [[TMP0:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
37; CHECK-NEXT:    tail call void @free(ptr captures(none) [[TMP0]]) #[[ATTR0:[0-9]+]]
38; CHECK-NEXT:    ret void
39;
40  tail call void @free(ptr %0) #1
41  ret void
42}
43
44
45; TEST 3 (negative case)
46; Free occurs in same scc.
47; void free_in_scc1(char*p){
48;    free_in_scc2(p);
49; }
50; void free_in_scc2(char*p){
51;    free_in_scc1(p);
52;    free(p);
53; }
54
55
56define void @free_in_scc1(ptr nocapture %0) local_unnamed_addr #0 {
57; CHECK: Function Attrs: noinline nounwind uwtable
58; CHECK-LABEL: define {{[^@]+}}@free_in_scc1
59; CHECK-SAME: (ptr captures(none) [[TMP0:%.*]]) local_unnamed_addr #[[ATTR1]] {
60; CHECK-NEXT:    tail call void @free_in_scc2(ptr captures(none) [[TMP0]]) #[[ATTR0]]
61; CHECK-NEXT:    ret void
62;
63  tail call void @free_in_scc2(ptr %0) #1
64  ret void
65}
66
67
68define void @free_in_scc2(ptr nocapture %0) local_unnamed_addr #0 {
69; CHECK: Function Attrs: noinline nounwind uwtable
70; CHECK-LABEL: define {{[^@]+}}@free_in_scc2
71; CHECK-SAME: (ptr captures(none) [[TMP0:%.*]]) local_unnamed_addr #[[ATTR1]] {
72; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr [[TMP0]], null
73; CHECK-NEXT:    br i1 [[CMP]], label [[REC:%.*]], label [[CALL:%.*]]
74; CHECK:       call:
75; CHECK-NEXT:    tail call void @free(ptr nonnull captures(none) [[TMP0]]) #[[ATTR0]]
76; CHECK-NEXT:    br label [[END:%.*]]
77; CHECK:       rec:
78; CHECK-NEXT:    tail call void @free_in_scc1(ptr captures(none) [[TMP0]]) #[[ATTR0]]
79; CHECK-NEXT:    br label [[END]]
80; CHECK:       end:
81; CHECK-NEXT:    ret void
82;
83  %cmp = icmp eq ptr %0, null
84  br i1 %cmp, label %rec, label %call
85call:
86  tail call void @free(ptr %0) #1
87  br label %end
88rec:
89  tail call void @free_in_scc1(ptr %0)
90  br label %end
91end:
92  ret void
93}
94
95
96; TEST 4 (positive case)
97; Free doesn't occur.
98; void mutual_recursion1(){
99;    mutual_recursion2();
100; }
101; void mutual_recursion2(){
102;     mutual_recursion1();
103; }
104
105
106define void @mutual_recursion1() #0 {
107; TUNIT: Function Attrs: mustprogress nofree noinline nosync nounwind willreturn memory(none) uwtable
108; TUNIT-LABEL: define {{[^@]+}}@mutual_recursion1
109; TUNIT-SAME: () #[[ATTR4:[0-9]+]] {
110; TUNIT-NEXT:    ret void
111;
112; CGSCC: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable
113; CGSCC-LABEL: define {{[^@]+}}@mutual_recursion1
114; CGSCC-SAME: () #[[ATTR3]] {
115; CGSCC-NEXT:    ret void
116;
117  call void @mutual_recursion2()
118  ret void
119}
120
121define void @mutual_recursion2() #0 {
122; TUNIT: Function Attrs: mustprogress nofree noinline nosync nounwind willreturn memory(none) uwtable
123; TUNIT-LABEL: define {{[^@]+}}@mutual_recursion2
124; TUNIT-SAME: () #[[ATTR4]] {
125; TUNIT-NEXT:    ret void
126;
127; CGSCC: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable
128; CGSCC-LABEL: define {{[^@]+}}@mutual_recursion2
129; CGSCC-SAME: () #[[ATTR3]] {
130; CGSCC-NEXT:    ret void
131;
132  call void @mutual_recursion1()
133  ret void
134}
135
136
137; TEST 5
138; C++ delete operation (negative case)
139; void delete_op (char p[]){
140;     delete [] p;
141; }
142
143define void @_Z9delete_opPc(ptr %0) local_unnamed_addr #0 {
144; CHECK: Function Attrs: noinline nounwind uwtable
145; CHECK-LABEL: define {{[^@]+}}@_Z9delete_opPc
146; CHECK-SAME: (ptr [[TMP0:%.*]]) local_unnamed_addr #[[ATTR1]] {
147; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
148; CHECK-NEXT:    br i1 [[TMP2]], label [[TMP4:%.*]], label [[TMP3:%.*]]
149; CHECK:       3:
150; CHECK-NEXT:    tail call void @_ZdaPv(ptr nonnull [[TMP0]]) #[[ATTR2:[0-9]+]]
151; CHECK-NEXT:    br label [[TMP4]]
152; CHECK:       4:
153; CHECK-NEXT:    ret void
154;
155  %2 = icmp eq ptr %0, null
156  br i1 %2, label %4, label %3
157
158; <label>:3:                                      ; preds = %1
159  tail call void @_ZdaPv(ptr nonnull %0) #2
160  br label %4
161
162; <label>:4:                                      ; preds = %3, %1
163  ret void
164}
165
166
167; TEST 6 (negative case)
168; Call realloc
169define noalias ptr @call_realloc(ptr nocapture %0, i64 %1) local_unnamed_addr #0 {
170; CHECK: Function Attrs: noinline nounwind uwtable
171; CHECK-LABEL: define {{[^@]+}}@call_realloc
172; CHECK-SAME: (ptr captures(none) [[TMP0:%.*]], i64 [[TMP1:%.*]]) local_unnamed_addr #[[ATTR1]] {
173; CHECK-NEXT:    [[RET:%.*]] = tail call ptr @realloc(ptr captures(none) [[TMP0]], i64 [[TMP1]]) #[[ATTR2]]
174; CHECK-NEXT:    ret ptr [[RET]]
175;
176  %ret = tail call ptr @realloc(ptr %0, i64 %1) #2
177  ret ptr %ret
178}
179
180
181; TEST 7 (positive case)
182; Call function declaration with "nofree"
183
184
185; CHECK: Function Attrs:  nofree noinline nounwind memory(none) uwtable
186declare void @nofree_function() nofree readnone #0
187
188define void @call_nofree_function() #0 {
189; TUNIT: Function Attrs: mustprogress nofree noinline nosync nounwind willreturn memory(none) uwtable
190; TUNIT-LABEL: define {{[^@]+}}@call_nofree_function
191; TUNIT-SAME: () #[[ATTR4]] {
192; TUNIT-NEXT:    ret void
193;
194; CGSCC: Function Attrs: mustprogress nofree noinline nosync nounwind willreturn memory(none) uwtable
195; CGSCC-LABEL: define {{[^@]+}}@call_nofree_function
196; CGSCC-SAME: () #[[ATTR5:[0-9]+]] {
197; CGSCC-NEXT:    ret void
198;
199  tail call void @nofree_function()
200  ret void
201}
202
203; TEST 8 (negative case)
204; Call function declaration without "nofree"
205
206
207; CHECK: Function Attrs: noinline nounwind uwtable
208declare void @maybe_free() #0
209
210
211define void @call_maybe_free() #0 {
212; CHECK: Function Attrs: noinline nounwind uwtable
213; CHECK-LABEL: define {{[^@]+}}@call_maybe_free
214; CHECK-SAME: () #[[ATTR1]] {
215; CHECK-NEXT:    tail call void @maybe_free() #[[ATTR0]]
216; CHECK-NEXT:    ret void
217;
218  tail call void @maybe_free()
219  ret void
220}
221
222
223; TEST 9 (negative case)
224; Call both of above functions
225
226define void @call_both() #0 {
227; CHECK: Function Attrs: noinline nounwind uwtable
228; CHECK-LABEL: define {{[^@]+}}@call_both
229; CHECK-SAME: () #[[ATTR1]] {
230; CHECK-NEXT:    tail call void @maybe_free() #[[ATTR0]]
231; CHECK-NEXT:    ret void
232;
233  tail call void @maybe_free()
234  tail call void @nofree_function()
235  ret void
236}
237
238
239; TEST 10 (positive case)
240; Call intrinsic function
241; CHECK: Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
242declare float @llvm.floor.f32(float)
243
244define void @call_floor(float %a) #0 {
245; CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable
246; CHECK-LABEL: define {{[^@]+}}@call_floor
247; CHECK-SAME: (float [[A:%.*]]) #[[ATTR3]] {
248; CHECK-NEXT:    ret void
249;
250  tail call float @llvm.floor.f32(float %a)
251  ret void
252}
253
254define float @call_floor2(float %a) #0 {
255; CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable
256; CHECK-LABEL: define {{[^@]+}}@call_floor2
257; CHECK-SAME: (float [[A:%.*]]) #[[ATTR3]] {
258; CHECK-NEXT:    [[C:%.*]] = tail call nofpclass(sub) float @llvm.floor.f32(float [[A]]) #[[ATTR14:[0-9]+]]
259; CHECK-NEXT:    ret float [[C]]
260;
261  %c = tail call float @llvm.floor.f32(float %a)
262  ret float %c
263}
264
265; TEST 11 (positive case)
266; Check propagation.
267
268define void @f1() #0 {
269; TUNIT: Function Attrs: mustprogress nofree noinline nosync nounwind willreturn memory(none) uwtable
270; TUNIT-LABEL: define {{[^@]+}}@f1
271; TUNIT-SAME: () #[[ATTR4]] {
272; TUNIT-NEXT:    ret void
273;
274; CGSCC: Function Attrs: mustprogress nofree noinline nosync nounwind willreturn memory(none) uwtable
275; CGSCC-LABEL: define {{[^@]+}}@f1
276; CGSCC-SAME: () #[[ATTR5]] {
277; CGSCC-NEXT:    ret void
278;
279  tail call void @nofree_function()
280  ret void
281}
282
283define void @f2() #0 {
284; TUNIT: Function Attrs: mustprogress nofree noinline nosync nounwind willreturn memory(none) uwtable
285; TUNIT-LABEL: define {{[^@]+}}@f2
286; TUNIT-SAME: () #[[ATTR4]] {
287; TUNIT-NEXT:    ret void
288;
289; CGSCC: Function Attrs: mustprogress nofree noinline nosync nounwind willreturn memory(none) uwtable
290; CGSCC-LABEL: define {{[^@]+}}@f2
291; CGSCC-SAME: () #[[ATTR5]] {
292; CGSCC-NEXT:    ret void
293;
294  tail call void @f1()
295  ret void
296}
297
298; TEST 12 NoFree argument - positive.
299define double @test12(ptr nocapture readonly %a) {
300; CHECK: Function Attrs: nofree nounwind
301; CHECK-LABEL: define {{[^@]+}}@test12
302; CHECK-SAME: (ptr nofree noundef nonnull readonly align 8 captures(none) dereferenceable(8) [[A:%.*]]) #[[ATTR7:[0-9]+]] {
303; CHECK-NEXT:  entry:
304; CHECK-NEXT:    [[TMP0:%.*]] = load double, ptr [[A]], align 8
305; CHECK-NEXT:    [[CALL:%.*]] = tail call double @cos(double [[TMP0]]) #[[ATTR8:[0-9]+]]
306; CHECK-NEXT:    ret double [[CALL]]
307;
308entry:
309  %0 = load double, ptr %a, align 8
310  %call = tail call double @cos(double %0) #2
311  ret double %call
312}
313
314declare double @cos(double) nobuiltin nounwind nofree
315
316; FIXME: %a should be nofree.
317; TEST 13 NoFree argument - positive.
318define noalias ptr @test13(ptr nocapture readonly %a) {
319; CHECK: Function Attrs: nounwind
320; CHECK-LABEL: define {{[^@]+}}@test13
321; CHECK-SAME: (ptr nofree noundef nonnull readonly align 8 captures(none) dereferenceable(8) [[A:%.*]]) #[[ATTR0]] {
322; CHECK-NEXT:  entry:
323; CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[A]], align 8
324; CHECK-NEXT:    [[CALL:%.*]] = tail call noalias ptr @malloc(i64 [[TMP0]]) #[[ATTR2]]
325; CHECK-NEXT:    ret ptr [[CALL]]
326;
327entry:
328  %0 = load i64, ptr %a, align 8
329  %call = tail call noalias ptr @malloc(i64 %0) #2
330  ret ptr %call
331}
332
333define void @test14(ptr nocapture %0, ptr nocapture %1) {
334; CHECK: Function Attrs: nounwind
335; CHECK-LABEL: define {{[^@]+}}@test14
336; CHECK-SAME: (ptr captures(none) [[TMP0:%.*]], ptr nofree readnone captures(none) [[TMP1:%.*]]) #[[ATTR0]] {
337; CHECK-NEXT:    tail call void @free(ptr captures(none) [[TMP0]]) #[[ATTR0]]
338; CHECK-NEXT:    ret void
339;
340  tail call void @free(ptr %0) #1
341  ret void
342}
343
344; UTC_ARGS: --enable
345
346define void @nonnull_assume_pos(ptr %arg1, ptr %arg2, ptr %arg3, ptr %arg4) {
347; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_pos
348; ATTRIBUTOR-SAME: (ptr nofree [[ARG1:%.*]], ptr [[ARG2:%.*]], ptr nofree [[ARG3:%.*]], ptr [[ARG4:%.*]])
349; ATTRIBUTOR-NEXT:    call void @llvm.assume(i1 true) #11 [ "nofree"(ptr [[ARG1]]), "nofree"(ptr [[ARG3]]) ]
350; ATTRIBUTOR-NEXT:    call void @unknown(ptr nofree [[ARG1]], ptr [[ARG2]], ptr nofree [[ARG3]], ptr [[ARG4]])
351; ATTRIBUTOR-NEXT:    ret void
352;
353; CHECK-LABEL: define {{[^@]+}}@nonnull_assume_pos
354; CHECK-SAME: (ptr nofree [[ARG1:%.*]], ptr [[ARG2:%.*]], ptr nofree [[ARG3:%.*]], ptr [[ARG4:%.*]]) {
355; CHECK-NEXT:    call void @llvm.assume(i1 noundef true) #[[ATTR15:[0-9]+]] [ "nofree"(ptr [[ARG1]]), "nofree"(ptr [[ARG3]]) ]
356; CHECK-NEXT:    call void @unknown(ptr nofree [[ARG1]], ptr [[ARG2]], ptr nofree [[ARG3]], ptr [[ARG4]])
357; CHECK-NEXT:    ret void
358;
359  call void @llvm.assume(i1 true) ["nofree"(ptr %arg1), "nofree"(ptr %arg3)]
360  call void @unknown(ptr %arg1, ptr %arg2, ptr %arg3, ptr %arg4)
361  ret void
362}
363define void @nonnull_assume_neg(ptr %arg1, ptr %arg2, ptr %arg3, ptr %arg4) {
364; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_neg
365; ATTRIBUTOR-SAME: (ptr [[ARG1:%.*]], ptr [[ARG2:%.*]], ptr [[ARG3:%.*]], ptr [[ARG4:%.*]])
366; ATTRIBUTOR-NEXT:    call void @unknown(ptr [[ARG1]], ptr [[ARG2]], ptr [[ARG3]], ptr [[ARG4]])
367; ATTRIBUTOR-NEXT:    call void @llvm.assume(i1 true) [ "nofree"(ptr [[ARG1]]), "nofree"(ptr [[ARG3]]) ]
368; ATTRIBUTOR-NEXT:    ret void
369;
370; CHECK-LABEL: define {{[^@]+}}@nonnull_assume_neg
371; CHECK-SAME: (ptr [[ARG1:%.*]], ptr [[ARG2:%.*]], ptr [[ARG3:%.*]], ptr [[ARG4:%.*]]) {
372; CHECK-NEXT:    call void @unknown(ptr [[ARG1]], ptr [[ARG2]], ptr [[ARG3]], ptr [[ARG4]])
373; CHECK-NEXT:    call void @llvm.assume(i1 noundef true) [ "nofree"(ptr [[ARG1]]), "nofree"(ptr [[ARG3]]) ]
374; CHECK-NEXT:    ret void
375;
376  call void @unknown(ptr %arg1, ptr %arg2, ptr %arg3, ptr %arg4)
377  call void @llvm.assume(i1 true) ["nofree"(ptr %arg1), "nofree"(ptr %arg3)]
378  ret void
379}
380define void @nonnull_assume_call(ptr %arg1, ptr %arg2, ptr %arg3, ptr %arg4) {
381; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_call
382; ATTRIBUTOR-SAME: (ptr [[ARG1:%.*]], ptr [[ARG2:%.*]], ptr [[ARG3:%.*]], ptr [[ARG4:%.*]])
383; ATTRIBUTOR-NEXT:    call void @unknown(ptr [[ARG1]], ptr [[ARG2]], ptr [[ARG3]], ptr [[ARG4]])
384; ATTRIBUTOR-NEXT:    call void @use_i8_ptr(ptr noalias readnone [[ARG1]])
385; ATTRIBUTOR-NEXT:    call void @use_i8_ptr(ptr noalias readnone [[ARG2]])
386; ATTRIBUTOR-NEXT:    call void @llvm.assume(i1 true) [ "nofree"(ptr [[ARG1]]), "nofree"(ptr [[ARG3]]) ]
387; ATTRIBUTOR-NEXT:    call void @use_i8_ptr(ptr noalias nofree readnone [[ARG3]])
388; ATTRIBUTOR-NEXT:    call void @use_i8_ptr(ptr noalias readnone [[ARG4]])
389; ATTRIBUTOR-NEXT:    call void @use_i8_ptr_ret(ptr noalias nofree readnone [[ARG1]])
390; ATTRIBUTOR-NEXT:    call void @use_i8_ptr_ret(ptr noalias readnone [[ARG2]])
391; ATTRIBUTOR-NEXT:    call void @llvm.assume(i1 true) [ "nofree"(ptr [[ARG1]]), "nofree"(ptr [[ARG4]]) ]
392; ATTRIBUTOR-NEXT:    call void @use_i8_ptr_ret(ptr noalias nofree readnone [[ARG3]])
393; ATTRIBUTOR-NEXT:    call void @use_i8_ptr_ret(ptr noalias nofree readnone [[ARG4]])
394; ATTRIBUTOR-NEXT:    ret void
395;
396; CHECK-LABEL: define {{[^@]+}}@nonnull_assume_call
397; CHECK-SAME: (ptr [[ARG1:%.*]], ptr [[ARG2:%.*]], ptr [[ARG3:%.*]], ptr [[ARG4:%.*]]) {
398; CHECK-NEXT:    call void @unknown(ptr [[ARG1]], ptr [[ARG2]], ptr [[ARG3]], ptr [[ARG4]])
399; CHECK-NEXT:    call void @use_i8_ptr(ptr noalias nofree readnone captures(none) [[ARG1]]) #[[ATTR0]]
400; CHECK-NEXT:    call void @use_i8_ptr(ptr noalias nofree readnone captures(none) [[ARG2]]) #[[ATTR0]]
401; CHECK-NEXT:    call void @llvm.assume(i1 noundef true) [ "nofree"(ptr [[ARG1]]), "nofree"(ptr [[ARG3]]) ]
402; CHECK-NEXT:    call void @use_i8_ptr(ptr noalias nofree readnone captures(none) [[ARG3]]) #[[ATTR0]]
403; CHECK-NEXT:    call void @use_i8_ptr(ptr noalias nofree readnone captures(none) [[ARG4]]) #[[ATTR0]]
404; CHECK-NEXT:    call void @use_i8_ptr_ret(ptr noalias nofree readnone captures(none) [[ARG1]]) #[[ATTR0]]
405; CHECK-NEXT:    call void @use_i8_ptr_ret(ptr noalias nofree readnone captures(none) [[ARG2]]) #[[ATTR0]]
406; CHECK-NEXT:    call void @llvm.assume(i1 noundef true) [ "nofree"(ptr [[ARG1]]), "nofree"(ptr [[ARG4]]) ]
407; CHECK-NEXT:    call void @use_i8_ptr_ret(ptr noalias nofree readnone captures(none) [[ARG3]]) #[[ATTR0]]
408; CHECK-NEXT:    call void @use_i8_ptr_ret(ptr noalias nofree readnone captures(none) [[ARG4]]) #[[ATTR0]]
409; CHECK-NEXT:    ret void
410;
411  call void @unknown(ptr %arg1, ptr %arg2, ptr %arg3, ptr %arg4)
412  call void @use_i8_ptr(ptr %arg1)
413  call void @use_i8_ptr(ptr %arg2)
414  call void @llvm.assume(i1 true) ["nofree"(ptr %arg1), "nofree"(ptr %arg3)]
415  call void @use_i8_ptr(ptr %arg3)
416  call void @use_i8_ptr(ptr %arg4)
417  call void @use_i8_ptr_ret(ptr %arg1)
418  call void @use_i8_ptr_ret(ptr %arg2)
419  call void @llvm.assume(i1 true) ["nofree"(ptr %arg1), "nofree"(ptr %arg4)]
420  call void @use_i8_ptr_ret(ptr %arg3)
421  call void @use_i8_ptr_ret(ptr %arg4)
422  ret void
423}
424
425; FIXME: function is nofree
426define weak void @implied_nofree1() readnone {
427; CHECK: Function Attrs: nosync memory(none)
428; CHECK-LABEL: define {{[^@]+}}@implied_nofree1
429; CHECK-SAME: () #[[ATTR9:[0-9]+]] {
430; CHECK-NEXT:    ret void
431;
432  ret void
433}
434; FIXME: function is nofree
435define weak void @implied_nofree2() readonly {
436; CHECK: Function Attrs: nosync memory(read)
437; CHECK-LABEL: define {{[^@]+}}@implied_nofree2
438; CHECK-SAME: () #[[ATTR10:[0-9]+]] {
439; CHECK-NEXT:    ret void
440;
441  ret void
442}
443define weak void @implied_nofree3(ptr readnone %a) {
444; CHECK-LABEL: define {{[^@]+}}@implied_nofree3
445; CHECK-SAME: (ptr nofree readnone [[A:%.*]]) {
446; CHECK-NEXT:    ret void
447;
448  ret void
449}
450define weak void @implied_nofree4(ptr readonly %a) {
451; CHECK-LABEL: define {{[^@]+}}@implied_nofree4
452; CHECK-SAME: (ptr nofree readonly [[A:%.*]]) {
453; CHECK-NEXT:    ret void
454;
455  ret void
456}
457; FIXME: %a is nofree
458define weak void @implied_nofree5(ptr %a) readonly {
459; CHECK: Function Attrs: nosync memory(read)
460; CHECK-LABEL: define {{[^@]+}}@implied_nofree5
461; CHECK-SAME: (ptr [[A:%.*]]) #[[ATTR10]] {
462; CHECK-NEXT:    ret void
463;
464  ret void
465}
466define weak void @implied_nofree6(ptr %a) nofree {
467; CHECK: Function Attrs: nofree
468; CHECK-LABEL: define {{[^@]+}}@implied_nofree6
469; CHECK-SAME: (ptr nofree [[A:%.*]]) #[[ATTR11:[0-9]+]] {
470; CHECK-NEXT:    ret void
471;
472  ret void
473}
474
475declare void @llvm.assume(i1)
476declare void @unknown(ptr, ptr, ptr, ptr)
477declare void @use_i8_ptr(ptr nocapture readnone) nounwind
478declare void @use_i8_ptr_ret(ptr nocapture readnone) nounwind willreturn
479
480declare noalias ptr @malloc(i64)
481
482attributes #0 = { nounwind uwtable noinline }
483attributes #1 = { nounwind }
484attributes #2 = { nobuiltin nounwind }
485;.
486; TUNIT: attributes #[[ATTR0]] = { nounwind }
487; TUNIT: attributes #[[ATTR1]] = { noinline nounwind uwtable }
488; TUNIT: attributes #[[ATTR2]] = { nobuiltin nounwind }
489; TUNIT: attributes #[[ATTR3]] = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable }
490; TUNIT: attributes #[[ATTR4]] = { mustprogress nofree noinline nosync nounwind willreturn memory(none) uwtable }
491; TUNIT: attributes #[[ATTR5:[0-9]+]] = { nofree noinline nounwind memory(none) uwtable }
492; TUNIT: attributes #[[ATTR6:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
493; TUNIT: attributes #[[ATTR7]] = { nofree nounwind }
494; TUNIT: attributes #[[ATTR8]] = { nobuiltin nofree nounwind }
495; TUNIT: attributes #[[ATTR9]] = { nosync memory(none) }
496; TUNIT: attributes #[[ATTR10]] = { nosync memory(read) }
497; TUNIT: attributes #[[ATTR11]] = { nofree }
498; TUNIT: attributes #[[ATTR12:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }
499; TUNIT: attributes #[[ATTR13:[0-9]+]] = { nounwind willreturn }
500; TUNIT: attributes #[[ATTR14]] = { nofree nosync willreturn }
501; TUNIT: attributes #[[ATTR15]] = { nofree willreturn memory(write) }
502;.
503; CGSCC: attributes #[[ATTR0]] = { nounwind }
504; CGSCC: attributes #[[ATTR1]] = { noinline nounwind uwtable }
505; CGSCC: attributes #[[ATTR2]] = { nobuiltin nounwind }
506; CGSCC: attributes #[[ATTR3]] = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable }
507; CGSCC: attributes #[[ATTR4:[0-9]+]] = { nofree noinline nounwind memory(none) uwtable }
508; CGSCC: attributes #[[ATTR5]] = { mustprogress nofree noinline nosync nounwind willreturn memory(none) uwtable }
509; CGSCC: attributes #[[ATTR6:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
510; CGSCC: attributes #[[ATTR7]] = { nofree nounwind }
511; CGSCC: attributes #[[ATTR8]] = { nobuiltin nofree nounwind }
512; CGSCC: attributes #[[ATTR9]] = { nosync memory(none) }
513; CGSCC: attributes #[[ATTR10]] = { nosync memory(read) }
514; CGSCC: attributes #[[ATTR11]] = { nofree }
515; CGSCC: attributes #[[ATTR12:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }
516; CGSCC: attributes #[[ATTR13:[0-9]+]] = { nounwind willreturn }
517; CGSCC: attributes #[[ATTR14]] = { nofree nosync willreturn }
518; CGSCC: attributes #[[ATTR15]] = { nofree willreturn memory(write) }
519;.
520