1; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR
2; Copied from Transforms/FunctoinAttrs/nofree-attributor.ll
3
4target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
5
6; Test cases specifically designed for the "nofree" function attribute.
7; We use FIXME's to indicate problems and missing attributes.
8
9; Free functions
10declare void @free(i8* nocapture) local_unnamed_addr #1
11declare noalias i8* @realloc(i8* nocapture, i64) local_unnamed_addr #0
12declare void @_ZdaPv(i8*) local_unnamed_addr #2
13
14
15; TEST 1 (positive case)
16; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
17; ATTRIBUTOR-NEXT: define void @only_return()
18define void @only_return() #0 {
19    ret void
20}
21
22
23; TEST 2 (negative case)
24; Only free
25; void only_free(char* p) {
26;    free(p);
27; }
28
29; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
30; ATTRIBUTOR-NOT: nofree
31; ATTRIBUTOR-NEXT: define void @only_free(i8* nocapture %0) local_unnamed_addr #1
32define void @only_free(i8* nocapture %0) local_unnamed_addr #0 {
33    tail call void @free(i8* %0) #1
34    ret void
35}
36
37
38; TEST 3 (negative case)
39; Free occurs in same scc.
40; void free_in_scc1(char*p){
41;    free_in_scc2(p);
42; }
43; void free_in_scc2(char*p){
44;    free_in_scc1(p);
45;    free(p);
46; }
47
48
49; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
50; ATTRIBUTOR-NOT: nofree
51; ATTRIBUTOR-NEXT :define void @free_in_scc1(i8* nocapture %0) local_unnamed_addr
52define void @free_in_scc1(i8* nocapture %0) local_unnamed_addr #0 {
53  tail call void @free_in_scc2(i8* %0) #1
54  ret void
55}
56
57
58; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
59; ATTRIBUTOR-NOT: nofree
60; ATTRIBUTOR: define void @free_in_scc2(i8* nocapture %0) local_unnamed_addr
61define void @free_in_scc2(i8* nocapture %0) local_unnamed_addr #0 {
62  %cmp = icmp eq i8* %0, null
63  br i1 %cmp, label %rec, label %call
64call:
65  tail call void @free(i8* %0) #1
66  br label %end
67rec:
68  tail call void @free_in_scc1(i8* %0)
69  br label %end
70end:
71  ret void
72}
73
74
75; TEST 4 (positive case)
76; Free doesn't occur.
77; void mutual_recursion1(){
78;    mutual_recursion2();
79; }
80; void mutual_recursion2(){
81;     mutual_recursion1();
82; }
83
84
85; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable
86; ATTRIBUTOR-NEXT: define void @mutual_recursion1()
87define void @mutual_recursion1() #0 {
88  call void @mutual_recursion2()
89  ret void
90}
91
92; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable
93; ATTRIBUTOR-NEXT: define void @mutual_recursion2()
94define void @mutual_recursion2() #0 {
95  call void @mutual_recursion1()
96  ret void
97}
98
99
100; TEST 5
101; C++ delete operation (negative case)
102; void delete_op (char p[]){
103;     delete [] p;
104; }
105
106; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
107; ATTRIBUTOR-NOT: nofree
108; ATTRIBUTOR-NEXT: define void @_Z9delete_opPc(i8* %0) local_unnamed_addr #1
109define void @_Z9delete_opPc(i8* %0) local_unnamed_addr #0 {
110  %2 = icmp eq i8* %0, null
111  br i1 %2, label %4, label %3
112
113; <label>:3:                                      ; preds = %1
114  tail call void @_ZdaPv(i8* nonnull %0) #2
115  br label %4
116
117; <label>:4:                                      ; preds = %3, %1
118  ret void
119}
120
121
122; TEST 6 (negative case)
123; Call realloc
124; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
125; ATTRIBUTOR-NOT: nofree
126; ATTRIBUTOR-NEXT: define noalias i8* @call_realloc(i8* nocapture %0, i64 %1) local_unnamed_addr
127define noalias i8* @call_realloc(i8* nocapture %0, i64 %1) local_unnamed_addr #0 {
128    %ret = tail call i8* @realloc(i8* %0, i64 %1) #2
129    ret i8* %ret
130}
131
132
133; TEST 7 (positive case)
134; Call function declaration with "nofree"
135
136
137; ATTRIBUTOR: Function Attrs:  nofree noinline nounwind readnone uwtable
138; ATTRIBUTOR-NEXT: declare void @nofree_function()
139declare void @nofree_function() nofree readnone #0
140
141; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
142; ATTRIBUTOR-NEXT: define void @call_nofree_function()
143define void @call_nofree_function() #0 {
144    tail call void @nofree_function()
145    ret void
146}
147
148; TEST 8 (negative case)
149; Call function declaration without "nofree"
150
151
152; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
153; ATTRIBUTOR-NEXT: declare void @maybe_free()
154declare void @maybe_free() #0
155
156
157; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
158; ATTRIBUTOR-NOT: nofree
159; ATTRIBUTOR-NEXT: define void @call_maybe_free()
160define void @call_maybe_free() #0 {
161    tail call void @maybe_free()
162    ret void
163}
164
165
166; TEST 9 (negative case)
167; Call both of above functions
168
169; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
170; ATTRIBUTOR-NOT: nofree
171; ATTRIBUTOR-NEXT: define void @call_both()
172define void @call_both() #0 {
173    tail call void @maybe_free()
174    tail call void @nofree_function()
175    ret void
176}
177
178
179; TEST 10 (positive case)
180; Call intrinsic function
181; ATTRIBUTOR: Function Attrs: nounwind readnone speculatable
182; ATTRIBUTOR-NEXT: declare float @llvm.floor.f32(float)
183declare float @llvm.floor.f32(float)
184
185; FIXME: missing nofree
186; ATTRIBUTOR: Function Attrs: noinline nosync nounwind readnone uwtable
187; ATTRIBUTOR-NEXT: define void @call_floor(float %a)
188
189define void @call_floor(float %a) #0 {
190    tail call float @llvm.floor.f32(float %a)
191    ret void
192}
193
194; TEST 11 (positive case)
195; Check propagation.
196
197; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
198; ATTRIBUTOR-NEXT: define void @f1()
199define void @f1() #0 {
200    tail call void @nofree_function()
201    ret void
202}
203
204; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
205; ATTRIBUTOR-NEXT: define void @f2()
206define void @f2() #0 {
207    tail call void @f1()
208    ret void
209}
210
211; TEST 12 NoFree argument - positive.
212; ATTRIBUTOR: define double @test12(double* nocapture nofree nonnull readonly align 8 dereferenceable(8) %a)
213define double @test12(double* nocapture readonly %a) {
214entry:
215	%0 = load double, double* %a, align 8
216	%call = tail call double @cos(double %0) #2
217	ret double %call
218}
219
220declare double @cos(double) nobuiltin nounwind nofree
221
222; FIXME: %a should be nofree.
223; TEST 13 NoFree argument - positive.
224; ATTRIBUTOR: define noalias i32* @test13(i64* nocapture nonnull readonly align 8 dereferenceable(8) %a)
225define noalias i32* @test13(i64* nocapture readonly %a) {
226entry:
227	%0 = load i64, i64* %a, align 8
228	%call = tail call noalias i8* @malloc(i64 %0) #2
229	%1 = bitcast i8* %call to i32*
230	ret i32* %1
231}
232
233; ATTRIBUTOR: define void @test14(i8* nocapture %0, i8* nocapture nofree readnone %1)
234define void @test14(i8* nocapture %0, i8* nocapture %1) {
235	tail call void @free(i8* %0) #1
236	ret void
237}
238
239declare noalias i8* @malloc(i64)
240
241attributes #0 = { nounwind uwtable noinline }
242attributes #1 = { nounwind }
243attributes #2 = { nobuiltin nounwind }
244