|  | ; RUN: opt -functionattrs --disable-nofree-inference=false -S < %s | FileCheck %s --check-prefix=FNATTR | 
|  | ; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=7 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR | 
|  |  | 
|  | 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: noinline norecurse nounwind readnone uwtable | 
|  | ; FNATTR-NEXT: define void @only_return() | 
|  | ; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable | 
|  | ; ATTRIBUTOR-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 | 
|  | ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable | 
|  | ; ATTRIBUTOR-NOT: nofree | 
|  | ; ATTRIBUTOR-NEXT: define void @only_free(i8* nocapture %0) local_unnamed_addr #1 | 
|  | 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 | 
|  | ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable | 
|  | ; ATTRIBUTOR-NOT: nofree | 
|  | ; ATTRIBUTOR-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 | 
|  | ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable | 
|  | ; ATTRIBUTOR-NOT: nofree | 
|  | ; ATTRIBUTOR: 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: noinline nounwind readnone uwtable | 
|  | ; FNATTR-NEXT: define void @mutual_recursion1() | 
|  | ; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind uwtable | 
|  | ; ATTRIBUTOR-NEXT: define void @mutual_recursion1() | 
|  | define void @mutual_recursion1() #0 { | 
|  | call void @mutual_recursion2() | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; FNATTR: Function Attrs: noinline nounwind readnone uwtable | 
|  | ; FNATTR-NEXT: define void @mutual_recursion2() | 
|  | ; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind uwtable | 
|  | ; ATTRIBUTOR-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 | 
|  | ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable | 
|  | ; ATTRIBUTOR-NOT: nofree | 
|  | ; ATTRIBUTOR-NEXT: define void @_Z9delete_opPc(i8* %0) local_unnamed_addr #1 | 
|  | 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 | 
|  | ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable | 
|  | ; ATTRIBUTOR-NOT: nofree | 
|  | ; ATTRIBUTOR-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() | 
|  | ; ATTRIBUTOR: Function Attrs:  nofree noinline nounwind readnone uwtable | 
|  | ; ATTRIBUTOR-NEXT: declare void @nofree_function() | 
|  | declare void @nofree_function() nofree readnone #0 | 
|  |  | 
|  | ; FNATTR: Function Attrs: noinline nounwind readnone uwtable | 
|  | ; FNATTR-NEXT: define void @call_nofree_function() | 
|  | ; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable | 
|  | ; ATTRIBUTOR-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" | 
|  |  | 
|  |  | 
|  | ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable | 
|  | ; ATTRIBUTOR-NEXT: declare void @maybe_free() | 
|  | declare void @maybe_free() #0 | 
|  |  | 
|  |  | 
|  | ; FNATTR: Function Attrs: noinline nounwind uwtable | 
|  | ; FNATTR: define void @call_maybe_free() | 
|  | ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable | 
|  | ; ATTRIBUTOR-NOT: nofree | 
|  | ; ATTRIBUTOR-NEXT: 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() | 
|  | ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable | 
|  | ; ATTRIBUTOR-NOT: nofree | 
|  | ; ATTRIBUTOR-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: noinline readnone speculatable | 
|  | ; FNATTRS-NEXT: declare float @llvm.floor.f32(float %0) | 
|  | ; ATTRIBUTOR: Function Attrs: nounwind readnone speculatable | 
|  | ; ATTRIBUTOR-NEXT: declare float @llvm.floor.f32(float) | 
|  | declare float @llvm.floor.f32(float) | 
|  |  | 
|  | ; FNATTRS: Function Attrs: noinline nounwind uwtable | 
|  | ; FNATTRS-NEXT: define void @call_floor(float %a) | 
|  | ; FIXME: missing nofree | 
|  | ; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable | 
|  | ; ATTRIBUTOR-NEXT: define void @call_floor(float %a) | 
|  |  | 
|  | 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() | 
|  | ; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable | 
|  | ; ATTRIBUTOR-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() | 
|  | ; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable | 
|  | ; ATTRIBUTOR-NEXT: define void @f2() | 
|  | define void @f2() #0 { | 
|  | tail call void @f1() | 
|  | ret void | 
|  | } | 
|  |  | 
|  |  | 
|  | attributes #0 = { nounwind uwtable noinline } | 
|  | attributes #1 = { nounwind } | 
|  | attributes #2 = { nobuiltin nounwind } |