diff --git a/clang/test/CodeGenOpenCL/convergent.cl b/clang/test/CodeGenOpenCL/convergent.cl --- a/clang/test/CodeGenOpenCL/convergent.cl +++ b/clang/test/CodeGenOpenCL/convergent.cl @@ -134,7 +134,7 @@ __asm__ volatile("s_barrier"); } -// CHECK: attributes #0 = { nofree noinline norecurse nounwind willreturn " +// CHECK: attributes #0 = { nofree noinline norecurse nounwind willreturn mustprogress " // CHECK: attributes #1 = { {{[^}]*}}convergent{{[^}]*}} } // CHECK: attributes #2 = { {{[^}]*}}convergent{{[^}]*}} } // CHECK: attributes #3 = { {{[^}]*}}convergent noduplicate{{[^}]*}} } diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -57,6 +57,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Utils/Local.h" #include #include #include @@ -1556,21 +1557,7 @@ ++NumNoSync; }, /* RequiresExactDefinition= */ true}); - bool Changed = AI.run(SCCNodes); - - // readnone + not convergent implies nosync - // (This is here so that we don't have to duplicate the function local - // memory reasoning of the readnone analysis.) - for (Function *F : SCCNodes) { - if (!F || F->hasNoSync()) - continue; - if (!F->doesNotAccessMemory() || F->isConvergent()) - continue; - F->setNoSync(); - NumNoSync++; - Changed = true; - } - return Changed; + return AI.run(SCCNodes); } static SCCNodesResult createSCCNodeSet(ArrayRef Functions) { @@ -1630,6 +1617,14 @@ Changed |= addNoSyncAttr(Nodes.SCCNodes); + // Finally, infer the maximal set of attributes from the ones we've inferred + // above. This is handling the cases where one attribute on a signature + // implies another, but for implementation reasons the inference rule for + // the later is missing (or simply less sophisticated). + for (Function *F : Nodes.SCCNodes) + if (F) + Changed |= inferAttributesFromOthers(*F); + return Changed; } diff --git a/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll b/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll --- a/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll +++ b/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll @@ -72,13 +72,13 @@ 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 #1 = { nofree norecurse nosync nounwind willreturn writeonly } -; CHECK: attributes #2 = { nounwind readonly } +; CHECK: attributes #0 = { nofree norecurse nosync nounwind readnone willreturn mustprogress } +; CHECK: attributes #1 = { nofree norecurse nosync nounwind willreturn writeonly mustprogress } +; CHECK: attributes #2 = { nofree nounwind readonly } ; CHECK: attributes #3 = { nounwind } -; CHECK: attributes #4 = { nosync nounwind readnone willreturn } -; CHECK: attributes #5 = { nofree nosync nounwind willreturn } -; CHECK: attributes #6 = { nofree norecurse nosync nounwind willreturn } +; CHECK: attributes #4 = { nofree nosync nounwind readnone willreturn mustprogress } +; CHECK: attributes #5 = { nofree nosync nounwind willreturn mustprogress } +; CHECK: attributes #6 = { nofree norecurse nosync nounwind willreturn mustprogress } ; CHECK: attributes #7 = { argmemonly nofree nosync nounwind willreturn } ; Root note. diff --git a/llvm/test/CodeGen/AMDGPU/inline-attr.ll b/llvm/test/CodeGen/AMDGPU/inline-attr.ll --- a/llvm/test/CodeGen/AMDGPU/inline-attr.ll +++ b/llvm/test/CodeGen/AMDGPU/inline-attr.ll @@ -6,14 +6,14 @@ ; GCN: define amdgpu_kernel void @caller(float addrspace(1)* nocapture %p) local_unnamed_addr #1 { ; GCN: %mul.i = fmul float %load, 1.500000e+01 -; UNSAFE: attributes #0 = { norecurse nosync nounwind readnone willreturn "unsafe-fp-math"="true" } -; UNSAFE: attributes #1 = { nofree norecurse nosync nounwind willreturn "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="true" } +; UNSAFE: attributes #0 = { nofree norecurse nosync nounwind readnone willreturn mustprogress "unsafe-fp-math"="true" } +; UNSAFE: attributes #1 = { nofree norecurse nosync nounwind willreturn mustprogress "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="true" } -; NOINFS: attributes #0 = { norecurse nosync nounwind readnone willreturn "no-infs-fp-math"="true" } -; NOINFS: attributes #1 = { nofree norecurse nosync nounwind willreturn "less-precise-fpmad"="false" "no-infs-fp-math"="true" "no-nans-fp-math"="false" "unsafe-fp-math"="false" } +; NOINFS: attributes #0 = { nofree norecurse nosync nounwind readnone willreturn mustprogress "no-infs-fp-math"="true" } +; NOINFS: attributes #1 = { nofree norecurse nosync nounwind willreturn mustprogress "less-precise-fpmad"="false" "no-infs-fp-math"="true" "no-nans-fp-math"="false" "unsafe-fp-math"="false" } -; NONANS: attributes #0 = { norecurse nosync nounwind readnone willreturn "no-nans-fp-math"="true" } -; NONANS: attributes #1 = { nofree norecurse nosync nounwind willreturn "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="true" "unsafe-fp-math"="false" } +; NONANS: attributes #0 = { nofree norecurse nosync nounwind readnone willreturn mustprogress "no-nans-fp-math"="true" } +; NONANS: attributes #1 = { nofree norecurse nosync nounwind willreturn mustprogress "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="true" "unsafe-fp-math"="false" } define float @foo(float %x) #0 { entry: diff --git a/llvm/test/Other/cgscc-devirt-iteration.ll b/llvm/test/Other/cgscc-devirt-iteration.ll --- a/llvm/test/Other/cgscc-devirt-iteration.ll +++ b/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 ()* @@ -56,8 +56,8 @@ define void @test2_a(void ()** %ignore) { ; BEFORE-NOT: Function Attrs -; AFTER1: Function Attrs: readonly -; AFTER2: Function Attrs: nosync readnone +; AFTER1: Function Attrs: nofree readonly +; AFTER2: Function Attrs: nofree nosync readnone ; BEFORE: define void @test2_a(void ()** %ignore) ; AFTER: define void @test2_a(void ()** readnone %ignore) entry: @@ -77,8 +77,8 @@ define void @test2_b() { ; BEFORE-NOT: Function Attrs -; AFTER1: Function Attrs: readonly -; AFTER2: Function Attrs: nosync readnone +; AFTER1: Function Attrs: nofree readonly +; AFTER2: Function Attrs: nofree nosync readnone ; CHECK-LABEL: define void @test2_b() entry: %f2ptr = alloca void ()* diff --git a/llvm/test/Other/cgscc-iterate-function-mutation.ll b/llvm/test/Other/cgscc-iterate-function-mutation.ll --- a/llvm/test/Other/cgscc-iterate-function-mutation.ll +++ b/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 } diff --git a/llvm/test/Other/cgscc-observe-devirt.ll b/llvm/test/Other/cgscc-observe-devirt.ll --- a/llvm/test/Other/cgscc-observe-devirt.ll +++ b/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 } diff --git a/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll b/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll --- a/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll +++ b/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 } diff --git a/llvm/test/Transforms/FunctionAttrs/atomic.ll b/llvm/test/Transforms/FunctionAttrs/atomic.ll --- a/llvm/test/Transforms/FunctionAttrs/atomic.ll +++ b/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 #1 = { nofree norecurse nounwind ssp uwtable willreturn } +; CHECK: attributes #0 = { nofree norecurse nosync nounwind readnone ssp uwtable willreturn mustprogress } +; CHECK: attributes #1 = { nofree norecurse nounwind ssp uwtable willreturn mustprogress } diff --git a/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll b/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll --- a/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll +++ b/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 mustprogress } ; CHECK-NOT: attributes diff --git a/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll b/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll --- a/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll +++ b/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) diff --git a/llvm/test/Transforms/FunctionAttrs/nofree.ll b/llvm/test/Transforms/FunctionAttrs/nofree.ll --- a/llvm/test/Transforms/FunctionAttrs/nofree.ll +++ b/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 diff --git a/llvm/test/Transforms/FunctionAttrs/nosync.ll b/llvm/test/Transforms/FunctionAttrs/nosync.ll --- a/llvm/test/Transforms/FunctionAttrs/nosync.ll +++ b/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]] diff --git a/llvm/test/Transforms/FunctionAttrs/nounwind.ll b/llvm/test/Transforms/FunctionAttrs/nounwind.ll --- a/llvm/test/Transforms/FunctionAttrs/nounwind.ll +++ b/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() diff --git a/llvm/test/Transforms/FunctionAttrs/optnone.ll b/llvm/test/Transforms/FunctionAttrs/optnone.ll --- a/llvm/test/Transforms/FunctionAttrs/optnone.ll +++ b/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 mustprogress } ; CHECK-LABEL: attributes #1 ; CHECK: = { noinline optnone } diff --git a/llvm/test/Transforms/FunctionAttrs/willreturn-callsites.ll b/llvm/test/Transforms/FunctionAttrs/willreturn-callsites.ll --- a/llvm/test/Transforms/FunctionAttrs/willreturn-callsites.ll +++ b/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() # diff --git a/llvm/test/Transforms/FunctionAttrs/writeonly.ll b/llvm/test/Transforms/FunctionAttrs/writeonly.ll --- a/llvm/test/Transforms/FunctionAttrs/writeonly.ll +++ b/llvm/test/Transforms/FunctionAttrs/writeonly.ll @@ -27,4 +27,4 @@ ; CHECK: attributes #0 = { {{.*}} readnone {{.*}} } ; CHECK: attributes #1 = { {{.*}} readonly {{.*}} } -; CHECK: attributes #2 = { {{.*}} writeonly } +; CHECK: attributes #2 = { {{.*}} writeonly {{.*}} } diff --git a/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll b/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll --- a/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll +++ b/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll @@ -52,5 +52,5 @@ !28 = !DILocation(line: 9, column: 18, scope: !2) !29 = !DILocation(line: 10, column: 1, scope: !2) -; CHECK: attributes #0 = { nofree norecurse nosync nounwind willreturn } +; CHECK: attributes #0 = { nofree norecurse nosync nounwind willreturn mustprogress } ; CHECK-NOT: foo.coefficient1 diff --git a/llvm/test/Transforms/Inline/cgscc-update.ll b/llvm/test/Transforms/Inline/cgscc-update.ll --- a/llvm/test/Transforms/Inline/cgscc-update.ll +++ b/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: