; RUN: opt -passes=function-attrs --aa-pipeline=basic-aa --disable-nofree-inference=false -S < %s | FileCheck %s --check-prefix=FNATTR target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" ; Test cases specifically designed for the "nofree" function attribute. ; We use FIXME's to indicate problems and missing attributes. ; Free functions declare void @free(i8* nocapture) local_unnamed_addr #1 declare noalias i8* @realloc(i8* nocapture, i64) local_unnamed_addr #0 declare void @_ZdaPv(i8*) local_unnamed_addr #2 ; TEST 1 (positive case) ; FNATTR: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind readnone willreturn uwtable ; FNATTR-NEXT: define void @only_return() define void @only_return() #0 { ret void } ; TEST 2 (negative case) ; Only free ; void only_free(char* p) { ; free(p); ; } ; FNATTR: Function Attrs: noinline nounwind uwtable ; FNATTR-NEXT: define void @only_free(i8* nocapture %0) local_unnamed_addr define void @only_free(i8* nocapture %0) local_unnamed_addr #0 { tail call void @free(i8* %0) #1 ret void } ; TEST 3 (negative case) ; Free occurs in same scc. ; void free_in_scc1(char*p){ ; free_in_scc2(p); ; } ; void free_in_scc2(char*p){ ; free_in_scc1(p); ; free(p); ; } ; FNATTR: Function Attrs: noinline nounwind uwtable ; FNATTR-NEXT: define void @free_in_scc1(i8* nocapture %0) local_unnamed_addr define void @free_in_scc1(i8* nocapture %0) local_unnamed_addr #0 { tail call void @free_in_scc2(i8* %0) #1 ret void } ; FNATTR: Function Attrs: noinline nounwind uwtable ; FNATTR-NEXT: define void @free_in_scc2(i8* nocapture %0) local_unnamed_addr define void @free_in_scc2(i8* nocapture %0) local_unnamed_addr #0 { %cmp = icmp eq i8* %0, null br i1 %cmp, label %rec, label %call call: tail call void @free(i8* %0) #1 br label %end rec: tail call void @free_in_scc1(i8* %0) br label %end end: ret void } ; TEST 4 (positive case) ; Free doesn't occur. ; void mutual_recursion1(){ ; mutual_recursion2(); ; } ; void mutual_recursion2(){ ; mutual_recursion1(); ; } ; FNATTR: Function Attrs: nofree noinline nosync nounwind readnone uwtable ; FNATTR-NEXT: define void @mutual_recursion1() define void @mutual_recursion1() #0 { call void @mutual_recursion2() ret void } ; FNATTR: Function Attrs: nofree noinline nosync nounwind readnone uwtable ; FNATTR-NEXT: define void @mutual_recursion2() define void @mutual_recursion2() #0 { call void @mutual_recursion1() ret void } ; TEST 5 ; C++ delete operation (negative case) ; void delete_op (char p[]){ ; delete [] p; ; } ; FNATTR: Function Attrs: noinline nounwind uwtable ; FNATTR-NEXT: define void @_Z9delete_opPc(i8* %0) local_unnamed_addr define void @_Z9delete_opPc(i8* %0) local_unnamed_addr #0 { %2 = icmp eq i8* %0, null br i1 %2, label %4, label %3 ; <label>:3: ; preds = %1 tail call void @_ZdaPv(i8* nonnull %0) #2 br label %4 ; <label>:4: ; preds = %3, %1 ret void } ; TEST 6 (negative case) ; Call realloc ; FNATTR: Function Attrs: noinline nounwind uwtable ; FNATTR-NEXT: define noalias i8* @call_realloc(i8* nocapture %0, i64 %1) local_unnamed_addr define noalias i8* @call_realloc(i8* nocapture %0, i64 %1) local_unnamed_addr #0 { %ret = tail call i8* @realloc(i8* %0, i64 %1) #2 ret i8* %ret } ; TEST 7 (positive case) ; Call function declaration with "nofree" ; FNATTR: Function Attrs: nofree noinline nounwind readnone uwtable ; FNATTR-NEXT: declare void @nofree_function() declare void @nofree_function() nofree readnone #0 ; FNATTR: Function Attrs: nofree noinline nosync nounwind readnone uwtable ; FNATTR-NEXT: define void @call_nofree_function() define void @call_nofree_function() #0 { tail call void @nofree_function() ret void } ; TEST 8 (negative case) ; Call function declaration without "nofree" declare void @maybe_free() #0 ; FNATTR: Function Attrs: noinline nounwind uwtable ; FNATTR: define void @call_maybe_free() define void @call_maybe_free() #0 { tail call void @maybe_free() ret void } ; TEST 9 (negative case) ; Call both of above functions ; FNATTR: Function Attrs: noinline nounwind uwtable ; FNATTR-NEXT: define void @call_both() define void @call_both() #0 { tail call void @maybe_free() tail call void @nofree_function() ret void } ; TEST 10 (positive case) ; Call intrinsic function ; FNATTRS: Function Attrs: nofree noinline nosync readnone speculatable ; FNATTRS-NEXT: declare float @llvm.floor.f32(float %0) declare float @llvm.floor.f32(float) ; FNATTRS: Function Attrs: noinline nounwind uwtable ; FNATTRS-NEXT: define void @call_floor(float %a) ; FIXME: missing nofree define void @call_floor(float %a) #0 { tail call float @llvm.floor.f32(float %a) ret void } ; TEST 11 (positive case) ; Check propagation. ; FNATTRS: Function Attrs: noinline nounwind uwtable ; FNATTRS-NEXT: define void @f1() define void @f1() #0 { tail call void @nofree_function() ret void } ; FNATTRS: Function Attrs: noinline nounwind uwtable ; FNATTRS-NEXT: define void @f2() define void @f2() #0 { tail call void @f1() ret void } declare noalias i8* @malloc(i64) attributes #0 = { nounwind uwtable noinline } attributes #1 = { nounwind } attributes #2 = { nobuiltin nounwind }