Index: llvm/include/llvm/IR/Function.h =================================================================== --- llvm/include/llvm/IR/Function.h +++ llvm/include/llvm/IR/Function.h @@ -627,7 +627,7 @@ /// Determine if the call might deallocate memory. bool doesNotFreeMemory() const { - return onlyReadsMemory() || hasFnAttribute(Attribute::NoFree); + return hasFnAttribute(Attribute::NoFree); } void setDoesNotFreeMemory() { addFnAttr(Attribute::NoFree); Index: llvm/lib/Transforms/IPO/FunctionAttrs.cpp =================================================================== --- llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -1270,6 +1270,11 @@ if (CB->hasFnAttr(Attribute::NoFree)) return false; + // readnone/readonly implies nofree + // (This helps bootstrap from partially annotated functions) + if (CB->onlyReadsMemory()) + return false; + Function *Callee = CB->getCalledFunction(); if (!Callee) return true; @@ -1367,7 +1372,20 @@ /* RequiresExactDefinition= */ true}); // Perform all the requested attribute inference actions. - return AI.run(SCCNodes); + bool Changed = AI.run(SCCNodes); + // readonly implies nofree + // (This is here so that we don't have to duplicate the function local + // memory reasoning of the readonly analysis.) + for (Function *F : SCCNodes) { + if (!F || F->doesNotFreeMemory()) + continue; + if (!F->onlyReadsMemory()) + continue; + F->setDoesNotFreeMemory(); + NumNoFree++; + Changed = true; + } + return Changed; } static bool addNoRecurseAttrs(const SCCNodeSet &SCCNodes) { Index: llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll =================================================================== --- llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll +++ llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll @@ -72,11 +72,11 @@ declare void @callee(i32* %p) nounwind declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1) nounwind -; CHECK: attributes #0 = { norecurse nosync nounwind readnone willreturn } +; CHECK: attributes #0 = { nofree norecurse nosync nounwind readnone willreturn } ; CHECK: attributes #1 = { nofree norecurse nosync nounwind willreturn writeonly } -; CHECK: attributes #2 = { nounwind readonly } +; CHECK: attributes #2 = { nofree nounwind readonly } ; CHECK: attributes #3 = { nounwind } -; CHECK: attributes #4 = { nosync nounwind readnone willreturn } +; CHECK: attributes #4 = { nofree nosync nounwind readnone willreturn } ; CHECK: attributes #5 = { nofree nosync nounwind willreturn } ; CHECK: attributes #6 = { nofree norecurse nosync nounwind willreturn } ; CHECK: attributes #7 = { argmemonly nofree nosync nounwind willreturn } Index: llvm/test/Other/cgscc-devirt-iteration.ll =================================================================== --- llvm/test/Other/cgscc-devirt-iteration.ll +++ llvm/test/Other/cgscc-devirt-iteration.ll @@ -28,7 +28,7 @@ define void @test1() { ; BEFORE-NOT: Function Attrs -; AFTER: Function Attrs: nosync readnone +; AFTER: Function Attrs: nofree nosync readnone ; CHECK-LABEL: define void @test1() entry: %fptr = alloca void ()* @@ -57,7 +57,7 @@ define void @test2_a(void ()** %ignore) { ; BEFORE-NOT: Function Attrs ; AFTER1: Function Attrs: readonly -; AFTER2: Function Attrs: nosync readnone +; AFTER2: Function Attrs: nofree nosync readnone ; BEFORE: define void @test2_a(void ()** %ignore) ; AFTER: define void @test2_a(void ()** readnone %ignore) entry: @@ -78,7 +78,7 @@ define void @test2_b() { ; BEFORE-NOT: Function Attrs ; AFTER1: Function Attrs: readonly -; AFTER2: Function Attrs: nosync readnone +; AFTER2: Function Attrs: nofree nosync readnone ; CHECK-LABEL: define void @test2_b() entry: %f2ptr = alloca void ()* Index: llvm/test/Other/cgscc-iterate-function-mutation.ll =================================================================== --- llvm/test/Other/cgscc-iterate-function-mutation.ll +++ llvm/test/Other/cgscc-iterate-function-mutation.ll @@ -1,8 +1,8 @@ ; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(function-attrs,function(simplify-cfg))' -S < %s | FileCheck %s -declare void @readnone() nosync readnone +declare void @readnone() nofree nosync readnone declare void @unknown() -declare void @reference_function_pointer(void()*) nosync readnone +declare void @reference_function_pointer(void()*) nofree nosync readnone ; The @test1_* set of functions checks that when we mutate functions with ; simplify-cfg to delete call edges and this ends up splitting both the SCCs @@ -338,4 +338,4 @@ ret void } -; CHECK: attributes #0 = { nosync readnone } +; CHECK: attributes #0 = { nofree nosync readnone } Index: llvm/test/Other/cgscc-observe-devirt.ll =================================================================== --- llvm/test/Other/cgscc-observe-devirt.ll +++ llvm/test/Other/cgscc-observe-devirt.ll @@ -10,7 +10,7 @@ ; without requiring the outer manager to iterate doesn't break any invariant. ; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(function-attrs,function(gvn),function-attrs)' -S < %s | FileCheck %s --check-prefix=AFTER -declare void @readnone() nosync readnone +declare void @readnone() nofree nosync readnone declare void @unknown() ; The @test1_* checks that if we refine an indirect call to a direct call and @@ -103,4 +103,4 @@ ret void } -; CHECK: attributes #0 = { nosync readnone } +; CHECK: attributes #0 = { nofree nosync readnone } Index: llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll +++ llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll @@ -8,7 +8,8 @@ ret i32 %tmp } -; CHECK: declare i32 @e() #0 +; CHECK: declare i32 @e() #1 declare i32 @e() readonly -; CHECK: attributes #0 = { readonly } +; CHECK: attributes #0 = { nofree readonly } +; CHECK: attributes #1 = { readonly } Index: llvm/test/Transforms/FunctionAttrs/atomic.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/atomic.ll +++ llvm/test/Transforms/FunctionAttrs/atomic.ll @@ -20,5 +20,5 @@ ret i32 %r } -; CHECK: attributes #0 = { norecurse nosync nounwind readnone ssp uwtable willreturn } +; CHECK: attributes #0 = { nofree norecurse nosync nounwind readnone ssp uwtable willreturn } ; CHECK: attributes #1 = { nofree norecurse nounwind ssp uwtable willreturn } Index: llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll +++ llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll @@ -28,5 +28,5 @@ attributes #0 = { argmemonly } attributes #1 = { inaccessiblememonly } attributes #2 = { inaccessiblemem_or_argmemonly } -; CHECK: attributes #0 = { norecurse nosync nounwind readnone willreturn } +; CHECK: attributes #0 = { nofree norecurse nosync nounwind readnone willreturn } ; CHECK-NOT: attributes Index: llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll +++ llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll @@ -12,7 +12,7 @@ ; TEST 1 (positive case) -; FNATTR: Function Attrs: noinline norecurse nosync nounwind readnone uwtable +; FNATTR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable ; FNATTR-NEXT: define void @only_return() define void @only_return() #0 { ret void @@ -78,14 +78,14 @@ ; } -; FNATTR: Function Attrs: noinline nosync nounwind readnone uwtable +; 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: noinline nosync nounwind readnone uwtable +; FNATTR: Function Attrs: nofree noinline nosync nounwind readnone uwtable ; FNATTR-NEXT: define void @mutual_recursion2() define void @mutual_recursion2() #0 { call void @mutual_recursion1() @@ -132,7 +132,7 @@ ; FNATTR-NEXT: declare void @nofree_function() declare void @nofree_function() nofree readnone #0 -; FNATTR: Function Attrs: noinline nosync nounwind readnone uwtable +; 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() @@ -168,7 +168,7 @@ ; TEST 10 (positive case) ; Call intrinsic function -; FNATTRS: Function Attrs: noinline nosync readnone speculatable +; FNATTRS: Function Attrs: nofree noinline nosync readnone speculatable ; FNATTRS-NEXT: declare float @llvm.floor.f32(float %0) declare float @llvm.floor.f32(float) Index: llvm/test/Transforms/FunctionAttrs/nofree.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/nofree.ll +++ llvm/test/Transforms/FunctionAttrs/nofree.ll @@ -36,7 +36,7 @@ declare void @free(i8* nocapture) local_unnamed_addr #2 define i32 @_Z4foo3Pi(i32* nocapture readonly %a) local_unnamed_addr #3 { -; CHECK: Function Attrs: norecurse nosync nounwind readonly uwtable willreturn +; CHECK: Function Attrs: nofree norecurse nosync nounwind readonly uwtable willreturn ; CHECK-LABEL: @_Z4foo3Pi( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[A:%.*]], align 4 Index: llvm/test/Transforms/FunctionAttrs/nosync.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/nosync.ll +++ llvm/test/Transforms/FunctionAttrs/nosync.ll @@ -6,7 +6,7 @@ ; Base case, empty function define void @test1() { -; CHECK: Function Attrs: norecurse nosync nounwind readnone willreturn +; CHECK: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; CHECK-LABEL: @test1( ; CHECK-NEXT: ret void ; @@ -15,7 +15,7 @@ ; Show the bottom up walk define void @test2() { -; CHECK: Function Attrs: norecurse nosync nounwind readnone willreturn +; CHECK: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; CHECK-LABEL: @test2( ; CHECK-NEXT: call void @test1() ; CHECK-NEXT: ret void @@ -38,7 +38,7 @@ } define i32 @test4(i32 %a, i32 %b) { -; CHECK: Function Attrs: norecurse nosync nounwind readnone willreturn +; CHECK: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; CHECK-LABEL: @test4( ; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A:%.*]], [[B:%.*]] ; CHECK-NEXT: ret i32 [[A]] @@ -137,7 +137,7 @@ } define i32 @load_unordered(i32* nocapture readonly %0) norecurse nounwind uwtable { -; CHECK: Function Attrs: norecurse nosync nounwind readonly uwtable willreturn +; CHECK: Function Attrs: nofree norecurse nosync nounwind readonly uwtable willreturn ; CHECK-LABEL: @load_unordered( ; CHECK-NEXT: [[TMP2:%.*]] = load atomic i32, i32* [[TMP0:%.*]] unordered, align 4 ; CHECK-NEXT: ret i32 [[TMP2]] @@ -271,7 +271,7 @@ ; negative. Convergent define void @convergent_readnone(){ -; CHECK: Function Attrs: nosync readnone +; CHECK: Function Attrs: nofree nosync readnone ; CHECK-LABEL: @convergent_readnone( ; CHECK-NEXT: call void @readnone_test() ; CHECK-NEXT: ret void @@ -299,7 +299,7 @@ declare float @llvm.cos(float %val) readnone define float @cos_test(float %x) { -; CHECK: Function Attrs: nosync nounwind readnone willreturn +; CHECK: Function Attrs: nofree nosync nounwind readnone willreturn ; CHECK-LABEL: @cos_test( ; CHECK-NEXT: [[C:%.*]] = call float @llvm.cos.f32(float [[X:%.*]]) ; CHECK-NEXT: ret float [[C]] Index: llvm/test/Transforms/FunctionAttrs/nounwind.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/nounwind.ll +++ llvm/test/Transforms/FunctionAttrs/nounwind.ll @@ -1,14 +1,14 @@ ; RUN: opt < %s -function-attrs -S | FileCheck %s ; TEST 1 -; CHECK: Function Attrs: norecurse nosync nounwind readnone +; CHECK: Function Attrs: nofree norecurse nosync nounwind readnone ; CHECK-NEXT: define i32 @foo1() define i32 @foo1() { ret i32 1 } ; TEST 2 -; CHECK: Function Attrs: nosync nounwind readnone +; CHECK: Function Attrs: nofree nosync nounwind readnone ; CHECK-NEXT: define i32 @scc1_foo() define i32 @scc1_foo() { %1 = call i32 @scc1_bar() @@ -17,7 +17,7 @@ ; TEST 3 -; CHECK: Function Attrs: nosync nounwind readnone +; CHECK: Function Attrs: nofree nosync nounwind readnone ; CHECK-NEXT: define i32 @scc1_bar() define i32 @scc1_bar() { %1 = call i32 @scc1_foo() Index: llvm/test/Transforms/FunctionAttrs/optnone.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/optnone.ll +++ llvm/test/Transforms/FunctionAttrs/optnone.ll @@ -20,6 +20,6 @@ ; CHECK: (i8*) #1 ; CHECK-LABEL: attributes #0 -; CHECK: = { norecurse nosync nounwind readnone willreturn } +; CHECK: = { nofree norecurse nosync nounwind readnone willreturn } ; CHECK-LABEL: attributes #1 ; CHECK: = { noinline optnone } Index: llvm/test/Transforms/FunctionAttrs/willreturn-callsites.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/willreturn-callsites.ll +++ llvm/test/Transforms/FunctionAttrs/willreturn-callsites.ll @@ -38,7 +38,7 @@ } define void @test_fn_mustprogress_readonly_calls(i32* %ptr) mustprogress { -; CHECK: Function Attrs: readonly willreturn mustprogress +; CHECK: Function Attrs: nofree readonly willreturn mustprogress ; CHECK-LABEL: @test_fn_mustprogress_readonly_calls( ; CHECK-NOT: call void @decl_readonly() # ; CHECK-NOT: call void @decl_readnone() # Index: llvm/test/Transforms/Inline/cgscc-update.ll =================================================================== --- llvm/test/Transforms/Inline/cgscc-update.ll +++ llvm/test/Transforms/Inline/cgscc-update.ll @@ -27,7 +27,7 @@ } ; This function should have had 'readnone' deduced for its SCC. -; CHECK: Function Attrs: noinline nosync nounwind readnone +; CHECK: Function Attrs: nofree noinline nosync nounwind readnone ; CHECK-NEXT: define void @test1_g() define void @test1_g() noinline { entry: @@ -36,7 +36,7 @@ } ; This function should have had 'readnone' deduced for its SCC. -; CHECK: Function Attrs: noinline nosync nounwind readnone +; CHECK: Function Attrs: nofree noinline nosync nounwind readnone ; CHECK-NEXT: define void @test1_h() define void @test1_h() noinline { entry: @@ -59,7 +59,7 @@ } ; This function should have had 'readnone' deduced for its SCC. -; CHECK: Function Attrs: noinline nosync nounwind readnone +; CHECK: Function Attrs: nofree noinline nosync nounwind readnone ; CHECK-NEXT: define void @test2_g() define void @test2_g() noinline { entry: @@ -69,7 +69,7 @@ } ; This function should have had 'readnone' deduced for its SCC. -; CHECK: Function Attrs: noinline nosync nounwind readnone +; CHECK: Function Attrs: nofree noinline nosync nounwind readnone ; CHECK-NEXT: define void @test2_h() define void @test2_h() noinline { entry: @@ -152,7 +152,7 @@ ; form a new SCC and should use that can deduce precise function attrs. ; This function should have had 'readnone' deduced for its SCC. -; CHECK: Function Attrs: noinline nosync nounwind readnone +; CHECK: Function Attrs: nofree noinline nosync nounwind readnone ; CHECK-NEXT: define void @test4_f1() define void @test4_f1() noinline { entry: @@ -175,7 +175,7 @@ } ; This function should have had 'readnone' deduced for its SCC. -; CHECK: Function Attrs: noinline nosync nounwind readnone +; CHECK: Function Attrs: nofree noinline nosync nounwind readnone ; CHECK-NEXT: define void @test4_h() define void @test4_h() noinline { entry: